diff --git a/.gitignore b/.gitignore index 4055e12ee8..5668d02782 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ /qapi/qapi-commands-common.[ch] /qapi/qapi-commands-crypto.[ch] /qapi/qapi-commands-introspect.[ch] +/qapi/qapi-commands-job.[ch] /qapi/qapi-commands-migration.[ch] /qapi/qapi-commands-misc.[ch] /qapi/qapi-commands-net.[ch] @@ -53,6 +54,7 @@ /qapi/qapi-events-common.[ch] /qapi/qapi-events-crypto.[ch] /qapi/qapi-events-introspect.[ch] +/qapi/qapi-events-job.[ch] /qapi/qapi-events-migration.[ch] /qapi/qapi-events-misc.[ch] /qapi/qapi-events-net.[ch] @@ -71,6 +73,7 @@ /qapi/qapi-types-common.[ch] /qapi/qapi-types-crypto.[ch] /qapi/qapi-types-introspect.[ch] +/qapi/qapi-types-job.[ch] /qapi/qapi-types-migration.[ch] /qapi/qapi-types-misc.[ch] /qapi/qapi-types-net.[ch] @@ -88,6 +91,7 @@ /qapi/qapi-visit-common.[ch] /qapi/qapi-visit-crypto.[ch] /qapi/qapi-visit-introspect.[ch] +/qapi/qapi-visit-job.[ch] /qapi/qapi-visit-migration.[ch] /qapi/qapi-visit-misc.[ch] /qapi/qapi-visit-net.[ch] @@ -151,6 +155,7 @@ .sdk *.gcda *.gcno +*.gcov /pc-bios/bios-pq/status /pc-bios/vgabios-pq/status /pc-bios/optionrom/linuxboot.asm @@ -206,3 +211,4 @@ trace-dtrace-root.h trace-dtrace-root.dtrace trace-ust-all.h trace-ust-all.c +/target/arm/decode-sve.inc.c diff --git a/.gitmodules b/.gitmodules index 49e9c2e3f4..d108478e0a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "roms/vgabios"] - path = roms/vgabios - url = git://git.qemu-project.org/vgabios.git/ [submodule "roms/seabios"] path = roms/seabios url = git://git.qemu-project.org/seabios.git/ diff --git a/.shippable.yml b/.shippable.yml index 60f2ce9218..f74a3de3ff 100644 --- a/.shippable.yml +++ b/.shippable.yml @@ -35,13 +35,5 @@ build: options: "-e HOME=/root" ci: - unset CC - # some targets require newer up to date packages, for example TARGET_LIST matching - # aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) - # see the configure script: - # error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:" - # " (1) Preferred: Install the DTC (libfdt) devel package" - # " (2) Fetch the DTC submodule, using:" - # " git submodule update --init dtc" - - dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} - make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) diff --git a/CODING_STYLE b/CODING_STYLE index 12ba58ee29..ec075dedc4 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -124,6 +124,23 @@ We use traditional C-style /* */ comments and avoid // comments. Rationale: The // form is valid in C99, so this is purely a matter of consistency of style. The checkpatch script will warn you about this. +Multiline comment blocks should have a row of stars on the left, +and the initial /* and terminating */ both on their own lines: + /* + * like + * this + */ +This is the same format required by the Linux kernel coding style. + +(Some of the existing comments in the codebase use the GNU Coding +Standards form which does not have stars on the left, or other +variations; avoid these when writing new comments, but don't worry +about converting to the preferred form unless you're editing that +comment anyway.) + +Rationale: Consistency, and ease of visually picking out a multiline +comment from the surrounding code. + 8. trace-events style 8.1 0x prefix diff --git a/COPYING.PYTHON b/COPYING.PYTHON deleted file mode 100644 index 4d3f1ef276..0000000000 --- a/COPYING.PYTHON +++ /dev/null @@ -1,270 +0,0 @@ -A. HISTORY OF THE SOFTWARE -========================== - -Python was created in the early 1990s by Guido van Rossum at Stichting -Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands -as a successor of a language called ABC. Guido remains Python's -principal author, although it includes many contributions from others. - -In 1995, Guido continued his work on Python at the Corporation for -National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) -in Reston, Virginia where he released several versions of the -software. - -In May 2000, Guido and the Python core development team moved to -BeOpen.com to form the BeOpen PythonLabs team. In October of the same -year, the PythonLabs team moved to Digital Creations (now Zope -Corporation, see http://www.zope.com). In 2001, the Python Software -Foundation (PSF, see http://www.python.org/psf/) was formed, a -non-profit organization created specifically to own Python-related -Intellectual Property. Zope Corporation is a sponsoring member of -the PSF. - -All Python releases are Open Source (see http://www.opensource.org for -the Open Source Definition). Historically, most, but not all, Python -releases have also been GPL-compatible; the table below summarizes -the various releases. - - Release Derived Year Owner GPL- - from compatible? (1) - - 0.9.0 thru 1.2 1991-1995 CWI yes - 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes - 1.6 1.5.2 2000 CNRI no - 2.0 1.6 2000 BeOpen.com no - 1.6.1 1.6 2001 CNRI yes (2) - 2.1 2.0+1.6.1 2001 PSF no - 2.0.1 2.0+1.6.1 2001 PSF yes - 2.1.1 2.1+2.0.1 2001 PSF yes - 2.2 2.1.1 2001 PSF yes - 2.1.2 2.1.1 2002 PSF yes - 2.1.3 2.1.2 2002 PSF yes - 2.2.1 2.2 2002 PSF yes - 2.2.2 2.2.1 2002 PSF yes - 2.2.3 2.2.2 2003 PSF yes - 2.3 2.2.2 2002-2003 PSF yes - 2.3.1 2.3 2002-2003 PSF yes - 2.3.2 2.3.1 2002-2003 PSF yes - 2.3.3 2.3.2 2002-2003 PSF yes - 2.3.4 2.3.3 2004 PSF yes - 2.3.5 2.3.4 2005 PSF yes - 2.4 2.3 2004 PSF yes - 2.4.1 2.4 2005 PSF yes - 2.4.2 2.4.1 2005 PSF yes - 2.4.3 2.4.2 2006 PSF yes - 2.5 2.4 2006 PSF yes - 2.7 2.6 2010 PSF yes - -Footnotes: - -(1) GPL-compatible doesn't mean that we're distributing Python under - the GPL. All Python licenses, unlike the GPL, let you distribute - a modified version without making your changes open source. The - GPL-compatible licenses make it possible to combine Python with - other software that is released under the GPL; the others don't. - -(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, - because its license has a choice of law clause. According to - CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 - is "not incompatible" with the GPL. - -Thanks to the many outside volunteers who have worked under Guido's -direction to make these releases possible. - - -B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON -=============================================================== - -PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 --------------------------------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using this software ("Python") in source or binary form and -its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python -alone or in any derivative version, provided, however, that PSF's -License Agreement and PSF's notice of copyright, i.e., "Copyright (c) -2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights -Reserved" are retained in Python alone or in any derivative version -prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python. - -4. PSF is making Python available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 -------------------------------------------- - -BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 - -1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an -office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the -Individual or Organization ("Licensee") accessing and otherwise using -this software in source or binary form and its associated -documentation ("the Software"). - -2. Subject to the terms and conditions of this BeOpen Python License -Agreement, BeOpen hereby grants Licensee a non-exclusive, -royalty-free, world-wide license to reproduce, analyze, test, perform -and/or display publicly, prepare derivative works, distribute, and -otherwise use the Software alone or in any derivative version, -provided, however, that the BeOpen Python License is retained in the -Software, alone or in any derivative version prepared by Licensee. - -3. BeOpen is making the Software available to Licensee on an "AS IS" -basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE -SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS -AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY -DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -5. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -6. This License Agreement shall be governed by and interpreted in all -respects by the law of the State of California, excluding conflict of -law provisions. Nothing in this License Agreement shall be deemed to -create any relationship of agency, partnership, or joint venture -between BeOpen and Licensee. This License Agreement does not grant -permission to use BeOpen trademarks or trade names in a trademark -sense to endorse or promote products or services of Licensee, or any -third party. As an exception, the "BeOpen Python" logos available at -http://www.pythonlabs.com/logos.html may be used according to the -permissions granted on that web page. - -7. By copying, installing or otherwise using the software, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 ---------------------------------------- - -1. This LICENSE AGREEMENT is between the Corporation for National -Research Initiatives, having an office at 1895 Preston White Drive, -Reston, VA 20191 ("CNRI"), and the Individual or Organization -("Licensee") accessing and otherwise using Python 1.6.1 software in -source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, CNRI -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python 1.6.1 -alone or in any derivative version, provided, however, that CNRI's -License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) -1995-2001 Corporation for National Research Initiatives; All Rights -Reserved" are retained in Python 1.6.1 alone or in any derivative -version prepared by Licensee. Alternately, in lieu of CNRI's License -Agreement, Licensee may substitute the following text (omitting the -quotes): "Python 1.6.1 is made available subject to the terms and -conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the Internet using the following -unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the Internet -using the following URL: http://hdl.handle.net/1895.22/1013". - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python 1.6.1 or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python 1.6.1. - -4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" -basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. This License Agreement shall be governed by the federal -intellectual property law of the United States, including without -limitation the federal copyright law, and, to the extent such -U.S. federal law does not apply, by the law of the Commonwealth of -Virginia, excluding Virginia's conflict of law provisions. -Notwithstanding the foregoing, with regard to derivative works based -on Python 1.6.1 that incorporate non-separable material that was -previously distributed under the GNU General Public License (GPL), the -law of the Commonwealth of Virginia shall govern this License -Agreement only as to issues arising under or with respect to -Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this -License Agreement shall be deemed to create any relationship of -agency, partnership, or joint venture between CNRI and Licensee. This -License Agreement does not grant permission to use CNRI trademarks or -trade name in a trademark sense to endorse or promote products or -services of Licensee, or any third party. - -8. By clicking on the "ACCEPT" button where indicated, or by copying, -installing or otherwise using Python 1.6.1, Licensee agrees to be -bound by the terms and conditions of this License Agreement. - - ACCEPT - - -CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 --------------------------------------------------- - -Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, -The Netherlands. All rights reserved. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of Stichting Mathematisch -Centrum or CWI not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE -FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/HACKING b/HACKING index 4125c97d8d..0fc3e0fc04 100644 --- a/HACKING +++ b/HACKING @@ -118,6 +118,15 @@ Please note that g_malloc will exit on allocation failure, so there is no need to test for failure (as you would have to with malloc). Calling g_malloc with a zero size is valid and will return NULL. +Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following +reasons: + + a. It catches multiplication overflowing size_t; + b. It returns T * instead of void *, letting compiler catch more type + errors. + +Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though. + Memory allocated by qemu_memalign or qemu_blockalign must be freed with qemu_vfree, since breaking this will cause problems on Win32. diff --git a/MAINTAINERS b/MAINTAINERS index 24b70169bc..666e936812 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -135,6 +135,8 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: target/arm/ +F: tests/tcg/arm/ +F: tests/tcg/aarch64/ F: hw/arm/ F: hw/cpu/a*mpcore.c F: include/hw/cpu/a*mpcore.h @@ -185,7 +187,7 @@ F: disas/microblaze.c MIPS M: Aurelien Jarno -M: Yongbok Kim +M: Aleksandar Markovic S: Maintained F: target/mips/ F: hw/mips/ @@ -283,6 +285,8 @@ M: Richard Henderson M: Eduardo Habkost S: Maintained F: target/i386/ +F: tests/tcg/i386/ +F: tests/tcg/x86_64/ F: hw/i386/ F: disas/i386.c T: git git://github.com/ehabkost/qemu.git x86-next @@ -303,6 +307,10 @@ F: target/tricore/ F: hw/tricore/ F: include/hw/tricore/ +Multiarch Linux User Tests +M: Alex Bennée +F: tests/tcg/multiarch/ + Guest CPU Cores (KVM): ---------------------- @@ -447,6 +455,10 @@ F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h +F: hw/misc/tz-ppc.c +F: include/hw/misc/tz-ppc.h +F: hw/misc/tz-mpc.c +F: include/hw/misc/tz-mpc.h ARM cores M: Peter Maydell @@ -489,9 +501,10 @@ F: include/hw/arm/digic.h F: hw/*/digic* Gumstix +M: Philippe Mathieu-Daudé L: qemu-devel@nongnu.org L: qemu-arm@nongnu.org -S: Orphan +S: Odd Fixes F: hw/arm/gumstix.c i.MX31 @@ -515,8 +528,11 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/arm/mps2.c -F: hw/misc/mps2-scc.c -F: include/hw/misc/mps2-scc.h +F: hw/arm/mps2-tz.c +F: hw/misc/mps2-*.c +F: include/hw/misc/mps2-*.h +F: hw/arm/iotkit.c +F: include/hw/arm/iotkit.h Musicpal M: Jan Kiszka @@ -629,6 +645,17 @@ M: Subbaraya Sundeep S: Maintained F: hw/arm/msf2-som.c +ASPEED BMCs +M: Cédric Le Goater +R: Andrew Jeffery +R: Joel Stanley +L: qemu-arm@nongnu.org +S: Maintained +F: hw/*/*aspeed* +F: include/hw/*/*aspeed* +F: hw/net/ftgmac100.c +F: include/hw/net/ftgmac100.h + CRIS Machines ------------- Axis Dev88 @@ -691,7 +718,7 @@ S: Maintained F: hw/mips/mips_malta.c Mipssim -M: Yongbok Kim +M: Aleksandar Markovic S: Odd Fixes F: hw/mips/mips_mipssim.c F: hw/net/mipsnet.c @@ -702,7 +729,7 @@ S: Maintained F: hw/mips/mips_r4k.c Fulong 2E -M: Yongbok Kim +M: Aleksandar Markovic S: Odd Fixes F: hw/mips/mips_fulong2e.c F: hw/isa/vt82c686.c @@ -761,8 +788,11 @@ F: hw/ppc/mac_newworld.c F: hw/pci-host/uninorth.c F: hw/pci-bridge/dec.[hc] F: hw/misc/macio/ -F: include/hw/ppc/mac_dbdma.h +F: hw/misc/mos6522.c F: hw/nvram/mac_nvram.c +F: include/hw/misc/macio/ +F: include/hw/misc/mos6522.h +F: include/hw/ppc/mac_dbdma.h Old World M: Alexander Graf @@ -821,6 +851,7 @@ M: BALATON Zoltan L: qemu-ppc@nongnu.org S: Maintained F: hw/ide/sii3112.c +F: hw/timer/m41t80.c SH4 Machines ------------ @@ -909,7 +940,7 @@ X86 Machines ------------ PC M: Michael S. Tsirkin -M: Marcel Apfelbaum +M: Marcel Apfelbaum S: Supported F: include/hw/i386/ F: hw/i386/ @@ -959,7 +990,7 @@ F: include/hw/timer/mc146818rtc* Machine core M: Eduardo Habkost -M: Marcel Apfelbaum +M: Marcel Apfelbaum S: Supported F: hw/core/machine.c F: hw/core/null-machine.c @@ -998,6 +1029,7 @@ F: hw/block/cdrom.c F: hw/block/hd-geometry.c F: tests/ide-test.c F: tests/ahci-test.c +F: tests/cdrom-test.c F: tests/libqos/ahci* T: git git://github.com/jnsnow/qemu.git ide @@ -1033,7 +1065,7 @@ F: hw/ipack/ PCI M: Michael S. Tsirkin -M: Marcel Apfelbaum +M: Marcel Apfelbaum S: Supported F: include/hw/pci/* F: hw/misc/pci-testdev.c @@ -1314,6 +1346,33 @@ S: Maintained F: include/hw/misc/unimp.h F: hw/misc/unimp.c +Standard VGA +M: Gerd Hoffmann +S: Maintained +F: hw/display/vga* +F: hw/display/bochs-display.c +F: include/hw/display/vga.h +F: include/hw/display/bochs-vbe.h + +ramfb +M: Gerd Hoffmann +S: Maintained +F: hw/display/ramfb*.c +F: include/hw/display/ramfb.h + +virtio-gpu +M: Gerd Hoffmann +S: Maintained +F: hw/display/virtio-gpu* +F: hw/display/virtio-vga.c +F: include/hw/virtio/virtio-gpu.h + +Cirrus VGA +M: Gerd Hoffmann +S: Odd Fixes +W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/ +F: hw/display/cirrus* + Subsystems ---------- Audio @@ -1339,6 +1398,8 @@ F: qemu-img* F: qemu-io* F: tests/qemu-iotests/ F: util/qemu-progress.c +F: qobject/block-qdict.c +F: test/check-block-qdict.c T: git git://repo.or.cz/qemu/kevin.git block Block I/O path @@ -1369,10 +1430,14 @@ L: qemu-block@nongnu.org S: Supported F: blockjob.c F: include/block/blockjob.h +F: job.c +F: job-qmp.c +F: include/block/job.h F: block/backup.c F: block/commit.c F: block/stream.c F: block/mirror.c +F: qapi/job.json T: git git://github.com/codyprime/qemu-kvm-jtc.git block Block QAPI, monitor, command line @@ -1573,7 +1638,8 @@ F: tests/test-*-visitor.c F: tests/test-qapi-*.c F: tests/test-qmp-*.c F: tests/test-visitor-serialization.c -F: scripts/qapi* +F: scripts/qapi-gen.py +F: scripts/qapi/* F: docs/devel/qapi* T: git git://repo.or.cz/qemu/armbru.git qapi-next @@ -1645,6 +1711,7 @@ S: Maintained F: slirp/ F: net/slirp.c F: include/net/slirp.h +T: git https://people.debian.org/~sthibault/qemu.git slirp T: git git://git.kiszka.org/qemu.git queues/slirp Stubs @@ -1656,6 +1723,8 @@ Tracing M: Stefan Hajnoczi S: Maintained F: trace/ +F: trace-events +F: qemu-option-trace.texi F: scripts/tracetool.py F: scripts/tracetool/ F: docs/devel/tracing.txt @@ -1781,6 +1850,12 @@ F: include/sysemu/replay.h F: docs/replay.txt F: stubs/replay.c +IOVA Tree +M: Peter Xu +S: Maintained +F: include/qemu/iova-tree.h +F: util/iova-tree.c + Usermode Emulation ------------------ Overall @@ -1921,6 +1996,7 @@ F: nbd/ F: include/block/nbd* F: qemu-nbd.* F: blockdev-nbd.c +F: docs/interop/nbd.txt T: git git://repo.or.cz/qemu/ericb.git nbd NFS @@ -1976,6 +2052,12 @@ S: Supported F: block/quorum.c L: qemu-block@nongnu.org +blklogwrites +M: Ari Sundholm +L: qemu-block@nongnu.org +S: Supported +F: block/blklogwrites.c + blkverify M: Stefan Hajnoczi L: qemu-block@nongnu.org @@ -2075,7 +2157,7 @@ F: docs/block-replication.txt PVRDMA M: Yuval Shaia -M: Marcel Apfelbaum +M: Marcel Apfelbaum S: Maintained F: hw/rdma/* F: hw/rdma/vmw/* @@ -2090,6 +2172,7 @@ R: Philippe Mathieu-Daudé L: qemu-devel@nongnu.org S: Maintained F: .travis.yml +F: scripts/travis/ F: .shippable.yml F: tests/docker/ F: tests/vm/ @@ -2097,6 +2180,13 @@ W: https://travis-ci.org/qemu/qemu W: https://app.shippable.com/github/qemu/qemu W: http://patchew.org/QEMU/ +Guest Test Compilation Support +M: Alex Bennée +R: Philippe Mathieu-Daudé +F: tests/tcg/Makefile +F: tests/tcg/Makefile.include +L: qemu-devel@nongnu.org + Documentation ------------- Build system architecture @@ -2104,6 +2194,10 @@ M: Daniel P. Berrange S: Odd Fixes F: docs/devel/build-system.txt +Incompatible changes +R: libvir-list@redhat.com +F: qemu-deprecated.texi + Build System ------------ GIT submodules diff --git a/Makefile b/Makefile index 00270c96b3..721ed25e1f 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,6 @@ ifneq ($(wildcard config-host.mak),) all: include config-host.mak -PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON) - git-submodule-update: .PHONY: git-submodule-update @@ -98,6 +96,7 @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c +GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c @@ -116,6 +115,7 @@ GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c +GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c @@ -133,6 +133,7 @@ GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c +GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c @@ -150,6 +151,7 @@ GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c +GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c @@ -318,6 +320,7 @@ KEYCODEMAP_FILES = \ ui/input-keymap-xorgkbd-to-qcode.c \ ui/input-keymap-xorgxquartz-to-qcode.c \ ui/input-keymap-xorgxwin-to-qcode.c \ + ui/input-keymap-osx-to-qcode.c \ $(NULL) GENERATED_FILES += $(KEYCODEMAP_FILES) @@ -347,7 +350,7 @@ $(call set-vpath, $(SRC_PATH)) LIBS+=-lz $(LIBS_TOOLS) -HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) +HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF) ifdef BUILD_DOCS DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 @@ -485,7 +488,7 @@ subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests $(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) dtc/%: .git-submodule-status - mkdir -p $@ + @mkdir -p $@ # Overriding CFLAGS causes us to lose defines added in the sub-makefile. # Not overriding CFLAGS leads to mis-matches between compilation modes. @@ -563,7 +566,6 @@ $(SRC_PATH)/scripts/qapi/types.py \ $(SRC_PATH)/scripts/qapi/visit.py \ $(SRC_PATH)/scripts/qapi/common.py \ $(SRC_PATH)/scripts/qapi/doc.py \ -$(SRC_PATH)/scripts/ordereddict.py \ $(SRC_PATH)/scripts/qapi-gen.py qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ @@ -572,7 +574,7 @@ qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \ qga/qapi-generated/qga-qapi-doc.texi: \ qga/qapi-generated/qapi-gen-timestamp ; qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) - $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o qga/qapi-generated -p "qga-" $<, \ "GEN","$(@:%-timestamp=%)") @>$@ @@ -582,6 +584,7 @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ $(SRC_PATH)/qapi/char.json \ $(SRC_PATH)/qapi/crypto.json \ $(SRC_PATH)/qapi/introspect.json \ + $(SRC_PATH)/qapi/job.json \ $(SRC_PATH)/qapi/migration.json \ $(SRC_PATH)/qapi/misc.json \ $(SRC_PATH)/qapi/net.json \ @@ -601,6 +604,7 @@ qapi/qapi-types-char.c qapi/qapi-types-char.h \ qapi/qapi-types-common.c qapi/qapi-types-common.h \ qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ +qapi/qapi-types-job.c qapi/qapi-types-job.h \ qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ qapi/qapi-types-net.c qapi/qapi-types-net.h \ @@ -619,6 +623,7 @@ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ +qapi/qapi-visit-job.c qapi/qapi-visit-job.h \ qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ @@ -636,6 +641,7 @@ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ +qapi/qapi-commands-job.c qapi/qapi-commands-job.h \ qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ @@ -653,6 +659,7 @@ qapi/qapi-events-char.c qapi/qapi-events-char.h \ qapi/qapi-events-common.c qapi/qapi-events-common.h \ qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ +qapi/qapi-events-job.c qapi/qapi-events-job.h \ qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ qapi/qapi-events-net.c qapi/qapi-events-net.h \ @@ -667,7 +674,7 @@ qapi/qapi-introspect.h qapi/qapi-introspect.c \ qapi/qapi-doc.texi: \ qapi-gen-timestamp ; qapi-gen-timestamp: $(qapi-modules) $(qapi-py) - $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o "qapi" -b $<, \ "GEN","$(@:%-timestamp=%)") @>$@ @@ -716,6 +723,14 @@ module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak $(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ "GEN","$@") +ifdef CONFIG_GCOV +.PHONY: clean-coverage +clean-coverage: + $(call quiet-command, \ + find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \ + "CLEAN", "coverage files") +endif + clean: # 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 @@ -971,6 +986,16 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \ docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi +# Reports/Analysis + +%/coverage-report.html: + @mkdir -p $* + $(call quiet-command,\ + gcovr -p --html --html-details -o $@, \ + "GEN", "coverage-report.html") + +.PHONY: coverage-report +coverage-report: $(CURDIR)/reports/coverage/coverage-report.html ifdef CONFIG_WIN32 @@ -1047,9 +1072,6 @@ endif include $(SRC_PATH)/tests/docker/Makefile.include include $(SRC_PATH)/tests/vm/Makefile.include -printgen: - @echo $(GENERATED_FILES) - .PHONY: help help: @echo 'Generic targets:' @@ -1069,6 +1091,9 @@ endif echo '') @echo 'Cleaning targets:' @echo ' clean - Remove most generated files but keep the config' +ifdef CONFIG_GCOV + @echo ' clean-coverage - Remove coverage files' +endif @echo ' distclean - Remove all generated files' @echo ' dist - Build a distributable tarball' @echo '' @@ -1080,6 +1105,9 @@ endif @echo 'Documentation targets:' @echo ' html info pdf txt' @echo ' - Build documentation in specified format' +ifdef CONFIG_GCOV + @echo ' coverage-report - Create code coverage report' +endif @echo '' ifdef CONFIG_WIN32 @echo 'Windows targets:' diff --git a/Makefile.objs b/Makefile.objs index c6c9b8fc21..7a9828da28 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -10,6 +10,7 @@ util-obj-y += qapi/qapi-types-char.o util-obj-y += qapi/qapi-types-common.o util-obj-y += qapi/qapi-types-crypto.o util-obj-y += qapi/qapi-types-introspect.o +util-obj-y += qapi/qapi-types-job.o util-obj-y += qapi/qapi-types-migration.o util-obj-y += qapi/qapi-types-misc.o util-obj-y += qapi/qapi-types-net.o @@ -28,6 +29,7 @@ util-obj-y += qapi/qapi-visit-char.o util-obj-y += qapi/qapi-visit-common.o util-obj-y += qapi/qapi-visit-crypto.o util-obj-y += qapi/qapi-visit-introspect.o +util-obj-y += qapi/qapi-visit-job.o util-obj-y += qapi/qapi-visit-migration.o util-obj-y += qapi/qapi-visit-misc.o util-obj-y += qapi/qapi-visit-net.o @@ -45,6 +47,7 @@ util-obj-y += qapi/qapi-events-char.o util-obj-y += qapi/qapi-events-common.o util-obj-y += qapi/qapi-events-crypto.o util-obj-y += qapi/qapi-events-introspect.o +util-obj-y += qapi/qapi-events-job.o util-obj-y += qapi/qapi-events-migration.o util-obj-y += qapi/qapi-events-misc.o util-obj-y += qapi/qapi-events-net.o @@ -63,7 +66,7 @@ chardev-obj-y = chardev/ # block-obj-y is code used by both qemu system emulation and qemu-img block-obj-y += nbd/ -block-obj-y += block.o blockjob.o +block-obj-y += block.o blockjob.o job.o block-obj-y += block/ scsi/ block-obj-y += qemu-io-cmds.o block-obj-$(CONFIG_REPLICATION) += replication.o @@ -94,6 +97,7 @@ io-obj-y = io/ ifeq ($(CONFIG_SOFTMMU),y) common-obj-y = blockdev.o blockdev-nbd.o block/ common-obj-y += bootdevice.o iothread.o +common-obj-y += job-qmp.o common-obj-y += net/ common-obj-y += qdev-monitor.o device-hotplug.o common-obj-$(CONFIG_WIN32) += os-win32.o @@ -140,6 +144,7 @@ common-obj-y += qapi/qapi-commands-char.o common-obj-y += qapi/qapi-commands-common.o common-obj-y += qapi/qapi-commands-crypto.o common-obj-y += qapi/qapi-commands-introspect.o +common-obj-y += qapi/qapi-commands-job.o common-obj-y += qapi/qapi-commands-migration.o common-obj-y += qapi/qapi-commands-misc.o common-obj-y += qapi/qapi-commands-net.o @@ -191,66 +196,67 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/ ###################################################################### trace-events-subdirs = -trace-events-subdirs += util -trace-events-subdirs += crypto -trace-events-subdirs += io -trace-events-subdirs += migration +trace-events-subdirs += accel/kvm +trace-events-subdirs += accel/tcg +trace-events-subdirs += audio trace-events-subdirs += block trace-events-subdirs += chardev +trace-events-subdirs += crypto +trace-events-subdirs += hw/9pfs +trace-events-subdirs += hw/acpi +trace-events-subdirs += hw/alpha +trace-events-subdirs += hw/arm +trace-events-subdirs += hw/audio trace-events-subdirs += hw/block trace-events-subdirs += hw/block/dataplane trace-events-subdirs += hw/char -trace-events-subdirs += hw/intc -trace-events-subdirs += hw/net -trace-events-subdirs += hw/rdma -trace-events-subdirs += hw/rdma/vmw -trace-events-subdirs += hw/virtio -trace-events-subdirs += hw/audio -trace-events-subdirs += hw/misc -trace-events-subdirs += hw/misc/macio -trace-events-subdirs += hw/usb -trace-events-subdirs += hw/scsi -trace-events-subdirs += hw/nvram trace-events-subdirs += hw/display -trace-events-subdirs += hw/input -trace-events-subdirs += hw/timer trace-events-subdirs += hw/dma -trace-events-subdirs += hw/sparc -trace-events-subdirs += hw/sparc64 -trace-events-subdirs += hw/sd -trace-events-subdirs += hw/isa -trace-events-subdirs += hw/mem +trace-events-subdirs += hw/hppa +trace-events-subdirs += hw/i2c trace-events-subdirs += hw/i386 trace-events-subdirs += hw/i386/xen -trace-events-subdirs += hw/9pfs -trace-events-subdirs += hw/ppc +trace-events-subdirs += hw/ide +trace-events-subdirs += hw/input +trace-events-subdirs += hw/intc +trace-events-subdirs += hw/isa +trace-events-subdirs += hw/mem +trace-events-subdirs += hw/misc +trace-events-subdirs += hw/misc/macio +trace-events-subdirs += hw/net +trace-events-subdirs += hw/nvram trace-events-subdirs += hw/pci trace-events-subdirs += hw/pci-host +trace-events-subdirs += hw/ppc +trace-events-subdirs += hw/rdma +trace-events-subdirs += hw/rdma/vmw trace-events-subdirs += hw/s390x -trace-events-subdirs += hw/vfio -trace-events-subdirs += hw/acpi -trace-events-subdirs += hw/arm -trace-events-subdirs += hw/alpha -trace-events-subdirs += hw/hppa -trace-events-subdirs += hw/xen -trace-events-subdirs += hw/ide +trace-events-subdirs += hw/scsi +trace-events-subdirs += hw/sd +trace-events-subdirs += hw/sparc +trace-events-subdirs += hw/sparc64 +trace-events-subdirs += hw/timer trace-events-subdirs += hw/tpm -trace-events-subdirs += ui -trace-events-subdirs += audio +trace-events-subdirs += hw/usb +trace-events-subdirs += hw/vfio +trace-events-subdirs += hw/virtio +trace-events-subdirs += hw/xen +trace-events-subdirs += io +trace-events-subdirs += linux-user +trace-events-subdirs += migration +trace-events-subdirs += nbd trace-events-subdirs += net +trace-events-subdirs += qapi +trace-events-subdirs += qom +trace-events-subdirs += scsi trace-events-subdirs += target/arm trace-events-subdirs += target/i386 trace-events-subdirs += target/mips -trace-events-subdirs += target/sparc -trace-events-subdirs += target/s390x trace-events-subdirs += target/ppc -trace-events-subdirs += qom -trace-events-subdirs += linux-user -trace-events-subdirs += qapi -trace-events-subdirs += accel/tcg -trace-events-subdirs += accel/kvm -trace-events-subdirs += nbd -trace-events-subdirs += scsi +trace-events-subdirs += target/s390x +trace-events-subdirs += target/sparc +trace-events-subdirs += ui +trace-events-subdirs += util trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) diff --git a/Makefile.target b/Makefile.target index d0ec77a307..4d56298bbf 100644 --- a/Makefile.target +++ b/Makefile.target @@ -36,6 +36,11 @@ endif PROGS=$(QEMU_PROG) $(QEMU_PROGW) STPFILES= +# Makefile Tests +ifdef CONFIG_USER_ONLY +include $(SRC_PATH)/tests/tcg/Makefile.include +endif + config-target.h: config-target.h-timestamp config-target.h-timestamp: config-target.mak @@ -97,7 +102,7 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o -obj-y += fpu/softfloat.o +obj-$(CONFIG_TCG) += fpu/softfloat.o obj-y += target/$(TARGET_BASE_ARCH)/ obj-y += disas.o obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o @@ -138,6 +143,7 @@ obj-y += hw/ obj-y += memory.o obj-y += memory_mapping.o obj-y += dump.o +obj-$(TARGET_X86_64) += win_dump.o obj-y += migration/ram.o LIBS := $(libs_softmmu) $(LIBS) diff --git a/VERSION b/VERSION index d8b698973a..4a36342fca 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.12.0 +3.0.0 diff --git a/accel/accel.c b/accel/accel.c index 93e2434c87..966b2d8f53 100644 --- a/accel/accel.c +++ b/accel/accel.c @@ -70,8 +70,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) void configure_accelerator(MachineState *ms) { - const char *accel, *p; - char buf[10]; + const char *accel; + char **accel_list, **tmp; int ret; bool accel_initialised = false; bool init_failed = false; @@ -83,13 +83,10 @@ void configure_accelerator(MachineState *ms) accel = "tcg"; } - p = accel; - while (!accel_initialised && *p != '\0') { - if (*p == ':') { - p++; - } - p = get_opt_name(buf, sizeof(buf), p, ':'); - acc = accel_find(buf); + accel_list = g_strsplit(accel, ":", 0); + + for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { + acc = accel_find(*tmp); if (!acc) { continue; } @@ -107,6 +104,7 @@ void configure_accelerator(MachineState *ms) accel_initialised = true; } } + g_strfreev(accel_list); if (!accel_initialised) { if (!init_failed) { @@ -126,6 +124,15 @@ void accel_register_compat_props(AccelState *accel) register_compat_props_array(class->global_props); } +void accel_setup_post(MachineState *ms) +{ + AccelState *accel = ms->accelerator; + AccelClass *acc = ACCEL_GET_CLASS(accel); + if (acc->setup_post) { + acc->setup_post(ms, accel); + } +} + static void register_accel_types(void) { type_register_static(&accel_type); diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index ffee68e603..eb7db92a5e 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -256,7 +256,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram, return 0; } -static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) +static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new) { KVMState *s = kvm_state; struct kvm_userspace_memory_region mem; @@ -267,7 +267,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) mem.userspace_addr = (unsigned long)slot->ram; mem.flags = slot->flags; - if (slot->memory_size && mem.flags & KVM_MEM_READONLY) { + if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) { /* Set the slot size to 0 before setting the slot to the desired * value. This is needed based on KVM commit 75d61fbc. */ mem.memory_size = 0; @@ -275,6 +275,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) } mem.memory_size = slot->memory_size; ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); + slot->old_flags = mem.flags; trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, mem.memory_size, mem.userspace_addr, ret); return ret; @@ -391,17 +392,14 @@ static int kvm_mem_flags(MemoryRegion *mr) static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, MemoryRegion *mr) { - int old_flags; - - old_flags = mem->flags; mem->flags = kvm_mem_flags(mr); /* If nothing changed effectively, no need to issue ioctl */ - if (mem->flags == old_flags) { + if (mem->flags == mem->old_flags) { return 0; } - return kvm_set_user_memory_region(kml, mem); + return kvm_set_user_memory_region(kml, mem, false); } static int kvm_section_update_flags(KVMMemoryListener *kml, @@ -755,7 +753,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, /* unregister the slot */ mem->memory_size = 0; - err = kvm_set_user_memory_region(kml, mem); + mem->flags = 0; + err = kvm_set_user_memory_region(kml, mem, false); if (err) { fprintf(stderr, "%s: error unregistering slot: %s\n", __func__, strerror(-err)); @@ -771,7 +770,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml, mem->ram = ram; mem->flags = kvm_mem_flags(mr); - err = kvm_set_user_memory_region(kml, mem); + err = kvm_set_user_memory_region(kml, mem, true); if (err) { fprintf(stderr, "%s: error registering slot: %s\n", __func__, strerror(-err)); diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index ee575a8718..76ae461749 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -21,10 +21,6 @@ void tb_flush(CPUState *cpu) { } -void tb_unlock(void) -{ -} - void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) { } diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index e022df4571..d751bcba48 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -18,26 +18,37 @@ * License along with this library; if not, see . */ +#include "trace/mem.h" + #if DATA_SIZE == 16 # define SUFFIX o # define DATA_TYPE Int128 # define BSWAP bswap128 +# define SHIFT 4 #elif DATA_SIZE == 8 # define SUFFIX q # define DATA_TYPE uint64_t +# define SDATA_TYPE int64_t # define BSWAP bswap64 +# define SHIFT 3 #elif DATA_SIZE == 4 # define SUFFIX l # define DATA_TYPE uint32_t +# define SDATA_TYPE int32_t # define BSWAP bswap32 +# define SHIFT 2 #elif DATA_SIZE == 2 # define SUFFIX w # define DATA_TYPE uint16_t +# define SDATA_TYPE int16_t # define BSWAP bswap16 +# define SHIFT 1 #elif DATA_SIZE == 1 # define SUFFIX b # define DATA_TYPE uint8_t +# define SDATA_TYPE int8_t # define BSWAP +# define SHIFT 0 #else # error unsupported data size #endif @@ -48,14 +59,37 @@ # define ABI_TYPE uint32_t #endif +#define ATOMIC_TRACE_RMW do { \ + uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ + \ + trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \ + trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, \ + info | TRACE_MEM_ST); \ + } while (0) + +#define ATOMIC_TRACE_LD do { \ + uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \ + \ + trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \ + } while (0) + +# define ATOMIC_TRACE_ST do { \ + uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \ + \ + trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \ + } while (0) + /* Define host-endian atomic operations. Note that END is used within the ATOMIC_NAME macro, and redefined below. */ #if DATA_SIZE == 1 # define END +# define MEND _be /* either le or be would be fine */ #elif defined(HOST_WORDS_BIGENDIAN) # define END _be +# define MEND _be #else # define END _le +# define MEND _le #endif ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, @@ -63,7 +97,10 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); + DATA_TYPE ret; + + ATOMIC_TRACE_RMW; + ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); ATOMIC_MMU_CLEANUP; return ret; } @@ -73,6 +110,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) { ATOMIC_MMU_DECLS; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; + + ATOMIC_TRACE_LD; __atomic_load(haddr, &val, __ATOMIC_RELAXED); ATOMIC_MMU_CLEANUP; return val; @@ -83,6 +122,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + + ATOMIC_TRACE_ST; __atomic_store(haddr, &val, __ATOMIC_RELAXED); ATOMIC_MMU_CLEANUP; } @@ -92,7 +133,10 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ret = atomic_xchg__nocheck(haddr, val); + DATA_TYPE ret; + + ATOMIC_TRACE_RMW; + ret = atomic_xchg__nocheck(haddr, val); ATOMIC_MMU_CLEANUP; return ret; } @@ -103,7 +147,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ { \ ATOMIC_MMU_DECLS; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ - DATA_TYPE ret = atomic_##X(haddr, val); \ + DATA_TYPE ret; \ + \ + ATOMIC_TRACE_RMW; \ + ret = atomic_##X(haddr, val); \ ATOMIC_MMU_CLEANUP; \ return ret; \ } @@ -118,9 +165,48 @@ GEN_ATOMIC_HELPER(or_fetch) GEN_ATOMIC_HELPER(xor_fetch) #undef GEN_ATOMIC_HELPER + +/* These helpers are, as a whole, full barriers. Within the helper, + * the leading barrier is explicit and the trailing barrier is within + * cmpxchg primitive. + * + * Trace this load + RMW loop as a single RMW op. This way, regardless + * of CF_PARALLEL's value, we'll trace just a read and a write. + */ +#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ + ABI_TYPE xval EXTRA_ARGS) \ +{ \ + ATOMIC_MMU_DECLS; \ + XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ + XDATA_TYPE cmp, old, new, val = xval; \ + \ + ATOMIC_TRACE_RMW; \ + smp_mb(); \ + cmp = atomic_read__nocheck(haddr); \ + do { \ + old = cmp; new = FN(old, val); \ + cmp = atomic_cmpxchg__nocheck(haddr, old, new); \ + } while (cmp != old); \ + ATOMIC_MMU_CLEANUP; \ + return RET; \ +} + +GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old) + +GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) + +#undef GEN_ATOMIC_HELPER_FN #endif /* DATA SIZE >= 16 */ #undef END +#undef MEND #if DATA_SIZE > 1 @@ -128,8 +214,10 @@ GEN_ATOMIC_HELPER(xor_fetch) within the ATOMIC_NAME macro. */ #ifdef HOST_WORDS_BIGENDIAN # define END _le +# define MEND _le #else # define END _be +# define MEND _be #endif ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, @@ -137,7 +225,10 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); + DATA_TYPE ret; + + ATOMIC_TRACE_RMW; + ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); ATOMIC_MMU_CLEANUP; return BSWAP(ret); } @@ -147,6 +238,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) { ATOMIC_MMU_DECLS; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; + + ATOMIC_TRACE_LD; __atomic_load(haddr, &val, __ATOMIC_RELAXED); ATOMIC_MMU_CLEANUP; return BSWAP(val); @@ -157,6 +250,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; + + ATOMIC_TRACE_ST; val = BSWAP(val); __atomic_store(haddr, &val, __ATOMIC_RELAXED); ATOMIC_MMU_CLEANUP; @@ -167,7 +262,10 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, { ATOMIC_MMU_DECLS; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val)); + ABI_TYPE ret; + + ATOMIC_TRACE_RMW; + ret = atomic_xchg__nocheck(haddr, BSWAP(val)); ATOMIC_MMU_CLEANUP; return BSWAP(ret); } @@ -178,7 +276,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ { \ ATOMIC_MMU_DECLS; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ - DATA_TYPE ret = atomic_##X(haddr, BSWAP(val)); \ + DATA_TYPE ret; \ + \ + ATOMIC_TRACE_RMW; \ + ret = atomic_##X(haddr, BSWAP(val)); \ ATOMIC_MMU_CLEANUP; \ return BSWAP(ret); \ } @@ -192,54 +293,64 @@ GEN_ATOMIC_HELPER(xor_fetch) #undef GEN_ATOMIC_HELPER +/* These helpers are, as a whole, full barriers. Within the helper, + * the leading barrier is explicit and the trailing barrier is within + * cmpxchg primitive. + * + * Trace this load + RMW loop as a single RMW op. This way, regardless + * of CF_PARALLEL's value, we'll trace just a read and a write. + */ +#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ + ABI_TYPE xval EXTRA_ARGS) \ +{ \ + ATOMIC_MMU_DECLS; \ + XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ + XDATA_TYPE ldo, ldn, old, new, val = xval; \ + \ + ATOMIC_TRACE_RMW; \ + smp_mb(); \ + ldn = atomic_read__nocheck(haddr); \ + do { \ + ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \ + ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \ + } while (ldo != ldn); \ + ATOMIC_MMU_CLEANUP; \ + return RET; \ +} + +GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old) + +GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) +GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) + /* Note that for addition, we need to use a separate cmpxchg loop instead of bswaps for the reverse-host-endian helpers. */ -ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, - ABI_TYPE val EXTRA_ARGS) -{ - ATOMIC_MMU_DECLS; - DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ldo, ldn, ret, sto; +#define ADD(X, Y) (X + Y) +GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old) +GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) +#undef ADD - ldo = atomic_read__nocheck(haddr); - while (1) { - ret = BSWAP(ldo); - sto = BSWAP(ret + val); - ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); - if (ldn == ldo) { - ATOMIC_MMU_CLEANUP; - return ret; - } - ldo = ldn; - } -} - -ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr, - ABI_TYPE val EXTRA_ARGS) -{ - ATOMIC_MMU_DECLS; - DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; - DATA_TYPE ldo, ldn, ret, sto; - - ldo = atomic_read__nocheck(haddr); - while (1) { - ret = BSWAP(ldo) + val; - sto = BSWAP(ret); - ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto); - if (ldn == ldo) { - ATOMIC_MMU_CLEANUP; - return ret; - } - ldo = ldn; - } -} +#undef GEN_ATOMIC_HELPER_FN #endif /* DATA_SIZE >= 16 */ #undef END +#undef MEND #endif /* DATA_SIZE > 1 */ +#undef ATOMIC_TRACE_ST +#undef ATOMIC_TRACE_LD +#undef ATOMIC_TRACE_RMW + #undef BSWAP #undef ABI_TYPE #undef DATA_TYPE +#undef SDATA_TYPE #undef SUFFIX #undef DATA_SIZE +#undef SHIFT diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 81153e7a13..c738b7f7d6 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -25,7 +25,6 @@ #include "qemu/atomic.h" #include "sysemu/qtest.h" #include "qemu/timer.h" -#include "exec/address-spaces.h" #include "qemu/rcu.h" #include "exec/tb-hash.h" #include "exec/tb-lookup.h" @@ -156,11 +155,14 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb) if (qemu_loglevel_mask(CPU_LOG_TB_CPU) && qemu_log_in_addr_range(itb->pc)) { qemu_log_lock(); + int flags = 0; + if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { + flags |= CPU_DUMP_FPU; + } #if defined(TARGET_I386) - log_cpu_state(cpu, CPU_DUMP_CCOP); -#else - log_cpu_state(cpu, 0); + flags |= CPU_DUMP_CCOP; #endif + log_cpu_state(cpu, flags); qemu_log_unlock(); } #endif /* DEBUG_DISAS */ @@ -210,20 +212,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles, We only end up here when an existing TB is too long. */ cflags |= MIN(max_cycles, CF_COUNT_MASK); - tb_lock(); + mmap_lock(); tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, orig_tb->flags, cflags); tb->orig_tb = orig_tb; - tb_unlock(); + mmap_unlock(); /* execute the generated code */ trace_exec_tb_nocache(tb, tb->pc); cpu_tb_exec(cpu, tb); - tb_lock(); + mmap_lock(); tb_phys_invalidate(tb, -1); - tb_remove(tb); - tb_unlock(); + mmap_unlock(); + tcg_tb_remove(tb); } #endif @@ -242,12 +244,7 @@ void cpu_exec_step_atomic(CPUState *cpu) tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); if (tb == NULL) { mmap_lock(); - tb_lock(); - tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); - if (likely(tb == NULL)) { - tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); - } - tb_unlock(); + tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); mmap_unlock(); } @@ -262,15 +259,14 @@ void cpu_exec_step_atomic(CPUState *cpu) cpu_tb_exec(cpu, tb); cc->cpu_exec_exit(cpu); } else { - /* We may have exited due to another problem here, so we need - * to reset any tb_locks we may have taken but didn't release. + /* * The mmap_lock is dropped by tb_gen_code if it runs out of * memory. */ #ifndef CONFIG_SOFTMMU tcg_debug_assert(!have_mmap_lock()); #endif - tb_lock_reset(); + assert_no_pages_locked(); } if (in_exclusive_region) { @@ -293,7 +289,7 @@ struct tb_desc { uint32_t trace_vcpu_dstate; }; -static bool tb_cmp(const void *p, const void *d) +static bool tb_lookup_cmp(const void *p, const void *d) { const TranslationBlock *tb = p; const struct tb_desc *desc = d; @@ -338,7 +334,7 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, phys_pc = get_page_addr_code(desc.env, pc); desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); - return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h); + return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); } void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) @@ -352,28 +348,43 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) } } -/* Called with tb_lock held. */ static inline void tb_add_jump(TranslationBlock *tb, int n, TranslationBlock *tb_next) { + uintptr_t old; + assert(n < ARRAY_SIZE(tb->jmp_list_next)); - if (tb->jmp_list_next[n]) { - /* Another thread has already done this while we were - * outside of the lock; nothing to do in this case */ - return; + qemu_spin_lock(&tb_next->jmp_lock); + + /* make sure the destination TB is valid */ + if (tb_next->cflags & CF_INVALID) { + goto out_unlock_next; } + /* Atomically claim the jump destination slot only if it was NULL */ + old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next); + if (old) { + goto out_unlock_next; + } + + /* patch the native jump address */ + tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); + + /* add in TB jmp list */ + tb->jmp_list_next[n] = tb_next->jmp_list_head; + tb_next->jmp_list_head = (uintptr_t)tb | n; + + qemu_spin_unlock(&tb_next->jmp_lock); + qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, "Linking TBs %p [" TARGET_FMT_lx "] index %d -> %p [" TARGET_FMT_lx "]\n", tb->tc.ptr, tb->pc, n, tb_next->tc.ptr, tb_next->pc); + return; - /* patch the native jump address */ - tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); - - /* add in TB jmp circular list */ - tb->jmp_list_next[n] = tb_next->jmp_list_first; - tb_next->jmp_list_first = (uintptr_t)tb | n; + out_unlock_next: + qemu_spin_unlock(&tb_next->jmp_lock); + return; } static inline TranslationBlock *tb_find(CPUState *cpu, @@ -383,27 +394,11 @@ static inline TranslationBlock *tb_find(CPUState *cpu, TranslationBlock *tb; target_ulong cs_base, pc; uint32_t flags; - bool acquired_tb_lock = false; tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); if (tb == NULL) { - /* mmap_lock is needed by tb_gen_code, and mmap_lock must be - * taken outside tb_lock. As system emulation is currently - * single threaded the locks are NOPs. - */ mmap_lock(); - tb_lock(); - acquired_tb_lock = true; - - /* There's a chance that our desired tb has been translated while - * taking the locks so we check again inside the lock. - */ - tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask); - if (likely(tb == NULL)) { - /* if no translated code available, then translate it now */ - tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); - } - + tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask); mmap_unlock(); /* We add the TB in the virtual pc hash table for the fast lookup */ atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); @@ -419,16 +414,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu, #endif /* See if we can patch the calling TB. */ if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { - if (!acquired_tb_lock) { - tb_lock(); - acquired_tb_lock = true; - } - if (!(tb->cflags & CF_INVALID)) { - tb_add_jump(last_tb, tb_exit, tb); - } - } - if (acquired_tb_lock) { - tb_unlock(); + tb_add_jump(last_tb, tb_exit, tb); } return tb; } @@ -704,7 +690,9 @@ int cpu_exec(CPUState *cpu) g_assert(cpu == current_cpu); g_assert(cc == CPU_GET_CLASS(cpu)); #endif /* buggy compiler */ - tb_lock_reset(); +#ifndef CONFIG_SOFTMMU + tcg_debug_assert(!have_mmap_lock()); +#endif if (qemu_mutex_iothread_locked()) { qemu_mutex_unlock_iothread(); } diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 05439039e9..563fa30117 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -125,8 +125,6 @@ static void tlb_flush_nocheck(CPUState *cpu) atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1); tlb_debug("(count: %zu)\n", tlb_flush_count()); - tb_lock(); - memset(env->tlb_table, -1, sizeof(env->tlb_table)); memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table)); cpu_tb_jmp_cache_clear(cpu); @@ -135,8 +133,6 @@ static void tlb_flush_nocheck(CPUState *cpu) env->tlb_flush_addr = -1; env->tlb_flush_mask = 0; - tb_unlock(); - atomic_mb_set(&cpu->pending_tlb_flush, 0); } @@ -180,8 +176,6 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) assert_cpu_is_self(cpu); - tb_lock(); - tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { @@ -197,8 +191,6 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) cpu_tb_jmp_cache_clear(cpu); tlb_debug("done\n"); - - tb_unlock(); } void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) @@ -243,20 +235,30 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); } - - -static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr) +static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry, + target_ulong page) { - if (addr == (tlb_entry->addr_read & - (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || - addr == (tlb_entry->addr_write & - (TARGET_PAGE_MASK | TLB_INVALID_MASK)) || - addr == (tlb_entry->addr_code & - (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + return tlb_hit_page(tlb_entry->addr_read, page) || + tlb_hit_page(tlb_entry->addr_write, page) || + tlb_hit_page(tlb_entry->addr_code, page); +} + +static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong page) +{ + if (tlb_hit_page_anyprot(tlb_entry, page)) { memset(tlb_entry, -1, sizeof(*tlb_entry)); } } +static inline void tlb_flush_vtlb_page(CPUArchState *env, int mmu_idx, + target_ulong page) +{ + int k; + for (k = 0; k < CPU_VTLB_SIZE; k++) { + tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], page); + } +} + static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data) { CPUArchState *env = cpu->env_ptr; @@ -282,14 +284,7 @@ static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data) i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); - } - - /* check whether there are entries that need to be flushed in the vtlb */ - for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { - int k; - for (k = 0; k < CPU_VTLB_SIZE; k++) { - tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr); - } + tlb_flush_vtlb_page(env, mmu_idx, addr); } tb_flush_jmp_cache(cpu, addr); @@ -321,7 +316,6 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); int mmu_idx; - int i; assert_cpu_is_self(cpu); @@ -331,11 +325,7 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { if (test_bit(mmu_idx, &mmu_idx_bitmap)) { tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr); - - /* check whether there are vltb entries that need to be flushed */ - for (i = 0; i < CPU_VTLB_SIZE; i++) { - tlb_flush_entry(&env->tlb_v_table[mmu_idx][i], addr); - } + tlb_flush_vtlb_page(env, mmu_idx, addr); } } @@ -620,27 +610,42 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, target_ulong address; target_ulong code_address; uintptr_t addend; - CPUTLBEntry *te, *tv, tn; - hwaddr iotlb, xlat, sz; - unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; + CPUTLBEntry *te, tn; + hwaddr iotlb, xlat, sz, paddr_page; + target_ulong vaddr_page; int asidx = cpu_asidx_from_attrs(cpu, attrs); assert_cpu_is_self(cpu); - assert(size >= TARGET_PAGE_SIZE); - if (size != TARGET_PAGE_SIZE) { - tlb_add_large_page(env, vaddr, size); - } - sz = size; - section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz); + if (size < TARGET_PAGE_SIZE) { + sz = TARGET_PAGE_SIZE; + } else { + if (size > TARGET_PAGE_SIZE) { + tlb_add_large_page(env, vaddr, size); + } + sz = size; + } + vaddr_page = vaddr & TARGET_PAGE_MASK; + paddr_page = paddr & TARGET_PAGE_MASK; + + section = address_space_translate_for_iotlb(cpu, asidx, paddr_page, + &xlat, &sz, attrs, &prot); assert(sz >= TARGET_PAGE_SIZE); tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx " prot=%x idx=%d\n", vaddr, paddr, prot, mmu_idx); - address = vaddr; - if (!memory_region_is_ram(section->mr) && !memory_region_is_romd(section->mr)) { + address = vaddr_page; + if (size < TARGET_PAGE_SIZE) { + /* + * Slow-path the TLB entries; we will repeat the MMU check and TLB + * fill on every access. + */ + address |= TLB_RECHECK; + } + if (!memory_region_is_ram(section->mr) && + !memory_region_is_romd(section->mr)) { /* IO memory case */ address |= TLB_MMIO; addend = 0; @@ -649,26 +654,47 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat; } + /* Make sure there's no cached translation for the new page. */ + tlb_flush_vtlb_page(env, mmu_idx, vaddr_page); + code_address = address; - iotlb = memory_region_section_get_iotlb(cpu, section, vaddr, paddr, xlat, - prot, &address); + iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page, + paddr_page, xlat, prot, &address); - index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + index = (vaddr_page >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); te = &env->tlb_table[mmu_idx][index]; - /* do not discard the translation in te, evict it into a victim tlb */ - tv = &env->tlb_v_table[mmu_idx][vidx]; - /* addr_write can race with tlb_reset_dirty_range */ - copy_tlb_helper(tv, te, true); + /* + * Only evict the old entry to the victim tlb if it's for a + * different page; otherwise just overwrite the stale data. + */ + if (!tlb_hit_page_anyprot(te, vaddr_page)) { + unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; + CPUTLBEntry *tv = &env->tlb_v_table[mmu_idx][vidx]; - env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; + /* Evict the old entry into the victim tlb. */ + copy_tlb_helper(tv, te, true); + env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; + } /* refill the tlb */ - env->iotlb[mmu_idx][index].addr = iotlb - vaddr; + /* + * At this point iotlb contains a physical section number in the lower + * TARGET_PAGE_BITS, and either + * + the ram_addr_t of the page base of the target RAM (if NOTDIRTY or ROM) + * + the offset within section->mr of the page base (otherwise) + * We subtract the vaddr_page (which is page aligned and thus won't + * disturb the low bits) to give an offset which can be added to the + * (non-page-aligned) vaddr of the eventual memory access to get + * the MemoryRegion offset for the access. Note that the vaddr we + * subtract here is that of the page base, and not the same as the + * vaddr we add back in io_readx()/io_writex()/get_page_addr_code(). + */ + env->iotlb[mmu_idx][index].addr = iotlb - vaddr_page; env->iotlb[mmu_idx][index].attrs = attrs; /* Now calculate the new entry */ - tn.addend = addend - vaddr; + tn.addend = addend - vaddr_page; if (prot & PAGE_READ) { tn.addr_read = address; } else { @@ -689,7 +715,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, tn.addr_write = address | TLB_MMIO; } else if (memory_region_is_ram(section->mr) && cpu_physical_memory_is_clean( - memory_region_get_ram_addr(section->mr) + xlat)) { + memory_region_get_ram_addr(section->mr) + xlat)) { tn.addr_write = address | TLB_NOTDIRTY; } else { tn.addr_write = address; @@ -762,16 +788,43 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, int mmu_idx, - target_ulong addr, uintptr_t retaddr, int size) + target_ulong addr, uintptr_t retaddr, + bool recheck, int size) { CPUState *cpu = ENV_GET_CPU(env); - hwaddr physaddr = iotlbentry->addr; - MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); + hwaddr mr_offset; + MemoryRegionSection *section; + MemoryRegion *mr; uint64_t val; bool locked = false; MemTxResult r; - physaddr = (physaddr & TARGET_PAGE_MASK) + addr; + if (recheck) { + /* + * This is a TLB_RECHECK access, where the MMU protection + * covers a smaller range than a target page, and we must + * repeat the MMU check here. This tlb_fill() call might + * longjump out if this access should cause a guest exception. + */ + int index; + target_ulong tlb_addr; + + tlb_fill(cpu, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addr = env->tlb_table[mmu_idx][index].addr_read; + if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { + /* RAM access */ + uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend; + + return ldn_p((void *)haddr, size); + } + /* Fall through for handling IO accesses */ + } + + section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); + mr = section->mr; + mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; cpu->mem_io_pc = retaddr; if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { cpu_io_recompile(cpu, retaddr); @@ -783,9 +836,13 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, qemu_mutex_lock_iothread(); locked = true; } - r = memory_region_dispatch_read(mr, physaddr, + r = memory_region_dispatch_read(mr, mr_offset, &val, size, iotlbentry->attrs); if (r != MEMTX_OK) { + hwaddr physaddr = mr_offset + + section->offset_within_address_space - + section->offset_within_region; + cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_LOAD, mmu_idx, iotlbentry->attrs, r, retaddr); } @@ -799,15 +856,42 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, int mmu_idx, uint64_t val, target_ulong addr, - uintptr_t retaddr, int size) + uintptr_t retaddr, bool recheck, int size) { CPUState *cpu = ENV_GET_CPU(env); - hwaddr physaddr = iotlbentry->addr; - MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); + hwaddr mr_offset; + MemoryRegionSection *section; + MemoryRegion *mr; bool locked = false; MemTxResult r; - physaddr = (physaddr & TARGET_PAGE_MASK) + addr; + if (recheck) { + /* + * This is a TLB_RECHECK access, where the MMU protection + * covers a smaller range than a target page, and we must + * repeat the MMU check here. This tlb_fill() call might + * longjump out if this access should cause a guest exception. + */ + int index; + target_ulong tlb_addr; + + tlb_fill(cpu, addr, size, MMU_DATA_STORE, mmu_idx, retaddr); + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { + /* RAM access */ + uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend; + + stn_p((void *)haddr, size, val); + return; + } + /* Fall through for handling IO accesses */ + } + + section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); + mr = section->mr; + mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { cpu_io_recompile(cpu, retaddr); } @@ -818,9 +902,13 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, qemu_mutex_lock_iothread(); locked = true; } - r = memory_region_dispatch_write(mr, physaddr, + r = memory_region_dispatch_write(mr, mr_offset, val, size, iotlbentry->attrs); if (r != MEMTX_OK) { + hwaddr physaddr = mr_offset + + section->offset_within_address_space - + section->offset_within_region; + cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_STORE, mmu_idx, iotlbentry->attrs, r, retaddr); } @@ -868,24 +956,51 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, */ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) { - int mmu_idx, index, pd; + int mmu_idx, index; void *p; MemoryRegion *mr; + MemoryRegionSection *section; CPUState *cpu = ENV_GET_CPU(env); CPUIOTLBEntry *iotlbentry; - hwaddr physaddr; + hwaddr physaddr, mr_offset; index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); mmu_idx = cpu_mmu_index(env, true); - if (unlikely(env->tlb_table[mmu_idx][index].addr_code != - (addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)))) { - if (!VICTIM_TLB_HIT(addr_read, addr)) { + if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) { + if (!VICTIM_TLB_HIT(addr_code, addr)) { tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0); } + assert(tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr)); } + + if (unlikely(env->tlb_table[mmu_idx][index].addr_code & TLB_RECHECK)) { + /* + * This is a TLB_RECHECK access, where the MMU protection + * covers a smaller range than a target page, and we must + * repeat the MMU check here. This tlb_fill() call might + * longjump out if this access should cause a guest exception. + */ + int index; + target_ulong tlb_addr; + + tlb_fill(cpu, addr, 0, MMU_INST_FETCH, mmu_idx, 0); + + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addr = env->tlb_table[mmu_idx][index].addr_code; + if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { + /* RAM access. We can't handle this, so for now just stop */ + cpu_abort(cpu, "Unable to handle guest executing from RAM within " + "a small MPU region at 0x" TARGET_FMT_lx, addr); + } + /* + * Fall through to handle IO accesses (which will almost certainly + * also result in failure) + */ + } + iotlbentry = &env->iotlb[mmu_idx][index]; - pd = iotlbentry->addr & ~TARGET_PAGE_MASK; - mr = iotlb_to_region(cpu, pd, iotlbentry->attrs); + section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); + mr = section->mr; if (memory_region_is_unassigned(mr)) { qemu_mutex_lock_iothread(); if (memory_region_request_mmio_ptr(mr, addr)) { @@ -906,7 +1021,10 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) * and use the MemTXResult it produced). However it is the * simplest place we have currently available for the check. */ - physaddr = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; + mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; + physaddr = mr_offset + + section->offset_within_address_space - + section->offset_within_region; cpu_transaction_failed(cpu, physaddr, addr, 0, MMU_INST_FETCH, mmu_idx, iotlbentry->attrs, MEMTX_DECODE_ERROR, 0); @@ -934,8 +1052,7 @@ void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { /* TLB entry is for a different page */ if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE, @@ -979,8 +1096,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, } /* Check TLB entry and enforce page permissions. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, 1 << s_bits, MMU_DATA_STORE, mmu_idx, retaddr); @@ -988,8 +1104,8 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, tlb_addr = tlbe->addr_write & ~TLB_INVALID_MASK; } - /* Notice an IO access */ - if (unlikely(tlb_addr & TLB_MMIO)) { + /* Notice an IO access or a needs-MMU-lookup access */ + if (unlikely(tlb_addr & (TLB_MMIO | TLB_RECHECK))) { /* There's really nothing that can be done to support this apart from stop-the-world. */ goto stop_the_world; diff --git a/accel/tcg/softmmu_template.h b/accel/tcg/softmmu_template.h index 239ea6692b..badbf14880 100644 --- a/accel/tcg/softmmu_template.h +++ b/accel/tcg/softmmu_template.h @@ -98,10 +98,12 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, size_t mmu_idx, size_t index, target_ulong addr, - uintptr_t retaddr) + uintptr_t retaddr, + bool recheck) { CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; - return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE); + return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck, + DATA_SIZE); } #endif @@ -121,8 +123,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, } /* If the TLB entry is for a different page, reload and try again. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, mmu_idx, retaddr); @@ -138,7 +139,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ - res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); + res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, + tlb_addr & TLB_RECHECK); res = TGT_LE(res); return res; } @@ -188,8 +190,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, } /* If the TLB entry is for a different page, reload and try again. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, mmu_idx, retaddr); @@ -205,7 +206,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ - res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); + res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, + tlb_addr & TLB_RECHECK); res = TGT_BE(res); return res; } @@ -259,10 +261,12 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env, size_t mmu_idx, size_t index, DATA_TYPE val, target_ulong addr, - uintptr_t retaddr) + uintptr_t retaddr, + bool recheck) { CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; - return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE); + return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, + recheck, DATA_SIZE); } void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, @@ -280,8 +284,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } /* If the TLB entry is for a different page, reload and try again. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); @@ -298,7 +301,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ val = TGT_LE(val); - glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); + glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, + retaddr, tlb_addr & TLB_RECHECK); return; } @@ -315,7 +319,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; - if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) + if (!tlb_hit_page(tlb_addr2, page2) && !VICTIM_TLB_HIT(addr_write, page2)) { tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); @@ -357,8 +361,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, } /* If the TLB entry is for a different page, reload and try again. */ - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); @@ -375,7 +378,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ val = TGT_BE(val); - glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); + glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr, + tlb_addr & TLB_RECHECK); return; } @@ -392,7 +396,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; - if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) + if (!tlb_hit_page(tlb_addr2, page2) && !VICTIM_TLB_HIT(addr_write, page2)) { tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 2536959a18..1bd39d136d 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -125,11 +125,19 @@ GEN_ATOMIC_HELPERS(fetch_add) GEN_ATOMIC_HELPERS(fetch_and) GEN_ATOMIC_HELPERS(fetch_or) GEN_ATOMIC_HELPERS(fetch_xor) +GEN_ATOMIC_HELPERS(fetch_smin) +GEN_ATOMIC_HELPERS(fetch_umin) +GEN_ATOMIC_HELPERS(fetch_smax) +GEN_ATOMIC_HELPERS(fetch_umax) GEN_ATOMIC_HELPERS(add_fetch) GEN_ATOMIC_HELPERS(and_fetch) GEN_ATOMIC_HELPERS(or_fetch) GEN_ATOMIC_HELPERS(xor_fetch) +GEN_ATOMIC_HELPERS(smin_fetch) +GEN_ATOMIC_HELPERS(umin_fetch) +GEN_ATOMIC_HELPERS(smax_fetch) +GEN_ATOMIC_HELPERS(umax_fetch) GEN_ATOMIC_HELPERS(xchg) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index f409d42d54..1571987113 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -46,7 +46,7 @@ #endif #endif #else -#include "exec/address-spaces.h" +#include "exec/ram_addr.h" #endif #include "exec/cputlb.h" @@ -88,13 +88,13 @@ #endif /* Access to the various translations structures need to be serialised via locks - * for consistency. This is automatic for SoftMMU based system - * emulation due to its single threaded nature. In user-mode emulation - * access to the memory related structures are protected with the - * mmap_lock. + * for consistency. + * In user-mode emulation access to the memory related structures are protected + * with mmap_lock. + * In !user-mode we use per-page locks. */ #ifdef CONFIG_SOFTMMU -#define assert_memory_lock() tcg_debug_assert(have_tb_lock) +#define assert_memory_lock() #else #define assert_memory_lock() tcg_debug_assert(have_mmap_lock()) #endif @@ -103,17 +103,76 @@ typedef struct PageDesc { /* list of TBs intersecting this ram page */ - TranslationBlock *first_tb; + uintptr_t first_tb; #ifdef CONFIG_SOFTMMU /* in order to optimize self modifying code, we count the number of lookups we do to a given page to use a bitmap */ - unsigned int code_write_count; unsigned long *code_bitmap; + unsigned int code_write_count; #else unsigned long flags; #endif +#ifndef CONFIG_USER_ONLY + QemuSpin lock; +#endif } PageDesc; +/** + * struct page_entry - page descriptor entry + * @pd: pointer to the &struct PageDesc of the page this entry represents + * @index: page index of the page + * @locked: whether the page is locked + * + * This struct helps us keep track of the locked state of a page, without + * bloating &struct PageDesc. + * + * A page lock protects accesses to all fields of &struct PageDesc. + * + * See also: &struct page_collection. + */ +struct page_entry { + PageDesc *pd; + tb_page_addr_t index; + bool locked; +}; + +/** + * struct page_collection - tracks a set of pages (i.e. &struct page_entry's) + * @tree: Binary search tree (BST) of the pages, with key == page index + * @max: Pointer to the page in @tree with the highest page index + * + * To avoid deadlock we lock pages in ascending order of page index. + * When operating on a set of pages, we need to keep track of them so that + * we can lock them in order and also unlock them later. For this we collect + * pages (i.e. &struct page_entry's) in a binary search @tree. Given that the + * @tree implementation we use does not provide an O(1) operation to obtain the + * highest-ranked element, we use @max to keep track of the inserted page + * with the highest index. This is valuable because if a page is not in + * the tree and its index is higher than @max's, then we can lock it + * without breaking the locking order rule. + * + * Note on naming: 'struct page_set' would be shorter, but we already have a few + * page_set_*() helpers, so page_collection is used instead to avoid confusion. + * + * See also: page_collection_lock(). + */ +struct page_collection { + GTree *tree; + struct page_entry *max; +}; + +/* list iterators for lists of tagged pointers in TranslationBlock */ +#define TB_FOR_EACH_TAGGED(head, tb, n, field) \ + for (n = (head) & 1, tb = (TranslationBlock *)((head) & ~1); \ + tb; tb = (TranslationBlock *)tb->field[n], n = (uintptr_t)tb & 1, \ + tb = (TranslationBlock *)((uintptr_t)tb & ~1)) + +#define PAGE_FOR_EACH_TB(pagedesc, tb, n) \ + TB_FOR_EACH_TAGGED((pagedesc)->first_tb, tb, n, page_next) + +#define TB_FOR_EACH_JMP(head_tb, tb, n) \ + TB_FOR_EACH_TAGGED((head_tb)->jmp_list_head, tb, n, jmp_list_next) + /* In system mode we want L1_MAP to be based on ram offsets, while in user mode we want it to be based on virtual addresses. */ #if !defined(CONFIG_USER_ONLY) @@ -132,7 +191,7 @@ typedef struct PageDesc { /* Make sure all possible CPU event bits fit in tb->trace_vcpu_dstate */ QEMU_BUILD_BUG_ON(CPU_TRACE_DSTATE_MAX_EVENTS > - sizeof(((TranslationBlock *)0)->trace_vcpu_dstate) + sizeof_field(TranslationBlock, trace_vcpu_dstate) * BITS_PER_BYTE); /* @@ -157,9 +216,6 @@ __thread TCGContext *tcg_ctx; TBContext tb_ctx; bool parallel_cpus; -/* translation block context */ -static __thread int have_tb_lock; - static void page_table_config_init(void) { uint32_t v_l1_bits; @@ -180,33 +236,6 @@ static void page_table_config_init(void) assert(v_l2_levels >= 0); } -#define assert_tb_locked() tcg_debug_assert(have_tb_lock) -#define assert_tb_unlocked() tcg_debug_assert(!have_tb_lock) - -void tb_lock(void) -{ - assert_tb_unlocked(); - qemu_mutex_lock(&tb_ctx.tb_lock); - have_tb_lock++; -} - -void tb_unlock(void) -{ - assert_tb_locked(); - have_tb_lock--; - qemu_mutex_unlock(&tb_ctx.tb_lock); -} - -void tb_lock_reset(void) -{ - if (have_tb_lock) { - qemu_mutex_unlock(&tb_ctx.tb_lock); - have_tb_lock = 0; - } -} - -static TranslationBlock *tb_find_pc(uintptr_t tc_ptr); - void cpu_gen_init(void) { tcg_context_init(&tcg_init_ctx); @@ -298,7 +327,6 @@ static int encode_search(TranslationBlock *tb, uint8_t *block) } /* The cpu state corresponding to 'searched_pc' is restored. - * Called with tb_lock held. * When reset_icount is true, current TB will be interrupted and * icount should be recalculated. */ @@ -335,7 +363,7 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, return -1; found: - if (reset_icount && (tb->cflags & CF_USE_ICOUNT)) { + if (reset_icount && (tb_cflags(tb) & CF_USE_ICOUNT)) { assert(use_icount); /* Reset the cycle counter to the start of the block and shift if to the number of actually executed instructions */ @@ -364,8 +392,7 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) * - fault during translation (instruction fetch) * - fault from helper (not using GETPC() macro) * - * Either way we need return early to avoid blowing up on a - * recursive tb_lock() as we can't resolve it here. + * Either way we need return early as we can't resolve it here. * * We are using unsigned arithmetic so if host_pc < * tcg_init_ctx.code_gen_buffer check_offset will wrap to way @@ -374,18 +401,16 @@ bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) check_offset = host_pc - (uintptr_t) tcg_init_ctx.code_gen_buffer; if (check_offset < tcg_init_ctx.code_gen_buffer_size) { - tb_lock(); - tb = tb_find_pc(host_pc); + tb = tcg_tb_lookup(host_pc); if (tb) { cpu_restore_state_from_tb(cpu, tb, host_pc, will_exit); - if (tb->cflags & CF_NOCACHE) { + if (tb_cflags(tb) & CF_NOCACHE) { /* one-shot translation, invalidate it immediately */ tb_phys_invalidate(tb, -1); - tb_remove(tb); + tcg_tb_remove(tb); } r = true; } - tb_unlock(); } return r; @@ -462,20 +487,12 @@ static void page_init(void) #endif } -/* If alloc=1: - * Called with tb_lock held for system emulation. - * Called with mmap_lock held for user-mode emulation. - */ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) { PageDesc *pd; void **lp; int i; - if (alloc) { - assert_memory_lock(); - } - /* Level 1. Always allocated. */ lp = l1_map + ((index >> v_l1_shift) & (v_l1_size - 1)); @@ -484,11 +501,17 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) void **p = atomic_rcu_read(lp); if (p == NULL) { + void *existing; + if (!alloc) { return NULL; } p = g_new0(void *, V_L2_SIZE); - atomic_rcu_set(lp, p); + existing = atomic_cmpxchg(lp, NULL, p); + if (unlikely(existing)) { + g_free(p); + p = existing; + } } lp = p + ((index >> (i * V_L2_BITS)) & (V_L2_SIZE - 1)); @@ -496,11 +519,26 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) pd = atomic_rcu_read(lp); if (pd == NULL) { + void *existing; + if (!alloc) { return NULL; } pd = g_new0(PageDesc, V_L2_SIZE); - atomic_rcu_set(lp, pd); +#ifndef CONFIG_USER_ONLY + { + int i; + + for (i = 0; i < V_L2_SIZE; i++) { + qemu_spin_init(&pd[i].lock); + } + } +#endif + existing = atomic_cmpxchg(lp, NULL, pd); + if (unlikely(existing)) { + g_free(pd); + pd = existing; + } } return pd + (index & (V_L2_SIZE - 1)); @@ -511,6 +549,349 @@ static inline PageDesc *page_find(tb_page_addr_t index) return page_find_alloc(index, 0); } +static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, + PageDesc **ret_p2, tb_page_addr_t phys2, int alloc); + +/* In user-mode page locks aren't used; mmap_lock is enough */ +#ifdef CONFIG_USER_ONLY + +#define assert_page_locked(pd) tcg_debug_assert(have_mmap_lock()) + +static inline void page_lock(PageDesc *pd) +{ } + +static inline void page_unlock(PageDesc *pd) +{ } + +static inline void page_lock_tb(const TranslationBlock *tb) +{ } + +static inline void page_unlock_tb(const TranslationBlock *tb) +{ } + +struct page_collection * +page_collection_lock(tb_page_addr_t start, tb_page_addr_t end) +{ + return NULL; +} + +void page_collection_unlock(struct page_collection *set) +{ } +#else /* !CONFIG_USER_ONLY */ + +#ifdef CONFIG_DEBUG_TCG + +static __thread GHashTable *ht_pages_locked_debug; + +static void ht_pages_locked_debug_init(void) +{ + if (ht_pages_locked_debug) { + return; + } + ht_pages_locked_debug = g_hash_table_new(NULL, NULL); +} + +static bool page_is_locked(const PageDesc *pd) +{ + PageDesc *found; + + ht_pages_locked_debug_init(); + found = g_hash_table_lookup(ht_pages_locked_debug, pd); + return !!found; +} + +static void page_lock__debug(PageDesc *pd) +{ + ht_pages_locked_debug_init(); + g_assert(!page_is_locked(pd)); + g_hash_table_insert(ht_pages_locked_debug, pd, pd); +} + +static void page_unlock__debug(const PageDesc *pd) +{ + bool removed; + + ht_pages_locked_debug_init(); + g_assert(page_is_locked(pd)); + removed = g_hash_table_remove(ht_pages_locked_debug, pd); + g_assert(removed); +} + +static void +do_assert_page_locked(const PageDesc *pd, const char *file, int line) +{ + if (unlikely(!page_is_locked(pd))) { + error_report("assert_page_lock: PageDesc %p not locked @ %s:%d", + pd, file, line); + abort(); + } +} + +#define assert_page_locked(pd) do_assert_page_locked(pd, __FILE__, __LINE__) + +void assert_no_pages_locked(void) +{ + ht_pages_locked_debug_init(); + g_assert(g_hash_table_size(ht_pages_locked_debug) == 0); +} + +#else /* !CONFIG_DEBUG_TCG */ + +#define assert_page_locked(pd) + +static inline void page_lock__debug(const PageDesc *pd) +{ +} + +static inline void page_unlock__debug(const PageDesc *pd) +{ +} + +#endif /* CONFIG_DEBUG_TCG */ + +static inline void page_lock(PageDesc *pd) +{ + page_lock__debug(pd); + qemu_spin_lock(&pd->lock); +} + +static inline void page_unlock(PageDesc *pd) +{ + qemu_spin_unlock(&pd->lock); + page_unlock__debug(pd); +} + +/* lock the page(s) of a TB in the correct acquisition order */ +static inline void page_lock_tb(const TranslationBlock *tb) +{ + page_lock_pair(NULL, tb->page_addr[0], NULL, tb->page_addr[1], 0); +} + +static inline void page_unlock_tb(const TranslationBlock *tb) +{ + PageDesc *p1 = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); + + page_unlock(p1); + if (unlikely(tb->page_addr[1] != -1)) { + PageDesc *p2 = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); + + if (p2 != p1) { + page_unlock(p2); + } + } +} + +static inline struct page_entry * +page_entry_new(PageDesc *pd, tb_page_addr_t index) +{ + struct page_entry *pe = g_malloc(sizeof(*pe)); + + pe->index = index; + pe->pd = pd; + pe->locked = false; + return pe; +} + +static void page_entry_destroy(gpointer p) +{ + struct page_entry *pe = p; + + g_assert(pe->locked); + page_unlock(pe->pd); + g_free(pe); +} + +/* returns false on success */ +static bool page_entry_trylock(struct page_entry *pe) +{ + bool busy; + + busy = qemu_spin_trylock(&pe->pd->lock); + if (!busy) { + g_assert(!pe->locked); + pe->locked = true; + page_lock__debug(pe->pd); + } + return busy; +} + +static void do_page_entry_lock(struct page_entry *pe) +{ + page_lock(pe->pd); + g_assert(!pe->locked); + pe->locked = true; +} + +static gboolean page_entry_lock(gpointer key, gpointer value, gpointer data) +{ + struct page_entry *pe = value; + + do_page_entry_lock(pe); + return FALSE; +} + +static gboolean page_entry_unlock(gpointer key, gpointer value, gpointer data) +{ + struct page_entry *pe = value; + + if (pe->locked) { + pe->locked = false; + page_unlock(pe->pd); + } + return FALSE; +} + +/* + * Trylock a page, and if successful, add the page to a collection. + * Returns true ("busy") if the page could not be locked; false otherwise. + */ +static bool page_trylock_add(struct page_collection *set, tb_page_addr_t addr) +{ + tb_page_addr_t index = addr >> TARGET_PAGE_BITS; + struct page_entry *pe; + PageDesc *pd; + + pe = g_tree_lookup(set->tree, &index); + if (pe) { + return false; + } + + pd = page_find(index); + if (pd == NULL) { + return false; + } + + pe = page_entry_new(pd, index); + g_tree_insert(set->tree, &pe->index, pe); + + /* + * If this is either (1) the first insertion or (2) a page whose index + * is higher than any other so far, just lock the page and move on. + */ + if (set->max == NULL || pe->index > set->max->index) { + set->max = pe; + do_page_entry_lock(pe); + return false; + } + /* + * Try to acquire out-of-order lock; if busy, return busy so that we acquire + * locks in order. + */ + return page_entry_trylock(pe); +} + +static gint tb_page_addr_cmp(gconstpointer ap, gconstpointer bp, gpointer udata) +{ + tb_page_addr_t a = *(const tb_page_addr_t *)ap; + tb_page_addr_t b = *(const tb_page_addr_t *)bp; + + if (a == b) { + return 0; + } else if (a < b) { + return -1; + } + return 1; +} + +/* + * Lock a range of pages ([@start,@end[) as well as the pages of all + * intersecting TBs. + * Locking order: acquire locks in ascending order of page index. + */ +struct page_collection * +page_collection_lock(tb_page_addr_t start, tb_page_addr_t end) +{ + struct page_collection *set = g_malloc(sizeof(*set)); + tb_page_addr_t index; + PageDesc *pd; + + start >>= TARGET_PAGE_BITS; + end >>= TARGET_PAGE_BITS; + g_assert(start <= end); + + set->tree = g_tree_new_full(tb_page_addr_cmp, NULL, NULL, + page_entry_destroy); + set->max = NULL; + assert_no_pages_locked(); + + retry: + g_tree_foreach(set->tree, page_entry_lock, NULL); + + for (index = start; index <= end; index++) { + TranslationBlock *tb; + int n; + + pd = page_find(index); + if (pd == NULL) { + continue; + } + if (page_trylock_add(set, index << TARGET_PAGE_BITS)) { + g_tree_foreach(set->tree, page_entry_unlock, NULL); + goto retry; + } + assert_page_locked(pd); + PAGE_FOR_EACH_TB(pd, tb, n) { + if (page_trylock_add(set, tb->page_addr[0]) || + (tb->page_addr[1] != -1 && + page_trylock_add(set, tb->page_addr[1]))) { + /* drop all locks, and reacquire in order */ + g_tree_foreach(set->tree, page_entry_unlock, NULL); + goto retry; + } + } + } + return set; +} + +void page_collection_unlock(struct page_collection *set) +{ + /* entries are unlocked and freed via page_entry_destroy */ + g_tree_destroy(set->tree); + g_free(set); +} + +#endif /* !CONFIG_USER_ONLY */ + +static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1, + PageDesc **ret_p2, tb_page_addr_t phys2, int alloc) +{ + PageDesc *p1, *p2; + tb_page_addr_t page1; + tb_page_addr_t page2; + + assert_memory_lock(); + g_assert(phys1 != -1); + + page1 = phys1 >> TARGET_PAGE_BITS; + page2 = phys2 >> TARGET_PAGE_BITS; + + p1 = page_find_alloc(page1, alloc); + if (ret_p1) { + *ret_p1 = p1; + } + if (likely(phys2 == -1)) { + page_lock(p1); + return; + } else if (page1 == page2) { + page_lock(p1); + if (ret_p2) { + *ret_p2 = p1; + } + return; + } + p2 = page_find_alloc(page2, alloc); + if (ret_p2) { + *ret_p2 = p2; + } + if (page1 < page2) { + page_lock(p1); + page_lock(p2); + } else { + page_lock(p2); + page_lock(p1); + } +} + #if defined(CONFIG_USER_ONLY) /* Currently it is not recommended to allocate big chunks of data in user mode. It will change when a dedicated libc will be used. */ @@ -644,11 +1025,8 @@ static inline void *alloc_code_gen_buffer(void) static inline void *alloc_code_gen_buffer(void) { size_t size = tcg_ctx->code_gen_buffer_size; - void *buf; - - buf = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, + return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - return buf; } #else static inline void *alloc_code_gen_buffer(void) @@ -731,48 +1109,6 @@ static inline void *alloc_code_gen_buffer(void) } #endif /* USE_STATIC_CODE_GEN_BUFFER, WIN32, POSIX */ -/* compare a pointer @ptr and a tb_tc @s */ -static int ptr_cmp_tb_tc(const void *ptr, const struct tb_tc *s) -{ - if (ptr >= s->ptr + s->size) { - return 1; - } else if (ptr < s->ptr) { - return -1; - } - return 0; -} - -static gint tb_tc_cmp(gconstpointer ap, gconstpointer bp) -{ - const struct tb_tc *a = ap; - const struct tb_tc *b = bp; - - /* - * When both sizes are set, we know this isn't a lookup. - * This is the most likely case: every TB must be inserted; lookups - * are a lot less frequent. - */ - if (likely(a->size && b->size)) { - if (a->ptr > b->ptr) { - return 1; - } else if (a->ptr < b->ptr) { - return -1; - } - /* a->ptr == b->ptr should happen only on deletions */ - g_assert(a->size == b->size); - return 0; - } - /* - * All lookups have either .size field set to 0. - * From the glib sources we see that @ap is always the lookup key. However - * the docs provide no guarantee, so we just mark this case as likely. - */ - if (likely(a->size == 0)) { - return ptr_cmp_tb_tc(a->ptr, b); - } - return ptr_cmp_tb_tc(b->ptr, a); -} - static inline void code_gen_alloc(size_t tb_size) { tcg_ctx->code_gen_buffer_size = size_code_gen_buffer(tb_size); @@ -781,15 +1117,27 @@ static inline void code_gen_alloc(size_t tb_size) fprintf(stderr, "Could not allocate dynamic translator buffer\n"); exit(1); } - tb_ctx.tb_tree = g_tree_new(tb_tc_cmp); - qemu_mutex_init(&tb_ctx.tb_lock); +} + +static bool tb_cmp(const void *ap, const void *bp) +{ + const TranslationBlock *a = ap; + const TranslationBlock *b = bp; + + return a->pc == b->pc && + a->cs_base == b->cs_base && + a->flags == b->flags && + (tb_cflags(a) & CF_HASH_MASK) == (tb_cflags(b) & CF_HASH_MASK) && + a->trace_vcpu_dstate == b->trace_vcpu_dstate && + a->page_addr[0] == b->page_addr[0] && + a->page_addr[1] == b->page_addr[1]; } static void tb_htable_init(void) { unsigned int mode = QHT_MODE_AUTO_RESIZE; - qht_init(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE, mode); + qht_init(&tb_ctx.htable, tb_cmp, CODE_GEN_HTABLE_SIZE, mode); } /* Must be called before using the QEMU cpus. 'tb_size' is the size @@ -812,14 +1160,12 @@ void tcg_exec_init(unsigned long tb_size) /* * Allocate a new translation block. Flush the translation buffer if * too many translation blocks or too much generated code. - * - * Called with tb_lock held. */ static TranslationBlock *tb_alloc(target_ulong pc) { TranslationBlock *tb; - assert_tb_locked(); + assert_memory_lock(); tb = tcg_tb_alloc(tcg_ctx); if (unlikely(tb == NULL)) { @@ -828,16 +1174,10 @@ static TranslationBlock *tb_alloc(target_ulong pc) return tb; } -/* Called with tb_lock held. */ -void tb_remove(TranslationBlock *tb) -{ - assert_tb_locked(); - - g_tree_remove(tb_ctx.tb_tree, &tb->tc); -} - +/* call with @p->lock held */ static inline void invalidate_page_bitmap(PageDesc *p) { + assert_page_locked(p); #ifdef CONFIG_SOFTMMU g_free(p->code_bitmap); p->code_bitmap = NULL; @@ -857,8 +1197,10 @@ static void page_flush_tb_1(int level, void **lp) PageDesc *pd = *lp; for (i = 0; i < V_L2_SIZE; ++i) { - pd[i].first_tb = NULL; + page_lock(&pd[i]); + pd[i].first_tb = (uintptr_t)NULL; invalidate_page_bitmap(pd + i); + page_unlock(&pd[i]); } } else { void **pp = *lp; @@ -890,8 +1232,7 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data) /* flush all the translation blocks */ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) { - tb_lock(); - + mmap_lock(); /* If it is already been done on request of another CPU, * just retry. */ @@ -900,10 +1241,10 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) } if (DEBUG_TB_FLUSH_GATE) { - size_t nb_tbs = g_tree_nnodes(tb_ctx.tb_tree); + size_t nb_tbs = tcg_nb_tbs(); size_t host_size = 0; - g_tree_foreach(tb_ctx.tb_tree, tb_host_size_iter, &host_size); + tcg_tb_foreach(tb_host_size_iter, &host_size); printf("qemu: flush code_size=%zu nb_tbs=%zu avg_tb_size=%zu\n", tcg_code_size(), nb_tbs, nb_tbs > 0 ? host_size / nb_tbs : 0); } @@ -912,10 +1253,6 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) cpu_tb_jmp_cache_clear(cpu); } - /* Increment the refcount first so that destroy acts as a reset */ - g_tree_ref(tb_ctx.tb_tree); - g_tree_destroy(tb_ctx.tb_tree); - qht_reset_size(&tb_ctx.htable, CODE_GEN_HTABLE_SIZE); page_flush_tb(); @@ -925,7 +1262,7 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count) atomic_mb_set(&tb_ctx.tb_flush_count, tb_ctx.tb_flush_count + 1); done: - tb_unlock(); + mmap_unlock(); } void tb_flush(CPUState *cpu) @@ -959,7 +1296,7 @@ do_tb_invalidate_check(struct qht *ht, void *p, uint32_t hash, void *userp) /* verify that all the pages have correct rights for code * - * Called with tb_lock held. + * Called with mmap_lock held. */ static void tb_invalidate_check(target_ulong address) { @@ -989,51 +1326,75 @@ static void tb_page_check(void) #endif /* CONFIG_USER_ONLY */ -static inline void tb_page_remove(TranslationBlock **ptb, TranslationBlock *tb) +/* + * user-mode: call with mmap_lock held + * !user-mode: call with @pd->lock held + */ +static inline void tb_page_remove(PageDesc *pd, TranslationBlock *tb) { TranslationBlock *tb1; + uintptr_t *pprev; unsigned int n1; - for (;;) { - tb1 = *ptb; - n1 = (uintptr_t)tb1 & 3; - tb1 = (TranslationBlock *)((uintptr_t)tb1 & ~3); + assert_page_locked(pd); + pprev = &pd->first_tb; + PAGE_FOR_EACH_TB(pd, tb1, n1) { if (tb1 == tb) { - *ptb = tb1->page_next[n1]; - break; + *pprev = tb1->page_next[n1]; + return; } - ptb = &tb1->page_next[n1]; + pprev = &tb1->page_next[n1]; } + g_assert_not_reached(); } -/* remove the TB from a list of TBs jumping to the n-th jump target of the TB */ -static inline void tb_remove_from_jmp_list(TranslationBlock *tb, int n) +/* remove @orig from its @n_orig-th jump list */ +static inline void tb_remove_from_jmp_list(TranslationBlock *orig, int n_orig) { - TranslationBlock *tb1; - uintptr_t *ptb, ntb; - unsigned int n1; + uintptr_t ptr, ptr_locked; + TranslationBlock *dest; + TranslationBlock *tb; + uintptr_t *pprev; + int n; - ptb = &tb->jmp_list_next[n]; - if (*ptb) { - /* find tb(n) in circular list */ - for (;;) { - ntb = *ptb; - n1 = ntb & 3; - tb1 = (TranslationBlock *)(ntb & ~3); - if (n1 == n && tb1 == tb) { - break; - } - if (n1 == 2) { - ptb = &tb1->jmp_list_first; - } else { - ptb = &tb1->jmp_list_next[n1]; - } - } - /* now we can suppress tb(n) from the list */ - *ptb = tb->jmp_list_next[n]; - - tb->jmp_list_next[n] = (uintptr_t)NULL; + /* mark the LSB of jmp_dest[] so that no further jumps can be inserted */ + ptr = atomic_or_fetch(&orig->jmp_dest[n_orig], 1); + dest = (TranslationBlock *)(ptr & ~1); + if (dest == NULL) { + return; } + + qemu_spin_lock(&dest->jmp_lock); + /* + * While acquiring the lock, the jump might have been removed if the + * destination TB was invalidated; check again. + */ + ptr_locked = atomic_read(&orig->jmp_dest[n_orig]); + if (ptr_locked != ptr) { + qemu_spin_unlock(&dest->jmp_lock); + /* + * The only possibility is that the jump was unlinked via + * tb_jump_unlink(dest). Seeing here another destination would be a bug, + * because we set the LSB above. + */ + g_assert(ptr_locked == 1 && dest->cflags & CF_INVALID); + return; + } + /* + * We first acquired the lock, and since the destination pointer matches, + * we know for sure that @orig is in the jmp list. + */ + pprev = &dest->jmp_list_head; + TB_FOR_EACH_JMP(dest, tb, n) { + if (tb == orig && n == n_orig) { + *pprev = tb->jmp_list_next[n]; + /* no need to set orig->jmp_dest[n]; setting the LSB was enough */ + qemu_spin_unlock(&dest->jmp_lock); + return; + } + pprev = &tb->jmp_list_next[n]; + } + g_assert_not_reached(); } /* reset the jump entry 'n' of a TB so that it is not chained to @@ -1045,59 +1406,61 @@ static inline void tb_reset_jump(TranslationBlock *tb, int n) } /* remove any jumps to the TB */ -static inline void tb_jmp_unlink(TranslationBlock *tb) +static inline void tb_jmp_unlink(TranslationBlock *dest) { - TranslationBlock *tb1; - uintptr_t *ptb, ntb; - unsigned int n1; + TranslationBlock *tb; + int n; - ptb = &tb->jmp_list_first; - for (;;) { - ntb = *ptb; - n1 = ntb & 3; - tb1 = (TranslationBlock *)(ntb & ~3); - if (n1 == 2) { - break; - } - tb_reset_jump(tb1, n1); - *ptb = tb1->jmp_list_next[n1]; - tb1->jmp_list_next[n1] = (uintptr_t)NULL; + qemu_spin_lock(&dest->jmp_lock); + + TB_FOR_EACH_JMP(dest, tb, n) { + tb_reset_jump(tb, n); + atomic_and(&tb->jmp_dest[n], (uintptr_t)NULL | 1); + /* No need to clear the list entry; setting the dest ptr is enough */ } + dest->jmp_list_head = (uintptr_t)NULL; + + qemu_spin_unlock(&dest->jmp_lock); } -/* invalidate one TB - * - * Called with tb_lock held. +/* + * In user-mode, call with mmap_lock held. + * In !user-mode, if @rm_from_page_list is set, call with the TB's pages' + * locks held. */ -void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) +static void do_tb_phys_invalidate(TranslationBlock *tb, bool rm_from_page_list) { CPUState *cpu; PageDesc *p; uint32_t h; tb_page_addr_t phys_pc; - assert_tb_locked(); + assert_memory_lock(); + /* make sure no further incoming jumps will be chained to this TB */ + qemu_spin_lock(&tb->jmp_lock); atomic_set(&tb->cflags, tb->cflags | CF_INVALID); + qemu_spin_unlock(&tb->jmp_lock); /* remove the TB from the hash list */ phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK); - h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, + h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb_cflags(tb) & CF_HASH_MASK, tb->trace_vcpu_dstate); - if (!qht_remove(&tb_ctx.htable, tb, h)) { + if (!(tb->cflags & CF_NOCACHE) && + !qht_remove(&tb_ctx.htable, tb, h)) { return; } /* remove the TB from the page list */ - if (tb->page_addr[0] != page_addr) { + if (rm_from_page_list) { p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS); - tb_page_remove(&p->first_tb, tb); - invalidate_page_bitmap(p); - } - if (tb->page_addr[1] != -1 && tb->page_addr[1] != page_addr) { - p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); - tb_page_remove(&p->first_tb, tb); + tb_page_remove(p, tb); invalidate_page_bitmap(p); + if (tb->page_addr[1] != -1) { + p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS); + tb_page_remove(p, tb); + invalidate_page_bitmap(p); + } } /* remove the TB from the hash list */ @@ -1115,21 +1478,41 @@ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) /* suppress any remaining jumps to this TB */ tb_jmp_unlink(tb); - tb_ctx.tb_phys_invalidate_count++; + atomic_set(&tcg_ctx->tb_phys_invalidate_count, + tcg_ctx->tb_phys_invalidate_count + 1); +} + +static void tb_phys_invalidate__locked(TranslationBlock *tb) +{ + do_tb_phys_invalidate(tb, true); +} + +/* invalidate one TB + * + * Called with mmap_lock held in user-mode. + */ +void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) +{ + if (page_addr == -1) { + page_lock_tb(tb); + do_tb_phys_invalidate(tb, true); + page_unlock_tb(tb); + } else { + do_tb_phys_invalidate(tb, false); + } } #ifdef CONFIG_SOFTMMU +/* call with @p->lock held */ static void build_page_bitmap(PageDesc *p) { int n, tb_start, tb_end; TranslationBlock *tb; + assert_page_locked(p); p->code_bitmap = bitmap_new(TARGET_PAGE_SIZE); - tb = p->first_tb; - while (tb != NULL) { - n = (uintptr_t)tb & 3; - tb = (TranslationBlock *)((uintptr_t)tb & ~3); + PAGE_FOR_EACH_TB(p, tb, n) { /* NOTE: this is subtle as a TB may span two physical pages */ if (n == 0) { /* NOTE: tb_end may be after the end of the page, but @@ -1144,7 +1527,6 @@ static void build_page_bitmap(PageDesc *p) tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK); } bitmap_set(p->code_bitmap, tb_start, tb_end - tb_start); - tb = tb->page_next[n]; } } #endif @@ -1152,24 +1534,23 @@ static void build_page_bitmap(PageDesc *p) /* add the tb in the target page and protect it if necessary * * Called with mmap_lock held for user-mode emulation. + * Called with @p->lock held in !user-mode. */ -static inline void tb_alloc_page(TranslationBlock *tb, - unsigned int n, tb_page_addr_t page_addr) +static inline void tb_page_add(PageDesc *p, TranslationBlock *tb, + unsigned int n, tb_page_addr_t page_addr) { - PageDesc *p; #ifndef CONFIG_USER_ONLY bool page_already_protected; #endif - assert_memory_lock(); + assert_page_locked(p); tb->page_addr[n] = page_addr; - p = page_find_alloc(page_addr >> TARGET_PAGE_BITS, 1); tb->page_next[n] = p->first_tb; #ifndef CONFIG_USER_ONLY - page_already_protected = p->first_tb != NULL; + page_already_protected = p->first_tb != (uintptr_t)NULL; #endif - p->first_tb = (TranslationBlock *)((uintptr_t)tb | n); + p->first_tb = (uintptr_t)tb | n; invalidate_page_bitmap(p); #if defined(CONFIG_USER_ONLY) @@ -1212,32 +1593,69 @@ static inline void tb_alloc_page(TranslationBlock *tb, * (-1) to indicate that only one page contains the TB. * * Called with mmap_lock held for user-mode emulation. + * + * Returns a pointer @tb, or a pointer to an existing TB that matches @tb. + * Note that in !user-mode, another thread might have already added a TB + * for the same block of guest code that @tb corresponds to. In that case, + * the caller should discard the original @tb, and use instead the returned TB. */ -static void tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, - tb_page_addr_t phys_page2) +static TranslationBlock * +tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, + tb_page_addr_t phys_page2) { - uint32_t h; + PageDesc *p; + PageDesc *p2 = NULL; assert_memory_lock(); - /* add in the page list */ - tb_alloc_page(tb, 0, phys_pc & TARGET_PAGE_MASK); - if (phys_page2 != -1) { - tb_alloc_page(tb, 1, phys_page2); + /* + * Add the TB to the page list, acquiring first the pages's locks. + * We keep the locks held until after inserting the TB in the hash table, + * so that if the insertion fails we know for sure that the TBs are still + * in the page descriptors. + * Note that inserting into the hash table first isn't an option, since + * we can only insert TBs that are fully initialized. + */ + page_lock_pair(&p, phys_pc, &p2, phys_page2, 1); + tb_page_add(p, tb, 0, phys_pc & TARGET_PAGE_MASK); + if (p2) { + tb_page_add(p2, tb, 1, phys_page2); } else { tb->page_addr[1] = -1; } - /* add in the hash table */ - h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, - tb->trace_vcpu_dstate); - qht_insert(&tb_ctx.htable, tb, h); + if (!(tb->cflags & CF_NOCACHE)) { + void *existing_tb = NULL; + uint32_t h; + + /* add in the hash table */ + h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->cflags & CF_HASH_MASK, + tb->trace_vcpu_dstate); + qht_insert(&tb_ctx.htable, tb, h, &existing_tb); + + /* remove TB from the page(s) if we couldn't insert it */ + if (unlikely(existing_tb)) { + tb_page_remove(p, tb); + invalidate_page_bitmap(p); + if (p2) { + tb_page_remove(p2, tb); + invalidate_page_bitmap(p2); + } + tb = existing_tb; + } + } + + if (p2 && p2 != p) { + page_unlock(p2); + } + page_unlock(p); #ifdef CONFIG_USER_ONLY if (DEBUG_TB_CHECK_GATE) { tb_page_check(); } #endif + return tb; } /* Called with mmap_lock held for user mode emulation. */ @@ -1246,7 +1664,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, uint32_t flags, int cflags) { CPUArchState *env = cpu->env_ptr; - TranslationBlock *tb; + TranslationBlock *tb, *existing_tb; tb_page_addr_t phys_pc, phys_page2; target_ulong virt_page2; tcg_insn_unit *gen_code_buf; @@ -1370,12 +1788,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu, CODE_GEN_ALIGN)); /* init jump list */ - assert(((uintptr_t)tb & 3) == 0); - tb->jmp_list_first = (uintptr_t)tb | 2; + qemu_spin_init(&tb->jmp_lock); + tb->jmp_list_head = (uintptr_t)NULL; tb->jmp_list_next[0] = (uintptr_t)NULL; tb->jmp_list_next[1] = (uintptr_t)NULL; + tb->jmp_dest[0] = (uintptr_t)NULL; + tb->jmp_dest[1] = (uintptr_t)NULL; - /* init original jump addresses wich has been set during tcg_gen_code() */ + /* init original jump addresses which have been set during tcg_gen_code() */ if (tb->jmp_reset_offset[0] != TB_JMP_RESET_OFFSET_INVALID) { tb_reset_jump(tb, 0); } @@ -1389,66 +1809,36 @@ TranslationBlock *tb_gen_code(CPUState *cpu, if ((pc & TARGET_PAGE_MASK) != virt_page2) { phys_page2 = get_page_addr_code(env, virt_page2); } - /* As long as consistency of the TB stuff is provided by tb_lock in user - * mode and is implicit in single-threaded softmmu emulation, no explicit - * memory barrier is required before tb_link_page() makes the TB visible - * through the physical hash table and physical page list. + /* + * No explicit memory barrier is required -- tb_link_page() makes the + * TB visible in a consistent state. */ - tb_link_page(tb, phys_pc, phys_page2); - g_tree_insert(tb_ctx.tb_tree, &tb->tc, tb); + existing_tb = tb_link_page(tb, phys_pc, phys_page2); + /* if the TB already exists, discard what we just translated */ + if (unlikely(existing_tb != tb)) { + uintptr_t orig_aligned = (uintptr_t)gen_code_buf; + + orig_aligned -= ROUND_UP(sizeof(*tb), qemu_icache_linesize); + atomic_set(&tcg_ctx->code_gen_ptr, (void *)orig_aligned); + return existing_tb; + } + tcg_tb_insert(tb); return tb; } /* - * Invalidate all TBs which intersect with the target physical address range - * [start;end[. NOTE: start and end may refer to *different* physical pages. - * 'is_cpu_write_access' should be true if called from a real cpu write - * access: the virtual CPU will exit the current TB if code is modified inside - * this TB. - * - * Called with mmap_lock held for user-mode emulation, grabs tb_lock - * Called with tb_lock held for system-mode emulation + * @p must be non-NULL. + * user-mode: call with mmap_lock held. + * !user-mode: call with all @pages locked. */ -static void tb_invalidate_phys_range_1(tb_page_addr_t start, tb_page_addr_t end) +static void +tb_invalidate_phys_page_range__locked(struct page_collection *pages, + PageDesc *p, tb_page_addr_t start, + tb_page_addr_t end, + int is_cpu_write_access) { - while (start < end) { - tb_invalidate_phys_page_range(start, end, 0); - start &= TARGET_PAGE_MASK; - start += TARGET_PAGE_SIZE; - } -} - -#ifdef CONFIG_SOFTMMU -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) -{ - assert_tb_locked(); - tb_invalidate_phys_range_1(start, end); -} -#else -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end) -{ - assert_memory_lock(); - tb_lock(); - tb_invalidate_phys_range_1(start, end); - tb_unlock(); -} -#endif -/* - * Invalidate all TBs which intersect with the target physical address range - * [start;end[. NOTE: start and end must refer to the *same* physical page. - * 'is_cpu_write_access' should be true if called from a real cpu write - * access: the virtual CPU will exit the current TB if code is modified inside - * this TB. - * - * Called with tb_lock/mmap_lock held for user-mode emulation - * Called with tb_lock held for system-mode emulation - */ -void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, - int is_cpu_write_access) -{ - TranslationBlock *tb, *tb_next; + TranslationBlock *tb; tb_page_addr_t tb_start, tb_end; - PageDesc *p; int n; #ifdef TARGET_HAS_PRECISE_SMC CPUState *cpu = current_cpu; @@ -1461,13 +1851,8 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, uint32_t current_flags = 0; #endif /* TARGET_HAS_PRECISE_SMC */ - assert_memory_lock(); - assert_tb_locked(); + assert_page_locked(p); - p = page_find(start >> TARGET_PAGE_BITS); - if (!p) { - return; - } #if defined(TARGET_HAS_PRECISE_SMC) if (cpu != NULL) { env = cpu->env_ptr; @@ -1477,11 +1862,8 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, /* we remove all the TBs in the range [start, end[ */ /* XXX: see if in some cases it could be faster to invalidate all the code */ - tb = p->first_tb; - while (tb != NULL) { - n = (uintptr_t)tb & 3; - tb = (TranslationBlock *)((uintptr_t)tb & ~3); - tb_next = tb->page_next[n]; + PAGE_FOR_EACH_TB(p, tb, n) { + assert_page_locked(p); /* NOTE: this is subtle as a TB may span two physical pages */ if (n == 0) { /* NOTE: tb_end may be after the end of the page, but @@ -1499,11 +1881,11 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, current_tb = NULL; if (cpu->mem_io_pc) { /* now we have a real cpu fault */ - current_tb = tb_find_pc(cpu->mem_io_pc); + current_tb = tcg_tb_lookup(cpu->mem_io_pc); } } if (current_tb == tb && - (current_tb->cflags & CF_COUNT_MASK) != 1) { + (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { /* If we are modifying the current TB, we must stop its execution. We could be more precise by checking that the modification is after the current PC, but it @@ -1517,9 +1899,8 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, ¤t_flags); } #endif /* TARGET_HAS_PRECISE_SMC */ - tb_phys_invalidate(tb, -1); + tb_phys_invalidate__locked(tb); } - tb = tb_next; } #if !defined(CONFIG_USER_ONLY) /* if no code remaining, no need to continue to use slow writes */ @@ -1530,19 +1911,86 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, #endif #ifdef TARGET_HAS_PRECISE_SMC if (current_tb_modified) { + page_collection_unlock(pages); /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | curr_cflags(); + mmap_unlock(); cpu_loop_exit_noexc(cpu); } #endif } +/* + * Invalidate all TBs which intersect with the target physical address range + * [start;end[. NOTE: start and end must refer to the *same* physical page. + * 'is_cpu_write_access' should be true if called from a real cpu write + * access: the virtual CPU will exit the current TB if code is modified inside + * this TB. + * + * Called with mmap_lock held for user-mode emulation + */ +void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, + int is_cpu_write_access) +{ + struct page_collection *pages; + PageDesc *p; + + assert_memory_lock(); + + p = page_find(start >> TARGET_PAGE_BITS); + if (p == NULL) { + return; + } + pages = page_collection_lock(start, end); + tb_invalidate_phys_page_range__locked(pages, p, start, end, + is_cpu_write_access); + page_collection_unlock(pages); +} + +/* + * Invalidate all TBs which intersect with the target physical address range + * [start;end[. NOTE: start and end may refer to *different* physical pages. + * 'is_cpu_write_access' should be true if called from a real cpu write + * access: the virtual CPU will exit the current TB if code is modified inside + * this TB. + * + * Called with mmap_lock held for user-mode emulation. + */ +#ifdef CONFIG_SOFTMMU +void tb_invalidate_phys_range(ram_addr_t start, ram_addr_t end) +#else +void tb_invalidate_phys_range(target_ulong start, target_ulong end) +#endif +{ + struct page_collection *pages; + tb_page_addr_t next; + + assert_memory_lock(); + + pages = page_collection_lock(start, end); + for (next = (start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + start < end; + start = next, next += TARGET_PAGE_SIZE) { + PageDesc *pd = page_find(start >> TARGET_PAGE_BITS); + tb_page_addr_t bound = MIN(next, end); + + if (pd == NULL) { + continue; + } + tb_invalidate_phys_page_range__locked(pages, pd, start, bound, 0); + } + page_collection_unlock(pages); +} + #ifdef CONFIG_SOFTMMU /* len must be <= 8 and start must be a multiple of len. * Called via softmmu_template.h when code areas are written to with * iothread mutex not held. + * + * Call with all @pages in the range [@start, @start + len[ locked. */ -void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) +void tb_invalidate_phys_page_fast(struct page_collection *pages, + tb_page_addr_t start, int len) { PageDesc *p; @@ -1561,11 +2009,10 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) if (!p) { return; } + + assert_page_locked(p); if (!p->code_bitmap && ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD) { - /* build code bitmap. FIXME: writes should be protected by - * tb_lock, reads by tb_lock or RCU. - */ build_page_bitmap(p); } if (p->code_bitmap) { @@ -1579,7 +2026,7 @@ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) } } else { do_invalidate: - tb_invalidate_phys_page_range(start, start + len, 1); + tb_invalidate_phys_page_range__locked(pages, p, start, start + len, 1); } } #else @@ -1612,22 +2059,19 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) return false; } - tb_lock(); - tb = p->first_tb; #ifdef TARGET_HAS_PRECISE_SMC - if (tb && pc != 0) { - current_tb = tb_find_pc(pc); + if (p->first_tb && pc != 0) { + current_tb = tcg_tb_lookup(pc); } if (cpu != NULL) { env = cpu->env_ptr; } #endif - while (tb != NULL) { - n = (uintptr_t)tb & 3; - tb = (TranslationBlock *)((uintptr_t)tb & ~3); + assert_page_locked(p); + PAGE_FOR_EACH_TB(p, tb, n) { #ifdef TARGET_HAS_PRECISE_SMC if (current_tb == tb && - (current_tb->cflags & CF_COUNT_MASK) != 1) { + (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { /* If we are modifying the current TB, we must stop its execution. We could be more precise by checking that the modification is after the current PC, but it @@ -1641,64 +2085,28 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc) } #endif /* TARGET_HAS_PRECISE_SMC */ tb_phys_invalidate(tb, addr); - tb = tb->page_next[n]; } - p->first_tb = NULL; + p->first_tb = (uintptr_t)NULL; #ifdef TARGET_HAS_PRECISE_SMC if (current_tb_modified) { /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | curr_cflags(); - /* tb_lock will be reset after cpu_loop_exit_noexc longjmps - * back into the cpu_exec loop. */ return true; } #endif - tb_unlock(); return false; } #endif -/* - * Find the TB 'tb' such that - * tb->tc.ptr <= tc_ptr < tb->tc.ptr + tb->tc.size - * Return NULL if not found. - */ -static TranslationBlock *tb_find_pc(uintptr_t tc_ptr) -{ - struct tb_tc s = { .ptr = (void *)tc_ptr }; - - return g_tree_lookup(tb_ctx.tb_tree, &s); -} - -#if !defined(CONFIG_USER_ONLY) -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) -{ - ram_addr_t ram_addr; - MemoryRegion *mr; - hwaddr l = 1; - - rcu_read_lock(); - mr = address_space_translate(as, addr, &addr, &l, false); - if (!(memory_region_is_ram(mr) - || memory_region_is_romd(mr))) { - rcu_read_unlock(); - return; - } - ram_addr = memory_region_get_ram_addr(mr) + addr; - tb_lock(); - tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); - tb_unlock(); - rcu_read_unlock(); -} -#endif /* !defined(CONFIG_USER_ONLY) */ - -/* Called with tb_lock held. */ +/* user-mode: call with mmap_lock held */ void tb_check_watchpoint(CPUState *cpu) { TranslationBlock *tb; - tb = tb_find_pc(cpu->mem_io_pc); + assert_memory_lock(); + + tb = tcg_tb_lookup(cpu->mem_io_pc); if (tb) { /* We can use retranslation to find the PC. */ cpu_restore_state_from_tb(cpu, tb, cpu->mem_io_pc, true); @@ -1731,8 +2139,7 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) TranslationBlock *tb; uint32_t n; - tb_lock(); - tb = tb_find_pc(retaddr); + tb = tcg_tb_lookup(retaddr); if (!tb) { cpu_abort(cpu, "cpu_io_recompile: could not find TB for pc=%p", (void *)retaddr); @@ -1765,13 +2172,13 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) /* Generate a new TB executing the I/O insn. */ cpu->cflags_next_tb = curr_cflags() | CF_LAST_IO | n; - if (tb->cflags & CF_NOCACHE) { + if (tb_cflags(tb) & CF_NOCACHE) { if (tb->orig_tb) { /* Invalidate original TB if this TB was generated in * cpu_exec_nocache() */ tb_phys_invalidate(tb->orig_tb, -1); } - tb_remove(tb); + tcg_tb_remove(tb); } /* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not @@ -1779,9 +2186,6 @@ void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr) * repeating the fault, which is horribly inefficient. * Better would be to execute just this insn uncached, or generate a * second new TB. - * - * cpu_loop_exit_noexc will longjmp back to cpu_exec where the - * tb_lock gets reset. */ cpu_loop_exit_noexc(cpu); } @@ -1842,6 +2246,7 @@ static void print_qht_statistics(FILE *f, fprintf_function cpu_fprintf, } struct tb_tree_stats { + size_t nb_tbs; size_t host_size; size_t target_size; size_t max_target_size; @@ -1855,6 +2260,7 @@ static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) const TranslationBlock *tb = value; struct tb_tree_stats *tst = data; + tst->nb_tbs++; tst->host_size += tb->tc.size; tst->target_size += tb->size; if (tb->size > tst->max_target_size) { @@ -1878,10 +2284,8 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) struct qht_stats hst; size_t nb_tbs; - tb_lock(); - - nb_tbs = g_tree_nnodes(tb_ctx.tb_tree); - g_tree_foreach(tb_ctx.tb_tree, tb_tree_stats_iter, &tst); + tcg_tb_foreach(tb_tree_stats_iter, &tst); + nb_tbs = tst.nb_tbs; /* XXX: avoid using doubles ? */ cpu_fprintf(f, "Translation buffer state:\n"); /* @@ -1913,11 +2317,9 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) cpu_fprintf(f, "\nStatistics:\n"); cpu_fprintf(f, "TB flush count %u\n", atomic_read(&tb_ctx.tb_flush_count)); - cpu_fprintf(f, "TB invalidate count %d\n", tb_ctx.tb_phys_invalidate_count); + cpu_fprintf(f, "TB invalidate count %zu\n", tcg_tb_phys_invalidate_count()); cpu_fprintf(f, "TLB flush count %zu\n", tlb_flush_count()); tcg_dump_info(f, cpu_fprintf); - - tb_unlock(); } void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf) @@ -2185,7 +2587,7 @@ int page_unprotect(target_ulong address, uintptr_t pc) * set the page to PAGE_WRITE and did the TB invalidate for us. */ #ifdef TARGET_HAS_PRECISE_SMC - TranslationBlock *current_tb = tb_find_pc(pc); + TranslationBlock *current_tb = tcg_tb_lookup(pc); if (current_tb) { current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID; } diff --git a/accel/tcg/translate-all.h b/accel/tcg/translate-all.h index ba8e4d63c4..08e2f23a46 100644 --- a/accel/tcg/translate-all.h +++ b/accel/tcg/translate-all.h @@ -23,10 +23,13 @@ /* translate-all.c */ -void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); +struct page_collection *page_collection_lock(tb_page_addr_t start, + tb_page_addr_t end); +void page_collection_unlock(struct page_collection *set); +void tb_invalidate_phys_page_fast(struct page_collection *pages, + tb_page_addr_t start, int len); void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access); -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end); void tb_check_watchpoint(CPUState *cpu); #ifdef CONFIG_USER_ONLY diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 23c6602cd9..0f9dca9113 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -34,8 +34,6 @@ void translator_loop_temp_check(DisasContextBase *db) void translator_loop(const TranslatorOps *ops, DisasContextBase *db, CPUState *cpu, TranslationBlock *tb) { - int max_insns; - /* Initialize DisasContext */ db->tb = tb; db->pc_first = tb->pc; @@ -45,18 +43,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, db->singlestep_enabled = cpu->singlestep_enabled; /* Instruction counting */ - max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; + db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; + if (db->max_insns == 0) { + db->max_insns = CF_COUNT_MASK; } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; + if (db->max_insns > TCG_MAX_INSNS) { + db->max_insns = TCG_MAX_INSNS; } if (db->singlestep_enabled || singlestep) { - max_insns = 1; + db->max_insns = 1; } - max_insns = ops->init_disas_context(db, cpu, max_insns); + ops->init_disas_context(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ /* Reset the temp count so that we can identify leaks */ @@ -95,7 +93,8 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, update db->pc_next and db->is_jmp to indicate what should be done next -- either exiting this loop or locate the start of the next instruction. */ - if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) { + if (db->num_insns == db->max_insns + && (tb_cflags(db->tb) & CF_LAST_IO)) { /* Accept I/O on the last instruction. */ gen_io_start(); ops->translate_insn(db, cpu); @@ -111,7 +110,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, /* Stop translation if the output buffer is full, or we have executed all of the allowed instructions. */ - if (tcg_op_buf_full() || db->num_insns >= max_insns) { + if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { db->is_jmp = DISAS_TOO_MANY; break; } diff --git a/accel/tcg/user-exec-stub.c b/accel/tcg/user-exec-stub.c index dbcf1ade9c..a32b4496af 100644 --- a/accel/tcg/user-exec-stub.c +++ b/accel/tcg/user-exec-stub.c @@ -2,6 +2,9 @@ #include "qemu-common.h" #include "qom/cpu.h" #include "sysemu/replay.h" +#include "sysemu/sysemu.h" + +bool enable_cpu_pm = false; void cpu_resume(CPUState *cpu) { diff --git a/arch_init.c b/arch_init.c index 6ee07478bd..f4f3f610c8 100644 --- a/arch_init.c +++ b/arch_init.c @@ -29,6 +29,7 @@ #include "hw/pci/pci.h" #include "hw/audio/soundhw.h" #include "qapi/qapi-commands-misc.h" +#include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/error-report.h" #include "hw/acpi/acpi.h" @@ -51,14 +52,14 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_ARM #elif defined(TARGET_CRIS) #define QEMU_ARCH QEMU_ARCH_CRIS -#elif defined(TARGET_I386) -#define QEMU_ARCH QEMU_ARCH_I386 #elif defined(TARGET_HPPA) #define QEMU_ARCH QEMU_ARCH_HPPA -#elif defined(TARGET_M68K) -#define QEMU_ARCH QEMU_ARCH_M68K +#elif defined(TARGET_I386) +#define QEMU_ARCH QEMU_ARCH_I386 #elif defined(TARGET_LM32) #define QEMU_ARCH QEMU_ARCH_LM32 +#elif defined(TARGET_M68K) +#define QEMU_ARCH QEMU_ARCH_M68K #elif defined(TARGET_MICROBLAZE) #define QEMU_ARCH QEMU_ARCH_MICROBLAZE #elif defined(TARGET_MIPS) @@ -79,12 +80,12 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_SH4 #elif defined(TARGET_SPARC) #define QEMU_ARCH QEMU_ARCH_SPARC -#elif defined(TARGET_XTENSA) -#define QEMU_ARCH QEMU_ARCH_XTENSA -#elif defined(TARGET_UNICORE32) -#define QEMU_ARCH QEMU_ARCH_UNICORE32 #elif defined(TARGET_TRICORE) #define QEMU_ARCH QEMU_ARCH_TRICORE +#elif defined(TARGET_UNICORE32) +#define QEMU_ARCH QEMU_ARCH_UNICORE32 +#elif defined(TARGET_XTENSA) +#define QEMU_ARCH QEMU_ARCH_XTENSA #endif const uint32_t arch_type = QEMU_ARCH; @@ -112,7 +113,8 @@ TargetInfo *qmp_query_target(Error **errp) { TargetInfo *info = g_malloc0(sizeof(*info)); - info->arch = g_strdup(TARGET_NAME); + info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1, + &error_abort); return info; } diff --git a/audio/audio.c b/audio/audio.c index 6eccdb17ee..1ace47f510 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -29,6 +29,7 @@ #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "sysemu/replay.h" +#include "trace.h" #define AUDIO_CAP "audio" #include "audio_int.h" @@ -335,9 +336,8 @@ static int audio_get_conf_int (const char *key, int defval, int *defaultp) char *strval; strval = getenv (key); - if (strval) { + if (strval && !qemu_strtoi(strval, NULL, 10, &val)) { *defaultp = 0; - val = atoi (strval); return val; } else { @@ -1130,6 +1130,10 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) /* * Timer */ + +static bool audio_timer_running; +static uint64_t audio_timer_last; + static int audio_is_timer_needed (void) { HWVoiceIn *hwi = NULL; @@ -1149,14 +1153,31 @@ static void audio_reset_timer (AudioState *s) if (audio_is_timer_needed ()) { timer_mod_anticipate_ns(s->ts, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); - } - else { - timer_del (s->ts); + if (!audio_timer_running) { + audio_timer_running = true; + audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + trace_audio_timer_start(conf.period.ticks / SCALE_MS); + } + } else { + timer_del(s->ts); + if (audio_timer_running) { + audio_timer_running = false; + trace_audio_timer_stop(); + } } } static void audio_timer (void *opaque) { + int64_t now, diff; + + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + diff = now - audio_timer_last; + if (diff > conf.period.ticks * 3 / 2) { + trace_audio_timer_delayed(diff / SCALE_MS); + } + audio_timer_last = now; + audio_run ("timer"); audio_reset_timer (opaque); } diff --git a/audio/trace-events b/audio/trace-events index d37639e611..c986469319 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -15,3 +15,8 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d" # audio/ossaudio.c oss_version(int version) "OSS version = 0x%x" oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" + +# audio/audio.c +audio_timer_start(int interval) "interval %d ms" +audio_timer_stop(void) "" +audio_timer_delayed(int interval) "interval %d ms" diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index 862d4f2580..d539f14d59 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" +#include "hw/virtio/vhost-user.h" #include "standard-headers/linux/virtio_crypto.h" #include "sysemu/cryptodev-vhost.h" #include "chardev/char-fe.h" @@ -46,6 +47,7 @@ typedef struct CryptoDevBackendVhostUser { CryptoDevBackend parent_obj; + VhostUserState *vhost_user; CharBackend chr; char *chr_name; bool opened; @@ -102,7 +104,7 @@ cryptodev_vhost_user_start(int queues, continue; } - options.opaque = &s->chr; + options.opaque = s->vhost_user; options.backend_type = VHOST_BACKEND_TYPE_USER; options.cc = b->conf.peers.ccs[i]; s->vhost_crypto[i] = cryptodev_vhost_init(&options); @@ -155,7 +157,6 @@ static void cryptodev_vhost_user_event(void *opaque, int event) { CryptoDevBackendVhostUser *s = opaque; CryptoDevBackend *b = CRYPTODEV_BACKEND(s); - Error *err = NULL; int queues = b->conf.peers.queues; assert(queues < MAX_CRYPTO_QUEUE_NUM); @@ -172,10 +173,6 @@ static void cryptodev_vhost_user_event(void *opaque, int event) cryptodev_vhost_user_stop(queues, s); break; } - - if (err) { - error_report_err(err); - } } static void cryptodev_vhost_user_init( @@ -185,6 +182,7 @@ static void cryptodev_vhost_user_init( size_t i; Error *local_err = NULL; Chardev *chr; + VhostUserState *user; CryptoDevBackendClient *cc; CryptoDevBackendVhostUser *s = CRYPTODEV_BACKEND_VHOST_USER(backend); @@ -215,6 +213,15 @@ static void cryptodev_vhost_user_init( } } + user = vhost_user_init(); + if (!user) { + error_setg(errp, "Failed to init vhost_user"); + return; + } + + user->chr = &s->chr; + s->vhost_user = user; + qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, cryptodev_vhost_user_event, NULL, s, NULL, true); @@ -299,6 +306,12 @@ static void cryptodev_vhost_user_cleanup( backend->conf.peers.ccs[i] = NULL; } } + + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } } static void cryptodev_vhost_user_set_chardev(Object *obj, diff --git a/backends/hostmem.c b/backends/hostmem.c index f61093654e..4908946cd3 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -18,6 +18,7 @@ #include "qapi/visitor.h" #include "qemu/config-file.h" #include "qom/object_interfaces.h" +#include "qemu/mmap-alloc.h" #ifdef CONFIG_NUMA #include @@ -246,8 +247,7 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend) return memory_region_size(&backend->mr) != 0; } -MemoryRegion * -host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp) +MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend) { return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; } @@ -262,6 +262,23 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend) return backend->is_mapped; } +#ifdef __linux__ +size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) +{ + Object *obj = OBJECT(memdev); + char *path = object_property_get_str(obj, "mem-path", NULL); + size_t pagesize = qemu_mempath_getpagesize(path); + + g_free(path); + return pagesize; +} +#else +size_t host_memory_backend_pagesize(HostMemoryBackend *memdev) +{ + return getpagesize(); +} +#endif + static void host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) { @@ -351,24 +368,6 @@ host_memory_backend_can_be_deleted(UserCreatable *uc) } } -static char *get_id(Object *o, Error **errp) -{ - HostMemoryBackend *backend = MEMORY_BACKEND(o); - - return g_strdup(backend->id); -} - -static void set_id(Object *o, const char *str, Error **errp) -{ - HostMemoryBackend *backend = MEMORY_BACKEND(o); - - if (backend->id) { - error_setg(errp, "cannot change property value"); - return; - } - backend->id = g_strdup(str); -} - static bool host_memory_backend_get_share(Object *o, Error **errp) { HostMemoryBackend *backend = MEMORY_BACKEND(o); @@ -416,18 +415,11 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) &HostMemPolicy_lookup, host_memory_backend_get_policy, host_memory_backend_set_policy, &error_abort); - object_class_property_add_str(oc, "id", get_id, set_id, &error_abort); object_class_property_add_bool(oc, "share", host_memory_backend_get_share, host_memory_backend_set_share, &error_abort); } -static void host_memory_backend_finalize(Object *o) -{ - HostMemoryBackend *backend = MEMORY_BACKEND(o); - g_free(backend->id); -} - static const TypeInfo host_memory_backend_info = { .name = TYPE_MEMORY_BACKEND, .parent = TYPE_OBJECT, @@ -436,7 +428,6 @@ static const TypeInfo host_memory_backend_info = { .class_init = host_memory_backend_class_init, .instance_size = sizeof(HostMemoryBackend), .instance_init = host_memory_backend_init, - .instance_finalize = host_memory_backend_finalize, .interfaces = (InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } diff --git a/block.c b/block.c index a2caadf0a0..39f373e035 100644 --- a/block.c +++ b/block.c @@ -27,6 +27,7 @@ #include "block/block_int.h" #include "block/blockjob.h" #include "block/nbd.h" +#include "block/qdict.h" #include "qemu/error-report.h" #include "module_block.h" #include "qemu/module.h" @@ -332,6 +333,10 @@ BlockDriverState *bdrv_new(void) qemu_co_queue_init(&bs->flush_queue); + for (i = 0; i < bdrv_drain_all_count; i++) { + bdrv_drained_begin(bs); + } + QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); return bs; @@ -720,7 +725,7 @@ static int find_image_format(BlockBackend *file, const char *filename, * Set the current 'total_sectors' value * Return 0 on success, -errno on error. */ -static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) +int refresh_total_sectors(BlockDriverState *bs, int64_t hint) { BlockDriver *drv = bs->drv; @@ -817,7 +822,13 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c) static void bdrv_child_cb_drained_begin(BdrvChild *child) { BlockDriverState *bs = child->opaque; - bdrv_drained_begin(bs); + bdrv_do_drained_begin_quiesce(bs, NULL, false); +} + +static bool bdrv_child_cb_drained_poll(BdrvChild *child) +{ + BlockDriverState *bs = child->opaque; + return bdrv_drain_poll(bs, false, NULL, false); } static void bdrv_child_cb_drained_end(BdrvChild *child) @@ -901,9 +912,11 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options, } const BdrvChildRole child_file = { + .parent_is_bds = true, .get_parent_desc = bdrv_child_get_parent_desc, .inherit_options = bdrv_inherited_options, .drained_begin = bdrv_child_cb_drained_begin, + .drained_poll = bdrv_child_cb_drained_poll, .drained_end = bdrv_child_cb_drained_end, .attach = bdrv_child_cb_attach, .detach = bdrv_child_cb_detach, @@ -925,9 +938,11 @@ static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options, } const BdrvChildRole child_format = { + .parent_is_bds = true, .get_parent_desc = bdrv_child_get_parent_desc, .inherit_options = bdrv_inherited_fmt_options, .drained_begin = bdrv_child_cb_drained_begin, + .drained_poll = bdrv_child_cb_drained_poll, .drained_end = bdrv_child_cb_drained_end, .attach = bdrv_child_cb_attach, .detach = bdrv_child_cb_detach, @@ -1042,11 +1057,13 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, } const BdrvChildRole child_backing = { + .parent_is_bds = true, .get_parent_desc = bdrv_child_get_parent_desc, .attach = bdrv_backing_attach, .detach = bdrv_backing_detach, .inherit_options = bdrv_backing_options, .drained_begin = bdrv_child_cb_drained_begin, + .drained_poll = bdrv_child_cb_drained_poll, .drained_end = bdrv_child_cb_drained_end, .inactivate = bdrv_child_cb_inactivate, .update_filename = bdrv_backing_update_filename, @@ -1139,6 +1156,12 @@ static void bdrv_assign_node_name(BlockDriverState *bs, goto out; } + /* Make sure that the node name isn't truncated */ + if (strlen(node_name) >= sizeof(bs->node_name)) { + error_setg(errp, "Node name too long"); + goto out; + } + /* copy node name into the bs and insert it into the graph list */ pstrcpy(bs->node_name, sizeof(bs->node_name), node_name); QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); @@ -1151,7 +1174,7 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, int open_flags, Error **errp) { Error *local_err = NULL; - int ret; + int i, ret; bdrv_assign_node_name(bs, node_name, &local_err); if (local_err) { @@ -1199,6 +1222,12 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, assert(bdrv_min_mem_align(bs) != 0); assert(is_power_of_2(bs->bl.request_alignment)); + for (i = 0; i < bs->quiesce_counter; i++) { + if (drv->bdrv_co_drain_begin) { + drv->bdrv_co_drain_begin(bs); + } + } + return 0; open_failed: bs->drv = NULL; @@ -1227,9 +1256,9 @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp); if (ret < 0) { - QDECREF(bs->explicit_options); + qobject_unref(bs->explicit_options); bs->explicit_options = NULL; - QDECREF(bs->options); + qobject_unref(bs->options); bs->options = NULL; bdrv_unref(bs); return NULL; @@ -1460,7 +1489,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp) options = qobject_to(QDict, options_obj); if (!options) { - qobject_decref(options_obj); + qobject_unref(options_obj); error_setg(errp, "Invalid JSON object given"); return NULL; } @@ -1490,7 +1519,7 @@ static void parse_json_protocol(QDict *options, const char **pfilename, /* Options given in the filename have lower priority than options * specified directly */ qdict_join(options, json_options, false); - QDECREF(json_options); + qobject_unref(json_options); *pfilename = NULL; } @@ -1620,13 +1649,24 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs) /* Returns whether the image file can be written to after the reopen queue @q * has been successfully applied, or right now if @q is NULL. */ -static bool bdrv_is_writable(BlockDriverState *bs, BlockReopenQueue *q) +static bool bdrv_is_writable_after_reopen(BlockDriverState *bs, + BlockReopenQueue *q) { int flags = bdrv_reopen_get_flags(q, bs); return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR; } +/* + * Return whether the BDS can be written to. This is not necessarily + * the same as !bdrv_is_read_only(bs), as inactivated images may not + * be written to but do not count as read-only images. + */ +bool bdrv_is_writable(BlockDriverState *bs) +{ + return bdrv_is_writable_after_reopen(bs, NULL); +} + static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, BdrvChild *c, const BdrvChildRole *role, BlockReopenQueue *reopen_queue, @@ -1664,7 +1704,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q, /* Write permissions never work with read-only images */ if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && - !bdrv_is_writable(bs, q)) + !bdrv_is_writable_after_reopen(bs, q)) { error_setg(errp, "Block node is read-only"); return -EPERM; @@ -1914,12 +1954,6 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, return 0; } -#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ - | BLK_PERM_WRITE \ - | BLK_PERM_WRITE_UNCHANGED \ - | BLK_PERM_RESIZE) -#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH) - void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, const BdrvChildRole *role, BlockReopenQueue *reopen_queue, @@ -1956,7 +1990,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, &perm, &shared); /* Format drivers may touch metadata even if the guest doesn't write */ - if (bdrv_is_writable(bs, reopen_queue)) { + if (bdrv_is_writable_after_reopen(bs, reopen_queue)) { perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; } @@ -2009,7 +2043,12 @@ static void bdrv_replace_child_noperm(BdrvChild *child, child->role->detach(child); } if (old_bs->quiesce_counter && child->role->drained_end) { - for (i = 0; i < old_bs->quiesce_counter; i++) { + int num = old_bs->quiesce_counter; + if (child->role->parent_is_bds) { + num -= bdrv_drain_all_count; + } + assert(num >= 0); + for (i = 0; i < num; i++) { child->role->drained_end(child); } } @@ -2021,8 +2060,13 @@ static void bdrv_replace_child_noperm(BdrvChild *child, if (new_bs) { QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); if (new_bs->quiesce_counter && child->role->drained_begin) { - for (i = 0; i < new_bs->quiesce_counter; i++) { - child->role->drained_begin(child); + int num = new_bs->quiesce_counter; + if (child->role->parent_is_bds) { + num -= bdrv_drain_all_count; + } + assert(num >= 0); + for (i = 0; i < num; i++) { + bdrv_parent_drained_begin_single(child, true); } } @@ -2182,16 +2226,6 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) } } -static void bdrv_parent_cb_resize(BlockDriverState *bs) -{ - BdrvChild *c; - QLIST_FOREACH(c, &bs->parents, next_parent) { - if (c->role->resize) { - c->role->resize(c); - } - } -} - /* * Sets the backing file link of a BDS. A new reference is created; callers * which don't need their own reference any more must call bdrv_unref(). @@ -2273,7 +2307,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, if (reference || qdict_haskey(options, "file.filename")) { backing_filename[0] = '\0'; } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { - QDECREF(options); + qobject_unref(options); goto free_exit; } else { bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX, @@ -2281,7 +2315,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); - QDECREF(options); + qobject_unref(options); goto free_exit; } } @@ -2289,7 +2323,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, if (!bs->drv || !bs->drv->supports_backing) { ret = -EINVAL; error_setg(errp, "Driver doesn't support backing files"); - QDECREF(options); + qobject_unref(options); goto free_exit; } @@ -2323,7 +2357,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, free_exit: g_free(backing_filename); - QDECREF(tmp_parent_options); + qobject_unref(tmp_parent_options); return ret; } @@ -2356,7 +2390,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, error_setg(errp, "A block device must be specified for \"%s\"", bdref_key); } - QDECREF(image_options); + qobject_unref(image_options); goto done; } @@ -2449,7 +2483,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) obj = NULL; fail: - qobject_decref(obj); + qobject_unref(obj); visit_free(v); return bs; } @@ -2519,7 +2553,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs, } out: - QDECREF(snapshot_options); + qobject_unref(snapshot_options); g_free(tmp_filename); return bs_snapshot; } @@ -2530,7 +2564,7 @@ out: * options is a QDict of options to pass to the block drivers, or NULL for an * empty set of options. The reference to the QDict belongs to the block layer * after the call (even on failure), so if the caller intends to reuse the - * dictionary, it needs to use QINCREF() before calling bdrv_open. + * dictionary, it needs to use qobject_ref() before calling bdrv_open. * * If *pbs is NULL, a new BDS will be created with a pointer to it stored there. * If it is not NULL, the referenced BDS will be reused. @@ -2561,7 +2595,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, if (reference) { bool options_non_empty = options ? qdict_size(options) : false; - QDECREF(options); + qobject_unref(options); if (filename || options_non_empty) { error_setg(errp, "Cannot reference an existing block device with " @@ -2752,7 +2786,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, bdrv_parent_cb_change_media(bs, true); - QDECREF(options); + qobject_unref(options); /* For snapshot=on, create a temporary qcow2 overlay. bs points to the * temporary snapshot afterwards. */ @@ -2776,10 +2810,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, fail: blk_unref(file); - QDECREF(snapshot_options); - QDECREF(bs->explicit_options); - QDECREF(bs->options); - QDECREF(options); + qobject_unref(snapshot_options); + qobject_unref(bs->explicit_options); + qobject_unref(bs->options); + qobject_unref(options); bs->options = NULL; bs->explicit_options = NULL; bdrv_unref(bs); @@ -2788,8 +2822,8 @@ fail: close_and_fail: bdrv_unref(bs); - QDECREF(snapshot_options); - QDECREF(options); + qobject_unref(snapshot_options); + qobject_unref(options); error_propagate(errp, local_err); return NULL; } @@ -2884,7 +2918,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, old_options = qdict_clone_shallow(bs->explicit_options); } bdrv_join_options(bs, options, old_options); - QDECREF(old_options); + qobject_unref(old_options); explicit_options = qdict_clone_shallow(options); @@ -2899,13 +2933,13 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, qemu_opts_absorb_qdict(opts, options_copy, NULL); update_flags_from_options(&flags, opts); qemu_opts_del(opts); - QDECREF(options_copy); + qobject_unref(options_copy); } /* Old values are used for options that aren't set yet */ old_options = qdict_clone_shallow(bs->options); bdrv_join_options(bs, options, old_options); - QDECREF(old_options); + qobject_unref(old_options); /* bdrv_open_inherit() sets and clears some additional flags internally */ flags &= ~BDRV_O_PROTOCOL; @@ -2917,8 +2951,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, bs_entry = g_new0(BlockReopenQueueEntry, 1); QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); } else { - QDECREF(bs_entry->state.options); - QDECREF(bs_entry->state.explicit_options); + qobject_unref(bs_entry->state.options); + qobject_unref(bs_entry->state.explicit_options); } bs_entry->state.bs = bs; @@ -2968,7 +3002,7 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, * * Reopens all BDS specified in the queue, with the appropriate * flags. All devices are prepared for reopen, and failure of any - * device will cause all device changes to be abandonded, and intermediate + * device will cause all device changes to be abandoned, and intermediate * data cleaned up. * * If all devices prepare successfully, then the changes are committed @@ -3008,9 +3042,9 @@ cleanup: if (ret && bs_entry->prepared) { bdrv_reopen_abort(&bs_entry->state); } else if (ret) { - QDECREF(bs_entry->state.explicit_options); + qobject_unref(bs_entry->state.explicit_options); } - QDECREF(bs_entry->state.options); + qobject_unref(bs_entry->state.options); g_free(bs_entry); } g_free(bs_queue); @@ -3253,7 +3287,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) } /* set BDS specific flags now */ - QDECREF(bs->explicit_options); + qobject_unref(bs->explicit_options); bs->explicit_options = reopen_state->explicit_options; bs->open_flags = reopen_state->flags; @@ -3296,7 +3330,7 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state) drv->bdrv_reopen_abort(reopen_state); } - QDECREF(reopen_state->explicit_options); + qobject_unref(reopen_state->explicit_options); bdrv_abort_perm_update(reopen_state->bs); } @@ -3343,11 +3377,11 @@ static void bdrv_close(BlockDriverState *bs) bs->total_sectors = 0; bs->encrypted = false; bs->sg = false; - QDECREF(bs->options); - QDECREF(bs->explicit_options); + qobject_unref(bs->options); + qobject_unref(bs->explicit_options); bs->options = NULL; bs->explicit_options = NULL; - QDECREF(bs->full_open_options); + qobject_unref(bs->full_open_options); bs->full_open_options = NULL; bdrv_release_named_dirty_bitmaps(bs); @@ -3362,7 +3396,7 @@ static void bdrv_close(BlockDriverState *bs) void bdrv_close_all(void) { - block_job_cancel_sync_all(); + assert(job_next(NULL) == NULL); nbd_export_close_all(); /* Drop references from requests still in flight, such as canceled block @@ -3383,16 +3417,39 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) return false; } - if (c->role == &child_backing) { - /* If @from is a backing file of @to, ignore the child to avoid - * creating a loop. We only want to change the pointer of other - * parents. */ - QLIST_FOREACH(to_c, &to->children, next) { - if (to_c == c) { - break; - } - } - if (to_c) { + /* If the child @c belongs to the BDS @to, replacing the current + * c->bs by @to would mean to create a loop. + * + * Such a case occurs when appending a BDS to a backing chain. + * For instance, imagine the following chain: + * + * guest device -> node A -> further backing chain... + * + * Now we create a new BDS B which we want to put on top of this + * chain, so we first attach A as its backing node: + * + * node B + * | + * v + * guest device -> node A -> further backing chain... + * + * Finally we want to replace A by B. When doing that, we want to + * replace all pointers to A by pointers to B -- except for the + * pointer from B because (1) that would create a loop, and (2) + * that pointer should simply stay intact: + * + * guest device -> node B + * | + * v + * node A -> further backing chain... + * + * In general, when replacing a node A (c->bs) by a node B (@to), + * if A is a child of B, that means we cannot replace A by B there + * because that would create a loop. Silently detaching A from B + * is also not really an option. So overall just leaving A in + * place there is the most sensible choice. */ + QLIST_FOREACH(to_c, &to->children, next) { + if (to_c == c) { return false; } } @@ -3418,6 +3475,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, /* Put all parents into @list and calculate their cumulative permissions */ QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { + assert(c->bs == from); if (!should_update_child(c, to)) { continue; } @@ -3717,58 +3775,6 @@ exit: return ret; } -/** - * Truncate file to 'offset' bytes (needed only for file protocols) - */ -int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, - Error **errp) -{ - BlockDriverState *bs = child->bs; - BlockDriver *drv = bs->drv; - int ret; - - assert(child->perm & BLK_PERM_RESIZE); - - /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ - if (!drv) { - error_setg(errp, "No medium inserted"); - return -ENOMEDIUM; - } - if (offset < 0) { - error_setg(errp, "Image size cannot be negative"); - return -EINVAL; - } - - if (!drv->bdrv_truncate) { - if (bs->file && drv->is_filter) { - return bdrv_truncate(bs->file, offset, prealloc, errp); - } - error_setg(errp, "Image format driver does not support resize"); - return -ENOTSUP; - } - if (bs->read_only) { - error_setg(errp, "Image is read-only"); - return -EACCES; - } - - assert(!(bs->open_flags & BDRV_O_INACTIVE)); - - ret = drv->bdrv_truncate(bs, offset, prealloc, errp); - if (ret < 0) { - return ret; - } - ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); - if (ret < 0) { - error_setg_errno(errp, -ret, "Could not refresh total sector count"); - } else { - offset = bs->total_sectors * BDRV_SECTOR_SIZE; - } - bdrv_dirty_bitmap_truncate(bs, offset); - bdrv_parent_cb_resize(bs); - atomic_inc(&bs->write_gen); - return ret; -} - /** * Length of a allocated file in bytes. Sparse files are counted by actual * allocated space. Return < 0 if error or unknown. @@ -4025,6 +4031,14 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs) return QTAILQ_NEXT(bs, node_list); } +BlockDriverState *bdrv_next_all_states(BlockDriverState *bs) +{ + if (!bs) { + return QTAILQ_FIRST(&all_bdrv_states); + } + return QTAILQ_NEXT(bs, bs_list); +} + const char *bdrv_get_node_name(const BlockDriverState *bs) { return bs->node_name; @@ -4936,7 +4950,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) AioContext *ctx = bdrv_get_aio_context(bs); aio_disable_external(ctx); - bdrv_parent_drained_begin(bs, NULL); + bdrv_parent_drained_begin(bs, NULL, false); bdrv_drain(bs); /* ensure there are no in-flight requests */ while (aio_poll(ctx, false)) { @@ -4950,7 +4964,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) */ aio_context_acquire(new_context); bdrv_attach_aio_context(bs, new_context); - bdrv_parent_drained_end(bs, NULL); + bdrv_parent_drained_end(bs, NULL, false); aio_enable_external(ctx); aio_context_release(new_context); } @@ -4996,15 +5010,19 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs, } int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque) + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + Error **errp) { if (!bs->drv) { + error_setg(errp, "Node is ejected"); return -ENOMEDIUM; } if (!bs->drv->bdrv_amend_options) { + error_setg(errp, "Block driver '%s' does not support option amendment", + bs->drv->format_name); return -ENOTSUP; } - return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque); + return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp); } /* This function will be called by the bdrv_recurse_is_first_non_filter method @@ -5134,8 +5152,8 @@ static bool append_open_options(QDict *d, BlockDriverState *bs) continue; } - qobject_incref(qdict_entry_value(entry)); - qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); + qdict_put_obj(d, qdict_entry_key(entry), + qobject_ref(qdict_entry_value(entry))); found_any = true; } @@ -5174,21 +5192,21 @@ void bdrv_refresh_filename(BlockDriverState *bs) * information before refreshing it */ bs->exact_filename[0] = '\0'; if (bs->full_open_options) { - QDECREF(bs->full_open_options); + qobject_unref(bs->full_open_options); bs->full_open_options = NULL; } opts = qdict_new(); append_open_options(opts, bs); drv->bdrv_refresh_filename(bs, opts); - QDECREF(opts); + qobject_unref(opts); } else if (bs->file) { /* Try to reconstruct valid information from the underlying file */ bool has_open_options; bs->exact_filename[0] = '\0'; if (bs->full_open_options) { - QDECREF(bs->full_open_options); + qobject_unref(bs->full_open_options); bs->full_open_options = NULL; } @@ -5207,12 +5225,12 @@ void bdrv_refresh_filename(BlockDriverState *bs) * suffices without querying the (exact_)filename of this BDS. */ if (bs->file->bs->full_open_options) { qdict_put_str(opts, "driver", drv->format_name); - QINCREF(bs->file->bs->full_open_options); - qdict_put(opts, "file", bs->file->bs->full_open_options); + qdict_put(opts, "file", + qobject_ref(bs->file->bs->full_open_options)); bs->full_open_options = opts; } else { - QDECREF(opts); + qobject_unref(opts); } } else if (!bs->full_open_options && qdict_size(bs->options)) { /* There is no underlying file BDS (at least referenced by BDS.file), @@ -5246,7 +5264,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) QString *json = qobject_to_json(QOBJECT(bs->full_open_options)); snprintf(bs->filename, sizeof(bs->filename), "json:%s", qstring_get_str(json)); - QDECREF(json); + qobject_unref(json); } } diff --git a/block/Makefile.objs b/block/Makefile.objs index d644bac60a..c8337bf186 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -5,6 +5,7 @@ block-obj-y += qed-check.o block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o block-obj-y += quorum.o block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o +block-obj-y += blklogwrites.o block-obj-y += block-backend.o snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += file-posix.o @@ -26,7 +27,7 @@ block-obj-y += accounting.o dirty-bitmap.o block-obj-y += write-threshold.o block-obj-y += backup.o block-obj-$(CONFIG_REPLICATION) += replication.o -block-obj-y += throttle.o +block-obj-y += throttle.o copy-on-read.o block-obj-y += crypto.o diff --git a/block/backup.c b/block/backup.c index 453cd62c24..8630d32926 100644 --- a/block/backup.c +++ b/block/backup.c @@ -27,7 +27,6 @@ #include "qemu/error-report.h" #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) -#define SLICE_TIME 100000000ULL /* ns */ typedef struct BackupBlockJob { BlockJob common; @@ -35,10 +34,10 @@ typedef struct BackupBlockJob { /* bitmap for sync=incremental */ BdrvDirtyBitmap *sync_bitmap; MirrorSyncMode sync_mode; - RateLimit limit; BlockdevOnError on_source_error; BlockdevOnError on_target_error; CoRwlock flush_rwlock; + uint64_t len; uint64_t bytes_read; int64_t cluster_size; bool compress; @@ -46,8 +45,14 @@ typedef struct BackupBlockJob { QLIST_HEAD(, CowRequest) inflight_reqs; HBitmap *copy_bitmap; + bool use_copy_range; + int64_t copy_range_size; + + bool serialize_target_writes; } BackupBlockJob; +static const BlockJobDriver backup_job_driver; + /* See if in-flight requests overlap and wait for them to complete */ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, int64_t start, @@ -85,19 +90,104 @@ static void cow_request_end(CowRequest *req) qemu_co_queue_restart_all(&req->wait_queue); } +/* Copy range to target with a bounce buffer and return the bytes copied. If + * error occurred, return a negative error number */ +static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, + int64_t start, + int64_t end, + bool is_write_notifier, + bool *error_is_read, + void **bounce_buffer) +{ + int ret; + struct iovec iov; + QEMUIOVector qiov; + BlockBackend *blk = job->common.blk; + int nbytes; + int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; + int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; + + hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); + nbytes = MIN(job->cluster_size, job->len - start); + if (!*bounce_buffer) { + *bounce_buffer = blk_blockalign(blk, job->cluster_size); + } + iov.iov_base = *bounce_buffer; + iov.iov_len = nbytes; + qemu_iovec_init_external(&qiov, &iov, 1); + + ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags); + if (ret < 0) { + trace_backup_do_cow_read_fail(job, start, ret); + if (error_is_read) { + *error_is_read = true; + } + goto fail; + } + + if (qemu_iovec_is_zero(&qiov)) { + ret = blk_co_pwrite_zeroes(job->target, start, + qiov.size, write_flags | BDRV_REQ_MAY_UNMAP); + } else { + ret = blk_co_pwritev(job->target, start, + qiov.size, &qiov, write_flags | + (job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0)); + } + if (ret < 0) { + trace_backup_do_cow_write_fail(job, start, ret); + if (error_is_read) { + *error_is_read = false; + } + goto fail; + } + + return nbytes; +fail: + hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); + return ret; + +} + +/* Copy range to target and return the bytes copied. If error occurred, return a + * negative error number. */ +static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job, + int64_t start, + int64_t end, + bool is_write_notifier) +{ + int ret; + int nr_clusters; + BlockBackend *blk = job->common.blk; + int nbytes; + int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0; + int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0; + + assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size)); + nbytes = MIN(job->copy_range_size, end - start); + nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size); + hbitmap_reset(job->copy_bitmap, start / job->cluster_size, + nr_clusters); + ret = blk_co_copy_range(blk, start, job->target, start, nbytes, + read_flags, write_flags); + if (ret < 0) { + trace_backup_do_cow_copy_range_fail(job, start, ret); + hbitmap_set(job->copy_bitmap, start / job->cluster_size, + nr_clusters); + return ret; + } + + return nbytes; +} + static int coroutine_fn backup_do_cow(BackupBlockJob *job, int64_t offset, uint64_t bytes, bool *error_is_read, bool is_write_notifier) { - BlockBackend *blk = job->common.blk; CowRequest cow_request; - struct iovec iov; - QEMUIOVector bounce_qiov; - void *bounce_buffer = NULL; int ret = 0; int64_t start, end; /* bytes */ - int n; /* bytes */ + void *bounce_buffer = NULL; qemu_co_rwlock_rdlock(&job->flush_rwlock); @@ -109,60 +199,38 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, wait_for_overlapping_requests(job, start, end); cow_request_begin(&cow_request, job, start, end); - for (; start < end; start += job->cluster_size) { + while (start < end) { if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) { trace_backup_do_cow_skip(job, start); + start += job->cluster_size; continue; /* already copied */ } - hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1); trace_backup_do_cow_process(job, start); - n = MIN(job->cluster_size, job->common.len - start); - - if (!bounce_buffer) { - bounce_buffer = blk_blockalign(blk, job->cluster_size); - } - iov.iov_base = bounce_buffer; - iov.iov_len = n; - qemu_iovec_init_external(&bounce_qiov, &iov, 1); - - ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov, - is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); - if (ret < 0) { - trace_backup_do_cow_read_fail(job, start, ret); - if (error_is_read) { - *error_is_read = true; + if (job->use_copy_range) { + ret = backup_cow_with_offload(job, start, end, is_write_notifier); + if (ret < 0) { + job->use_copy_range = false; } - hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); - goto out; } - - if (buffer_is_zero(iov.iov_base, iov.iov_len)) { - ret = blk_co_pwrite_zeroes(job->target, start, - bounce_qiov.size, BDRV_REQ_MAY_UNMAP); - } else { - ret = blk_co_pwritev(job->target, start, - bounce_qiov.size, &bounce_qiov, - job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); + if (!job->use_copy_range) { + ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier, + error_is_read, &bounce_buffer); } if (ret < 0) { - trace_backup_do_cow_write_fail(job, start, ret); - if (error_is_read) { - *error_is_read = false; - } - hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1); - goto out; + break; } /* Publish progress, guest I/O counts as progress too. Note that the * offset field is an opaque progress value, it is not a disk offset. */ - job->bytes_read += n; - job->common.offset += n; + start += ret; + job->bytes_read += ret; + job_progress_update(&job->common.job, ret); + ret = 0; } -out: if (bounce_buffer) { qemu_vfree(bounce_buffer); } @@ -190,17 +258,6 @@ static int coroutine_fn backup_before_write_notify( return backup_do_cow(job, req->offset, req->bytes, NULL, true); } -static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp) -{ - BackupBlockJob *s = container_of(job, BackupBlockJob, common); - - if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); - return; - } - ratelimit_set_speed(&s->limit, speed, SLICE_TIME); -} - static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) { BdrvDirtyBitmap *bm; @@ -217,25 +274,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) } } -static void backup_commit(BlockJob *job) +static void backup_commit(Job *job) { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); if (s->sync_bitmap) { backup_cleanup_sync_bitmap(s, 0); } } -static void backup_abort(BlockJob *job) +static void backup_abort(Job *job) { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); if (s->sync_bitmap) { backup_cleanup_sync_bitmap(s, -1); } } -static void backup_clean(BlockJob *job) +static void backup_clean(Job *job) { - BackupBlockJob *s = container_of(job, BackupBlockJob, common); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); assert(s->target); blk_unref(s->target); s->target = NULL; @@ -253,7 +310,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); int64_t len; - assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); + assert(block_job_driver(job) == &backup_job_driver); if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { error_setg(errp, "The backup job only supports block checkpoint in" @@ -261,7 +318,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) return; } - len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size); + len = DIV_ROUND_UP(backup_job->len, backup_job->cluster_size); hbitmap_set(backup_job->copy_bitmap, 0, len); } @@ -271,7 +328,7 @@ void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); int64_t start, end; - assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); + assert(block_job_driver(job) == &backup_job_driver); start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); @@ -284,7 +341,7 @@ void backup_cow_request_begin(CowRequest *req, BlockJob *job, BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); int64_t start, end; - assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); + assert(block_job_driver(job) == &backup_job_driver); start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); @@ -327,33 +384,29 @@ typedef struct { int ret; } BackupCompleteData; -static void backup_complete(BlockJob *job, void *opaque) +static void backup_complete(Job *job, void *opaque) { BackupCompleteData *data = opaque; - block_job_completed(job, data->ret); + job_completed(job, data->ret, NULL); g_free(data); } static bool coroutine_fn yield_and_check(BackupBlockJob *job) { - if (block_job_is_cancelled(&job->common)) { + uint64_t delay_ns; + + if (job_is_cancelled(&job->common.job)) { return true; } - /* we need to yield so that bdrv_drain_all() returns. - * (without, VM does not reboot) - */ - if (job->common.speed) { - uint64_t delay_ns = ratelimit_calculate_delay(&job->limit, - job->bytes_read); - job->bytes_read = 0; - block_job_sleep_ns(&job->common, delay_ns); - } else { - block_job_sleep_ns(&job->common, 0); - } + /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can + * return. Without a yield, the VM would not reboot. */ + delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read); + job->bytes_read = 0; + job_sleep_ns(&job->common.job, delay_ns); - if (block_job_is_cancelled(&job->common)) { + if (job_is_cancelled(&job->common.job)) { return true; } @@ -368,7 +421,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) HBitmapIter hbi; hbitmap_iter_init(&hbi, job->copy_bitmap, 0); - while ((cluster = hbitmap_iter_next(&hbi)) != -1) { + while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) { do { if (yield_and_check(job)) { return 0; @@ -420,8 +473,9 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); } - job->common.offset = job->common.len - - hbitmap_count(job->copy_bitmap) * job->cluster_size; + /* TODO job_progress_set_remaining() would make more sense */ + job_progress_update(&job->common.job, + job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size); bdrv_dirty_iter_free(dbi); } @@ -437,7 +491,9 @@ static void coroutine_fn backup_run(void *opaque) QLIST_INIT(&job->inflight_reqs); qemu_co_rwlock_init(&job->flush_rwlock); - nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size); + nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); + job_progress_set_remaining(&job->common.job, job->len); + job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { backup_incremental_init_copy_bitmap(job); @@ -452,16 +508,16 @@ static void coroutine_fn backup_run(void *opaque) if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { /* All bits are set in copy_bitmap to allow any cluster to be copied. * This does not actually require them to be copied. */ - while (!block_job_is_cancelled(&job->common)) { + while (!job_is_cancelled(&job->common.job)) { /* Yield until the job is cancelled. We just let our before_write * notify callback service CoW requests. */ - block_job_yield(&job->common); + job_yield(&job->common.job); } } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { ret = backup_run_incremental(job); } else { /* Both FULL and TOP SYNC_MODE's require copying.. */ - for (offset = 0; offset < job->common.len; + for (offset = 0; offset < job->len; offset += job->cluster_size) { bool error_is_read; int alloced = 0; @@ -530,17 +586,21 @@ static void coroutine_fn backup_run(void *opaque) data = g_malloc(sizeof(*data)); data->ret = ret; - block_job_defer_to_main_loop(&job->common, backup_complete, data); + job_defer_to_main_loop(&job->common.job, backup_complete, data); } static const BlockJobDriver backup_job_driver = { - .instance_size = sizeof(BackupBlockJob), - .job_type = BLOCK_JOB_TYPE_BACKUP, - .start = backup_run, - .set_speed = backup_set_speed, - .commit = backup_commit, - .abort = backup_abort, - .clean = backup_clean, + .job_driver = { + .instance_size = sizeof(BackupBlockJob), + .job_type = JOB_TYPE_BACKUP, + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = backup_run, + .commit = backup_commit, + .abort = backup_abort, + .clean = backup_clean, + }, .attached_aio_context = backup_attached_aio_context, .drain = backup_drain, }; @@ -553,7 +613,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, - BlockJobTxn *txn, Error **errp) + JobTxn *txn, Error **errp) { int64_t len; BlockDriverInfo bdi; @@ -620,7 +680,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, goto error; } - /* job->common.len is fixed, so we can't allow resize */ + /* job->len is fixed, so we can't allow resize */ job = block_job_create(job_id, &backup_job_driver, txn, bs, BLK_PERM_CONSISTENT_READ, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | @@ -646,6 +706,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, sync_bitmap : NULL; job->compress = compress; + /* Detect image-fleecing (and similar) schemes */ + job->serialize_target_writes = bdrv_chain_contains(target, bs); + /* If there is no backing file on the target, we cannot rely on COW if our * backup cluster size is smaller than the target cluster size. Even for * targets with a backing file, try to avoid COW if possible. */ @@ -672,11 +735,17 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, } else { job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); } + job->use_copy_range = true; + job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk), + blk_get_max_transfer(job->target)); + job->copy_range_size = MAX(job->cluster_size, + QEMU_ALIGN_UP(job->copy_range_size, + job->cluster_size)); /* Required permissions are already taken with target's blk_new() */ block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); - job->common.len = len; + job->len = len; return &job->common; @@ -685,8 +754,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); } if (job) { - backup_clean(&job->common); - block_job_early_fail(&job->common); + backup_clean(&job->common.job); + job_early_fail(&job->common.job); } return NULL; diff --git a/block/blkdebug.c b/block/blkdebug.c index 589712475a..0759452925 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options, if (c != filename) { QString *config_path; - config_path = qstring_from_substr(filename, 0, c - filename - 1); + config_path = qstring_from_substr(filename, 0, c - filename); qdict_put(options, "config", config_path); } @@ -398,10 +398,11 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, goto out; } - bs->supported_write_flags = BDRV_REQ_FUA & - bs->file->bs->supported_write_flags; - bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & - bs->file->bs->supported_zero_flags; + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & + bs->file->bs->supported_zero_flags); ret = -EINVAL; /* Set alignment overrides */ @@ -624,7 +625,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs, return err; } - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); + return bdrv_co_pdiscard(bs->file, offset, bytes); } static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, @@ -845,13 +846,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) opts = qdict_new(); qdict_put_str(opts, "driver", "blkdebug"); - QINCREF(bs->file->bs->full_open_options); - qdict_put(opts, "image", bs->file->bs->full_open_options); + qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options)); for (e = qdict_first(options); e; e = qdict_next(options, e)) { if (strcmp(qdict_entry_key(e), "x-image")) { - qobject_incref(qdict_entry_value(e)); - qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); + qdict_put_obj(opts, qdict_entry_key(e), + qobject_ref(qdict_entry_value(e))); } } diff --git a/block/blklogwrites.c b/block/blklogwrites.c new file mode 100644 index 0000000000..ff98cd5533 --- /dev/null +++ b/block/blklogwrites.c @@ -0,0 +1,550 @@ +/* + * Write logging blk driver based on blkverify and blkdebug. + * + * Copyright (c) 2017 Tuomas Tynkkynen + * Copyright (c) 2018 Aapo Vienamo + * Copyright (c) 2018 Ari Sundholm + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/sockets.h" /* for EINPROGRESS on Windows */ +#include "block/block_int.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" +#include "qemu/cutils.h" +#include "qemu/option.h" + +/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */ + +#define LOG_FLUSH_FLAG (1 << 0) +#define LOG_FUA_FLAG (1 << 1) +#define LOG_DISCARD_FLAG (1 << 2) +#define LOG_MARK_FLAG (1 << 3) +#define LOG_FLAG_MASK (LOG_FLUSH_FLAG \ + | LOG_FUA_FLAG \ + | LOG_DISCARD_FLAG \ + | LOG_MARK_FLAG) + +#define WRITE_LOG_VERSION 1ULL +#define WRITE_LOG_MAGIC 0x6a736677736872ULL + +/* All fields are little-endian. */ +struct log_write_super { + uint64_t magic; + uint64_t version; + uint64_t nr_entries; + uint32_t sectorsize; +} QEMU_PACKED; + +struct log_write_entry { + uint64_t sector; + uint64_t nr_sectors; + uint64_t flags; + uint64_t data_len; +} QEMU_PACKED; + +/* End of disk format structures. */ + +typedef struct { + BdrvChild *log_file; + uint32_t sectorsize; + uint32_t sectorbits; + uint64_t cur_log_sector; + uint64_t nr_entries; + uint64_t update_interval; +} BDRVBlkLogWritesState; + +static QemuOptsList runtime_opts = { + .name = "blklogwrites", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "log-append", + .type = QEMU_OPT_BOOL, + .help = "Append to an existing log", + }, + { + .name = "log-sector-size", + .type = QEMU_OPT_SIZE, + .help = "Log sector size", + }, + { + .name = "log-super-update-interval", + .type = QEMU_OPT_NUMBER, + .help = "Log superblock update interval (# of write requests)", + }, + { /* end of list */ } + }, +}; + +static inline uint32_t blk_log_writes_log2(uint32_t value) +{ + assert(value > 0); + return 31 - clz32(value); +} + +static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size) +{ + return is_power_of_2(sector_size) && + sector_size >= sizeof(struct log_write_super) && + sector_size >= sizeof(struct log_write_entry) && + sector_size < (1ull << 24); +} + +static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log, + uint32_t sector_size, + uint64_t nr_entries, + Error **errp) +{ + uint64_t cur_sector = 1; + uint64_t cur_idx = 0; + uint32_t sector_bits = blk_log_writes_log2(sector_size); + struct log_write_entry cur_entry; + + while (cur_idx < nr_entries) { + int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry, + sizeof(cur_entry)); + if (read_ret < 0) { + error_setg_errno(errp, -read_ret, + "Failed to read log entry %"PRIu64, cur_idx); + return (uint64_t)-1ull; + } + + if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) { + error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64, + le64_to_cpu(cur_entry.flags), cur_idx); + return (uint64_t)-1ull; + } + + /* Account for the sector of the entry itself */ + ++cur_sector; + + /* + * Account for the data of the write. + * For discards, this data is not present. + */ + if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) { + cur_sector += le64_to_cpu(cur_entry.nr_sectors); + } + + ++cur_idx; + } + + return cur_sector; +} + +static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + BDRVBlkLogWritesState *s = bs->opaque; + QemuOpts *opts; + Error *local_err = NULL; + int ret; + uint64_t log_sector_size; + bool log_append; + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + goto fail; + } + + /* Open the file */ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, + &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + goto fail; + } + + /* Open the log file */ + s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false, + &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + goto fail; + } + + log_append = qemu_opt_get_bool(opts, "log-append", false); + + if (log_append) { + struct log_write_super log_sb = { 0, 0, 0, 0 }; + + if (qemu_opt_find(opts, "log-sector-size")) { + ret = -EINVAL; + error_setg(errp, "log-append and log-sector-size are mutually " + "exclusive"); + goto fail_log; + } + + /* Read log superblock or fake one for an empty log */ + if (!bdrv_getlength(s->log_file->bs)) { + log_sb.magic = cpu_to_le64(WRITE_LOG_MAGIC); + log_sb.version = cpu_to_le64(WRITE_LOG_VERSION); + log_sb.nr_entries = cpu_to_le64(0); + log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE); + } else { + ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb)); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not read log superblock"); + goto fail_log; + } + } + + if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) { + ret = -EINVAL; + error_setg(errp, "Invalid log superblock magic"); + goto fail_log; + } + + if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) { + ret = -EINVAL; + error_setg(errp, "Unsupported log version %"PRIu64, + le64_to_cpu(log_sb.version)); + goto fail_log; + } + + log_sector_size = le32_to_cpu(log_sb.sectorsize); + s->cur_log_sector = 1; + s->nr_entries = 0; + + if (blk_log_writes_sector_size_valid(log_sector_size)) { + s->cur_log_sector = + blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size, + le64_to_cpu(log_sb.nr_entries), &local_err); + if (local_err) { + ret = -EINVAL; + error_propagate(errp, local_err); + goto fail_log; + } + + s->nr_entries = le64_to_cpu(log_sb.nr_entries); + } + } else { + log_sector_size = qemu_opt_get_size(opts, "log-sector-size", + BDRV_SECTOR_SIZE); + s->cur_log_sector = 1; + s->nr_entries = 0; + } + + if (!blk_log_writes_sector_size_valid(log_sector_size)) { + ret = -EINVAL; + error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size); + goto fail_log; + } + + s->sectorsize = log_sector_size; + s->sectorbits = blk_log_writes_log2(log_sector_size); + s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval", + 4096); + if (!s->update_interval) { + ret = -EINVAL; + error_setg(errp, "Invalid log superblock update interval %"PRIu64, + s->update_interval); + goto fail_log; + } + + ret = 0; +fail_log: + if (ret < 0) { + bdrv_unref_child(bs, s->log_file); + s->log_file = NULL; + } +fail: + if (ret < 0) { + bdrv_unref_child(bs, bs->file); + bs->file = NULL; + } + qemu_opts_del(opts); + return ret; +} + +static void blk_log_writes_close(BlockDriverState *bs) +{ + BDRVBlkLogWritesState *s = bs->opaque; + + bdrv_unref_child(bs, s->log_file); + s->log_file = NULL; +} + +static int64_t blk_log_writes_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file->bs); +} + +static void blk_log_writes_refresh_filename(BlockDriverState *bs, + QDict *options) +{ + BDRVBlkLogWritesState *s = bs->opaque; + + /* bs->file->bs has already been refreshed */ + bdrv_refresh_filename(s->log_file->bs); + + if (bs->file->bs->full_open_options + && s->log_file->bs->full_open_options) + { + QDict *opts = qdict_new(); + qdict_put_str(opts, "driver", "blklogwrites"); + + qobject_ref(bs->file->bs->full_open_options); + qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options)); + qobject_ref(s->log_file->bs->full_open_options); + qdict_put_obj(opts, "log", + QOBJECT(s->log_file->bs->full_open_options)); + qdict_put_int(opts, "log-sector-size", s->sectorsize); + + bs->full_open_options = opts; + } +} + +static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *ro_q, + uint64_t perm, uint64_t shrd, + uint64_t *nperm, uint64_t *nshrd) +{ + if (!c) { + *nperm = perm & DEFAULT_PERM_PASSTHROUGH; + *nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED; + return; + } + + if (!strcmp(c->name, "log")) { + bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); + } else { + bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); + } +} + +static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp) +{ + BDRVBlkLogWritesState *s = bs->opaque; + bs->bl.request_alignment = s->sectorsize; +} + +static int coroutine_fn +blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); +} + +typedef struct BlkLogWritesFileReq { + BlockDriverState *bs; + uint64_t offset; + uint64_t bytes; + int file_flags; + QEMUIOVector *qiov; + int (*func)(struct BlkLogWritesFileReq *r); + int file_ret; +} BlkLogWritesFileReq; + +typedef struct { + BlockDriverState *bs; + QEMUIOVector *qiov; + struct log_write_entry entry; + uint64_t zero_size; + int log_ret; +} BlkLogWritesLogReq; + +static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr) +{ + BDRVBlkLogWritesState *s = lr->bs->opaque; + uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits; + + s->nr_entries++; + s->cur_log_sector += + ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits; + + lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size, + lr->qiov, 0); + + /* Logging for the "write zeroes" operation */ + if (lr->log_ret == 0 && lr->zero_size) { + cur_log_offset = s->cur_log_sector << s->sectorbits; + s->cur_log_sector += + ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits; + + lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset, + lr->zero_size, 0); + } + + /* Update super block on flush or every update interval */ + if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG) + || (s->nr_entries % s->update_interval == 0))) + { + struct log_write_super super = { + .magic = cpu_to_le64(WRITE_LOG_MAGIC), + .version = cpu_to_le64(WRITE_LOG_VERSION), + .nr_entries = cpu_to_le64(s->nr_entries), + .sectorsize = cpu_to_le32(s->sectorsize), + }; + void *zeroes = g_malloc0(s->sectorsize - sizeof(super)); + QEMUIOVector qiov; + + qemu_iovec_init(&qiov, 2); + qemu_iovec_add(&qiov, &super, sizeof(super)); + qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super)); + + lr->log_ret = + bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0); + if (lr->log_ret == 0) { + lr->log_ret = bdrv_co_flush(s->log_file->bs); + } + qemu_iovec_destroy(&qiov); + g_free(zeroes); + } +} + +static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr) +{ + fr->file_ret = fr->func(fr); +} + +static int coroutine_fn +blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + int (*file_func)(BlkLogWritesFileReq *r), + uint64_t entry_flags, bool is_zero_write) +{ + QEMUIOVector log_qiov; + size_t niov = qiov ? qiov->niov : 0; + BDRVBlkLogWritesState *s = bs->opaque; + BlkLogWritesFileReq fr = { + .bs = bs, + .offset = offset, + .bytes = bytes, + .file_flags = flags, + .qiov = qiov, + .func = file_func, + }; + BlkLogWritesLogReq lr = { + .bs = bs, + .qiov = &log_qiov, + .entry = { + .sector = cpu_to_le64(offset >> s->sectorbits), + .nr_sectors = cpu_to_le64(bytes >> s->sectorbits), + .flags = cpu_to_le64(entry_flags), + .data_len = 0, + }, + .zero_size = is_zero_write ? bytes : 0, + }; + void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry)); + + assert((1 << s->sectorbits) == s->sectorsize); + assert(bs->bl.request_alignment == s->sectorsize); + assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment)); + assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment)); + + qemu_iovec_init(&log_qiov, niov + 2); + qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry)); + qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry)); + if (qiov) { + qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size); + } + + blk_log_writes_co_do_file(&fr); + blk_log_writes_co_do_log(&lr); + + qemu_iovec_destroy(&log_qiov); + g_free(zeroes); + + if (lr.log_ret < 0) { + return lr.log_ret; + } + + return fr.file_ret; +} + +static int coroutine_fn +blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr) +{ + return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes, + fr->qiov, fr->file_flags); +} + +static int coroutine_fn +blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr) +{ + return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes, + fr->file_flags); +} + +static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr) +{ + return bdrv_co_flush(fr->bs->file->bs); +} + +static int coroutine_fn +blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr) +{ + return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes); +} + +static int coroutine_fn +blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + return blk_log_writes_co_log(bs, offset, bytes, qiov, flags, + blk_log_writes_co_do_file_pwritev, 0, false); +} + +static int coroutine_fn +blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, + BdrvRequestFlags flags) +{ + return blk_log_writes_co_log(bs, offset, bytes, NULL, flags, + blk_log_writes_co_do_file_pwrite_zeroes, 0, + true); +} + +static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs) +{ + return blk_log_writes_co_log(bs, 0, 0, NULL, 0, + blk_log_writes_co_do_file_flush, + LOG_FLUSH_FLAG, false); +} + +static int coroutine_fn +blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) +{ + return blk_log_writes_co_log(bs, offset, count, NULL, 0, + blk_log_writes_co_do_file_pdiscard, + LOG_DISCARD_FLAG, false); +} + +static BlockDriver bdrv_blk_log_writes = { + .format_name = "blklogwrites", + .instance_size = sizeof(BDRVBlkLogWritesState), + + .bdrv_open = blk_log_writes_open, + .bdrv_close = blk_log_writes_close, + .bdrv_getlength = blk_log_writes_getlength, + .bdrv_refresh_filename = blk_log_writes_refresh_filename, + .bdrv_child_perm = blk_log_writes_child_perm, + .bdrv_refresh_limits = blk_log_writes_refresh_limits, + + .bdrv_co_preadv = blk_log_writes_co_preadv, + .bdrv_co_pwritev = blk_log_writes_co_pwritev, + .bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes, + .bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk, + .bdrv_co_pdiscard = blk_log_writes_co_pdiscard, + .bdrv_co_block_status = bdrv_co_block_status_from_file, + + .is_filter = true, +}; + +static void bdrv_blk_log_writes_init(void) +{ + bdrv_register(&bdrv_blk_log_writes); +} + +block_init(bdrv_blk_log_writes_init); diff --git a/block/blkreplay.c b/block/blkreplay.c index fe5a9b4a98..766150ade6 100755 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -35,6 +35,9 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; + ret = 0; fail: return ret; @@ -110,7 +113,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { uint64_t reqid = blkreplay_next_id(); - int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes); + int ret = bdrv_co_pdiscard(bs->file, offset, bytes); block_request_create(reqid, bs, qemu_coroutine_self()); qemu_coroutine_yield(); diff --git a/block/blkverify.c b/block/blkverify.c index 331365be33..89bf4386e3 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options, } /* TODO Implement option pass-through and set raw.filename here */ - raw_path = qstring_from_substr(filename, 0, c - filename - 1); + raw_path = qstring_from_substr(filename, 0, c - filename); qdict_put(options, "x-raw", raw_path); /* TODO Allow multi-level nesting and set file.filename here */ @@ -141,6 +141,9 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; + ret = 0; fail: qemu_opts_del(opts); @@ -291,10 +294,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) QDict *opts = qdict_new(); qdict_put_str(opts, "driver", "blkverify"); - QINCREF(bs->file->bs->full_open_options); - qdict_put(opts, "raw", bs->file->bs->full_open_options); - QINCREF(s->test_file->bs->full_open_options); - qdict_put(opts, "test", s->test_file->bs->full_open_options); + qdict_put(opts, "raw", + qobject_ref(bs->file->bs->full_open_options)); + qdict_put(opts, "test", + qobject_ref(s->test_file->bs->full_open_options)); bs->full_open_options = opts; } diff --git a/block/block-backend.c b/block/block-backend.c index 681b240b12..f2f75a977d 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -768,6 +768,11 @@ void blk_remove_bs(BlockBackend *blk) blk_update_root_state(blk); + /* bdrv_root_unref_child() will cause blk->root to become stale and may + * switch to a completion coroutine later on. Let's drain all I/O here + * to avoid that and a potential QEMU crash. + */ + blk_drain(blk); bdrv_root_unref_child(blk->root); blk->root = NULL; } @@ -1555,7 +1560,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes) return ret; } - return bdrv_co_pdiscard(blk_bs(blk), offset, bytes); + return bdrv_co_pdiscard(blk->root, offset, bytes); } int blk_co_flush(BlockBackend *blk) @@ -1865,13 +1870,7 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason) AioContext *blk_get_aio_context(BlockBackend *blk) { - BlockDriverState *bs = blk_bs(blk); - - if (bs) { - return bdrv_get_aio_context(bs); - } else { - return qemu_get_aio_context(); - } + return bdrv_get_aio_context(blk_bs(blk)); } static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) @@ -2217,3 +2216,22 @@ void blk_unregister_buf(BlockBackend *blk, void *host) { bdrv_unregister_buf(blk_bs(blk), host); } + +int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, + BlockBackend *blk_out, int64_t off_out, + int bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + int r; + r = blk_check_byte_request(blk_in, off_in, bytes); + if (r) { + return r; + } + r = blk_check_byte_request(blk_out, off_out, bytes); + if (r) { + return r; + } + return bdrv_co_copy_range(blk_in->root, off_in, + blk_out->root, off_out, + bytes, read_flags, write_flags); +} diff --git a/block/commit.c b/block/commit.c index 1432baeef4..e1814d9693 100644 --- a/block/commit.c +++ b/block/commit.c @@ -31,11 +31,8 @@ enum { COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ }; -#define SLICE_TIME 100000000ULL /* ns */ - typedef struct CommitBlockJob { BlockJob common; - RateLimit limit; BlockDriverState *commit_top_bs; BlockBackend *top; BlockBackend *base; @@ -75,9 +72,10 @@ typedef struct { int ret; } CommitCompleteData; -static void commit_complete(BlockJob *job, void *opaque) +static void commit_complete(Job *job, void *opaque) { - CommitBlockJob *s = container_of(job, CommitBlockJob, common); + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); + BlockJob *bjob = &s->common; CommitCompleteData *data = opaque; BlockDriverState *top = blk_bs(s->top); BlockDriverState *base = blk_bs(s->base); @@ -93,7 +91,7 @@ static void commit_complete(BlockJob *job, void *opaque) * the normal backing chain can be restored. */ blk_unref(s->base); - if (!block_job_is_cancelled(&s->common) && ret == 0) { + if (!job_is_cancelled(job) && ret == 0) { /* success */ ret = bdrv_drop_intermediate(s->commit_top_bs, base, s->backing_file_str); @@ -114,12 +112,12 @@ static void commit_complete(BlockJob *job, void *opaque) blk_unref(s->top); /* If there is more than one reference to the job (e.g. if called from - * block_job_finish_sync()), block_job_completed() won't free it and - * therefore the blockers on the intermediate nodes remain. This would - * cause bdrv_set_backing_hd() to fail. */ - block_job_remove_all_bdrv(job); + * job_finish_sync()), job_completed() won't free it and therefore the + * blockers on the intermediate nodes remain. This would cause + * bdrv_set_backing_hd() to fail. */ + block_job_remove_all_bdrv(bjob); - block_job_completed(&s->common, ret); + job_completed(job, ret, NULL); g_free(data); /* If bdrv_drop_intermediate() didn't already do that, remove the commit @@ -146,21 +144,21 @@ static void coroutine_fn commit_run(void *opaque) int64_t n = 0; /* bytes */ void *buf = NULL; int bytes_written = 0; - int64_t base_len; + int64_t len, base_len; - ret = s->common.len = blk_getlength(s->top); - - if (s->common.len < 0) { + ret = len = blk_getlength(s->top); + if (len < 0) { goto out; } + job_progress_set_remaining(&s->common.job, len); ret = base_len = blk_getlength(s->base); if (base_len < 0) { goto out; } - if (base_len < s->common.len) { - ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL); + if (base_len < len) { + ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL); if (ret) { goto out; } @@ -168,14 +166,14 @@ static void coroutine_fn commit_run(void *opaque) buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); - for (offset = 0; offset < s->common.len; offset += n) { + for (offset = 0; offset < len; offset += n) { bool copy; /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. */ - block_job_sleep_ns(&s->common, delay_ns); - if (block_job_is_cancelled(&s->common)) { + job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job)) { break; } /* Copy if allocated above the base */ @@ -198,10 +196,10 @@ static void coroutine_fn commit_run(void *opaque) } } /* Publish progress */ - s->common.offset += n; + job_progress_update(&s->common.job, n); - if (copy && s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, n); + if (copy) { + delay_ns = block_job_ratelimit_get_delay(&s->common, n); } else { delay_ns = 0; } @@ -214,25 +212,18 @@ out: data = g_malloc(sizeof(*data)); data->ret = ret; - block_job_defer_to_main_loop(&s->common, commit_complete, data); -} - -static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) -{ - CommitBlockJob *s = container_of(job, CommitBlockJob, common); - - if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); - return; - } - ratelimit_set_speed(&s->limit, speed, SLICE_TIME); + job_defer_to_main_loop(&s->common.job, commit_complete, data); } static const BlockJobDriver commit_job_driver = { - .instance_size = sizeof(CommitBlockJob), - .job_type = BLOCK_JOB_TYPE_COMMIT, - .set_speed = commit_set_speed, - .start = commit_run, + .job_driver = { + .instance_size = sizeof(CommitBlockJob), + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = commit_run, + }, }; static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, @@ -292,7 +283,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, } s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, - speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); + speed, JOB_DEFAULT, NULL, NULL, errp); if (!s) { return; } @@ -382,7 +373,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, s->on_error = on_error; trace_commit_start(bs, base, top, s); - block_job_start(&s->common); + job_start(&s->common.job); return; fail: @@ -395,7 +386,7 @@ fail: if (commit_top_bs) { bdrv_replace_node(commit_top_bs, top, &error_abort); } - block_job_early_fail(&s->common); + job_early_fail(&s->common.job); } diff --git a/block/copy-on-read.c b/block/copy-on-read.c new file mode 100644 index 0000000000..a19164f9eb --- /dev/null +++ b/block/copy-on-read.c @@ -0,0 +1,173 @@ +/* + * Copy-on-read filter block driver + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Author: + * Max Reitz + * + * 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 or + * (at your option) version 3 of the License. + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "block/block_int.h" + + +static int cor_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) +{ + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, + errp); + if (!bs->file) { + return -EINVAL; + } + + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & + bs->file->bs->supported_write_flags); + + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & + bs->file->bs->supported_zero_flags); + + return 0; +} + + +static void cor_close(BlockDriverState *bs) +{ +} + + +#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ + | BLK_PERM_WRITE \ + | BLK_PERM_RESIZE) +#define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH) + +static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + if (c == NULL) { + *nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED; + *nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED; + return; + } + + *nperm = (perm & PERM_PASSTHROUGH) | + (c->perm & PERM_UNCHANGED); + *nshared = (shared & PERM_PASSTHROUGH) | + (c->shared_perm & PERM_UNCHANGED); +} + + +static int64_t cor_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file->bs); +} + + +static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) +{ + return bdrv_co_truncate(bs->file, offset, prealloc, errp); +} + + +static int coroutine_fn cor_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + return bdrv_co_preadv(bs->file, offset, bytes, qiov, + flags | BDRV_REQ_COPY_ON_READ); +} + + +static int coroutine_fn cor_co_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + + return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); +} + + +static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs, + int64_t offset, int bytes, + BdrvRequestFlags flags) +{ + return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); +} + + +static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs, + int64_t offset, int bytes) +{ + return bdrv_co_pdiscard(bs->file, offset, bytes); +} + + +static void cor_eject(BlockDriverState *bs, bool eject_flag) +{ + bdrv_eject(bs->file->bs, eject_flag); +} + + +static void cor_lock_medium(BlockDriverState *bs, bool locked) +{ + bdrv_lock_medium(bs->file->bs, locked); +} + + +static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, + BlockDriverState *candidate) +{ + return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); +} + + +BlockDriver bdrv_copy_on_read = { + .format_name = "copy-on-read", + + .bdrv_open = cor_open, + .bdrv_close = cor_close, + .bdrv_child_perm = cor_child_perm, + + .bdrv_getlength = cor_getlength, + .bdrv_co_truncate = cor_co_truncate, + + .bdrv_co_preadv = cor_co_preadv, + .bdrv_co_pwritev = cor_co_pwritev, + .bdrv_co_pwrite_zeroes = cor_co_pwrite_zeroes, + .bdrv_co_pdiscard = cor_co_pdiscard, + + .bdrv_eject = cor_eject, + .bdrv_lock_medium = cor_lock_medium, + + .bdrv_co_block_status = bdrv_co_block_status_from_file, + + .bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter, + + .has_variable_length = true, + .is_filter = true, +}; + +static void bdrv_copy_on_read_init(void) +{ + bdrv_register(&bdrv_copy_on_read); +} + +block_init(bdrv_copy_on_read_init); diff --git a/block/create.c b/block/create.c index 8bd8a03719..915cd41bcc 100644 --- a/block/create.c +++ b/block/create.c @@ -24,28 +24,51 @@ #include "qemu/osdep.h" #include "block/block_int.h" +#include "qemu/job.h" #include "qapi/qapi-commands-block-core.h" +#include "qapi/qapi-visit-block-core.h" +#include "qapi/clone-visitor.h" #include "qapi/error.h" -typedef struct BlockdevCreateCo { +typedef struct BlockdevCreateJob { + Job common; BlockDriver *drv; BlockdevCreateOptions *opts; int ret; - Error **errp; -} BlockdevCreateCo; + Error *err; +} BlockdevCreateJob; -static void coroutine_fn bdrv_co_create_co_entry(void *opaque) +static void blockdev_create_complete(Job *job, void *opaque) { - BlockdevCreateCo *cco = opaque; - cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp); + BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); + + job_completed(job, s->ret, s->err); } -void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) +static void coroutine_fn blockdev_create_run(void *opaque) { + BlockdevCreateJob *s = opaque; + + job_progress_set_remaining(&s->common, 1); + s->ret = s->drv->bdrv_co_create(s->opts, &s->err); + job_progress_update(&s->common, 1); + + qapi_free_BlockdevCreateOptions(s->opts); + job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); +} + +static const JobDriver blockdev_create_job_driver = { + .instance_size = sizeof(BlockdevCreateJob), + .job_type = JOB_TYPE_CREATE, + .start = blockdev_create_run, +}; + +void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, + Error **errp) +{ + BlockdevCreateJob *s; const char *fmt = BlockdevDriver_str(options->driver); BlockDriver *drv = bdrv_find_format(fmt); - Coroutine *co; - BlockdevCreateCo cco; /* If the driver is in the schema, we know that it exists. But it may not * be whitelisted. */ @@ -55,22 +78,24 @@ void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) return; } - /* Call callback if it exists */ + /* Error out if the driver doesn't support .bdrv_co_create */ if (!drv->bdrv_co_create) { error_setg(errp, "Driver does not support blockdev-create"); return; } - cco = (BlockdevCreateCo) { - .drv = drv, - .opts = options, - .ret = -EINPROGRESS, - .errp = errp, - }; - - co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); - qemu_coroutine_enter(co); - while (cco.ret == -EINPROGRESS) { - aio_poll(qemu_get_aio_context(), true); + /* Create the block job */ + /* TODO Running in the main context. Block drivers need to error out or add + * locking when they use a BDS in a different AioContext. */ + s = job_create(job_id, &blockdev_create_job_driver, NULL, + qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS, + NULL, NULL, errp); + if (!s) { + return; } + + s->drv = drv, + s->opts = QAPI_CLONE(BlockdevCreateOptions, options), + + job_start(&s->common); } diff --git a/block/crypto.c b/block/crypto.c index bc6c7e3795..146d81c90a 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -21,15 +21,15 @@ #include "qemu/osdep.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "crypto/block.h" #include "qapi/opts-visitor.h" #include "qapi/qapi-visit-crypto.h" -#include "qapi/qmp/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" #include "qemu/option.h" -#include "block/crypto.h" +#include "crypto.h" typedef struct BlockCrypto BlockCrypto; @@ -148,102 +148,36 @@ static QemuOptsList block_crypto_create_opts_luks = { QCryptoBlockOpenOptions * -block_crypto_open_opts_init(QCryptoBlockFormat format, - QDict *opts, - Error **errp) +block_crypto_open_opts_init(QDict *opts, Error **errp) { Visitor *v; - QCryptoBlockOpenOptions *ret = NULL; - Error *local_err = NULL; + QCryptoBlockOpenOptions *ret; - ret = g_new0(QCryptoBlockOpenOptions, 1); - ret->format = format; - - v = qobject_input_visitor_new_keyval(QOBJECT(opts)); - - visit_start_struct(v, NULL, NULL, 0, &local_err); - if (local_err) { - goto out; + v = qobject_input_visitor_new_flat_confused(opts, errp); + if (!v) { + return NULL; } - switch (format) { - case Q_CRYPTO_BLOCK_FORMAT_LUKS: - visit_type_QCryptoBlockOptionsLUKS_members( - v, &ret->u.luks, &local_err); - break; + visit_type_QCryptoBlockOpenOptions(v, NULL, &ret, errp); - case Q_CRYPTO_BLOCK_FORMAT_QCOW: - visit_type_QCryptoBlockOptionsQCow_members( - v, &ret->u.qcow, &local_err); - break; - - default: - error_setg(&local_err, "Unsupported block format %d", format); - break; - } - if (!local_err) { - visit_check_struct(v, &local_err); - } - - visit_end_struct(v, NULL); - - out: - if (local_err) { - error_propagate(errp, local_err); - qapi_free_QCryptoBlockOpenOptions(ret); - ret = NULL; - } visit_free(v); return ret; } QCryptoBlockCreateOptions * -block_crypto_create_opts_init(QCryptoBlockFormat format, - QDict *opts, - Error **errp) +block_crypto_create_opts_init(QDict *opts, Error **errp) { Visitor *v; - QCryptoBlockCreateOptions *ret = NULL; - Error *local_err = NULL; + QCryptoBlockCreateOptions *ret; - ret = g_new0(QCryptoBlockCreateOptions, 1); - ret->format = format; - - v = qobject_input_visitor_new_keyval(QOBJECT(opts)); - - visit_start_struct(v, NULL, NULL, 0, &local_err); - if (local_err) { - goto out; + v = qobject_input_visitor_new_flat_confused(opts, errp); + if (!v) { + return NULL; } - switch (format) { - case Q_CRYPTO_BLOCK_FORMAT_LUKS: - visit_type_QCryptoBlockCreateOptionsLUKS_members( - v, &ret->u.luks, &local_err); - break; + visit_type_QCryptoBlockCreateOptions(v, NULL, &ret, errp); - case Q_CRYPTO_BLOCK_FORMAT_QCOW: - visit_type_QCryptoBlockOptionsQCow_members( - v, &ret->u.qcow, &local_err); - break; - - default: - error_setg(&local_err, "Unsupported block format %d", format); - break; - } - if (!local_err) { - visit_check_struct(v, &local_err); - } - - visit_end_struct(v, NULL); - - out: - if (local_err) { - error_propagate(errp, local_err); - qapi_free_QCryptoBlockCreateOptions(ret); - ret = NULL; - } visit_free(v); return ret; } @@ -281,8 +215,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, } cryptoopts = qemu_opts_to_qdict(opts, NULL); + qdict_put_str(cryptoopts, "format", QCryptoBlockFormat_str(format)); - open_opts = block_crypto_open_opts_init(format, cryptoopts, errp); + open_opts = block_crypto_open_opts_init(cryptoopts, errp); if (!open_opts) { goto cleanup; } @@ -305,7 +240,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, ret = 0; cleanup: - QDECREF(cryptoopts); + qobject_unref(cryptoopts); qapi_free_QCryptoBlockOpenOptions(open_opts); return ret; } @@ -351,8 +286,9 @@ static int block_crypto_co_create_generic(BlockDriverState *bs, return ret; } -static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn +block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BlockCrypto *crypto = bs->opaque; uint64_t payload_offset = @@ -365,7 +301,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, offset += payload_offset; - return bdrv_truncate(bs->file, offset, prealloc, errp); + return bdrv_co_truncate(bs->file, offset, prealloc, errp); } static void block_crypto_close(BlockDriverState *bs) @@ -605,8 +541,8 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, &block_crypto_create_opts_luks, true); - create_opts = block_crypto_create_opts_init(Q_CRYPTO_BLOCK_FORMAT_LUKS, - cryptoopts, errp); + qdict_put_str(cryptoopts, "format", "luks"); + create_opts = block_crypto_create_opts_init(cryptoopts, errp); if (!create_opts) { ret = -EINVAL; goto fail; @@ -615,7 +551,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, /* Create protocol layer */ ret = bdrv_create_file(filename, opts, errp); if (ret < 0) { - return ret; + goto fail; } bs = bdrv_open(filename, NULL, NULL, @@ -635,7 +571,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename, fail: bdrv_unref(bs); qapi_free_QCryptoBlockCreateOptions(create_opts); - QDECREF(cryptoopts); + qobject_unref(cryptoopts); return ret; } @@ -694,7 +630,7 @@ BlockDriver bdrv_crypto_luks = { .bdrv_child_perm = bdrv_format_default_perms, .bdrv_co_create = block_crypto_co_create_luks, .bdrv_co_create_opts = block_crypto_co_create_opts_luks, - .bdrv_truncate = block_crypto_truncate, + .bdrv_co_truncate = block_crypto_co_truncate, .create_opts = &block_crypto_create_opts_luks, .bdrv_reopen_prepare = block_crypto_reopen_prepare, diff --git a/block/crypto.h b/block/crypto.h index 0f985ea4e2..dd7d47903c 100644 --- a/block/crypto.h +++ b/block/crypto.h @@ -89,13 +89,9 @@ } QCryptoBlockCreateOptions * -block_crypto_create_opts_init(QCryptoBlockFormat format, - QDict *opts, - Error **errp); +block_crypto_create_opts_init(QDict *opts, Error **errp); QCryptoBlockOpenOptions * -block_crypto_open_opts_init(QCryptoBlockFormat format, - QDict *opts, - Error **errp); +block_crypto_open_opts_init(QDict *opts, Error **errp); #endif /* BLOCK_CRYPTO_H__ */ diff --git a/block/curl.c b/block/curl.c index aa42535783..229bb84a27 100644 --- a/block/curl.c +++ b/block/curl.c @@ -804,7 +804,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, } /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not * know or the size is zero. From 7.19.4 CURL returns -1 if size is not - * known and zero if it is realy zero-length file. */ + * known and zero if it is really zero-length file. */ #if LIBCURL_VERSION_NUM >= 0x071304 if (d < 0) { pstrcpy(state->errmsg, CURL_ERROR_SIZE, diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index 967159479d..c9b8a6fd52 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -97,15 +97,6 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name) return NULL; } -/* Called with BQL taken. */ -void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap) -{ - assert(!bdrv_dirty_bitmap_frozen(bitmap)); - g_free(bitmap->name); - bitmap->name = NULL; - bitmap->persistent = false; -} - /* Called with BQL taken. */ BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, uint32_t granularity, @@ -250,57 +241,31 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, return 0; } +void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) +{ + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + bitmap->disabled = false; +} + /* Called with BQL taken. */ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) { + assert(bitmap->mutex == bitmap->successor->mutex); qemu_mutex_lock(bitmap->mutex); - bdrv_enable_dirty_bitmap(bitmap->successor); + bdrv_enable_dirty_bitmap_locked(bitmap->successor); qemu_mutex_unlock(bitmap->mutex); } -/* Called within bdrv_dirty_bitmap_lock..unlock */ -static void bdrv_do_release_matching_dirty_bitmap_locked( - BlockDriverState *bs, BdrvDirtyBitmap *bitmap, - bool (*cond)(BdrvDirtyBitmap *bitmap)) +/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */ +static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) { - BdrvDirtyBitmap *bm, *next; - - QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { - if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) { - assert(!bm->active_iterators); - assert(!bdrv_dirty_bitmap_frozen(bm)); - assert(!bm->meta); - QLIST_REMOVE(bm, list); - hbitmap_free(bm->bitmap); - g_free(bm->name); - g_free(bm); - - if (bitmap) { - return; - } - } - } - - if (bitmap) { - abort(); - } -} - -/* Called with BQL taken. */ -static void bdrv_do_release_matching_dirty_bitmap( - BlockDriverState *bs, BdrvDirtyBitmap *bitmap, - bool (*cond)(BdrvDirtyBitmap *bitmap)) -{ - bdrv_dirty_bitmaps_lock(bs); - bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, cond); - bdrv_dirty_bitmaps_unlock(bs); -} - -/* Called within bdrv_dirty_bitmap_lock..unlock */ -static void bdrv_release_dirty_bitmap_locked(BlockDriverState *bs, - BdrvDirtyBitmap *bitmap) -{ - bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, NULL); + assert(!bitmap->active_iterators); + assert(!bdrv_dirty_bitmap_frozen(bitmap)); + assert(!bitmap->meta); + QLIST_REMOVE(bitmap, list); + hbitmap_free(bitmap->bitmap); + g_free(bitmap->name); + g_free(bitmap); } /** @@ -353,7 +318,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, error_setg(errp, "Merging of parent and successor bitmap failed"); return NULL; } - bdrv_release_dirty_bitmap_locked(bs, successor); + bdrv_release_dirty_bitmap_locked(successor); parent->successor = NULL; return parent; @@ -391,15 +356,12 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) bdrv_dirty_bitmaps_unlock(bs); } -static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap) -{ - return !!bdrv_dirty_bitmap_name(bitmap); -} - /* Called with BQL taken. */ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) { - bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL); + bdrv_dirty_bitmaps_lock(bs); + bdrv_release_dirty_bitmap_locked(bitmap); + bdrv_dirty_bitmaps_unlock(bs); } /** @@ -410,7 +372,15 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) */ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) { - bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name); + BdrvDirtyBitmap *bm, *next; + + bdrv_dirty_bitmaps_lock(bs); + QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { + if (bdrv_dirty_bitmap_name(bm)) { + bdrv_release_dirty_bitmap_locked(bm); + } + } + bdrv_dirty_bitmaps_unlock(bs); } /** @@ -418,11 +388,19 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) * bdrv_inactivate_recurse()). * There must not be any frozen bitmaps attached. * This function does not remove persistent bitmaps from the storage. + * Called with BQL taken. */ void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) { - bdrv_do_release_matching_dirty_bitmap(bs, NULL, - bdrv_dirty_bitmap_get_persistance); + BdrvDirtyBitmap *bm, *next; + + bdrv_dirty_bitmaps_lock(bs); + QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { + if (bdrv_dirty_bitmap_get_persistance(bm)) { + bdrv_release_dirty_bitmap_locked(bm); + } + } + bdrv_dirty_bitmaps_unlock(bs); } /** @@ -442,18 +420,19 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, } } -/* Called with BQL taken. */ void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) { + bdrv_dirty_bitmap_lock(bitmap); assert(!bdrv_dirty_bitmap_frozen(bitmap)); bitmap->disabled = true; + bdrv_dirty_bitmap_unlock(bitmap); } -/* Called with BQL taken. */ void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) { - assert(!bdrv_dirty_bitmap_frozen(bitmap)); - bitmap->disabled = false; + bdrv_dirty_bitmap_lock(bitmap); + bdrv_enable_dirty_bitmap_locked(bitmap); + bdrv_dirty_bitmap_unlock(bitmap); } BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) @@ -546,7 +525,62 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) { - return hbitmap_iter_next(&iter->hbi); + return hbitmap_iter_next(&iter->hbi, true); +} + +/** + * Return the next consecutively dirty area in the dirty bitmap + * belonging to the given iterator @iter. + * + * @max_offset: Maximum value that may be returned for + * *offset + *bytes + * @offset: Will contain the start offset of the next dirty area + * @bytes: Will contain the length of the next dirty area + * + * Returns: True if a dirty area could be found before max_offset + * (which means that *offset and *bytes then contain valid + * values), false otherwise. + * + * Note that @iter is never advanced if false is returned. If an area + * is found (which means that true is returned), it will be advanced + * past that area. + */ +bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, + uint64_t *offset, int *bytes) +{ + uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap); + uint64_t gran_max_offset; + int64_t ret; + int size; + + if (max_offset == iter->bitmap->size) { + /* If max_offset points to the image end, round it up by the + * bitmap granularity */ + gran_max_offset = ROUND_UP(max_offset, granularity); + } else { + gran_max_offset = max_offset; + } + + ret = hbitmap_iter_next(&iter->hbi, false); + if (ret < 0 || ret + granularity > gran_max_offset) { + return false; + } + + *offset = ret; + size = 0; + + assert(granularity <= INT_MAX); + + do { + /* Advance iterator */ + ret = hbitmap_iter_next(&iter->hbi, true); + size += granularity; + } while (ret + granularity <= gran_max_offset && + hbitmap_iter_next(&iter->hbi, false) == ret + granularity && + size <= INT_MAX - granularity); + + *bytes = MIN(size, max_offset - *offset); + return true; } /* Called within bdrv_dirty_bitmap_lock..unlock */ @@ -755,3 +789,21 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) { return hbitmap_next_zero(bitmap->bitmap, offset); } + +void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + Error **errp) +{ + /* only bitmaps from one bds are supported */ + assert(dest->mutex == src->mutex); + + qemu_mutex_lock(dest->mutex); + + assert(bdrv_dirty_bitmap_enabled(dest)); + assert(!bdrv_dirty_bitmap_readonly(dest)); + + if (!hbitmap_merge(dest->bitmap, src->bitmap)) { + error_setg(errp, "Bitmaps are incompatible and can't be merged"); + } + + qemu_mutex_unlock(dest->mutex); +} diff --git a/block/file-posix.c b/block/file-posix.c index 3794c0007a..fe83cbf0eb 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -59,6 +59,7 @@ #ifdef __linux__ #include #include +#include #include #include #include @@ -161,6 +162,7 @@ typedef struct BDRVRawState { bool page_cache_inconsistent:1; bool has_fallocate; bool needs_alignment; + bool check_cache_dropped; PRManager *pr_mgr; } BDRVRawState; @@ -168,6 +170,7 @@ typedef struct BDRVRawState { typedef struct BDRVRawReopenState { int fd; int open_flags; + bool check_cache_dropped; } BDRVRawReopenState; static int fd_open(BlockDriverState *bs); @@ -185,6 +188,16 @@ typedef struct RawPosixAIOData { #define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */ off_t aio_offset; int aio_type; + union { + struct { + int aio_fd2; + off_t aio_offset2; + }; + struct { + PreallocMode prealloc; + Error **errp; + }; + }; } RawPosixAIOData; #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) @@ -415,12 +428,18 @@ static QemuOptsList raw_runtime_opts = { .type = QEMU_OPT_STRING, .help = "id of persistent reservation manager object (default: none)", }, + { + .name = "x-check-cache-dropped", + .type = QEMU_OPT_BOOL, + .help = "check that page cache was dropped on live migration (default: off)" + }, { /* end of list */ } }, }; static int raw_open_common(BlockDriverState *bs, QDict *options, - int bdrv_flags, int open_flags, Error **errp) + int bdrv_flags, int open_flags, + bool device, Error **errp) { BDRVRawState *s = bs->opaque; QemuOpts *opts; @@ -500,6 +519,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } } + s->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", + false); + s->open_flags = open_flags; raw_parse_flags(bdrv_flags, &s->open_flags); @@ -532,11 +554,17 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, #ifdef CONFIG_LINUX_AIO /* Currently Linux does AIO only for files opened with O_DIRECT */ - if (s->use_linux_aio && !(s->open_flags & O_DIRECT)) { - error_setg(errp, "aio=native was specified, but it requires " - "cache.direct=on, which was not specified."); - ret = -EINVAL; - goto fail; + if (s->use_linux_aio) { + if (!(s->open_flags & O_DIRECT)) { + error_setg(errp, "aio=native was specified, but it requires " + "cache.direct=on, which was not specified."); + ret = -EINVAL; + goto fail; + } + if (!aio_setup_linux_aio(bdrv_get_aio_context(bs), errp)) { + error_prepend(errp, "Unable to use native AIO: "); + goto fail; + } } #else if (s->use_linux_aio) { @@ -558,10 +586,32 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, error_setg_errno(errp, errno, "Could not stat file"); goto fail; } - if (S_ISREG(st.st_mode)) { - s->discard_zeroes = true; - s->has_fallocate = true; + + if (!device) { + if (S_ISBLK(st.st_mode)) { + warn_report("Opening a block device as a file using the '%s' " + "driver is deprecated", bs->drv->format_name); + } else if (S_ISCHR(st.st_mode)) { + warn_report("Opening a character device as a file using the '%s' " + "driver is deprecated", bs->drv->format_name); + } else if (!S_ISREG(st.st_mode)) { + error_setg(errp, "A regular file was expected by the '%s' driver, " + "but something else was given", bs->drv->format_name); + ret = -EINVAL; + goto fail; + } else { + s->discard_zeroes = true; + s->has_fallocate = true; + } + } else { + if (!(S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) { + error_setg(errp, "'%s' driver expects either " + "a character or block device", bs->drv->format_name); + ret = -EINVAL; + goto fail; + } } + if (S_ISBLK(st.st_mode)) { #ifdef BLKDISCARDZEROES unsigned int arg; @@ -598,7 +648,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } #endif - bs->supported_zero_flags = s->discard_zeroes ? BDRV_REQ_MAY_UNMAP : 0; + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP; ret = 0; fail: if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) { @@ -614,7 +664,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, BDRVRawState *s = bs->opaque; s->type = FTYPE_FILE; - return raw_open_common(bs, options, flags, 0, errp); + return raw_open_common(bs, options, flags, 0, false, errp); } typedef enum { @@ -630,7 +680,7 @@ typedef enum { * file; if @unlock == true, also unlock the unneeded bytes. * @shared_perm_lock_bits is the mask of all permissions that are NOT shared. */ -static int raw_apply_lock_bytes(BDRVRawState *s, +static int raw_apply_lock_bytes(int fd, uint64_t perm_lock_bits, uint64_t shared_perm_lock_bits, bool unlock, Error **errp) @@ -641,13 +691,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s, PERM_FOREACH(i) { int off = RAW_LOCK_PERM_BASE + i; if (perm_lock_bits & (1ULL << i)) { - ret = qemu_lock_fd(s->lock_fd, off, 1, false); + ret = qemu_lock_fd(fd, off, 1, false); if (ret) { error_setg(errp, "Failed to lock byte %d", off); return ret; } } else if (unlock) { - ret = qemu_unlock_fd(s->lock_fd, off, 1); + ret = qemu_unlock_fd(fd, off, 1); if (ret) { error_setg(errp, "Failed to unlock byte %d", off); return ret; @@ -657,13 +707,13 @@ static int raw_apply_lock_bytes(BDRVRawState *s, PERM_FOREACH(i) { int off = RAW_LOCK_SHARED_BASE + i; if (shared_perm_lock_bits & (1ULL << i)) { - ret = qemu_lock_fd(s->lock_fd, off, 1, false); + ret = qemu_lock_fd(fd, off, 1, false); if (ret) { error_setg(errp, "Failed to lock byte %d", off); return ret; } } else if (unlock) { - ret = qemu_unlock_fd(s->lock_fd, off, 1); + ret = qemu_unlock_fd(fd, off, 1); if (ret) { error_setg(errp, "Failed to unlock byte %d", off); return ret; @@ -674,8 +724,7 @@ static int raw_apply_lock_bytes(BDRVRawState *s, } /* Check "unshared" bytes implied by @perm and ~@shared_perm in the file. */ -static int raw_check_lock_bytes(BDRVRawState *s, - uint64_t perm, uint64_t shared_perm, +static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, Error **errp) { int ret; @@ -685,7 +734,7 @@ static int raw_check_lock_bytes(BDRVRawState *s, int off = RAW_LOCK_SHARED_BASE + i; uint64_t p = 1ULL << i; if (perm & p) { - ret = qemu_lock_fd_test(s->lock_fd, off, 1, true); + ret = qemu_lock_fd_test(fd, off, 1, true); if (ret) { char *perm_name = bdrv_perm_names(p); error_setg(errp, @@ -702,7 +751,7 @@ static int raw_check_lock_bytes(BDRVRawState *s, int off = RAW_LOCK_PERM_BASE + i; uint64_t p = 1ULL << i; if (!(shared_perm & p)) { - ret = qemu_lock_fd_test(s->lock_fd, off, 1, true); + ret = qemu_lock_fd_test(fd, off, 1, true); if (ret) { char *perm_name = bdrv_perm_names(p); error_setg(errp, @@ -739,11 +788,11 @@ static int raw_handle_perm_lock(BlockDriverState *bs, switch (op) { case RAW_PL_PREPARE: - ret = raw_apply_lock_bytes(s, s->perm | new_perm, + ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm, ~s->shared_perm | ~new_shared, false, errp); if (!ret) { - ret = raw_check_lock_bytes(s, new_perm, new_shared, errp); + ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp); if (!ret) { return 0; } @@ -751,7 +800,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs, op = RAW_PL_ABORT; /* fall through to unlock bytes. */ case RAW_PL_ABORT: - raw_apply_lock_bytes(s, s->perm, ~s->shared_perm, true, &local_err); + raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm, + true, &local_err); if (local_err) { /* Theoretically the above call only unlocks bytes and it cannot * fail. Something weird happened, report it. @@ -760,7 +810,8 @@ static int raw_handle_perm_lock(BlockDriverState *bs, } break; case RAW_PL_COMMIT: - raw_apply_lock_bytes(s, new_perm, ~new_shared, true, &local_err); + raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared, + true, &local_err); if (local_err) { /* Theoretically the above call only unlocks bytes and it cannot * fail. Something weird happened, report it. @@ -777,6 +828,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, { BDRVRawState *s; BDRVRawReopenState *rs; + QemuOpts *opts; int ret = 0; Error *local_err = NULL; @@ -787,6 +839,19 @@ static int raw_reopen_prepare(BDRVReopenState *state, state->opaque = g_new0(BDRVRawReopenState, 1); rs = state->opaque; + rs->fd = -1; + + /* Handle options changes */ + opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, state->options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + rs->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", + s->check_cache_dropped); if (s->type == FTYPE_CD) { rs->open_flags |= O_NONBLOCK; @@ -794,8 +859,6 @@ static int raw_reopen_prepare(BDRVReopenState *state, raw_parse_flags(state->flags, &rs->open_flags); - rs->fd = -1; - int fcntl_flags = O_APPEND | O_NONBLOCK; #ifdef O_NOATIME fcntl_flags |= O_NOATIME; @@ -850,6 +913,8 @@ static int raw_reopen_prepare(BDRVReopenState *state, } } +out: + qemu_opts_del(opts); return ret; } @@ -858,6 +923,7 @@ static void raw_reopen_commit(BDRVReopenState *state) BDRVRawReopenState *rs = state->opaque; BDRVRawState *s = state->bs->opaque; + s->check_cache_dropped = rs->check_cache_dropped; s->open_flags = rs->open_flags; qemu_close(s->fd); @@ -1421,6 +1487,81 @@ static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) return -ENOTSUP; } +static ssize_t handle_aiocb_write_zeroes_unmap(RawPosixAIOData *aiocb) +{ + BDRVRawState *s G_GNUC_UNUSED = aiocb->bs->opaque; + int ret; + + /* First try to write zeros and unmap at the same time */ + +#ifdef CONFIG_FALLOCATE_PUNCH_HOLE + ret = do_fallocate(s->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, + aiocb->aio_offset, aiocb->aio_nbytes); + if (ret != -ENOTSUP) { + return ret; + } +#endif + +#ifdef CONFIG_XFS + if (s->is_xfs) { + /* xfs_discard() guarantees that the discarded area reads as all-zero + * afterwards, so we can use it here. */ + return xfs_discard(s, aiocb->aio_offset, aiocb->aio_nbytes); + } +#endif + + /* If we couldn't manage to unmap while guaranteed that the area reads as + * all-zero afterwards, just write zeroes without unmapping */ + ret = handle_aiocb_write_zeroes(aiocb); + return ret; +} + +#ifndef HAVE_COPY_FILE_RANGE +static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, + off_t *out_off, size_t len, unsigned int flags) +{ +#ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, in_fd, in_off, out_fd, + out_off, len, flags); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif + +static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb) +{ + uint64_t bytes = aiocb->aio_nbytes; + off_t in_off = aiocb->aio_offset; + off_t out_off = aiocb->aio_offset2; + + while (bytes) { + ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, + aiocb->aio_fd2, &out_off, + bytes, 0); + trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, + aiocb->aio_fd2, out_off, bytes, 0, ret); + if (ret == 0) { + /* No progress (e.g. when beyond EOF), let the caller fall back to + * buffer I/O. */ + return -ENOSPC; + } + if (ret < 0) { + switch (errno) { + case ENOSYS: + return -ENOTSUP; + case EINTR: + continue; + default: + return -errno; + } + } + bytes -= ret; + } + return 0; +} + static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) { int ret = -EOPNOTSUPP; @@ -1461,6 +1602,125 @@ static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) return ret; } +static int handle_aiocb_truncate(RawPosixAIOData *aiocb) +{ + int result = 0; + int64_t current_length = 0; + char *buf = NULL; + struct stat st; + int fd = aiocb->aio_fildes; + int64_t offset = aiocb->aio_offset; + Error **errp = aiocb->errp; + + if (fstat(fd, &st) < 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not stat file"); + return result; + } + + current_length = st.st_size; + if (current_length > offset && aiocb->prealloc != PREALLOC_MODE_OFF) { + error_setg(errp, "Cannot use preallocation for shrinking files"); + return -ENOTSUP; + } + + switch (aiocb->prealloc) { +#ifdef CONFIG_POSIX_FALLOCATE + case PREALLOC_MODE_FALLOC: + /* + * Truncating before posix_fallocate() makes it about twice slower on + * file systems that do not support fallocate(), trying to check if a + * block is allocated before allocating it, so don't do that here. + */ + if (offset != current_length) { + result = -posix_fallocate(fd, current_length, + offset - current_length); + if (result != 0) { + /* posix_fallocate() doesn't set errno. */ + error_setg_errno(errp, -result, + "Could not preallocate new data"); + } + } else { + result = 0; + } + goto out; +#endif + case PREALLOC_MODE_FULL: + { + int64_t num = 0, left = offset - current_length; + off_t seek_result; + + /* + * Knowing the final size from the beginning could allow the file + * system driver to do less allocations and possibly avoid + * fragmentation of the file. + */ + if (ftruncate(fd, offset) != 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not resize file"); + goto out; + } + + buf = g_malloc0(65536); + + seek_result = lseek(fd, current_length, SEEK_SET); + if (seek_result < 0) { + result = -errno; + error_setg_errno(errp, -result, + "Failed to seek to the old end of file"); + goto out; + } + + while (left > 0) { + num = MIN(left, 65536); + result = write(fd, buf, num); + if (result < 0) { + if (errno == EINTR) { + continue; + } + result = -errno; + error_setg_errno(errp, -result, + "Could not write zeros for preallocation"); + goto out; + } + left -= result; + } + if (result >= 0) { + result = fsync(fd); + if (result < 0) { + result = -errno; + error_setg_errno(errp, -result, + "Could not flush file to disk"); + goto out; + } + } + goto out; + } + case PREALLOC_MODE_OFF: + if (ftruncate(fd, offset) != 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not resize file"); + } + return result; + default: + result = -ENOTSUP; + error_setg(errp, "Unsupported preallocation mode: %s", + PreallocMode_str(aiocb->prealloc)); + return result; + } + +out: + if (result < 0) { + if (ftruncate(fd, current_length) < 0) { + error_report("Failed to restore old file length: %s", + strerror(errno)); + } + } + + g_free(buf); + return result; +} + static int aio_worker(void *arg) { RawPosixAIOData *aiocb = arg; @@ -1501,6 +1761,15 @@ static int aio_worker(void *arg) case QEMU_AIO_WRITE_ZEROES: ret = handle_aiocb_write_zeroes(aiocb); break; + case QEMU_AIO_WRITE_ZEROES | QEMU_AIO_DISCARD: + ret = handle_aiocb_write_zeroes_unmap(aiocb); + break; + case QEMU_AIO_COPY_RANGE: + ret = handle_aiocb_copy_range(aiocb); + break; + case QEMU_AIO_TRUNCATE: + ret = handle_aiocb_truncate(aiocb); + break; default: fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); ret = -EINVAL; @@ -1511,9 +1780,10 @@ static int aio_worker(void *arg) return ret; } -static int paio_submit_co(BlockDriverState *bs, int fd, - int64_t offset, QEMUIOVector *qiov, - int bytes, int type) +static int paio_submit_co_full(BlockDriverState *bs, int fd, + int64_t offset, int fd2, int64_t offset2, + QEMUIOVector *qiov, + int bytes, int type) { RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); ThreadPool *pool; @@ -1521,6 +1791,8 @@ static int paio_submit_co(BlockDriverState *bs, int fd, acb->bs = bs; acb->aio_type = type; acb->aio_fildes = fd; + acb->aio_fd2 = fd2; + acb->aio_offset2 = offset2; acb->aio_nbytes = bytes; acb->aio_offset = offset; @@ -1531,34 +1803,16 @@ static int paio_submit_co(BlockDriverState *bs, int fd, assert(qiov->size == bytes); } - trace_paio_submit_co(offset, bytes, type); + trace_file_paio_submit_co(offset, bytes, type); pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); return thread_pool_submit_co(pool, aio_worker, acb); } -static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd, - int64_t offset, QEMUIOVector *qiov, int bytes, - BlockCompletionFunc *cb, void *opaque, int type) +static inline int paio_submit_co(BlockDriverState *bs, int fd, + int64_t offset, QEMUIOVector *qiov, + int bytes, int type) { - RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); - ThreadPool *pool; - - acb->bs = bs; - acb->aio_type = type; - acb->aio_fildes = fd; - - acb->aio_nbytes = bytes; - acb->aio_offset = offset; - - if (qiov) { - acb->aio_iov = qiov->iov; - acb->aio_niov = qiov->niov; - assert(qiov->size == acb->aio_nbytes); - } - - trace_paio_submit(acb, opaque, offset, bytes, type); - pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); - return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); + return paio_submit_co_full(bs, fd, offset, -1, 0, qiov, bytes, type); } static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, @@ -1627,15 +1881,33 @@ static void raw_aio_unplug(BlockDriverState *bs) #endif } -static BlockAIOCB *raw_aio_flush(BlockDriverState *bs, - BlockCompletionFunc *cb, void *opaque) +static int raw_co_flush_to_disk(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; + int ret; - if (fd_open(bs) < 0) - return NULL; + ret = fd_open(bs); + if (ret < 0) { + return ret; + } - return paio_submit(bs, s->fd, 0, NULL, 0, cb, opaque, QEMU_AIO_FLUSH); + return paio_submit_co(bs, s->fd, 0, NULL, 0, QEMU_AIO_FLUSH); +} + +static void raw_aio_attach_aio_context(BlockDriverState *bs, + AioContext *new_context) +{ +#ifdef CONFIG_LINUX_AIO + BDRVRawState *s = bs->opaque; + if (s->use_linux_aio) { + Error *local_err; + if (!aio_setup_linux_aio(new_context, &local_err)) { + error_reportf_err(local_err, "Unable to use native AIO, " + "falling back to thread pool: "); + s->use_linux_aio = false; + } + } +#endif } static void raw_close(BlockDriverState *bs) @@ -1658,121 +1930,29 @@ static void raw_close(BlockDriverState *bs) * * Returns: 0 on success, -errno on failure. */ -static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc, - Error **errp) +static int coroutine_fn +raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, + PreallocMode prealloc, Error **errp) { - int result = 0; - int64_t current_length = 0; - char *buf = NULL; - struct stat st; + RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); + ThreadPool *pool; - if (fstat(fd, &st) < 0) { - result = -errno; - error_setg_errno(errp, -result, "Could not stat file"); - return result; - } + *acb = (RawPosixAIOData) { + .bs = bs, + .aio_fildes = fd, + .aio_type = QEMU_AIO_TRUNCATE, + .aio_offset = offset, + .prealloc = prealloc, + .errp = errp, + }; - current_length = st.st_size; - if (current_length > offset && prealloc != PREALLOC_MODE_OFF) { - error_setg(errp, "Cannot use preallocation for shrinking files"); - return -ENOTSUP; - } - - switch (prealloc) { -#ifdef CONFIG_POSIX_FALLOCATE - case PREALLOC_MODE_FALLOC: - /* - * Truncating before posix_fallocate() makes it about twice slower on - * file systems that do not support fallocate(), trying to check if a - * block is allocated before allocating it, so don't do that here. - */ - if (offset != current_length) { - result = -posix_fallocate(fd, current_length, offset - current_length); - if (result != 0) { - /* posix_fallocate() doesn't set errno. */ - error_setg_errno(errp, -result, - "Could not preallocate new data"); - } - } else { - result = 0; - } - goto out; -#endif - case PREALLOC_MODE_FULL: - { - int64_t num = 0, left = offset - current_length; - off_t seek_result; - - /* - * Knowing the final size from the beginning could allow the file - * system driver to do less allocations and possibly avoid - * fragmentation of the file. - */ - if (ftruncate(fd, offset) != 0) { - result = -errno; - error_setg_errno(errp, -result, "Could not resize file"); - goto out; - } - - buf = g_malloc0(65536); - - seek_result = lseek(fd, current_length, SEEK_SET); - if (seek_result < 0) { - result = -errno; - error_setg_errno(errp, -result, - "Failed to seek to the old end of file"); - goto out; - } - - while (left > 0) { - num = MIN(left, 65536); - result = write(fd, buf, num); - if (result < 0) { - result = -errno; - error_setg_errno(errp, -result, - "Could not write zeros for preallocation"); - goto out; - } - left -= result; - } - if (result >= 0) { - result = fsync(fd); - if (result < 0) { - result = -errno; - error_setg_errno(errp, -result, - "Could not flush file to disk"); - goto out; - } - } - goto out; - } - case PREALLOC_MODE_OFF: - if (ftruncate(fd, offset) != 0) { - result = -errno; - error_setg_errno(errp, -result, "Could not resize file"); - } - return result; - default: - result = -ENOTSUP; - error_setg(errp, "Unsupported preallocation mode: %s", - PreallocMode_str(prealloc)); - return result; - } - -out: - if (result < 0) { - if (ftruncate(fd, current_length) < 0) { - error_report("Failed to restore old file length: %s", - strerror(errno)); - } - } - - g_free(buf); - return result; + /* @bs can be NULL, bdrv_get_aio_context() returns the main context then */ + pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_co(pool, aio_worker, acb); } -static int raw_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVRawState *s = bs->opaque; struct stat st; @@ -1785,7 +1965,7 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset, } if (S_ISREG(st.st_mode)) { - return raw_regular_truncate(s->fd, offset, prealloc, errp); + return raw_regular_truncate(bs, s->fd, offset, prealloc, errp); } if (prealloc != PREALLOC_MODE_OFF) { @@ -1987,10 +2167,13 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs) return (int64_t)st.st_blocks * 512; } -static int raw_co_create(BlockdevCreateOptions *options, Error **errp) +static int coroutine_fn +raw_co_create(BlockdevCreateOptions *options, Error **errp) { BlockdevCreateOptionsFile *file_opts; + Error *local_err = NULL; int fd; + uint64_t perm, shared; int result = 0; /* Validate options and set default values */ @@ -2005,14 +2188,44 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) } /* Create file */ - fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, - 0644); + fd = qemu_open(file_opts->filename, O_RDWR | O_CREAT | O_BINARY, 0644); if (fd < 0) { result = -errno; error_setg_errno(errp, -result, "Could not create file"); goto out; } + /* Take permissions: We want to discard everything, so we need + * BLK_PERM_WRITE; and truncation to the desired size requires + * BLK_PERM_RESIZE. + * On the other hand, we cannot share the RESIZE permission + * because we promise that after this function, the file has the + * size given in the options. If someone else were to resize it + * concurrently, we could not guarantee that. + * Note that after this function, we can no longer guarantee that + * the file is not touched by a third party, so it may be resized + * then. */ + perm = BLK_PERM_WRITE | BLK_PERM_RESIZE; + shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; + + /* Step one: Take locks */ + result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp); + if (result < 0) { + goto out_close; + } + + /* Step two: Check that nobody else has taken conflicting locks */ + result = raw_check_lock_bytes(fd, perm, shared, errp); + if (result < 0) { + goto out_unlock; + } + + /* Clear the file by truncating it to 0 */ + result = raw_regular_truncate(NULL, fd, 0, PREALLOC_MODE_OFF, errp); + if (result < 0) { + goto out_unlock; + } + if (file_opts->nocow) { #ifdef __linux__ /* Set NOCOW flag to solve performance issue on fs like btrfs. @@ -2028,10 +2241,22 @@ static int raw_co_create(BlockdevCreateOptions *options, Error **errp) #endif } - result = raw_regular_truncate(fd, file_opts->size, file_opts->preallocation, - errp); + /* Resize and potentially preallocate the file to the desired + * final size */ + result = raw_regular_truncate(NULL, fd, file_opts->size, + file_opts->preallocation, errp); if (result < 0) { - goto out_close; + goto out_unlock; + } + +out_unlock: + raw_apply_lock_bytes(fd, 0, 0, true, &local_err); + if (local_err) { + /* The above call should not fail, and if it does, that does + * not mean the whole creation operation has failed. So + * report it the user for their convenience, but do not report + * it to the caller. */ + error_report_err(local_err); } out_close: @@ -2236,14 +2461,126 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, return ret | BDRV_BLOCK_OFFSET_VALID; } -static coroutine_fn BlockAIOCB *raw_aio_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes, - BlockCompletionFunc *cb, void *opaque) +#if defined(__linux__) +/* Verify that the file is not in the page cache */ +static void check_cache_dropped(BlockDriverState *bs, Error **errp) +{ + const size_t window_size = 128 * 1024 * 1024; + BDRVRawState *s = bs->opaque; + void *window = NULL; + size_t length = 0; + unsigned char *vec; + size_t page_size; + off_t offset; + off_t end; + + /* mincore(2) page status information requires 1 byte per page */ + page_size = sysconf(_SC_PAGESIZE); + vec = g_malloc(DIV_ROUND_UP(window_size, page_size)); + + end = raw_getlength(bs); + + for (offset = 0; offset < end; offset += window_size) { + void *new_window; + size_t new_length; + size_t vec_end; + size_t i; + int ret; + + /* Unmap previous window if size has changed */ + new_length = MIN(end - offset, window_size); + if (new_length != length) { + munmap(window, length); + window = NULL; + length = 0; + } + + new_window = mmap(window, new_length, PROT_NONE, MAP_PRIVATE, + s->fd, offset); + if (new_window == MAP_FAILED) { + error_setg_errno(errp, errno, "mmap failed"); + break; + } + + window = new_window; + length = new_length; + + ret = mincore(window, length, vec); + if (ret < 0) { + error_setg_errno(errp, errno, "mincore failed"); + break; + } + + vec_end = DIV_ROUND_UP(length, page_size); + for (i = 0; i < vec_end; i++) { + if (vec[i] & 0x1) { + error_setg(errp, "page cache still in use!"); + break; + } + } + } + + if (window) { + munmap(window, length); + } + + g_free(vec); +} +#endif /* __linux__ */ + +static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs, + Error **errp) +{ + BDRVRawState *s = bs->opaque; + int ret; + + ret = fd_open(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "The file descriptor is not open"); + return; + } + + if (s->open_flags & O_DIRECT) { + return; /* No host kernel page cache */ + } + +#if defined(__linux__) + /* This sets the scene for the next syscall... */ + ret = bdrv_co_flush(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "flush failed"); + return; + } + + /* Linux does not invalidate pages that are dirty, locked, or mmapped by a + * process. These limitations are okay because we just fsynced the file, + * we don't use mmap, and the file should not be in use by other processes. + */ + ret = posix_fadvise(s->fd, 0, 0, POSIX_FADV_DONTNEED); + if (ret != 0) { /* the return value is a positive errno */ + error_setg_errno(errp, ret, "fadvise failed"); + return; + } + + if (s->check_cache_dropped) { + check_cache_dropped(bs, errp); + } +#else /* __linux__ */ + /* Do nothing. Live migration to a remote host with cache.direct=off is + * unsupported on other host operating systems. Cache consistency issues + * may occur but no error is reported here, partly because that's the + * historical behavior and partly because it's hard to differentiate valid + * configurations that should not cause errors. + */ +#endif /* !__linux__ */ +} + +static coroutine_fn int +raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { BDRVRawState *s = bs->opaque; - return paio_submit(bs, s->fd, offset, NULL, bytes, - cb, opaque, QEMU_AIO_DISCARD); + return paio_submit_co(bs, s->fd, offset, NULL, bytes, QEMU_AIO_DISCARD); } static int coroutine_fn raw_co_pwrite_zeroes( @@ -2251,15 +2588,13 @@ static int coroutine_fn raw_co_pwrite_zeroes( int bytes, BdrvRequestFlags flags) { BDRVRawState *s = bs->opaque; + int operation = QEMU_AIO_WRITE_ZEROES; - if (!(flags & BDRV_REQ_MAY_UNMAP)) { - return paio_submit_co(bs, s->fd, offset, NULL, bytes, - QEMU_AIO_WRITE_ZEROES); - } else if (s->discard_zeroes) { - return paio_submit_co(bs, s->fd, offset, NULL, bytes, - QEMU_AIO_DISCARD); + if (flags & BDRV_REQ_MAY_UNMAP) { + operation |= QEMU_AIO_DISCARD; } - return -ENOTSUP; + + return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); } static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) @@ -2312,6 +2647,40 @@ static void raw_abort_perm_update(BlockDriverState *bs) raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); } +static int coroutine_fn raw_co_copy_range_from( + BlockDriverState *bs, BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, uint64_t bytes, + BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) +{ + return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, + read_flags, write_flags); +} + +static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + BDRVRawState *s = bs->opaque; + BDRVRawState *src_s; + + assert(dst->bs == bs); + if (src->bs->drv->bdrv_co_copy_range_to != raw_co_copy_range_to) { + return -ENOTSUP; + } + + src_s = src->bs->opaque; + if (fd_open(src->bs) < 0 || fd_open(dst->bs) < 0) { + return -EIO; + } + return paio_submit_co_full(bs, src_s->fd, src_offset, s->fd, dst_offset, + NULL, bytes, QEMU_AIO_COPY_RANGE); +} + BlockDriver bdrv_file = { .format_name = "file", .protocol_name = "file", @@ -2328,17 +2697,21 @@ BlockDriver bdrv_file = { .bdrv_co_create_opts = raw_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_co_block_status = raw_co_block_status, + .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, - .bdrv_aio_flush = raw_aio_flush, - .bdrv_aio_pdiscard = raw_aio_pdiscard, + .bdrv_co_flush_to_disk = raw_co_flush_to_disk, + .bdrv_co_pdiscard = raw_co_pdiscard, + .bdrv_co_copy_range_from = raw_co_copy_range_from, + .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .bdrv_get_info = raw_get_info, .bdrv_get_allocated_file_size @@ -2622,7 +2995,7 @@ hdev_open_Mac_error: s->type = FTYPE_FILE; - ret = raw_open_common(bs, options, flags, 0, &local_err); + ret = raw_open_common(bs, options, flags, 0, true, &local_err); if (ret < 0) { error_propagate(errp, local_err); #if defined(__APPLE__) && defined(__MACH__) @@ -2696,37 +3069,37 @@ static int fd_open(BlockDriverState *bs) return -EIO; } -static coroutine_fn BlockAIOCB *hdev_aio_pdiscard(BlockDriverState *bs, - int64_t offset, int bytes, - BlockCompletionFunc *cb, void *opaque) +static coroutine_fn int +hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { BDRVRawState *s = bs->opaque; + int ret; - if (fd_open(bs) < 0) { - return NULL; + ret = fd_open(bs); + if (ret < 0) { + return ret; } - return paio_submit(bs, s->fd, offset, NULL, bytes, - cb, opaque, QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); + return paio_submit_co(bs, s->fd, offset, NULL, bytes, + QEMU_AIO_DISCARD | QEMU_AIO_BLKDEV); } static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { BDRVRawState *s = bs->opaque; + int operation = QEMU_AIO_WRITE_ZEROES | QEMU_AIO_BLKDEV; int rc; rc = fd_open(bs); if (rc < 0) { return rc; } - if (!(flags & BDRV_REQ_MAY_UNMAP)) { - return paio_submit_co(bs, s->fd, offset, NULL, bytes, - QEMU_AIO_WRITE_ZEROES|QEMU_AIO_BLKDEV); - } else if (s->discard_zeroes) { - return paio_submit_co(bs, s->fd, offset, NULL, bytes, - QEMU_AIO_DISCARD|QEMU_AIO_BLKDEV); + + if (flags & BDRV_REQ_MAY_UNMAP) { + operation |= QEMU_AIO_DISCARD; } - return -ENOTSUP; + + return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); } static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts, @@ -2805,17 +3178,21 @@ static BlockDriver bdrv_host_device = { .bdrv_reopen_abort = raw_reopen_abort, .bdrv_co_create_opts = hdev_co_create_opts, .create_opts = &raw_create_opts, + .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes, .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, - .bdrv_aio_flush = raw_aio_flush, - .bdrv_aio_pdiscard = hdev_aio_pdiscard, + .bdrv_co_flush_to_disk = raw_co_flush_to_disk, + .bdrv_co_pdiscard = hdev_co_pdiscard, + .bdrv_co_copy_range_from = raw_co_copy_range_from, + .bdrv_co_copy_range_to = raw_co_copy_range_to, .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .bdrv_get_info = raw_get_info, .bdrv_get_allocated_file_size @@ -2849,7 +3226,7 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, s->type = FTYPE_CD; /* open will not fail even if no CD is inserted, so add O_NONBLOCK */ - return raw_open_common(bs, options, flags, O_NONBLOCK, errp); + return raw_open_common(bs, options, flags, O_NONBLOCK, true, errp); } static int cdrom_probe_device(const char *filename) @@ -2927,16 +3304,18 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_reopen_abort = raw_reopen_abort, .bdrv_co_create_opts = hdev_co_create_opts, .create_opts = &raw_create_opts, + .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, - .bdrv_aio_flush = raw_aio_flush, + .bdrv_co_flush_to_disk = raw_co_flush_to_disk, .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .has_variable_length = true, .bdrv_get_allocated_file_size @@ -2962,7 +3341,7 @@ static int cdrom_open(BlockDriverState *bs, QDict *options, int flags, s->type = FTYPE_CD; - ret = raw_open_common(bs, options, flags, 0, &local_err); + ret = raw_open_common(bs, options, flags, 0, true, &local_err); if (ret) { error_propagate(errp, local_err); return ret; @@ -3061,12 +3440,13 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, - .bdrv_aio_flush = raw_aio_flush, + .bdrv_co_flush_to_disk = raw_co_flush_to_disk, .bdrv_refresh_limits = raw_refresh_limits, .bdrv_io_plug = raw_aio_plug, .bdrv_io_unplug = raw_aio_unplug, + .bdrv_attach_aio_context = raw_aio_attach_aio_context, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .has_variable_length = true, .bdrv_get_allocated_file_size diff --git a/block/file-win32.c b/block/file-win32.c index 2e2f746bb1..f1e2187f3b 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -162,7 +162,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile, acb->aio_nbytes = count; acb->aio_offset = offset; - trace_paio_submit(acb, opaque, offset, count, type); + trace_file_paio_submit(acb, opaque, offset, count, type); pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); } @@ -251,7 +251,11 @@ static void raw_probe_alignment(BlockDriverState *bs, Error **errp) &dg.Geometry.BytesPerSector, &freeClusters, &totalClusters); bs->bl.request_alignment = dg.Geometry.BytesPerSector; + return; } + + /* XXX Does Windows support AIO on less than 512-byte alignment? */ + bs->bl.request_alignment = 512; } static void raw_parse_flags(int flags, bool use_aio, int *access_flags, @@ -410,32 +414,32 @@ fail: return ret; } -static BlockAIOCB *raw_aio_readv(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockCompletionFunc *cb, void *opaque) +static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, void *opaque) { BDRVRawState *s = bs->opaque; if (s->aio) { - return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, - nb_sectors, cb, opaque, QEMU_AIO_READ); + return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, + cb, opaque, QEMU_AIO_READ); } else { - return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, - nb_sectors << BDRV_SECTOR_BITS, + return paio_submit(bs, s->hfile, offset, qiov, bytes, cb, opaque, QEMU_AIO_READ); } } -static BlockAIOCB *raw_aio_writev(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockCompletionFunc *cb, void *opaque) +static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, void *opaque) { BDRVRawState *s = bs->opaque; if (s->aio) { - return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, - nb_sectors, cb, opaque, QEMU_AIO_WRITE); + return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov, + cb, opaque, QEMU_AIO_WRITE); } else { - return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, - nb_sectors << BDRV_SECTOR_BITS, + return paio_submit(bs, s->hfile, offset, qiov, bytes, cb, opaque, QEMU_AIO_WRITE); } } @@ -463,8 +467,8 @@ static void raw_close(BlockDriverState *bs) } } -static int raw_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVRawState *s = bs->opaque; LONG low, high; @@ -632,11 +636,11 @@ BlockDriver bdrv_file = { .bdrv_co_create_opts = raw_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, - .bdrv_aio_readv = raw_aio_readv, - .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_preadv = raw_aio_preadv, + .bdrv_aio_pwritev = raw_aio_pwritev, .bdrv_aio_flush = raw_aio_flush, - .bdrv_truncate = raw_truncate, + .bdrv_co_truncate = raw_co_truncate, .bdrv_getlength = raw_getlength, .bdrv_get_allocated_file_size = raw_get_allocated_file_size, @@ -708,6 +712,12 @@ static void hdev_parse_filename(const char *filename, QDict *options, bdrv_parse_filename_strip_prefix(filename, "host_device:", options); } +static void hdev_refresh_limits(BlockDriverState *bs, Error **errp) +{ + /* XXX Does Windows support AIO on less than 512-byte alignment? */ + bs->bl.request_alignment = 512; +} + static int hdev_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -793,9 +803,10 @@ static BlockDriver bdrv_host_device = { .bdrv_probe_device = hdev_probe_device, .bdrv_file_open = hdev_open, .bdrv_close = raw_close, + .bdrv_refresh_limits = hdev_refresh_limits, - .bdrv_aio_readv = raw_aio_readv, - .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_preadv = raw_aio_preadv, + .bdrv_aio_pwritev = raw_aio_pwritev, .bdrv_aio_flush = raw_aio_flush, .bdrv_detach_aio_context = raw_detach_aio_context, diff --git a/block/gluster.c b/block/gluster.c index 4adc1a875b..4fd55a9cc5 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" @@ -650,7 +651,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf, } gsconf = NULL; - QDECREF(backing_options); + qobject_unref(backing_options); backing_options = NULL; g_free(str); str = NULL; @@ -663,7 +664,7 @@ out: qapi_free_SocketAddress(gsconf); qemu_opts_del(opts); g_free(str); - QDECREF(backing_options); + qobject_unref(backing_options); errno = EINVAL; return -errno; } @@ -1176,8 +1177,10 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, return acb.ret; } -static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs, + int64_t offset, + PreallocMode prealloc, + Error **errp) { BDRVGlusterState *s = bs->opaque; return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); @@ -1194,8 +1197,10 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs, static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov) + QEMUIOVector *qiov, + int flags) { + assert(!flags); return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); } @@ -1321,7 +1326,7 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs) * If @start is in a trailing hole or beyond EOF, return -ENXIO. * If we can't find out, return a negative errno other than -ENXIO. * - * (Shamefully copied from file-posix.c, only miniscule adaptions.) + * (Shamefully copied from file-posix.c, only minuscule adaptions.) */ static int find_allocation(BlockDriverState *bs, off_t start, off_t *data, off_t *hole) @@ -1496,7 +1501,7 @@ static BlockDriver bdrv_gluster = { .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, - .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, @@ -1525,7 +1530,7 @@ static BlockDriver bdrv_gluster_tcp = { .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, - .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, @@ -1554,7 +1559,7 @@ static BlockDriver bdrv_gluster_unix = { .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, - .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, @@ -1589,7 +1594,7 @@ static BlockDriver bdrv_gluster_rdma = { .bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_getlength = qemu_gluster_getlength, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, - .bdrv_truncate = qemu_gluster_truncate, + .bdrv_co_truncate = qemu_gluster_co_truncate, .bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, diff --git a/block/io.c b/block/io.c index bd9a19a9c4..7100344c7b 100644 --- a/block/io.c +++ b/block/io.c @@ -38,29 +38,32 @@ /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) +static AioWait drain_all_aio_wait; + +static void bdrv_parent_cb_resize(BlockDriverState *bs); static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags); -void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore) +void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents) { BdrvChild *c, *next; QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { - if (c == ignore) { + if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { continue; } - if (c->role->drained_begin) { - c->role->drained_begin(c); - } + bdrv_parent_drained_begin_single(c, false); } } -void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) +void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents) { BdrvChild *c, *next; QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { - if (c == ignore) { + if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { continue; } if (c->role->drained_end) { @@ -69,6 +72,40 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore) } } +static bool bdrv_parent_drained_poll_single(BdrvChild *c) +{ + if (c->role->drained_poll) { + return c->role->drained_poll(c); + } + return false; +} + +static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents) +{ + BdrvChild *c, *next; + bool busy = false; + + QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { + if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { + continue; + } + busy |= bdrv_parent_drained_poll_single(c); + } + + return busy; +} + +void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll) +{ + if (c->role->drained_begin) { + c->role->drained_begin(c); + } + if (poll) { + BDRV_POLL_WHILE(c->bs, bdrv_parent_drained_poll_single(c)); + } +} + static void bdrv_merge_limits(BlockLimits *dst, const BlockLimits *src) { dst->opt_transfer = MAX(dst->opt_transfer, src->opt_transfer); @@ -92,7 +129,8 @@ void bdrv_refresh_limits(BlockDriverState *bs, Error **errp) } /* Default alignment based on whether driver has byte interface */ - bs->bl.request_alignment = drv->bdrv_co_preadv ? 1 : 512; + bs->bl.request_alignment = (drv->bdrv_co_preadv || + drv->bdrv_aio_preadv) ? 1 : 512; /* Take some limits from the children as a default */ if (bs->file) { @@ -147,7 +185,9 @@ typedef struct { bool done; bool begin; bool recursive; + bool poll; BdrvChild *parent; + bool ignore_bds_parents; } BdrvCoDrainData; static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) @@ -163,67 +203,83 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) /* Set data->done before reading bs->wakeup. */ atomic_mb_set(&data->done, true); - bdrv_wakeup(bs); + bdrv_dec_in_flight(bs); + + if (data->begin) { + g_free(data); + } } /* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */ -static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive) +static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) { - BdrvChild *child, *tmp; - BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; + BdrvCoDrainData *data; if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || (!begin && !bs->drv->bdrv_co_drain_end)) { return; } - data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data); - bdrv_coroutine_enter(bs, data.co); - BDRV_POLL_WHILE(bs, !data.done); + data = g_new(BdrvCoDrainData, 1); + *data = (BdrvCoDrainData) { + .bs = bs, + .done = false, + .begin = begin + }; - if (recursive) { - QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { - bdrv_drain_invoke(child->bs, begin, true); - } + /* Make sure the driver callback completes during the polling phase for + * drain_begin. */ + bdrv_inc_in_flight(bs); + data->co = qemu_coroutine_create(bdrv_drain_invoke_entry, data); + aio_co_schedule(bdrv_get_aio_context(bs), data->co); + + if (!begin) { + BDRV_POLL_WHILE(bs, !data->done); + g_free(data); } } -static bool bdrv_drain_recurse(BlockDriverState *bs) +/* Returns true if BDRV_POLL_WHILE() should go into a blocking aio_poll() */ +bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, + BdrvChild *ignore_parent, bool ignore_bds_parents) { - BdrvChild *child, *tmp; - bool waited; + BdrvChild *child, *next; - /* Wait for drained requests to finish */ - waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); + if (bdrv_parent_drained_poll(bs, ignore_parent, ignore_bds_parents)) { + return true; + } - QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { - BlockDriverState *bs = child->bs; - bool in_main_loop = - qemu_get_current_aio_context() == qemu_get_aio_context(); - assert(bs->refcnt > 0); - if (in_main_loop) { - /* In case the recursive bdrv_drain_recurse processes a - * block_job_defer_to_main_loop BH and modifies the graph, - * let's hold a reference to bs until we are done. - * - * IOThread doesn't have such a BH, and it is not safe to call - * bdrv_unref without BQL, so skip doing it there. - */ - bdrv_ref(bs); - } - waited |= bdrv_drain_recurse(bs); - if (in_main_loop) { - bdrv_unref(bs); + if (atomic_read(&bs->in_flight)) { + return true; + } + + if (recursive) { + assert(!ignore_bds_parents); + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { + if (bdrv_drain_poll(child->bs, recursive, child, false)) { + return true; + } } } - return waited; + return false; +} + +static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive, + BdrvChild *ignore_parent) +{ + /* Execute pending BHs first and check everything else only after the BHs + * have executed. */ + while (aio_poll(bs->aio_context, false)); + + return bdrv_drain_poll(bs, recursive, ignore_parent, false); } static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent); + BdrvChild *parent, bool ignore_bds_parents, + bool poll); static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, - BdrvChild *parent); + BdrvChild *parent, bool ignore_bds_parents); static void bdrv_co_drain_bh_cb(void *opaque) { @@ -231,11 +287,18 @@ static void bdrv_co_drain_bh_cb(void *opaque) Coroutine *co = data->co; BlockDriverState *bs = data->bs; - bdrv_dec_in_flight(bs); - if (data->begin) { - bdrv_do_drained_begin(bs, data->recursive, data->parent); + if (bs) { + bdrv_dec_in_flight(bs); + if (data->begin) { + bdrv_do_drained_begin(bs, data->recursive, data->parent, + data->ignore_bds_parents, data->poll); + } else { + bdrv_do_drained_end(bs, data->recursive, data->parent, + data->ignore_bds_parents); + } } else { - bdrv_do_drained_end(bs, data->recursive, data->parent); + assert(data->begin); + bdrv_drain_all_begin(); } data->done = true; @@ -244,7 +307,9 @@ static void bdrv_co_drain_bh_cb(void *opaque) static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, bool begin, bool recursive, - BdrvChild *parent) + BdrvChild *parent, + bool ignore_bds_parents, + bool poll) { BdrvCoDrainData data; @@ -259,8 +324,12 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, .begin = begin, .recursive = recursive, .parent = parent, + .ignore_bds_parents = ignore_bds_parents, + .poll = poll, }; - bdrv_inc_in_flight(bs); + if (bs) { + bdrv_inc_in_flight(bs); + } aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), bdrv_co_drain_bh_cb, &data); @@ -270,79 +339,106 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, assert(data.done); } -void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, - BdrvChild *parent) +void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, + BdrvChild *parent, bool ignore_bds_parents) { - BdrvChild *child, *next; - - if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, true, recursive, parent); - return; - } + assert(!qemu_in_coroutine()); /* Stop things in parent-to-child order */ if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { aio_disable_external(bdrv_get_aio_context(bs)); } - bdrv_parent_drained_begin(bs, parent); - bdrv_drain_invoke(bs, true, false); - bdrv_drain_recurse(bs); + bdrv_parent_drained_begin(bs, parent, ignore_bds_parents); + bdrv_drain_invoke(bs, true); +} + +static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive, + BdrvChild *parent, bool ignore_bds_parents, + bool poll) +{ + BdrvChild *child, *next; + + if (qemu_in_coroutine()) { + bdrv_co_yield_to_drain(bs, true, recursive, parent, ignore_bds_parents, + poll); + return; + } + + bdrv_do_drained_begin_quiesce(bs, parent, ignore_bds_parents); if (recursive) { + assert(!ignore_bds_parents); bs->recursive_quiesce_counter++; QLIST_FOREACH_SAFE(child, &bs->children, next, next) { - bdrv_do_drained_begin(child->bs, true, child); + bdrv_do_drained_begin(child->bs, true, child, ignore_bds_parents, + false); } } + + /* + * Wait for drained requests to finish. + * + * Calling BDRV_POLL_WHILE() only once for the top-level node is okay: The + * call is needed so things in this AioContext can make progress even + * though we don't return to the main AioContext loop - this automatically + * includes other nodes in the same AioContext and therefore all child + * nodes. + */ + if (poll) { + assert(!ignore_bds_parents); + BDRV_POLL_WHILE(bs, bdrv_drain_poll_top_level(bs, recursive, parent)); + } } void bdrv_drained_begin(BlockDriverState *bs) { - bdrv_do_drained_begin(bs, false, NULL); + bdrv_do_drained_begin(bs, false, NULL, false, true); } void bdrv_subtree_drained_begin(BlockDriverState *bs) { - bdrv_do_drained_begin(bs, true, NULL); + bdrv_do_drained_begin(bs, true, NULL, false, true); } -void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, - BdrvChild *parent) +static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive, + BdrvChild *parent, bool ignore_bds_parents) { BdrvChild *child, *next; int old_quiesce_counter; if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs, false, recursive, parent); + bdrv_co_yield_to_drain(bs, false, recursive, parent, ignore_bds_parents, + false); return; } assert(bs->quiesce_counter > 0); old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter); /* Re-enable things in child-to-parent order */ - bdrv_drain_invoke(bs, false, false); - bdrv_parent_drained_end(bs, parent); + bdrv_drain_invoke(bs, false); + bdrv_parent_drained_end(bs, parent, ignore_bds_parents); if (old_quiesce_counter == 1) { aio_enable_external(bdrv_get_aio_context(bs)); } if (recursive) { + assert(!ignore_bds_parents); bs->recursive_quiesce_counter--; QLIST_FOREACH_SAFE(child, &bs->children, next, next) { - bdrv_do_drained_end(child->bs, true, child); + bdrv_do_drained_end(child->bs, true, child, ignore_bds_parents); } } } void bdrv_drained_end(BlockDriverState *bs) { - bdrv_do_drained_end(bs, false, NULL); + bdrv_do_drained_end(bs, false, NULL, false); } void bdrv_subtree_drained_end(BlockDriverState *bs) { - bdrv_do_drained_end(bs, true, NULL); + bdrv_do_drained_end(bs, true, NULL, false); } void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) @@ -350,7 +446,7 @@ void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent) int i; for (i = 0; i < new_parent->recursive_quiesce_counter; i++) { - bdrv_do_drained_begin(child->bs, true, child); + bdrv_do_drained_begin(child->bs, true, child, false, true); } } @@ -359,7 +455,7 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) int i; for (i = 0; i < old_parent->recursive_quiesce_counter; i++) { - bdrv_do_drained_end(child->bs, true, child); + bdrv_do_drained_end(child->bs, true, child, false); } } @@ -369,10 +465,6 @@ void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent) * * Note that unlike bdrv_drain_all(), the caller must hold the BlockDriverState * AioContext. - * - * Only this BlockDriverState's AioContext is run, so in-flight requests must - * not depend on events in other AioContexts. In that case, use - * bdrv_drain_all() instead. */ void coroutine_fn bdrv_co_drain(BlockDriverState *bs) { @@ -387,6 +479,39 @@ void bdrv_drain(BlockDriverState *bs) bdrv_drained_end(bs); } +static void bdrv_drain_assert_idle(BlockDriverState *bs) +{ + BdrvChild *child, *next; + + assert(atomic_read(&bs->in_flight) == 0); + QLIST_FOREACH_SAFE(child, &bs->children, next, next) { + bdrv_drain_assert_idle(child->bs); + } +} + +unsigned int bdrv_drain_all_count = 0; + +static bool bdrv_drain_all_poll(void) +{ + BlockDriverState *bs = NULL; + bool result = false; + + /* Execute pending BHs first (may modify the graph) and check everything + * else only after the BHs have executed. */ + while (aio_poll(qemu_get_aio_context(), false)); + + /* bdrv_drain_poll() can't make changes to the graph and we are holding the + * main AioContext lock, so iterating bdrv_next_all_states() is safe. */ + while ((bs = bdrv_next_all_states(bs))) { + AioContext *aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + result |= bdrv_drain_poll(bs, false, NULL, true); + aio_context_release(aio_context); + } + + return result; +} + /* * Wait for pending requests to complete across all BlockDriverStates * @@ -401,73 +526,51 @@ void bdrv_drain(BlockDriverState *bs) */ void bdrv_drain_all_begin(void) { - /* Always run first iteration so any pending completion BHs run */ - bool waited = true; - BlockDriverState *bs; - BdrvNextIterator it; - GSList *aio_ctxs = NULL, *ctx; + BlockDriverState *bs = NULL; - /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread - * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on - * nodes in several different AioContexts, so make sure we're in the main - * context. */ + if (qemu_in_coroutine()) { + bdrv_co_yield_to_drain(NULL, true, false, NULL, true, true); + return; + } + + /* AIO_WAIT_WHILE() with a NULL context can only be called from the main + * loop AioContext, so make sure we're in the main context. */ assert(qemu_get_current_aio_context() == qemu_get_aio_context()); + assert(bdrv_drain_all_count < INT_MAX); + bdrv_drain_all_count++; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + /* Quiesce all nodes, without polling in-flight requests yet. The graph + * cannot change during this loop. */ + while ((bs = bdrv_next_all_states(bs))) { AioContext *aio_context = bdrv_get_aio_context(bs); - /* Stop things in parent-to-child order */ aio_context_acquire(aio_context); - aio_disable_external(aio_context); - bdrv_parent_drained_begin(bs, NULL); - bdrv_drain_invoke(bs, true, true); + bdrv_do_drained_begin(bs, false, NULL, true, false); aio_context_release(aio_context); - - if (!g_slist_find(aio_ctxs, aio_context)) { - aio_ctxs = g_slist_prepend(aio_ctxs, aio_context); - } } - /* Note that completion of an asynchronous I/O operation can trigger any - * number of other I/O operations on other devices---for example a - * coroutine can submit an I/O request to another device in response to - * request completion. Therefore we must keep looping until there was no - * more activity rather than simply draining each device independently. - */ - while (waited) { - waited = false; + /* Now poll the in-flight requests */ + AIO_WAIT_WHILE(&drain_all_aio_wait, NULL, bdrv_drain_all_poll()); - for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) { - AioContext *aio_context = ctx->data; - - aio_context_acquire(aio_context); - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - if (aio_context == bdrv_get_aio_context(bs)) { - waited |= bdrv_drain_recurse(bs); - } - } - aio_context_release(aio_context); - } + while ((bs = bdrv_next_all_states(bs))) { + bdrv_drain_assert_idle(bs); } - - g_slist_free(aio_ctxs); } void bdrv_drain_all_end(void) { - BlockDriverState *bs; - BdrvNextIterator it; + BlockDriverState *bs = NULL; - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + while ((bs = bdrv_next_all_states(bs))) { AioContext *aio_context = bdrv_get_aio_context(bs); - /* Re-enable things in child-to-parent order */ aio_context_acquire(aio_context); - bdrv_drain_invoke(bs, false, true); - bdrv_parent_drained_end(bs, NULL); - aio_enable_external(aio_context); + bdrv_do_drained_end(bs, false, NULL, true); aio_context_release(aio_context); } + + assert(bdrv_drain_all_count > 0); + bdrv_drain_all_count--; } void bdrv_drain_all(void) @@ -499,9 +602,11 @@ static void tracked_request_end(BdrvTrackedRequest *req) static void tracked_request_begin(BdrvTrackedRequest *req, BlockDriverState *bs, int64_t offset, - unsigned int bytes, + uint64_t bytes, enum BdrvTrackedRequestType type) { + assert(bytes <= INT64_MAX && offset <= INT64_MAX - bytes); + *req = (BdrvTrackedRequest){ .bs = bs, .offset = offset, @@ -523,7 +628,7 @@ static void tracked_request_begin(BdrvTrackedRequest *req, static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) { int64_t overlap_offset = req->offset & ~(align - 1); - unsigned int overlap_bytes = ROUND_UP(req->offset + req->bytes, align) + uint64_t overlap_bytes = ROUND_UP(req->offset + req->bytes, align) - overlap_offset; if (!req->serialising) { @@ -535,6 +640,18 @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); } +static bool is_request_serialising_and_aligned(BdrvTrackedRequest *req) +{ + /* + * If the request is serialising, overlap_offset and overlap_bytes are set, + * so we can check if the request is aligned. Otherwise, don't care and + * return false. + */ + + return req->serialising && (req->offset == req->overlap_offset) && + (req->bytes == req->overlap_bytes); +} + /** * Round a region to cluster boundaries */ @@ -569,7 +686,7 @@ static int bdrv_get_cluster_size(BlockDriverState *bs) } static bool tracked_request_overlaps(BdrvTrackedRequest *req, - int64_t offset, unsigned int bytes) + int64_t offset, uint64_t bytes) { /* aaaa bbbb */ if (offset >= req->overlap_offset + req->overlap_bytes) { @@ -590,6 +707,7 @@ void bdrv_inc_in_flight(BlockDriverState *bs) void bdrv_wakeup(BlockDriverState *bs) { aio_wait_kick(bdrv_get_aio_wait(bs)); + aio_wait_kick(&drain_all_aio_wait); } void bdrv_dec_in_flight(BlockDriverState *bs) @@ -924,23 +1042,14 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags); } - sector_num = offset >> BDRV_SECTOR_BITS; - nb_sectors = bytes >> BDRV_SECTOR_BITS; - - assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); - assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); - - if (drv->bdrv_co_readv) { - return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); - } else { + if (drv->bdrv_aio_preadv) { BlockAIOCB *acb; CoroutineIOCompletion co = { .coroutine = qemu_coroutine_self(), }; - acb = bs->drv->bdrv_aio_readv(bs, sector_num, qiov, nb_sectors, - bdrv_co_io_em_complete, &co); + acb = drv->bdrv_aio_preadv(bs, offset, bytes, qiov, flags, + bdrv_co_io_em_complete, &co); if (acb == NULL) { return -EIO; } else { @@ -948,6 +1057,16 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, return co.ret; } } + + sector_num = offset >> BDRV_SECTOR_BITS; + nb_sectors = bytes >> BDRV_SECTOR_BITS; + + assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); + assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); + assert(drv->bdrv_co_readv); + + return drv->bdrv_co_readv(bs, sector_num, nb_sectors, qiov); } static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, @@ -972,6 +1091,25 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, goto emulate_flags; } + if (drv->bdrv_aio_pwritev) { + BlockAIOCB *acb; + CoroutineIOCompletion co = { + .coroutine = qemu_coroutine_self(), + }; + + acb = drv->bdrv_aio_pwritev(bs, offset, bytes, qiov, + flags & bs->supported_write_flags, + bdrv_co_io_em_complete, &co); + flags &= ~bs->supported_write_flags; + if (acb == NULL) { + ret = -EIO; + } else { + qemu_coroutine_yield(); + ret = co.ret; + } + goto emulate_flags; + } + sector_num = offset >> BDRV_SECTOR_BITS; nb_sectors = bytes >> BDRV_SECTOR_BITS; @@ -979,28 +1117,10 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0); assert((bytes >> BDRV_SECTOR_BITS) <= BDRV_REQUEST_MAX_SECTORS); - if (drv->bdrv_co_writev_flags) { - ret = drv->bdrv_co_writev_flags(bs, sector_num, nb_sectors, qiov, - flags & bs->supported_write_flags); - flags &= ~bs->supported_write_flags; - } else if (drv->bdrv_co_writev) { - assert(!bs->supported_write_flags); - ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov); - } else { - BlockAIOCB *acb; - CoroutineIOCompletion co = { - .coroutine = qemu_coroutine_self(), - }; - - acb = bs->drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors, - bdrv_co_io_em_complete, &co); - if (acb == NULL) { - ret = -EIO; - } else { - qemu_coroutine_yield(); - ret = co.ret; - } - } + assert(drv->bdrv_co_writev); + ret = drv->bdrv_co_writev(bs, sector_num, nb_sectors, qiov, + flags & bs->supported_write_flags); + flags &= ~bs->supported_write_flags; emulate_flags: if (ret == 0 && (flags & BDRV_REQ_FUA)) { @@ -1095,6 +1215,12 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, pnum = MIN(cluster_bytes, max_transfer); } + /* Stop at EOF if the image ends in the middle of the cluster */ + if (ret == 0 && pnum == 0) { + assert(progress >= bytes); + break; + } + assert(skip_bytes < pnum); if (ret <= 0) { @@ -1115,13 +1241,15 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, /* FIXME: Should we (perhaps conditionally) be setting * BDRV_REQ_MAY_UNMAP, if it will allow for a sparser copy * that still correctly reads as zero? */ - ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum, 0); + ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, pnum, + BDRV_REQ_WRITE_UNCHANGED); } else { /* This does not change the data on the disk, it is not * necessary to flush even in cache=writethrough mode. */ ret = bdrv_driver_pwritev(bs, cluster_offset, pnum, - &local_qiov, 0); + &local_qiov, + BDRV_REQ_WRITE_UNCHANGED); } if (ret < 0) { @@ -1198,6 +1326,9 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child, mark_request_serialising(req, bdrv_get_cluster_size(bs)); } + /* BDRV_REQ_SERIALISING is only for write operation */ + assert(!(flags & BDRV_REQ_SERIALISING)); + if (!(flags & BDRV_REQ_NO_SERIALISING)) { wait_serialising_requests(req); } @@ -1336,24 +1467,6 @@ int coroutine_fn bdrv_co_preadv(BdrvChild *child, return ret; } -static int coroutine_fn bdrv_co_do_readv(BdrvChild *child, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, - BdrvRequestFlags flags) -{ - if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return -EINVAL; - } - - return bdrv_co_preadv(child, sector_num << BDRV_SECTOR_BITS, - nb_sectors << BDRV_SECTOR_BITS, qiov, flags); -} - -int coroutine_fn bdrv_co_readv(BdrvChild *child, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) -{ - return bdrv_co_do_readv(child, sector_num, nb_sectors, qiov, 0); -} - static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { @@ -1463,6 +1576,92 @@ fail: return ret; } +static inline int coroutine_fn +bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes, + BdrvTrackedRequest *req, int flags) +{ + BlockDriverState *bs = child->bs; + bool waited; + int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); + + if (bs->read_only) { + return -EPERM; + } + + /* BDRV_REQ_NO_SERIALISING is only for read operation */ + assert(!(flags & BDRV_REQ_NO_SERIALISING)); + assert(!(bs->open_flags & BDRV_O_INACTIVE)); + assert((bs->open_flags & BDRV_O_NO_IO) == 0); + assert(!(flags & ~BDRV_REQ_MASK)); + + if (flags & BDRV_REQ_SERIALISING) { + mark_request_serialising(req, bdrv_get_cluster_size(bs)); + } + + waited = wait_serialising_requests(req); + + assert(!waited || !req->serialising || + is_request_serialising_and_aligned(req)); + assert(req->overlap_offset <= offset); + assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); + assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); + + switch (req->type) { + case BDRV_TRACKED_WRITE: + case BDRV_TRACKED_DISCARD: + if (flags & BDRV_REQ_WRITE_UNCHANGED) { + assert(child->perm & (BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE)); + } else { + assert(child->perm & BLK_PERM_WRITE); + } + return notifier_with_return_list_notify(&bs->before_write_notifiers, + req); + case BDRV_TRACKED_TRUNCATE: + assert(child->perm & BLK_PERM_RESIZE); + return 0; + default: + abort(); + } +} + +static inline void coroutine_fn +bdrv_co_write_req_finish(BdrvChild *child, int64_t offset, uint64_t bytes, + BdrvTrackedRequest *req, int ret) +{ + int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); + BlockDriverState *bs = child->bs; + + atomic_inc(&bs->write_gen); + + /* + * Discard cannot extend the image, but in error handling cases, such as + * when reverting a qcow2 cluster allocation, the discarded range can pass + * the end of image file, so we cannot assert about BDRV_TRACKED_DISCARD + * here. Instead, just skip it, since semantically a discard request + * beyond EOF cannot expand the image anyway. + */ + if (ret == 0 && + (req->type == BDRV_TRACKED_TRUNCATE || + end_sector > bs->total_sectors) && + req->type != BDRV_TRACKED_DISCARD) { + bs->total_sectors = end_sector; + bdrv_parent_cb_resize(bs); + bdrv_dirty_bitmap_truncate(bs, end_sector << BDRV_SECTOR_BITS); + } + if (req->bytes) { + switch (req->type) { + case BDRV_TRACKED_WRITE: + stat64_max(&bs->wr_highest_offset, offset + bytes); + /* fall through, to set dirty bits */ + case BDRV_TRACKED_DISCARD: + bdrv_set_dirty(bs, offset, bytes); + break; + default: + break; + } + } +} + /* * Forwards an already correctly aligned write request to the BlockDriver, * after possibly fragmenting it. @@ -1473,10 +1672,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, { BlockDriverState *bs = child->bs; BlockDriver *drv = bs->drv; - bool waited; int ret; - int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); uint64_t bytes_remaining = bytes; int max_transfer; @@ -1492,19 +1689,10 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, assert((offset & (align - 1)) == 0); assert((bytes & (align - 1)) == 0); assert(!qiov || bytes == qiov->size); - assert((bs->open_flags & BDRV_O_NO_IO) == 0); - assert(!(flags & ~BDRV_REQ_MASK)); max_transfer = QEMU_ALIGN_DOWN(MIN_NON_ZERO(bs->bl.max_transfer, INT_MAX), align); - waited = wait_serialising_requests(req); - assert(!waited || !req->serialising); - assert(req->overlap_offset <= offset); - assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); - assert(child->perm & BLK_PERM_WRITE); - assert(end_sector <= bs->total_sectors || child->perm & BLK_PERM_RESIZE); - - ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); + ret = bdrv_co_write_req_prepare(child, offset, bytes, req, flags); if (!ret && bs->detect_zeroes != BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF && !(flags & BDRV_REQ_ZERO_WRITE) && drv->bdrv_co_pwrite_zeroes && @@ -1553,15 +1741,10 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child, } bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE); - atomic_inc(&bs->write_gen); - bdrv_set_dirty(bs, offset, bytes); - - stat64_max(&bs->wr_highest_offset, offset + bytes); - if (ret >= 0) { - bs->total_sectors = MAX(bs->total_sectors, end_sector); ret = 0; } + bdrv_co_write_req_finish(child, offset, bytes, req, ret); return ret; } @@ -1676,10 +1859,6 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, if (!bs->drv) { return -ENOMEDIUM; } - if (bs->read_only) { - return -EPERM; - } - assert(!(bs->open_flags & BDRV_O_INACTIVE)); ret = bdrv_check_byte_request(bs, offset, bytes); if (ret < 0) { @@ -1792,24 +1971,6 @@ out: return ret; } -static int coroutine_fn bdrv_co_do_writev(BdrvChild *child, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, - BdrvRequestFlags flags) -{ - if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { - return -EINVAL; - } - - return bdrv_co_pwritev(child, sector_num << BDRV_SECTOR_BITS, - nb_sectors << BDRV_SECTOR_BITS, qiov, flags); -} - -int coroutine_fn bdrv_co_writev(BdrvChild *child, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) -{ - return bdrv_co_do_writev(child, sector_num, nb_sectors, qiov, 0); -} - int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, int bytes, BdrvRequestFlags flags) { @@ -2529,7 +2690,7 @@ int bdrv_flush(BlockDriverState *bs) } typedef struct DiscardCo { - BlockDriverState *bs; + BdrvChild *child; int64_t offset; int bytes; int ret; @@ -2538,17 +2699,17 @@ static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque) { DiscardCo *rwco = opaque; - rwco->ret = bdrv_co_pdiscard(rwco->bs, rwco->offset, rwco->bytes); + rwco->ret = bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes); } -int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, - int bytes) +int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) { BdrvTrackedRequest req; int max_pdiscard, ret; int head, tail, align; + BlockDriverState *bs = child->bs; - if (!bs->drv) { + if (!bs || !bs->drv) { return -ENOMEDIUM; } @@ -2559,10 +2720,7 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, ret = bdrv_check_byte_request(bs, offset, bytes); if (ret < 0) { return ret; - } else if (bs->read_only) { - return -EPERM; } - assert(!(bs->open_flags & BDRV_O_INACTIVE)); /* Do nothing if disabled. */ if (!(bs->open_flags & BDRV_O_UNMAP)) { @@ -2586,7 +2744,7 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, bdrv_inc_in_flight(bs); tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_DISCARD); - ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req); + ret = bdrv_co_write_req_prepare(child, offset, bytes, &req, 0); if (ret < 0) { goto out; } @@ -2652,18 +2810,17 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, } ret = 0; out: - atomic_inc(&bs->write_gen); - bdrv_set_dirty(bs, req.offset, req.bytes); + bdrv_co_write_req_finish(child, req.offset, req.bytes, &req, ret); tracked_request_end(&req); bdrv_dec_in_flight(bs); return ret; } -int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) +int bdrv_pdiscard(BdrvChild *child, int64_t offset, int bytes) { Coroutine *co; DiscardCo rwco = { - .bs = bs, + .child = child, .offset = offset, .bytes = bytes, .ret = NOT_DONE, @@ -2674,8 +2831,8 @@ int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) bdrv_pdiscard_co_entry(&rwco); } else { co = qemu_coroutine_create(bdrv_pdiscard_co_entry, &rwco); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, rwco.ret == NOT_DONE); + bdrv_coroutine_enter(child->bs, co); + BDRV_POLL_WHILE(child->bs, rwco.ret == NOT_DONE); } return rwco.ret; @@ -2826,3 +2983,258 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host) bdrv_unregister_buf(child->bs, host); } } + +static int coroutine_fn bdrv_co_copy_range_internal( + BdrvChild *src, uint64_t src_offset, BdrvChild *dst, + uint64_t dst_offset, uint64_t bytes, + BdrvRequestFlags read_flags, BdrvRequestFlags write_flags, + bool recurse_src) +{ + BdrvTrackedRequest req; + int ret; + + if (!dst || !dst->bs) { + return -ENOMEDIUM; + } + ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes); + if (ret) { + return ret; + } + if (write_flags & BDRV_REQ_ZERO_WRITE) { + return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags); + } + + if (!src || !src->bs) { + return -ENOMEDIUM; + } + ret = bdrv_check_byte_request(src->bs, src_offset, bytes); + if (ret) { + return ret; + } + + if (!src->bs->drv->bdrv_co_copy_range_from + || !dst->bs->drv->bdrv_co_copy_range_to + || src->bs->encrypted || dst->bs->encrypted) { + return -ENOTSUP; + } + + if (recurse_src) { + bdrv_inc_in_flight(src->bs); + tracked_request_begin(&req, src->bs, src_offset, bytes, + BDRV_TRACKED_READ); + + /* BDRV_REQ_SERIALISING is only for write operation */ + assert(!(read_flags & BDRV_REQ_SERIALISING)); + if (!(read_flags & BDRV_REQ_NO_SERIALISING)) { + wait_serialising_requests(&req); + } + + ret = src->bs->drv->bdrv_co_copy_range_from(src->bs, + src, src_offset, + dst, dst_offset, + bytes, + read_flags, write_flags); + + tracked_request_end(&req); + bdrv_dec_in_flight(src->bs); + } else { + bdrv_inc_in_flight(dst->bs); + tracked_request_begin(&req, dst->bs, dst_offset, bytes, + BDRV_TRACKED_WRITE); + ret = bdrv_co_write_req_prepare(dst, dst_offset, bytes, &req, + write_flags); + if (!ret) { + ret = dst->bs->drv->bdrv_co_copy_range_to(dst->bs, + src, src_offset, + dst, dst_offset, + bytes, + read_flags, write_flags); + } + bdrv_co_write_req_finish(dst, dst_offset, bytes, &req, ret); + tracked_request_end(&req); + bdrv_dec_in_flight(dst->bs); + } + + return ret; +} + +/* Copy range from @src to @dst. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. */ +int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + trace_bdrv_co_copy_range_from(src, src_offset, dst, dst_offset, bytes, + read_flags, write_flags); + return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, + bytes, read_flags, write_flags, true); +} + +/* Copy range from @src to @dst. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. */ +int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + trace_bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, + read_flags, write_flags); + return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset, + bytes, read_flags, write_flags, false); +} + +int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + return bdrv_co_copy_range_from(src, src_offset, + dst, dst_offset, + bytes, read_flags, write_flags); +} + +static void bdrv_parent_cb_resize(BlockDriverState *bs) +{ + BdrvChild *c; + QLIST_FOREACH(c, &bs->parents, next_parent) { + if (c->role->resize) { + c->role->resize(c); + } + } +} + +/** + * Truncate file to 'offset' bytes (needed only for file protocols) + */ +int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, + PreallocMode prealloc, Error **errp) +{ + BlockDriverState *bs = child->bs; + BlockDriver *drv = bs->drv; + BdrvTrackedRequest req; + int64_t old_size, new_bytes; + int ret; + + + /* if bs->drv == NULL, bs is closed, so there's nothing to do here */ + if (!drv) { + error_setg(errp, "No medium inserted"); + return -ENOMEDIUM; + } + if (offset < 0) { + error_setg(errp, "Image size cannot be negative"); + return -EINVAL; + } + + old_size = bdrv_getlength(bs); + if (old_size < 0) { + error_setg_errno(errp, -old_size, "Failed to get old image size"); + return old_size; + } + + if (offset > old_size) { + new_bytes = offset - old_size; + } else { + new_bytes = 0; + } + + bdrv_inc_in_flight(bs); + tracked_request_begin(&req, bs, offset - new_bytes, new_bytes, + BDRV_TRACKED_TRUNCATE); + + /* If we are growing the image and potentially using preallocation for the + * new area, we need to make sure that no write requests are made to it + * concurrently or they might be overwritten by preallocation. */ + if (new_bytes) { + mark_request_serialising(&req, 1); + } + if (bs->read_only) { + error_setg(errp, "Image is read-only"); + ret = -EACCES; + goto out; + } + ret = bdrv_co_write_req_prepare(child, offset - new_bytes, new_bytes, &req, + 0); + if (ret < 0) { + error_setg_errno(errp, -ret, + "Failed to prepare request for truncation"); + goto out; + } + + if (!drv->bdrv_co_truncate) { + if (bs->file && drv->is_filter) { + ret = bdrv_co_truncate(bs->file, offset, prealloc, errp); + goto out; + } + error_setg(errp, "Image format driver does not support resize"); + ret = -ENOTSUP; + goto out; + } + + ret = drv->bdrv_co_truncate(bs, offset, prealloc, errp); + if (ret < 0) { + goto out; + } + ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not refresh total sector count"); + } else { + offset = bs->total_sectors * BDRV_SECTOR_SIZE; + } + /* It's possible that truncation succeeded but refresh_total_sectors + * failed, but the latter doesn't affect how we should finish the request. + * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. */ + bdrv_co_write_req_finish(child, offset - new_bytes, new_bytes, &req, 0); + +out: + tracked_request_end(&req); + bdrv_dec_in_flight(bs); + + return ret; +} + +typedef struct TruncateCo { + BdrvChild *child; + int64_t offset; + PreallocMode prealloc; + Error **errp; + int ret; +} TruncateCo; + +static void coroutine_fn bdrv_truncate_co_entry(void *opaque) +{ + TruncateCo *tco = opaque; + tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc, + tco->errp); +} + +int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, + Error **errp) +{ + Coroutine *co; + TruncateCo tco = { + .child = child, + .offset = offset, + .prealloc = prealloc, + .errp = errp, + .ret = NOT_DONE, + }; + + if (qemu_in_coroutine()) { + /* Fast-path if already in coroutine context */ + bdrv_truncate_co_entry(&tco); + } else { + co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco); + qemu_coroutine_enter(co); + BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE); + } + + return tco.ret; +} diff --git a/block/iscsi.c b/block/iscsi.c index f5aecfc883..bb69faf34a 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -33,6 +33,7 @@ #include "qemu/bitops.h" #include "qemu/bitmap.h" #include "block/block_int.h" +#include "block/qdict.h" #include "scsi/constants.h" #include "qemu/iov.h" #include "qemu/option.h" @@ -43,6 +44,7 @@ #include "qapi/qmp/qstring.h" #include "crypto/secret.h" #include "scsi/utils.h" +#include "trace.h" /* Conflict between scsi/utils.h and libiscsi! :( */ #define SCSI_XFER_NONE ISCSI_XFER_NONE @@ -68,6 +70,7 @@ typedef struct IscsiLun { QemuMutex mutex; struct scsi_inquiry_logical_block_provisioning lbp; struct scsi_inquiry_block_limits bl; + struct scsi_inquiry_device_designator *dd; unsigned char *zeroblock; /* The allocmap tracks which clusters (pages) on the iSCSI target are * allocated and which are not. In case a target returns zeros for @@ -555,9 +558,20 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun, offset / iscsilun->cluster_size) == size); } +static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask, + IscsiLun *iscsilun) +{ + while (!iTask->complete) { + iscsi_set_events(iscsilun); + qemu_mutex_unlock(&iscsilun->mutex); + qemu_coroutine_yield(); + qemu_mutex_lock(&iscsilun->mutex); + } +} + static int coroutine_fn -iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - QEMUIOVector *iov, int flags) +iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, + QEMUIOVector *iov, int flags) { IscsiLun *iscsilun = bs->opaque; struct IscsiTask iTask; @@ -616,12 +630,7 @@ retry: scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); #endif - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); @@ -692,13 +701,7 @@ retry: ret = -ENOMEM; goto out_unlock; } - - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.do_retry) { if (iTask.task != NULL) { @@ -732,7 +735,7 @@ retry: goto out_unlock; } - *pnum = lbasd->num_blocks * iscsilun->block_size; + *pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size; if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { @@ -862,13 +865,8 @@ retry: #if LIBISCSI_API_VERSION < (20160603) scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); #endif - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); iTask.task = NULL; @@ -905,12 +903,7 @@ retry: return -ENOMEM; } - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); @@ -1142,12 +1135,7 @@ retry: goto out_unlock; } - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.task != NULL) { scsi_free_scsi_task(iTask.task); @@ -1243,12 +1231,7 @@ retry: return -ENOMEM; } - while (!iTask.complete) { - iscsi_set_events(iscsilun); - qemu_mutex_unlock(&iscsilun->mutex); - qemu_coroutine_yield(); - qemu_mutex_lock(&iscsilun->mutex); - } + iscsi_co_wait_for_task(&iTask, iscsilun); if (iTask.status == SCSI_STATUS_CHECK_CONDITION && iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && @@ -1732,14 +1715,34 @@ static QemuOptsList runtime_opts = { .name = "timeout", .type = QEMU_OPT_NUMBER, }, - { - .name = "filename", - .type = QEMU_OPT_STRING, - }, { /* end of list */ } }, }; +static void iscsi_save_designator(IscsiLun *lun, + struct scsi_inquiry_device_identification *inq_di) +{ + struct scsi_inquiry_device_designator *desig, *copy = NULL; + + for (desig = inq_di->designators; desig; desig = desig->next) { + if (desig->association || + desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) { + continue; + } + /* NAA works better than T10 vendor ID based designator. */ + if (!copy || copy->designator_type < desig->designator_type) { + copy = desig; + } + } + if (copy) { + lun->dd = g_new(struct scsi_inquiry_device_designator, 1); + *lun->dd = *copy; + lun->dd->next = NULL; + lun->dd->designator = g_malloc(copy->designator_length); + memcpy(lun->dd->designator, copy->designator, copy->designator_length); + } +} + static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { @@ -1751,27 +1754,12 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, char *initiator_name = NULL; QemuOpts *opts; Error *local_err = NULL; - const char *transport_name, *portal, *target, *filename; + const char *transport_name, *portal, *target; #if LIBISCSI_API_VERSION >= (20160603) enum iscsi_transport_type transport; #endif int i, ret = 0, timeout = 0, lun; - /* If we are given a filename, parse the filename, with precedence given to - * filename encoded options */ - filename = qdict_get_try_str(options, "filename"); - if (filename) { - warn_report("'filename' option specified. " - "This is an unsupported option, and may be deprecated " - "in the future"); - iscsi_parse_filename(filename, options, &local_err); - if (local_err) { - ret = -EINVAL; - error_propagate(errp, local_err); - goto exit; - } - } - opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { @@ -1922,6 +1910,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, struct scsi_task *inq_task; struct scsi_inquiry_logical_block_provisioning *inq_lbp; struct scsi_inquiry_block_limits *inq_bl; + struct scsi_inquiry_device_identification *inq_di; switch (inq_vpd->pages[i]) { case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, @@ -1947,6 +1936,17 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, sizeof(struct scsi_inquiry_block_limits)); scsi_free_scsi_task(inq_task); break; + case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION: + inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, + SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION, + (void **) &inq_di, errp); + if (inq_task == NULL) { + ret = -EINVAL; + goto out; + } + iscsi_save_designator(iscsilun, inq_di); + scsi_free_scsi_task(inq_task); + break; default: break; } @@ -1989,7 +1989,7 @@ out: } memset(iscsilun, 0, sizeof(IscsiLun)); } -exit: + return ret; } @@ -2003,6 +2003,10 @@ static void iscsi_close(BlockDriverState *bs) iscsi_logout_sync(iscsi); } iscsi_destroy_context(iscsi); + if (iscsilun->dd) { + g_free(iscsilun->dd->designator); + g_free(iscsilun->dd); + } g_free(iscsilun->zeroblock); iscsi_allocmap_free(iscsilun); qemu_mutex_destroy(&iscsilun->mutex); @@ -2082,8 +2086,8 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state) } } -static int iscsi_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { IscsiLun *iscsilun = bs->opaque; Error *local_err = NULL; @@ -2143,7 +2147,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt } else { ret = iscsi_open(bs, bs_options, 0, NULL); } - QDECREF(bs_options); + qobject_unref(bs_options); if (ret != 0) { goto out; @@ -2184,6 +2188,226 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs, iscsi_allocmap_invalidate(iscsilun); } +static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, + read_flags, write_flags); +} + +static struct scsi_task *iscsi_xcopy_task(int param_len) +{ + struct scsi_task *task; + + task = g_new0(struct scsi_task, 1); + + task->cdb[0] = EXTENDED_COPY; + task->cdb[10] = (param_len >> 24) & 0xFF; + task->cdb[11] = (param_len >> 16) & 0xFF; + task->cdb[12] = (param_len >> 8) & 0xFF; + task->cdb[13] = param_len & 0xFF; + task->cdb_size = 16; + task->xfer_dir = SCSI_XFER_WRITE; + task->expxferlen = param_len; + + return task; +} + +static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun) +{ + struct scsi_inquiry_device_designator *dd = lun->dd; + + memset(desc, 0, 32); + desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */ + desc[4] = dd->code_set; + desc[5] = (dd->designator_type & 0xF) + | ((dd->association & 3) << 4); + desc[7] = dd->designator_length; + memcpy(desc + 8, dd->designator, MIN(dd->designator_length, 20)); + + desc[28] = 0; + desc[29] = (lun->block_size >> 16) & 0xFF; + desc[30] = (lun->block_size >> 8) & 0xFF; + desc[31] = lun->block_size & 0xFF; +} + +static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index, + int dst_index) +{ + hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */ + hdr[1] = ((dc << 1) | cat) & 0xFF; + hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF; + /* don't account for the first 4 bytes in descriptor header*/ + hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF; + hdr[4] = (src_index >> 8) & 0xFF; + hdr[5] = src_index & 0xFF; + hdr[6] = (dst_index >> 8) & 0xFF; + hdr[7] = dst_index & 0xFF; +} + +static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat, + int src_index, int dst_index, int num_blks, + uint64_t src_lba, uint64_t dst_lba) +{ + iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index); + + /* The caller should verify the request size */ + assert(num_blks < 65536); + desc[10] = (num_blks >> 8) & 0xFF; + desc[11] = num_blks & 0xFF; + desc[12] = (src_lba >> 56) & 0xFF; + desc[13] = (src_lba >> 48) & 0xFF; + desc[14] = (src_lba >> 40) & 0xFF; + desc[15] = (src_lba >> 32) & 0xFF; + desc[16] = (src_lba >> 24) & 0xFF; + desc[17] = (src_lba >> 16) & 0xFF; + desc[18] = (src_lba >> 8) & 0xFF; + desc[19] = src_lba & 0xFF; + desc[20] = (dst_lba >> 56) & 0xFF; + desc[21] = (dst_lba >> 48) & 0xFF; + desc[22] = (dst_lba >> 40) & 0xFF; + desc[23] = (dst_lba >> 32) & 0xFF; + desc[24] = (dst_lba >> 24) & 0xFF; + desc[25] = (dst_lba >> 16) & 0xFF; + desc[26] = (dst_lba >> 8) & 0xFF; + desc[27] = dst_lba & 0xFF; +} + +static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str, + int list_id_usage, int prio, + int tgt_desc_len, + int seg_desc_len, int inline_data_len) +{ + buf[0] = list_id; + buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7); + buf[2] = (tgt_desc_len >> 8) & 0xFF; + buf[3] = tgt_desc_len & 0xFF; + buf[8] = (seg_desc_len >> 24) & 0xFF; + buf[9] = (seg_desc_len >> 16) & 0xFF; + buf[10] = (seg_desc_len >> 8) & 0xFF; + buf[11] = seg_desc_len & 0xFF; + buf[12] = (inline_data_len >> 24) & 0xFF; + buf[13] = (inline_data_len >> 16) & 0xFF; + buf[14] = (inline_data_len >> 8) & 0xFF; + buf[15] = inline_data_len & 0xFF; +} + +static void iscsi_xcopy_data(struct iscsi_data *data, + IscsiLun *src, int64_t src_lba, + IscsiLun *dst, int64_t dst_lba, + uint16_t num_blocks) +{ + uint8_t *buf; + const int src_offset = XCOPY_DESC_OFFSET; + const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE; + const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE; + + data->size = XCOPY_DESC_OFFSET + + IDENT_DESCR_TGT_DESCR_SIZE * 2 + + XCOPY_BLK2BLK_SEG_DESC_SIZE; + data->data = g_malloc0(data->size); + buf = data->data; + + /* Initialise the parameter list header */ + iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */, + 0, 2 * IDENT_DESCR_TGT_DESCR_SIZE, + XCOPY_BLK2BLK_SEG_DESC_SIZE, + 0); + + /* Initialise CSCD list with one src + one dst descriptor */ + iscsi_populate_target_desc(&buf[src_offset], src); + iscsi_populate_target_desc(&buf[dst_offset], dst); + + /* Initialise one segment descriptor */ + iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks, + src_lba, dst_lba); +} + +static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + IscsiLun *dst_lun = dst->bs->opaque; + IscsiLun *src_lun; + struct IscsiTask iscsi_task; + struct iscsi_data data; + int r = 0; + int block_size; + + if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) { + return -ENOTSUP; + } + src_lun = src->bs->opaque; + + if (!src_lun->dd || !dst_lun->dd) { + return -ENOTSUP; + } + if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) { + return -ENOTSUP; + } + if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) { + return -ENOTSUP; + } + if (dst_lun->block_size != src_lun->block_size || + !dst_lun->block_size) { + return -ENOTSUP; + } + + block_size = dst_lun->block_size; + if (bytes / block_size > 65535) { + return -ENOTSUP; + } + + iscsi_xcopy_data(&data, + src_lun, src_offset / block_size, + dst_lun, dst_offset / block_size, + bytes / block_size); + + iscsi_co_init_iscsitask(dst_lun, &iscsi_task); + + qemu_mutex_lock(&dst_lun->mutex); + iscsi_task.task = iscsi_xcopy_task(data.size); +retry: + if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun, + iscsi_task.task, iscsi_co_generic_cb, + &data, + &iscsi_task) != 0) { + r = -EIO; + goto out_unlock; + } + + iscsi_co_wait_for_task(&iscsi_task, dst_lun); + + if (iscsi_task.do_retry) { + iscsi_task.complete = 0; + goto retry; + } + + if (iscsi_task.status != SCSI_STATUS_GOOD) { + r = iscsi_task.err_code; + goto out_unlock; + } + +out_unlock: + + trace_iscsi_xcopy(src_lun, src_offset, dst_lun, dst_offset, bytes, r); + g_free(iscsi_task.task); + qemu_mutex_unlock(&dst_lun->mutex); + g_free(iscsi_task.err_str); + return r; +} + static QemuOptsList iscsi_create_opts = { .name = "iscsi-create-opts", .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), @@ -2213,14 +2437,16 @@ static BlockDriver bdrv_iscsi = { .bdrv_getlength = iscsi_getlength, .bdrv_get_info = iscsi_get_info, - .bdrv_truncate = iscsi_truncate, + .bdrv_co_truncate = iscsi_co_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, .bdrv_co_block_status = iscsi_co_block_status, .bdrv_co_pdiscard = iscsi_co_pdiscard, + .bdrv_co_copy_range_from = iscsi_co_copy_range_from, + .bdrv_co_copy_range_to = iscsi_co_copy_range_to, .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, .bdrv_co_readv = iscsi_co_readv, - .bdrv_co_writev_flags = iscsi_co_writev_flags, + .bdrv_co_writev = iscsi_co_writev, .bdrv_co_flush_to_disk = iscsi_co_flush, #ifdef __linux__ @@ -2248,14 +2474,16 @@ static BlockDriver bdrv_iser = { .bdrv_getlength = iscsi_getlength, .bdrv_get_info = iscsi_get_info, - .bdrv_truncate = iscsi_truncate, + .bdrv_co_truncate = iscsi_co_truncate, .bdrv_refresh_limits = iscsi_refresh_limits, .bdrv_co_block_status = iscsi_co_block_status, .bdrv_co_pdiscard = iscsi_co_pdiscard, + .bdrv_co_copy_range_from = iscsi_co_copy_range_from, + .bdrv_co_copy_range_to = iscsi_co_copy_range_to, .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, .bdrv_co_readv = iscsi_co_readv, - .bdrv_co_writev_flags = iscsi_co_writev_flags, + .bdrv_co_writev = iscsi_co_writev, .bdrv_co_flush_to_disk = iscsi_co_flush, #ifdef __linux__ diff --git a/block/linux-aio.c b/block/linux-aio.c index 88b8d55ec7..19eb922fdd 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -15,6 +15,7 @@ #include "block/raw-aio.h" #include "qemu/event_notifier.h" #include "qemu/coroutine.h" +#include "qapi/error.h" #include @@ -470,16 +471,21 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) qemu_laio_poll_cb); } -LinuxAioState *laio_init(void) +LinuxAioState *laio_init(Error **errp) { + int rc; LinuxAioState *s; s = g_malloc0(sizeof(*s)); - if (event_notifier_init(&s->e, false) < 0) { + rc = event_notifier_init(&s->e, false); + if (rc < 0) { + error_setg_errno(errp, -rc, "failed to to initialize event notifier"); goto out_free_state; } - if (io_setup(MAX_EVENTS, &s->ctx) != 0) { + rc = io_setup(MAX_EVENTS, &s->ctx); + if (rc < 0) { + error_setg_errno(errp, -rc, "failed to create linux AIO context"); goto out_close_efd; } diff --git a/block/mirror.c b/block/mirror.c index 820f512c7b..b48c3f8cf5 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -13,6 +13,8 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/coroutine.h" +#include "qemu/range.h" #include "trace.h" #include "block/blockjob_int.h" #include "block/block_int.h" @@ -22,7 +24,6 @@ #include "qemu/ratelimit.h" #include "qemu/bitmap.h" -#define SLICE_TIME 100000000ULL /* ns */ #define MAX_IN_FLIGHT 16 #define MAX_IO_BYTES (1 << 20) /* 1 Mb */ #define DEFAULT_MIRROR_BUF_SIZE (MAX_IN_FLIGHT * MAX_IO_BYTES) @@ -34,12 +35,12 @@ typedef struct MirrorBuffer { QSIMPLEQ_ENTRY(MirrorBuffer) next; } MirrorBuffer; +typedef struct MirrorOp MirrorOp; + typedef struct MirrorBlockJob { BlockJob common; - RateLimit limit; BlockBackend *target; BlockDriverState *mirror_top_bs; - BlockDriverState *source; BlockDriverState *base; /* The name of the graph node to replace */ @@ -50,8 +51,12 @@ typedef struct MirrorBlockJob { Error *replace_blocker; bool is_none_mode; BlockMirrorBackingMode backing_mode; + MirrorCopyMode copy_mode; BlockdevOnError on_source_error, on_target_error; bool synced; + /* Set when the target is synced (dirty bitmap is clean, nothing + * in flight) and the job is running in active mode */ + bool actively_synced; bool should_complete; int64_t granularity; size_t buf_size; @@ -67,25 +72,47 @@ typedef struct MirrorBlockJob { unsigned long *in_flight_bitmap; int in_flight; int64_t bytes_in_flight; + QTAILQ_HEAD(MirrorOpList, MirrorOp) ops_in_flight; int ret; bool unmap; - bool waiting_for_io; int target_cluster_size; int max_iov; bool initial_zeroing_ongoing; + int in_active_write_counter; } MirrorBlockJob; -typedef struct MirrorOp { +typedef struct MirrorBDSOpaque { + MirrorBlockJob *job; +} MirrorBDSOpaque; + +struct MirrorOp { MirrorBlockJob *s; QEMUIOVector qiov; int64_t offset; uint64_t bytes; -} MirrorOp; + + /* The pointee is set by mirror_co_read(), mirror_co_zero(), and + * mirror_co_discard() before yielding for the first time */ + int64_t *bytes_handled; + + bool is_pseudo_op; + bool is_active_write; + CoQueue waiting_requests; + + QTAILQ_ENTRY(MirrorOp) next; +}; + +typedef enum MirrorMethod { + MIRROR_METHOD_COPY, + MIRROR_METHOD_ZERO, + MIRROR_METHOD_DISCARD, +} MirrorMethod; static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, int error) { s->synced = false; + s->actively_synced = false; if (read) { return block_job_error_action(&s->common, s->on_source_error, true, error); @@ -95,7 +122,42 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, } } -static void mirror_iteration_done(MirrorOp *op, int ret) +static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self, + MirrorBlockJob *s, + uint64_t offset, + uint64_t bytes) +{ + uint64_t self_start_chunk = offset / s->granularity; + uint64_t self_end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity); + uint64_t self_nb_chunks = self_end_chunk - self_start_chunk; + + while (find_next_bit(s->in_flight_bitmap, self_end_chunk, + self_start_chunk) < self_end_chunk && + s->ret >= 0) + { + MirrorOp *op; + + QTAILQ_FOREACH(op, &s->ops_in_flight, next) { + uint64_t op_start_chunk = op->offset / s->granularity; + uint64_t op_nb_chunks = DIV_ROUND_UP(op->offset + op->bytes, + s->granularity) - + op_start_chunk; + + if (op == self) { + continue; + } + + if (ranges_overlap(self_start_chunk, self_nb_chunks, + op_start_chunk, op_nb_chunks)) + { + qemu_co_queue_wait(&op->waiting_requests, NULL); + break; + } + } + } +} + +static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret) { MirrorBlockJob *s = op->s; struct iovec *iov; @@ -115,26 +177,25 @@ static void mirror_iteration_done(MirrorOp *op, int ret) chunk_num = op->offset / s->granularity; nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); + bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); + QTAILQ_REMOVE(&s->ops_in_flight, op, next); if (ret >= 0) { if (s->cow_bitmap) { bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); } if (!s->initial_zeroing_ongoing) { - s->common.offset += op->bytes; + job_progress_update(&s->common.job, op->bytes); } } qemu_iovec_destroy(&op->qiov); - g_free(op); - if (s->waiting_for_io) { - qemu_coroutine_enter(s->common.co); - } + qemu_co_queue_restart_all(&op->waiting_requests); + g_free(op); } -static void mirror_write_complete(void *opaque, int ret) +static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret) { - MirrorOp *op = opaque; MirrorBlockJob *s = op->s; aio_context_acquire(blk_get_aio_context(s->common.blk)); @@ -151,9 +212,8 @@ static void mirror_write_complete(void *opaque, int ret) aio_context_release(blk_get_aio_context(s->common.blk)); } -static void mirror_read_complete(void *opaque, int ret) +static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret) { - MirrorOp *op = opaque; MirrorBlockJob *s = op->s; aio_context_acquire(blk_get_aio_context(s->common.blk)); @@ -168,8 +228,9 @@ static void mirror_read_complete(void *opaque, int ret) mirror_iteration_done(op, ret); } else { - blk_aio_pwritev(s->target, op->offset, &op->qiov, - 0, mirror_write_complete, op); + ret = blk_co_pwritev(s->target, op->offset, + op->qiov.size, &op->qiov, 0); + mirror_write_complete(op, ret); } aio_context_release(blk_get_aio_context(s->common.blk)); } @@ -218,68 +279,80 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset, return ret; } -static inline void mirror_wait_for_io(MirrorBlockJob *s) +static inline void mirror_wait_for_any_operation(MirrorBlockJob *s, bool active) { - assert(!s->waiting_for_io); - s->waiting_for_io = true; - qemu_coroutine_yield(); - s->waiting_for_io = false; + MirrorOp *op; + + QTAILQ_FOREACH(op, &s->ops_in_flight, next) { + /* Do not wait on pseudo ops, because it may in turn wait on + * some other operation to start, which may in fact be the + * caller of this function. Since there is only one pseudo op + * at any given time, we will always find some real operation + * to wait on. */ + if (!op->is_pseudo_op && op->is_active_write == active) { + qemu_co_queue_wait(&op->waiting_requests, NULL); + return; + } + } + abort(); } -/* Submit async read while handling COW. - * Returns: The number of bytes copied after and including offset, - * excluding any bytes copied prior to offset due to alignment. - * This will be @bytes if no alignment is necessary, or - * (new_end - offset) if tail is rounded up or down due to - * alignment or buffer limit. - */ -static uint64_t mirror_do_read(MirrorBlockJob *s, int64_t offset, - uint64_t bytes) +static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) { - BlockBackend *source = s->common.blk; + /* Only non-active operations use up in-flight slots */ + mirror_wait_for_any_operation(s, false); +} + +/* Perform a mirror copy operation. + * + * *op->bytes_handled is set to the number of bytes copied after and + * including offset, excluding any bytes copied prior to offset due + * to alignment. This will be op->bytes if no alignment is necessary, + * or (new_end - op->offset) if the tail is rounded up or down due to + * alignment or buffer limit. + */ +static void coroutine_fn mirror_co_read(void *opaque) +{ + MirrorOp *op = opaque; + MirrorBlockJob *s = op->s; int nb_chunks; uint64_t ret; - MirrorOp *op; uint64_t max_bytes; max_bytes = s->granularity * s->max_iov; /* We can only handle as much as buf_size at a time. */ - bytes = MIN(s->buf_size, MIN(max_bytes, bytes)); - assert(bytes); - assert(bytes < BDRV_REQUEST_MAX_BYTES); - ret = bytes; + op->bytes = MIN(s->buf_size, MIN(max_bytes, op->bytes)); + assert(op->bytes); + assert(op->bytes < BDRV_REQUEST_MAX_BYTES); + *op->bytes_handled = op->bytes; if (s->cow_bitmap) { - ret += mirror_cow_align(s, &offset, &bytes); + *op->bytes_handled += mirror_cow_align(s, &op->offset, &op->bytes); } - assert(bytes <= s->buf_size); + /* Cannot exceed BDRV_REQUEST_MAX_BYTES + INT_MAX */ + assert(*op->bytes_handled <= UINT_MAX); + assert(op->bytes <= s->buf_size); /* The offset is granularity-aligned because: * 1) Caller passes in aligned values; * 2) mirror_cow_align is used only when target cluster is larger. */ - assert(QEMU_IS_ALIGNED(offset, s->granularity)); + assert(QEMU_IS_ALIGNED(op->offset, s->granularity)); /* The range is sector-aligned, since bdrv_getlength() rounds up. */ - assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE)); - nb_chunks = DIV_ROUND_UP(bytes, s->granularity); + assert(QEMU_IS_ALIGNED(op->bytes, BDRV_SECTOR_SIZE)); + nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity); while (s->buf_free_count < nb_chunks) { - trace_mirror_yield_in_flight(s, offset, s->in_flight); - mirror_wait_for_io(s); + trace_mirror_yield_in_flight(s, op->offset, s->in_flight); + mirror_wait_for_free_in_flight_slot(s); } - /* Allocate a MirrorOp that is used as an AIO callback. */ - op = g_new(MirrorOp, 1); - op->s = s; - op->offset = offset; - op->bytes = bytes; - /* Now make a QEMUIOVector taking enough granularity-sized chunks * from s->buf_free. */ qemu_iovec_init(&op->qiov, nb_chunks); while (nb_chunks-- > 0) { MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); - size_t remaining = bytes - op->qiov.size; + size_t remaining = op->bytes - op->qiov.size; QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next); s->buf_free_count--; @@ -288,44 +361,92 @@ static uint64_t mirror_do_read(MirrorBlockJob *s, int64_t offset, /* Copy the dirty cluster. */ s->in_flight++; - s->bytes_in_flight += bytes; - trace_mirror_one_iteration(s, offset, bytes); + s->bytes_in_flight += op->bytes; + trace_mirror_one_iteration(s, op->offset, op->bytes); - blk_aio_preadv(source, offset, &op->qiov, 0, mirror_read_complete, op); - return ret; + ret = bdrv_co_preadv(s->mirror_top_bs->backing, op->offset, op->bytes, + &op->qiov, 0); + mirror_read_complete(op, ret); } -static void mirror_do_zero_or_discard(MirrorBlockJob *s, - int64_t offset, - uint64_t bytes, - bool is_discard) +static void coroutine_fn mirror_co_zero(void *opaque) +{ + MirrorOp *op = opaque; + int ret; + + op->s->in_flight++; + op->s->bytes_in_flight += op->bytes; + *op->bytes_handled = op->bytes; + + ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, + op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); + mirror_write_complete(op, ret); +} + +static void coroutine_fn mirror_co_discard(void *opaque) +{ + MirrorOp *op = opaque; + int ret; + + op->s->in_flight++; + op->s->bytes_in_flight += op->bytes; + *op->bytes_handled = op->bytes; + + ret = blk_co_pdiscard(op->s->target, op->offset, op->bytes); + mirror_write_complete(op, ret); +} + +static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, + unsigned bytes, MirrorMethod mirror_method) { MirrorOp *op; + Coroutine *co; + int64_t bytes_handled = -1; - /* Allocate a MirrorOp that is used as an AIO callback. The qiov is zeroed - * so the freeing in mirror_iteration_done is nop. */ - op = g_new0(MirrorOp, 1); - op->s = s; - op->offset = offset; - op->bytes = bytes; + op = g_new(MirrorOp, 1); + *op = (MirrorOp){ + .s = s, + .offset = offset, + .bytes = bytes, + .bytes_handled = &bytes_handled, + }; + qemu_co_queue_init(&op->waiting_requests); - s->in_flight++; - s->bytes_in_flight += bytes; - if (is_discard) { - blk_aio_pdiscard(s->target, offset, - op->bytes, mirror_write_complete, op); - } else { - blk_aio_pwrite_zeroes(s->target, offset, - op->bytes, s->unmap ? BDRV_REQ_MAY_UNMAP : 0, - mirror_write_complete, op); + switch (mirror_method) { + case MIRROR_METHOD_COPY: + co = qemu_coroutine_create(mirror_co_read, op); + break; + case MIRROR_METHOD_ZERO: + co = qemu_coroutine_create(mirror_co_zero, op); + break; + case MIRROR_METHOD_DISCARD: + co = qemu_coroutine_create(mirror_co_discard, op); + break; + default: + abort(); } + + QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); + qemu_coroutine_enter(co); + /* At this point, ownership of op has been moved to the coroutine + * and the object may already be freed */ + + /* Assert that this value has been set */ + assert(bytes_handled >= 0); + + /* Same assertion as in mirror_co_read() (and for mirror_co_read() + * and mirror_co_discard(), bytes_handled == op->bytes, which + * is the @bytes parameter given to this function) */ + assert(bytes_handled <= UINT_MAX); + return bytes_handled; } static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) { - BlockDriverState *source = s->source; - int64_t offset, first_chunk; - uint64_t delay_ns = 0; + BlockDriverState *source = s->mirror_top_bs->backing->bs; + MirrorOp *pseudo_op; + int64_t offset; + uint64_t delay_ns = 0, ret = 0; /* At least the first dirty chunk is mirrored in one iteration. */ int nb_chunks = 1; bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target)); @@ -341,13 +462,9 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) } bdrv_dirty_bitmap_unlock(s->dirty_bitmap); - first_chunk = offset / s->granularity; - while (test_bit(first_chunk, s->in_flight_bitmap)) { - trace_mirror_yield_in_flight(s, offset, s->in_flight); - mirror_wait_for_io(s); - } + mirror_wait_on_conflicts(NULL, s, offset, 1); - block_job_pause_point(&s->common); + job_pause_point(&s->common.job); /* Find the number of consective dirty chunks following the first dirty * one, and wait for in flight requests in them. */ @@ -382,16 +499,27 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) nb_chunks * s->granularity); bdrv_dirty_bitmap_unlock(s->dirty_bitmap); + /* Before claiming an area in the in-flight bitmap, we have to + * create a MirrorOp for it so that conflicting requests can wait + * for it. mirror_perform() will create the real MirrorOps later, + * for now we just create a pseudo operation that will wake up all + * conflicting requests once all real operations have been + * launched. */ + pseudo_op = g_new(MirrorOp, 1); + *pseudo_op = (MirrorOp){ + .offset = offset, + .bytes = nb_chunks * s->granularity, + .is_pseudo_op = true, + }; + qemu_co_queue_init(&pseudo_op->waiting_requests); + QTAILQ_INSERT_TAIL(&s->ops_in_flight, pseudo_op, next); + bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks); while (nb_chunks > 0 && offset < s->bdev_length) { int ret; int64_t io_bytes; int64_t io_bytes_acct; - enum MirrorMethod { - MIRROR_METHOD_COPY, - MIRROR_METHOD_ZERO, - MIRROR_METHOD_DISCARD - } mirror_method = MIRROR_METHOD_COPY; + MirrorMethod mirror_method = MIRROR_METHOD_COPY; assert(!(offset % s->granularity)); ret = bdrv_block_status_above(source, NULL, offset, @@ -421,39 +549,34 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) while (s->in_flight >= MAX_IN_FLIGHT) { trace_mirror_yield_in_flight(s, offset, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); } if (s->ret < 0) { - return 0; + ret = 0; + goto fail; } io_bytes = mirror_clip_bytes(s, offset, io_bytes); - switch (mirror_method) { - case MIRROR_METHOD_COPY: - io_bytes = io_bytes_acct = mirror_do_read(s, offset, io_bytes); - break; - case MIRROR_METHOD_ZERO: - case MIRROR_METHOD_DISCARD: - mirror_do_zero_or_discard(s, offset, io_bytes, - mirror_method == MIRROR_METHOD_DISCARD); - if (write_zeroes_ok) { - io_bytes_acct = 0; - } else { - io_bytes_acct = io_bytes; - } - break; - default: - abort(); + io_bytes = mirror_perform(s, offset, io_bytes, mirror_method); + if (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok) { + io_bytes_acct = 0; + } else { + io_bytes_acct = io_bytes; } assert(io_bytes); offset += io_bytes; nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity); - if (s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, io_bytes_acct); - } + delay_ns = block_job_ratelimit_get_delay(&s->common, io_bytes_acct); } - return delay_ns; + + ret = delay_ns; +fail: + QTAILQ_REMOVE(&s->ops_in_flight, pseudo_op, next); + qemu_co_queue_restart_all(&pseudo_op->waiting_requests); + g_free(pseudo_op); + + return ret; } static void mirror_free_init(MirrorBlockJob *s) @@ -480,7 +603,7 @@ static void mirror_free_init(MirrorBlockJob *s) static void mirror_wait_for_all_io(MirrorBlockJob *s) { while (s->in_flight > 0) { - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); } } @@ -488,12 +611,14 @@ typedef struct { int ret; } MirrorExitData; -static void mirror_exit(BlockJob *job, void *opaque) +static void mirror_exit(Job *job, void *opaque) { - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); + BlockJob *bjob = &s->common; MirrorExitData *data = opaque; + MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque; AioContext *replace_aio_context = NULL; - BlockDriverState *src = s->source; + BlockDriverState *src = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); BlockDriverState *mirror_top_bs = s->mirror_top_bs; Error *local_err = NULL; @@ -501,7 +626,7 @@ static void mirror_exit(BlockJob *job, void *opaque) bdrv_release_dirty_bitmap(src, s->dirty_bitmap); /* Make sure that the source BDS doesn't go away before we called - * block_job_completed(). */ + * job_completed(). */ bdrv_ref(src); bdrv_ref(mirror_top_bs); bdrv_ref(target_bs); @@ -572,7 +697,7 @@ static void mirror_exit(BlockJob *job, void *opaque) * the blockers on the intermediate nodes so that the resulting state is * valid. Also give up permissions on mirror_top_bs->backing, which might * block the removal. */ - block_job_remove_all_bdrv(job); + block_job_remove_all_bdrv(bjob); bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, &error_abort); bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort); @@ -580,11 +705,12 @@ static void mirror_exit(BlockJob *job, void *opaque) /* We just changed the BDS the job BB refers to (with either or both of the * bdrv_replace_node() calls), so switch the BB back so the cleanup does * the right thing. We don't need any permissions any more now. */ - blk_remove_bs(job->blk); - blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); - blk_insert_bs(job->blk, mirror_top_bs, &error_abort); + blk_remove_bs(bjob->blk); + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); + blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); - block_job_completed(&s->common, data->ret); + bs_opaque->job = NULL; + job_completed(job, data->ret, NULL); g_free(data); bdrv_drained_end(src); @@ -596,11 +722,11 @@ static void mirror_throttle(MirrorBlockJob *s) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - if (now - s->last_pause_ns > SLICE_TIME) { + if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) { s->last_pause_ns = now; - block_job_sleep_ns(&s->common, 0); + job_sleep_ns(&s->common.job, 0); } else { - block_job_pause_point(&s->common); + job_pause_point(&s->common.job); } } @@ -608,7 +734,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) { int64_t offset; BlockDriverState *base = s->base; - BlockDriverState *bs = s->source; + BlockDriverState *bs = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); int ret; int64_t count; @@ -626,7 +752,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) mirror_throttle(s); - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { s->initial_zeroing_ongoing = false; return 0; } @@ -634,11 +760,11 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) if (s->in_flight >= MAX_IN_FLIGHT) { trace_mirror_yield(s, UINT64_MAX, s->buf_free_count, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); continue; } - mirror_do_zero_or_discard(s, offset, bytes, false); + mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO); offset += bytes; } @@ -654,7 +780,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s) mirror_throttle(s); - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { return 0; } @@ -690,7 +816,7 @@ static void coroutine_fn mirror_run(void *opaque) { MirrorBlockJob *s = opaque; MirrorExitData *data; - BlockDriverState *bs = s->source; + BlockDriverState *bs = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); bool need_drain = true; int64_t length; @@ -699,7 +825,7 @@ static void coroutine_fn mirror_run(void *opaque) checking for a NULL string */ int ret = 0; - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { goto immediate_exit; } @@ -730,13 +856,14 @@ static void coroutine_fn mirror_run(void *opaque) } if (s->bdev_length == 0) { - /* Report BLOCK_JOB_READY and wait for complete. */ - block_job_event_ready(&s->common); + /* Transition to the READY state and wait for complete. */ + job_transition_to_ready(&s->common.job); s->synced = true; - while (!block_job_is_cancelled(&s->common) && !s->should_complete) { - block_job_yield(&s->common); + s->actively_synced = true; + while (!job_is_cancelled(&s->common.job) && !s->should_complete) { + job_yield(&s->common.job); } - s->common.cancelled = false; + s->common.job.cancelled = false; goto immediate_exit; } @@ -772,7 +899,7 @@ static void coroutine_fn mirror_run(void *opaque) s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); if (!s->is_none_mode) { ret = mirror_dirty_init(s); - if (ret < 0 || block_job_is_cancelled(&s->common)) { + if (ret < 0 || job_is_cancelled(&s->common.job)) { goto immediate_exit; } } @@ -784,32 +911,36 @@ static void coroutine_fn mirror_run(void *opaque) int64_t cnt, delta; bool should_complete; + /* Do not start passive operations while there are active + * writes in progress */ + while (s->in_active_write_counter) { + mirror_wait_for_any_operation(s, true); + } + if (s->ret < 0) { ret = s->ret; goto immediate_exit; } - block_job_pause_point(&s->common); + job_pause_point(&s->common.job); cnt = bdrv_get_dirty_count(s->dirty_bitmap); - /* s->common.offset contains the number of bytes already processed so - * far, cnt is the number of dirty bytes remaining and - * s->bytes_in_flight is the number of bytes currently being - * processed; together those are the current total operation length */ - s->common.len = s->common.offset + s->bytes_in_flight + cnt; + /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is + * the number of bytes currently being processed; together those are + * the current remaining operation length */ + job_progress_set_remaining(&s->common.job, s->bytes_in_flight + cnt); /* Note that even when no rate limit is applied we need to yield * periodically with no pending I/O so that bdrv_drain_all() returns. - * We do so every SLICE_TIME nanoseconds, or when there is an error, - * or when the source is clean, whichever comes first. - */ + * We do so every BLKOCK_JOB_SLICE_TIME nanoseconds, or when there is + * an error, or when the source is clean, whichever comes first. */ delta = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->last_pause_ns; - if (delta < SLICE_TIME && + if (delta < BLOCK_JOB_SLICE_TIME && s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || (cnt == 0 && s->in_flight > 0)) { trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight); - mirror_wait_for_io(s); + mirror_wait_for_free_in_flight_slot(s); continue; } else if (cnt != 0) { delay_ns = mirror_iteration(s); @@ -829,12 +960,15 @@ static void coroutine_fn mirror_run(void *opaque) * report completion. This way, block-job-cancel will leave * the target in a consistent state. */ - block_job_event_ready(&s->common); + job_transition_to_ready(&s->common.job); s->synced = true; + if (s->copy_mode != MIRROR_COPY_MODE_BACKGROUND) { + s->actively_synced = true; + } } should_complete = s->should_complete || - block_job_is_cancelled(&s->common); + job_is_cancelled(&s->common.job); cnt = bdrv_get_dirty_count(s->dirty_bitmap); } @@ -862,18 +996,23 @@ static void coroutine_fn mirror_run(void *opaque) * completion. */ assert(QLIST_EMPTY(&bs->tracked_requests)); - s->common.cancelled = false; + s->common.job.cancelled = false; need_drain = false; break; } ret = 0; + + if (s->synced && !should_complete) { + delay_ns = (s->in_flight == 0 && + cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); + } trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); - if (block_job_is_cancelled(&s->common) && s->common.force) { + job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job) && + (!s->synced || s->common.job.force_cancel)) + { break; - } else if (!should_complete) { - delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0); - block_job_sleep_ns(&s->common, delay_ns); } s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } @@ -884,8 +1023,8 @@ immediate_exit: * or it was cancelled prematurely so that we do not guarantee that * the target is a copy of the source. */ - assert(ret < 0 || ((s->common.force || !s->synced) && - block_job_is_cancelled(&s->common))); + assert(ret < 0 || ((s->common.job.force_cancel || !s->synced) && + job_is_cancelled(&s->common.job))); assert(need_drain); mirror_wait_for_all_io(s); } @@ -902,23 +1041,12 @@ immediate_exit: if (need_drain) { bdrv_drained_begin(bs); } - block_job_defer_to_main_loop(&s->common, mirror_exit, data); + job_defer_to_main_loop(&s->common.job, mirror_exit, data); } -static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp) +static void mirror_complete(Job *job, Error **errp) { - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); - - if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); - return; - } - ratelimit_set_speed(&s->limit, speed, SLICE_TIME); -} - -static void mirror_complete(BlockJob *job, Error **errp) -{ - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockDriverState *target; target = blk_bs(s->target); @@ -965,16 +1093,22 @@ static void mirror_complete(BlockJob *job, Error **errp) } s->should_complete = true; - block_job_enter(&s->common); + job_enter(job); } -static void mirror_pause(BlockJob *job) +static void mirror_pause(Job *job) { - MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); mirror_wait_for_all_io(s); } +static bool mirror_drained_poll(BlockJob *job) +{ + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + return !!s->in_flight; +} + static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); @@ -998,37 +1132,263 @@ static void mirror_drain(BlockJob *job) } static const BlockJobDriver mirror_job_driver = { - .instance_size = sizeof(MirrorBlockJob), - .job_type = BLOCK_JOB_TYPE_MIRROR, - .set_speed = mirror_set_speed, - .start = mirror_run, - .complete = mirror_complete, - .pause = mirror_pause, + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_MIRROR, + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = mirror_run, + .pause = mirror_pause, + .complete = mirror_complete, + }, + .drained_poll = mirror_drained_poll, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; static const BlockJobDriver commit_active_job_driver = { - .instance_size = sizeof(MirrorBlockJob), - .job_type = BLOCK_JOB_TYPE_COMMIT, - .set_speed = mirror_set_speed, - .start = mirror_run, - .complete = mirror_complete, - .pause = mirror_pause, + .job_driver = { + .instance_size = sizeof(MirrorBlockJob), + .job_type = JOB_TYPE_COMMIT, + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = mirror_run, + .pause = mirror_pause, + .complete = mirror_complete, + }, + .drained_poll = mirror_drained_poll, .attached_aio_context = mirror_attached_aio_context, .drain = mirror_drain, }; +static void do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + BdrvDirtyBitmapIter *iter; + QEMUIOVector target_qiov; + uint64_t dirty_offset; + int dirty_bytes; + + if (qiov) { + qemu_iovec_init(&target_qiov, qiov->niov); + } + + iter = bdrv_dirty_iter_new(job->dirty_bitmap); + bdrv_set_dirty_iter(iter, offset); + + while (true) { + bool valid_area; + int ret; + + bdrv_dirty_bitmap_lock(job->dirty_bitmap); + valid_area = bdrv_dirty_iter_next_area(iter, offset + bytes, + &dirty_offset, &dirty_bytes); + if (!valid_area) { + bdrv_dirty_bitmap_unlock(job->dirty_bitmap); + break; + } + + bdrv_reset_dirty_bitmap_locked(job->dirty_bitmap, + dirty_offset, dirty_bytes); + bdrv_dirty_bitmap_unlock(job->dirty_bitmap); + + job_progress_increase_remaining(&job->common.job, dirty_bytes); + + assert(dirty_offset - offset <= SIZE_MAX); + if (qiov) { + qemu_iovec_reset(&target_qiov); + qemu_iovec_concat(&target_qiov, qiov, + dirty_offset - offset, dirty_bytes); + } + + switch (method) { + case MIRROR_METHOD_COPY: + ret = blk_co_pwritev(job->target, dirty_offset, dirty_bytes, + qiov ? &target_qiov : NULL, flags); + break; + + case MIRROR_METHOD_ZERO: + assert(!qiov); + ret = blk_co_pwrite_zeroes(job->target, dirty_offset, dirty_bytes, + flags); + break; + + case MIRROR_METHOD_DISCARD: + assert(!qiov); + ret = blk_co_pdiscard(job->target, dirty_offset, dirty_bytes); + break; + + default: + abort(); + } + + if (ret >= 0) { + job_progress_update(&job->common.job, dirty_bytes); + } else { + BlockErrorAction action; + + bdrv_set_dirty_bitmap(job->dirty_bitmap, dirty_offset, dirty_bytes); + job->actively_synced = false; + + action = mirror_error_action(job, false, -ret); + if (action == BLOCK_ERROR_ACTION_REPORT) { + if (!job->ret) { + job->ret = ret; + } + break; + } + } + } + + bdrv_dirty_iter_free(iter); + if (qiov) { + qemu_iovec_destroy(&target_qiov); + } +} + +static MirrorOp *coroutine_fn active_write_prepare(MirrorBlockJob *s, + uint64_t offset, + uint64_t bytes) +{ + MirrorOp *op; + uint64_t start_chunk = offset / s->granularity; + uint64_t end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity); + + op = g_new(MirrorOp, 1); + *op = (MirrorOp){ + .s = s, + .offset = offset, + .bytes = bytes, + .is_active_write = true, + }; + qemu_co_queue_init(&op->waiting_requests); + QTAILQ_INSERT_TAIL(&s->ops_in_flight, op, next); + + s->in_active_write_counter++; + + mirror_wait_on_conflicts(op, s, offset, bytes); + + bitmap_set(s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); + + return op; +} + +static void coroutine_fn active_write_settle(MirrorOp *op) +{ + uint64_t start_chunk = op->offset / op->s->granularity; + uint64_t end_chunk = DIV_ROUND_UP(op->offset + op->bytes, + op->s->granularity); + + if (!--op->s->in_active_write_counter && op->s->actively_synced) { + BdrvChild *source = op->s->mirror_top_bs->backing; + + if (QLIST_FIRST(&source->bs->parents) == source && + QLIST_NEXT(source, next_parent) == NULL) + { + /* Assert that we are back in sync once all active write + * operations are settled. + * Note that we can only assert this if the mirror node + * is the source node's only parent. */ + assert(!bdrv_get_dirty_count(op->s->dirty_bitmap)); + } + } + bitmap_clear(op->s->in_flight_bitmap, start_chunk, end_chunk - start_chunk); + QTAILQ_REMOVE(&op->s->ops_in_flight, op, next); + qemu_co_queue_restart_all(&op->waiting_requests); + g_free(op); +} + static int coroutine_fn bdrv_mirror_top_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } +static int coroutine_fn bdrv_mirror_top_do_write(BlockDriverState *bs, + MirrorMethod method, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, + int flags) +{ + MirrorOp *op = NULL; + MirrorBDSOpaque *s = bs->opaque; + int ret = 0; + bool copy_to_target; + + copy_to_target = s->job->ret >= 0 && + s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; + + if (copy_to_target) { + op = active_write_prepare(s->job, offset, bytes); + } + + switch (method) { + case MIRROR_METHOD_COPY: + ret = bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); + break; + + case MIRROR_METHOD_ZERO: + ret = bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); + break; + + case MIRROR_METHOD_DISCARD: + ret = bdrv_co_pdiscard(bs->backing, offset, bytes); + break; + + default: + abort(); + } + + if (ret < 0) { + goto out; + } + + if (copy_to_target) { + do_sync_target_write(s->job, method, offset, bytes, qiov, flags); + } + +out: + if (copy_to_target) { + active_write_settle(op); + } + return ret; +} + static int coroutine_fn bdrv_mirror_top_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { - return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags); + MirrorBDSOpaque *s = bs->opaque; + QEMUIOVector bounce_qiov; + void *bounce_buf; + int ret = 0; + bool copy_to_target; + + copy_to_target = s->job->ret >= 0 && + s->job->copy_mode == MIRROR_COPY_MODE_WRITE_BLOCKING; + + if (copy_to_target) { + /* The guest might concurrently modify the data to write; but + * the data on source and destination must match, so we have + * to use a bounce buffer if we are going to write to the + * target now. */ + bounce_buf = qemu_blockalign(bs, bytes); + iov_to_buf_full(qiov->iov, qiov->niov, 0, bounce_buf, bytes); + + qemu_iovec_init(&bounce_qiov, 1); + qemu_iovec_add(&bounce_qiov, bounce_buf, bytes); + qiov = &bounce_qiov; + } + + ret = bdrv_mirror_top_do_write(bs, MIRROR_METHOD_COPY, offset, bytes, qiov, + flags); + + if (copy_to_target) { + qemu_iovec_destroy(&bounce_qiov); + qemu_vfree(bounce_buf); + } + + return ret; } static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) @@ -1043,13 +1403,15 @@ static int coroutine_fn bdrv_mirror_top_flush(BlockDriverState *bs) static int coroutine_fn bdrv_mirror_top_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { - return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_ZERO, offset, bytes, NULL, + flags); } static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { - return bdrv_co_pdiscard(bs->backing->bs, offset, bytes); + return bdrv_mirror_top_do_write(bs, MIRROR_METHOD_DISCARD, offset, bytes, + NULL, 0); } static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs, QDict *opts) @@ -1111,10 +1473,11 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, const BlockJobDriver *driver, bool is_none_mode, BlockDriverState *base, bool auto_complete, const char *filter_node_name, - bool is_mirror, + bool is_mirror, MirrorCopyMode copy_mode, Error **errp) { MirrorBlockJob *s; + MirrorBDSOpaque *bs_opaque; BlockDriverState *mirror_top_bs; bool target_graph_mod; bool target_is_backing; @@ -1148,6 +1511,10 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, mirror_top_bs->implicit = true; } mirror_top_bs->total_sectors = bs->total_sectors; + mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; + mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; + bs_opaque = g_new0(MirrorBDSOpaque, 1); + mirror_top_bs->opaque = bs_opaque; bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs)); /* bdrv_append takes ownership of the mirror_top_bs reference, need to keep @@ -1172,10 +1539,11 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, if (!s) { goto fail; } + bs_opaque->job = s; + /* The block job now has a reference to this node */ bdrv_unref(mirror_top_bs); - s->source = bs; s->mirror_top_bs = mirror_top_bs; /* No resize for the target either; while the mirror is still running, a @@ -1213,6 +1581,7 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, s->on_target_error = on_target_error; s->is_none_mode = is_none_mode; s->backing_mode = backing_mode; + s->copy_mode = copy_mode; s->base = base; s->granularity = granularity; s->buf_size = ROUND_UP(buf_size, granularity); @@ -1248,8 +1617,10 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, } } + QTAILQ_INIT(&s->ops_in_flight); + trace_mirror_start(bs, s, opaque); - block_job_start(&s->common); + job_start(&s->common.job); return; fail: @@ -1260,7 +1631,8 @@ fail: g_free(s->replaces); blk_unref(s->target); - block_job_early_fail(&s->common); + bs_opaque->job = NULL; + job_early_fail(&s->common.job); } bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, @@ -1276,7 +1648,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, - bool unmap, const char *filter_node_name, Error **errp) + bool unmap, const char *filter_node_name, + MirrorCopyMode copy_mode, Error **errp) { bool is_none_mode; BlockDriverState *base; @@ -1287,11 +1660,11 @@ void mirror_start(const char *job_id, BlockDriverState *bs, } is_none_mode = mode == MIRROR_SYNC_MODE_NONE; base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; - mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces, + mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces, speed, granularity, buf_size, backing_mode, on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, is_none_mode, base, false, - filter_node_name, true, errp); + filter_node_name, true, copy_mode, errp); } void commit_active_start(const char *job_id, BlockDriverState *bs, @@ -1314,7 +1687,8 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, MIRROR_LEAVE_BACKING_CHAIN, on_error, on_error, true, cb, opaque, &commit_active_job_driver, false, base, auto_complete, - filter_node_name, false, &local_err); + filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, + &local_err); if (local_err) { error_propagate(errp, local_err); goto error_restore_flags; diff --git a/block/nbd-client.c b/block/nbd-client.c index e7caf49fbb..9686ecbd5e 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -259,14 +259,18 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, if (extent->length == 0 || (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, - client->info.min_block)) || - extent->length > orig_length) - { + client->info.min_block))) { error_setg(errp, "Protocol error: server sent status chunk with " "invalid length"); return -EINVAL; } + /* The server is allowed to send us extra information on the final + * extent; just clamp it to the length we requested. */ + if (extent->length > orig_length) { + extent->length = orig_length; + } + return 0; } @@ -966,6 +970,7 @@ int nbd_client_init(BlockDriverState *bs, const char *export, QCryptoTLSCreds *tlscreds, const char *hostname, + const char *x_dirty_bitmap, Error **errp) { NBDClientSession *client = nbd_get_client_session(bs); @@ -978,9 +983,11 @@ int nbd_client_init(BlockDriverState *bs, client->info.request_sizes = true; client->info.structured_reply = true; client->info.base_allocation = true; + client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap); ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, tlscreds, hostname, &client->ioc, &client->info, errp); + g_free(client->info.x_dirty_bitmap); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); return ret; diff --git a/block/nbd-client.h b/block/nbd-client.h index 0ece76e5af..cfc90550b9 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -45,6 +45,7 @@ int nbd_client_init(BlockDriverState *bs, const char *export_name, QCryptoTLSCreds *tlscreds, const char *hostname, + const char *x_dirty_bitmap, Error **errp); void nbd_client_close(BlockDriverState *bs); diff --git a/block/nbd.c b/block/nbd.c index 1e2b3ba2d3..e87699fb73 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -27,7 +27,8 @@ */ #include "qemu/osdep.h" -#include "block/nbd-client.h" +#include "nbd-client.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qemu/uri.h" #include "block/block_int.h" @@ -108,7 +109,7 @@ static int nbd_parse_uri(const char *filename, QDict *options) /* strip braces from literal IPv6 address */ if (uri->server[0] == '[') { host = qstring_from_substr(uri->server, 1, - strlen(uri->server) - 2); + strlen(uri->server) - 1); } else { host = qstring_from_str(uri->server); } @@ -262,7 +263,6 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, { SocketAddress *saddr = NULL; QDict *addr = NULL; - QObject *crumpled_addr = NULL; Visitor *iv = NULL; Error *local_err = NULL; @@ -272,20 +272,11 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, goto done; } - crumpled_addr = qdict_crumple(addr, errp); - if (!crumpled_addr) { + iv = qobject_input_visitor_new_flat_confused(addr, errp); + if (!iv) { goto done; } - /* - * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive - * server.type=inet. .to doesn't matter, it's ignored anyway. - * That's because when @options come from -blockdev or - * blockdev_add, members are typed according to the QAPI schema, - * but when they come from -drive, they're all QString. The - * visitor expects the former. - */ - iv = qobject_input_visitor_new(crumpled_addr); visit_type_SocketAddress(iv, NULL, &saddr, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -293,8 +284,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, } done: - QDECREF(addr); - qobject_decref(crumpled_addr); + qobject_unref(addr); visit_free(iv); return saddr; } @@ -388,6 +378,12 @@ static QemuOptsList nbd_runtime_opts = { .type = QEMU_OPT_STRING, .help = "ID of the TLS credentials to use", }, + { + .name = "x-dirty-bitmap", + .type = QEMU_OPT_STRING, + .help = "experimental: expose named dirty bitmap in place of " + "block status", + }, { /* end of list */ } }, }; @@ -448,8 +444,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, } /* NBD handshake */ - ret = nbd_client_init(bs, sioc, s->export, - tlscreds, hostname, errp); + ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname, + qemu_opt_get(opts, "x-dirty-bitmap"), errp); error: if (sioc) { object_unref(OBJECT(sioc)); diff --git a/block/nfs.c b/block/nfs.c index 2577df4b26..eab1a2c408 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -29,6 +29,7 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "trace.h" #include "qemu/iov.h" #include "qemu/option.h" @@ -555,24 +556,29 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options, Error **errp) { BlockdevOptionsNfs *opts = NULL; - QObject *crumpled = NULL; Visitor *v; + const QDictEntry *e; Error *local_err = NULL; - crumpled = qdict_crumple(options, errp); - if (crumpled == NULL) { + v = qobject_input_visitor_new_flat_confused(options, errp); + if (!v) { return NULL; } - v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); visit_free(v); - qobject_decref(crumpled); if (local_err) { + error_propagate(errp, local_err); return NULL; } + /* Remove the processed options from the QDict (the visitor processes + * _all_ options in the QDict) */ + while ((e = qdict_first(options))) { + qdict_del(options, e->key); + } + return opts; } @@ -683,7 +689,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts, ret = 0; out: - QDECREF(options); + qobject_unref(options); qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -737,8 +743,9 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) return (task.ret < 0 ? task.ret : st.st_blocks * 512); } -static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn +nfs_file_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { NFSClient *client = bs->opaque; int ret; @@ -867,7 +874,7 @@ static BlockDriver bdrv_nfs = { .bdrv_has_zero_init = nfs_has_zero_init, .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, - .bdrv_truncate = nfs_file_truncate, + .bdrv_co_truncate = nfs_file_co_truncate, .bdrv_file_open = nfs_file_open, .bdrv_close = nfs_file_close, diff --git a/block/null.c b/block/null.c index 806a8631e4..5d610fdfba 100644 --- a/block/null.c +++ b/block/null.c @@ -93,6 +93,7 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags, } s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false); qemu_opts_del(opts); + bs->supported_write_flags = BDRV_REQ_FUA; return ret; } @@ -116,22 +117,22 @@ static coroutine_fn int null_co_common(BlockDriverState *bs) return 0; } -static coroutine_fn int null_co_readv(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov) +static coroutine_fn int null_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { BDRVNullState *s = bs->opaque; if (s->read_zeroes) { - qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); + qemu_iovec_memset(qiov, 0, 0, bytes); } return null_co_common(bs); } -static coroutine_fn int null_co_writev(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov) +static coroutine_fn int null_co_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { return null_co_common(bs); } @@ -186,26 +187,26 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs, return &acb->common; } -static BlockAIOCB *null_aio_readv(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, - int nb_sectors, - BlockCompletionFunc *cb, - void *opaque) +static BlockAIOCB *null_aio_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, + void *opaque) { BDRVNullState *s = bs->opaque; if (s->read_zeroes) { - qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); + qemu_iovec_memset(qiov, 0, 0, bytes); } return null_aio_common(bs, cb, opaque); } -static BlockAIOCB *null_aio_writev(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, - int nb_sectors, - BlockCompletionFunc *cb, - void *opaque) +static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, + void *opaque) { return null_aio_common(bs, cb, opaque); } @@ -244,7 +245,6 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs, static void null_refresh_filename(BlockDriverState *bs, QDict *opts) { - QINCREF(opts); qdict_del(opts, "filename"); if (!qdict_size(opts)) { @@ -253,7 +253,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts) } qdict_put_str(opts, "driver", bs->drv->format_name); - bs->full_open_options = opts; + bs->full_open_options = qobject_ref(opts); } static BlockDriver bdrv_null_co = { @@ -266,8 +266,8 @@ static BlockDriver bdrv_null_co = { .bdrv_close = null_close, .bdrv_getlength = null_getlength, - .bdrv_co_readv = null_co_readv, - .bdrv_co_writev = null_co_writev, + .bdrv_co_preadv = null_co_preadv, + .bdrv_co_pwritev = null_co_pwritev, .bdrv_co_flush_to_disk = null_co_flush, .bdrv_reopen_prepare = null_reopen_prepare, @@ -286,8 +286,8 @@ static BlockDriver bdrv_null_aio = { .bdrv_close = null_close, .bdrv_getlength = null_getlength, - .bdrv_aio_readv = null_aio_readv, - .bdrv_aio_writev = null_aio_writev, + .bdrv_aio_preadv = null_aio_preadv, + .bdrv_aio_pwritev = null_aio_pwritev, .bdrv_aio_flush = null_aio_flush, .bdrv_reopen_prepare = null_reopen_prepare, diff --git a/block/nvme.c b/block/nvme.c index c4f3a7bc94..6f71122bf5 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -1073,7 +1073,6 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state, static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) { - QINCREF(opts); qdict_del(opts, "filename"); if (!qdict_size(opts)) { @@ -1082,7 +1081,7 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) } qdict_put_str(opts, "driver", bs->drv->format_name); - bs->full_open_options = opts; + bs->full_open_options = qobject_ref(opts); } static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) diff --git a/block/parallels.c b/block/parallels.c index 799215e079..cc9445879d 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -31,6 +31,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" @@ -226,14 +227,15 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, }; qemu_iovec_init_external(&qiov, &iov, 1); - ret = bdrv_co_readv(bs->backing, idx * s->tracks, nb_cow_sectors, - &qiov); + ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE, + nb_cow_bytes, &qiov, 0); if (ret < 0) { qemu_vfree(iov.iov_base); return ret; } - ret = bdrv_co_writev(bs->file, s->data_end, nb_cow_sectors, &qiov); + ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE, + nb_cow_bytes, &qiov, 0); qemu_vfree(iov.iov_base); if (ret < 0) { return ret; @@ -311,13 +313,15 @@ static int coroutine_fn parallels_co_block_status(BlockDriverState *bs, } static coroutine_fn int parallels_co_writev(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) + int64_t sector_num, int nb_sectors, + QEMUIOVector *qiov, int flags) { BDRVParallelsState *s = bs->opaque; uint64_t bytes_done = 0; QEMUIOVector hd_qiov; int ret = 0; + assert(!flags); qemu_iovec_init(&hd_qiov, qiov->niov); while (nb_sectors > 0) { @@ -337,7 +341,8 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs, qemu_iovec_reset(&hd_qiov); qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); - ret = bdrv_co_writev(bs->file, position, n, &hd_qiov); + ret = bdrv_co_pwritev(bs->file, position * BDRV_SECTOR_SIZE, nbytes, + &hd_qiov, 0); if (ret < 0) { break; } @@ -376,7 +381,8 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, if (position < 0) { if (bs->backing) { - ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); + ret = bdrv_co_preadv(bs->backing, sector_num * BDRV_SECTOR_SIZE, + nbytes, &hd_qiov, 0); if (ret < 0) { break; } @@ -384,7 +390,8 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, qemu_iovec_memset(&hd_qiov, 0, 0, nbytes); } } else { - ret = bdrv_co_readv(bs->file, position, n, &hd_qiov); + ret = bdrv_co_preadv(bs->file, position * BDRV_SECTOR_SIZE, nbytes, + &hd_qiov, 0); if (ret < 0) { break; } @@ -613,8 +620,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, BlockdevCreateOptions *create_options = NULL; Error *local_err = NULL; BlockDriverState *bs = NULL; - QDict *qdict = NULL; - QObject *qobj; + QDict *qdict; Visitor *v; int ret; @@ -650,15 +656,12 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "parallels"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto done; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); @@ -682,7 +685,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename, ret = 0; done: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; diff --git a/block/qapi.c b/block/qapi.c index 04c6fc69b9..339727f0f4 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -593,15 +593,29 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, p_next = &info->next; } } else { - for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { + for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { BlockStatsList *info = g_malloc0(sizeof(*info)); AioContext *ctx = blk_get_aio_context(blk); BlockStats *s; + char *qdev; + + if (!*blk_name(blk) && !blk_get_attached_dev(blk)) { + continue; + } aio_context_acquire(ctx); s = bdrv_query_bds_stats(blk_bs(blk), true); s->has_device = true; s->device = g_strdup(blk_name(blk)); + + qdev = blk_get_attached_dev_id(blk); + if (qdev && *qdev) { + s->has_qdev = true; + s->qdev = qdev; + } else { + g_free(qdev); + } + bdrv_query_blk_stats(s->stats, blk); aio_context_release(ctx); @@ -773,7 +787,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f, visit_complete(v, &obj); data = qdict_get(qobject_to(QDict, obj), "data"); dump_qobject(func_fprintf, f, 1, data); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); } diff --git a/block/qcow.c b/block/qcow.c index f92891676c..385d935258 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -26,6 +26,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" @@ -37,7 +38,7 @@ #include "qapi/qapi-visit-block-core.h" #include "crypto/block.h" #include "migration/blocker.h" -#include "block/crypto.h" +#include "crypto.h" /**************************************************************/ /* QEMU COW block driver with compression and encryption support */ @@ -69,7 +70,6 @@ typedef struct QCowHeader { typedef struct BDRVQcowState { int cluster_bits; int cluster_size; - int cluster_sectors; int l2_bits; int l2_size; unsigned int l1_size; @@ -202,9 +202,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } - qdict_del(encryptopts, "format"); - crypto_opts = block_crypto_open_opts_init( - Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); + qdict_put_str(encryptopts, "format", "qcow"); + crypto_opts = block_crypto_open_opts_init(encryptopts, errp); if (!crypto_opts) { ret = -EINVAL; goto fail; @@ -235,7 +234,6 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, } s->cluster_bits = header.cluster_bits; s->cluster_size = 1 << s->cluster_bits; - s->cluster_sectors = 1 << (s->cluster_bits - 9); s->l2_bits = header.l2_bits; s->l2_size = 1 << s->l2_bits; bs->total_sectors = header.size / 512; @@ -315,7 +313,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - QDECREF(encryptopts); + qobject_unref(encryptopts); qapi_free_QCryptoBlockOpenOptions(crypto_opts); qemu_co_mutex_init(&s->lock); return 0; @@ -326,7 +324,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, g_free(s->cluster_cache); g_free(s->cluster_data); qcrypto_block_free(s->crypto); - QDECREF(encryptopts); + qobject_unref(encryptopts); qapi_free_QCryptoBlockOpenOptions(crypto_opts); return ret; } @@ -345,8 +343,8 @@ static int qcow_reopen_prepare(BDRVReopenState *state, * * 0 to not allocate. * - * 1 to allocate a normal cluster (for sector indexes 'n_start' to - * 'n_end') + * 1 to allocate a normal cluster (for sector-aligned byte offsets 'n_start' + * to 'n_end' within the cluster) * * 2 to allocate a compressed cluster of size * 'compressed_size'. 'compressed_size' must be > 0 and < @@ -440,9 +438,10 @@ static int get_cluster_offset(BlockDriverState *bs, if (!allocate) return 0; BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); + assert(QEMU_IS_ALIGNED(n_start | n_end, BDRV_SECTOR_SIZE)); /* allocate a new cluster */ if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && - (n_end - n_start) < s->cluster_sectors) { + (n_end - n_start) < s->cluster_size) { /* if the cluster is already compressed, we must decompress it in the case it is not completely overwritten */ @@ -480,16 +479,15 @@ static int get_cluster_offset(BlockDriverState *bs, /* if encrypted, we must initialize the cluster content which won't be written */ if (bs->encrypted && - (n_end - n_start) < s->cluster_sectors) { - uint64_t start_sect; + (n_end - n_start) < s->cluster_size) { + uint64_t start_offset; assert(s->crypto); - start_sect = (offset & ~(s->cluster_size - 1)) >> 9; - for(i = 0; i < s->cluster_sectors; i++) { + start_offset = offset & ~(s->cluster_size - 1); + for (i = 0; i < s->cluster_size; i += BDRV_SECTOR_SIZE) { if (i < n_start || i >= n_end) { - memset(s->cluster_data, 0x00, 512); + memset(s->cluster_data, 0x00, BDRV_SECTOR_SIZE); if (qcrypto_block_encrypt(s->crypto, - (start_sect + i) * - BDRV_SECTOR_SIZE, + start_offset + i, s->cluster_data, BDRV_SECTOR_SIZE, NULL) < 0) { @@ -497,8 +495,9 @@ static int get_cluster_offset(BlockDriverState *bs, } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = bdrv_pwrite(bs->file, - cluster_offset + i * 512, - s->cluster_data, 512); + cluster_offset + i, + s->cluster_data, + BDRV_SECTOR_SIZE); if (ret < 0) { return ret; } @@ -612,11 +611,21 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) return 0; } -static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) +static void qcow_refresh_limits(BlockDriverState *bs, Error **errp) +{ + /* At least encrypted images require 512-byte alignment. Apply the + * limit universally, rather than just on encrypted images, as + * it's easier to let the block layer handle rounding than to + * audit this code further. */ + bs->bl.request_alignment = BDRV_SECTOR_SIZE; +} + +static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, + int flags) { BDRVQcowState *s = bs->opaque; - int index_in_cluster; + int offset_in_cluster; int ret = 0, n; uint64_t cluster_offset; struct iovec hd_iov; @@ -624,6 +633,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, uint8_t *buf; void *orig_buf; + assert(!flags); if (qiov->niov > 1) { buf = orig_buf = qemu_try_blockalign(bs, qiov->size); if (buf == NULL) { @@ -636,36 +646,35 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, qemu_co_mutex_lock(&s->lock); - while (nb_sectors != 0) { + while (bytes != 0) { /* prepare next request */ - ret = get_cluster_offset(bs, sector_num << 9, - 0, 0, 0, 0, &cluster_offset); + ret = get_cluster_offset(bs, offset, 0, 0, 0, 0, &cluster_offset); if (ret < 0) { break; } - index_in_cluster = sector_num & (s->cluster_sectors - 1); - n = s->cluster_sectors - index_in_cluster; - if (n > nb_sectors) { - n = nb_sectors; + offset_in_cluster = offset & (s->cluster_size - 1); + n = s->cluster_size - offset_in_cluster; + if (n > bytes) { + n = bytes; } if (!cluster_offset) { if (bs->backing) { /* read from the base image */ hd_iov.iov_base = (void *)buf; - hd_iov.iov_len = n * 512; + hd_iov.iov_len = n; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); /* qcow2 emits this on bs->file instead of bs->backing */ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); - ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); + ret = bdrv_co_preadv(bs->backing, offset, n, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { break; } } else { /* Note: in this case, no need to wait */ - memset(buf, 0, 512 * n); + memset(buf, 0, n); } } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ @@ -673,21 +682,19 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, ret = -EIO; break; } - memcpy(buf, - s->cluster_cache + index_in_cluster * 512, 512 * n); + memcpy(buf, s->cluster_cache + offset_in_cluster, n); } else { if ((cluster_offset & 511) != 0) { ret = -EIO; break; } hd_iov.iov_base = (void *)buf; - hd_iov.iov_len = n * 512; + hd_iov.iov_len = n; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - ret = bdrv_co_readv(bs->file, - (cluster_offset >> 9) + index_in_cluster, - n, &hd_qiov); + ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster, + n, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { break; @@ -695,8 +702,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, if (bs->encrypted) { assert(s->crypto); if (qcrypto_block_decrypt(s->crypto, - sector_num * BDRV_SECTOR_SIZE, buf, - n * BDRV_SECTOR_SIZE, NULL) < 0) { + offset, buf, n, NULL) < 0) { ret = -EIO; break; } @@ -704,9 +710,9 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, } ret = 0; - nb_sectors -= n; - sector_num += n; - buf += n * 512; + bytes -= n; + offset += n; + buf += n; } qemu_co_mutex_unlock(&s->lock); @@ -719,11 +725,12 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, return ret; } -static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) +static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, + uint64_t bytes, QEMUIOVector *qiov, + int flags) { BDRVQcowState *s = bs->opaque; - int index_in_cluster; + int offset_in_cluster; uint64_t cluster_offset; int ret = 0, n; struct iovec hd_iov; @@ -731,6 +738,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, uint8_t *buf; void *orig_buf; + assert(!flags); s->cluster_cache_offset = -1; /* disable compressed cache */ /* We must always copy the iov when encrypting, so we @@ -748,16 +756,14 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, qemu_co_mutex_lock(&s->lock); - while (nb_sectors != 0) { - - index_in_cluster = sector_num & (s->cluster_sectors - 1); - n = s->cluster_sectors - index_in_cluster; - if (n > nb_sectors) { - n = nb_sectors; + while (bytes != 0) { + offset_in_cluster = offset & (s->cluster_size - 1); + n = s->cluster_size - offset_in_cluster; + if (n > bytes) { + n = bytes; } - ret = get_cluster_offset(bs, sector_num << 9, 1, 0, - index_in_cluster, - index_in_cluster + n, &cluster_offset); + ret = get_cluster_offset(bs, offset, 1, 0, offset_in_cluster, + offset_in_cluster + n, &cluster_offset); if (ret < 0) { break; } @@ -767,30 +773,28 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, } if (bs->encrypted) { assert(s->crypto); - if (qcrypto_block_encrypt(s->crypto, sector_num * BDRV_SECTOR_SIZE, - buf, n * BDRV_SECTOR_SIZE, NULL) < 0) { + if (qcrypto_block_encrypt(s->crypto, offset, buf, n, NULL) < 0) { ret = -EIO; break; } } hd_iov.iov_base = (void *)buf; - hd_iov.iov_len = n * 512; + hd_iov.iov_len = n; qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_co_mutex_unlock(&s->lock); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); - ret = bdrv_co_writev(bs->file, - (cluster_offset >> 9) + index_in_cluster, - n, &hd_qiov); + ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster, + n, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { break; } ret = 0; - nb_sectors -= n; - sector_num += n; - buf += n * 512; + bytes -= n; + offset += n; + buf += n; } qemu_co_mutex_unlock(&s->lock); @@ -934,6 +938,7 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts, ret = 0; exit: blk_unref(qcow_blk); + bdrv_unref(bs); qcrypto_block_free(crypto); return ret; } @@ -943,8 +948,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, { BlockdevCreateOptions *create_options = NULL; BlockDriverState *bs = NULL; - QDict *qdict = NULL; - QObject *qobj; + QDict *qdict; Visitor *v; const char *val; Error *local_err = NULL; @@ -994,15 +998,12 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "qcow"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); @@ -1025,7 +1026,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename, ret = 0; fail: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; @@ -1109,8 +1110,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, if (ret != Z_STREAM_END || out_len >= s->cluster_size) { /* could not compress: write normal cluster */ - ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS, - bytes >> BDRV_SECTOR_BITS, qiov); + ret = qcow_co_pwritev(bs, offset, bytes, qiov, 0); if (ret < 0) { goto fail; } @@ -1195,9 +1195,10 @@ static BlockDriver bdrv_qcow = { .bdrv_co_create_opts = qcow_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, .supports_backing = true, + .bdrv_refresh_limits = qcow_refresh_limits, - .bdrv_co_readv = qcow_co_readv, - .bdrv_co_writev = qcow_co_writev, + .bdrv_co_preadv = qcow_co_preadv, + .bdrv_co_pwritev = qcow_co_pwritev, .bdrv_co_block_status = qcow_co_block_status, .bdrv_make_empty = qcow_make_empty, diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index 6e93ec43e1..ba978ad2aa 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -30,7 +30,7 @@ #include "qemu/cutils.h" #include "block/block_int.h" -#include "block/qcow2.h" +#include "qcow2.h" /* NOTICE: BME here means Bitmaps Extension and used as a namespace for * _internal_ constants. Please do not use this _internal_ abbreviation for @@ -254,7 +254,6 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb) ret = bitmap_table_load(bs, tb, &bitmap_table); if (ret < 0) { - assert(bitmap_table == NULL); return ret; } @@ -776,7 +775,12 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, } } - ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size); + /* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not + * necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap + * directory in-place (actually, turn-off the extension), which is checked + * in qcow2_check_metadata_overlap() */ + ret = qcow2_pre_write_overlap_check( + bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size); if (ret < 0) { goto fail; } diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 1aee726c6a..d37fe08b3d 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -28,7 +28,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" -#include "block/qcow2.h" +#include "qcow2.h" #include "qemu/bswap.h" #include "trace.h" @@ -994,6 +994,17 @@ err: return ret; } +/** + * Frees the allocated clusters because the request failed and they won't + * actually be linked. + */ +void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) +{ + BDRVQcow2State *s = bs->opaque; + qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits, + QCOW2_DISCARD_NEVER); +} + /* * Returns the number of contiguous clusters that can be used for an allocating * write, but require COW to be performed (this includes yet unallocated space, diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 6b8b63514a..3c539f02e5 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "block/block_int.h" -#include "block/qcow2.h" +#include "qcow2.h" #include "qemu/range.h" #include "qemu/bswap.h" #include "qemu/cutils.h" @@ -734,7 +734,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret) /* Discard is optional, ignore the return value */ if (ret >= 0) { - bdrv_pdiscard(bs->file->bs, d->offset, d->bytes); + bdrv_pdiscard(bs->file, d->offset, d->bytes); } g_free(d); @@ -1577,9 +1577,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, case QCOW2_CLUSTER_COMPRESSED: /* Compressed clusters don't have QCOW_OFLAG_COPIED */ if (l2_entry & QCOW_OFLAG_COPIED) { - fprintf(stderr, "ERROR: cluster %" PRId64 ": " + fprintf(stderr, "ERROR: coffset=0x%" PRIx64 ": " "copied flag must never be set for compressed " - "clusters\n", l2_entry >> s->cluster_bits); + "clusters\n", l2_entry & s->cluster_offset_mask); l2_entry &= ~QCOW_OFLAG_COPIED; res->corruptions++; } @@ -1799,6 +1799,19 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, int ret; uint64_t refcount; int i, j; + bool repair; + + if (fix & BDRV_FIX_ERRORS) { + /* Always repair */ + repair = true; + } else if (fix & BDRV_FIX_LEAKS) { + /* Repair only if that seems safe: This function is always + * called after the refcounts have been fixed, so the refcount + * is accurate if that repair was successful */ + repair = !res->check_errors && !res->corruptions && !res->leaks; + } else { + repair = false; + } for (i = 0; i < s->l1_size; i++) { uint64_t l1_entry = s->l1_table[i]; @@ -1818,10 +1831,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : - "ERROR", - i, l1_entry, refcount); - if (fix & BDRV_FIX_ERRORS) { + repair ? "Repairing" : "ERROR", i, l1_entry, refcount); + if (repair) { s->l1_table[i] = refcount == 1 ? l1_entry | QCOW_OFLAG_COPIED : l1_entry & ~QCOW_OFLAG_COPIED; @@ -1862,10 +1873,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED data cluster: " "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", - fix & BDRV_FIX_ERRORS ? "Repairing" : - "ERROR", - l2_entry, refcount); - if (fix & BDRV_FIX_ERRORS) { + repair ? "Repairing" : "ERROR", l2_entry, refcount); + if (repair) { l2_table[j] = cpu_to_be64(refcount == 1 ? l2_entry | QCOW_OFLAG_COPIED : l2_entry & ~QCOW_OFLAG_COPIED); @@ -2696,6 +2705,16 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, } } + if ((chk & QCOW2_OL_BITMAP_DIRECTORY) && + (s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS)) + { + if (overlaps_with(s->bitmap_directory_offset, + s->bitmap_directory_size)) + { + return QCOW2_OL_BITMAP_DIRECTORY; + } + } + return 0; } diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 74293be470..bb6a5b7516 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" -#include "block/qcow2.h" +#include "qcow2.h" #include "qemu/bswap.h" #include "qemu/error-report.h" #include "qemu/cutils.h" diff --git a/block/qcow2.c b/block/qcow2.c index ef68772aca..ec9e6238a0 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -23,11 +23,15 @@ */ #include "qemu/osdep.h" + +#define ZLIB_CONST +#include + #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" -#include -#include "block/qcow2.h" +#include "qcow2.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-events-block-core.h" @@ -39,7 +43,8 @@ #include "qemu/bswap.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" -#include "block/crypto.h" +#include "crypto.h" +#include "block/thread-pool.h" /* Differences with QCOW: @@ -674,6 +679,11 @@ static QemuOptsList qcow2_runtime_opts = { .type = QEMU_OPT_BOOL, .help = "Check for unintended writes into an inactive L2 table", }, + { + .name = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, + .type = QEMU_OPT_BOOL, + .help = "Check for unintended writes into the bitmap directory", + }, { .name = QCOW2_OPT_CACHE_SIZE, .type = QEMU_OPT_SIZE, @@ -707,14 +717,15 @@ static QemuOptsList qcow2_runtime_opts = { }; static const char *overlap_bool_option_names[QCOW2_OL_MAX_BITNR] = { - [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER, - [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1, - [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2, - [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, - [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, - [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, - [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1, - [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2, + [QCOW2_OL_MAIN_HEADER_BITNR] = QCOW2_OPT_OVERLAP_MAIN_HEADER, + [QCOW2_OL_ACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L1, + [QCOW2_OL_ACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_ACTIVE_L2, + [QCOW2_OL_REFCOUNT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, + [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, + [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, + [QCOW2_OL_INACTIVE_L1_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L1, + [QCOW2_OL_INACTIVE_L2_BITNR] = QCOW2_OPT_OVERLAP_INACTIVE_L2, + [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, }; static void cache_clean_timer_cb(void *opaque) @@ -768,6 +779,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, BDRVQcow2State *s = bs->opaque; uint64_t combined_cache_size; bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; + int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE); l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE); @@ -785,7 +797,7 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, if (l2_cache_size_set && refcount_cache_size_set) { error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE " and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set " - "the same time"); + "at the same time"); return; } else if (*l2_cache_size > combined_cache_size) { error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed " @@ -802,23 +814,28 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, } else if (refcount_cache_size_set) { *l2_cache_size = combined_cache_size - *refcount_cache_size; } else { - *refcount_cache_size = combined_cache_size - / (DEFAULT_L2_REFCOUNT_SIZE_RATIO + 1); - *l2_cache_size = combined_cache_size - *refcount_cache_size; + uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; + uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); + + /* Assign as much memory as possible to the L2 cache, and + * use the remainder for the refcount cache */ + if (combined_cache_size >= max_l2_cache + min_refcount_cache) { + *l2_cache_size = max_l2_cache; + *refcount_cache_size = combined_cache_size - *l2_cache_size; + } else { + *refcount_cache_size = + MIN(combined_cache_size, min_refcount_cache); + *l2_cache_size = combined_cache_size - *refcount_cache_size; + } } } else { - if (!l2_cache_size_set && !refcount_cache_size_set) { + if (!l2_cache_size_set) { *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE, (uint64_t)DEFAULT_L2_CACHE_CLUSTERS * s->cluster_size); - *refcount_cache_size = *l2_cache_size - / DEFAULT_L2_REFCOUNT_SIZE_RATIO; - } else if (!l2_cache_size_set) { - *l2_cache_size = *refcount_cache_size - * DEFAULT_L2_REFCOUNT_SIZE_RATIO; - } else if (!refcount_cache_size_set) { - *refcount_cache_size = *l2_cache_size - / DEFAULT_L2_REFCOUNT_SIZE_RATIO; + } + if (!refcount_cache_size_set) { + *refcount_cache_size = min_refcount_cache; } } @@ -1033,9 +1050,8 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, ret = -EINVAL; goto fail; } - qdict_del(encryptopts, "format"); - r->crypto_opts = block_crypto_open_opts_init( - Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp); + qdict_put_str(encryptopts, "format", "qcow"); + r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); break; case QCOW_CRYPT_LUKS: @@ -1046,9 +1062,8 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, ret = -EINVAL; goto fail; } - qdict_del(encryptopts, "format"); - r->crypto_opts = block_crypto_open_opts_init( - Q_CRYPTO_BLOCK_FORMAT_LUKS, encryptopts, errp); + qdict_put_str(encryptopts, "format", "luks"); + r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp); break; default: @@ -1063,7 +1078,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, ret = 0; fail: - QDECREF(encryptopts); + qobject_unref(encryptopts); qemu_opts_del(opts); opts = NULL; return ret; @@ -1536,6 +1551,9 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, qcow2_check_refcounts(bs, &result, 0); } #endif + + qemu_co_queue_init(&s->compress_wait_queue); + return ret; fail: @@ -1755,6 +1773,41 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, return status; } +static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs, + QCowL2Meta **pl2meta, + bool link_l2) +{ + int ret = 0; + QCowL2Meta *l2meta = *pl2meta; + + while (l2meta != NULL) { + QCowL2Meta *next; + + if (link_l2) { + ret = qcow2_alloc_cluster_link_l2(bs, l2meta); + if (ret) { + goto out; + } + } else { + qcow2_alloc_cluster_abort(bs, l2meta); + } + + /* Take the request off the list of running requests */ + if (l2meta->nb_clusters != 0) { + QLIST_REMOVE(l2meta, next_in_flight); + } + + qemu_co_queue_restart_all(&l2meta->dependent_requests); + + next = l2meta->next; + g_free(l2meta); + l2meta = next; + } +out: + *pl2meta = l2meta; + return ret; +} + static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) @@ -2041,24 +2094,9 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, } } - while (l2meta != NULL) { - QCowL2Meta *next; - - ret = qcow2_alloc_cluster_link_l2(bs, l2meta); - if (ret < 0) { - goto fail; - } - - /* Take the request off the list of running requests */ - if (l2meta->nb_clusters != 0) { - QLIST_REMOVE(l2meta, next_in_flight); - } - - qemu_co_queue_restart_all(&l2meta->dependent_requests); - - next = l2meta->next; - g_free(l2meta); - l2meta = next; + ret = qcow2_handle_l2meta(bs, &l2meta, true); + if (ret) { + goto fail; } bytes -= cur_bytes; @@ -2069,18 +2107,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, ret = 0; fail: - while (l2meta != NULL) { - QCowL2Meta *next; - - if (l2meta->nb_clusters != 0) { - QLIST_REMOVE(l2meta, next_in_flight); - } - qemu_co_queue_restart_all(&l2meta->dependent_requests); - - next = l2meta->next; - g_free(l2meta); - l2meta = next; - } + qcow2_handle_l2meta(bs, &l2meta, false); qemu_co_mutex_unlock(&s->lock); @@ -2183,7 +2210,7 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, qemu_co_mutex_lock(&s->lock); ret = qcow2_do_open(bs, options, flags, &local_err); qemu_co_mutex_unlock(&s->lock); - QDECREF(options); + qobject_unref(options); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "Could not reopen qcow2 layer: "); @@ -2507,15 +2534,6 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, return ret; } - -typedef struct PreallocCo { - BlockDriverState *bs; - uint64_t offset; - uint64_t new_length; - - int ret; -} PreallocCo; - /** * Preallocates metadata structures for data clusters between @offset (in the * guest disk) and @new_length (which is thus generally the new guest disk @@ -2523,21 +2541,15 @@ typedef struct PreallocCo { * * Returns: 0 on success, -errno on failure. */ -static void coroutine_fn preallocate_co(void *opaque) +static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, + uint64_t new_length) { - PreallocCo *params = opaque; - BlockDriverState *bs = params->bs; - uint64_t offset = params->offset; - uint64_t new_length = params->new_length; - BDRVQcow2State *s = bs->opaque; uint64_t bytes; uint64_t host_offset = 0; unsigned int cur_bytes; int ret; QCowL2Meta *meta; - qemu_co_mutex_lock(&s->lock); - assert(offset <= new_length); bytes = new_length - offset; @@ -2546,7 +2558,7 @@ static void coroutine_fn preallocate_co(void *opaque) ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes, &host_offset, &meta); if (ret < 0) { - goto done; + return ret; } while (meta) { @@ -2556,7 +2568,7 @@ static void coroutine_fn preallocate_co(void *opaque) if (ret < 0) { qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters, QCOW2_DISCARD_NEVER); - goto done; + return ret; } /* There are no dependent requests, but we need to remove our @@ -2583,35 +2595,11 @@ static void coroutine_fn preallocate_co(void *opaque) ret = bdrv_pwrite(bs->file, (host_offset + cur_bytes) - 1, &data, 1); if (ret < 0) { - goto done; + return ret; } } - ret = 0; - -done: - qemu_co_mutex_unlock(&s->lock); - params->ret = ret; -} - -static int preallocate(BlockDriverState *bs, - uint64_t offset, uint64_t new_length) -{ - PreallocCo params = { - .bs = bs, - .offset = offset, - .new_length = new_length, - .ret = -EINPROGRESS, - }; - - if (qemu_in_coroutine()) { - preallocate_co(¶ms); - } else { - Coroutine *co = qemu_coroutine_create(preallocate_co, ¶ms); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, params.ret == -EINPROGRESS); - } - return params.ret; + return 0; } /* qcow2_refcount_metadata_size: @@ -3027,7 +3015,11 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) /* And if we're supposed to preallocate metadata, do that now */ if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) { - ret = preallocate(blk_bs(blk), 0, qcow2_opts->size); + BDRVQcow2State *s = blk_bs(blk)->opaque; + qemu_co_mutex_lock(&s->lock); + ret = preallocate_co(blk_bs(blk), 0, qcow2_opts->size); + qemu_co_mutex_unlock(&s->lock); + if (ret < 0) { error_setg_errno(errp, -ret, "Could not preallocate metadata"); goto out; @@ -3066,8 +3058,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt Error **errp) { BlockdevCreateOptions *create_options = NULL; - QDict *qdict = NULL; - QObject *qobj; + QDict *qdict; Visitor *v; BlockDriverState *bs = NULL; Error *local_err = NULL; @@ -3138,15 +3129,12 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt qdict_put_str(qdict, "file", bs->node_name); /* Now get the QAPI type BlockdevCreateOptions */ - qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto finish; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); @@ -3168,7 +3156,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt ret = 0; finish: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; @@ -3267,8 +3255,168 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs, return ret; } -static int qcow2_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn +qcow2_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + BDRVQcow2State *s = bs->opaque; + int ret; + unsigned int cur_bytes; /* number of bytes in current iteration */ + BdrvChild *child = NULL; + BdrvRequestFlags cur_write_flags; + + assert(!bs->encrypted); + qemu_co_mutex_lock(&s->lock); + + while (bytes != 0) { + uint64_t copy_offset = 0; + /* prepare next request */ + cur_bytes = MIN(bytes, INT_MAX); + cur_write_flags = write_flags; + + ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset); + if (ret < 0) { + goto out; + } + + switch (ret) { + case QCOW2_CLUSTER_UNALLOCATED: + if (bs->backing && bs->backing->bs) { + int64_t backing_length = bdrv_getlength(bs->backing->bs); + if (src_offset >= backing_length) { + cur_write_flags |= BDRV_REQ_ZERO_WRITE; + } else { + child = bs->backing; + cur_bytes = MIN(cur_bytes, backing_length - src_offset); + copy_offset = src_offset; + } + } else { + cur_write_flags |= BDRV_REQ_ZERO_WRITE; + } + break; + + case QCOW2_CLUSTER_ZERO_PLAIN: + case QCOW2_CLUSTER_ZERO_ALLOC: + cur_write_flags |= BDRV_REQ_ZERO_WRITE; + break; + + case QCOW2_CLUSTER_COMPRESSED: + ret = -ENOTSUP; + goto out; + + case QCOW2_CLUSTER_NORMAL: + child = bs->file; + copy_offset += offset_into_cluster(s, src_offset); + if ((copy_offset & 511) != 0) { + ret = -EIO; + goto out; + } + break; + + default: + abort(); + } + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_copy_range_from(child, + copy_offset, + dst, dst_offset, + cur_bytes, read_flags, cur_write_flags); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto out; + } + + bytes -= cur_bytes; + src_offset += cur_bytes; + dst_offset += cur_bytes; + } + ret = 0; + +out: + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +static int coroutine_fn +qcow2_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + BDRVQcow2State *s = bs->opaque; + int offset_in_cluster; + int ret; + unsigned int cur_bytes; /* number of sectors in current iteration */ + uint64_t cluster_offset; + QCowL2Meta *l2meta = NULL; + + assert(!bs->encrypted); + s->cluster_cache_offset = -1; /* disable compressed cache */ + + qemu_co_mutex_lock(&s->lock); + + while (bytes != 0) { + + l2meta = NULL; + + offset_in_cluster = offset_into_cluster(s, dst_offset); + cur_bytes = MIN(bytes, INT_MAX); + + /* TODO: + * If src->bs == dst->bs, we could simply copy by incrementing + * the refcnt, without copying user data. + * Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */ + ret = qcow2_alloc_cluster_offset(bs, dst_offset, &cur_bytes, + &cluster_offset, &l2meta); + if (ret < 0) { + goto fail; + } + + assert((cluster_offset & 511) == 0); + + ret = qcow2_pre_write_overlap_check(bs, 0, + cluster_offset + offset_in_cluster, cur_bytes); + if (ret < 0) { + goto fail; + } + + qemu_co_mutex_unlock(&s->lock); + ret = bdrv_co_copy_range_to(src, src_offset, + bs->file, + cluster_offset + offset_in_cluster, + cur_bytes, read_flags, write_flags); + qemu_co_mutex_lock(&s->lock); + if (ret < 0) { + goto fail; + } + + ret = qcow2_handle_l2meta(bs, &l2meta, true); + if (ret) { + goto fail; + } + + bytes -= cur_bytes; + src_offset += cur_bytes; + dst_offset += cur_bytes; + } + ret = 0; + +fail: + qcow2_handle_l2meta(bs, &l2meta, false); + + qemu_co_mutex_unlock(&s->lock); + + trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); + + return ret; +} + +static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVQcow2State *s = bs->opaque; uint64_t old_length; @@ -3288,17 +3436,21 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, return -EINVAL; } + qemu_co_mutex_lock(&s->lock); + /* cannot proceed if image has snapshots */ if (s->nb_snapshots) { error_setg(errp, "Can't resize an image which has snapshots"); - return -ENOTSUP; + ret = -ENOTSUP; + goto fail; } /* cannot proceed if image has bitmaps */ if (s->nb_bitmaps) { /* TODO: resize bitmaps in the image */ error_setg(errp, "Can't resize an image which has bitmaps"); - return -ENOTSUP; + ret = -ENOTSUP; + goto fail; } old_length = bs->total_sectors * 512; @@ -3309,7 +3461,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (prealloc != PREALLOC_MODE_OFF) { error_setg(errp, "Preallocation can't be used for shrinking an image"); - return -EINVAL; + ret = -EINVAL; + goto fail; } ret = qcow2_cluster_discard(bs, ROUND_UP(offset, s->cluster_size), @@ -3318,40 +3471,42 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, QCOW2_DISCARD_ALWAYS, true); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to discard cropped clusters"); - return ret; + goto fail; } ret = qcow2_shrink_l1_table(bs, new_l1_size); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to reduce the number of L2 tables"); - return ret; + goto fail; } ret = qcow2_shrink_reftable(bs); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to discard unused refblocks"); - return ret; + goto fail; } old_file_size = bdrv_getlength(bs->file->bs); if (old_file_size < 0) { error_setg_errno(errp, -old_file_size, "Failed to inquire current file length"); - return old_file_size; + ret = old_file_size; + goto fail; } last_cluster = qcow2_get_last_cluster(bs, old_file_size); if (last_cluster < 0) { error_setg_errno(errp, -last_cluster, "Failed to find the last cluster"); - return last_cluster; + ret = last_cluster; + goto fail; } if ((last_cluster + 1) * s->cluster_size < old_file_size) { Error *local_err = NULL; - bdrv_truncate(bs->file, (last_cluster + 1) * s->cluster_size, - PREALLOC_MODE_OFF, &local_err); + bdrv_co_truncate(bs->file, (last_cluster + 1) * s->cluster_size, + PREALLOC_MODE_OFF, &local_err); if (local_err) { warn_reportf_err(local_err, "Failed to truncate the tail of the image: "); @@ -3361,7 +3516,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, ret = qcow2_grow_l1_table(bs, new_l1_size, true); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to grow the L1 table"); - return ret; + goto fail; } } @@ -3370,10 +3525,10 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, break; case PREALLOC_MODE_METADATA: - ret = preallocate(bs, old_length, offset); + ret = preallocate_co(bs, old_length, offset); if (ret < 0) { error_setg_errno(errp, -ret, "Preallocation failed"); - return ret; + goto fail; } break; @@ -3389,7 +3544,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (old_file_size < 0) { error_setg_errno(errp, -old_file_size, "Failed to inquire current file length"); - return old_file_size; + ret = old_file_size; + goto fail; } old_file_size = ROUND_UP(old_file_size, s->cluster_size); @@ -3419,7 +3575,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (allocation_start < 0) { error_setg_errno(errp, -allocation_start, "Failed to resize refcount structures"); - return allocation_start; + ret = allocation_start; + goto fail; } clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start, @@ -3427,7 +3584,8 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (clusters_allocated < 0) { error_setg_errno(errp, -clusters_allocated, "Failed to allocate data clusters"); - return -clusters_allocated; + ret = clusters_allocated; + goto fail; } assert(clusters_allocated == nb_new_data_clusters); @@ -3435,13 +3593,13 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, /* Allocate the data area */ new_file_size = allocation_start + nb_new_data_clusters * s->cluster_size; - ret = bdrv_truncate(bs->file, new_file_size, prealloc, errp); + ret = bdrv_co_truncate(bs->file, new_file_size, prealloc, errp); if (ret < 0) { error_prepend(errp, "Failed to resize underlying file: "); qcow2_free_clusters(bs, allocation_start, nb_new_data_clusters * s->cluster_size, QCOW2_DISCARD_OTHER); - return ret; + goto fail; } /* Create the necessary L2 entries */ @@ -3464,7 +3622,7 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, qcow2_free_clusters(bs, host_offset, nb_new_data_clusters * s->cluster_size, QCOW2_DISCARD_OTHER); - return ret; + goto fail; } guest_offset += nb_clusters * s->cluster_size; @@ -3480,11 +3638,11 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, if (prealloc != PREALLOC_MODE_OFF) { /* Flush metadata before actually changing the image size */ - ret = bdrv_flush(bs); + ret = qcow2_write_caches(bs); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to flush the preallocated area to disk"); - return ret; + goto fail; } } @@ -3494,13 +3652,114 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset, &offset, sizeof(uint64_t)); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to update the image size"); - return ret; + goto fail; } s->l1_vm_state_index = new_l1_size; + ret = 0; +fail: + qemu_co_mutex_unlock(&s->lock); + return ret; +} + +/* + * qcow2_compress() + * + * @dest - destination buffer, at least of @size-1 bytes + * @src - source buffer, @size bytes + * + * Returns: compressed size on success + * -1 if compression is inefficient + * -2 on any other error + */ +static ssize_t qcow2_compress(void *dest, const void *src, size_t size) +{ + ssize_t ret; + z_stream strm; + + /* best compression, small window, no zlib header */ + memset(&strm, 0, sizeof(strm)); + ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, + -12, 9, Z_DEFAULT_STRATEGY); + if (ret != 0) { + return -2; + } + + /* strm.next_in is not const in old zlib versions, such as those used on + * OpenBSD/NetBSD, so cast the const away */ + strm.avail_in = size; + strm.next_in = (void *) src; + strm.avail_out = size - 1; + strm.next_out = dest; + + ret = deflate(&strm, Z_FINISH); + if (ret == Z_STREAM_END) { + ret = size - 1 - strm.avail_out; + } else { + ret = (ret == Z_OK ? -1 : -2); + } + + deflateEnd(&strm); + + return ret; +} + +#define MAX_COMPRESS_THREADS 4 + +typedef struct Qcow2CompressData { + void *dest; + const void *src; + size_t size; + ssize_t ret; +} Qcow2CompressData; + +static int qcow2_compress_pool_func(void *opaque) +{ + Qcow2CompressData *data = opaque; + + data->ret = qcow2_compress(data->dest, data->src, data->size); + return 0; } +static void qcow2_compress_complete(void *opaque, int ret) +{ + qemu_coroutine_enter(opaque); +} + +/* See qcow2_compress definition for parameters description */ +static ssize_t qcow2_co_compress(BlockDriverState *bs, + void *dest, const void *src, size_t size) +{ + BDRVQcow2State *s = bs->opaque; + BlockAIOCB *acb; + ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + Qcow2CompressData arg = { + .dest = dest, + .src = src, + .size = size, + }; + + while (s->nb_compress_threads >= MAX_COMPRESS_THREADS) { + qemu_co_queue_wait(&s->compress_wait_queue, NULL); + } + + s->nb_compress_threads++; + acb = thread_pool_submit_aio(pool, qcow2_compress_pool_func, &arg, + qcow2_compress_complete, + qemu_coroutine_self()); + + if (!acb) { + s->nb_compress_threads--; + return -EINVAL; + } + qemu_coroutine_yield(); + s->nb_compress_threads--; + qemu_co_queue_next(&s->compress_wait_queue); + + return arg.ret; +} + /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ static coroutine_fn int @@ -3510,8 +3769,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, BDRVQcow2State *s = bs->opaque; QEMUIOVector hd_qiov; struct iovec iov; - z_stream strm; - int ret, out_len; + int ret; + size_t out_len; uint8_t *buf, *out_buf; int64_t cluster_offset; @@ -3522,7 +3781,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, if (cluster_offset < 0) { return cluster_offset; } - return bdrv_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, NULL); + return bdrv_co_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, + NULL); } if (offset_into_cluster(s, offset)) { @@ -3544,32 +3804,11 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, out_buf = g_malloc(s->cluster_size); - /* best compression, small window, no zlib header */ - memset(&strm, 0, sizeof(strm)); - ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, - Z_DEFLATED, -12, - 9, Z_DEFAULT_STRATEGY); - if (ret != 0) { + out_len = qcow2_co_compress(bs, out_buf, buf, s->cluster_size); + if (out_len == -2) { ret = -EINVAL; goto fail; - } - - strm.avail_in = s->cluster_size; - strm.next_in = (uint8_t *)buf; - strm.avail_out = s->cluster_size; - strm.next_out = out_buf; - - ret = deflate(&strm, Z_FINISH); - if (ret != Z_STREAM_END && ret != Z_OK) { - deflateEnd(&strm); - ret = -EINVAL; - goto fail; - } - out_len = strm.next_out - out_buf; - - deflateEnd(&strm); - - if (ret != Z_STREAM_END || out_len >= s->cluster_size) { + } else if (out_len == -1) { /* could not compress: write normal cluster */ ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0); if (ret < 0) { @@ -3996,7 +4235,6 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) switch (encrypt_info->format) { case Q_CRYPTO_BLOCK_FORMAT_QCOW: qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_AES; - qencrypt->u.aes = encrypt_info->u.qcow; break; case Q_CRYPTO_BLOCK_FORMAT_LUKS: qencrypt->format = BLOCKDEV_QCOW2_ENCRYPTION_FORMAT_LUKS; @@ -4042,22 +4280,21 @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, * have to be removed. */ static int qcow2_downgrade(BlockDriverState *bs, int target_version, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque) + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + Error **errp) { BDRVQcow2State *s = bs->opaque; int current_version = s->qcow_version; int ret; - if (target_version == current_version) { - return 0; - } else if (target_version > current_version) { - return -EINVAL; - } else if (target_version != 2) { - return -EINVAL; - } + /* This is qcow2_downgrade(), not qcow2_upgrade() */ + assert(target_version < current_version); + + /* There are no other versions (now) that you can downgrade to */ + assert(target_version == 2); if (s->refcount_order != 4) { - error_report("compat=0.10 requires refcount_bits=16"); + error_setg(errp, "compat=0.10 requires refcount_bits=16"); return -ENOTSUP; } @@ -4065,6 +4302,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { ret = qcow2_mark_clean(bs); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to make the image clean"); return ret; } } @@ -4074,6 +4312,8 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, * best thing to do anyway */ if (s->incompatible_features) { + error_setg(errp, "Cannot downgrade an image with incompatible features " + "%#" PRIx64 " set", s->incompatible_features); return -ENOTSUP; } @@ -4087,6 +4327,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, ret = qcow2_expand_zero_clusters(bs, status_cb, cb_opaque); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to turn zero into data clusters"); return ret; } @@ -4094,6 +4335,7 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, ret = qcow2_update_header(bs); if (ret < 0) { s->qcow_version = current_version; + error_setg_errno(errp, -ret, "Failed to update the image header"); return ret; } return 0; @@ -4171,7 +4413,8 @@ static void qcow2_amend_helper_cb(BlockDriverState *bs, static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, - void *cb_opaque) + void *cb_opaque, + Error **errp) { BDRVQcow2State *s = bs->opaque; int old_version = s->qcow_version, new_version = old_version; @@ -4183,7 +4426,6 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, bool encrypt; int encformat; int refcount_bits = s->refcount_bits; - Error *local_err = NULL; int ret; QemuOptDesc *desc = opts->list->desc; Qcow2AmendHelperCBInfo helper_cb_info; @@ -4204,11 +4446,11 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, } else if (!strcmp(compat, "1.1")) { new_version = 3; } else { - error_report("Unknown compatibility level %s", compat); + error_setg(errp, "Unknown compatibility level %s", compat); return -EINVAL; } } else if (!strcmp(desc->name, BLOCK_OPT_PREALLOC)) { - error_report("Cannot change preallocation mode"); + error_setg(errp, "Cannot change preallocation mode"); return -ENOTSUP; } else if (!strcmp(desc->name, BLOCK_OPT_SIZE)) { new_size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, 0); @@ -4221,7 +4463,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, !!s->crypto); if (encrypt != !!s->crypto) { - error_report("Changing the encryption flag is not supported"); + error_setg(errp, + "Changing the encryption flag is not supported"); return -ENOTSUP; } } else if (!strcmp(desc->name, BLOCK_OPT_ENCRYPT_FORMAT)) { @@ -4229,17 +4472,19 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, qemu_opt_get(opts, BLOCK_OPT_ENCRYPT_FORMAT)); if (encformat != s->crypt_method_header) { - error_report("Changing the encryption format is not supported"); + error_setg(errp, + "Changing the encryption format is not supported"); return -ENOTSUP; } } else if (g_str_has_prefix(desc->name, "encrypt.")) { - error_report("Changing the encryption parameters is not supported"); + error_setg(errp, + "Changing the encryption parameters is not supported"); return -ENOTSUP; } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, cluster_size); if (cluster_size != s->cluster_size) { - error_report("Changing the cluster size is not supported"); + error_setg(errp, "Changing the cluster size is not supported"); return -ENOTSUP; } } else if (!strcmp(desc->name, BLOCK_OPT_LAZY_REFCOUNTS)) { @@ -4252,8 +4497,8 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (refcount_bits <= 0 || refcount_bits > 64 || !is_power_of_2(refcount_bits)) { - error_report("Refcount width must be a power of two and may " - "not exceed 64 bits"); + error_setg(errp, "Refcount width must be a power of two and " + "may not exceed 64 bits"); return -EINVAL; } } else { @@ -4278,6 +4523,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, ret = qcow2_update_header(bs); if (ret < 0) { s->qcow_version = old_version; + error_setg_errno(errp, -ret, "Failed to update the image header"); return ret; } } @@ -4286,18 +4532,17 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, int refcount_order = ctz32(refcount_bits); if (new_version < 3 && refcount_bits != 16) { - error_report("Different refcount widths than 16 bits require " - "compatibility level 1.1 or above (use compat=1.1 or " - "greater)"); + error_setg(errp, "Refcount widths other than 16 bits require " + "compatibility level 1.1 or above (use compat=1.1 or " + "greater)"); return -EINVAL; } helper_cb_info.current_operation = QCOW2_CHANGING_REFCOUNT_ORDER; ret = qcow2_change_refcount_order(bs, refcount_order, &qcow2_amend_helper_cb, - &helper_cb_info, &local_err); + &helper_cb_info, errp); if (ret < 0) { - error_report_err(local_err); return ret; } } @@ -4307,6 +4552,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, backing_file ?: s->image_backing_file, backing_format ?: s->image_backing_format); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to change the backing file"); return ret; } } @@ -4314,14 +4560,16 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (s->use_lazy_refcounts != lazy_refcounts) { if (lazy_refcounts) { if (new_version < 3) { - error_report("Lazy refcounts only supported with compatibility " - "level 1.1 and above (use compat=1.1 or greater)"); + error_setg(errp, "Lazy refcounts only supported with " + "compatibility level 1.1 and above (use compat=1.1 " + "or greater)"); return -EINVAL; } s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; ret = qcow2_update_header(bs); if (ret < 0) { s->compatible_features &= ~QCOW2_COMPAT_LAZY_REFCOUNTS; + error_setg_errno(errp, -ret, "Failed to update the image header"); return ret; } s->use_lazy_refcounts = true; @@ -4329,6 +4577,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, /* make image clean first */ ret = qcow2_mark_clean(bs); if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to make the image clean"); return ret; } /* now disallow lazy refcounts */ @@ -4336,6 +4585,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, ret = qcow2_update_header(bs); if (ret < 0) { s->compatible_features |= QCOW2_COMPAT_LAZY_REFCOUNTS; + error_setg_errno(errp, -ret, "Failed to update the image header"); return ret; } s->use_lazy_refcounts = false; @@ -4344,17 +4594,15 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (new_size) { BlockBackend *blk = blk_new(BLK_PERM_RESIZE, BLK_PERM_ALL); - ret = blk_insert_bs(blk, bs, &local_err); + ret = blk_insert_bs(blk, bs, errp); if (ret < 0) { - error_report_err(local_err); blk_unref(blk); return ret; } - ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, &local_err); + ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, errp); blk_unref(blk); if (ret < 0) { - error_report_err(local_err); return ret; } } @@ -4363,7 +4611,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, if (new_version < old_version) { helper_cb_info.current_operation = QCOW2_DOWNGRADING; ret = qcow2_downgrade(bs, new_version, &qcow2_amend_helper_cb, - &helper_cb_info); + &helper_cb_info, errp); if (ret < 0) { return ret; } @@ -4386,7 +4634,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, char *message; va_list ap; - fatal = fatal && !bs->read_only; + fatal = fatal && bdrv_is_writable(bs); if (s->signaled_corruption && (!fatal || (s->incompatible_features & QCOW2_INCOMPAT_CORRUPT))) @@ -4515,7 +4763,9 @@ BlockDriver bdrv_qcow2 = { .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, .bdrv_co_pdiscard = qcow2_co_pdiscard, - .bdrv_truncate = qcow2_truncate, + .bdrv_co_copy_range_from = qcow2_co_copy_range_from, + .bdrv_co_copy_range_to = qcow2_co_copy_range_to, + .bdrv_co_truncate = qcow2_co_truncate, .bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed, .bdrv_make_empty = qcow2_make_empty, diff --git a/block/qcow2.h b/block/qcow2.h index adf5c3950f..81b844e936 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -77,10 +77,6 @@ #define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ #define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ -/* The refblock cache needs only a fourth of the L2 cache size to cover as many - * clusters */ -#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4 - #define DEFAULT_CLUSTER_SIZE 65536 @@ -98,6 +94,7 @@ #define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table" #define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1" #define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" +#define QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY "overlap-check.bitmap-directory" #define QCOW2_OPT_CACHE_SIZE "cache-size" #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" #define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" @@ -330,6 +327,9 @@ typedef struct BDRVQcow2State { * override) */ char *image_backing_file; char *image_backing_format; + + CoQueue compress_wait_queue; + int nb_compress_threads; } BDRVQcow2State; typedef struct Qcow2COWRegion { @@ -401,34 +401,36 @@ typedef enum QCow2ClusterType { } QCow2ClusterType; typedef enum QCow2MetadataOverlap { - QCOW2_OL_MAIN_HEADER_BITNR = 0, - QCOW2_OL_ACTIVE_L1_BITNR = 1, - QCOW2_OL_ACTIVE_L2_BITNR = 2, - QCOW2_OL_REFCOUNT_TABLE_BITNR = 3, - QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4, - QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, - QCOW2_OL_INACTIVE_L1_BITNR = 6, - QCOW2_OL_INACTIVE_L2_BITNR = 7, + QCOW2_OL_MAIN_HEADER_BITNR = 0, + QCOW2_OL_ACTIVE_L1_BITNR = 1, + QCOW2_OL_ACTIVE_L2_BITNR = 2, + QCOW2_OL_REFCOUNT_TABLE_BITNR = 3, + QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4, + QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, + QCOW2_OL_INACTIVE_L1_BITNR = 6, + QCOW2_OL_INACTIVE_L2_BITNR = 7, + QCOW2_OL_BITMAP_DIRECTORY_BITNR = 8, - QCOW2_OL_MAX_BITNR = 8, + QCOW2_OL_MAX_BITNR = 9, - QCOW2_OL_NONE = 0, - QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR), - QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR), - QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR), - QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), - QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), - QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), - QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR), + QCOW2_OL_NONE = 0, + QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR), + QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR), + QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR), + QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), + QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), + QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), + QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR), /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv * reads. */ - QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR), + QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR), + QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR), } QCow2MetadataOverlap; /* Perform all overlap checks which can be done in constant time */ #define QCOW2_OL_CONSTANT \ (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \ - QCOW2_OL_SNAPSHOT_TABLE) + QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY) /* Perform all overlap checks which don't require disk access */ #define QCOW2_OL_CACHED \ @@ -618,6 +620,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int compressed_size); int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); +void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, uint64_t bytes, enum qcow2_discard_type type, bool full_discard); diff --git a/block/qed.c b/block/qed.c index 35ff505066..689ea9d4d5 100644 --- a/block/qed.c +++ b/block/qed.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/bswap.h" @@ -721,8 +722,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, Error **errp) { BlockdevCreateOptions *create_options = NULL; - QDict *qdict = NULL; - QObject *qobj; + QDict *qdict; Visitor *v; BlockDriverState *bs = NULL; Error *local_err = NULL; @@ -762,15 +762,12 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "qed"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); @@ -789,7 +786,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename, ret = bdrv_qed_co_create(create_options, errp); fail: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; @@ -1437,8 +1434,9 @@ static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs, static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, - QEMUIOVector *qiov) + QEMUIOVector *qiov, int flags) { + assert(!flags); return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE); } @@ -1469,8 +1467,10 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, QED_AIOCB_WRITE | QED_AIOCB_ZERO); } -static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs, + int64_t offset, + PreallocMode prealloc, + Error **errp) { BDRVQEDState *s = bs->opaque; uint64_t old_image_size; @@ -1680,7 +1680,7 @@ static BlockDriver bdrv_qed = { .bdrv_co_readv = bdrv_qed_co_readv, .bdrv_co_writev = bdrv_qed_co_writev, .bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes, - .bdrv_truncate = bdrv_qed_truncate, + .bdrv_co_truncate = bdrv_qed_co_truncate, .bdrv_getlength = bdrv_qed_getlength, .bdrv_get_info = bdrv_qed_get_info, .bdrv_refresh_limits = bdrv_qed_refresh_limits, diff --git a/block/quorum.c b/block/quorum.c index cfe484a945..9152da8c58 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -17,6 +17,7 @@ #include "qemu/cutils.h" #include "qemu/option.h" #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qapi/qapi-events-block.h" #include "qapi/qmp/qdict.h" @@ -115,6 +116,7 @@ struct QuorumAIOCB { /* Request metadata */ uint64_t offset; uint64_t bytes; + int flags; QEMUIOVector *qiov; /* calling IOV */ @@ -157,7 +159,8 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b) static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, QEMUIOVector *qiov, uint64_t offset, - uint64_t bytes) + uint64_t bytes, + int flags) { BDRVQuorumState *s = bs->opaque; QuorumAIOCB *acb = g_new(QuorumAIOCB, 1); @@ -168,6 +171,7 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, .bs = bs, .offset = offset, .bytes = bytes, + .flags = flags, .qiov = qiov, .votes.compare = quorum_sha256_compare, .votes.vote_list = QLIST_HEAD_INITIALIZER(acb.votes.vote_list), @@ -271,9 +275,11 @@ static void quorum_rewrite_entry(void *opaque) BDRVQuorumState *s = acb->bs->opaque; /* Ignore any errors, it's just a correction attempt for already - * corrupted data. */ + * corrupted data. + * Mask out BDRV_REQ_WRITE_UNCHANGED because this overwrites the + * area with different data from the other children. */ bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes, - acb->qiov, 0); + acb->qiov, acb->flags & ~BDRV_REQ_WRITE_UNCHANGED); /* Wake up the caller after the last rewrite */ acb->rewrite_count--; @@ -608,7 +614,7 @@ static void read_quorum_children_entry(void *opaque) static int read_quorum_children(QuorumAIOCB *acb) { BDRVQuorumState *s = acb->bs->opaque; - int i, ret; + int i; acb->children_read = s->num_children; for (i = 0; i < s->num_children; i++) { @@ -643,9 +649,7 @@ static int read_quorum_children(QuorumAIOCB *acb) qemu_coroutine_yield(); } - ret = acb->vote_ret; - - return ret; + return acb->vote_ret; } static int read_fifo_child(QuorumAIOCB *acb) @@ -673,7 +677,7 @@ static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { BDRVQuorumState *s = bs->opaque; - QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); + QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); int ret; acb->is_read = true; @@ -699,7 +703,7 @@ static void write_quorum_entry(void *opaque) sacb->bs = s->children[i]->bs; sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes, - acb->qiov, 0); + acb->qiov, acb->flags); if (sacb->ret == 0) { acb->success_count++; } else { @@ -719,7 +723,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { BDRVQuorumState *s = bs->opaque; - QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); + QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags); int i, ret; for (i = 0; i < s->num_children; i++) { @@ -961,6 +965,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, } s->next_child_index = s->num_children; + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; + g_free(opened); goto exit; @@ -1082,8 +1088,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) children = qlist_new(); for (i = 0; i < s->num_children; i++) { - QINCREF(s->children[i]->bs->full_open_options); - qlist_append(children, s->children[i]->bs->full_open_options); + qlist_append(children, + qobject_ref(s->children[i]->bs->full_open_options)); } opts = qdict_new(); diff --git a/block/raw-format.c b/block/raw-format.c index a378547c99..2fd69cdb08 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -167,16 +167,37 @@ static void raw_reopen_abort(BDRVReopenState *state) state->opaque = NULL; } +/* Check and adjust the offset, against 'offset' and 'size' options. */ +static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset, + uint64_t bytes, bool is_write) +{ + BDRVRawState *s = bs->opaque; + + if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) { + /* There's not enough space for the write, or the read request is + * out-of-range. Don't read/write anything to prevent leaking out of + * the size specified in options. */ + return is_write ? -ENOSPC : -EINVAL; + } + + if (*offset > INT64_MAX - s->offset) { + return -EINVAL; + } + *offset += s->offset; + + return 0; +} + static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { - BDRVRawState *s = bs->opaque; + int ret; - if (offset > UINT64_MAX - s->offset) { - return -EINVAL; + ret = raw_adjust_offset(bs, &offset, bytes, false); + if (ret) { + return ret; } - offset += s->offset; BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); @@ -186,23 +207,11 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { - BDRVRawState *s = bs->opaque; void *buf = NULL; BlockDriver *drv; QEMUIOVector local_qiov; int ret; - if (s->has_size && (offset > s->size || bytes > (s->size - offset))) { - /* There's not enough space for the data. Don't write anything and just - * fail to prevent leaking out of the size specified in options. */ - return -ENOSPC; - } - - if (offset > UINT64_MAX - s->offset) { - ret = -EINVAL; - goto fail; - } - if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) { /* Handling partial writes would be a pain - so we just * require that guests have 512-byte request alignment if @@ -237,7 +246,10 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset, qiov = &local_qiov; } - offset += s->offset; + ret = raw_adjust_offset(bs, &offset, bytes, true); + if (ret) { + goto fail; + } BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); @@ -267,23 +279,25 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { - BDRVRawState *s = bs->opaque; - if (offset > UINT64_MAX - s->offset) { - return -EINVAL; + int ret; + + ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); + if (ret) { + return ret; } - offset += s->offset; return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); } static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { - BDRVRawState *s = bs->opaque; - if (offset > UINT64_MAX - s->offset) { - return -EINVAL; + int ret; + + ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true); + if (ret) { + return ret; } - offset += s->offset; - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); + return bdrv_co_pdiscard(bs->file, offset, bytes); } static int64_t raw_getlength(BlockDriverState *bs) @@ -352,8 +366,8 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) } } -static int raw_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVRawState *s = bs->opaque; @@ -369,7 +383,7 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset, s->size = offset; offset += s->offset; - return bdrv_truncate(bs->file, offset, prealloc, errp); + return bdrv_co_truncate(bs->file, offset, prealloc, errp); } static void raw_eject(BlockDriverState *bs, bool eject_flag) @@ -415,10 +429,11 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, } bs->sg = bs->file->bs->sg; - bs->supported_write_flags = BDRV_REQ_FUA & - bs->file->bs->supported_write_flags; - bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & - bs->file->bs->supported_zero_flags; + bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | + (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); + bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & + bs->file->bs->supported_zero_flags); if (bs->probed && !bdrv_is_read_only(bs)) { fprintf(stderr, @@ -482,6 +497,44 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo) return bdrv_probe_geometry(bs->file->bs, geo); } +static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs, + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + int ret; + + ret = raw_adjust_offset(bs, &src_offset, bytes, false); + if (ret) { + return ret; + } + return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset, + bytes, read_flags, write_flags); +} + +static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags) +{ + int ret; + + ret = raw_adjust_offset(bs, &dst_offset, bytes, true); + if (ret) { + return ret; + } + return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes, + read_flags, write_flags); +} + BlockDriver bdrv_raw = { .format_name = "raw", .instance_size = sizeof(BDRVRawState), @@ -498,7 +551,9 @@ BlockDriver bdrv_raw = { .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, .bdrv_co_pdiscard = &raw_co_pdiscard, .bdrv_co_block_status = &raw_co_block_status, - .bdrv_truncate = &raw_truncate, + .bdrv_co_copy_range_from = &raw_co_copy_range_from, + .bdrv_co_copy_range_to = &raw_co_copy_range_to, + .bdrv_co_truncate = &raw_co_truncate, .bdrv_getlength = &raw_getlength, .has_variable_length = true, .bdrv_measure = &raw_measure, diff --git a/block/rbd.c b/block/rbd.c index c9359d0ad8..ca8e5bbace 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -18,6 +18,7 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "block/block_int.h" +#include "block/qdict.h" #include "crypto/secret.h" #include "qemu/cutils.h" #include "qapi/qmp/qstring.h" @@ -226,27 +227,57 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, done: g_free(buf); - QDECREF(keypairs); + qobject_unref(keypairs); return; } -static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, +static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp) +{ + /* XXX Does RBD support AIO on less than 512-byte alignment? */ + bs->bl.request_alignment = 512; +} + + +static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts, Error **errp) { - if (secretid == 0) { - return 0; + char *key, *acr; + int r; + GString *accu; + RbdAuthModeList *auth; + + if (opts->key_secret) { + key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp); + if (!key) { + return -EIO; + } + r = rados_conf_set(cluster, "key", key); + g_free(key); + if (r < 0) { + error_setg_errno(errp, -r, "Could not set 'key'"); + return r; + } } - gchar *secret = qcrypto_secret_lookup_as_base64(secretid, - errp); - if (!secret) { - return -1; + if (opts->has_auth_client_required) { + accu = g_string_new(""); + for (auth = opts->auth_client_required; auth; auth = auth->next) { + if (accu->str[0]) { + g_string_append_c(accu, ';'); + } + g_string_append(accu, RbdAuthMode_str(auth->value)); + } + acr = g_string_free(accu, FALSE); + r = rados_conf_set(cluster, "auth_client_required", acr); + g_free(acr); + if (r < 0) { + error_setg_errno(errp, -r, + "Could not set 'auth_client_required'"); + return r; + } } - rados_conf_set(cluster, "key", secret); - g_free(secret); - return 0; } @@ -275,17 +306,17 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json, key = qstring_get_str(name); ret = rados_conf_set(cluster, key, qstring_get_str(value)); - QDECREF(value); + qobject_unref(value); if (ret < 0) { error_setg_errno(errp, -ret, "invalid conf option %s", key); - QDECREF(name); + qobject_unref(name); ret = -EINVAL; break; } - QDECREF(name); + qobject_unref(name); } - QDECREF(keypairs); + qobject_unref(keypairs); return ret; } @@ -337,9 +368,7 @@ static QemuOptsList runtime_opts = { }, }; -/* FIXME Deprecate and remove keypairs or make it available in QMP. - * password_secret should eventually be configurable in opts->location. Support - * for it in .bdrv_open will make it work here as well. */ +/* FIXME Deprecate and remove keypairs or make it available in QMP. */ static int qemu_rbd_do_create(BlockdevCreateOptions *options, const char *keypairs, const char *password_secret, Error **errp) @@ -449,7 +478,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename, } exit: - QDECREF(options); + qobject_unref(options); qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -545,6 +574,16 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, Error *local_err = NULL; int r; + if (secretid) { + if (opts->key_secret) { + error_setg(errp, + "Legacy 'password-secret' clashes with 'key-secret'"); + return -EINVAL; + } + opts->key_secret = g_strdup(secretid); + opts->has_key_secret = true; + } + mon_host = qemu_rbd_mon_host(opts, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -577,8 +616,8 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx, } } - if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) { - r = -EIO; + r = qemu_rbd_set_auth(*cluster, opts, errp); + if (r < 0) { goto failed_shutdown; } @@ -622,28 +661,11 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, BDRVRBDState *s = bs->opaque; BlockdevOptionsRbd *opts = NULL; Visitor *v; - QObject *crumpled = NULL; const QDictEntry *e; Error *local_err = NULL; - const char *filename; char *keypairs, *secretid; int r; - /* If we are given a filename, parse the filename, with precedence given to - * filename encoded options */ - filename = qdict_get_try_str(options, "filename"); - if (filename) { - warn_report("'filename' option specified. " - "This is an unsupported option, and may be deprecated " - "in the future"); - qemu_rbd_parse_filename(filename, options, &local_err); - qdict_del(options, "filename"); - if (local_err) { - error_propagate(errp, local_err); - return -EINVAL; - } - } - keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); if (keypairs) { qdict_del(options, "=keyvalue-pairs"); @@ -655,16 +677,14 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, } /* Convert the remaining options into a QAPI object */ - crumpled = qdict_crumple(options, errp); - if (crumpled == NULL) { + v = qobject_input_visitor_new_flat_confused(options, errp); + if (!v) { r = -EINVAL; goto out; } - v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); visit_free(v); - qobject_decref(crumpled); if (local_err) { error_propagate(errp, local_err); @@ -899,27 +919,23 @@ failed: return NULL; } -static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs, - int64_t sector_num, - QEMUIOVector *qiov, - int nb_sectors, - BlockCompletionFunc *cb, - void *opaque) -{ - return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov, - (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque, - RBD_AIO_READ); -} - -static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs, - int64_t sector_num, - QEMUIOVector *qiov, - int nb_sectors, +static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, BlockCompletionFunc *cb, void *opaque) { - return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov, - (int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque, + return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque, + RBD_AIO_READ); +} + +static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, + void *opaque) +{ + return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque, RBD_AIO_WRITE); } @@ -974,8 +990,10 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs) return info.size; } -static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs, + int64_t offset, + PreallocMode prealloc, + Error **errp) { BDRVRBDState *s = bs->opaque; int r; @@ -1158,6 +1176,7 @@ static BlockDriver bdrv_rbd = { .format_name = "rbd", .instance_size = sizeof(BDRVRBDState), .bdrv_parse_filename = qemu_rbd_parse_filename, + .bdrv_refresh_limits = qemu_rbd_refresh_limits, .bdrv_file_open = qemu_rbd_open, .bdrv_close = qemu_rbd_close, .bdrv_reopen_prepare = qemu_rbd_reopen_prepare, @@ -1167,11 +1186,11 @@ static BlockDriver bdrv_rbd = { .bdrv_get_info = qemu_rbd_getinfo, .create_opts = &qemu_rbd_create_opts, .bdrv_getlength = qemu_rbd_getlength, - .bdrv_truncate = qemu_rbd_truncate, + .bdrv_co_truncate = qemu_rbd_co_truncate, .protocol_name = "rbd", - .bdrv_aio_readv = qemu_rbd_aio_readv, - .bdrv_aio_writev = qemu_rbd_aio_writev, + .bdrv_aio_preadv = qemu_rbd_aio_preadv, + .bdrv_aio_pwritev = qemu_rbd_aio_pwritev, #ifdef LIBRBD_SUPPORTS_AIO_FLUSH .bdrv_aio_flush = qemu_rbd_aio_flush, diff --git a/block/replication.c b/block/replication.c index 6c0c7186d9..6349d6958e 100644 --- a/block/replication.c +++ b/block/replication.c @@ -145,7 +145,7 @@ static void replication_close(BlockDriverState *bs) replication_stop(s->rs, false, NULL); } if (s->stage == BLOCK_REPLICATION_FAILOVER) { - block_job_cancel_sync(s->active_disk->bs->job); + job_cancel_sync(&s->active_disk->bs->job->job); } if (s->mode == REPLICATION_MODE_SECONDARY) { @@ -246,13 +246,14 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, backup_cow_request_begin(&req, child->bs->job, sector_num * BDRV_SECTOR_SIZE, remaining_bytes); - ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, - qiov); + ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, + remaining_bytes, qiov, 0); backup_cow_request_end(&req); goto out; } - ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, qiov); + ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, + remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); out: return replication_return_value(s, ret); } @@ -260,7 +261,8 @@ out: static coroutine_fn int replication_co_writev(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, - QEMUIOVector *qiov) + QEMUIOVector *qiov, + int flags) { BDRVReplicationState *s = bs->opaque; QEMUIOVector hd_qiov; @@ -271,14 +273,15 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, int ret; int64_t n; + assert(!flags); ret = replication_get_io_status(s); if (ret < 0) { goto out; } if (ret == 0) { - ret = bdrv_co_writev(top, sector_num, - remaining_sectors, qiov); + ret = bdrv_co_pwritev(top, sector_num * BDRV_SECTOR_SIZE, + remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); return replication_return_value(s, ret); } @@ -304,7 +307,8 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs, qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count); target = ret ? top : base; - ret = bdrv_co_writev(target, sector_num, n, &hd_qiov); + ret = bdrv_co_pwritev(target, sector_num * BDRV_SECTOR_SIZE, + n * BDRV_SECTOR_SIZE, &hd_qiov, 0); if (ret < 0) { goto out1; } @@ -566,7 +570,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs, 0, MIRROR_SYNC_MODE_NONE, NULL, false, BLOCKDEV_ON_ERROR_REPORT, - BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL, + BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, backup_job_completed, bs, NULL, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -574,7 +578,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, aio_context_release(aio_context); return; } - block_job_start(job); + job_start(&job->job); break; default: aio_context_release(aio_context); @@ -679,7 +683,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) * disk, secondary disk in backup_job_completed(). */ if (s->secondary_disk->bs->job) { - block_job_cancel_sync(s->secondary_disk->bs->job); + job_cancel_sync(&s->secondary_disk->bs->job->job); } if (!failover) { @@ -691,7 +695,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) s->stage = BLOCK_REPLICATION_FAILOVER; commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs, - BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, + JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, NULL, replication_done, bs, true, errp); break; default: diff --git a/block/sheepdog.c b/block/sheepdog.c index 387f59c8aa..b229a664d9 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -24,6 +24,7 @@ #include "qemu/option.h" #include "qemu/sockets.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/bitops.h" #include "qemu/cutils.h" @@ -538,27 +539,17 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s, static SocketAddress *sd_server_config(QDict *options, Error **errp) { QDict *server = NULL; - QObject *crumpled_server = NULL; Visitor *iv = NULL; SocketAddress *saddr = NULL; Error *local_err = NULL; qdict_extract_subqdict(options, &server, "server."); - crumpled_server = qdict_crumple(server, errp); - if (!crumpled_server) { + iv = qobject_input_visitor_new_flat_confused(server, errp); + if (!iv) { goto done; } - /* - * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive - * server.type=inet. .to doesn't matter, it's ignored anyway. - * That's because when @options come from -blockdev or - * blockdev_add, members are typed according to the QAPI schema, - * but when they come from -drive, they're all QString. The - * visitor expects the former. - */ - iv = qobject_input_visitor_new(crumpled_server); visit_type_SocketAddress(iv, NULL, &saddr, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -567,8 +558,7 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp) done: visit_free(iv); - qobject_decref(crumpled_server); - QDECREF(server); + qobject_unref(server); return saddr; } @@ -1859,9 +1849,7 @@ out: error_setg_errno(errp, -ret, "Can't pre-allocate"); } out_with_err_set: - if (blk) { - blk_unref(blk); - } + blk_unref(blk); g_free(buf); return ret; @@ -1883,7 +1871,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, if (local_err) { error_propagate(errp, local_err); - qobject_decref(obj); + qobject_unref(obj); return -EINVAL; } @@ -1901,7 +1889,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size, ret = sd_prealloc(bs, 0, size, errp); fail: bdrv_unref(bs); - QDECREF(qdict); + qobject_unref(qdict); return ret; } @@ -1987,6 +1975,7 @@ static SheepdogRedundancy *parse_redundancy_str(const char *opt) } else { ret = qemu_strtol(n2, NULL, 10, &parity); if (ret < 0) { + g_free(redundancy); return NULL; } @@ -2181,9 +2170,8 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, { BlockdevCreateOptions *create_options = NULL; QDict *qdict, *location_qdict; - QObject *crumpled; Visitor *v; - const char *redundancy; + char *redundancy; Error *local_err = NULL; int ret; @@ -2217,16 +2205,14 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, } /* Get the QAPI object */ - crumpled = qdict_crumple(qdict, errp); - if (crumpled == NULL) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(crumpled); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); - qobject_decref(crumpled); if (local_err) { error_propagate(errp, local_err); @@ -2252,7 +2238,8 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts, ret = sd_co_create(create_options, errp); fail: qapi_free_BlockdevCreateOptions(create_options); - QDECREF(qdict); + qobject_unref(qdict); + g_free(redundancy); return ret; } @@ -2305,8 +2292,8 @@ static int64_t sd_getlength(BlockDriverState *bs) return s->inode.vdi_size; } -static int sd_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVSheepdogState *s = bs->opaque; int ret, fd; @@ -2335,7 +2322,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset, } /* we don't need to update entire object */ - datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); + datalen = SD_INODE_HEADER_SIZE; s->inode.vdi_size = offset; ret = write_object(fd, s->bs, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies, @@ -2612,15 +2599,17 @@ static void sd_aio_complete(SheepdogAIOCB *acb) } static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) + int nb_sectors, QEMUIOVector *qiov, + int flags) { SheepdogAIOCB acb; int ret; int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE; BDRVSheepdogState *s = bs->opaque; + assert(!flags); if (offset > s->inode.vdi_size) { - ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); + ret = sd_co_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); if (ret < 0) { return ret; } @@ -2701,7 +2690,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) */ strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag)); /* we don't need to update entire object */ - datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); + datalen = SD_INODE_HEADER_SIZE; inode = g_malloc(datalen); /* refresh inode. */ @@ -2936,13 +2925,14 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) QEMUSnapshotInfo *sn_tab = NULL; unsigned wlen, rlen; int found = 0; - static SheepdogInode inode; + SheepdogInode *inode; unsigned long *vdi_inuse; unsigned int start_nr; uint64_t hval; uint32_t vid; vdi_inuse = g_malloc(max); + inode = g_malloc(SD_INODE_HEADER_SIZE); fd = connect_to_sdog(s, &local_err); if (fd < 0) { @@ -2985,26 +2975,26 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) } /* we don't need to read entire object */ - ret = read_object(fd, s->bs, (char *)&inode, + ret = read_object(fd, s->bs, (char *)inode, vid_to_vdi_oid(vid), - 0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0, + 0, SD_INODE_HEADER_SIZE, 0, s->cache_flags); if (ret) { continue; } - if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) { - sn_tab[found].date_sec = inode.snap_ctime >> 32; - sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff; - sn_tab[found].vm_state_size = inode.vm_state_size; - sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec; + if (!strcmp(inode->name, s->name) && is_snapshot(inode)) { + sn_tab[found].date_sec = inode->snap_ctime >> 32; + sn_tab[found].date_nsec = inode->snap_ctime & 0xffffffff; + sn_tab[found].vm_state_size = inode->vm_state_size; + sn_tab[found].vm_clock_nsec = inode->vm_clock_nsec; snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), - "%" PRIu32, inode.snap_id); + "%" PRIu32, inode->snap_id); pstrcpy(sn_tab[found].name, - MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)), - inode.tag); + MIN(sizeof(sn_tab[found].name), sizeof(inode->tag)), + inode->tag); found++; } } @@ -3014,6 +3004,7 @@ out: *psn_tab = sn_tab; g_free(vdi_inuse); + g_free(inode); if (ret < 0) { return ret; @@ -3240,7 +3231,7 @@ static BlockDriver bdrv_sheepdog = { .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, .bdrv_get_allocated_file_size = sd_get_allocated_file_size, - .bdrv_truncate = sd_truncate, + .bdrv_co_truncate = sd_co_truncate, .bdrv_co_readv = sd_co_readv, .bdrv_co_writev = sd_co_writev, @@ -3277,7 +3268,7 @@ static BlockDriver bdrv_sheepdog_tcp = { .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, .bdrv_get_allocated_file_size = sd_get_allocated_file_size, - .bdrv_truncate = sd_truncate, + .bdrv_co_truncate = sd_co_truncate, .bdrv_co_readv = sd_co_readv, .bdrv_co_writev = sd_co_writev, @@ -3314,7 +3305,7 @@ static BlockDriver bdrv_sheepdog_unix = { .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_getlength = sd_getlength, .bdrv_get_allocated_file_size = sd_get_allocated_file_size, - .bdrv_truncate = sd_truncate, + .bdrv_co_truncate = sd_co_truncate, .bdrv_co_readv = sd_co_readv, .bdrv_co_writev = sd_co_writev, diff --git a/block/snapshot.c b/block/snapshot.c index eacc1f19a2..f9903bc94e 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "block/snapshot.h" #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" @@ -214,7 +215,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, bdrv_ref(file); qdict_extract_subqdict(options, &file_options, "file."); - QDECREF(file_options); + qobject_unref(file_options); qdict_put_str(options, "file", bdrv_get_node_name(file)); drv->bdrv_close(bs); @@ -223,7 +224,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, ret = bdrv_snapshot_goto(file, snapshot_id, errp); open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); - QDECREF(options); + qobject_unref(options); if (open_ret < 0) { bdrv_unref(file); bs->drv = NULL; diff --git a/block/ssh.c b/block/ssh.c index ab3acf0c22..7fbc27abdf 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -28,6 +28,7 @@ #include #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -605,7 +606,6 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) BlockdevOptionsSsh *result = NULL; QemuOpts *opts = NULL; Error *local_err = NULL; - QObject *crumpled; const QDictEntry *e; Visitor *v; @@ -622,23 +622,13 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp) } /* Create the QAPI object */ - crumpled = qdict_crumple(options, errp); - if (crumpled == NULL) { + v = qobject_input_visitor_new_flat_confused(options, errp); + if (!v) { goto fail; } - /* - * FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive. - * .to doesn't matter, it's ignored anyway. - * That's because when @options come from -blockdev or - * blockdev_add, members are typed according to the QAPI schema, - * but when they come from -drive, they're all QString. The - * visitor expects the former. - */ - v = qobject_input_visitor_new(crumpled); visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); visit_free(v); - qobject_decref(crumpled); if (local_err) { error_propagate(errp, local_err); @@ -917,7 +907,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, ret = ssh_co_create(create_options, errp); out: - QDECREF(uri_options); + qobject_unref(uri_options); qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -1164,11 +1154,13 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, static coroutine_fn int ssh_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) + int nb_sectors, QEMUIOVector *qiov, + int flags) { BDRVSSHState *s = bs->opaque; int ret; + assert(!flags); qemu_co_mutex_lock(&s->lock); ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE, nb_sectors * BDRV_SECTOR_SIZE, qiov); @@ -1251,8 +1243,8 @@ static int64_t ssh_getlength(BlockDriverState *bs) return length; } -static int ssh_truncate(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp) +static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) { BDRVSSHState *s = bs->opaque; @@ -1287,7 +1279,7 @@ static BlockDriver bdrv_ssh = { .bdrv_co_readv = ssh_co_readv, .bdrv_co_writev = ssh_co_writev, .bdrv_getlength = ssh_getlength, - .bdrv_truncate = ssh_truncate, + .bdrv_co_truncate = ssh_co_truncate, .bdrv_co_flush_to_disk = ssh_co_flush, .create_opts = &ssh_create_opts, }; diff --git a/block/stream.c b/block/stream.c index 1a85708fcf..9264b68a1e 100644 --- a/block/stream.c +++ b/block/stream.c @@ -29,11 +29,8 @@ enum { STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */ }; -#define SLICE_TIME 100000000ULL /* ns */ - typedef struct StreamBlockJob { BlockJob common; - RateLimit limit; BlockDriverState *base; BlockdevOnError on_error; char *backing_file_str; @@ -61,16 +58,16 @@ typedef struct { int ret; } StreamCompleteData; -static void stream_complete(BlockJob *job, void *opaque) +static void stream_complete(Job *job, void *opaque) { - StreamBlockJob *s = container_of(job, StreamBlockJob, common); + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); + BlockJob *bjob = &s->common; StreamCompleteData *data = opaque; - BlockDriverState *bs = blk_bs(job->blk); + BlockDriverState *bs = blk_bs(bjob->blk); BlockDriverState *base = s->base; Error *local_err = NULL; - if (!block_job_is_cancelled(&s->common) && bs->backing && - data->ret == 0) { + if (!job_is_cancelled(job) && bs->backing && data->ret == 0) { const char *base_id = NULL, *base_fmt = NULL; if (base) { base_id = s->backing_file_str; @@ -91,12 +88,12 @@ out: /* Reopen the image back in read-only mode if necessary */ if (s->bs_flags != bdrv_get_flags(bs)) { /* Give up write permissions before making it read-only */ - blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); + blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); bdrv_reopen(bs, s->bs_flags, NULL); } g_free(s->backing_file_str); - block_job_completed(&s->common, data->ret); + job_completed(job, data->ret, NULL); g_free(data); } @@ -107,6 +104,7 @@ static void coroutine_fn stream_run(void *opaque) BlockBackend *blk = s->common.blk; BlockDriverState *bs = blk_bs(blk); BlockDriverState *base = s->base; + int64_t len; int64_t offset = 0; uint64_t delay_ns = 0; int error = 0; @@ -118,11 +116,12 @@ static void coroutine_fn stream_run(void *opaque) goto out; } - s->common.len = bdrv_getlength(bs); - if (s->common.len < 0) { - ret = s->common.len; + len = bdrv_getlength(bs); + if (len < 0) { + ret = len; goto out; } + job_progress_set_remaining(&s->common.job, len); buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); @@ -135,14 +134,14 @@ static void coroutine_fn stream_run(void *opaque) bdrv_enable_copy_on_read(bs); } - for ( ; offset < s->common.len; offset += n) { + for ( ; offset < len; offset += n) { bool copy; /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. */ - block_job_sleep_ns(&s->common, delay_ns); - if (block_job_is_cancelled(&s->common)) { + job_sleep_ns(&s->common.job, delay_ns); + if (job_is_cancelled(&s->common.job)) { break; } @@ -159,7 +158,7 @@ static void coroutine_fn stream_run(void *opaque) /* Finish early if end of backing file has been reached */ if (ret == 0 && n == 0) { - n = s->common.len - offset; + n = len - offset; } copy = (ret == 1); @@ -185,9 +184,9 @@ static void coroutine_fn stream_run(void *opaque) ret = 0; /* Publish progress */ - s->common.offset += n; - if (copy && s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, n); + job_progress_update(&s->common.job, n); + if (copy) { + delay_ns = block_job_ratelimit_get_delay(&s->common, n); } else { delay_ns = 0; } @@ -206,25 +205,18 @@ out: /* Modify backing chain and close BDSes in main loop */ data = g_malloc(sizeof(*data)); data->ret = ret; - block_job_defer_to_main_loop(&s->common, stream_complete, data); -} - -static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) -{ - StreamBlockJob *s = container_of(job, StreamBlockJob, common); - - if (speed < 0) { - error_setg(errp, QERR_INVALID_PARAMETER, "speed"); - return; - } - ratelimit_set_speed(&s->limit, speed, SLICE_TIME); + job_defer_to_main_loop(&s->common.job, stream_complete, data); } static const BlockJobDriver stream_job_driver = { - .instance_size = sizeof(StreamBlockJob), - .job_type = BLOCK_JOB_TYPE_STREAM, - .set_speed = stream_set_speed, - .start = stream_run, + .job_driver = { + .instance_size = sizeof(StreamBlockJob), + .job_type = JOB_TYPE_STREAM, + .free = block_job_free, + .start = stream_run, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + }, }; void stream_start(const char *job_id, BlockDriverState *bs, @@ -251,7 +243,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, BLK_PERM_GRAPH_MOD, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE, - speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); + speed, JOB_DEFAULT, NULL, NULL, errp); if (!s) { goto fail; } @@ -272,7 +264,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, s->on_error = on_error; trace_stream_start(bs, base, s); - block_job_start(&s->common); + job_start(&s->common.job); return; fail: diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 36cc0430c3..e297b04e17 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -564,6 +564,10 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) qemu_mutex_lock(&tg->lock); for (i = 0; i < 2; i++) { + if (timer_pending(tgm->throttle_timers.timers[i])) { + tg->any_timer_armed[i] = false; + schedule_next_request(tgm, i); + } if (tg->tokens[i] == tgm) { token = throttle_group_next_tgm(tgm); /* Take care of the case where this is the last tgm in the group */ diff --git a/block/throttle.c b/block/throttle.c index 95ed06acd8..636c9764aa 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -36,9 +36,12 @@ static QemuOptsList throttle_opts = { }, }; -static int throttle_configure_tgm(BlockDriverState *bs, - ThrottleGroupMember *tgm, - QDict *options, Error **errp) +/* + * If this function succeeds then the throttle group name is stored in + * @group and must be freed by the caller. + * If there's an error then @group remains unmodified. + */ +static int throttle_parse_options(QDict *options, char **group, Error **errp) { int ret; const char *group_name; @@ -63,8 +66,7 @@ static int throttle_configure_tgm(BlockDriverState *bs, goto fin; } - /* Register membership to group with name group_name */ - throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs)); + *group = g_strdup(group_name); ret = 0; fin: qemu_opts_del(opts); @@ -75,16 +77,27 @@ static int throttle_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { ThrottleGroupMember *tgm = bs->opaque; + char *group; + int ret; bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, errp); if (!bs->file) { return -EINVAL; } - bs->supported_write_flags = bs->file->bs->supported_write_flags; - bs->supported_zero_flags = bs->file->bs->supported_zero_flags; + bs->supported_write_flags = bs->file->bs->supported_write_flags | + BDRV_REQ_WRITE_UNCHANGED; + bs->supported_zero_flags = bs->file->bs->supported_zero_flags | + BDRV_REQ_WRITE_UNCHANGED; - return throttle_configure_tgm(bs, tgm, options, errp); + ret = throttle_parse_options(options, &group, errp); + if (ret == 0) { + /* Register membership to group with name group_name */ + throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs)); + g_free(group); + } + + return ret; } static void throttle_close(BlockDriverState *bs) @@ -136,7 +149,7 @@ static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs, ThrottleGroupMember *tgm = bs->opaque; throttle_group_co_io_limits_intercept(tgm, bytes, true); - return bdrv_co_pdiscard(bs->file->bs, offset, bytes); + return bdrv_co_pdiscard(bs->file, offset, bytes); } static int throttle_co_flush(BlockDriverState *bs) @@ -160,35 +173,36 @@ static void throttle_attach_aio_context(BlockDriverState *bs, static int throttle_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp) { - ThrottleGroupMember *tgm; + int ret; + char *group = NULL; assert(reopen_state != NULL); assert(reopen_state->bs != NULL); - reopen_state->opaque = g_new0(ThrottleGroupMember, 1); - tgm = reopen_state->opaque; - - return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options, - errp); + ret = throttle_parse_options(reopen_state->options, &group, errp); + reopen_state->opaque = group; + return ret; } static void throttle_reopen_commit(BDRVReopenState *reopen_state) { - ThrottleGroupMember *old_tgm = reopen_state->bs->opaque; - ThrottleGroupMember *new_tgm = reopen_state->opaque; + BlockDriverState *bs = reopen_state->bs; + ThrottleGroupMember *tgm = bs->opaque; + char *group = reopen_state->opaque; - throttle_group_unregister_tgm(old_tgm); - g_free(old_tgm); - reopen_state->bs->opaque = new_tgm; + assert(group); + + if (strcmp(group, throttle_group_get_name(tgm))) { + throttle_group_unregister_tgm(tgm); + throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs)); + } + g_free(reopen_state->opaque); reopen_state->opaque = NULL; } static void throttle_reopen_abort(BDRVReopenState *reopen_state) { - ThrottleGroupMember *tgm = reopen_state->opaque; - - throttle_group_unregister_tgm(tgm); - g_free(tgm); + g_free(reopen_state->opaque); reopen_state->opaque = NULL; } diff --git a/block/trace-events b/block/trace-events index f8c50b4063..3e8c47bb24 100644 --- a/block/trace-events +++ b/block/trace-events @@ -4,11 +4,6 @@ bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\"" bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" -# blockjob.c -block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" -block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" -block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" - # block/block-backend.c blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" @@ -20,6 +15,8 @@ bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x" bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64 +bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" +bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" # block/stream.c stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" @@ -47,6 +44,7 @@ backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64 backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64 backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" +backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" # blockdev.c qmp_block_job_cancel(void *job) "job %p" @@ -59,8 +57,9 @@ qmp_block_stream(void *bs, void *job) "bs %p job %p" # block/file-win32.c # block/file-posix.c -paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" -paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" +file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" +file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" +file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64 # block/qcow2.c qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" @@ -154,3 +153,6 @@ nvme_free_req_queue_wait(void *q) "q %p" nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d" nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" + +# block/iscsi.c +iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d" diff --git a/block/vdi.c b/block/vdi.c index 4a2d1ff88d..6555cffb88 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -50,11 +50,12 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-block-core.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" @@ -83,9 +84,6 @@ /* Command line option for static images. */ #define BLOCK_OPT_STATIC "static" -#define KiB 1024 -#define MiB (KiB * KiB) - #define SECTOR_SIZE 512 #define DEFAULT_CLUSTER_SIZE (1 * MiB) @@ -434,7 +432,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } else if (header.block_size != DEFAULT_CLUSTER_SIZE) { error_setg(errp, "unsupported VDI image (block size %" PRIu32 - " is not %u)", header.block_size, DEFAULT_CLUSTER_SIZE); + " is not %" PRIu64 ")", + header.block_size, DEFAULT_CLUSTER_SIZE); ret = -ENOTSUP; goto fail; } else if (header.disk_size > @@ -865,6 +864,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, } } + ret = 0; exit: blk_unref(blk); bdrv_unref(bs_file); @@ -933,7 +933,11 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, } /* Get the QAPI object */ - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { + ret = -EINVAL; + goto done; + } visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); @@ -951,7 +955,7 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts, /* Create the vdi image (format layer) */ ret = vdi_co_do_create(create_options, block_size, errp); done: - QDECREF(qdict); + qobject_unref(qdict); qapi_free_BlockdevCreateOptions(create_options); bdrv_unref(bs_file); return ret; diff --git a/block/vhdx-endian.c b/block/vhdx-endian.c index 429d7556bd..41fbdd2b8f 100644 --- a/block/vhdx-endian.c +++ b/block/vhdx-endian.c @@ -19,7 +19,7 @@ #include "qemu-common.h" #include "block/block_int.h" #include "qemu/bswap.h" -#include "block/vhdx.h" +#include "vhdx.h" /* * All the VHDX formats on disk are little endian - the following diff --git a/block/vhdx-log.c b/block/vhdx-log.c index 0ac4863b25..d2f1b98199 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -24,7 +24,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/bswap.h" -#include "block/vhdx.h" +#include "vhdx.h" typedef struct VHDXLogSequence { diff --git a/block/vhdx.c b/block/vhdx.c index 6ac0424f61..0795ca1985 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -18,12 +18,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/crc32c.h" #include "qemu/bswap.h" -#include "block/vhdx.h" +#include "vhdx.h" #include "migration/blocker.h" #include "qemu/uuid.h" #include "qapi/qmp/qdict.h" @@ -184,7 +185,7 @@ uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size, /* Validates the checksum of the buffer, with an in-place CRC. * * Zero is substituted during crc calculation for the original crc field, - * and the crc field is restored afterwards. But the buffer will be modifed + * and the crc field is restored afterwards. But the buffer will be modified * during the calculation, so this may not be not suitable for multi-threaded * use. * @@ -1126,9 +1127,9 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num, break; case PAYLOAD_BLOCK_FULLY_PRESENT: qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_readv(bs->file, - sinfo.file_offset >> BDRV_SECTOR_BITS, - sinfo.sectors_avail, &hd_qiov); + ret = bdrv_co_preadv(bs->file, sinfo.file_offset, + sinfo.sectors_avail * BDRV_SECTOR_SIZE, + &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { goto exit; @@ -1226,7 +1227,8 @@ int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s) } static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov) + int nb_sectors, QEMUIOVector *qiov, + int flags) { int ret = -ENOTSUP; BDRVVHDXState *s = bs->opaque; @@ -1242,6 +1244,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, uint64_t bat_prior_offset = 0; bool bat_update = false; + assert(!flags); qemu_iovec_init(&hd_qiov, qiov->niov); qemu_co_mutex_lock(&s->lock); @@ -1346,9 +1349,9 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, } /* block exists, so we can just overwrite it */ qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_writev(bs->file, - sinfo.file_offset >> BDRV_SECTOR_BITS, - sectors_to_write, &hd_qiov); + ret = bdrv_co_pwritev(bs->file, sinfo.file_offset, + sectors_to_write * BDRV_SECTOR_SIZE, + &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); if (ret < 0) { goto error_bat_restore; @@ -1949,7 +1952,7 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts, goto delete_and_exit; } - + ret = 0; delete_and_exit: blk_unref(blk); bdrv_unref(bs); @@ -1962,8 +1965,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, Error **errp) { BlockdevCreateOptions *create_options = NULL; - QDict *qdict = NULL; - QObject *qobj; + QDict *qdict; Visitor *v; BlockDriverState *bs = NULL; Error *local_err = NULL; @@ -2002,15 +2004,12 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "vhdx"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); @@ -2049,7 +2048,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, ret = vhdx_co_create(create_options, errp); fail: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; diff --git a/block/vmdk.c b/block/vmdk.c index 84f8bbe480..a9d0084e36 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -333,6 +333,12 @@ static int vmdk_is_cid_valid(BlockDriverState *bs) if (!s->cid_checked && bs->backing) { BlockDriverState *p_bs = bs->backing->bs; + if (strcmp(p_bs->drv->format_name, "vmdk")) { + /* Backing file is not in vmdk format, so it does not have + * a CID, which makes the overlay's parent CID invalid */ + return 0; + } + if (vmdk_read_cid(p_bs, 0, &cur_pcid) != 0) { /* read failure: report as not valid */ return 0; diff --git a/block/vpc.c b/block/vpc.c index 44f99a4d1b..bf294abfa7 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #include "qemu/module.h" #include "qemu/option.h" @@ -1080,8 +1081,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts, Error **errp) { BlockdevCreateOptions *create_options = NULL; - QDict *qdict = NULL; - QObject *qobj; + QDict *qdict; Visitor *v; BlockDriverState *bs = NULL; Error *local_err = NULL; @@ -1118,15 +1118,12 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, qdict_put_str(qdict, "driver", "vpc"); qdict_put_str(qdict, "file", bs->node_name); - qobj = qdict_crumple(qdict, errp); - QDECREF(qdict); - qdict = qobject_to(QDict, qobj); - if (qdict == NULL) { + v = qobject_input_visitor_new_flat_confused(qdict, errp); + if (!v) { ret = -EINVAL; goto fail; } - v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_free(v); @@ -1157,7 +1154,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename, ret = vpc_co_create(create_options, errp); fail: - QDECREF(qdict); + qobject_unref(qdict); bdrv_unref(bs); qapi_free_BlockdevCreateOptions(create_options); return ret; diff --git a/block/vvfat.c b/block/vvfat.c index 1569783b0f..fc41841a5c 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -27,6 +27,7 @@ #include #include "qapi/error.h" #include "block/block_int.h" +#include "block/qdict.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/bswap.h" @@ -1244,8 +1245,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, s->fat2 = NULL; s->downcase_short_names = 1; - fprintf(stderr, "vvfat %s chs %d,%d,%d\n", - dirname, cyls, heads, secs); + DLOG(fprintf(stderr, "vvfat %s chs %d,%d,%d\n", + dirname, cyls, heads, secs)); s->sector_count = cyls * heads * secs - s->offset_to_bootsector; @@ -3133,6 +3134,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options, } static const BdrvChildRole child_vvfat_qcow = { + .parent_is_bds = true, .inherit_options = vvfat_qcow_options, }; @@ -3179,7 +3181,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) qdict_put_str(options, "write-target.driver", "qcow"); s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs, &child_vvfat_qcow, false, errp); - QDECREF(options); + qobject_unref(options); if (!s->qcow) { ret = -EINVAL; goto err; diff --git a/block/vxhs.c b/block/vxhs.c index 75cc6c8672..0cb0a007e9 100644 --- a/block/vxhs.c +++ b/block/vxhs.c @@ -12,6 +12,7 @@ #include #include #include "block/block_int.h" +#include "block/qdict.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -216,6 +217,12 @@ static void vxhs_parse_filename(const char *filename, QDict *options, } } +static void vxhs_refresh_limits(BlockDriverState *bs, Error **errp) +{ + /* XXX Does VXHS support AIO on less than 512-byte alignment? */ + bs->bl.request_alignment = 512; +} + static int vxhs_init_and_ref(void) { if (vxhs_ref++ == 0) { @@ -396,7 +403,7 @@ static int vxhs_open(BlockDriverState *bs, QDict *options, out: g_free(of_vsa_addr); - QDECREF(backing_options); + qobject_unref(backing_options); qemu_opts_del(tcp_opts); qemu_opts_del(opts); g_free(cacert); @@ -424,21 +431,17 @@ static const AIOCBInfo vxhs_aiocb_info = { * and is passed to QNIO. When QNIO completes the work, * it will be passed back through the callback. */ -static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num, - QEMUIOVector *qiov, int nb_sectors, +static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset, + QEMUIOVector *qiov, uint64_t size, BlockCompletionFunc *cb, void *opaque, VDISKAIOCmd iodir) { VXHSAIOCB *acb = NULL; BDRVVXHSState *s = bs->opaque; - size_t size; - uint64_t offset; int iio_flags = 0; int ret = 0; void *dev_handle = s->vdisk_hostinfo.dev_handle; - offset = sector_num * BDRV_SECTOR_SIZE; - size = nb_sectors * BDRV_SECTOR_SIZE; acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque); /* @@ -451,11 +454,11 @@ static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num, switch (iodir) { case VDISK_AIO_WRITE: ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov, - offset, (uint64_t)size, iio_flags); + offset, size, iio_flags); break; case VDISK_AIO_READ: ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov, - offset, (uint64_t)size, iio_flags); + offset, size, iio_flags); break; default: trace_vxhs_aio_rw_invalid(iodir); @@ -474,22 +477,20 @@ errout: return NULL; } -static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, - int nb_sectors, +static BlockAIOCB *vxhs_aio_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, BlockCompletionFunc *cb, void *opaque) { - return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb, - opaque, VDISK_AIO_READ); + return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_READ); } -static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, - int nb_sectors, - BlockCompletionFunc *cb, void *opaque) +static BlockAIOCB *vxhs_aio_pwritev(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags, + BlockCompletionFunc *cb, void *opaque) { - return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, - cb, opaque, VDISK_AIO_WRITE); + return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_WRITE); } static void vxhs_close(BlockDriverState *bs) @@ -561,10 +562,11 @@ static BlockDriver bdrv_vxhs = { .instance_size = sizeof(BDRVVXHSState), .bdrv_file_open = vxhs_open, .bdrv_parse_filename = vxhs_parse_filename, + .bdrv_refresh_limits = vxhs_refresh_limits, .bdrv_close = vxhs_close, .bdrv_getlength = vxhs_getlength, - .bdrv_aio_readv = vxhs_aio_readv, - .bdrv_aio_writev = vxhs_aio_writev, + .bdrv_aio_preadv = vxhs_aio_preadv, + .bdrv_aio_pwritev = vxhs_aio_pwritev, }; static void bdrv_vxhs_init(void) diff --git a/block/win32-aio.c b/block/win32-aio.c index 3be8f458fa..9cd355d42f 100644 --- a/block/win32-aio.c +++ b/block/win32-aio.c @@ -112,15 +112,14 @@ static const AIOCBInfo win32_aiocb_info = { BlockAIOCB *win32_aio_submit(BlockDriverState *bs, QEMUWin32AIOState *aio, HANDLE hfile, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, BlockCompletionFunc *cb, void *opaque, int type) { struct QEMUWin32AIOCB *waiocb; - uint64_t offset = sector_num * 512; DWORD rc; waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque); - waiocb->nbytes = nb_sectors * 512; + waiocb->nbytes = bytes; waiocb->qiov = qiov; waiocb->is_read = (type == QEMU_AIO_READ); diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 65a84739ed..1ef11041a7 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -220,3 +220,26 @@ void qmp_nbd_server_stop(Error **errp) nbd_server_free(nbd_server); nbd_server = NULL; } + +void qmp_x_nbd_server_add_bitmap(const char *name, const char *bitmap, + bool has_bitmap_export_name, + const char *bitmap_export_name, + Error **errp) +{ + NBDExport *exp; + + if (!nbd_server) { + error_setg(errp, "NBD server not running"); + return; + } + + exp = nbd_export_find(name); + if (exp == NULL) { + error_setg(errp, "Export '%s' is not found", name); + return; + } + + nbd_export_bitmap(exp, bitmap, + has_bitmap_export_name ? bitmap_export_name : bitmap, + errp); +} diff --git a/blockdev.c b/blockdev.c index 09a4fb4f58..3e719a22b9 100644 --- a/blockdev.c +++ b/blockdev.c @@ -35,6 +35,7 @@ #include "sysemu/blockdev.h" #include "hw/block/block.h" #include "block/blockjob.h" +#include "block/qdict.h" #include "block/throttle-groups.h" #include "monitor/monitor.h" #include "qemu/error-report.h" @@ -150,7 +151,7 @@ void blockdev_mark_auto_del(BlockBackend *blk) aio_context_acquire(aio_context); if (bs->job) { - block_job_cancel(bs->job, false); + job_cancel(&bs->job->job, false); } aio_context_release(aio_context); @@ -576,7 +577,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, blk_rs->read_only = read_only; blk_rs->detect_zeroes = detect_zeroes; - QDECREF(bs_opts); + qobject_unref(bs_opts); } else { if (file && !*file) { file = NULL; @@ -632,16 +633,16 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, err_no_bs_opts: qemu_opts_del(opts); - QDECREF(interval_dict); - QDECREF(interval_list); + qobject_unref(interval_dict); + qobject_unref(interval_list); return blk; early_err: qemu_opts_del(opts); - QDECREF(interval_dict); - QDECREF(interval_list); + qobject_unref(interval_dict); + qobject_unref(interval_list); err_no_opts: - QDECREF(bs_opts); + qobject_unref(bs_opts); return NULL; } @@ -1139,7 +1140,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) fail: qemu_opts_del(legacy_opts); - QDECREF(bs_opts); + qobject_unref(bs_opts); return dinfo; } @@ -1455,7 +1456,7 @@ typedef struct BlkActionOps { struct BlkActionState { TransactionAction *action; const BlkActionOps *ops; - BlockJobTxn *block_job_txn; + JobTxn *block_job_txn; TransactionProperties *txn_props; QSIMPLEQ_ENTRY(BlkActionState) entry; }; @@ -1873,7 +1874,7 @@ typedef struct DriveBackupState { BlockJob *job; } DriveBackupState; -static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, +static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, Error **errp); static void drive_backup_prepare(BlkActionState *common, Error **errp) @@ -1919,7 +1920,7 @@ static void drive_backup_commit(BlkActionState *common) aio_context_acquire(aio_context); assert(state->job); - block_job_start(state->job); + job_start(&state->job->job); aio_context_release(aio_context); } @@ -1934,7 +1935,7 @@ static void drive_backup_abort(BlkActionState *common) aio_context = bdrv_get_aio_context(state->bs); aio_context_acquire(aio_context); - block_job_cancel_sync(state->job); + job_cancel_sync(&state->job->job); aio_context_release(aio_context); } @@ -1963,7 +1964,7 @@ typedef struct BlockdevBackupState { BlockJob *job; } BlockdevBackupState; -static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, +static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, Error **errp); static void blockdev_backup_prepare(BlkActionState *common, Error **errp) @@ -1977,7 +1978,7 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp) assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); backup = common->action->u.blockdev_backup.data; - bs = qmp_get_root_bs(backup->device, errp); + bs = bdrv_lookup_bs(backup->device, backup->device, errp); if (!bs) { return; } @@ -2017,7 +2018,7 @@ static void blockdev_backup_commit(BlkActionState *common) aio_context_acquire(aio_context); assert(state->job); - block_job_start(state->job); + job_start(&state->job->job); aio_context_release(aio_context); } @@ -2032,7 +2033,7 @@ static void blockdev_backup_abort(BlkActionState *common) aio_context = bdrv_get_aio_context(state->bs); aio_context_acquire(aio_context); - block_job_cancel_sync(state->job); + job_cancel_sync(&state->job->job); aio_context_release(aio_context); } @@ -2061,6 +2062,7 @@ typedef struct BlockDirtyBitmapState { BlockDriverState *bs; HBitmap *backup; bool prepared; + bool was_enabled; } BlockDirtyBitmapState; static void block_dirty_bitmap_add_prepare(BlkActionState *common, @@ -2081,6 +2083,7 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common, action->has_granularity, action->granularity, action->has_persistent, action->persistent, action->has_autoload, action->autoload, + action->has_x_disabled, action->x_disabled, &local_err); if (!local_err) { @@ -2160,6 +2163,74 @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common) hbitmap_free(state->backup); } +static void block_dirty_bitmap_enable_prepare(BlkActionState *common, + Error **errp) +{ + BlockDirtyBitmap *action; + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (action_check_completion_mode(common, errp) < 0) { + return; + } + + action = common->action->u.x_block_dirty_bitmap_enable.data; + state->bitmap = block_dirty_bitmap_lookup(action->node, + action->name, + NULL, + errp); + if (!state->bitmap) { + return; + } + + state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); + bdrv_enable_dirty_bitmap(state->bitmap); +} + +static void block_dirty_bitmap_enable_abort(BlkActionState *common) +{ + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (!state->was_enabled) { + bdrv_disable_dirty_bitmap(state->bitmap); + } +} + +static void block_dirty_bitmap_disable_prepare(BlkActionState *common, + Error **errp) +{ + BlockDirtyBitmap *action; + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (action_check_completion_mode(common, errp) < 0) { + return; + } + + action = common->action->u.x_block_dirty_bitmap_disable.data; + state->bitmap = block_dirty_bitmap_lookup(action->node, + action->name, + NULL, + errp); + if (!state->bitmap) { + return; + } + + state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); + bdrv_disable_dirty_bitmap(state->bitmap); +} + +static void block_dirty_bitmap_disable_abort(BlkActionState *common) +{ + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (state->was_enabled) { + bdrv_enable_dirty_bitmap(state->bitmap); + } +} + static void abort_prepare(BlkActionState *common, Error **errp) { error_setg(errp, "Transaction aborted using Abort action"); @@ -2220,7 +2291,17 @@ static const BlkActionOps actions[] = { .prepare = block_dirty_bitmap_clear_prepare, .commit = block_dirty_bitmap_clear_commit, .abort = block_dirty_bitmap_clear_abort, - } + }, + [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_enable_prepare, + .abort = block_dirty_bitmap_enable_abort, + }, + [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_DISABLE] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_disable_prepare, + .abort = block_dirty_bitmap_disable_abort, + } }; /** @@ -2252,7 +2333,7 @@ void qmp_transaction(TransactionActionList *dev_list, Error **errp) { TransactionActionList *dev_entry = dev_list; - BlockJobTxn *block_job_txn = NULL; + JobTxn *block_job_txn = NULL; BlkActionState *state, *next; Error *local_err = NULL; @@ -2260,11 +2341,11 @@ void qmp_transaction(TransactionActionList *dev_list, QSIMPLEQ_INIT(&snap_bdrv_states); /* Does this transaction get canceled as a group on failure? - * If not, we don't really need to make a BlockJobTxn. + * If not, we don't really need to make a JobTxn. */ props = get_transaction_properties(props); if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { - block_job_txn = block_job_txn_new(); + block_job_txn = job_txn_new(); } /* drain all i/o before any operations */ @@ -2323,7 +2404,7 @@ exit: if (!has_props) { qapi_free_TransactionProperties(props); } - block_job_txn_unref(block_job_txn); + job_txn_unref(block_job_txn); } void qmp_eject(bool has_device, const char *device, @@ -2810,6 +2891,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, bool has_granularity, uint32_t granularity, bool has_persistent, bool persistent, bool has_autoload, bool autoload, + bool has_disabled, bool disabled, Error **errp) { BlockDriverState *bs; @@ -2844,6 +2926,10 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, warn_report("Autoload option is deprecated and its value is ignored"); } + if (!has_disabled) { + disabled = false; + } + if (persistent && !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) { @@ -2855,6 +2941,10 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, return; } + if (disabled) { + bdrv_disable_dirty_bitmap(bitmap); + } + bdrv_dirty_bitmap_set_persistance(bitmap, persistent); } @@ -2890,7 +2980,6 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, } } - bdrv_dirty_bitmap_make_anon(bitmap); bdrv_release_dirty_bitmap(bs, bitmap); } @@ -2932,6 +3021,78 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, bdrv_clear_dirty_bitmap(bitmap, NULL); } +void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap) { + return; + } + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently frozen and cannot be enabled", + name); + return; + } + + bdrv_enable_dirty_bitmap(bitmap); +} + +void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, + Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *bitmap; + + bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); + if (!bitmap) { + return; + } + + if (bdrv_dirty_bitmap_frozen(bitmap)) { + error_setg(errp, + "Bitmap '%s' is currently frozen and cannot be disabled", + name); + return; + } + + bdrv_disable_dirty_bitmap(bitmap); +} + +void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name, + const char *src_name, Error **errp) +{ + BlockDriverState *bs; + BdrvDirtyBitmap *dst, *src; + + dst = block_dirty_bitmap_lookup(node, dst_name, &bs, errp); + if (!dst) { + return; + } + + if (bdrv_dirty_bitmap_frozen(dst)) { + error_setg(errp, "Bitmap '%s' is frozen and cannot be modified", + dst_name); + return; + } else if (bdrv_dirty_bitmap_readonly(dst)) { + error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", + dst_name); + return; + } + + src = bdrv_find_dirty_bitmap(bs, src_name); + if (!src) { + error_setg(errp, "Dirty bitmap '%s' not found", src_name); + return; + } + + bdrv_merge_dirty_bitmap(dst, src, errp); +} + BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, const char *name, Error **errp) @@ -3253,7 +3414,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, goto out; } commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, - BLOCK_JOB_DEFAULT, speed, on_error, + JOB_DEFAULT, speed, on_error, filter_node_name, NULL, NULL, false, &local_err); } else { BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); @@ -3273,7 +3434,7 @@ out: aio_context_release(aio_context); } -static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, +static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, Error **errp) { BlockDriverState *bs; @@ -3284,7 +3445,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, AioContext *aio_context; QDict *options = NULL; Error *local_err = NULL; - int flags, job_flags = BLOCK_JOB_DEFAULT; + int flags, job_flags = JOB_DEFAULT; int64_t size; bool set_backing_hd = false; @@ -3407,10 +3568,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, } } if (!backup->auto_finalize) { - job_flags |= BLOCK_JOB_MANUAL_FINALIZE; + job_flags |= JOB_MANUAL_FINALIZE; } if (!backup->auto_dismiss) { - job_flags |= BLOCK_JOB_MANUAL_DISMISS; + job_flags |= JOB_MANUAL_DISMISS; } job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, @@ -3434,7 +3595,7 @@ void qmp_drive_backup(DriveBackup *arg, Error **errp) BlockJob *job; job = do_drive_backup(arg, NULL, errp); if (job) { - block_job_start(job); + job_start(&job->job); } } @@ -3443,7 +3604,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) return bdrv_named_nodes_list(errp); } -BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, +BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, Error **errp) { BlockDriverState *bs; @@ -3451,7 +3612,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, Error *local_err = NULL; AioContext *aio_context; BlockJob *job = NULL; - int job_flags = BLOCK_JOB_DEFAULT; + int job_flags = JOB_DEFAULT; if (!backup->has_speed) { backup->speed = 0; @@ -3475,7 +3636,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, backup->compress = false; } - bs = qmp_get_root_bs(backup->device, errp); + bs = bdrv_lookup_bs(backup->device, backup->device, errp); if (!bs) { return NULL; } @@ -3500,10 +3661,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, } } if (!backup->auto_finalize) { - job_flags |= BLOCK_JOB_MANUAL_FINALIZE; + job_flags |= JOB_MANUAL_FINALIZE; } if (!backup->auto_dismiss) { - job_flags |= BLOCK_JOB_MANUAL_DISMISS; + job_flags |= JOB_MANUAL_DISMISS; } job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, NULL, backup->compress, @@ -3522,7 +3683,7 @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp) BlockJob *job; job = do_blockdev_backup(arg, NULL, errp); if (job) { - block_job_start(job); + job_start(&job->job); } } @@ -3544,6 +3705,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, bool has_unmap, bool unmap, bool has_filter_node_name, const char *filter_node_name, + bool has_copy_mode, MirrorCopyMode copy_mode, Error **errp) { @@ -3568,6 +3730,9 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, if (!has_filter_node_name) { filter_node_name = NULL; } + if (!has_copy_mode) { + copy_mode = MIRROR_COPY_MODE_BACKGROUND; + } if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", @@ -3598,7 +3763,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, has_replaces ? replaces : NULL, speed, granularity, buf_size, sync, backing_mode, on_source_error, on_target_error, unmap, filter_node_name, - errp); + copy_mode, errp); } void qmp_drive_mirror(DriveMirror *arg, Error **errp) @@ -3744,6 +3909,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) arg->has_on_target_error, arg->on_target_error, arg->has_unmap, arg->unmap, false, NULL, + arg->has_copy_mode, arg->copy_mode, &local_err); bdrv_unref(target_bs); error_propagate(errp, local_err); @@ -3764,6 +3930,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, BlockdevOnError on_target_error, bool has_filter_node_name, const char *filter_node_name, + bool has_copy_mode, MirrorCopyMode copy_mode, Error **errp) { BlockDriverState *bs; @@ -3796,6 +3963,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, has_on_target_error, on_target_error, true, true, has_filter_node_name, filter_node_name, + has_copy_mode, copy_mode, &local_err); error_propagate(errp, local_err); @@ -3853,14 +4021,14 @@ void qmp_block_job_cancel(const char *device, force = false; } - if (block_job_user_paused(job) && !force) { + if (job_user_paused(&job->job) && !force) { error_setg(errp, "The block job for device '%s' is currently paused", device); goto out; } trace_qmp_block_job_cancel(job); - block_job_user_cancel(job, force, errp); + job_user_cancel(&job->job, force, errp); out: aio_context_release(aio_context); } @@ -3875,7 +4043,7 @@ void qmp_block_job_pause(const char *device, Error **errp) } trace_qmp_block_job_pause(job); - block_job_user_pause(job, errp); + job_user_pause(&job->job, errp); aio_context_release(aio_context); } @@ -3889,7 +4057,7 @@ void qmp_block_job_resume(const char *device, Error **errp) } trace_qmp_block_job_resume(job); - block_job_user_resume(job, errp); + job_user_resume(&job->job, errp); aio_context_release(aio_context); } @@ -3903,7 +4071,7 @@ void qmp_block_job_complete(const char *device, Error **errp) } trace_qmp_block_job_complete(job); - block_job_complete(job, errp); + job_complete(&job->job, errp); aio_context_release(aio_context); } @@ -3917,21 +4085,23 @@ void qmp_block_job_finalize(const char *id, Error **errp) } trace_qmp_block_job_finalize(job); - block_job_finalize(job, errp); + job_finalize(&job->job, errp); aio_context_release(aio_context); } void qmp_block_job_dismiss(const char *id, Error **errp) { AioContext *aio_context; - BlockJob *job = find_block_job(id, &aio_context, errp); + BlockJob *bjob = find_block_job(id, &aio_context, errp); + Job *job; - if (!job) { + if (!bjob) { return; } - trace_qmp_block_job_dismiss(job); - block_job_dismiss(&job, errp); + trace_qmp_block_job_dismiss(bjob); + job = &bjob->job; + job_dismiss(&job, errp); aio_context_release(aio_context); } @@ -4031,7 +4201,7 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr) qdict = qemu_opts_to_qdict(opts, NULL); if (!qdict_get_try_str(qdict, "node-name")) { - QDECREF(qdict); + qobject_unref(qdict); error_report("'node-name' needs to be specified"); goto out; } diff --git a/blockjob.c b/blockjob.c index 27f957e571..be5903aa96 100644 --- a/blockjob.c +++ b/blockjob.c @@ -34,103 +34,8 @@ #include "qapi/qapi-events-block-core.h" #include "qapi/qmp/qerror.h" #include "qemu/coroutine.h" -#include "qemu/id.h" #include "qemu/timer.h" -/* Right now, this mutex is only needed to synchronize accesses to job->busy - * and job->sleep_timer, such as concurrent calls to block_job_do_yield and - * block_job_enter. */ -static QemuMutex block_job_mutex; - -/* BlockJob State Transition Table */ -bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = { - /* U, C, R, P, Y, S, W, D, X, E, N */ - /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, - /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, - /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, - /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, - /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, - /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, - /* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, - /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, - /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -}; - -bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = { - /* U, C, R, P, Y, S, W, D, X, E, N */ - [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, - [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, - [BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, - [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, -}; - -static void block_job_state_transition(BlockJob *job, BlockJobStatus s1) -{ - BlockJobStatus s0 = job->status; - assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX); - trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ? - "allowed" : "disallowed", - BlockJobStatus_str(s0), - BlockJobStatus_str(s1)); - assert(BlockJobSTT[s0][s1]); - job->status = s1; -} - -static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp) -{ - assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX); - trace_block_job_apply_verb(job, BlockJobStatus_str(job->status), - BlockJobVerb_str(bv), - BlockJobVerbTable[bv][job->status] ? - "allowed" : "prohibited"); - if (BlockJobVerbTable[bv][job->status]) { - return 0; - } - error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", - job->id, BlockJobStatus_str(job->status), BlockJobVerb_str(bv)); - return -EPERM; -} - -static void block_job_lock(void) -{ - qemu_mutex_lock(&block_job_mutex); -} - -static void block_job_unlock(void) -{ - qemu_mutex_unlock(&block_job_mutex); -} - -static void __attribute__((__constructor__)) block_job_init(void) -{ - qemu_mutex_init(&block_job_mutex); -} - -static void block_job_event_cancelled(BlockJob *job); -static void block_job_event_completed(BlockJob *job, const char *msg); -static int block_job_event_pending(BlockJob *job); -static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)); - -/* Transactional group of block jobs */ -struct BlockJobTxn { - - /* Is this txn being cancelled? */ - bool aborting; - - /* List of jobs */ - QLIST_HEAD(, BlockJob) jobs; - - /* Reference count */ - int refcnt; -}; - -static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs); - /* * The block job API is composed of two categories of functions. * @@ -146,133 +51,78 @@ static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs); * blockjob_int.h. */ -BlockJob *block_job_next(BlockJob *job) +static bool is_block_job(Job *job) { - if (!job) { - return QLIST_FIRST(&block_jobs); - } - return QLIST_NEXT(job, job_list); + return job_type(job) == JOB_TYPE_BACKUP || + job_type(job) == JOB_TYPE_COMMIT || + job_type(job) == JOB_TYPE_MIRROR || + job_type(job) == JOB_TYPE_STREAM; +} + +BlockJob *block_job_next(BlockJob *bjob) +{ + Job *job = bjob ? &bjob->job : NULL; + + do { + job = job_next(job); + } while (job && !is_block_job(job)); + + return job ? container_of(job, BlockJob, job) : NULL; } BlockJob *block_job_get(const char *id) { - BlockJob *job; + Job *job = job_get(id); - QLIST_FOREACH(job, &block_jobs, job_list) { - if (job->id && !strcmp(id, job->id)) { - return job; - } + if (job && is_block_job(job)) { + return container_of(job, BlockJob, job); + } else { + return NULL; } - - return NULL; -} - -BlockJobTxn *block_job_txn_new(void) -{ - BlockJobTxn *txn = g_new0(BlockJobTxn, 1); - QLIST_INIT(&txn->jobs); - txn->refcnt = 1; - return txn; -} - -static void block_job_txn_ref(BlockJobTxn *txn) -{ - txn->refcnt++; -} - -void block_job_txn_unref(BlockJobTxn *txn) -{ - if (txn && --txn->refcnt == 0) { - g_free(txn); - } -} - -void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job) -{ - if (!txn) { - return; - } - - assert(!job->txn); - job->txn = txn; - - QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); - block_job_txn_ref(txn); -} - -static void block_job_txn_del_job(BlockJob *job) -{ - if (job->txn) { - QLIST_REMOVE(job, txn_list); - block_job_txn_unref(job->txn); - job->txn = NULL; - } -} - -static void block_job_pause(BlockJob *job) -{ - job->pause_count++; -} - -static void block_job_resume(BlockJob *job) -{ - assert(job->pause_count > 0); - job->pause_count--; - if (job->pause_count) { - return; - } - block_job_enter(job); -} - -void block_job_ref(BlockJob *job) -{ - ++job->refcnt; } static void block_job_attached_aio_context(AioContext *new_context, void *opaque); static void block_job_detach_aio_context(void *opaque); -void block_job_unref(BlockJob *job) +void block_job_free(Job *job) { - if (--job->refcnt == 0) { - assert(job->status == BLOCK_JOB_STATUS_NULL); - assert(!job->txn); - BlockDriverState *bs = blk_bs(job->blk); - QLIST_REMOVE(job, job_list); - bs->job = NULL; - block_job_remove_all_bdrv(job); - blk_remove_aio_context_notifier(job->blk, - block_job_attached_aio_context, - block_job_detach_aio_context, job); - blk_unref(job->blk); - error_free(job->blocker); - g_free(job->id); - assert(!timer_pending(&job->sleep_timer)); - g_free(job); - } + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); + + bs->job = NULL; + block_job_remove_all_bdrv(bjob); + blk_remove_aio_context_notifier(bjob->blk, + block_job_attached_aio_context, + block_job_detach_aio_context, bjob); + blk_unref(bjob->blk); + error_free(bjob->blocker); } static void block_job_attached_aio_context(AioContext *new_context, void *opaque) { BlockJob *job = opaque; + const JobDriver *drv = job->job.driver; + BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver); - if (job->driver->attached_aio_context) { - job->driver->attached_aio_context(job, new_context); + job->job.aio_context = new_context; + if (bjdrv->attached_aio_context) { + bjdrv->attached_aio_context(job, new_context); } - block_job_resume(job); + job_resume(&job->job); } -static void block_job_drain(BlockJob *job) +void block_job_drain(Job *job) { - /* If job is !job->busy this kicks it into the next pause point. */ - block_job_enter(job); + BlockJob *bjob = container_of(job, BlockJob, job); + const JobDriver *drv = job->driver; + BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver); - blk_drain(job->blk); - if (job->driver->drain) { - job->driver->drain(job); + blk_drain(bjob->blk); + if (bjdrv->drain) { + bjdrv->drain(bjob); } } @@ -281,40 +131,62 @@ static void block_job_detach_aio_context(void *opaque) BlockJob *job = opaque; /* In case the job terminates during aio_poll()... */ - block_job_ref(job); + job_ref(&job->job); - block_job_pause(job); + job_pause(&job->job); - while (!job->paused && !job->completed) { - block_job_drain(job); + while (!job->job.paused && !job_is_completed(&job->job)) { + job_drain(&job->job); } - block_job_unref(job); + job->job.aio_context = NULL; + job_unref(&job->job); } static char *child_job_get_parent_desc(BdrvChild *c) { BlockJob *job = c->opaque; - return g_strdup_printf("%s job '%s'", - BlockJobType_str(job->driver->job_type), - job->id); + return g_strdup_printf("%s job '%s'", job_type_str(&job->job), job->job.id); } static void child_job_drained_begin(BdrvChild *c) { BlockJob *job = c->opaque; - block_job_pause(job); + job_pause(&job->job); +} + +static bool child_job_drained_poll(BdrvChild *c) +{ + BlockJob *bjob = c->opaque; + Job *job = &bjob->job; + const BlockJobDriver *drv = block_job_driver(bjob); + + /* An inactive or completed job doesn't have any pending requests. Jobs + * with !job->busy are either already paused or have a pause point after + * being reentered, so no job driver code will run before they pause. */ + if (!job->busy || job_is_completed(job) || job->deferred_to_main_loop) { + return false; + } + + /* Otherwise, assume that it isn't fully stopped yet, but allow the job to + * override this assumption. */ + if (drv->drained_poll) { + return drv->drained_poll(bjob); + } else { + return true; + } } static void child_job_drained_end(BdrvChild *c) { BlockJob *job = c->opaque; - block_job_resume(job); + job_resume(&job->job); } static const BdrvChildRole child_job = { .get_parent_desc = child_job_get_parent_desc, .drained_begin = child_job_drained_begin, + .drained_poll = child_job_drained_poll, .drained_end = child_job_drained_end, .stay_at_node = true, }; @@ -351,463 +223,50 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, bool block_job_is_internal(BlockJob *job) { - return (job->id == NULL); + return (job->job.id == NULL); } -static bool block_job_started(BlockJob *job) +const BlockJobDriver *block_job_driver(BlockJob *job) { - return job->co; + return container_of(job->job.driver, BlockJobDriver, job_driver); } -/** - * All jobs must allow a pause point before entering their job proper. This - * ensures that jobs can be paused prior to being started, then resumed later. - */ -static void coroutine_fn block_job_co_entry(void *opaque) -{ - BlockJob *job = opaque; - - assert(job && job->driver && job->driver->start); - block_job_pause_point(job); - job->driver->start(job); -} - -static void block_job_sleep_timer_cb(void *opaque) -{ - BlockJob *job = opaque; - - block_job_enter(job); -} - -void block_job_start(BlockJob *job) -{ - assert(job && !block_job_started(job) && job->paused && - job->driver && job->driver->start); - job->co = qemu_coroutine_create(block_job_co_entry, job); - job->pause_count--; - job->busy = true; - job->paused = false; - block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING); - bdrv_coroutine_enter(blk_bs(job->blk), job->co); -} - -static void block_job_decommission(BlockJob *job) -{ - assert(job); - job->completed = true; - job->busy = false; - job->paused = false; - job->deferred_to_main_loop = true; - block_job_txn_del_job(job); - block_job_state_transition(job, BLOCK_JOB_STATUS_NULL); - block_job_unref(job); -} - -static void block_job_do_dismiss(BlockJob *job) -{ - block_job_decommission(job); -} - -static void block_job_conclude(BlockJob *job) -{ - block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED); - if (job->auto_dismiss || !block_job_started(job)) { - block_job_do_dismiss(job); - } -} - -static void block_job_update_rc(BlockJob *job) -{ - if (!job->ret && block_job_is_cancelled(job)) { - job->ret = -ECANCELED; - } - if (job->ret) { - block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING); - } -} - -static int block_job_prepare(BlockJob *job) -{ - if (job->ret == 0 && job->driver->prepare) { - job->ret = job->driver->prepare(job); - } - return job->ret; -} - -static void block_job_commit(BlockJob *job) -{ - assert(!job->ret); - if (job->driver->commit) { - job->driver->commit(job); - } -} - -static void block_job_abort(BlockJob *job) -{ - assert(job->ret); - if (job->driver->abort) { - job->driver->abort(job); - } -} - -static void block_job_clean(BlockJob *job) -{ - if (job->driver->clean) { - job->driver->clean(job); - } -} - -static int block_job_finalize_single(BlockJob *job) -{ - assert(job->completed); - - /* Ensure abort is called for late-transactional failures */ - block_job_update_rc(job); - - if (!job->ret) { - block_job_commit(job); - } else { - block_job_abort(job); - } - block_job_clean(job); - - if (job->cb) { - job->cb(job->opaque, job->ret); - } - - /* Emit events only if we actually started */ - if (block_job_started(job)) { - if (block_job_is_cancelled(job)) { - block_job_event_cancelled(job); - } else { - const char *msg = NULL; - if (job->ret < 0) { - msg = strerror(-job->ret); - } - block_job_event_completed(job, msg); - } - } - - block_job_txn_del_job(job); - block_job_conclude(job); - return 0; -} - -static void block_job_cancel_async(BlockJob *job, bool force) -{ - if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) { - block_job_iostatus_reset(job); - } - if (job->user_paused) { - /* Do not call block_job_enter here, the caller will handle it. */ - job->user_paused = false; - job->pause_count--; - } - job->cancelled = true; - /* To prevent 'force == false' overriding a previous 'force == true' */ - job->force |= force; -} - -static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock) -{ - AioContext *ctx; - BlockJob *job, *next; - int rc = 0; - - QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { - if (lock) { - ctx = blk_get_aio_context(job->blk); - aio_context_acquire(ctx); - } - rc = fn(job); - if (lock) { - aio_context_release(ctx); - } - if (rc) { - break; - } - } - return rc; -} - -static int block_job_finish_sync(BlockJob *job, - void (*finish)(BlockJob *, Error **errp), - Error **errp) -{ - Error *local_err = NULL; - int ret; - - assert(blk_bs(job->blk)->job == job); - - block_job_ref(job); - - if (finish) { - finish(job, &local_err); - } - if (local_err) { - error_propagate(errp, local_err); - block_job_unref(job); - return -EBUSY; - } - /* block_job_drain calls block_job_enter, and it should be enough to - * induce progress until the job completes or moves to the main thread. - */ - while (!job->deferred_to_main_loop && !job->completed) { - block_job_drain(job); - } - while (!job->completed) { - aio_poll(qemu_get_aio_context(), true); - } - ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret; - block_job_unref(job); - return ret; -} - -static void block_job_completed_txn_abort(BlockJob *job) -{ - AioContext *ctx; - BlockJobTxn *txn = job->txn; - BlockJob *other_job; - - if (txn->aborting) { - /* - * We are cancelled by another job, which will handle everything. - */ - return; - } - txn->aborting = true; - block_job_txn_ref(txn); - - /* We are the first failed job. Cancel other jobs. */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - ctx = blk_get_aio_context(other_job->blk); - aio_context_acquire(ctx); - } - - /* Other jobs are effectively cancelled by us, set the status for - * them; this job, however, may or may not be cancelled, depending - * on the caller, so leave it. */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (other_job != job) { - block_job_cancel_async(other_job, false); - } - } - while (!QLIST_EMPTY(&txn->jobs)) { - other_job = QLIST_FIRST(&txn->jobs); - ctx = blk_get_aio_context(other_job->blk); - if (!other_job->completed) { - assert(other_job->cancelled); - block_job_finish_sync(other_job, NULL, NULL); - } - block_job_finalize_single(other_job); - aio_context_release(ctx); - } - - block_job_txn_unref(txn); -} - -static int block_job_needs_finalize(BlockJob *job) -{ - return !job->auto_finalize; -} - -static void block_job_do_finalize(BlockJob *job) -{ - int rc; - assert(job && job->txn); - - /* prepare the transaction to complete */ - rc = block_job_txn_apply(job->txn, block_job_prepare, true); - if (rc) { - block_job_completed_txn_abort(job); - } else { - block_job_txn_apply(job->txn, block_job_finalize_single, true); - } -} - -static void block_job_completed_txn_success(BlockJob *job) -{ - BlockJobTxn *txn = job->txn; - BlockJob *other_job; - - block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING); - - /* - * Successful completion, see if there are other running jobs in this - * txn. - */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - if (!other_job->completed) { - return; - } - assert(other_job->ret == 0); - } - - block_job_txn_apply(txn, block_job_event_pending, false); - - /* If no jobs need manual finalization, automatically do so */ - if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) { - block_job_do_finalize(job); - } -} - -/* Assumes the block_job_mutex is held */ -static bool block_job_timer_pending(BlockJob *job) +/* Assumes the job_mutex is held */ +static bool job_timer_pending(Job *job) { return timer_pending(&job->sleep_timer); } void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) { - Error *local_err = NULL; int64_t old_speed = job->speed; - if (!job->driver->set_speed) { - error_setg(errp, QERR_UNSUPPORTED); + if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) { return; } - if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) { - return; - } - job->driver->set_speed(job, speed, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (speed < 0) { + error_setg(errp, QERR_INVALID_PARAMETER, "speed"); return; } + ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME); + job->speed = speed; if (speed && speed <= old_speed) { return; } /* kick only if a timer is pending */ - block_job_enter_cond(job, block_job_timer_pending); + job_enter_cond(&job->job, job_timer_pending); } -void block_job_complete(BlockJob *job, Error **errp) +int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n) { - /* Should not be reachable via external interface for internal jobs */ - assert(job->id); - if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) { - return; - } - if (job->pause_count || job->cancelled || !job->driver->complete) { - error_setg(errp, "The active block job '%s' cannot be completed", - job->id); - return; + if (!job->speed) { + return 0; } - job->driver->complete(job, errp); -} - -void block_job_finalize(BlockJob *job, Error **errp) -{ - assert(job && job->id && job->txn); - if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) { - return; - } - block_job_do_finalize(job); -} - -void block_job_dismiss(BlockJob **jobptr, Error **errp) -{ - BlockJob *job = *jobptr; - /* similarly to _complete, this is QMP-interface only. */ - assert(job->id); - if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) { - return; - } - - block_job_do_dismiss(job); - *jobptr = NULL; -} - -void block_job_user_pause(BlockJob *job, Error **errp) -{ - if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) { - return; - } - if (job->user_paused) { - error_setg(errp, "Job is already paused"); - return; - } - job->user_paused = true; - block_job_pause(job); -} - -bool block_job_user_paused(BlockJob *job) -{ - return job->user_paused; -} - -void block_job_user_resume(BlockJob *job, Error **errp) -{ - assert(job); - if (!job->user_paused || job->pause_count <= 0) { - error_setg(errp, "Can't resume a job that was not paused"); - return; - } - if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) { - return; - } - block_job_iostatus_reset(job); - job->user_paused = false; - block_job_resume(job); -} - -void block_job_cancel(BlockJob *job, bool force) -{ - if (job->status == BLOCK_JOB_STATUS_CONCLUDED) { - block_job_do_dismiss(job); - return; - } - block_job_cancel_async(job, force); - if (!block_job_started(job)) { - block_job_completed(job, -ECANCELED); - } else if (job->deferred_to_main_loop) { - block_job_completed_txn_abort(job); - } else { - block_job_enter(job); - } -} - -void block_job_user_cancel(BlockJob *job, bool force, Error **errp) -{ - if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) { - return; - } - block_job_cancel(job, force); -} - -/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be - * used with block_job_finish_sync() without the need for (rather nasty) - * function pointer casts there. */ -static void block_job_cancel_err(BlockJob *job, Error **errp) -{ - block_job_cancel(job, false); -} - -int block_job_cancel_sync(BlockJob *job) -{ - return block_job_finish_sync(job, &block_job_cancel_err, NULL); -} - -void block_job_cancel_sync_all(void) -{ - BlockJob *job; - AioContext *aio_context; - - while ((job = QLIST_FIRST(&block_jobs))) { - aio_context = blk_get_aio_context(job->blk); - aio_context_acquire(aio_context); - block_job_cancel_sync(job); - aio_context_release(aio_context); - } -} - -int block_job_complete_sync(BlockJob *job, Error **errp) -{ - return block_job_finish_sync(job, &block_job_complete, errp); + return ratelimit_calculate_delay(&job->limit, n); } BlockJobInfo *block_job_query(BlockJob *job, Error **errp) @@ -819,18 +278,20 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp) return NULL; } info = g_new0(BlockJobInfo, 1); - info->type = g_strdup(BlockJobType_str(job->driver->job_type)); - info->device = g_strdup(job->id); - info->len = job->len; - info->busy = atomic_read(&job->busy); - info->paused = job->pause_count > 0; - info->offset = job->offset; + info->type = g_strdup(job_type_str(&job->job)); + info->device = g_strdup(job->job.id); + info->busy = atomic_read(&job->job.busy); + info->paused = job->job.pause_count > 0; + info->offset = job->job.progress_current; + info->len = job->job.progress_total; info->speed = job->speed; info->io_status = job->iostatus; - info->ready = job->ready; - info->status = job->status; - info->auto_finalize = job->auto_finalize; - info->auto_dismiss = job->auto_dismiss; + info->ready = job_is_ready(&job->job), + info->status = job->job.status; + info->auto_finalize = job->job.auto_finalize; + info->auto_dismiss = job->job.auto_dismiss; + info->has_error = job->job.ret != 0; + info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL; return info; } @@ -842,54 +303,81 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) } } -static void block_job_event_cancelled(BlockJob *job) +static void block_job_event_cancelled(Notifier *n, void *opaque) { + BlockJob *job = opaque; + if (block_job_is_internal(job)) { return; } - qapi_event_send_block_job_cancelled(job->driver->job_type, - job->id, - job->len, - job->offset, + qapi_event_send_block_job_cancelled(job_type(&job->job), + job->job.id, + job->job.progress_total, + job->job.progress_current, job->speed, &error_abort); } -static void block_job_event_completed(BlockJob *job, const char *msg) +static void block_job_event_completed(Notifier *n, void *opaque) { + BlockJob *job = opaque; + const char *msg = NULL; + if (block_job_is_internal(job)) { return; } - qapi_event_send_block_job_completed(job->driver->job_type, - job->id, - job->len, - job->offset, + if (job->job.ret < 0) { + msg = strerror(-job->job.ret); + } + + qapi_event_send_block_job_completed(job_type(&job->job), + job->job.id, + job->job.progress_total, + job->job.progress_current, job->speed, !!msg, msg, &error_abort); } -static int block_job_event_pending(BlockJob *job) +static void block_job_event_pending(Notifier *n, void *opaque) { - block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING); - if (!job->auto_finalize && !block_job_is_internal(job)) { - qapi_event_send_block_job_pending(job->driver->job_type, - job->id, - &error_abort); + BlockJob *job = opaque; + + if (block_job_is_internal(job)) { + return; } - return 0; + + qapi_event_send_block_job_pending(job_type(&job->job), + job->job.id, + &error_abort); } +static void block_job_event_ready(Notifier *n, void *opaque) +{ + BlockJob *job = opaque; + + if (block_job_is_internal(job)) { + return; + } + + qapi_event_send_block_job_ready(job_type(&job->job), + job->job.id, + job->job.progress_total, + job->job.progress_current, + job->speed, &error_abort); +} + + /* * API for block job drivers and the block layer. These functions are * declared in blockjob_int.h. */ void *block_job_create(const char *job_id, const BlockJobDriver *driver, - BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm, + JobTxn *txn, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, int64_t speed, int flags, BlockCompletionFunc *cb, void *opaque, Error **errp) { @@ -902,29 +390,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } - if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) { + if (job_id == NULL && !(flags & JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); - if (!*job_id) { - error_setg(errp, "An explicit job ID is required for this node"); - return NULL; - } - } - - if (job_id) { - if (flags & BLOCK_JOB_INTERNAL) { - error_setg(errp, "Cannot specify job ID for internal block job"); - return NULL; - } - - if (!id_wellformed(job_id)) { - error_setg(errp, "Invalid job ID '%s'", job_id); - return NULL; - } - - if (block_job_get(job_id)) { - error_setg(errp, "Job ID '%s' already in use", job_id); - return NULL; - } } blk = blk_new(perm, shared_perm); @@ -934,32 +401,39 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, return NULL; } - job = g_malloc0(driver->instance_size); - job->driver = driver; - job->id = g_strdup(job_id); - job->blk = blk; - job->cb = cb; - job->opaque = opaque; - job->busy = false; - job->paused = true; - job->pause_count = 1; - job->refcnt = 1; - job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE); - job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS); - block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED); - aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, - QEMU_CLOCK_REALTIME, SCALE_NS, - block_job_sleep_timer_cb, job); + job = job_create(job_id, &driver->job_driver, txn, blk_get_aio_context(blk), + flags, cb, opaque, errp); + if (job == NULL) { + blk_unref(blk); + return NULL; + } + + assert(is_block_job(&job->job)); + assert(job->job.driver->free == &block_job_free); + assert(job->job.driver->user_resume == &block_job_user_resume); + assert(job->job.driver->drain == &block_job_drain); + + job->blk = blk; + + job->finalize_cancelled_notifier.notify = block_job_event_cancelled; + job->finalize_completed_notifier.notify = block_job_event_completed; + job->pending_notifier.notify = block_job_event_pending; + job->ready_notifier.notify = block_job_event_ready; + + notifier_list_add(&job->job.on_finalize_cancelled, + &job->finalize_cancelled_notifier); + notifier_list_add(&job->job.on_finalize_completed, + &job->finalize_completed_notifier); + notifier_list_add(&job->job.on_pending, &job->pending_notifier); + notifier_list_add(&job->job.on_ready, &job->ready_notifier); error_setg(&job->blocker, "block device is in use by block job: %s", - BlockJobType_str(driver->job_type)); + job_type_str(&job->job)); block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort); bs->job = job; bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); - QLIST_INSERT_HEAD(&block_jobs, job, job_list); - blk_add_aio_context_notifier(blk, block_job_attached_aio_context, block_job_detach_aio_context, job); @@ -969,225 +443,28 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, block_job_set_speed(job, speed, &local_err); if (local_err) { - block_job_early_fail(job); + job_early_fail(&job->job); error_propagate(errp, local_err); return NULL; } } - /* Single jobs are modeled as single-job transactions for sake of - * consolidating the job management logic */ - if (!txn) { - txn = block_job_txn_new(); - block_job_txn_add_job(txn, job); - block_job_txn_unref(txn); - } else { - block_job_txn_add_job(txn, job); - } - return job; } -void block_job_pause_all(void) -{ - BlockJob *job = NULL; - while ((job = block_job_next(job))) { - AioContext *aio_context = blk_get_aio_context(job->blk); - - aio_context_acquire(aio_context); - block_job_ref(job); - block_job_pause(job); - aio_context_release(aio_context); - } -} - -void block_job_early_fail(BlockJob *job) -{ - assert(job->status == BLOCK_JOB_STATUS_CREATED); - block_job_decommission(job); -} - -void block_job_completed(BlockJob *job, int ret) -{ - assert(job && job->txn && !job->completed); - assert(blk_bs(job->blk)->job == job); - job->completed = true; - job->ret = ret; - block_job_update_rc(job); - trace_block_job_completed(job, ret, job->ret); - if (job->ret) { - block_job_completed_txn_abort(job); - } else { - block_job_completed_txn_success(job); - } -} - -static bool block_job_should_pause(BlockJob *job) -{ - return job->pause_count > 0; -} - -/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. - * Reentering the job coroutine with block_job_enter() before the timer has - * expired is allowed and cancels the timer. - * - * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be - * called explicitly. */ -static void block_job_do_yield(BlockJob *job, uint64_t ns) -{ - block_job_lock(); - if (ns != -1) { - timer_mod(&job->sleep_timer, ns); - } - job->busy = false; - block_job_unlock(); - qemu_coroutine_yield(); - - /* Set by block_job_enter before re-entering the coroutine. */ - assert(job->busy); -} - -void coroutine_fn block_job_pause_point(BlockJob *job) -{ - assert(job && block_job_started(job)); - - if (!block_job_should_pause(job)) { - return; - } - if (block_job_is_cancelled(job)) { - return; - } - - if (job->driver->pause) { - job->driver->pause(job); - } - - if (block_job_should_pause(job) && !block_job_is_cancelled(job)) { - BlockJobStatus status = job->status; - block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \ - BLOCK_JOB_STATUS_STANDBY : \ - BLOCK_JOB_STATUS_PAUSED); - job->paused = true; - block_job_do_yield(job, -1); - job->paused = false; - block_job_state_transition(job, status); - } - - if (job->driver->resume) { - job->driver->resume(job); - } -} - -void block_job_resume_all(void) -{ - BlockJob *job, *next; - - QLIST_FOREACH_SAFE(job, &block_jobs, job_list, next) { - AioContext *aio_context = blk_get_aio_context(job->blk); - - aio_context_acquire(aio_context); - block_job_resume(job); - block_job_unref(job); - aio_context_release(aio_context); - } -} - -/* - * Conditionally enter a block_job pending a call to fn() while - * under the block_job_lock critical section. - */ -static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job)) -{ - if (!block_job_started(job)) { - return; - } - if (job->deferred_to_main_loop) { - return; - } - - block_job_lock(); - if (job->busy) { - block_job_unlock(); - return; - } - - if (fn && !fn(job)) { - block_job_unlock(); - return; - } - - assert(!job->deferred_to_main_loop); - timer_del(&job->sleep_timer); - job->busy = true; - block_job_unlock(); - aio_co_wake(job->co); -} - -void block_job_enter(BlockJob *job) -{ - block_job_enter_cond(job, NULL); -} - -bool block_job_is_cancelled(BlockJob *job) -{ - return job->cancelled; -} - -void block_job_sleep_ns(BlockJob *job, int64_t ns) -{ - assert(job->busy); - - /* Check cancellation *before* setting busy = false, too! */ - if (block_job_is_cancelled(job)) { - return; - } - - if (!block_job_should_pause(job)) { - block_job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); - } - - block_job_pause_point(job); -} - -void block_job_yield(BlockJob *job) -{ - assert(job->busy); - - /* Check cancellation *before* setting busy = false, too! */ - if (block_job_is_cancelled(job)) { - return; - } - - if (!block_job_should_pause(job)) { - block_job_do_yield(job, -1); - } - - block_job_pause_point(job); -} - void block_job_iostatus_reset(BlockJob *job) { if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { return; } - assert(job->user_paused && job->pause_count > 0); + assert(job->job.user_paused && job->job.pause_count > 0); job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; } -void block_job_event_ready(BlockJob *job) +void block_job_user_resume(Job *job) { - block_job_state_transition(job, BLOCK_JOB_STATUS_READY); - job->ready = true; - - if (block_job_is_internal(job)) { - return; - } - - qapi_event_send_block_job_ready(job->driver->job_type, - job->id, - job->len, - job->offset, - job->speed, &error_abort); + BlockJob *bjob = container_of(job, BlockJob, job); + block_job_iostatus_reset(bjob); } BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, @@ -1214,63 +491,16 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, abort(); } if (!block_job_is_internal(job)) { - qapi_event_send_block_job_error(job->id, + qapi_event_send_block_job_error(job->job.id, is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE, action, &error_abort); } if (action == BLOCK_ERROR_ACTION_STOP) { - block_job_pause(job); + job_pause(&job->job); /* make the pause user visible, which will be resumed from QMP. */ - job->user_paused = true; + job->job.user_paused = true; block_job_iostatus_set_err(job, error); } return action; } - -typedef struct { - BlockJob *job; - AioContext *aio_context; - BlockJobDeferToMainLoopFn *fn; - void *opaque; -} BlockJobDeferToMainLoopData; - -static void block_job_defer_to_main_loop_bh(void *opaque) -{ - BlockJobDeferToMainLoopData *data = opaque; - AioContext *aio_context; - - /* Prevent race with block_job_defer_to_main_loop() */ - aio_context_acquire(data->aio_context); - - /* Fetch BDS AioContext again, in case it has changed */ - aio_context = blk_get_aio_context(data->job->blk); - if (aio_context != data->aio_context) { - aio_context_acquire(aio_context); - } - - data->fn(data->job, data->opaque); - - if (aio_context != data->aio_context) { - aio_context_release(aio_context); - } - - aio_context_release(data->aio_context); - - g_free(data); -} - -void block_job_defer_to_main_loop(BlockJob *job, - BlockJobDeferToMainLoopFn *fn, - void *opaque) -{ - BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data)); - data->job = job; - data->aio_context = blk_get_aio_context(job->blk); - data->fn = fn; - data->opaque = opaque; - job->deferred_to_main_loop = true; - - aio_bh_schedule_oneshot(qemu_get_aio_context(), - block_job_defer_to_main_loop_bh, data); -} diff --git a/bsd-user/main.c b/bsd-user/main.c index 283dc6fd25..0d3156974c 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-version.h" #include @@ -795,9 +796,9 @@ int main(int argc, char **argv) if (x86_stack_size <= 0) usage(); if (*r == 'M') - x86_stack_size *= 1024 * 1024; + x86_stack_size *= MiB; else if (*r == 'k' || *r == 'K') - x86_stack_size *= 1024; + x86_stack_size *= KiB; } else if (!strcmp(r, "L")) { interp_prefix = argv[optind++]; } else if (!strcmp(r, "p")) { @@ -898,9 +899,10 @@ int main(int argc, char **argv) cpu_model = "any"; #endif } + + /* init tcg before creating CPUs and to get qemu_host_page_size */ tcg_exec_init(0); - /* NOTE: we need to init the CPU at this stage to get - qemu_host_page_size */ + cpu_type = parse_cpu_model(cpu_model); cpu = cpu_create(cpu_type); env = cpu->env_ptr; @@ -917,7 +919,7 @@ int main(int argc, char **argv) envlist_free(envlist); /* - * Now that page sizes are configured in cpu_init() we can do + * Now that page sizes are configured in tcg_exec_init() we can do * proper page alignment for guest_base. */ guest_base = HOST_PAGE_ALIGN(guest_base); diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 20cd29d145..17f4cd80aa 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -21,6 +21,7 @@ #include "qemu.h" #include "qemu-common.h" #include "bsd-mman.h" +#include "exec/exec-all.h" //#define DEBUG_MMAP diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 19b2b8fecb..09e8aed9c7 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -19,7 +19,6 @@ #include "cpu.h" -#include "exec/exec-all.h" #include "exec/cpu_ldst.h" #undef DEBUG_REMAP diff --git a/chardev/char-mux.c b/chardev/char-mux.c index 1b925c8dec..6055e76293 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -304,6 +304,7 @@ void mux_set_focus(Chardev *chr, int focus) } d->focus = focus; + chr->be = d->backends[focus]; mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); } diff --git a/chardev/char-serial.c b/chardev/char-serial.c index feb52e559d..3299b46853 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -139,7 +139,7 @@ static void tty_serial_init(int fd, int speed, tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - tty.c_oflag |= OPOST; + tty.c_oflag &= ~OPOST; tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB); switch (data_bits) { @@ -265,7 +265,8 @@ static void qmp_chardev_open_serial(Chardev *chr, ChardevHostdev *serial = backend->u.serial.data; int fd; - fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); + fd = qmp_chardev_open_file_source(serial->device, O_RDWR | O_NONBLOCK, + errp); if (fd < 0) { return; } diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 159e69c3b1..efbad6ee7c 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -134,8 +134,11 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) s->write_msgfds, s->write_msgfds_num); - /* free the written msgfds, no matter what */ - if (s->write_msgfds_num) { + /* free the written msgfds in any cases + * other than ret < 0 && errno == EAGAIN + */ + if (!(ret < 0 && EAGAIN == errno) + && s->write_msgfds_num) { g_free(s->write_msgfds); s->write_msgfds = 0; s->write_msgfds_num = 0; diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index 96375f2ab8..9624220e6d 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -46,8 +46,10 @@ static bool stdio_echo_state; static void term_exit(void) { - tcsetattr(0, TCSANOW, &oldtty); - fcntl(0, F_SETFL, old_fd0_flags); + if (stdio_in_use) { + tcsetattr(0, TCSANOW, &oldtty); + fcntl(0, F_SETFL, old_fd0_flags); + } } static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo) diff --git a/configure b/configure index 09d8b31c78..55d33b77ec 100755 --- a/configure +++ b/configure @@ -60,6 +60,11 @@ do_compiler() { # is compiler binary to execute. local compiler="$1" shift + if test -n "$BASH_VERSION"; then eval ' + echo >>config.log " +funcs: ${FUNCNAME[*]} +lines: ${BASH_LINENO[*]}" + '; fi echo $compiler "$@" >> config.log $compiler "$@" >> config.log 2>&1 || return $? # Test passed. If this is an --enable-werror build, rerun @@ -284,7 +289,6 @@ libs_softmmu="" libs_tools="" audio_pt_int="" audio_win_int="" -cc_i386=i386-pc-linux-gnu-gcc libs_qga="" debug_info="yes" stack_protector="" @@ -296,6 +300,24 @@ then else git_update=no git_submodules="" + + if ! test -f "$source_path/ui/keycodemapdb/README" + then + echo + echo "ERROR: missing file $source_path/ui/keycodemapdb/README" + echo + echo "This is not a GIT checkout but module content appears to" + echo "be missing. Do not use 'git archive' or GitHub download links" + echo "to acquire QEMU source archives. Non-GIT builds are only" + echo "supported with source archives linked from:" + echo + echo " https://www.qemu.org/download/" + echo + echo "Developers working with GIT can use scripts/archive-source.sh" + echo "if they need to create valid source archives." + echo + exit 1 + fi fi git="git" @@ -451,6 +473,21 @@ jemalloc="no" replication="yes" vxhs="" libxml2="" +docker="no" +debug_mutex="no" + +# cross compilers defaults, can be overridden with --cross-cc-ARCH +cross_cc_aarch64="aarch64-linux-gnu-gcc" +cross_cc_aarch64_be="$cross_cc_aarch64" +cross_cc_cflags_aarch64_be="-mbig-endian" +cross_cc_arm="arm-linux-gnueabihf-gcc" +cross_cc_cflags_armeb="-mbig-endian" +cross_cc_i386="i386-pc-linux-gnu-gcc" +cross_cc_cflags_i386="" +cross_cc_powerpc="powerpc-linux-gnu-gcc" +cross_cc_powerpc="powerpc-linux-gnu-gcc" + +enabled_cross_compilers="" supported_cpu="no" supported_os="no" @@ -482,6 +519,14 @@ for opt do ;; --disable-debug-info) debug_info="no" ;; + --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%%=*} + eval "cross_cc_cflags_${cc_arch}=\$optarg" + ;; + --cross-cc-*) cc_arch=${opt#--cross-cc-}; cc_arch=${cc_arch%%=*} + eval "cross_cc_${cc_arch}=\$optarg" + ;; esac done # OS specific @@ -670,30 +715,37 @@ case "$cpu" in ppc|ppc64|s390|s390x|sparc64|x32) cpu="$cpu" supported_cpu="yes" + eval "cross_cc_${cpu}=\$host_cc" ;; i386|i486|i586|i686|i86pc|BePC) cpu="i386" supported_cpu="yes" + cross_cc_i386=$host_cc ;; x86_64|amd64) cpu="x86_64" supported_cpu="yes" + cross_cc_x86_64=$host_cc ;; armv*b|armv*l|arm) cpu="arm" supported_cpu="yes" + cross_cc_arm=$host_cc ;; aarch64) cpu="aarch64" supported_cpu="yes" + cross_cc_aarch64=$host_cc ;; mips*) cpu="mips" supported_cpu="yes" + cross_cc_mips=$host_cc ;; sparc|sun4[cdmuv]) cpu="sparc" supported_cpu="yes" + cross_cc_sparc=$host_cc ;; *) # This will result in either an error or falling back to TCI later @@ -911,6 +963,8 @@ for opt do ;; --disable-debug-info) ;; + --cross-cc-*) + ;; --enable-modules) modules="yes" ;; @@ -959,6 +1013,8 @@ for opt do ;; --firmwarepath=*) firmwarepath="$optarg" ;; + --host=*|--build=*|\ + --disable-dependency-tracking|\ --sbindir=*|--sharedstatedir=*|\ --oldincludedir=*|--datarootdir=*|--infodir=*|--localedir=*|\ --htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*) @@ -1004,6 +1060,7 @@ for opt do --enable-debug) # Enable debugging options that aren't excessively noisy debug_tcg="yes" + debug_mutex="yes" debug="yes" strip_opt="no" fortify_source="no" @@ -1374,6 +1431,10 @@ for opt do ;; --disable-git-update) git_update=no ;; + --enable-debug-mutex) debug_mutex=yes + ;; + --disable-debug-mutex) debug_mutex=no + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1394,31 +1455,44 @@ case "$cpu" in ppc) CPU_CFLAGS="-m32" LDFLAGS="-m32 $LDFLAGS" + cross_cc_powerpc=$cc + cross_cc_cflags_powerpc=$CPU_CFLAGS ;; ppc64) CPU_CFLAGS="-m64" LDFLAGS="-m64 $LDFLAGS" + cross_cc_ppc64=$cc + cross_cc_cflags_ppc64=$CPU_CFLAGS ;; sparc) CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc" LDFLAGS="-m32 -mv8plus $LDFLAGS" + cross_cc_sparc=$cc + cross_cc_cflags_sparc=$CPU_CFLAGS ;; sparc64) CPU_CFLAGS="-m64 -mcpu=ultrasparc" LDFLAGS="-m64 $LDFLAGS" + cross_cc_sparc64=$cc + cross_cc_cflags_sparc64=$CPU_CFLAGS ;; s390) CPU_CFLAGS="-m31" LDFLAGS="-m31 $LDFLAGS" + cross_cc_s390=$cc + cross_cc_cflags_s390=$CPU_CFLAGS ;; s390x) CPU_CFLAGS="-m64" LDFLAGS="-m64 $LDFLAGS" + cross_cc_s390x=$cc + cross_cc_cflags_s390x=$CPU_CFLAGS ;; i386) CPU_CFLAGS="-m32" LDFLAGS="-m32 $LDFLAGS" - cc_i386='$(CC) -m32' + cross_cc_i386=$cc + cross_cc_cflags_i386=$CPU_CFLAGS ;; x86_64) # ??? Only extremely old AMD cpus do not have cmpxchg16b. @@ -1426,12 +1500,14 @@ case "$cpu" in # runtime and generate the fallback to serial emulation. CPU_CFLAGS="-m64 -mcx16" LDFLAGS="-m64 $LDFLAGS" - cc_i386='$(CC) -m32' + cross_cc_x86_64=$cc + cross_cc_cflags_x86_64=$CPU_CFLAGS ;; x32) CPU_CFLAGS="-mx32" LDFLAGS="-mx32 $LDFLAGS" - cc_i386='$(CC) -m32' + cross_cc_i386=$cc + cross_cc_cflags_i386=$CPU_CFLAGS ;; # No special flags required for other host CPUs esac @@ -1493,6 +1569,8 @@ Advanced options (experts only): --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS --extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_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 --make=MAKE use specified make [$make] --install=INSTALL use specified install [$install] --python=PYTHON use specified python [$python] @@ -1581,7 +1659,7 @@ disabled with --disable-FEATURE, default is enabled if available: virtfs VirtFS mpath Multipath persistent reservation passthrough xen xen backend driver support - xen-pci-passthrough + xen-pci-passthrough PCI passthrough support for Xen brlapi BrlAPI (Braile) curl curl connectivity membarrier membarrier system call (for Linux 4.14+ or Windows) @@ -1631,6 +1709,7 @@ disabled with --disable-FEATURE, default is enabled if available: crypto-afalg Linux AF_ALG crypto backend driver vhost-user vhost-user support capstone capstone disassembler support + debug-mutex mutex debugging support NOTE: The object files are built at the place where configure is launched EOF @@ -1643,8 +1722,8 @@ fi # Note that if the Python conditional here evaluates True we will exit # with status 1 which is a shell 'false' value. -if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6))'; then - error_exit "Cannot use '$python', Python 2 >= 2.6 or Python 3 is required." \ +if ! $python -c 'import sys; sys.exit(sys.version_info < (2,7))'; then + error_exit "Cannot use '$python', Python 2 >= 2.7 or Python 3 is required." \ "Use --python=/path/to/python to specify a supported Python." fi @@ -2189,6 +2268,9 @@ if test "$xen" != "no" ; then xen=yes xen_pc="xencontrol xenstore xenguest xenforeignmemory xengnttab" xen_pc="$xen_pc xenevtchn xendevicemodel" + if $pkg_config --exists xentoolcore; then + xen_pc="$xen_pc xentoolcore" + fi QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags $xen_pc)" libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu" LDFLAGS="$($pkg_config --libs $xen_pc) $LDFLAGS" @@ -2218,20 +2300,46 @@ EOF # Xen unstable elif cat > $TMPC < +#include +int main(void) { + xendevicemodel_handle *xd; + xenforeignmemory_handle *xfmem; + + xd = xendevicemodel_open(0, 0); + xendevicemodel_pin_memory_cacheattr(xd, 0, 0, 0, 0); + + xfmem = xenforeignmemory_open(0, 0); + xenforeignmemory_map_resource(xfmem, 0, 0, 0, 0, 0, NULL, 0, 0); + + return 0; +} +EOF + compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs -lxentoolcore" + then + xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore" + xen_ctrl_version=41100 + xen=yes + elif + cat > $TMPC < +#include int main(void) { xenforeignmemory_handle *xfmem; xfmem = xenforeignmemory_open(0, 0); xenforeignmemory_map2(xfmem, 0, 0, 0, 0, 0, 0, 0); + xentoolcore_restrict_all(0); return 0; } EOF - compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs" + compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs -lxentoolcore" then - xen_stable_libs="-lxendevicemodel $xen_stable_libs" + xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore" xen_ctrl_version=41000 xen=yes elif @@ -2493,20 +2601,7 @@ fi ########################################## # Windows Hypervisor Platform accelerator (WHPX) check if test "$whpx" != "no" ; then - cat > $TMPC << EOF -#include -#include -#include -int main(void) { - WHV_CAPABILITY whpx_cap; - UINT32 writtenSize; - WHvGetCapability(WHvCapabilityCodeFeatures, &whpx_cap, sizeof(whpx_cap), - &writtenSize); - return 0; -} -EOF - if compile_prog "" "-lWinHvPlatform -lWinHvEmulation" ; then - libs_softmmu="$libs_softmmu -lWinHvPlatform -lWinHvEmulation" + if check_include "WinHvPlatform.h" && check_include "WinHvEmulation.h"; then whpx="yes" else if test "$whpx" = "yes"; then @@ -3392,11 +3487,7 @@ fi ########################################## # glib support probe -if test "$mingw32" = yes; then - glib_req_ver=2.30 -else - glib_req_ver=2.22 -fi +glib_req_ver=2.40 glib_modules="gthread-2.0 gobject-2.0" if test "$modules" = yes; then glib_modules="$glib_modules gmodule-export-2.0" @@ -3761,7 +3852,7 @@ fi fdt_required=no for target in $target_list; do case $target in - aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) + aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu) fdt_required=yes ;; esac @@ -3787,22 +3878,22 @@ int main(void) { fdt_first_subnode(0, 0); return 0; } EOF if compile_prog "" "$fdt_libs" ; then # system DTC is good - use it - fdt=yes + fdt=system else # have GIT checkout, so activate dtc submodule if test -e "${source_path}/.git" ; then git_submodules="${git_submodules} dtc" fi if test -d "${source_path}/dtc/libfdt" || test -e "${source_path}/.git" ; then - fdt=yes - dtc_internal="yes" + fdt=git mkdir -p dtc if [ "$pwd_is_source_path" != "y" ] ; then symlink "$source_path/dtc/Makefile" "dtc/Makefile" symlink "$source_path/dtc/scripts" "dtc/scripts" fi fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt" - fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs" + fdt_ldflags="-L\$(BUILD_DIR)/dtc/libfdt" + fdt_libs="$fdt_libs" elif test "$fdt" = "yes" ; then # Not a git build & no libfdt found, prompt for system install error_exit "DTC (libfdt) version >= 1.4.2 not present." \ @@ -3821,7 +3912,7 @@ libs_softmmu="$libs_softmmu $fdt_libs" # opengl probe (for sdl2, gtk, milkymist-tmu2) if test "$opengl" != "no" ; then - opengl_pkgs="epoxy libdrm gbm" + opengl_pkgs="epoxy gbm" if $pkg_config $opengl_pkgs; then opengl_cflags="$($pkg_config --cflags $opengl_pkgs)" opengl_libs="$($pkg_config --libs $opengl_pkgs)" @@ -4478,7 +4569,7 @@ fi # check for smartcard support if test "$smartcard" != "no"; then - if $pkg_config libcacard; then + if $pkg_config --atleast-version=2.5.1 libcacard; then libcacard_cflags=$($pkg_config --cflags libcacard) libcacard_libs=$($pkg_config --libs libcacard) smartcard="yes" @@ -4604,6 +4695,7 @@ int main(void) { virgl_renderer_poll(); return 0; } EOF virgl_cflags=$($pkg_config --cflags virglrenderer 2>/dev/null) virgl_libs=$($pkg_config --libs virglrenderer 2>/dev/null) + virgl_version=$($pkg_config --modversion virglrenderer 2>/dev/null) if $pkg_config virglrenderer >/dev/null 2>&1 && \ compile_prog "$virgl_cflags" "$virgl_libs" ; then virglrenderer="yes" @@ -4751,6 +4843,21 @@ if compile_prog "" "" ; then sem_timedwait=yes fi +########################################## +# check if we have strchrnul + +strchrnul=no +cat > $TMPC << EOF +#include +int main(void); +// Use a haystack that the compiler shouldn't be able to constant fold +char *haystack = (char*)&main; +int main(void) { return strchrnul(haystack, 'x') != &haystack[6]; } +EOF +if compile_prog "" "" ; then + strchrnul=yes +fi + ########################################## # check if trace backend exists @@ -5176,6 +5283,20 @@ if test "$fortify_source" != "no"; then fi fi +############################################### +# Check if copy_file_range is provided by glibc +have_copy_file_range=no +cat > $TMPC << EOF +#include +int main(void) { + copy_file_range(0, NULL, 0, NULL, 0, 0); + return 0; +} +EOF +if compile_prog "" "" ; then + have_copy_file_range=yes +fi + ########################################## # check if struct fsxattr is available via linux/fs.h @@ -5442,6 +5563,17 @@ EOF fi fi +########################################## +# Docker and cross-compiler support +# +# This is specifically for building test +# cases for foreign architectures, not +# cross-compiling QEMU itself. + +if has "docker"; then + docker=$($python $source_path/tests/docker/docker.py probe) +fi + ########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -5744,6 +5876,7 @@ echo_version() { # prepend pixman and ftd flags after all config tests are done QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS" +QEMU_LDFLAGS="$fdt_ldflags $QEMU_LDFLAGS" libs_softmmu="$pixman_libs $libs_softmmu" echo "Install prefix $prefix" @@ -5774,6 +5907,7 @@ echo "ARFLAGS $ARFLAGS" echo "CFLAGS $CFLAGS" echo "QEMU_CFLAGS $QEMU_CFLAGS" echo "LDFLAGS $LDFLAGS" +echo "QEMU_LDFLAGS $QEMU_LDFLAGS" echo "make $make" echo "install $install" echo "python $python" @@ -5805,7 +5939,7 @@ echo "nettle $nettle $(echo_version $nettle $nettle_version)" echo "nettle kdf $nettle_kdf" echo "libtasn1 $tasn1" echo "curses support $curses" -echo "virgl support $virglrenderer" +echo "virgl support $virglrenderer $(echo_version $virglrenderer $virgl_version)" echo "curl support $curl" echo "mingw32 support $mingw32" echo "Audio drivers $audio_drv_list" @@ -5882,6 +6016,7 @@ echo "seccomp support $seccomp" echo "coroutine backend $coroutine" echo "coroutine pool $coroutine_pool" echo "debug stack usage $debug_stack_usage" +echo "mutex debugging $debug_mutex" echo "crypto afalg $crypto_afalg" echo "GlusterFS support $glusterfs" echo "gcov $gcov_tool" @@ -5903,6 +6038,7 @@ echo "avx2 optimization $avx2_opt" echo "replication support $replication" echo "VxHS block device $vxhs" echo "capstone $capstone" +echo "docker $docker" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -6206,6 +6342,9 @@ fi if test "$sem_timedwait" = "yes" ; then echo "CONFIG_SEM_TIMEDWAIT=y" >> $config_host_mak fi +if test "$strchrnul" = "yes" ; then + echo "HAVE_STRCHRNUL=y" >> $config_host_mak +fi if test "$byteswap_h" = "yes" ; then echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak fi @@ -6277,6 +6416,9 @@ fi if test "$have_fsxattr" = "yes" ; then echo "HAVE_FSXATTR=y" >> $config_host_mak fi +if test "$have_copy_file_range" = "yes" ; then + echo "HAVE_COPY_FILE_RANGE=y" >> $config_host_mak +fi if test "$vte" = "yes" ; then echo "CONFIG_VTE=y" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak @@ -6333,7 +6475,7 @@ fi if test "$preadv" = "yes" ; then echo "CONFIG_PREADV=y" >> $config_host_mak fi -if test "$fdt" = "yes" ; then +if test "$fdt" != "no" ; then echo "CONFIG_FDT=y" >> $config_host_mak fi if test "$membarrier" = "yes" ; then @@ -6631,6 +6773,9 @@ fi if test "$capstone" != "no" ; then echo "CONFIG_CAPSTONE=y" >> $config_host_mak fi +if test "$debug_mutex" = "yes" ; then + echo "CONFIG_DEBUG_MUTEX=y" >> $config_host_mak +fi # Hold two types of flag: # CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on @@ -6675,7 +6820,6 @@ echo "CC=$cc" >> $config_host_mak if $iasl -h > /dev/null 2>&1; then echo "IASL=$iasl" >> $config_host_mak fi -echo "CC_I386=$cc_i386" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak echo "CXX=$cxx" >> $config_host_mak echo "OBJCC=$objcc" >> $config_host_mak @@ -6708,6 +6852,7 @@ else fi echo "LDFLAGS=$LDFLAGS" >> $config_host_mak echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak +echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak echo "LD_REL_FLAGS=$LD_REL_FLAGS" >> $config_host_mak echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak echo "LIBS+=$LIBS" >> $config_host_mak @@ -6726,6 +6871,10 @@ if test "$gcov" = "yes" ; then echo "GCOV=$gcov_tool" >> $config_host_mak fi +if test "$docker" != "no"; then + echo "HAVE_USER_DOCKER=y" >> $config_host_mak +fi + # use included Linux headers if test "$linux" = "yes" ; then mkdir -p linux-headers @@ -6789,6 +6938,10 @@ case "$target" in ;; esac +target_compiler="" +target_compiler_static="" +target_compiler_cflags="" + mkdir -p $target_dir echo "# Automatically generated by configure - do not modify" > $config_target_mak @@ -6804,19 +6957,25 @@ TARGET_ABI_DIR="" case "$target_name" in i386) gdb_xml_files="i386-32bit.xml i386-32bit-core.xml i386-32bit-sse.xml" + target_compiler=$cross_cc_i386 + target_compiler_cflags=$cross_cc_ccflags_i386 ;; x86_64) TARGET_BASE_ARCH=i386 gdb_xml_files="i386-64bit.xml i386-64bit-core.xml i386-64bit-sse.xml" + target_compiler=$cross_cc_x86_64 ;; alpha) mttcg="yes" + target_compiler=$cross_cc_alpha ;; arm|armeb) TARGET_ARCH=arm bflt="yes" mttcg="yes" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" + target_compiler=$cross_cc_arm + eval "target_compiler_cflags=\$cross_cc_cflags_${target_name}" ;; aarch64|aarch64_be) TARGET_ARCH=aarch64 @@ -6824,58 +6983,75 @@ case "$target_name" in bflt="yes" mttcg="yes" gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" + target_compiler=$cross_cc_aarch64 + eval "target_compiler_cflags=\$cross_cc_cflags_${target_name}" ;; cris) + target_compiler=$cross_cc_cris ;; hppa) mttcg="yes" + target_compiler=$cross_cc_hppa ;; lm32) + target_compiler=$cross_cc_lm32 ;; m68k) bflt="yes" gdb_xml_files="cf-core.xml cf-fp.xml m68k-fp.xml" + target_compiler=$cross_cc_m68k ;; microblaze|microblazeel) TARGET_ARCH=microblaze bflt="yes" + echo "TARGET_ABI32=y" >> $config_target_mak + target_compiler=$cross_cc_microblaze ;; mips|mipsel) TARGET_ARCH=mips + target_compiler=$cross_cc_mips echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak ;; mipsn32|mipsn32el) TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips + target_compiler=$cross_cc_mipsn32 echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak echo "TARGET_ABI32=y" >> $config_target_mak ;; mips64|mips64el) TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips + target_compiler=$cross_cc_mips64 echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak ;; moxie) + target_compiler=$cross_cc_moxie ;; nios2) + target_compiler=$cross_cc_nios2 ;; or1k) + target_compiler=$cross_cc_or1k TARGET_ARCH=openrisc TARGET_BASE_ARCH=openrisc ;; ppc) gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + target_compiler=$cross_cc_powerpc ;; ppcemb) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" + target_compiler=$cross_cc_ppcemb ;; ppc64) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc mttcg=yes gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" + target_compiler=$cross_cc_ppc64 ;; ppc64le) TARGET_ARCH=ppc64 @@ -6883,6 +7059,7 @@ case "$target_name" in TARGET_ABI_DIR=ppc mttcg=yes gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" + target_compiler=$cross_cc_ppc64le ;; ppc64abi32) TARGET_ARCH=ppc64 @@ -6890,45 +7067,57 @@ case "$target_name" in TARGET_ABI_DIR=ppc echo "TARGET_ABI32=y" >> $config_target_mak gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" + target_compiler=$cross_cc_ppc64abi32 ;; riscv32) TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes + target_compiler=$cross_cc_riscv32 ;; riscv64) TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes + target_compiler=$cross_cc_riscv64 ;; sh4|sh4eb) TARGET_ARCH=sh4 bflt="yes" + target_compiler=$cross_cc_sh4 ;; sparc) + target_compiler=$cross_cc_sparc ;; sparc64) TARGET_BASE_ARCH=sparc + target_compiler=$cross_cc_sparc64 ;; sparc32plus) TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_ABI_DIR=sparc + target_compiler=$cross_cc_sparc32plus echo "TARGET_ABI32=y" >> $config_target_mak ;; s390x) mttcg=yes gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml s390-virt.xml s390-gs.xml" + target_compiler=$cross_cc_s390x ;; tilegx) + target_compiler=$cross_cc_tilegx ;; tricore) + target_compiler=$cross_cc_tricore ;; unicore32) + target_compiler=$cross_cc_unicore32 ;; xtensa|xtensaeb) TARGET_ARCH=xtensa mttcg="yes" + target_compiler=$cross_cc_xtensa ;; *) error_exit "Unsupported target CPU" @@ -6939,6 +7128,27 @@ if [ "$TARGET_BASE_ARCH" = "" ]; then TARGET_BASE_ARCH=$TARGET_ARCH fi +# Do we have a cross compiler for this target? +if has $target_compiler; then + + write_c_skeleton + + if ! do_compiler "$target_compiler" $target_compiler_cflags -o $TMPE $TMPC -static ; then + # For host systems we might get away with building without -static + if ! do_compiler "$target_compiler" $target_compiler_cflags -o $TMPE $TMPC ; then + target_compiler="" + else + enabled_cross_compilers="${enabled_cross_compilers} '${target_compiler}'" + target_compiler_static="n" + fi + else + enabled_cross_compilers="${enabled_cross_compilers} '${target_compiler}'" + target_compiler_static="y" + fi +else + target_compiler="" +fi + symlink "$source_path/Makefile.target" "$target_dir/Makefile" upper() { @@ -7012,6 +7222,19 @@ if test "$target_bsd_user" = "yes" ; then echo "CONFIG_BSD_USER=y" >> $config_target_mak fi +if test -n "$target_compiler"; then + echo "CROSS_CC_GUEST=\"$target_compiler\"" >> $config_target_mak + + if test -n "$target_compiler_static"; then + echo "CROSS_CC_GUEST_STATIC=$target_compiler_static" >> $config_target_mak + fi + + if test -n "$target_compiler_cflags"; then + echo "CROSS_CC_GUEST_CFLAGS=$target_compiler_cflags" >> $config_target_mak + fi +fi + + # generate QEMU_CFLAGS/LDFLAGS for targets cflags="" @@ -7134,7 +7357,12 @@ echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak done # for target in $targets -if [ "$dtc_internal" = "yes" ]; then +if test -n "$enabled_cross_compilers"; then + echo + echo "NOTE: cross-compilers enabled: $enabled_cross_compilers" +fi + +if [ "$fdt" = "git" ]; then echo "config-host.h: subdir-dtc" >> $config_host_mak fi if [ "$capstone" = "git" -o "$capstone" = "internal" ]; then @@ -7207,9 +7435,11 @@ for rom in seabios vgabios ; do done # set up tests data directory -if [ ! -e tests/data ]; then - symlink "$source_path/tests/data" tests/data -fi +for tests_subdir in acceptance data; do + if [ ! -e tests/$tests_subdir ]; then + symlink "$source_path/tests/$tests_subdir" tests/$tests_subdir + fi +done # set up qemu-iotests in this build directory iotests_common_env="tests/qemu-iotests/common.env" diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c index beeed0c43f..a6b46cdc03 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -314,22 +314,19 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) msg.msg_controllen = 0; } - /* Set the version in the flags when sending the reply */ - vmsg->flags &= ~VHOST_USER_VERSION_MASK; - vmsg->flags |= VHOST_USER_VERSION; - vmsg->flags |= VHOST_USER_REPLY_MASK; - do { rc = sendmsg(conn_fd, &msg, 0); } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); - do { - if (vmsg->data) { - rc = write(conn_fd, vmsg->data, vmsg->size); - } else { - rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size); - } - } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + if (vmsg->size) { + do { + if (vmsg->data) { + rc = write(conn_fd, vmsg->data, vmsg->size); + } else { + rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size); + } + } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); + } if (rc <= 0) { vu_panic(dev, "Error while writing: %s", strerror(errno)); @@ -339,6 +336,39 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) return true; } +static bool +vu_send_reply(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) +{ + /* Set the version in the flags when sending the reply */ + vmsg->flags &= ~VHOST_USER_VERSION_MASK; + vmsg->flags |= VHOST_USER_VERSION; + vmsg->flags |= VHOST_USER_REPLY_MASK; + + return vu_message_write(dev, conn_fd, vmsg); +} + +static bool +vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg) +{ + VhostUserMsg msg_reply; + + if ((vmsg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) { + return true; + } + + if (!vu_message_read(dev, dev->slave_fd, &msg_reply)) { + return false; + } + + if (msg_reply.request != vmsg->request) { + DPRINT("Received unexpected msg type. Expected %d received %d", + vmsg->request, msg_reply.request); + return false; + } + + return msg_reply.payload.u64 == 0; +} + /* Kick the log_call_fd if required. */ static void vu_log_kick(VuDev *dev) @@ -534,7 +564,7 @@ vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) /* Send the message back to qemu with the addresses filled in */ vmsg->fd_num = 0; - if (!vu_message_write(dev, dev->sock, vmsg)) { + if (!vu_send_reply(dev, dev->sock, vmsg)) { vu_panic(dev, "failed to respond to set-mem-table for postcopy"); return false; } @@ -914,6 +944,41 @@ void vu_set_queue_handler(VuDev *dev, VuVirtq *vq, } } +bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, + int size, int offset) +{ + int qidx = vq - dev->vq; + int fd_num = 0; + VhostUserMsg vmsg = { + .request = VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG, + .flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK, + .size = sizeof(vmsg.payload.area), + .payload.area = { + .u64 = qidx & VHOST_USER_VRING_IDX_MASK, + .size = size, + .offset = offset, + }, + }; + + if (fd == -1) { + vmsg.payload.area.u64 |= VHOST_USER_VRING_NOFD_MASK; + } else { + vmsg.fds[fd_num++] = fd; + } + + vmsg.fd_num = fd_num; + + if ((dev->protocol_features & VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD) == 0) { + return false; + } + + if (!vu_message_write(dev, dev->slave_fd, &vmsg)) { + return false; + } + + return vu_process_message_reply(dev, &vmsg); +} + static bool vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg) { @@ -966,7 +1031,9 @@ static bool vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) { uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD | - 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ; + 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ | + 1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER | + 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD; if (have_userfault()) { features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT; @@ -1250,7 +1317,7 @@ vu_dispatch(VuDev *dev) goto end; } - if (!vu_message_write(dev, dev->sock, &vmsg)) { + if (!vu_send_reply(dev, dev->sock, &vmsg)) { goto end; } diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index b27075ea3b..4aa55b4d2d 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -51,6 +51,8 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, VHOST_USER_PROTOCOL_F_CONFIG = 9, + VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, + VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, VHOST_USER_PROTOCOL_F_MAX }; @@ -92,6 +94,14 @@ typedef enum VhostUserRequest { VHOST_USER_MAX } VhostUserRequest; +typedef enum VhostUserSlaveRequest { + VHOST_USER_SLAVE_NONE = 0, + VHOST_USER_SLAVE_IOTLB_MSG = 1, + VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2, + VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3, + VHOST_USER_SLAVE_MAX +} VhostUserSlaveRequest; + typedef struct VhostUserMemoryRegion { uint64_t guest_phys_addr; uint64_t memory_size; @@ -122,6 +132,12 @@ static VhostUserConfig c __attribute__ ((unused)); + sizeof(c.size) \ + sizeof(c.flags)) +typedef struct VhostUserVringArea { + uint64_t u64; + uint64_t size; + uint64_t offset; +} VhostUserVringArea; + #if defined(_WIN32) # define VU_PACKED __attribute__((gcc_struct, packed)) #else @@ -133,6 +149,7 @@ typedef struct VhostUserMsg { #define VHOST_USER_VERSION_MASK (0x3) #define VHOST_USER_REPLY_MASK (0x1 << 2) +#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) uint32_t flags; uint32_t size; /* the following payload size */ @@ -145,6 +162,7 @@ typedef struct VhostUserMsg { VhostUserMemory memory; VhostUserLog log; VhostUserConfig config; + VhostUserVringArea area; } payload; int fds[VHOST_MEMORY_MAX_NREGIONS]; @@ -368,6 +386,20 @@ VuVirtq *vu_get_queue(VuDev *dev, int qidx); void vu_set_queue_handler(VuDev *dev, VuVirtq *vq, vu_queue_handler_cb handler); +/** + * vu_set_queue_host_notifier: + * @dev: a VuDev context + * @vq: a VuVirtq queue + * @fd: a file descriptor + * @size: host page size + * @offset: notifier offset in @fd file + * + * Set queue's host notifier. This function may be called several + * times for the same queue. If called with -1 @fd, the notifier + * is removed. + */ +bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, + int size, int offset); /** * vu_queue_set_notification: diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index 67dac8155a..571f114a56 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -31,6 +31,7 @@ typedef struct VubDev { VugDev parent; int blk_fd; struct virtio_blk_config blkcfg; + bool enable_ro; char *blk_name; GMainLoop *loop; } VubDev; @@ -301,14 +302,33 @@ static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started) static uint64_t vub_get_features(VuDev *dev) { - return 1ull << VIRTIO_BLK_F_SIZE_MAX | - 1ull << VIRTIO_BLK_F_SEG_MAX | - 1ull << VIRTIO_BLK_F_TOPOLOGY | - 1ull << VIRTIO_BLK_F_BLK_SIZE | - 1ull << VIRTIO_BLK_F_FLUSH | - 1ull << VIRTIO_BLK_F_CONFIG_WCE | - 1ull << VIRTIO_F_VERSION_1 | - 1ull << VHOST_USER_F_PROTOCOL_FEATURES; + uint64_t features; + VugDev *gdev; + VubDev *vdev_blk; + + gdev = container_of(dev, VugDev, parent); + vdev_blk = container_of(gdev, VubDev, parent); + + features = 1ull << VIRTIO_BLK_F_SIZE_MAX | + 1ull << VIRTIO_BLK_F_SEG_MAX | + 1ull << VIRTIO_BLK_F_TOPOLOGY | + 1ull << VIRTIO_BLK_F_BLK_SIZE | + 1ull << VIRTIO_BLK_F_FLUSH | + 1ull << VIRTIO_BLK_F_CONFIG_WCE | + 1ull << VIRTIO_F_VERSION_1 | + 1ull << VHOST_USER_F_PROTOCOL_FEATURES; + + if (vdev_blk->enable_ro) { + features |= 1ull << VIRTIO_BLK_F_RO; + } + + return features; +} + +static uint64_t +vub_get_protocol_features(VuDev *dev) +{ + return 1ull << VHOST_USER_PROTOCOL_F_CONFIG; } static int @@ -373,6 +393,7 @@ vub_set_config(VuDev *vu_dev, const uint8_t *data, static const VuDevIface vub_iface = { .get_features = vub_get_features, .queue_set_started = vub_queue_set_started, + .get_protocol_features = vub_get_protocol_features, .get_config = vub_get_config, .set_config = vub_set_config, }; @@ -469,6 +490,7 @@ vub_new(char *blk_file) vub_free(vdev_blk); return NULL; } + vdev_blk->enable_ro = false; vdev_blk->blkcfg.wce = 0; vdev_blk->blk_name = blk_file; @@ -483,10 +505,11 @@ int main(int argc, char **argv) int opt; char *unix_socket = NULL; char *blk_file = NULL; + bool enable_ro = false; int lsock = -1, csock = -1; VubDev *vdev_blk = NULL; - while ((opt = getopt(argc, argv, "b:s:h")) != -1) { + while ((opt = getopt(argc, argv, "b:rs:h")) != -1) { switch (opt) { case 'b': blk_file = g_strdup(optarg); @@ -494,17 +517,20 @@ int main(int argc, char **argv) case 's': unix_socket = g_strdup(optarg); break; + case 'r': + enable_ro = true; + break; case 'h': default: - printf("Usage: %s [-b block device or file, -s UNIX domain socket]" - " | [ -h ]\n", argv[0]); + printf("Usage: %s [ -b block device or file, -s UNIX domain socket" + " | -r Enable read-only ] | [ -h ]\n", argv[0]); return 0; } } if (!unix_socket || !blk_file) { - printf("Usage: %s [-b block device or file, -s UNIX domain socket] |" - " [ -h ]\n", argv[0]); + printf("Usage: %s [ -b block device or file, -s UNIX domain socket" + " | -r Enable read-only ] | [ -h ]\n", argv[0]); return -1; } @@ -523,6 +549,9 @@ int main(int argc, char **argv) if (!vdev_blk) { goto err; } + if (enable_ro) { + vdev_blk->enable_ro = true; + } vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface); diff --git a/cpus.c b/cpus.c index 38eba8bff3..b5844b7103 100644 --- a/cpus.c +++ b/cpus.c @@ -1355,6 +1355,7 @@ static int tcg_cpu_exec(CPUState *cpu) int64_t ti; #endif + assert(tcg_enabled()); #ifdef CONFIG_PROFILER ti = profile_getclock(); #endif @@ -1397,6 +1398,7 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) { CPUState *cpu = arg; + assert(tcg_enabled()); rcu_register_thread(); tcg_register_thread(); @@ -1631,6 +1633,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) { CPUState *cpu = arg; + assert(tcg_enabled()); g_assert(!use_icount); rcu_register_thread(); @@ -1648,7 +1651,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) /* process any pending work */ cpu->exit_request = 1; - while (1) { + do { if (cpu_can_run(cpu)) { int r; qemu_mutex_unlock_iothread(); @@ -1854,6 +1857,7 @@ static void qemu_tcg_init_vcpu(CPUState *cpu) static QemuThread *single_tcg_cpu_thread; static int tcg_region_inited; + assert(tcg_enabled()); /* * Initialize TCG regions--once. Now is a good time, because: * (1) TCG's init context, prologue and target globals have been set up. @@ -2043,7 +2047,6 @@ int vm_stop(RunState state) int vm_prepare_start(void) { RunState requested; - int res = 0; qemu_vmstop_requested(&requested); if (runstate_is_running() && requested == RUN_STATE__MAX) { @@ -2057,17 +2060,18 @@ int vm_prepare_start(void) */ if (runstate_is_running()) { qapi_event_send_stop(&error_abort); - res = -1; - } else { - replay_enable_events(); - cpu_enable_ticks(); - runstate_set(RUN_STATE_RUNNING); - vm_state_notify(1, RUN_STATE_RUNNING); + qapi_event_send_resume(&error_abort); + return -1; } /* We are sending this now, but the CPUs will be resumed shortly later */ qapi_event_send_resume(&error_abort); - return res; + + replay_enable_events(); + cpu_enable_ticks(); + runstate_set(RUN_STATE_RUNNING); + vm_state_notify(1, RUN_STATE_RUNNING); + return 0; } void vm_start(void) @@ -2187,6 +2191,59 @@ CpuInfoList *qmp_query_cpus(Error **errp) return head; } +static CpuInfoArch sysemu_target_to_cpuinfo_arch(SysEmuTarget target) +{ + /* + * The @SysEmuTarget -> @CpuInfoArch mapping below is based on the + * TARGET_ARCH -> TARGET_BASE_ARCH mapping in the "configure" script. + */ + switch (target) { + case SYS_EMU_TARGET_I386: + case SYS_EMU_TARGET_X86_64: + return CPU_INFO_ARCH_X86; + + case SYS_EMU_TARGET_PPC: + case SYS_EMU_TARGET_PPCEMB: + case SYS_EMU_TARGET_PPC64: + return CPU_INFO_ARCH_PPC; + + case SYS_EMU_TARGET_SPARC: + case SYS_EMU_TARGET_SPARC64: + return CPU_INFO_ARCH_SPARC; + + case SYS_EMU_TARGET_MIPS: + case SYS_EMU_TARGET_MIPSEL: + case SYS_EMU_TARGET_MIPS64: + case SYS_EMU_TARGET_MIPS64EL: + return CPU_INFO_ARCH_MIPS; + + case SYS_EMU_TARGET_TRICORE: + return CPU_INFO_ARCH_TRICORE; + + case SYS_EMU_TARGET_S390X: + return CPU_INFO_ARCH_S390; + + case SYS_EMU_TARGET_RISCV32: + case SYS_EMU_TARGET_RISCV64: + return CPU_INFO_ARCH_RISCV; + + default: + return CPU_INFO_ARCH_OTHER; + } +} + +static void cpustate_to_cpuinfo_s390(CpuInfoS390 *info, const CPUState *cpu) +{ +#ifdef TARGET_S390X + S390CPU *s390_cpu = S390_CPU(cpu); + CPUS390XState *env = &s390_cpu->env; + + info->cpu_state = env->cpu_state; +#else + abort(); +#endif +} + /* * fast means: we NEVER interrupt vCPU threads to retrieve * information from KVM. @@ -2196,11 +2253,9 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) MachineState *ms = MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(ms); CpuInfoFastList *head = NULL, *cur_item = NULL; + SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, + -1, &error_abort); CPUState *cpu; -#if defined(TARGET_S390X) - S390CPU *s390_cpu; - CPUS390XState *env; -#endif CPU_FOREACH(cpu) { CpuInfoFastList *info = g_malloc0(sizeof(*info)); @@ -2218,12 +2273,12 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) info->value->props = props; } -#if defined(TARGET_S390X) - s390_cpu = S390_CPU(cpu); - env = &s390_cpu->env; - info->value->arch = CPU_INFO_ARCH_S390; - info->value->u.s390.cpu_state = env->cpu_state; -#endif + info->value->arch = sysemu_target_to_cpuinfo_arch(target); + info->value->target = target; + if (target == SYS_EMU_TARGET_S390X) { + cpustate_to_cpuinfo_s390(&info->value->u.s390x, cpu); + } + if (!cur_item) { head = cur_item = info; } else { diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index 2b99e08062..756bab111b 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -15,6 +15,7 @@ crypto-obj-$(CONFIG_AF_ALG) += cipher-afalg.o crypto-obj-$(CONFIG_AF_ALG) += hash-afalg.o crypto-obj-y += tlscreds.o crypto-obj-y += tlscredsanon.o +crypto-obj-y += tlscredspsk.o crypto-obj-y += tlscredsx509.o crypto-obj-y += tlssession.o crypto-obj-y += secret.o diff --git a/crypto/block-luks.c b/crypto/block-luks.c index d418ac30b8..5738124773 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -22,7 +22,7 @@ #include "qapi/error.h" #include "qemu/bswap.h" -#include "crypto/block-luks.h" +#include "block-luks.h" #include "crypto/hash.h" #include "crypto/afsplit.h" diff --git a/crypto/block-luks.h b/crypto/block-luks.h index b2d8a35c1b..befd8b2c56 100644 --- a/crypto/block-luks.h +++ b/crypto/block-luks.h @@ -21,7 +21,7 @@ #ifndef QCRYPTO_BLOCK_LUKS_H #define QCRYPTO_BLOCK_LUKS_H -#include "crypto/blockpriv.h" +#include "blockpriv.h" extern const QCryptoBlockDriver qcrypto_block_driver_luks; diff --git a/crypto/block-qcow.c b/crypto/block-qcow.c index 8817d6aaa7..4284e05167 100644 --- a/crypto/block-qcow.c +++ b/crypto/block-qcow.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "crypto/block-qcow.h" +#include "block-qcow.h" #include "crypto/secret.h" #define QCRYPTO_BLOCK_QCOW_SECTOR_SIZE 512 diff --git a/crypto/block-qcow.h b/crypto/block-qcow.h index 3e2c0a851a..6988fb210b 100644 --- a/crypto/block-qcow.h +++ b/crypto/block-qcow.h @@ -21,7 +21,7 @@ #ifndef QCRYPTO_BLOCK_QCOW_H #define QCRYPTO_BLOCK_QCOW_H -#include "crypto/blockpriv.h" +#include "blockpriv.h" extern const QCryptoBlockDriver qcrypto_block_driver_qcow; diff --git a/crypto/block.c b/crypto/block.c index f206d5eea8..e59d1140fe 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -20,9 +20,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "crypto/blockpriv.h" -#include "crypto/block-qcow.h" -#include "crypto/block-luks.h" +#include "blockpriv.h" +#include "block-qcow.h" +#include "block-luks.h" static const QCryptoBlockDriver *qcrypto_block_drivers[] = { [Q_CRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow, diff --git a/crypto/cipher.c b/crypto/cipher.c index bcbfb3d5b8..b3af57961b 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -150,11 +150,11 @@ qcrypto_cipher_munge_des_rfb_key(const uint8_t *key, #endif /* CONFIG_GCRYPT || CONFIG_NETTLE */ #ifdef CONFIG_GCRYPT -#include "crypto/cipher-gcrypt.c" +#include "cipher-gcrypt.c" #elif defined CONFIG_NETTLE -#include "crypto/cipher-nettle.c" +#include "cipher-nettle.c" #else -#include "crypto/cipher-builtin.c" +#include "cipher-builtin.c" #endif QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, diff --git a/crypto/hash-glib.c b/crypto/hash-glib.c index a5871cc72f..a0096c7c47 100644 --- a/crypto/hash-glib.c +++ b/crypto/hash-glib.c @@ -30,11 +30,7 @@ static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { [QCRYPTO_HASH_ALG_SHA224] = -1, [QCRYPTO_HASH_ALG_SHA256] = G_CHECKSUM_SHA256, [QCRYPTO_HASH_ALG_SHA384] = -1, -#if GLIB_CHECK_VERSION(2, 36, 0) [QCRYPTO_HASH_ALG_SHA512] = G_CHECKSUM_SHA512, -#else - [QCRYPTO_HASH_ALG_SHA512] = -1, -#endif [QCRYPTO_HASH_ALG_RIPEMD160] = -1, }; diff --git a/crypto/hmac-glib.c b/crypto/hmac-glib.c index a6c1730291..7df627329d 100644 --- a/crypto/hmac-glib.c +++ b/crypto/hmac-glib.c @@ -17,9 +17,6 @@ #include "crypto/hmac.h" #include "hmacpriv.h" -/* Support for HMAC Algos has been added in GLib 2.30 */ -#if GLIB_CHECK_VERSION(2, 30, 0) - static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = { [QCRYPTO_HASH_ALG_MD5] = G_CHECKSUM_MD5, [QCRYPTO_HASH_ALG_SHA1] = G_CHECKSUM_SHA1, @@ -126,39 +123,6 @@ qcrypto_glib_hmac_bytesv(QCryptoHmac *hmac, return 0; } -#else - -bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg) -{ - return false; -} - -void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg, - const uint8_t *key, size_t nkey, - Error **errp) -{ - return NULL; -} - -static void -qcrypto_glib_hmac_ctx_free(QCryptoHmac *hmac) -{ - return; -} - -static int -qcrypto_glib_hmac_bytesv(QCryptoHmac *hmac, - const struct iovec *iov, - size_t niov, - uint8_t **result, - size_t *resultlen, - Error **errp) -{ - return -1; -} - -#endif - QCryptoHmacDriver qcrypto_hmac_lib_driver = { .hmac_bytesv = qcrypto_glib_hmac_bytesv, .hmac_free = qcrypto_glib_hmac_ctx_free, diff --git a/crypto/ivgen-essiv.c b/crypto/ivgen-essiv.c index aeaa8fcd5b..43e258c6f7 100644 --- a/crypto/ivgen-essiv.c +++ b/crypto/ivgen-essiv.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "crypto/ivgen-essiv.h" +#include "ivgen-essiv.h" typedef struct QCryptoIVGenESSIV QCryptoIVGenESSIV; struct QCryptoIVGenESSIV { diff --git a/crypto/ivgen-essiv.h b/crypto/ivgen-essiv.h index 4a00af849a..f34dbab57b 100644 --- a/crypto/ivgen-essiv.h +++ b/crypto/ivgen-essiv.h @@ -18,7 +18,7 @@ * */ -#include "crypto/ivgenpriv.h" +#include "ivgenpriv.h" #ifndef QCRYPTO_IVGEN_ESSIV_H__ #define QCRYPTO_IVGEN_ESSIV_H__ diff --git a/crypto/ivgen-plain.c b/crypto/ivgen-plain.c index bf2fb7aac4..06f4145fe5 100644 --- a/crypto/ivgen-plain.c +++ b/crypto/ivgen-plain.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "crypto/ivgen-plain.h" +#include "ivgen-plain.h" static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen, const uint8_t *key, size_t nkey, diff --git a/crypto/ivgen-plain.h b/crypto/ivgen-plain.h index 0fe8835c3e..16e1ae5b27 100644 --- a/crypto/ivgen-plain.h +++ b/crypto/ivgen-plain.h @@ -18,7 +18,7 @@ * */ -#include "crypto/ivgenpriv.h" +#include "ivgenpriv.h" #ifndef QCRYPTO_IVGEN_PLAIN_H__ #define QCRYPTO_IVGEN_PLAIN_H__ diff --git a/crypto/ivgen-plain64.c b/crypto/ivgen-plain64.c index e4679a1e6e..fbb7724b20 100644 --- a/crypto/ivgen-plain64.c +++ b/crypto/ivgen-plain64.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "crypto/ivgen-plain.h" +#include "ivgen-plain.h" static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen, const uint8_t *key, size_t nkey, diff --git a/crypto/ivgen-plain64.h b/crypto/ivgen-plain64.h index c4104459b5..f8611bd705 100644 --- a/crypto/ivgen-plain64.h +++ b/crypto/ivgen-plain64.h @@ -18,7 +18,7 @@ * */ -#include "crypto/ivgenpriv.h" +#include "ivgenpriv.h" #ifndef QCRYPTO_IVGEN_PLAIN64_H__ #define QCRYPTO_IVGEN_PLAIN64_H__ diff --git a/crypto/ivgen.c b/crypto/ivgen.c index f66435112b..6a2b3ad01e 100644 --- a/crypto/ivgen.c +++ b/crypto/ivgen.c @@ -21,10 +21,10 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "crypto/ivgenpriv.h" -#include "crypto/ivgen-plain.h" -#include "crypto/ivgen-plain64.h" -#include "crypto/ivgen-essiv.h" +#include "ivgenpriv.h" +#include "ivgen-plain.h" +#include "ivgen-plain64.h" +#include "ivgen-essiv.h" QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg, diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c index 3cd41035bb..02255a6f3c 100644 --- a/crypto/tlscreds.c +++ b/crypto/tlscreds.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "crypto/tlscredspriv.h" +#include "tlscredspriv.h" #include "trace.h" #define DH_BITS 2048 diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c index 1464220080..7ad66d1e7d 100644 --- a/crypto/tlscredsanon.c +++ b/crypto/tlscredsanon.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "crypto/tlscredsanon.h" -#include "crypto/tlscredspriv.h" +#include "tlscredspriv.h" #include "qapi/error.h" #include "qom/object_interfaces.h" #include "trace.h" diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c new file mode 100644 index 0000000000..7be7c8efdd --- /dev/null +++ b/crypto/tlscredspsk.c @@ -0,0 +1,308 @@ +/* + * QEMU crypto TLS Pre-Shared Keys (PSK) support + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "crypto/tlscredspsk.h" +#include "tlscredspriv.h" +#include "qapi/error.h" +#include "qom/object_interfaces.h" +#include "trace.h" + + +#ifdef CONFIG_GNUTLS + +static int +lookup_key(const char *pskfile, const char *username, gnutls_datum_t *key, + Error **errp) +{ + const size_t ulen = strlen(username); + GError *gerr = NULL; + char *content = NULL; + char **lines = NULL; + size_t clen = 0, i; + int ret = -1; + + if (!g_file_get_contents(pskfile, &content, &clen, &gerr)) { + error_setg(errp, "Cannot read PSK file %s: %s", + pskfile, gerr->message); + g_error_free(gerr); + return -1; + } + + lines = g_strsplit(content, "\n", -1); + for (i = 0; lines[i] != NULL; ++i) { + if (strncmp(lines[i], username, ulen) == 0 && lines[i][ulen] == ':') { + key->data = (unsigned char *) g_strdup(&lines[i][ulen + 1]); + key->size = strlen(lines[i]) - ulen - 1; + ret = 0; + goto out; + } + } + error_setg(errp, "Username %s not found in PSK file %s", + username, pskfile); + + out: + free(content); + g_strfreev(lines); + return ret; +} + +static int +qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds, + Error **errp) +{ + char *pskfile = NULL, *dhparams = NULL; + const char *username; + int ret; + int rv = -1; + gnutls_datum_t key = { .data = NULL }; + + trace_qcrypto_tls_creds_psk_load(creds, + creds->parent_obj.dir ? creds->parent_obj.dir : ""); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + if (creds->username) { + error_setg(errp, "username should not be set when endpoint=server"); + goto cleanup; + } + + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_DH_PARAMS, + false, &dhparams, errp) < 0 || + qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_PSKFILE, + true, &pskfile, errp) < 0) { + goto cleanup; + } + + ret = gnutls_psk_allocate_server_credentials(&creds->data.server); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } + + if (qcrypto_tls_creds_get_dh_params_file(&creds->parent_obj, dhparams, + &creds->parent_obj.dh_params, + errp) < 0) { + goto cleanup; + } + + gnutls_psk_set_server_credentials_file(creds->data.server, pskfile); + gnutls_psk_set_server_dh_params(creds->data.server, + creds->parent_obj.dh_params); + } else { + if (qcrypto_tls_creds_get_path(&creds->parent_obj, + QCRYPTO_TLS_CREDS_PSKFILE, + true, &pskfile, errp) < 0) { + goto cleanup; + } + + if (creds->username) { + username = creds->username; + } else { + username = "qemu"; + } + if (lookup_key(pskfile, username, &key, errp) != 0) { + goto cleanup; + } + + ret = gnutls_psk_allocate_client_credentials(&creds->data.client); + if (ret < 0) { + error_setg(errp, "Cannot allocate credentials: %s", + gnutls_strerror(ret)); + goto cleanup; + } + + gnutls_psk_set_client_credentials(creds->data.client, + username, &key, GNUTLS_PSK_KEY_HEX); + } + + rv = 0; + cleanup: + g_free(key.data); + g_free(pskfile); + g_free(dhparams); + return rv; +} + + +static void +qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds) +{ + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + if (creds->data.client) { + gnutls_psk_free_client_credentials(creds->data.client); + creds->data.client = NULL; + } + } else { + if (creds->data.server) { + gnutls_psk_free_server_credentials(creds->data.server); + creds->data.server = NULL; + } + } + if (creds->parent_obj.dh_params) { + gnutls_dh_params_deinit(creds->parent_obj.dh_params); + creds->parent_obj.dh_params = NULL; + } +} + +#else /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_psk_load(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "TLS credentials support requires GNUTLS"); +} + + +static void +qcrypto_tls_creds_psk_unload(QCryptoTLSCredsPSK *creds G_GNUC_UNUSED) +{ + /* nada */ +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_psk_prop_set_loaded(Object *obj, + bool value, + Error **errp) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + if (value) { + qcrypto_tls_creds_psk_load(creds, errp); + } else { + qcrypto_tls_creds_psk_unload(creds); + } +} + + +#ifdef CONFIG_GNUTLS + + +static bool +qcrypto_tls_creds_psk_prop_get_loaded(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + if (creds->parent_obj.endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + return creds->data.server != NULL; + } else { + return creds->data.client != NULL; + } +} + + +#else /* ! CONFIG_GNUTLS */ + + +static bool +qcrypto_tls_creds_psk_prop_get_loaded(Object *obj G_GNUC_UNUSED, + Error **errp G_GNUC_UNUSED) +{ + return false; +} + + +#endif /* ! CONFIG_GNUTLS */ + + +static void +qcrypto_tls_creds_psk_complete(UserCreatable *uc, Error **errp) +{ + object_property_set_bool(OBJECT(uc), true, "loaded", errp); +} + + +static void +qcrypto_tls_creds_psk_finalize(Object *obj) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + qcrypto_tls_creds_psk_unload(creds); +} + +static void +qcrypto_tls_creds_psk_prop_set_username(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + creds->username = g_strdup(value); +} + + +static char * +qcrypto_tls_creds_psk_prop_get_username(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QCryptoTLSCredsPSK *creds = QCRYPTO_TLS_CREDS_PSK(obj); + + return g_strdup(creds->username); +} + +static void +qcrypto_tls_creds_psk_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = qcrypto_tls_creds_psk_complete; + + object_class_property_add_bool(oc, "loaded", + qcrypto_tls_creds_psk_prop_get_loaded, + qcrypto_tls_creds_psk_prop_set_loaded, + NULL); + object_class_property_add_str(oc, "username", + qcrypto_tls_creds_psk_prop_get_username, + qcrypto_tls_creds_psk_prop_set_username, + NULL); +} + + +static const TypeInfo qcrypto_tls_creds_psk_info = { + .parent = TYPE_QCRYPTO_TLS_CREDS, + .name = TYPE_QCRYPTO_TLS_CREDS_PSK, + .instance_size = sizeof(QCryptoTLSCredsPSK), + .instance_finalize = qcrypto_tls_creds_psk_finalize, + .class_size = sizeof(QCryptoTLSCredsPSKClass), + .class_init = qcrypto_tls_creds_psk_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qcrypto_tls_creds_psk_register_types(void) +{ + type_register_static(&qcrypto_tls_creds_psk_info); +} + + +type_init(qcrypto_tls_creds_psk_register_types); diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index 50eb54f6bb..98ee0424e5 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "crypto/tlscredsx509.h" -#include "crypto/tlscredspriv.h" +#include "tlscredspriv.h" #include "crypto/secret.h" #include "qapi/error.h" #include "qom/object_interfaces.h" diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 96a02deb69..66a6fbe19c 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "crypto/tlssession.h" #include "crypto/tlscredsanon.h" +#include "crypto/tlscredspsk.h" #include "crypto/tlscredsx509.h" #include "qapi/error.h" #include "qemu/acl.h" @@ -88,6 +89,14 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) return session->readFunc(buf, len, session->opaque); } +#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" + +#if GNUTLS_VERSION_MAJOR >= 3 +#define TLS_ECDHE_PSK "+ECDHE-PSK:" +#else +#define TLS_ECDHE_PSK "" +#endif +#define TLS_PRIORITY_ADDITIONAL_PSK TLS_ECDHE_PSK "+DHE-PSK:+PSK" QCryptoTLSSession * qcrypto_tls_session_new(QCryptoTLSCreds *creds, @@ -135,9 +144,12 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, char *prio; if (creds->priority != NULL) { - prio = g_strdup_printf("%s:+ANON-DH", creds->priority); + prio = g_strdup_printf("%s:%s", + creds->priority, + TLS_PRIORITY_ADDITIONAL_ANON); } else { - prio = g_strdup(CONFIG_TLS_PRIORITY ":+ANON-DH"); + prio = g_strdup(CONFIG_TLS_PRIORITY ":" + TLS_PRIORITY_ADDITIONAL_ANON); } ret = gnutls_priority_set_direct(session->handle, prio, NULL); @@ -162,6 +174,42 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, gnutls_strerror(ret)); goto error; } + } else if (object_dynamic_cast(OBJECT(creds), + TYPE_QCRYPTO_TLS_CREDS_PSK)) { + QCryptoTLSCredsPSK *pcreds = QCRYPTO_TLS_CREDS_PSK(creds); + char *prio; + + if (creds->priority != NULL) { + prio = g_strdup_printf("%s:%s", + creds->priority, + TLS_PRIORITY_ADDITIONAL_PSK); + } else { + prio = g_strdup(CONFIG_TLS_PRIORITY ":" + TLS_PRIORITY_ADDITIONAL_PSK); + } + + ret = gnutls_priority_set_direct(session->handle, prio, NULL); + if (ret < 0) { + error_setg(errp, "Unable to set TLS session priority %s: %s", + prio, gnutls_strerror(ret)); + g_free(prio); + goto error; + } + g_free(prio); + if (creds->endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_PSK, + pcreds->data.server); + } else { + ret = gnutls_credentials_set(session->handle, + GNUTLS_CRD_PSK, + pcreds->data.client); + } + if (ret < 0) { + error_setg(errp, "Cannot set session credentials: %s", + gnutls_strerror(ret)); + goto error; + } } else if (object_dynamic_cast(OBJECT(creds), TYPE_QCRYPTO_TLS_CREDS_X509)) { QCryptoTLSCredsX509 *tcreds = QCRYPTO_TLS_CREDS_X509(creds); @@ -353,6 +401,10 @@ qcrypto_tls_session_check_credentials(QCryptoTLSSession *session, TYPE_QCRYPTO_TLS_CREDS_ANON)) { trace_qcrypto_tls_session_check_creds(session, "nop"); return 0; + } else if (object_dynamic_cast(OBJECT(session->creds), + TYPE_QCRYPTO_TLS_CREDS_PSK)) { + trace_qcrypto_tls_session_check_creds(session, "nop"); + return 0; } else if (object_dynamic_cast(OBJECT(session->creds), TYPE_QCRYPTO_TLS_CREDS_X509)) { if (session->creds->verifyPeer) { diff --git a/crypto/trace-events b/crypto/trace-events index e589990359..597389b73c 100644 --- a/crypto/trace-events +++ b/crypto/trace-events @@ -7,6 +7,9 @@ qcrypto_tls_creds_get_path(void *creds, const char *filename, const char *path) # crypto/tlscredsanon.c qcrypto_tls_creds_anon_load(void *creds, const char *dir) "TLS creds anon load creds=%p dir=%s" +# crypto/tlscredspsk.c +qcrypto_tls_creds_psk_load(void *creds, const char *dir) "TLS creds psk load creds=%p dir=%s" + # crypto/tlscredsx509.c qcrypto_tls_creds_x509_load(void *creds, const char *dir) "TLS creds x509 load creds=%p dir=%s" qcrypto_tls_creds_x509_check_basic_constraints(void *creds, const char *file, int status) "TLS creds x509 check basic constraints creds=%p file=%s status=%d" diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak index 9ddccf855e..6f790f061a 100644 --- a/default-configs/aarch64-softmmu.mak +++ b/default-configs/aarch64-softmmu.mak @@ -8,3 +8,4 @@ CONFIG_DDC=y CONFIG_DPCD=y CONFIG_XLNX_ZYNQMP=y CONFIG_XLNX_ZYNQMP_ARM=y +CONFIG_ARM_SMMUV3=y diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index dd29e741c2..834d45cfaf 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -16,6 +16,7 @@ CONFIG_TSC2005=y CONFIG_LM832X=y CONFIG_TMP105=y CONFIG_TMP421=y +CONFIG_PCA9552=y CONFIG_STELLARIS=y CONFIG_STELLARIS_INPUT=y CONFIG_STELLARIS_ENET=y @@ -41,6 +42,7 @@ CONFIG_USB=y CONFIG_USB_MUSB=y CONFIG_USB_EHCI_SYSBUS=y CONFIG_PLATFORM_BUS=y +CONFIG_VIRTIO_MMIO=y CONFIG_ARM11MPCORE=y CONFIG_A9MPCORE=y @@ -106,6 +108,7 @@ CONFIG_CMSDK_APB_UART=y CONFIG_MPS2_FPGAIO=y CONFIG_MPS2_SCC=y +CONFIG_TZ_MPC=y CONFIG_TZ_PPC=y CONFIG_IOTKIT=y CONFIG_IOTKIT_SECCTL=y diff --git a/default-configs/hppa-softmmu.mak b/default-configs/hppa-softmmu.mak index 013e5f046f..4badc0521e 100644 --- a/default-configs/hppa-softmmu.mak +++ b/default-configs/hppa-softmmu.mak @@ -4,8 +4,6 @@ CONFIG_SERIAL=y CONFIG_SERIAL_ISA=y CONFIG_ISA_BUS=y CONFIG_I8259=y -CONFIG_VIRTIO_PCI=$(CONFIG_PCI) -CONFIG_VIRTIO=y CONFIG_E1000_PCI=y CONFIG_IDE_ISA=y CONFIG_IDE_CMD646=y diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/mips-softmmu-common.mak index e31f046b3b..fae2347ee7 100644 --- a/default-configs/mips-softmmu-common.mak +++ b/default-configs/mips-softmmu-common.mak @@ -4,6 +4,7 @@ include pci.mak include sound.mak include usb.mak CONFIG_ESP=y +CONFIG_SCSI=y CONFIG_VGA_ISA=y CONFIG_VGA_ISA_MM=y CONFIG_VGA_CIRRUS=y diff --git a/default-configs/pci.mak b/default-configs/pci.mak index 35e7596949..de53d20ac6 100644 --- a/default-configs/pci.mak +++ b/default-configs/pci.mak @@ -2,7 +2,7 @@ CONFIG_PCI=y # For now, CONFIG_IDE_CORE requires ISA, so we enable it here CONFIG_ISA_BUS=y CONFIG_VIRTIO_PCI=y -CONFIG_VIRTIO=y +include virtio.mak CONFIG_USB_UHCI=y CONFIG_USB_OHCI=y CONFIG_USB_EHCI=y @@ -15,6 +15,7 @@ CONFIG_PCNET_COMMON=y CONFIG_AC97=y CONFIG_HDA=y CONFIG_ES1370=y +CONFIG_SCSI=y CONFIG_LSI_SCSI_PCI=y CONFIG_VMW_PVSCSI_SCSI_PCI=y CONFIG_MEGASAS_SCSI_PCI=y @@ -45,5 +46,3 @@ CONFIG_VGA=y CONFIG_VGA_PCI=y CONFIG_IVSHMEM_DEVICE=$(CONFIG_IVSHMEM) CONFIG_ROCKER=y -CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) -CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 4d7be45ac5..3181bbf163 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -22,21 +22,28 @@ CONFIG_OPENPIC_KVM=$(call land,$(CONFIG_E500),$(CONFIG_KVM)) CONFIG_PLATFORM_BUS=y CONFIG_ETSEC=y # For Sam460ex +CONFIG_SAM460EX=y CONFIG_USB_EHCI_SYSBUS=y CONFIG_SM501=y +CONFIG_DDC=y CONFIG_IDE_SII3112=y CONFIG_I2C=y +CONFIG_BITBANG_I2C=y +CONFIG_M41T80=y +CONFIG_VGA_CIRRUS=y # For Macs CONFIG_MAC=y CONFIG_ESCC=y CONFIG_MACIO=y +CONFIG_MACIO_GPIO=y CONFIG_SUNGEM=y CONFIG_MOS6522=y CONFIG_CUDA=y CONFIG_ADB=y CONFIG_MAC_NVRAM=y CONFIG_MAC_DBDMA=y +CONFIG_MAC_PMU=y CONFIG_HEATHROW_PIC=y CONFIG_GRACKLE_PCI=y CONFIG_UNIN_PCI=y diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak index 67d18b2e0e..ac44f150c6 100644 --- a/default-configs/ppcemb-softmmu.mak +++ b/default-configs/ppcemb-softmmu.mak @@ -17,5 +17,7 @@ CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y CONFIG_USB_EHCI_SYSBUS=y CONFIG_SM501=y +CONFIG_DDC=y CONFIG_IDE_SII3112=y CONFIG_I2C=y +CONFIG_BITBANG_I2C=y diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak index f9e742120c..7937c69e22 100644 --- a/default-configs/riscv32-softmmu.mak +++ b/default-configs/riscv32-softmmu.mak @@ -1,4 +1,7 @@ # Default configuration for riscv-softmmu CONFIG_SERIAL=y -CONFIG_VIRTIO=y +CONFIG_VIRTIO_MMIO=y +include virtio.mak + +CONFIG_CADENCE=y diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak index f9e742120c..7937c69e22 100644 --- a/default-configs/riscv64-softmmu.mak +++ b/default-configs/riscv64-softmmu.mak @@ -1,4 +1,7 @@ # Default configuration for riscv-softmmu CONFIG_SERIAL=y -CONFIG_VIRTIO=y +CONFIG_VIRTIO_MMIO=y +include virtio.mak + +CONFIG_CADENCE=y diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index 2f4bfe73b4..d6b67d50f0 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -1,8 +1,6 @@ CONFIG_PCI=y CONFIG_VIRTIO_PCI=$(CONFIG_PCI) -CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) -CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) -CONFIG_VIRTIO=y +include virtio.mak CONFIG_SCLPCONSOLE=y CONFIG_TERMINAL3270=y CONFIG_S390_FLIC=y diff --git a/default-configs/sh4-softmmu.mak b/default-configs/sh4-softmmu.mak index 546d855088..caeccd55be 100644 --- a/default-configs/sh4-softmmu.mak +++ b/default-configs/sh4-softmmu.mak @@ -9,6 +9,8 @@ CONFIG_PFLASH_CFI02=y CONFIG_SH4=y CONFIG_IDE_MMIO=y CONFIG_SM501=y +CONFIG_I2C=y +CONFIG_DDC=y CONFIG_ISA_TESTDEV=y CONFIG_I82378=y CONFIG_I8259=y diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/sh4eb-softmmu.mak index 2d3fd49663..53b9cd7b5a 100644 --- a/default-configs/sh4eb-softmmu.mak +++ b/default-configs/sh4eb-softmmu.mak @@ -9,6 +9,8 @@ CONFIG_PFLASH_CFI02=y CONFIG_SH4=y CONFIG_IDE_MMIO=y CONFIG_SM501=y +CONFIG_I2C=y +CONFIG_DDC=y CONFIG_ISA_TESTDEV=y CONFIG_I82378=y CONFIG_I8259=y diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak index 004b0f4e77..12f97eeb20 100644 --- a/default-configs/sparc-softmmu.mak +++ b/default-configs/sparc-softmmu.mak @@ -2,6 +2,7 @@ CONFIG_ISA_BUS=y CONFIG_ECC=y +CONFIG_SCSI=y CONFIG_ESP=y CONFIG_ESCC=y CONFIG_M48T59=y diff --git a/default-configs/usb.mak b/default-configs/usb.mak index f4b85684f0..e42cfeabbe 100644 --- a/default-configs/usb.mak +++ b/default-configs/usb.mak @@ -3,6 +3,7 @@ CONFIG_USB_TABLET_WACOM=y CONFIG_USB_STORAGE_BOT=y CONFIG_USB_STORAGE_UAS=y CONFIG_USB_STORAGE_MTP=y +CONFIG_SCSI=y CONFIG_USB_SMARTCARD=y CONFIG_USB_AUDIO=y CONFIG_USB_SERIAL=y diff --git a/default-configs/virtio.mak b/default-configs/virtio.mak new file mode 100644 index 0000000000..1304849018 --- /dev/null +++ b/default-configs/virtio.mak @@ -0,0 +1,14 @@ +CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) +CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) +CONFIG_VIRTIO=y +CONFIG_VIRTIO_9P=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_BLK=y +CONFIG_VIRTIO_CRYPTO=y +CONFIG_VIRTIO_GPU=y +CONFIG_VIRTIO_INPUT=y +CONFIG_VIRTIO_NET=y +CONFIG_VIRTIO_RNG=y +CONFIG_SCSI=y +CONFIG_VIRTIO_SCSI=y +CONFIG_VIRTIO_SERIAL=y diff --git a/device_tree.c b/device_tree.c index 19458b32bf..6d9c9726f6 100644 --- a/device_tree.c +++ b/device_tree.c @@ -29,7 +29,7 @@ #include -#define FDT_MAX_SIZE 0x10000 +#define FDT_MAX_SIZE 0x100000 void *create_device_tree(int *sizep) { @@ -140,15 +140,16 @@ static void read_fstree(void *fdt, const char *dirname) const char *parent_node; if (strstr(dirname, root_dir) != dirname) { - error_setg(&error_fatal, "%s: %s must be searched within %s", - __func__, dirname, root_dir); + error_report("%s: %s must be searched within %s", + __func__, dirname, root_dir); + exit(1); } parent_node = &dirname[strlen(SYSFS_DT_BASEDIR)]; d = opendir(dirname); if (!d) { - error_setg(&error_fatal, "%s cannot open %s", __func__, dirname); - return; + error_report("%s cannot open %s", __func__, dirname); + exit(1); } while ((de = readdir(d)) != NULL) { @@ -162,7 +163,8 @@ static void read_fstree(void *fdt, const char *dirname) tmpnam = g_strdup_printf("%s/%s", dirname, de->d_name); if (lstat(tmpnam, &st) < 0) { - error_setg(&error_fatal, "%s cannot lstat %s", __func__, tmpnam); + error_report("%s cannot lstat %s", __func__, tmpnam); + exit(1); } if (S_ISREG(st.st_mode)) { @@ -170,8 +172,9 @@ static void read_fstree(void *fdt, const char *dirname) gsize len; if (!g_file_get_contents(tmpnam, &val, &len, NULL)) { - error_setg(&error_fatal, "%s not able to extract info from %s", - __func__, tmpnam); + error_report("%s not able to extract info from %s", + __func__, tmpnam); + exit(1); } if (strlen(parent_node) > 0) { @@ -206,9 +209,9 @@ void *load_device_tree_from_sysfs(void) host_fdt = create_device_tree(&host_fdt_size); read_fstree(host_fdt, SYSFS_DT_BASEDIR); if (fdt_check_header(host_fdt)) { - error_setg(&error_fatal, - "%s host device tree extracted into memory is invalid", - __func__); + error_report("%s host device tree extracted into memory is invalid", + __func__); + exit(1); } return host_fdt; } @@ -229,6 +232,61 @@ static int findnode_nofail(void *fdt, const char *node_path) return offset; } +char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp) +{ + char *prefix = g_strdup_printf("%s@", name); + unsigned int path_len = 16, n = 0; + GSList *path_list = NULL, *iter; + const char *iter_name; + int offset, len, ret; + char **path_array; + + offset = fdt_next_node(fdt, -1, NULL); + + while (offset >= 0) { + iter_name = fdt_get_name(fdt, offset, &len); + if (!iter_name) { + offset = len; + break; + } + if (!strcmp(iter_name, name) || g_str_has_prefix(iter_name, prefix)) { + char *path; + + path = g_malloc(path_len); + while ((ret = fdt_get_path(fdt, offset, path, path_len)) + == -FDT_ERR_NOSPACE) { + path_len += 16; + path = g_realloc(path, path_len); + } + path_list = g_slist_prepend(path_list, path); + n++; + } + offset = fdt_next_node(fdt, offset, NULL); + } + g_free(prefix); + + if (offset < 0 && offset != -FDT_ERR_NOTFOUND) { + error_setg(errp, "%s: abort parsing dt for %s node units: %s", + __func__, name, fdt_strerror(offset)); + for (iter = path_list; iter; iter = iter->next) { + g_free(iter->data); + } + g_slist_free(path_list); + return NULL; + } + + path_array = g_new(char *, n + 1); + path_array[n--] = NULL; + + for (iter = path_list; iter; iter = iter->next) { + path_array[n--] = iter->data; + } + + g_slist_free(path_list); + + return path_array; +} + char **qemu_fdt_node_path(void *fdt, const char *name, char *compat, Error **errp) { diff --git a/disas/m68k.c b/disas/m68k.c index 61b689ef3e..a687df437c 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -2017,6 +2017,20 @@ print_insn_m68k (bfd_vma memaddr, disassemble_info *info) } } + /* Don't match FPU insns with non-default coprocessor ID. */ + if (*d == '\0') + { + for (d = opc->args; *d; d += 2) + { + if (d[0] == 'I') + { + val = fetch_arg (buffer, 'd', 3, info); + if (val != 1) + break; + } + } + } + if (*d == '\0') if ((val = match_insn_m68k (memaddr, info, opc, & priv))) return val; diff --git a/disas/riscv.c b/disas/riscv.c index 74ad16eacd..7fd1019623 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -1470,8 +1470,9 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa) if (isa == rv128) { op = rv_op_c_sqsp; } else { - op = rv_op_c_fsdsp; break; + op = rv_op_c_fsdsp; } + break; case 6: op = rv_op_c_swsp; break; case 7: if (isa == rv32) { @@ -2769,25 +2770,6 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec) char tmp[64]; const char *fmt; - if (dec->op == rv_op_illegal) { - size_t len = inst_length(dec->inst); - switch (len) { - case 2: - snprintf(buf, buflen, "(0x%04" PRIx64 ")", dec->inst); - break; - case 4: - snprintf(buf, buflen, "(0x%08" PRIx64 ")", dec->inst); - break; - case 6: - snprintf(buf, buflen, "(0x%012" PRIx64 ")", dec->inst); - break; - default: - snprintf(buf, buflen, "(0x%016" PRIx64 ")", dec->inst); - break; - } - return; - } - fmt = opcode_data[dec->op].format; while (*fmt) { switch (*fmt) { @@ -3004,6 +2986,11 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst) format_inst(buf, buflen, 16, &dec); } +#define INST_FMT_2 "%04" PRIx64 " " +#define INST_FMT_4 "%08" PRIx64 " " +#define INST_FMT_6 "%012" PRIx64 " " +#define INST_FMT_8 "%016" PRIx64 " " + static int print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) { @@ -3031,6 +3018,21 @@ print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa) } } + switch (len) { + case 2: + (*info->fprintf_func)(info->stream, INST_FMT_2, inst); + break; + case 4: + (*info->fprintf_func)(info->stream, INST_FMT_4, inst); + break; + case 6: + (*info->fprintf_func)(info->stream, INST_FMT_6, inst); + break; + default: + (*info->fprintf_func)(info->stream, INST_FMT_8, inst); + break; + } + disasm_inst(buf, sizeof(buf), isa, memaddr, inst); (*info->fprintf_func)(info->stream, "%s", buf); diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt index e289be2f41..d7c7dcda8f 100644 --- a/docs/COLO-FT.txt +++ b/docs/COLO-FT.txt @@ -113,16 +113,16 @@ by using 'x-colo-lost-heartbeat' command. == Test procedure == 1. Startup qemu Primary: -# qemu-kvm -enable-kvm -m 2048 -smp 2 -qmp stdio -vnc :7 -name primary \ - -device piix3-usb-uhci \ +# qemu-system-x86_64 -accel kvm -m 2048 -smp 2 -qmp stdio -name primary \ + -device piix3-usb-uhci -vnc :7 \ -device usb-tablet -netdev tap,id=hn0,vhost=off \ -device virtio-net-pci,id=net-pci0,netdev=hn0 \ -drive if=virtio,id=primary-disk0,driver=quorum,read-pattern=fifo,vote-threshold=1,\ children.0.file.filename=1.raw,\ children.0.driver=raw -S Secondary: -# qemu-kvm -enable-kvm -m 2048 -smp 2 -qmp stdio -vnc :7 -name secondary \ - -device piix3-usb-uhci \ +# qemu-system-x86_64 -accel kvm -m 2048 -smp 2 -qmp stdio -name secondary \ + -device piix3-usb-uhci -vnc :7 \ -device usb-tablet -netdev tap,id=hn0,vhost=off \ -device virtio-net-pci,id=net-pci0,netdev=hn0 \ -drive if=none,id=secondary-disk0,file.filename=1.raw,driver=raw,node-name=node0 \ diff --git a/docs/can.txt b/docs/can.txt index a357105762..7ba23b259a 100644 --- a/docs/can.txt +++ b/docs/can.txt @@ -52,7 +52,7 @@ The ''kvaser_pci'' board/device model is compatible with and has been tested wit The tested setup was Linux 4.9 kernel on the host and guest side. Example for qemu-system-x86_64: - qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \ + 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 \ @@ -104,4 +104,4 @@ Links to other resources Slides http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can-slides.pdf (5) Linux SocketCAN utilities - https://github.com/linux-can/can-utils/ \ No newline at end of file + https://github.com/linux-can/can-utils/ diff --git a/docs/colo-proxy.txt b/docs/colo-proxy.txt index 8b726ea094..1f8e4b4e77 100644 --- a/docs/colo-proxy.txt +++ b/docs/colo-proxy.txt @@ -145,7 +145,7 @@ and redirect indev's packet to filter. COLO-compare, we do packet comparing job. Packets coming from the primary char indev will be sent to outdev. Packets coming from the secondary char dev will be dropped after comparing. -COLO-comapre need two input chardev and one output chardev: +COLO-compare needs two input chardevs and one output chardev: primary_in=chardev1-id (source: primary send packet) secondary_in=chardev2-id (source: secondary send packet) outdev=chardev3-id diff --git a/docs/config/mach-virt-graphical.cfg b/docs/config/mach-virt-graphical.cfg index 0fdf6846dd..d6d31b17f5 100644 --- a/docs/config/mach-virt-graphical.cfg +++ b/docs/config/mach-virt-graphical.cfg @@ -185,7 +185,7 @@ # attached to it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/config/mach-virt-serial.cfg b/docs/config/mach-virt-serial.cfg index aee9f1c5a1..18a7c83731 100644 --- a/docs/config/mach-virt-serial.cfg +++ b/docs/config/mach-virt-serial.cfg @@ -191,7 +191,7 @@ # attached to it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/config/q35-emulated.cfg b/docs/config/q35-emulated.cfg index c6416d6545..99ac918e78 100644 --- a/docs/config/q35-emulated.cfg +++ b/docs/config/q35-emulated.cfg @@ -130,7 +130,7 @@ # it to that controller so that the guest can use it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/config/q35-virtio-graphical.cfg b/docs/config/q35-virtio-graphical.cfg index 28bde2fc57..4207f11e4f 100644 --- a/docs/config/q35-virtio-graphical.cfg +++ b/docs/config/q35-virtio-graphical.cfg @@ -136,7 +136,7 @@ # attached to it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/config/q35-virtio-serial.cfg b/docs/config/q35-virtio-serial.cfg index c33c9cc07a..d2830aec5e 100644 --- a/docs/config/q35-virtio-serial.cfg +++ b/docs/config/q35-virtio-serial.cfg @@ -141,7 +141,7 @@ # attached to it. # # We also create an optical disk, mostly for installation -# purposes: once the guest OS has been succesfully +# purposes: once the guest OS has been successfully # installed, the guest will no longer boot from optical # media. If you don't want, or no longer want, to have an # optical disk in the guest you can safely comment out diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index 6a990cc243..57d8c524bf 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -53,9 +53,24 @@ The ``_{endian}`` infix is omitted for target-endian accesses. The target endian accessors are only available to source files which are built per-target. +There are also functions which take the size as an argument: + +load: ``ldn{endian}_p(ptr, sz)`` + +which performs an unsigned load of ``sz`` bytes from ``ptr`` +as an ``{endian}`` order value and returns it in a uint64_t. + +store: ``stn{endian}_p(ptr, sz, val)`` + +which stores ``val`` to ``ptr`` as an ``{endian}`` order value +of size ``sz`` bytes. + + Regexes for git grep - ``\`` - ``\`` + - ``\`` + - ``\`` ``cpu_{ld,st}_*`` ~~~~~~~~~~~~~~~~~ diff --git a/docs/devel/memory.txt b/docs/devel/memory.txt index 8ed810f8b9..c1dee1252c 100644 --- a/docs/devel/memory.txt +++ b/docs/devel/memory.txt @@ -77,9 +77,8 @@ MemoryRegion): - reservation region: a reservation region is primarily for debugging. It claims I/O space that is not supposed to be handled by QEMU itself. The typical use is to track parts of the address space which will be - handled by the host kernel when KVM is enabled. - You initialize these with memory_region_init_reservation(), or by - passing a NULL callback parameter to memory_region_init_io(). + handled by the host kernel when KVM is enabled. You initialize these + by passing a NULL callback parameter to memory_region_init_io(). It is valid to add subregions to a region which is not a pure container (that is, to an MMIO, RAM or ROM region). This means that the region diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index e32b087f6e..6ed3fce061 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -28,28 +28,28 @@ the guest to be stopped. Typically the time that the guest is unresponsive during live migration is the low hundred of milliseconds (notice that this depends on a lot of things). -Types of migration -================== +Transports +========== -Now that we have talked about live migration, there are several ways -to do migration: +The migration stream is normally just a byte stream that can be passed +over any transport. - tcp migration: do the migration using tcp sockets - unix migration: do the migration using unix sockets - exec migration: do the migration using the stdin/stdout through a process. -- fd migration: do the migration using an file descriptor that is +- fd migration: do the migration using a file descriptor that is passed to QEMU. QEMU doesn't care how this file descriptor is opened. -All these four migration protocols use the same infrastructure to +In addition, support is included for migration using RDMA, which +transports the page data using ``RDMA``, where the hardware takes care of +transporting the pages, and the load on the CPU is much lower. While the +internals of RDMA migration are a bit different, this isn't really visible +outside the RAM migration code. + +All these migration protocols use the same infrastructure to save/restore state devices. This infrastructure is shared with the savevm/loadvm functionality. -State Live Migration -==================== - -This is used for RAM and block devices. It is not yet ported to vmstate. - - Common infrastructure ===================== @@ -57,60 +57,75 @@ The files, sockets or fd's that carry the migration stream are abstracted by the ``QEMUFile`` type (see `migration/qemu-file.h`). In most cases this is connected to a subtype of ``QIOChannel`` (see `io/`). + Saving the state of one device ============================== -The state of a device is saved using intermediate buffers. There are -some helper functions to assist this saving. +For most devices, the state is saved in a single call to the migration +infrastructure; these are *non-iterative* devices. The data for these +devices is sent at the end of precopy migration, when the CPUs are paused. +There are also *iterative* devices, which contain a very large amount of +data (e.g. RAM or large tables). See the iterative device section below. -There is a new concept that we have to explain here: device state -version. When we migrate a device, we save/load the state as a series -of fields. Some times, due to bugs or new functionality, we need to -change the state to store more/different information. We use the -version to identify each time that we do a change. Each version is -associated with a series of fields saved. The `save_state` always saves -the state as the newer version. But `load_state` sometimes is able to -load state from an older version. +General advice for device developers +------------------------------------ -Legacy way ----------- +- The migration state saved should reflect the device being modelled rather + than the way your implementation works. That way if you change the implementation + later the migration stream will stay compatible. That model may include + internal state that's not directly visible in a register. -This way is going to disappear as soon as all current users are ported to VMSTATE. +- When saving a migration stream the device code may walk and check + the state of the device. These checks might fail in various ways (e.g. + discovering internal state is corrupt or that the guest has done something bad). + Consider carefully before asserting/aborting at this point, since the + normal response from users is that *migration broke their VM* since it had + apparently been running fine until then. In these error cases, the device + should log a message indicating the cause of error, and should consider + putting the device into an error state, allowing the rest of the VM to + continue execution. -Each device has to register two functions, one to save the state and -another to load the state back. +- The migration might happen at an inconvenient point, + e.g. right in the middle of the guest reprogramming the device, during + guest reboot or shutdown or while the device is waiting for external IO. + It's strongly preferred that migrations do not fail in this situation, + since in the cloud environment migrations might happen automatically to + VMs that the administrator doesn't directly control. -.. code:: c +- If you do need to fail a migration, ensure that sufficient information + is logged to identify what went wrong. - int register_savevm(DeviceState *dev, - const char *idstr, - int instance_id, - int version_id, - SaveStateHandler *save_state, - LoadStateHandler *load_state, - void *opaque); +- The destination should treat an incoming migration stream as hostile + (which we do to varying degrees in the existing code). Check that offsets + into buffers and the like can't cause overruns. Fail the incoming migration + in the case of a corrupted stream like this. - typedef void SaveStateHandler(QEMUFile *f, void *opaque); - typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); +- Take care with internal device state or behaviour that might become + migration version dependent. For example, the order of PCI capabilities + is required to stay constant across migration. Another example would + be that a special case handled by subsections (see below) might become + much more common if a default behaviour is changed. -The important functions for the device state format are the `save_state` -and `load_state`. Notice that `load_state` receives a version_id -parameter to know what state format is receiving. `save_state` doesn't -have a version_id parameter because it always uses the latest version. +- The state of the source should not be changed or destroyed by the + outgoing migration. Migrations timing out or being failed by + higher levels of management, or failures of the destination host are + not unusual, and in that case the VM is restarted on the source. + Note that the management layer can validly revert the migration + even though the QEMU level of migration has succeeded as long as it + does it before starting execution on the destination. + +- Buses and devices should be able to explicitly specify addresses when + instantiated, and management tools should use those. For example, + when hot adding USB devices it's important to specify the ports + and addresses, since implicit ordering based on the command line order + may be different on the destination. This can result in the + device state being loaded into the wrong device. VMState ------- -The legacy way of saving/loading state of the device had the problem -that we have to maintain two functions in sync. If we did one change -in one of them and not in the other, we would get a failed migration. - -VMState changed the way that state is saved/loaded. Instead of using -a function to save the state and another to load it, it was changed to -a declarative way of what the state consisted of. Now VMState is able -to interpret that definition to be able to load/save the state. As -the state is declared only once, it can't go out of sync in the -save/load functions. +Most device data can be described using the ``VMSTATE`` macros (mostly defined +in ``include/migration/vmstate.h``). An example (from hw/input/pckbd.c) @@ -137,103 +152,99 @@ We registered this with: vmstate_register(NULL, 0, &vmstate_kbd, s); -Note: talk about how vmstate <-> qdev interact, and what the instance ids mean. +For devices that are `qdev` based, we can register the device in the class +init function: -You can search for ``VMSTATE_*`` macros for lots of types used in QEMU in -include/hw/hw.h. +.. code:: c -More about versions -------------------- + dc->vmsd = &vmstate_kbd_isa; -Version numbers are intended for major incompatible changes to the -migration of a device, and using them breaks backwards-migration -compatibility; in general most changes can be made by adding Subsections -(see below) or _TEST macros (see below) which won't break compatibility. +The VMState macros take care of ensuring that the device data section +is formatted portably (normally big endian) and make some compile time checks +against the types of the fields in the structures. -You can see that there are several version fields: +VMState macros can include other VMStateDescriptions to store substructures +(see ``VMSTATE_STRUCT_``), arrays (``VMSTATE_ARRAY_``) and variable length +arrays (``VMSTATE_VARRAY_``). Various other macros exist for special +cases. -- `version_id`: the maximum version_id supported by VMState for that device. -- `minimum_version_id`: the minimum version_id that VMState is able to understand - for that device. -- `minimum_version_id_old`: For devices that were not able to port to vmstate, we can - assign a function that knows how to read this old state. This field is - ignored if there is no `load_state_old` handler. +Note that the format on the wire is still very raw; i.e. a VMSTATE_UINT32 +ends up with a 4 byte bigendian representation on the wire; in the future +it might be possible to use a more structured format. -So, VMState is able to read versions from minimum_version_id to -version_id. And the function ``load_state_old()`` (if present) is able to -load state from minimum_version_id_old to minimum_version_id. This -function is deprecated and will be removed when no more users are left. +Legacy way +---------- -Saving state will always create a section with the 'version_id' value -and thus can't be loaded by any older QEMU. +This way is going to disappear as soon as all current users are ported to VMSTATE; +although converting existing code can be tricky, and thus 'soon' is relative. -Massaging functions -------------------- +Each device has to register two functions, one to save the state and +another to load the state back. -Sometimes, it is not enough to be able to save the state directly -from one structure, we need to fill the correct values there. One -example is when we are using kvm. Before saving the cpu state, we -need to ask kvm to copy to QEMU the state that it is using. And the -opposite when we are loading the state, we need a way to tell kvm to -load the state for the cpu that we have just loaded from the QEMUFile. +.. code:: c -The functions to do that are inside a vmstate definition, and are called: + int register_savevm_live(DeviceState *dev, + const char *idstr, + int instance_id, + int version_id, + SaveVMHandlers *ops, + void *opaque); -- ``int (*pre_load)(void *opaque);`` +Two functions in the ``ops`` structure are the `save_state` +and `load_state` functions. Notice that `load_state` receives a version_id +parameter to know what state format is receiving. `save_state` doesn't +have a version_id parameter because it always uses the latest version. - This function is called before we load the state of one device. +Note that because the VMState macros still save the data in a raw +format, in many cases it's possible to replace legacy code +with a carefully constructed VMState description that matches the +byte layout of the existing code. -- ``int (*post_load)(void *opaque, int version_id);`` +Changing migration data structures +---------------------------------- - This function is called after we load the state of one device. - -- ``int (*pre_save)(void *opaque);`` - - This function is called before we save the state of one device. - -Example: You can look at hpet.c, that uses the three function to -massage the state that is transferred. - -If you use memory API functions that update memory layout outside -initialization (i.e., in response to a guest action), this is a strong -indication that you need to call these functions in a `post_load` callback. -Examples of such memory API functions are: - - - memory_region_add_subregion() - - memory_region_del_subregion() - - memory_region_set_readonly() - - memory_region_set_enabled() - - memory_region_set_address() - - memory_region_set_alias_offset() +When we migrate a device, we save/load the state as a series +of fields. Sometimes, due to bugs or new functionality, we need to +change the state to store more/different information. Changing the migration +state saved for a device can break migration compatibility unless +care is taken to use the appropriate techniques. In general QEMU tries +to maintain forward migration compatibility (i.e. migrating from +QEMU n->n+1) and there are users who benefit from backward compatibility +as well. Subsections ----------- -The use of version_id allows to be able to migrate from older versions -to newer versions of a device. But not the other way around. This -makes very complicated to fix bugs in stable branches. If we need to -add anything to the state to fix a bug, we have to disable migration -to older versions that don't have that bug-fix (i.e. a new field). +The most common structure change is adding new data, e.g. when adding +a newer form of device, or adding that state that you previously +forgot to migrate. This is best solved using a subsection. -But sometimes, that bug-fix is only needed sometimes, not always. For -instance, if the device is in the middle of a DMA operation, it is -using a specific functionality, .... - -It is impossible to create a way to make migration from any version to -any other version to work. But we can do better than only allowing -migration from older versions to newer ones. For that fields that are -only needed sometimes, we add the idea of subsections. A subsection -is "like" a device vmstate, but with a particularity, it has a Boolean -function that tells if that values are needed to be sent or not. If -this functions returns false, the subsection is not sent. +A subsection is "like" a device vmstate, but with a particularity, it +has a Boolean function that tells if that values are needed to be sent +or not. If this functions returns false, the subsection is not sent. +Subsections have a unique name, that is looked for on the receiving +side. On the receiving side, if we found a subsection for a device that we don't understand, we just fail the migration. If we understand all -the subsections, then we load the state with success. +the subsections, then we load the state with success. There's no check +that a subsection is loaded, so a newer QEMU that knows about a subsection +can (with care) load a stream from an older QEMU that didn't send +the subsection. + +If the new data is only needed in a rare case, then the subsection +can be made conditional on that case and the migration will still +succeed to older QEMUs in most cases. This is OK for data that's +critical, but in some use cases it's preferred that the migration +should succeed even with the data missing. To support this the +subsection can be connected to a device property and from there +to a versioned machine type. One important note is that the post_load() function is called "after" loading all subsections, because a newer subsection could change same -value that it uses. +value that it uses. A flag, and the combination of pre_load and post_load +can be used to detect whether a subsection was loaded, and to +fall back on default behaviour when the subsection isn't present. Example: @@ -288,9 +299,13 @@ save/send this state when we are in the middle of a pio operation not enabled, the values on that fields are garbage and don't need to be sent. +Connecting subsections to properties +------------------------------------ + Using a condition function that checks a 'property' to determine whether -to send a subsection allows backwards migration compatibility when -new subsections are added. +to send a subsection allows backward migration compatibility when +new subsections are added, especially when combined with versioned +machine types. For example: @@ -305,21 +320,7 @@ For example: Now that subsection will not be generated when using an older machine type and the migration stream will be accepted by older -QEMU versions. pre-load functions can be used to initialise state -on the newer version so that they default to suitable values -when loading streams created by older QEMU versions that do not -generate the subsection. - -In some cases subsections are added for data that had been accidentally -omitted by earlier versions; if the missing data causes the migration -process to succeed but the guest to behave badly then it may be better -to send the subsection and cause the migration to explicitly fail -with the unknown subsection error. If the bad behaviour only happens -with certain data values, making the subsection conditional on -the data value (rather than the machine type) allows migrations to succeed -in most cases. In general the preference is to tie the subsection to -the machine type, and allow reliable migrations, unless the behaviour -from omission of the subsection is really bad. +QEMU versions. Not sending existing elements ----------------------------- @@ -328,9 +329,13 @@ Sometimes members of the VMState are no longer needed: - removing them will break migration compatibility - - making them version dependent and bumping the version will break backwards migration compatibility. + - making them version dependent and bumping the version will break backward migration + compatibility. -The best way is to: +Adding a dummy field into the migration stream is normally the best way to preserve +compatibility. + +If the field really does need to be removed then: a) Add a new property/compatibility/function in the same way for subsections above. b) replace the VMSTATE macro with the _TEST version of the macro, e.g.: @@ -342,18 +347,208 @@ The best way is to: ``VMSTATE_UINT32_TEST(foo, barstruct, pre_version_baz)`` Sometime in the future when we no longer care about the ancient versions these can be killed off. + Note that for backward compatibility it's important to fill in the structure with + data that the destination will understand. + +Any difference in the predicates on the source and destination will end up +with different fields being enabled and data being loaded into the wrong +fields; for this reason conditional fields like this are very fragile. + +Versions +-------- + +Version numbers are intended for major incompatible changes to the +migration of a device, and using them breaks backward-migration +compatibility; in general most changes can be made by adding Subsections +(see above) or _TEST macros (see above) which won't break compatibility. + +Each version is associated with a series of fields saved. The `save_state` always saves +the state as the newer version. But `load_state` sometimes is able to +load state from an older version. + +You can see that there are several version fields: + +- `version_id`: the maximum version_id supported by VMState for that device. +- `minimum_version_id`: the minimum version_id that VMState is able to understand + for that device. +- `minimum_version_id_old`: For devices that were not able to port to vmstate, we can + assign a function that knows how to read this old state. This field is + ignored if there is no `load_state_old` handler. + +VMState is able to read versions from minimum_version_id to +version_id. And the function ``load_state_old()`` (if present) is able to +load state from minimum_version_id_old to minimum_version_id. This +function is deprecated and will be removed when no more users are left. + +There are *_V* forms of many ``VMSTATE_`` macros to load fields for version dependent fields, +e.g. + +.. code:: c + + VMSTATE_UINT16_V(ip_id, Slirp, 2), + +only loads that field for versions 2 and newer. + +Saving state will always create a section with the 'version_id' value +and thus can't be loaded by any older QEMU. + +Massaging functions +------------------- + +Sometimes, it is not enough to be able to save the state directly +from one structure, we need to fill the correct values there. One +example is when we are using kvm. Before saving the cpu state, we +need to ask kvm to copy to QEMU the state that it is using. And the +opposite when we are loading the state, we need a way to tell kvm to +load the state for the cpu that we have just loaded from the QEMUFile. + +The functions to do that are inside a vmstate definition, and are called: + +- ``int (*pre_load)(void *opaque);`` + + This function is called before we load the state of one device. + +- ``int (*post_load)(void *opaque, int version_id);`` + + This function is called after we load the state of one device. + +- ``int (*pre_save)(void *opaque);`` + + This function is called before we save the state of one device. + +Example: You can look at hpet.c, that uses the three function to +massage the state that is transferred. + +The ``VMSTATE_WITH_TMP`` macro may be useful when the migration +data doesn't match the stored device data well; it allows an +intermediate temporary structure to be populated with migration +data and then transferred to the main structure. + +If you use memory API functions that update memory layout outside +initialization (i.e., in response to a guest action), this is a strong +indication that you need to call these functions in a `post_load` callback. +Examples of such memory API functions are: + + - memory_region_add_subregion() + - memory_region_del_subregion() + - memory_region_set_readonly() + - memory_region_set_enabled() + - memory_region_set_address() + - memory_region_set_alias_offset() + +Iterative device migration +-------------------------- + +Some devices, such as RAM, Block storage or certain platform devices, +have large amounts of data that would mean that the CPUs would be +paused for too long if they were sent in one section. For these +devices an *iterative* approach is taken. + +The iterative devices generally don't use VMState macros +(although it may be possible in some cases) and instead use +qemu_put_*/qemu_get_* macros to read/write data to the stream. Specialist +versions exist for high bandwidth IO. + + +An iterative device must provide: + + - A ``save_setup`` function that initialises the data structures and + transmits a first section containing information on the device. In the + case of RAM this transmits a list of RAMBlocks and sizes. + + - A ``load_setup`` function that initialises the data structures on the + destination. + + - A ``save_live_pending`` function that is called repeatedly and must + indicate how much more data the iterative data must save. The core + migration code will use this to determine when to pause the CPUs + and complete the migration. + + - A ``save_live_iterate`` function (called after ``save_live_pending`` + when there is significant data still to be sent). It should send + a chunk of data until the point that stream bandwidth limits tell it + to stop. Each call generates one section. + + - A ``save_live_complete_precopy`` function that must transmit the + last section for the device containing any remaining data. + + - A ``load_state`` function used to load sections generated by + any of the save functions that generate sections. + + - ``cleanup`` functions for both save and load that are called + at the end of migration. + +Note that the contents of the sections for iterative migration tend +to be open-coded by the devices; care should be taken in parsing +the results and structuring the stream to make them easy to validate. + +Device ordering +--------------- + +There are cases in which the ordering of device loading matters; for +example in some systems where a device may assert an interrupt during loading, +if the interrupt controller is loaded later then it might lose the state. + +Some ordering is implicitly provided by the order in which the machine +definition creates devices, however this is somewhat fragile. + +The ``MigrationPriority`` enum provides a means of explicitly enforcing +ordering. Numerically higher priorities are loaded earlier. +The priority is set by setting the ``priority`` field of the top level +``VMStateDescription`` for the device. + +Stream structure +================ + +The stream tries to be word and endian agnostic, allowing migration between hosts +of different characteristics running the same VM. + + - Header + + - Magic + - Version + - VM configuration section + + - Machine type + - Target page bits + - List of sections + Each section contains a device, or one iteration of a device save. + + - section type + - section id + - ID string (First section of each device) + - instance id (First section of each device) + - version id (First section of each device) + - + - Footer mark + - EOF mark + - VM Description structure + Consisting of a JSON description of the contents for analysis only + +The ``device data`` in each section consists of the data produced +by the code described above. For non-iterative devices they have a single +section; iterative devices have an initial and last section and a set +of parts in between. +Note that there is very little checking by the common code of the integrity +of the ``device data`` contents, that's up to the devices themselves. +The ``footer mark`` provides a little bit of protection for the case where +the receiving side reads more or less data than expected. + +The ``ID string`` is normally unique, having been formed from a bus name +and device address, PCI devices and storage devices hung off PCI controllers +fit this pattern well. Some devices are fixed single instances (e.g. "pc-ram"). +Others (especially either older devices or system devices which for +some reason don't have a bus concept) make use of the ``instance id`` +for otherwise identically named devices. Return path ----------- -In most migration scenarios there is only a single data path that runs -from the source VM to the destination, typically along a single fd (although -possibly with another fd or similar for some fast way of throwing pages across). +Only a unidirectional stream is required for normal migration, however a +``return path`` can be created when bidirectional communication is desired. +This is primarily used by postcopy, but is also used to return a success +flag to the source at the end of migration. -However, some uses need two way communication; in particular the Postcopy -destination needs to be able to request pages on demand from the source. - -For these scenarios there is a 'return path' from the destination to the source; ``qemu_file_get_return_path(QEMUFile* fwdpath)`` gives the QEMUFile* for the return path. @@ -401,6 +596,20 @@ will now cause the transition from precopy to postcopy. It can be issued immediately after migration is started or any time later on. Issuing it after the end of a migration is harmless. +Blocktime is a postcopy live migration metric, intended to show how +long the vCPU was in state of interruptable sleep due to pagefault. +That metric is calculated both for all vCPUs as overlapped value, and +separately for each vCPU. These values are calculated on destination +side. To enable postcopy blocktime calculation, enter following +command on destination monitor: + +``migrate_set_capability postcopy-blocktime on`` + +Postcopy blocktime can be retrieved by query-migrate qmp command. +postcopy-blocktime value of qmp command will show overlapped blocking +time for all vCPU, postcopy-vcpu-blocktime will show list of blocking +time per vCPU. + .. note:: During the postcopy phase, the bandwidth limits set using ``migrate_set_speed`` is ignored (to avoid delaying requested pages that @@ -618,3 +827,28 @@ Retro-fitting postcopy to existing clients is possible: identified and the implication understood; for example if the guest memory access is made while holding a lock then all other threads waiting for that lock will also be blocked. + +Firmware +======== + +Migration migrates the copies of RAM and ROM, and thus when running +on the destination it includes the firmware from the source. Even after +resetting a VM, the old firmware is used. Only once QEMU has been restarted +is the new firmware in use. + +- Changes in firmware size can cause changes in the required RAMBlock size + to hold the firmware and thus migration can fail. In practice it's best + to pad firmware images to convenient powers of 2 with plenty of space + for growth. + +- Care should be taken with device emulation code so that newer + emulation code can work with older firmware to allow forward migration. + +- Care should be taken with newer firmware so that backward migration + to older systems with older device emulation code will work. + +In some cases it may be best to tie specific firmware versions to specific +versioned machine types to cut down on the combinations that will need +support. This is also useful when newer versions of firmware outgrow +the padding. + diff --git a/docs/devel/multi-thread-tcg.txt b/docs/devel/multi-thread-tcg.txt index a99b4564c6..782bebc28b 100644 --- a/docs/devel/multi-thread-tcg.txt +++ b/docs/devel/multi-thread-tcg.txt @@ -61,6 +61,7 @@ have their block-to-block jumps patched. Global TCG State ---------------- +### User-mode emulation We need to protect the entire code generation cycle including any post generation patching of the translated code. This also implies a shared translation buffer which contains code running on all cores. Any @@ -75,9 +76,11 @@ patching. (Current solution) -Mainly as part of the linux-user work all code generation is -serialised with a tb_lock(). For the SoftMMU tb_lock() also takes the -place of mmap_lock() in linux-user. +Code generation is serialised with mmap_lock(). + +### !User-mode emulation +Each vCPU has its own TCG context and associated TCG region, thereby +requiring no locking. Translation Blocks ------------------ @@ -131,15 +134,20 @@ DESIGN REQUIREMENT: Safely handle invalidation of TBs The direct jump themselves are updated atomically by the TCG tb_set_jmp_target() code. Modification to the linked lists that allow -searching for linked pages are done under the protect of the -tb_lock(). +searching for linked pages are done under the protection of tb->jmp_lock, +where tb is the destination block of a jump. Each origin block keeps a +pointer to its destinations so that the appropriate lock can be acquired before +iterating over a jump list. -The global page table is protected by the tb_lock() in system-mode and -mmap_lock() in linux-user mode. +The global page table is a lockless radix tree; cmpxchg is used +to atomically insert new elements. The lookup caches are updated atomically and the lookup hash uses QHT which is designed for concurrent safe lookup. +Parallel code generation is supported. QHT is used at insertion time +as the synchronization point across threads, thereby ensuring that we only +keep track of a single TranslationBlock for each guest code block. Memory maps and TLBs -------------------- @@ -190,7 +198,7 @@ work as "safe work" and exiting the cpu run loop. This ensure by the time execution restarts all flush operations have completed. TLB flag updates are all done atomically and are also protected by the -tb_lock() which is used by the functions that update the TLB in bulk. +corresponding page lock. (Known limitation) @@ -308,7 +316,7 @@ other cores sharing access to the memory. The classic example is the x86 cmpxchg instruction. The second type offer a pair of load/store instructions which offer a -guarantee that an region of memory has not been touched between the +guarantee that a region of memory has not been touched between the load and store instructions. An example of this is ARM's ldrex/strex pair where the strex instruction will return a flag indicating a successful store only if no other CPU has accessed the memory region diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index a569d24745..c2e11465f0 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -496,9 +496,11 @@ Resulting in these JSON objects: Notice that in a flat union, the discriminator name is controlled by the user, but because it must map to a base member with enum type, the -code generator can ensure that branches exist for all values of the -enum (although the order of the keys need not match the declaration of -the enum). In the resulting generated C data types, a flat union is +code generator ensures that branches match the existing values of the +enum. The order of the keys need not match the declaration of the enum. +The keys need not cover all possible enum values. Omitted enum values +are still valid branches that add no additional members to the data type. +In the resulting generated C data types, a flat union is represented as a struct with the base members included directly, and then a union of structures for each branch of the struct. @@ -559,7 +561,7 @@ following example objects: Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, '*returns': TYPE-NAME, '*boxed': true, '*gen': false, '*success-response': false, - '*allow-oob': true } + '*allow-oob': true, '*allow-preconfig': true } Commands are defined by using a dictionary containing several members, where three members are most common. The 'command' member is a @@ -622,66 +624,67 @@ its return value. In rare cases, QAPI cannot express a type-safe representation of a corresponding Client JSON Protocol command. You then have to suppress generation of a marshalling function by including a key 'gen' with -boolean value false, and instead write your own function. Please try -to avoid adding new commands that rely on this, and instead use -type-safe unions. For an example of this usage: +boolean value false, and instead write your own function. For +example: { 'command': 'netdev_add', 'data': {'type': 'str', 'id': 'str'}, 'gen': false } +Please try to avoid adding new commands that rely on this, and instead +use type-safe unions. + Normally, the QAPI schema is used to describe synchronous exchanges, where a response is expected. But in some cases, the action of a command is expected to change state in a way that a successful response is not possible (although the command will still return a normal dictionary error on failure). When a successful reply is not -possible, the command expression should include the optional key +possible, the command expression includes the optional key 'success-response' with boolean value false. So far, only QGA makes use of this member. -A command can be declared to support Out-Of-Band (OOB) execution. By -default, commands do not support OOB. To declare a command that -supports it, the schema includes an extra 'allow-oob' field. For -example: +Key 'allow-oob' declares whether the command supports out-of-band +(OOB) execution. It defaults to false. For example: { 'command': 'migrate_recover', 'data': { 'uri': 'str' }, 'allow-oob': true } -To execute a command with out-of-band priority, the client specifies -the "control" field in the request, with "run-oob" set to -true. Example: +See qmp-spec.txt for out-of-band execution syntax and semantics. - => { "execute": "command-support-oob", - "arguments": { ... }, - "control": { "run-oob": true } } - <= { "return": { } } +Commands supporting out-of-band execution can still be executed +in-band. -Without it, even the commands that support out-of-band execution will -still be run in-band. +When a command is executed in-band, its handler runs in the main +thread with the BQL held. -Under normal QMP command execution, the following apply to each -command: +When a command is executed out-of-band, its handler runs in a +dedicated monitor I/O thread with the BQL *not* held. -- They are executed in order, -- They run only in main thread of QEMU, -- They have the BQL taken during execution. +An OOB-capable command handler must satisfy the following conditions: -When a command is executed with OOB, the following changes occur: - -- They can be completed before a pending in-band command, -- They run in a dedicated monitor thread, -- They do not take the BQL during execution. - -OOB command handlers must satisfy the following conditions: - -- It executes extremely fast, -- It does not take any lock, or, it can take very small locks if all - critical regions also follow the rules for OOB command handler code, -- It does not invoke system calls that may block, +- It terminates quickly. +- It does not invoke system calls that may block. - It does not access guest RAM that may block when userfaultfd is enabled for postcopy live migration. +- It takes only "fast" locks, i.e. all critical sections protected by + any lock it takes also satisfy the conditions for OOB command + handler code. -If in doubt, do not implement OOB execution support. +The restrictions on locking limit access to shared state. Such access +requires synchronization, but OOB commands can't take the BQL or any +other "slow" lock. + +When in doubt, do not implement OOB execution support. + +Key 'allow-preconfig' declares whether the command is available before +the machine is built. It defaults to false. For example: + + { 'command': 'qmp_capabilities', + 'data': { '*enable': [ 'QMPCapability' ] }, + 'allow-preconfig': true } + +QMP is available before the machine is built only when QEMU was +started with --preconfig. === Events === @@ -728,6 +731,35 @@ Example: Red Hat, Inc. controls redhat.com, and may therefore add a downstream command __com.redhat_drive-mirror. +=== Configuring the schema === + +The 'struct', 'enum', 'union', 'alternate', 'command' and 'event' +top-level expressions can take an 'if' key. Its value must be a string +or a list of strings. A string is shorthand for a list containing just +that string. The code generated for the top-level expression will then +be guarded by #if COND for each COND in the list. + +Example: a conditional struct + + { 'struct': 'IfStruct', 'data': { 'foo': 'int' }, + 'if': ['defined(CONFIG_FOO)', 'defined(HAVE_BAR)'] } + +gets its generated code guarded like this: + + #if defined(CONFIG_FOO) + #if defined(HAVE_BAR) + ... generated code ... + #endif /* defined(HAVE_BAR) */ + #endif /* defined(CONFIG_FOO) */ + +Please note that you are responsible to ensure that the C code will +compile with an arbitrary combination of conditions, since the +generators are unable to check it at this point. + +The presence of 'if' keys in the schema is reflected through to the +introspection output depending on the build configuration. + + == Client JSON Protocol introspection == Clients of a Client JSON Protocol commonly need to figure out what @@ -1340,7 +1372,7 @@ Example: emit(EXAMPLE_QAPI_EVENT_MY_EVENT, qmp, &err); error_propagate(errp, err); - QDECREF(qmp); + qobject_unref(qmp); } const QEnumLookup example_QAPIEvent_lookup = { diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 0ca1a2d4b5..8e1fa3a66e 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -158,12 +158,21 @@ rarely used. See "QEMU iotests" section below for more information. GCC gcov support ---------------- -``gcov`` is a GCC tool to analyze the testing coverage by instrumenting the -tested code. To use it, configure QEMU with ``--enable-gcov`` option and build. -Then run ``make check`` as usual. There will be additional ``gcov`` output as -the testing goes on, showing the test coverage percentage numbers per analyzed -source file. More detailed reports can be obtained by running ``gcov`` command -on the output files under ``$build_dir/tests/``, please read the ``gcov`` +``gcov`` is a GCC tool to analyze the testing coverage by +instrumenting the tested code. To use it, configure QEMU with +``--enable-gcov`` option and build. Then run ``make check`` as usual. + +If you want to gather coverage information on a single test the ``make +clean-coverage`` target can be used to delete any existing coverage +information before running a single test. + +You can generate a HTML coverage report by executing ``make +coverage-report`` which will create +./reports/coverage/coverage-report.html. If you want to create it +elsewhere simply execute ``make /foo/bar/baz/coverage-report.html``. + +Further analysis can be conducted by running the ``gcov`` command +directly on the various .gcda output files. Please read the ``gcov`` documentation for more information. QEMU iotests @@ -246,6 +255,17 @@ comparable library support for invoking and interacting with QEMU programs. If you opt for Python, it is strongly recommended to write Python 3 compatible code. +Both Python and Bash frameworks in iotests provide helpers to manage test +images. They can be used to create and clean up images under the test +directory. If no I/O or any protocol specific feature is needed, it is often +more convenient to use the pseudo block driver, ``null-co://``, as the test +image, which doesn't require image creation or cleaning up. Avoid system-wide +devices or files whenever possible, such as ``/dev/null`` or ``/dev/zero``. +Otherwise, image locking implications have to be considered. For example, +another application on the host may have locked the file, possibly leading to a +test failure. If using such devices are explicitly desired, consider adding +``locking=off`` option to disable image locking. + Docker based tests ================== @@ -484,3 +504,195 @@ supported. To start the fuzzer, run Alternatively, some command different from "qemu-img info" can be tested, by changing the ``-c`` option. + +Acceptance tests using the Avocado Framework +============================================ + +The ``tests/acceptance`` directory hosts functional tests, also known +as acceptance level tests. They're usually higher level tests, and +may interact with external resources and with various guest operating +systems. + +These tests are written using the Avocado Testing Framework (which must +be installed separately) in conjunction with a the ``avocado_qemu.Test`` +class, implemented at ``tests/acceptance/avocado_qemu``. + +Tests based on ``avocado_qemu.Test`` can easily: + + * Customize the command line arguments given to the convenience + ``self.vm`` attribute (a QEMUMachine instance) + + * Interact with the QEMU monitor, send QMP commands and check + their results + + * Interact with the guest OS, using the convenience console device + (which may be useful to assert the effectiveness and correctness of + command line arguments or QMP commands) + + * Interact with external data files that accompany the test itself + (see ``self.get_data()``) + + * Download (and cache) remote data files, such as firmware and kernel + images + + * Have access to a library of guest OS images (by means of the + ``avocado.utils.vmimage`` library) + + * Make use of various other test related utilities available at the + test class itself and at the utility library: + + - http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test + - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html + +Installation +------------ + +To install Avocado and its dependencies, run: + +.. code:: + + pip install --user avocado-framework + +Alternatively, follow the instructions on this link: + + http://avocado-framework.readthedocs.io/en/latest/GetStartedGuide.html#installing-avocado + +Overview +-------- + +This directory provides the ``avocado_qemu`` Python module, containing +the ``avocado_qemu.Test`` class. Here's a simple usage example: + +.. code:: + + from avocado_qemu import Test + + + class Version(Test): + """ + :avocado: enable + :avocado: tags=quick + """ + def test_qmp_human_info_version(self): + self.vm.launch() + res = self.vm.command('human-monitor-command', + command_line='info version') + self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') + +To execute your test, run: + +.. code:: + + avocado run version.py + +Tests may be classified according to a convention by using docstring +directives such as ``:avocado: tags=TAG1,TAG2``. To run all tests +in the current directory, tagged as "quick", run: + +.. code:: + + avocado run -t quick . + +The ``avocado_qemu.Test`` base test class +----------------------------------------- + +The ``avocado_qemu.Test`` class has a number of characteristics that +are worth being mentioned right away. + +First of all, it attempts to give each test a ready to use QEMUMachine +instance, available at ``self.vm``. Because many tests will tweak the +QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) +is left to the test writer. + +At test "tear down", ``avocado_qemu.Test`` handles the QEMUMachine +shutdown. + +QEMUMachine +~~~~~~~~~~~ + +The QEMUMachine API is already widely used in the Python iotests, +device-crash-test and other Python scripts. It's a wrapper around the +execution of a QEMU binary, giving its users: + + * the ability to set command line arguments to be given to the QEMU + binary + + * a ready to use QMP connection and interface, which can be used to + send commands and inspect its results, as well as asynchronous + events + + * convenience methods to set commonly used command line arguments in + a more succinct and intuitive way + +QEMU binary selection +~~~~~~~~~~~~~~~~~~~~~ + +The QEMU binary used for the ``self.vm`` QEMUMachine instance will +primarily depend on the value of the ``qemu_bin`` parameter. If it's +not explicitly set, its default value will be the result of a dynamic +probe in the same source tree. A suitable binary will be one that +targets the architecture matching host machine. + +Based on this description, test writers will usually rely on one of +the following approaches: + +1) Set ``qemu_bin``, and use the given binary + +2) Do not set ``qemu_bin``, and use a QEMU binary named like + "${arch}-softmmu/qemu-system-${arch}", either in the current + working directory, or in the current source tree. + +The resulting ``qemu_bin`` value will be preserved in the +``avocado_qemu.Test`` as an attribute with the same name. + +Attribute reference +------------------- + +Besides the attributes and methods that are part of the base +``avocado.Test`` class, the following attributes are available on any +``avocado_qemu.Test`` instance. + +vm +~~ + +A QEMUMachine instance, initially configured according to the given +``qemu_bin`` parameter. + +qemu_bin +~~~~~~~~ + +The preserved value of the ``qemu_bin`` parameter or the result of the +dynamic probe for a QEMU binary in the current working directory or +source tree. + +Parameter reference +------------------- + +To understand how Avocado parameters are accessed by tests, and how +they can be passed to tests, please refer to:: + + http://avocado-framework.readthedocs.io/en/latest/WritingTests.html#accessing-test-parameters + +Parameter values can be easily seen in the log files, and will look +like the following: + +.. code:: + + PARAMS (key=qemu_bin, path=*, default=x86_64-softmmu/qemu-system-x86_64) => 'x86_64-softmmu/qemu-system-x86_64 + +qemu_bin +~~~~~~~~ + +The exact QEMU binary to be used on QEMUMachine. + +Uninstalling Avocado +-------------------- + +If you've followed the installation instructions above, you can easily +uninstall Avocado. Start by listing the packages you have installed:: + + pip list --user + +And remove any package you want with:: + + pip uninstall diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt index 07abbb345c..bc52f12485 100644 --- a/docs/devel/tracing.txt +++ b/docs/devel/tracing.txt @@ -18,7 +18,7 @@ for debugging, profiling, and observing execution. 3. Run the virtual machine to produce a trace file: - qemu -trace events=/tmp/events ... # your normal QEMU invocation + qemu --trace events=/tmp/events ... # your normal QEMU invocation 4. Pretty-print the binary trace file: @@ -104,6 +104,11 @@ Trace events should use types as follows: * For everything else, use primitive scalar types (char, int, long) with the appropriate signedness. + * Avoid floating point types (float and double) because SystemTap does not + support them. In most cases it is possible to round to an integer type + instead. This may require scaling the value first by multiplying it by 1000 + or the like when digits after the decimal point need to be preserved. + Format strings should reflect the types defined in the trace event. Take special care to use PRId64 and PRIu64 for int64_t and uint64_t types, respectively. This ensures portability between 32- and 64-bit platforms. @@ -152,11 +157,11 @@ The state of events can also be queried and modified through monitor commands: * trace-event NAME on|off Enable/disable a given trace event or a group of events (using wildcards). -The "-trace events=" command line argument can be used to enable the +The "--trace events=" command line argument can be used to enable the events listed in from the very beginning of the program. This file must contain one event name per line. -If a line in the "-trace events=" file begins with a '-', the trace event +If a line in the "--trace events=" file begins with a '-', the trace event will be disabled instead of enabled. This is useful when a wildcard was used to enable an entire family of events but one noisy event needs to be disabled. diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json new file mode 100644 index 0000000000..28f9bc1591 --- /dev/null +++ b/docs/interop/firmware.json @@ -0,0 +1,540 @@ +# -*- Mode: Python -*- +# +# Copyright (C) 2018 Red Hat, Inc. +# +# Authors: +# Daniel P. Berrange +# Laszlo Ersek +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +## +# = Firmware +## + +{ 'include' : 'common.json' } +{ 'include' : 'block-core.json' } + +## +# @FirmwareOSInterface: +# +# Lists the firmware-OS interface types provided by various firmware +# that is commonly used with QEMU virtual machines. +# +# @bios: Traditional x86 BIOS interface. For example, firmware built +# from the SeaBIOS project usually provides this interface. +# +# @openfirmware: The interface is defined by the (historical) IEEE +# 1275-1994 standard. Examples for firmware projects that +# provide this interface are: OpenBIOS, OpenHackWare, +# SLOF. +# +# @uboot: Firmware interface defined by the U-Boot project. +# +# @uefi: Firmware interface defined by the UEFI specification. For +# example, firmware built from the edk2 (EFI Development Kit II) +# project usually provides this interface. +# +# Since: 3.0 +## +{ 'enum' : 'FirmwareOSInterface', + 'data' : [ 'bios', 'openfirmware', 'uboot', 'uefi' ] } + +## +# @FirmwareDevice: +# +# Defines the device types that firmware can be mapped into. +# +# @flash: The firmware executable and its accompanying NVRAM file are to +# be mapped into a pflash chip each. +# +# @kernel: The firmware is to be loaded like a Linux kernel. This is +# similar to @memory but may imply additional processing that +# is specific to the target architecture and machine type. +# +# @memory: The firmware is to be mapped into memory. +# +# Since: 3.0 +## +{ 'enum' : 'FirmwareDevice', + 'data' : [ 'flash', 'kernel', 'memory' ] } + +## +# @FirmwareTarget: +# +# Defines the machine types that firmware may execute on. +# +# @architecture: Determines the emulation target (the QEMU system +# emulator) that can execute the firmware. +# +# @machines: Lists the machine types (known by the emulator that is +# specified through @architecture) that can execute the +# firmware. Elements of @machines are supposed to be concrete +# machine types, not aliases. Glob patterns are understood, +# which is especially useful for versioned machine types. +# (For example, the glob pattern "pc-i440fx-*" matches +# "pc-i440fx-2.12".) On the QEMU command line, "-machine +# type=..." specifies the requested machine type (but that +# option does not accept glob patterns). +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareTarget', + 'data' : { 'architecture' : 'SysEmuTarget', + 'machines' : [ 'str' ] } } + +## +# @FirmwareFeature: +# +# Defines the features that firmware may support, and the platform +# requirements that firmware may present. +# +# @acpi-s3: The firmware supports S3 sleep (suspend to RAM), as defined +# in the ACPI specification. On the "pc-i440fx-*" machine +# types of the @i386 and @x86_64 emulation targets, S3 can be +# enabled with "-global PIIX4_PM.disable_s3=0" and disabled +# with "-global PIIX4_PM.disable_s3=1". On the "pc-q35-*" +# machine types of the @i386 and @x86_64 emulation targets, S3 +# can be enabled with "-global ICH9-LPC.disable_s3=0" and +# disabled with "-global ICH9-LPC.disable_s3=1". +# +# @acpi-s4: The firmware supports S4 hibernation (suspend to disk), as +# defined in the ACPI specification. On the "pc-i440fx-*" +# machine types of the @i386 and @x86_64 emulation targets, S4 +# can be enabled with "-global PIIX4_PM.disable_s4=0" and +# disabled with "-global PIIX4_PM.disable_s4=1". On the +# "pc-q35-*" machine types of the @i386 and @x86_64 emulation +# targets, S4 can be enabled with "-global +# ICH9-LPC.disable_s4=0" and disabled with "-global +# ICH9-LPC.disable_s4=1". +# +# @amd-sev: The firmware supports running under AMD Secure Encrypted +# Virtualization, as specified in the AMD64 Architecture +# Programmer's Manual. QEMU command line options related to +# this feature are documented in +# "docs/amd-memory-encryption.txt". +# +# @enrolled-keys: The variable store (NVRAM) template associated with +# the firmware binary has the UEFI Secure Boot +# operational mode turned on, with certificates +# enrolled. +# +# @requires-smm: The firmware requires the platform to emulate SMM +# (System Management Mode), as defined in the AMD64 +# Architecture Programmer's Manual, and in the Intel(R)64 +# and IA-32 Architectures Software Developer's Manual. On +# the "pc-q35-*" machine types of the @i386 and @x86_64 +# emulation targets, SMM emulation can be enabled with +# "-machine smm=on". (On the "pc-q35-*" machine types of +# the @i386 emulation target, @requires-smm presents +# further CPU requirements; one combination known to work +# is "-cpu coreduo,-nx".) If the firmware is marked as +# both @secure-boot and @requires-smm, then write +# accesses to the pflash chip (NVRAM) that holds the UEFI +# variable store must be restricted to code that executes +# in SMM, using the additional option "-global +# driver=cfi.pflash01,property=secure,value=on". +# Furthermore, a large guest-physical address space +# (comprising guest RAM, memory hotplug range, and 64-bit +# PCI MMIO aperture), and/or a high VCPU count, may +# present high SMRAM requirements from the firmware. On +# the "pc-q35-*" machine types of the @i386 and @x86_64 +# emulation targets, the SMRAM size may be increased +# above the default 16MB with the "-global +# mch.extended-tseg-mbytes=uint16" option. As a rule of +# thumb, the default 16MB size suffices for 1TB of +# guest-phys address space and a few tens of VCPUs; for +# every further TB of guest-phys address space, add 8MB +# of SMRAM. 48MB should suffice for 4TB of guest-phys +# address space and 2-3 hundred VCPUs. +# +# @secure-boot: The firmware implements the software interfaces for UEFI +# Secure Boot, as defined in the UEFI specification. Note +# that without @requires-smm, guest code running with +# kernel privileges can undermine the security of Secure +# Boot. +# +# @verbose-dynamic: When firmware log capture is enabled, the firmware +# logs a large amount of debug messages, which may +# impact boot performance. With log capture disabled, +# there is no boot performance impact. On the +# "pc-i440fx-*" and "pc-q35-*" machine types of the +# @i386 and @x86_64 emulation targets, firmware log +# capture can be enabled with the QEMU command line +# options "-chardev file,id=fwdebug,path=LOGFILEPATH +# -device isa-debugcon,iobase=0x402,chardev=fwdebug". +# @verbose-dynamic is mutually exclusive with +# @verbose-static. +# +# @verbose-static: The firmware unconditionally produces a large amount +# of debug messages, which may impact boot performance. +# This feature may typically be carried by certain UEFI +# firmware for the "virt-*" machine types of the @arm +# and @aarch64 emulation targets, where the debug +# messages are written to the first (always present) +# PL011 UART. @verbose-static is mutually exclusive +# with @verbose-dynamic. +# +# Since: 3.0 +## +{ 'enum' : 'FirmwareFeature', + 'data' : [ 'acpi-s3', 'acpi-s4', 'amd-sev', 'enrolled-keys', + 'requires-smm', 'secure-boot', 'verbose-dynamic', + 'verbose-static' ] } + +## +# @FirmwareFlashFile: +# +# Defines common properties that are necessary for loading a firmware +# file into a pflash chip. The corresponding QEMU command line option is +# "-drive file=@filename,format=@format". Note however that the +# option-argument shown here is incomplete; it is completed under +# @FirmwareMappingFlash. +# +# @filename: Specifies the filename on the host filesystem where the +# firmware file can be found. +# +# @format: Specifies the block format of the file pointed-to by +# @filename, such as @raw or @qcow2. +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareFlashFile', + 'data' : { 'filename' : 'str', + 'format' : 'BlockdevDriver' } } + +## +# @FirmwareMappingFlash: +# +# Describes loading and mapping properties for the firmware executable +# and its accompanying NVRAM file, when @FirmwareDevice is @flash. +# +# @executable: Identifies the firmware executable. The firmware +# executable may be shared by multiple virtual machine +# definitions. The corresponding QEMU command line option +# is "-drive +# if=pflash,unit=0,readonly=on,file=@executable.@filename,format=@executable.@format". +# +# @nvram-template: Identifies the NVRAM template compatible with +# @executable. Management software instantiates an +# individual copy -- a specific NVRAM file -- from +# @nvram-template.@filename for each new virtual +# machine definition created. @nvram-template.@filename +# itself is never mapped into virtual machines, only +# individual copies of it are. An NVRAM file is +# typically used for persistently storing the +# non-volatile UEFI variables of a virtual machine +# definition. The corresponding QEMU command line +# option is "-drive +# if=pflash,unit=1,readonly=off,file=FILENAME_OF_PRIVATE_NVRAM_FILE,format=@nvram-template.@format". +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareMappingFlash', + 'data' : { 'executable' : 'FirmwareFlashFile', + 'nvram-template' : 'FirmwareFlashFile' } } + +## +# @FirmwareMappingKernel: +# +# Describes loading and mapping properties for the firmware executable, +# when @FirmwareDevice is @kernel. +# +# @filename: Identifies the firmware executable. The firmware executable +# may be shared by multiple virtual machine definitions. The +# corresponding QEMU command line option is "-kernel +# @filename". +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareMappingKernel', + 'data' : { 'filename' : 'str' } } + +## +# @FirmwareMappingMemory: +# +# Describes loading and mapping properties for the firmware executable, +# when @FirmwareDevice is @memory. +# +# @filename: Identifies the firmware executable. The firmware executable +# may be shared by multiple virtual machine definitions. The +# corresponding QEMU command line option is "-bios +# @filename". +# +# Since: 3.0 +## +{ 'struct' : 'FirmwareMappingMemory', + 'data' : { 'filename' : 'str' } } + +## +# @FirmwareMapping: +# +# Provides a discriminated structure for firmware to describe its +# loading / mapping properties. +# +# @device: Selects the device type that the firmware must be mapped +# into. +# +# Since: 3.0 +## +{ 'union' : 'FirmwareMapping', + 'base' : { 'device' : 'FirmwareDevice' }, + 'discriminator' : 'device', + 'data' : { 'flash' : 'FirmwareMappingFlash', + 'kernel' : 'FirmwareMappingKernel', + 'memory' : 'FirmwareMappingMemory' } } + +## +# @Firmware: +# +# Describes a firmware (or a firmware use case) to management software. +# +# It is possible for multiple @Firmware elements to match the search +# criteria of management software. Applications thus need rules to pick +# one of the many matches, and users need the ability to override distro +# defaults. +# +# It is recommended to create firmware JSON files (each containing a +# single @Firmware root element) with a double-digit prefix, for example +# "50-ovmf.json", "50-seabios-256k.json", etc, so they can be sorted in +# predictable order. The firmware JSON files should be searched for in +# three directories: +# +# - /usr/share/qemu/firmware -- populated by distro-provided firmware +# packages (XDG_DATA_DIRS covers +# /usr/share by default), +# +# - /etc/qemu/firmware -- exclusively for sysadmins' local additions, +# +# - $XDG_CONFIG_HOME/qemu/firmware -- exclusively for per-user local +# additions (XDG_CONFIG_HOME +# defaults to $HOME/.config). +# +# Top-down, the list of directories goes from general to specific. +# +# Management software should build a list of files from all three +# locations, then sort the list by filename (i.e., last pathname +# component). Management software should choose the first JSON file on +# the sorted list that matches the search criteria. If a more specific +# directory has a file with same name as a less specific directory, then +# the file in the more specific directory takes effect. If the more +# specific file is zero length, it hides the less specific one. +# +# For example, if a distro ships +# +# - /usr/share/qemu/firmware/50-ovmf.json +# +# - /usr/share/qemu/firmware/50-seabios-256k.json +# +# then the sysadmin can prevent the default OVMF being used at all with +# +# $ touch /etc/qemu/firmware/50-ovmf.json +# +# The sysadmin can replace/alter the distro default OVMF with +# +# $ vim /etc/qemu/firmware/50-ovmf.json +# +# or they can provide a parallel OVMF with higher priority +# +# $ vim /etc/qemu/firmware/10-ovmf.json +# +# or they can provide a parallel OVMF with lower priority +# +# $ vim /etc/qemu/firmware/99-ovmf.json +# +# @description: Provides a human-readable description of the firmware. +# Management software may or may not display @description. +# +# @interface-types: Lists the types of interfaces that the firmware can +# expose to the guest OS. This is a non-empty, ordered +# list; entries near the beginning of @interface-types +# are considered more native to the firmware, and/or +# to have a higher quality implementation in the +# firmware, than entries near the end of +# @interface-types. +# +# @mapping: Describes the loading / mapping properties of the firmware. +# +# @targets: Collects the target architectures (QEMU system emulators) +# and their machine types that may execute the firmware. +# +# @features: Lists the features that the firmware supports, and the +# platform requirements it presents. +# +# @tags: A list of auxiliary strings associated with the firmware for +# which @description is not appropriate, due to the latter's +# possible exposure to the end-user. @tags serves development and +# debugging purposes only, and management software shall +# explicitly ignore it. +# +# Since: 3.0 +# +# Examples: +# +# { +# "description": "SeaBIOS", +# "interface-types": [ +# "bios" +# ], +# "mapping": { +# "device": "memory", +# "filename": "/usr/share/seabios/bios-256k.bin" +# }, +# "targets": [ +# { +# "architecture": "i386", +# "machines": [ +# "pc-i440fx-*", +# "pc-q35-*" +# ] +# }, +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-i440fx-*", +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "acpi-s4" +# ], +# "tags": [ +# "CONFIG_BOOTSPLASH=n", +# "CONFIG_ROM_SIZE=256", +# "CONFIG_USE_SMM=n" +# ] +# } +# +# { +# "description": "OVMF with SB+SMM, empty varstore", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/OVMF/OVMF_VARS.fd", +# "format": "raw" +# } +# }, +# "targets": [ +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "amd-sev", +# "requires-smm", +# "secure-boot", +# "verbose-dynamic" +# ], +# "tags": [ +# "-a IA32", +# "-a X64", +# "-p OvmfPkg/OvmfPkgIa32X64.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D SMM_REQUIRE", +# "-D SECURE_BOOT_ENABLE", +# "-D FD_SIZE_4MB" +# ] +# } +# +# { +# "description": "OVMF with SB+SMM, SB enabled, MS certs enrolled", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/OVMF/OVMF_CODE.secboot.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/OVMF/OVMF_VARS.secboot.fd", +# "format": "raw" +# } +# }, +# "targets": [ +# { +# "architecture": "x86_64", +# "machines": [ +# "pc-q35-*" +# ] +# } +# ], +# "features": [ +# "acpi-s3", +# "amd-sev", +# "enrolled-keys", +# "requires-smm", +# "secure-boot", +# "verbose-dynamic" +# ], +# "tags": [ +# "-a IA32", +# "-a X64", +# "-p OvmfPkg/OvmfPkgIa32X64.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D SMM_REQUIRE", +# "-D SECURE_BOOT_ENABLE", +# "-D FD_SIZE_4MB" +# ] +# } +# +# { +# "description": "UEFI firmware for ARM64 virtual machines", +# "interface-types": [ +# "uefi" +# ], +# "mapping": { +# "device": "flash", +# "executable": { +# "filename": "/usr/share/AAVMF/AAVMF_CODE.fd", +# "format": "raw" +# }, +# "nvram-template": { +# "filename": "/usr/share/AAVMF/AAVMF_VARS.fd", +# "format": "raw" +# } +# }, +# "targets": [ +# { +# "architecture": "aarch64", +# "machines": [ +# "virt-*" +# ] +# } +# ], +# "features": [ +# +# ], +# "tags": [ +# "-a AARCH64", +# "-p ArmVirtPkg/ArmVirtQemu.dsc", +# "-t GCC48", +# "-b DEBUG", +# "-D DEBUG_PRINT_ERROR_LEVEL=0x80000000" +# ] +# } +## +{ 'struct' : 'Firmware', + 'data' : { 'description' : 'str', + 'interface-types' : [ 'FirmwareOSInterface' ], + 'mapping' : 'FirmwareMapping', + 'targets' : [ 'FirmwareTarget' ], + 'features' : [ 'FirmwareFeature' ], + 'tags' : [ 'str' ] } } diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt new file mode 100644 index 0000000000..77b5f45911 --- /dev/null +++ b/docs/interop/nbd.txt @@ -0,0 +1,38 @@ +Qemu supports the NBD protocol, and has an internal NBD client (see +block/nbd.c), an internal NBD server (see blockdev-nbd.c), and an +external NBD server tool (see qemu-nbd.c). The common code is placed +in nbd/*. + +The NBD protocol is specified here: +https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md + +The following paragraphs describe some specific properties of NBD +protocol realization in Qemu. + += Metadata namespaces = + +Qemu supports the "base:allocation" metadata context as defined in the +NBD protocol specification, and also defines an additional metadata +namespace "qemu". + + +== "qemu" namespace == + +The "qemu" namespace currently contains only one type of context, +related to exposing the contents of a dirty bitmap alongside the +associated disk contents. That context has the following form: + + qemu:dirty-bitmap: + +Each dirty-bitmap metadata context defines only one flag for extents +in reply for NBD_CMD_BLOCK_STATUS: + + bit 0: NBD_STATE_DIRTY, means that the extent is "dirty" + +For NBD_OPT_LIST_META_CONTEXT the following queries are supported +in addition to "qemu:dirty-bitmap:": + +* "qemu:" - returns list of all available metadata contexts in the + namespace. +* "qemu:dirty-bitmap:" - returns list of all available dirty-bitmap + metadata contexts. diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt index feb711fb6a..845d40a086 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.txt @@ -326,8 +326,8 @@ in the image file. It contains pointers to the second level structures which are called refcount blocks and are exactly one cluster in size. -Given a offset into the image file, the refcount of its cluster can be obtained -as follows: +Given an offset into the image file, the refcount of its cluster can be +obtained as follows: refcount_block_entries = (cluster_size * 8 / refcount_bits) @@ -365,7 +365,7 @@ The L1 table has a variable size (stored in the header) and may use multiple clusters, however it must be contiguous in the image file. L2 tables are exactly one cluster in size. -Given a offset into the virtual disk, the offset into the image file can be +Given an offset into the virtual disk, the offset into the image file can be obtained as follows: l2_entries = (cluster_size / sizeof(uint64_t)) @@ -400,10 +400,10 @@ L2 table entry: 62: 0 for standard clusters 1 for compressed clusters - 63: 0 for a cluster that is unused or requires COW, 1 if its - refcount is exactly one. This information is only accurate - in L2 tables that are reachable from the active L1 - table. + 63: 0 for clusters that are unused, compressed or require COW. + 1 for standard clusters whose refcount is exactly one. + This information is only accurate in L2 tables + that are reachable from the active L1 table. Standard Cluster Descriptor: diff --git a/docs/interop/qmp-intro.txt b/docs/interop/qmp-intro.txt index 900d69d612..9d54a718b8 100644 --- a/docs/interop/qmp-intro.txt +++ b/docs/interop/qmp-intro.txt @@ -52,13 +52,14 @@ Escape character is '^]'. "QMP": { "version": { "qemu": { - "micro": 50, - "minor": 6, - "major": 1 - }, - "package": "" - }, + "micro": 0, + "minor": 0, + "major": 3 + }, + "package": "v3.0.0" + }, "capabilities": [ + "oob" ] } } diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt index 6fa193a80b..1566b8ae5e 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.txt @@ -77,52 +77,65 @@ The greeting message format is: is the same of the query-version command) - The "capabilities" member specify the availability of features beyond the baseline specification; the order of elements in this array has no - particular significance, so a client must search the entire array - when looking for a particular capability + particular significance. 2.2.1 Capabilities ------------------ Currently supported capabilities are: -- "oob": the QMP server supports "Out-Of-Band" (OOB) command - execution. For more details, please see the "run-oob" parameter in - the "Issuing Commands" section below. Not all commands allow this - "oob" execution. The "query-qmp-schema" command can be used to - inspect which commands support "oob" execution. - -QMP clients can get a list of supported QMP capabilities of the QMP -server in the greeting message mentioned above. By default, all the -capabilities are off. To enable any QMP capabilities, the QMP client -needs to send the "qmp_capabilities" command with an extra parameter -for the requested capabilities. +- "oob": the QMP server supports "out-of-band" (OOB) command + execution, as described in section "2.3.1 Out-of-band execution". 2.3 Issuing Commands -------------------- The format for command execution is: -{ "execute": json-string, "arguments": json-object, "id": json-value, - "control": json-object } +{ "execute": json-string, "arguments": json-object, "id": json-value } + +or + +{ "exec-oob": json-string, "arguments": json-object, "id": json-value } Where, -- The "execute" member identifies the command to be executed by the Server +- The "execute" or "exec-oob" member identifies the command to be + executed by the server. The latter requests out-of-band execution. - The "arguments" member is used to pass any arguments required for the execution of the command, it is optional when no arguments are required. Each command documents what contents will be considered valid when handling the json-argument - The "id" member is a transaction identification associated with the - command execution. It is required for all commands if the OOB - - capability was enabled at startup, and optional otherwise. The same - "id" field will be part of the response if provided. The "id" member - can be any json-value, although most clients merely use a - json-number incremented for each successive command -- The "control" member is optional, and currently only used for - out-of-band execution. The handling or response of an "oob" command - can overtake prior in-band commands. To enable "oob" handling of a - particular command, just provide a control field with: { "control": - { "run-oob": true } } + command execution, it is optional and will be part of the response + if provided. The "id" member can be any json-value. A json-number + incremented for each successive command works fine. + +2.3.1 Out-of-band execution +--------------------------- + +The server normally reads, executes and responds to one command after +the other. The client therefore receives command responses in issue +order. + +With out-of-band execution enabled via capability negotiation (section +4.), the server reads and queues commands as they arrive. It executes +commands from the queue one after the other. Commands executed +out-of-band jump the queue: the command get executed right away, +possibly overtaking prior in-band commands. The client may therefore +receive such a command's response before responses from prior in-band +commands. + +To be able to match responses back to their commands, the client needs +to pass "id" with out-of-band commands. Passing it with all commands +is recommended for clients that accept capability "oob". + +If the client sends in-band commands faster than the server can +execute them, the server will eventually drop commands to limit the +queue length. The sever sends event COMMAND_DROPPED then. + +Only a few commands support out-of-band execution. The ones that do +have "allow-oob": true in output of query-qmp-schema. 2.4 Commands Responses ---------------------- @@ -223,12 +236,13 @@ This section provides some examples of real QMP usage, in all of them 3.1 Server greeting ------------------- -S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 }, - "package": ""}, "capabilities": []}} +S: { "QMP": {"version": {"qemu": {"micro": 0, "minor": 0, "major": 3}, + "package": "v3.0.0"}, "capabilities": ["oob"] } } -3.2 Client QMP negotiation --------------------------- -C: { "execute": "qmp_capabilities" } +3.2 Capabilities negotiation +---------------------------- + +C: { "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } } S: { "return": {}} 3.3 Simple 'stop' execution @@ -255,6 +269,15 @@ S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } } S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 }, "event": "POWERDOWN" } +3.7 Out-of-band execution +------------------------- + +C: { "exec-oob": "migrate-pause", "id": 42 } +S: { "id": 42, + "error": { "class": "GenericError", + "desc": "migrate-pause is currently only supported during postcopy-active state" } } + + 4. Capabilities Negotiation =========================== diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt index 534caab18a..f59667f498 100644 --- a/docs/interop/vhost-user.txt +++ b/docs/interop/vhost-user.txt @@ -108,12 +108,12 @@ Depending on the request type, payload can be: IOVA: a 64-bit I/O virtual address programmed by the guest Size: a 64-bit size User address: a 64-bit user address - Permissions: a 8-bit value: + Permissions: an 8-bit value: - 0: No access - 1: Read access - 2: Write access - 3: Read/Write access - Type: a 8-bit IOTLB message type: + Type: an 8-bit IOTLB message type: - 1: IOTLB miss - 2: IOTLB update - 3: IOTLB invalidate @@ -132,6 +132,16 @@ Depending on the request type, payload can be: Payload: Size bytes array holding the contents of the virtio device's configuration space + * Vring area description + ----------------------- + | u64 | size | offset | + ----------------------- + + u64: a 64-bit integer contains vring index and flags + Size: a 64-bit size of this area + Offset: a 64-bit offset of this area from the start of the + supplied file descriptor + In QEMU the vhost-user message is implemented with the following struct: typedef struct VhostUserMsg { @@ -146,6 +156,7 @@ typedef struct VhostUserMsg { VhostUserLog log; struct vhost_iotlb_msg iotlb; VhostUserConfig config; + VhostUserVringArea area; }; } QEMU_PACKED VhostUserMsg; @@ -367,6 +378,10 @@ The fd is provided via VHOST_USER_SET_SLAVE_REQ_FD ancillary data. A slave may then send VHOST_USER_SLAVE_* messages to the master using this fd communication channel. +If VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD protocol feature is negotiated, +slave can send file descriptors (at most 8 descriptors in each message) +to master via ancillary data using this fd communication channel. + Protocol features ----------------- @@ -380,6 +395,8 @@ Protocol features #define VHOST_USER_PROTOCOL_F_CRYPTO_SESSION 7 #define VHOST_USER_PROTOCOL_F_PAGEFAULT 8 #define VHOST_USER_PROTOCOL_F_CONFIG 9 +#define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10 +#define VHOST_USER_PROTOCOL_F_HOST_NOTIFIER 11 Master message types -------------------- @@ -777,6 +794,27 @@ Slave message types the VHOST_USER_NEED_REPLY flag, master must respond with zero when operation is successfully completed, or non-zero otherwise. + * VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG + + Id: 3 + Equivalent ioctl: N/A + Slave payload: vring area description + Master payload: N/A + + Sets host notifier for a specified queue. The queue index is contained + in the u64 field of the vring area description. The host notifier is + described by the file descriptor (typically it's a VFIO device fd) which + is passed as ancillary data and the size (which is mmap size and should + be the same as host page size) and offset (which is mmap offset) carried + in the vring area description. QEMU can mmap the file descriptor based + on the size and offset to get a memory range. Registering a host notifier + means mapping this memory range to the VM as the specified queue's notify + MMIO region. Slave sends this request to tell QEMU to de-register the + existing notifier if any and register the new notifier if the request is + sent with a file descriptor. + This request should be sent only when VHOST_USER_PROTOCOL_F_HOST_NOTIFIER + protocol feature has been successfully negotiated. + VHOST_USER_PROTOCOL_F_REPLY_ACK: ------------------------------- The original vhost-user specification only demands replies for certain diff --git a/docs/memory-hotplug.txt b/docs/memory-hotplug.txt index d96397c1af..6aa5e17e26 100644 --- a/docs/memory-hotplug.txt +++ b/docs/memory-hotplug.txt @@ -62,7 +62,7 @@ It's also possible to start a guest with memory cold-plugged into the hotpluggable memory slots. This might seem counterintuitive at first, but this allows for a lot of flexibility when using the file backend. -In the following command-line example, a 8GB guest is created where 6GB +In the following command-line example, an 8GB guest is created where 6GB comes from regular RAM, 1GB is a 1GB hugepage page and 256MB is from 2MB pages. Also, the guest has additional memory slots to hotplug more 2GB if needed: diff --git a/docs/multi-thread-compression.txt b/docs/multi-thread-compression.txt index d0caaf7b3b..bb88c6bdf1 100644 --- a/docs/multi-thread-compression.txt +++ b/docs/multi-thread-compression.txt @@ -62,7 +62,7 @@ RAM: 128G NIC: Intel I350 (10/100/1000Mbps) Host OS: CentOS 7 64-bit Guest OS: RHEL 6.5 64-bit -Parameter: qemu-system-x86_64 -enable-kvm -smp 4 -m 4096 +Parameter: qemu-system-x86_64 -accel kvm -smp 4 -m 4096 /share/ia32e_rhel6u5.qcow -monitor stdio There is no additional application is running on the guest when doing diff --git a/docs/multiseat.txt b/docs/multiseat.txt index 807518c8af..8dde36c845 100644 --- a/docs/multiseat.txt +++ b/docs/multiseat.txt @@ -18,7 +18,7 @@ or Next put together the qemu command line (sdk/gtk): -qemu -enable-kvm -usb $memory $disk $whatever \ +qemu -accel kvm -usb $memory $disk $whatever \ -display [ sdl | gtk ] \ -vga std \ -device usb-tablet @@ -62,7 +62,7 @@ to its own window so you can see both display devices side-by-side. For vnc some additional configuration on the command line is needed. We'll create two vnc server instances, and bind the second one to the -second seat, simliar to input devices: +second seat, similar to input devices: -display vnc=:1,id=primary \ -display vnc=:2,id=secondary,display=video.2 diff --git a/docs/nvdimm.txt b/docs/nvdimm.txt index e903d8bb09..24b443b655 100644 --- a/docs/nvdimm.txt +++ b/docs/nvdimm.txt @@ -153,3 +153,23 @@ guest NVDIMM region mapping structure. This unarmed flag indicates guest software that this vNVDIMM device contains a region that cannot accept persistent writes. In result, for example, the guest Linux NVDIMM driver, marks such vNVDIMM device as read-only. + +NVDIMM Persistence +------------------ + +ACPI 6.2 Errata A added support for a new Platform Capabilities Structure +which allows the platform to communicate what features it supports related to +NVDIMM data persistence. Users can provide a persistence value to a guest via +the optional "nvdimm-persistence" machine command line option: + + -machine pc,accel=kvm,nvdimm,nvdimm-persistence=cpu + +There are currently two valid values for this option: + +"mem-ctrl" - The platform supports flushing dirty data from the memory + controller to the NVDIMMs in the event of power loss. + +"cpu" - The platform supports flushing dirty data from the CPU cache to + the NVDIMMs in the event of power loss. This implies that the + platform also supports flushing dirty data through the memory + controller on power loss. diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt index 170191a242..8a09a5cc5f 100644 --- a/docs/qcow2-cache.txt +++ b/docs/qcow2-cache.txt @@ -116,31 +116,30 @@ There are three options available, and all of them take bytes: "refcount-cache-size": maximum size of the refcount block cache "cache-size": maximum size of both caches combined -There are two things that need to be taken into account: +There are a few things that need to be taken into account: - Both caches must have a size that is a multiple of the cluster size (or the cache entry size: see "Using smaller cache sizes" below). - - If you only set one of the options above, QEMU will automatically - adjust the others so that the L2 cache is 4 times bigger than the - refcount cache. + - The default L2 cache size is 8 clusters or 1MB (whichever is more), + and the minimum is 2 clusters (or 2 cache entries, see below). -This means that these options are equivalent: + - The default (and minimum) refcount cache size is 4 clusters. - -drive file=hd.qcow2,l2-cache-size=2097152 - -drive file=hd.qcow2,refcount-cache-size=524288 - -drive file=hd.qcow2,cache-size=2621440 + - If only "cache-size" is specified then QEMU will assign as much + memory as possible to the L2 cache before increasing the refcount + cache size. -The reason for this 1/4 ratio is to ensure that both caches cover the -same amount of disk space. Note however that this is only valid with -the default value of refcount_bits (16). If you are using a different -value you might want to calculate both cache sizes yourself since QEMU -will always use the same 1/4 ratio. +Unlike L2 tables, refcount blocks are not used during normal I/O but +only during allocations and internal snapshots. In most cases they are +accessed sequentially (even during random guest I/O) so increasing the +refcount cache size won't have any measurable effect in performance +(this can change if you are using internal snapshots, so you may want +to think about increasing the cache size if you use them heavily). -It's also worth mentioning that there's no strict need for both caches -to cover the same amount of disk space. The refcount cache is used -much less often than the L2 cache, so it's perfectly reasonable to -keep it small. +Before QEMU 2.12 the refcount cache had a default size of 1/4 of the +L2 cache size. This resulted in unnecessarily large caches, so now the +refcount cache is as small as possible unless overridden by the user. Using smaller cache entries diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt index 8f188d1d0b..98229b3405 100644 --- a/docs/qdev-device-use.txt +++ b/docs/qdev-device-use.txt @@ -277,9 +277,6 @@ devices and ne2k_isa are. Some PCI devices aren't available with -net nic, e.g. i82558a. -To connect to a VLAN instead of an ordinary host part, replace -netdev=NET-ID by vlan=VLAN. - === Graphics Devices === Host and guest part of graphics devices have always been separate. diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi index f1793692bb..38e9f34cc9 100644 --- a/docs/qemu-block-drivers.texi +++ b/docs/qemu-block-drivers.texi @@ -524,7 +524,7 @@ You can create a cloned image from the existing snapshot. @example qemu-img create -b sheepdog:///@var{base}#@var{tag} sheepdog:///@var{image} @end example -where @var{base} is a image name of the source snapshot and @var{tag} +where @var{base} is an image name of the source snapshot and @var{tag} is its tag name. You can use an unix socket instead of an inet socket: diff --git a/docs/qemupciserial.inf b/docs/qemupciserial.inf index 6f7eef49cc..7ca766745d 100644 --- a/docs/qemupciserial.inf +++ b/docs/qemupciserial.inf @@ -1,7 +1,7 @@ ; qemupciserial.inf for QEMU, based on MSPORTS.INF ; The driver itself is shipped with Windows (serial.sys). This is -; just a inf file to tell windows which pci id the serial pci card +; just an inf file to tell windows which pci id the serial pci card ; emulated by qemu has, and to apply a name tag to it which windows ; will show in the device manager. diff --git a/docs/specs/acpi_nvdimm.txt b/docs/specs/acpi_nvdimm.txt index 3f322e6f55..3ec42ecbce 100644 --- a/docs/specs/acpi_nvdimm.txt +++ b/docs/specs/acpi_nvdimm.txt @@ -72,7 +72,8 @@ for NVDIMM ACPI. Memory: QEMU uses BIOS Linker/loader feature to ask BIOS to allocate a memory - page and dynamically patch its into a int32 object named "MEMA" in ACPI. + page and dynamically patch its address into an int32 object named "MEMA" + in ACPI. This page is RAM-based and it is used to transfer data between _DSM method and QEMU. If ACPI has control, this pages is owned by ACPI which diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt index bb99a0257e..4d53e5c7d9 100644 --- a/docs/specs/pci-ids.txt +++ b/docs/specs/pci-ids.txt @@ -62,6 +62,7 @@ PCI devices (other than virtio): 1b36:000a PCI-PCI bridge (multiseat) 1b36:000b PCIe Expander Bridge (-device pxb-pcie) 1b36:000d PCI xhci usb host adapter +1b36:000f mdpy (mdev sample device), linux/samples/vfio-mdev/mdpy.c All these devices are documented in docs/specs. diff --git a/docs/specs/ppc-spapr-hcalls.txt b/docs/specs/ppc-spapr-hcalls.txt index 5bd8eab78f..93fe3da91b 100644 --- a/docs/specs/ppc-spapr-hcalls.txt +++ b/docs/specs/ppc-spapr-hcalls.txt @@ -10,7 +10,7 @@ calls which are mostly used as a private interface between the firmware running in the guest and QEMU. All those hypercalls start at hcall number 0xf000 which correspond -to a implementation specific range in PAPR. +to an implementation specific range in PAPR. - H_RTAS (0xf000) diff --git a/docs/specs/ppc-spapr-hotplug.txt b/docs/specs/ppc-spapr-hotplug.txt index f57e2a09c6..cc7833108e 100644 --- a/docs/specs/ppc-spapr-hotplug.txt +++ b/docs/specs/ppc-spapr-hotplug.txt @@ -387,4 +387,23 @@ Each LMB list entry consists of the following elements: - A 32bit flags word. The bit at bit position 0x00000008 defines whether the LMB is assigned to the 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 dyanamically 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 diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt index d1d71571e9..0e9bbebe1d 100644 --- a/docs/specs/tpm.txt +++ b/docs/specs/tpm.txt @@ -98,7 +98,7 @@ QEMU files related to the TPM passthrough device: Command line to start QEMU with the TPM passthrough device using the host's hardware TPM /dev/tpm0: -qemu-system-x86_64 -display sdl -enable-kvm \ +qemu-system-x86_64 -display sdl -accel kvm \ -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \ -device tpm-tis,tpmdev=tpm0 test.img @@ -164,7 +164,7 @@ swtpm socket --tpmstate dir=/tmp/mytpm1 \ Command line to start QEMU with the TPM emulator device communicating with the swtpm: -qemu-system-x86_64 -display sdl -enable-kvm \ +qemu-system-x86_64 -display sdl -accel kvm \ -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ -tpmdev emulator,id=tpm0,chardev=chrtpm \ @@ -200,3 +200,109 @@ crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 PCR-00: 35 4E 3B CE 23 9F 38 59 ... ... PCR-23: 00 00 00 00 00 00 00 00 ... + + +=== Migration with the TPM emulator === + +The TPM emulator supports the following types of virtual machine migration: + +- VM save / restore (migration into a file) +- Network migration +- Snapshotting (migration into storage like QoW2 or QED) + +The following command sequences can be used to test VM save / restore. + + +In a 1st terminal start an instance of a swtpm using the following command: + +mkdir /tmp/mytpm1 +swtpm socket --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ + --log level=20 --tpm2 + +In a 2nd terminal start the VM: + +qemu-system-x86_64 -display sdl -accel kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -monitor stdio \ + test.img + +Verify that the attached TPM is working as expected using applications inside +the VM. + +To store the state of the VM use the following command in the QEMU monitor in +the 2nd terminal: + +(qemu) migrate "exec:cat > testvm.bin" +(qemu) quit + +At this point a file called 'testvm.bin' should exists and the swtpm and QEMU +processes should have ended. + +To test 'VM restore' you have to start the swtpm with the same parameters +as before. If previously a TPM 2 [--tpm2] was saved, --tpm2 must now be +passed again on the command line. + +In the 1st terminal restart the swtpm with the same command line as before: + +swtpm socket --tpmstate dir=/tmp/mytpm1 \ + --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \ + --log level=20 --tpm2 + +In the 2nd terminal restore the state of the VM using the additional +'-incoming' option. + +qemu-system-x86_64 -display sdl -accel kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -incoming "exec:cat < testvm.bin" \ + test.img + + +Troubleshooting migration: + +There are several reasons why migration may fail. In case of problems, +please ensure that the command lines adhere to the following rules and, +if possible, that identical versions of QEMU and swtpm are used at all +times. + +VM save and restore: + - QEMU command line parameters should be identical apart from the + '-incoming' option on VM restore + - swtpm command line parameters should be identical + +VM migration to 'localhost': + - QEMU command line parameters should be identical apart from the + '-incoming' option on the destination side + - swtpm command line parameters should point to two different + directories on the source and destination swtpm (--tpmstate dir=...) + (especially if different versions of libtpms were to be used on the + same machine). + +VM migration across the network: + - QEMU command line parameters should be identical apart from the + '-incoming' option on the destination side + - swtpm command line parameters should be identical + +VM Snapshotting: + - QEMU command line parameters should be identical + - swtpm command line parameters should be identical + + +Besides that, migration failure reasons on the swtpm level may include +the following: + + - the versions of the swtpm on the source and destination sides are + incompatible + - downgrading of TPM state may not be supported + - the source and destination libtpms were compiled with different + compile-time options and the destination side refuses to accept the + state + - different migration keys are used on the source and destination side + and the destination side cannot decrypt the migrated state + (swtpm ... --migration-key ... ) diff --git a/docs/usb2.txt b/docs/usb2.txt index 09df45b5b1..f63c8d9465 100644 --- a/docs/usb2.txt +++ b/docs/usb2.txt @@ -1,27 +1,52 @@ -USB 2.0 Quick Start -=================== +USB Quick Start +=============== -The QEMU EHCI Adapter can be used with and without companion -controllers. See below for the companion controller mode. +XHCI controller support +----------------------- -When not running in companion controller mode there are two completely -separate USB busses: One USB 1.1 bus driven by the UHCI controller and -one USB 2.0 bus driven by the EHCI controller. Devices must be -attached to the correct controller manually. +QEMU has XHCI host adapter support. The XHCI hardware design is much +more virtualization-friendly when compared to EHCI and UHCI, thus XHCI +emulation uses less resources (especially cpu). So if your guest +supports XHCI (which should be the case for any operating system +released around 2010 or later) we recommend using it: -The '-usb' switch will make qemu create the UHCI controller as part of + qemu -device qemu-xhci + +XHCI supports USB 1.1, USB 2.0 and USB 3.0 devices, so this is the +only controller you need. With only a single USB controller (and +therefore only a single USB bus) present in the system there is no +need to use the bus= parameter when adding USB devices. + + +EHCI controller support +----------------------- + +The QEMU EHCI Adapter supports USB 2.0 devices. It can be used either +standalone or with companion controllers (UHCI, OHCI) for USB 1.1 +devices. The companion controller setup is more convenient to use +because it provides a single USB bus supporting both USB 2.0 and USB +1.1 devices. See next section for details. + +When running EHCI in standalone mode you can add UHCI or OHCI +controllers for USB 1.1 devices too. Each controller creates its own +bus though, so there are two completely separate USB buses: One USB +1.1 bus driven by the UHCI controller and one USB 2.0 bus driven by +the EHCI controller. Devices must be attached to the correct +controller manually. + +The easiest way to add a UHCI controller to a 'pc' machine is the +'-usb' switch. QEMU will create the UHCI controller as function of the PIIX3 chipset. The USB 1.1 bus will carry the name "usb-bus.0". You can use the standard -device switch to add a EHCI controller to your virtual machine. It is strongly recommended to specify an ID for -the controller so the USB 2.0 bus gets a individual name, for example +the controller so the USB 2.0 bus gets an individual name, for example '-device usb-ehci,id=ehci". This will give you a USB 2.0 bus named "ehci.0". -I strongly recommend to also use -device to attach usb devices because -you can specify the bus they should be attached to this way. Here is -a complete example: +When adding USB devices using the -device switch you can specify the +bus they should be attached to. Here is a complete example: qemu -M pc ${otheroptions} \ -drive if=none,id=usbstick,file=/path/to/image \ @@ -30,58 +55,44 @@ a complete example: -device usb-tablet,bus=usb-bus.0 \ -device usb-storage,bus=ehci.0,drive=usbstick -This attaches a usb tablet to the UHCI adapter and a usb mass storage +This attaches a USB tablet to the UHCI adapter and a USB mass storage device to the EHCI adapter. Companion controller support ---------------------------- -Companion controller support has been added recently. The operational -model described above with two completely separate busses still works -fine. Additionally the UHCI and OHCI controllers got the ability to -attach to a usb bus created by EHCI as companion controllers. This is -done by specifying the masterbus and firstport properties. masterbus -specifies the bus name the controller should attach to. firstport -specifies the first port the controller should attach to, which is -needed as usually one ehci controller with six ports has three uhci -companion controllers with two ports each. +The UHCI and OHCI controllers can attach to a USB bus created by EHCI +as companion controllers. This is done by specifying the masterbus +and firstport properties. masterbus specifies the bus name the +controller should attach to. firstport specifies the first port the +controller should attach to, which is needed as usually one EHCI +controller with six ports has three UHCI companion controllers with +two ports each. -There is a config file in docs which will do all this for you, just -try ... +There is a config file in docs which will do all this for +you, just try ... qemu -readconfig docs/config/ich9-ehci-uhci.cfg -... then use "bus=ehci.0" to assign your usb devices to that bus. +... then use "bus=ehci.0" to assign your USB devices to that bus. - -xhci controller support ------------------------ - -There is also xhci host controller support available. It got a lot -less testing than ehci and there are a bunch of known limitations, so -ehci may work better for you. On the other hand the xhci hardware -design is much more virtualization-friendly, thus xhci emulation uses -less resources (especially cpu). If you want to give xhci a try -use this to add the host controller ... - - qemu -device nec-usb-xhci,id=xhci - -... then use "bus=xhci.0" when assigning usb devices. +Using the '-usb' switch for 'q35' machines will create a similar +USB controller configuration. More USB tips & tricks ====================== -Recently the usb pass through driver (also known as usb-host) and the -qemu usb subsystem gained a few capabilities which are available only +Recently the USB pass through driver (also known as usb-host) and the +QEMU USB subsystem gained a few capabilities which are available only via qdev properties, i,e. when using '-device'. physical port addressing ------------------------ -First you can (for all usb devices) specify the physical port where +First you can (for all USB devices) specify the physical port where the device will show up in the guest. This can be done using the "port" property. UHCI has two root ports (1,2). EHCI has four root ports (1-4), the emulated (1.1) USB hub has eight ports. @@ -94,7 +105,7 @@ Plugging a hub into UHCI port 2 works like this: -device usb-hub,bus=usb-bus.0,port=2 -Plugging a virtual usb stick into port 4 of the hub just plugged works +Plugging a virtual USB stick into port 4 of the hub just plugged works this way: -device usb-storage,bus=usb-bus.0,port=2.4,drive=... @@ -143,7 +154,7 @@ practice only a few combinations are useful: Note that USB 1.1 devices are handled by UHCI/OHCI and USB 2.0 by EHCI. That means a device plugged into the very same physical port -may show up on different busses depending on the speed. The port I'm +may show up on different buses depending on the speed. The port I'm using for testing is bus 1 + port 1 for 2.0 devices and bus 3 + port 1 for 1.1 devices. Passing through any device plugged into that port and also assign them to the correct bus can be done this way: diff --git a/docs/virtio-balloon-stats.txt b/docs/virtio-balloon-stats.txt index 7a66d25da5..9985e1dffc 100644 --- a/docs/virtio-balloon-stats.txt +++ b/docs/virtio-balloon-stats.txt @@ -34,6 +34,8 @@ which will return a dictionary containing: - stat-total-memory - stat-available-memory - stat-disk-caches + - stat-htlb-pgalloc + - stat-htlb-pgfail o A key named last-update, which contains the last stats update timestamp in seconds. Since this timestamp is generated by the host, diff --git a/dump.c b/dump.c index b54cd42b21..04467b353e 100644 --- a/dump.c +++ b/dump.c @@ -29,6 +29,10 @@ #include "qemu/error-report.h" #include "hw/misc/vmcoreinfo.h" +#ifdef TARGET_X86_64 +#include "win_dump.h" +#endif + #include #ifdef CONFIG_LZO #include @@ -1866,7 +1870,11 @@ static void dump_process(DumpState *s, Error **errp) Error *local_err = NULL; DumpQueryResult *result = NULL; - if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) { + if (s->has_format && s->format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) { +#ifdef TARGET_X86_64 + create_win_dump(s, &local_err); +#endif + } else if (s->has_format && s->format != DUMP_GUEST_MEMORY_FORMAT_ELF) { create_kdump_vmcore(s, &local_err); } else { create_vmcore(s, &local_err); @@ -1970,6 +1978,13 @@ void qmp_dump_guest_memory(bool paging, const char *file, } #endif +#ifndef TARGET_X86_64 + if (has_format && format == DUMP_GUEST_MEMORY_FORMAT_WIN_DMP) { + error_setg(errp, "Windows dump is only available for x86-64"); + return; + } +#endif + #if !defined(WIN32) if (strstart(file, "fd:", &p)) { fd = monitor_get_fd(cur_mon, p, errp); @@ -2044,5 +2059,12 @@ DumpGuestMemoryCapability *qmp_query_dump_guest_memory_capability(Error **errp) item->value = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY; #endif + /* Windows dump is available only if target is x86_64 */ +#ifdef TARGET_X86_64 + item->next = g_malloc0(sizeof(DumpGuestMemoryFormatList)); + item = item->next; + item->value = DUMP_GUEST_MEMORY_FORMAT_WIN_DMP; +#endif + return cap; } diff --git a/exec.c b/exec.c index 02b1efebb7..4f5df07b6a 100644 --- a/exec.c +++ b/exec.c @@ -104,6 +104,9 @@ static MemoryRegion io_mem_unassigned; * (Set during postcopy) */ #define RAM_UF_ZEROPAGE (1 << 3) + +/* RAM can be migrated */ +#define RAM_MIGRATABLE (1 << 4) #endif #ifdef TARGET_PAGE_BITS_VARY @@ -461,6 +464,79 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x return section; } +/** + * address_space_translate_iommu - translate an address through an IOMMU + * memory region and then through the target address space. + * + * @iommu_mr: the IOMMU memory region that we start the translation from + * @addr: the address to be translated through the MMU + * @xlat: the translated address offset within the destination memory region. + * It cannot be %NULL. + * @plen_out: valid read/write length of the translated address. It + * cannot be %NULL. + * @page_mask_out: page mask for the translated address. This + * should only be meaningful for IOMMU translated + * addresses, since there may be huge pages that this bit + * would tell. It can be %NULL if we don't care about it. + * @is_write: whether the translation operation is for write + * @is_mmio: whether this can be MMIO, set true if it can + * @target_as: the address space targeted by the IOMMU + * @attrs: transaction attributes + * + * This function is called from RCU critical section. It is the common + * part of flatview_do_translate and address_space_translate_cached. + */ +static MemoryRegionSection address_space_translate_iommu(IOMMUMemoryRegion *iommu_mr, + hwaddr *xlat, + hwaddr *plen_out, + hwaddr *page_mask_out, + bool is_write, + bool is_mmio, + AddressSpace **target_as, + MemTxAttrs attrs) +{ + MemoryRegionSection *section; + hwaddr page_mask = (hwaddr)-1; + + do { + hwaddr addr = *xlat; + IOMMUMemoryRegionClass *imrc = memory_region_get_iommu_class_nocheck(iommu_mr); + int iommu_idx = 0; + IOMMUTLBEntry iotlb; + + if (imrc->attrs_to_index) { + iommu_idx = imrc->attrs_to_index(iommu_mr, attrs); + } + + iotlb = imrc->translate(iommu_mr, addr, is_write ? + IOMMU_WO : IOMMU_RO, iommu_idx); + + if (!(iotlb.perm & (1 << is_write))) { + goto unassigned; + } + + addr = ((iotlb.translated_addr & ~iotlb.addr_mask) + | (addr & iotlb.addr_mask)); + page_mask &= iotlb.addr_mask; + *plen_out = MIN(*plen_out, (addr | iotlb.addr_mask) - addr + 1); + *target_as = iotlb.target_as; + + section = address_space_translate_internal( + address_space_to_dispatch(iotlb.target_as), addr, xlat, + plen_out, is_mmio); + + iommu_mr = memory_region_get_iommu(section->mr); + } while (unlikely(iommu_mr)); + + if (page_mask_out) { + *page_mask_out = page_mask; + } + return *section; + +unassigned: + return (MemoryRegionSection) { .mr = &io_mem_unassigned }; +} + /** * flatview_do_translate - translate an address in FlatView * @@ -476,6 +552,8 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x * would tell. It can be @NULL if we don't care about it. * @is_write: whether the translation operation is for write * @is_mmio: whether this can be MMIO, set true if it can + * @target_as: the address space targeted by the IOMMU + * @attrs: memory transaction attributes * * This function is called from RCU critical section */ @@ -486,68 +564,39 @@ static MemoryRegionSection flatview_do_translate(FlatView *fv, hwaddr *page_mask_out, bool is_write, bool is_mmio, - AddressSpace **target_as) + AddressSpace **target_as, + MemTxAttrs attrs) { - IOMMUTLBEntry iotlb; MemoryRegionSection *section; IOMMUMemoryRegion *iommu_mr; - IOMMUMemoryRegionClass *imrc; - hwaddr page_mask = (hwaddr)(-1); hwaddr plen = (hwaddr)(-1); - if (plen_out) { - plen = *plen_out; + if (!plen_out) { + plen_out = &plen; } - for (;;) { - section = address_space_translate_internal( - flatview_to_dispatch(fv), addr, &addr, - &plen, is_mmio); + section = address_space_translate_internal( + flatview_to_dispatch(fv), addr, xlat, + plen_out, is_mmio); - iommu_mr = memory_region_get_iommu(section->mr); - if (!iommu_mr) { - break; - } - imrc = memory_region_get_iommu_class_nocheck(iommu_mr); - - iotlb = imrc->translate(iommu_mr, addr, is_write ? - IOMMU_WO : IOMMU_RO); - addr = ((iotlb.translated_addr & ~iotlb.addr_mask) - | (addr & iotlb.addr_mask)); - page_mask &= iotlb.addr_mask; - plen = MIN(plen, (addr | iotlb.addr_mask) - addr + 1); - if (!(iotlb.perm & (1 << is_write))) { - goto translate_fail; - } - - fv = address_space_to_flatview(iotlb.target_as); - *target_as = iotlb.target_as; + iommu_mr = memory_region_get_iommu(section->mr); + if (unlikely(iommu_mr)) { + return address_space_translate_iommu(iommu_mr, xlat, + plen_out, page_mask_out, + is_write, is_mmio, + target_as, attrs); } - - *xlat = addr; - - if (page_mask == (hwaddr)(-1)) { - /* Not behind an IOMMU, use default page size. */ - page_mask = ~TARGET_PAGE_MASK; - } - if (page_mask_out) { - *page_mask_out = page_mask; - } - - if (plen_out) { - *plen_out = plen; + /* Not behind an IOMMU, use default page size. */ + *page_mask_out = ~TARGET_PAGE_MASK; } return *section; - -translate_fail: - return (MemoryRegionSection) { .mr = &io_mem_unassigned }; } /* Called from RCU critical section */ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, - bool is_write) + bool is_write, MemTxAttrs attrs) { MemoryRegionSection section; hwaddr xlat, page_mask; @@ -557,7 +606,8 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, * but page mask. */ section = flatview_do_translate(address_space_to_flatview(as), addr, &xlat, - NULL, &page_mask, is_write, false, &as); + NULL, &page_mask, is_write, false, &as, + attrs); /* Illegal translation */ if (section.mr == &io_mem_unassigned) { @@ -583,7 +633,8 @@ iotlb_fail: /* Called from RCU critical section */ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, - hwaddr *plen, bool is_write) + hwaddr *plen, bool is_write, + MemTxAttrs attrs) { MemoryRegion *mr; MemoryRegionSection section; @@ -591,7 +642,7 @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, /* This can be MMIO, so setup MMIO bit. */ section = flatview_do_translate(fv, addr, xlat, plen, NULL, - is_write, true, &as); + is_write, true, &as, attrs); mr = section.mr; if (xen_enabled() && memory_access_is_direct(mr, is_write)) { @@ -602,18 +653,144 @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, return mr; } +typedef struct TCGIOMMUNotifier { + IOMMUNotifier n; + MemoryRegion *mr; + CPUState *cpu; + int iommu_idx; + bool active; +} TCGIOMMUNotifier; + +static void tcg_iommu_unmap_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) +{ + TCGIOMMUNotifier *notifier = container_of(n, TCGIOMMUNotifier, n); + + if (!notifier->active) { + return; + } + tlb_flush(notifier->cpu); + notifier->active = false; + /* We leave the notifier struct on the list to avoid reallocating it later. + * Generally the number of IOMMUs a CPU deals with will be small. + * In any case we can't unregister the iommu notifier from a notify + * callback. + */ +} + +static void tcg_register_iommu_notifier(CPUState *cpu, + IOMMUMemoryRegion *iommu_mr, + int iommu_idx) +{ + /* Make sure this CPU has an IOMMU notifier registered for this + * IOMMU/IOMMU index combination, so that we can flush its TLB + * when the IOMMU tells us the mappings we've cached have changed. + */ + MemoryRegion *mr = MEMORY_REGION(iommu_mr); + TCGIOMMUNotifier *notifier; + int i; + + for (i = 0; i < cpu->iommu_notifiers->len; i++) { + notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i); + if (notifier->mr == mr && notifier->iommu_idx == iommu_idx) { + break; + } + } + if (i == cpu->iommu_notifiers->len) { + /* Not found, add a new entry at the end of the array */ + cpu->iommu_notifiers = g_array_set_size(cpu->iommu_notifiers, i + 1); + notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i); + + notifier->mr = mr; + notifier->iommu_idx = iommu_idx; + notifier->cpu = cpu; + /* Rather than trying to register interest in the specific part + * of the iommu's address space that we've accessed and then + * expand it later as subsequent accesses touch more of it, we + * just register interest in the whole thing, on the assumption + * that iommu reconfiguration will be rare. + */ + iommu_notifier_init(¬ifier->n, + tcg_iommu_unmap_notify, + IOMMU_NOTIFIER_UNMAP, + 0, + HWADDR_MAX, + iommu_idx); + memory_region_register_iommu_notifier(notifier->mr, ¬ifier->n); + } + + if (!notifier->active) { + notifier->active = true; + } +} + +static void tcg_iommu_free_notifier_list(CPUState *cpu) +{ + /* Destroy the CPU's notifier list */ + int i; + TCGIOMMUNotifier *notifier; + + for (i = 0; i < cpu->iommu_notifiers->len; i++) { + notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i); + memory_region_unregister_iommu_notifier(notifier->mr, ¬ifier->n); + } + g_array_free(cpu->iommu_notifiers, true); +} + /* Called from RCU critical section */ MemoryRegionSection * address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, - hwaddr *xlat, hwaddr *plen) + hwaddr *xlat, hwaddr *plen, + MemTxAttrs attrs, int *prot) { MemoryRegionSection *section; + IOMMUMemoryRegion *iommu_mr; + IOMMUMemoryRegionClass *imrc; + IOMMUTLBEntry iotlb; + int iommu_idx; AddressSpaceDispatch *d = atomic_rcu_read(&cpu->cpu_ases[asidx].memory_dispatch); - section = address_space_translate_internal(d, addr, xlat, plen, false); + for (;;) { + section = address_space_translate_internal(d, addr, &addr, plen, false); + + iommu_mr = memory_region_get_iommu(section->mr); + if (!iommu_mr) { + break; + } + + imrc = memory_region_get_iommu_class_nocheck(iommu_mr); + + iommu_idx = imrc->attrs_to_index(iommu_mr, attrs); + tcg_register_iommu_notifier(cpu, iommu_mr, iommu_idx); + /* We need all the permissions, so pass IOMMU_NONE so the IOMMU + * doesn't short-cut its translation table walk. + */ + iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, iommu_idx); + addr = ((iotlb.translated_addr & ~iotlb.addr_mask) + | (addr & iotlb.addr_mask)); + /* Update the caller's prot bits to remove permissions the IOMMU + * is giving us a failure response for. If we get down to no + * permissions left at all we can give up now. + */ + if (!(iotlb.perm & IOMMU_RO)) { + *prot &= ~(PAGE_READ | PAGE_EXEC); + } + if (!(iotlb.perm & IOMMU_WO)) { + *prot &= ~PAGE_WRITE; + } + + if (!*prot) { + goto translate_fail; + } + + d = flatview_to_dispatch(address_space_to_flatview(iotlb.target_as)); + } assert(!memory_region_is_iommu(section->mr)); + *xlat = addr; return section; + +translate_fail: + return &d->map.sections[PHYS_SECTION_UNASSIGNED]; } #endif @@ -772,6 +949,9 @@ void cpu_exec_unrealizefn(CPUState *cpu) if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { vmstate_unregister(NULL, &vmstate_cpu_common, cpu); } +#ifndef CONFIG_USER_ONLY + tcg_iommu_free_notifier_list(cpu); +#endif } Property cpu_common_props[] = { @@ -819,6 +999,8 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp) if (cc->vmsd != NULL) { vmstate_register(NULL, cpu->cpu_index, cc->vmsd, cpu); } + + cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier)); #endif } @@ -846,15 +1028,40 @@ const char *parse_cpu_model(const char *cpu_model) } #if defined(CONFIG_USER_ONLY) -static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) +void tb_invalidate_phys_addr(target_ulong addr) { mmap_lock(); - tb_lock(); - tb_invalidate_phys_page_range(pc, pc + 1, 0); - tb_unlock(); + tb_invalidate_phys_page_range(addr, addr + 1, 0); mmap_unlock(); } + +static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) +{ + tb_invalidate_phys_addr(pc); +} #else +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs) +{ + ram_addr_t ram_addr; + MemoryRegion *mr; + hwaddr l = 1; + + if (!tcg_enabled()) { + return; + } + + rcu_read_lock(); + mr = address_space_translate(as, addr, &addr, &l, false, attrs); + if (!(memory_region_is_ram(mr) + || memory_region_is_romd(mr))) { + rcu_read_unlock(); + return; + } + ram_addr = memory_region_get_ram_addr(mr) + addr; + tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); + rcu_read_unlock(); +} + static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) { MemTxAttrs attrs; @@ -863,7 +1070,7 @@ static void breakpoint_invalidate(CPUState *cpu, target_ulong pc) if (phys != -1) { /* Locks grabbed by tb_invalidate_phys_addr */ tb_invalidate_phys_addr(cpu->cpu_ases[asidx].as, - phys | (pc & ~TARGET_PAGE_MASK)); + phys | (pc & ~TARGET_PAGE_MASK), attrs); } } #endif @@ -1089,6 +1296,7 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) struct sigaction act; sigfillset(&act.sa_mask); act.sa_handler = SIG_DFL; + act.sa_flags = 0; sigaction(SIGABRT, &act, NULL); } #endif @@ -1142,6 +1350,7 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) RAMBlock *block; ram_addr_t end; + assert(tcg_enabled()); end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; @@ -1488,19 +1697,13 @@ void ram_block_dump(Monitor *mon) */ static int find_max_supported_pagesize(Object *obj, void *opaque) { - char *mem_path; long *hpsize_min = opaque; if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) { - mem_path = object_property_get_str(obj, "mem-path", NULL); - if (mem_path) { - long hpsize = qemu_mempath_getpagesize(mem_path); - g_free(mem_path); - if (hpsize < *hpsize_min) { - *hpsize_min = hpsize; - } - } else { - *hpsize_min = getpagesize(); + long hpsize = host_memory_backend_pagesize(MEMORY_BACKEND(obj)); + + if (hpsize < *hpsize_min) { + *hpsize_min = hpsize; } } @@ -1513,11 +1716,7 @@ long qemu_getrampagesize(void) long mainrampagesize; Object *memdev_root; - if (mem_path) { - mainrampagesize = qemu_mempath_getpagesize(mem_path); - } else { - mainrampagesize = getpagesize(); - } + mainrampagesize = qemu_mempath_getpagesize(mem_path); /* it's possible we have memory-backend objects with * hugepage-backed RAM. these may get mapped into system @@ -1646,6 +1845,10 @@ static void *file_ram_alloc(RAMBlock *block, " must be multiples of page size 0x%zx", block->mr->align, block->page_size); return NULL; + } else if (block->mr->align && !is_power_of_2(block->mr->align)) { + error_setg(errp, "alignment 0x%" PRIx64 + " must be a power of two", block->mr->align); + return NULL; } block->mr->align = MAX(block->page_size, block->mr->align); #if defined(__s390x__) @@ -1758,7 +1961,7 @@ static ram_addr_t find_ram_offset(ram_addr_t size) return offset; } -unsigned long last_ram_page(void) +static unsigned long last_ram_page(void) { RAMBlock *block; ram_addr_t last = 0; @@ -1807,6 +2010,21 @@ void qemu_ram_set_uf_zeroable(RAMBlock *rb) rb->flags |= RAM_UF_ZEROPAGE; } +bool qemu_ram_is_migratable(RAMBlock *rb) +{ + return rb->flags & RAM_MIGRATABLE; +} + +void qemu_ram_set_migratable(RAMBlock *rb) +{ + rb->flags |= RAM_MIGRATABLE; +} + +void qemu_ram_unset_migratable(RAMBlock *rb) +{ + rb->flags &= ~RAM_MIGRATABLE; +} + /* Called with iothread lock held. */ void qemu_ram_set_idstr(RAMBlock *new_block, const char *name, DeviceState *dev) { @@ -2456,21 +2674,22 @@ void memory_notdirty_write_prepare(NotDirtyInfo *ndi, ndi->ram_addr = ram_addr; ndi->mem_vaddr = mem_vaddr; ndi->size = size; - ndi->locked = false; + ndi->pages = NULL; assert(tcg_enabled()); if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { - ndi->locked = true; - tb_lock(); - tb_invalidate_phys_page_fast(ram_addr, size); + ndi->pages = page_collection_lock(ram_addr, ram_addr + size); + tb_invalidate_phys_page_fast(ndi->pages, ram_addr, size); } } /* Called within RCU critical section. */ void memory_notdirty_write_complete(NotDirtyInfo *ndi) { - if (ndi->locked) { - tb_unlock(); + if (ndi->pages) { + assert(tcg_enabled()); + page_collection_unlock(ndi->pages); + ndi->pages = NULL; } /* Set both VGA and migration bits for simplicity and to remove @@ -2494,27 +2713,13 @@ static void notdirty_mem_write(void *opaque, hwaddr ram_addr, memory_notdirty_write_prepare(&ndi, current_cpu, current_cpu->mem_io_vaddr, ram_addr, size); - switch (size) { - case 1: - stb_p(qemu_map_ram_ptr(NULL, ram_addr), val); - break; - case 2: - stw_p(qemu_map_ram_ptr(NULL, ram_addr), val); - break; - case 4: - stl_p(qemu_map_ram_ptr(NULL, ram_addr), val); - break; - case 8: - stq_p(qemu_map_ram_ptr(NULL, ram_addr), val); - break; - default: - abort(); - } + stn_p(qemu_map_ram_ptr(NULL, ram_addr), size, val); memory_notdirty_write_complete(&ndi); } static bool notdirty_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return is_write; } @@ -2571,18 +2776,16 @@ static void check_watchpoint(int offset, int len, MemTxAttrs attrs, int flags) } cpu->watchpoint_hit = wp; - /* Both tb_lock and iothread_mutex will be reset when - * cpu_loop_exit or cpu_loop_exit_noexc longjmp - * back into the cpu_exec main loop. - */ - tb_lock(); + mmap_lock(); tb_check_watchpoint(cpu); if (wp->flags & BP_STOP_BEFORE_ACCESS) { cpu->exception_index = EXCP_DEBUG; + mmap_unlock(); cpu_loop_exit(cpu); } else { /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | curr_cflags(); + mmap_unlock(); cpu_loop_exit_noexc(cpu); } } @@ -2671,7 +2874,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, const uint8_t *buf, int len); static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, - bool is_write); + bool is_write, MemTxAttrs attrs); static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data, unsigned len, MemTxAttrs attrs) @@ -2688,22 +2891,8 @@ static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data, if (res) { return res; } - switch (len) { - case 1: - *data = ldub_p(buf); - return MEMTX_OK; - case 2: - *data = lduw_p(buf); - return MEMTX_OK; - case 4: - *data = ldl_p(buf); - return MEMTX_OK; - case 8: - *data = ldq_p(buf); - return MEMTX_OK; - default: - abort(); - } + *data = ldn_p(buf, len); + return MEMTX_OK; } static MemTxResult subpage_write(void *opaque, hwaddr addr, @@ -2717,27 +2906,13 @@ static MemTxResult subpage_write(void *opaque, hwaddr addr, " value %"PRIx64"\n", __func__, subpage, len, addr, value); #endif - switch (len) { - case 1: - stb_p(buf, value); - break; - case 2: - stw_p(buf, value); - break; - case 4: - stl_p(buf, value); - break; - case 8: - stq_p(buf, value); - break; - default: - abort(); - } + stn_p(buf, len, value); return flatview_write(subpage->fv, addr + subpage->base, attrs, buf, len); } static bool subpage_accepts(void *opaque, hwaddr addr, - unsigned len, bool is_write) + unsigned len, bool is_write, + MemTxAttrs attrs) { subpage_t *subpage = opaque; #if defined(DEBUG_SUBPAGE) @@ -2746,7 +2921,7 @@ static bool subpage_accepts(void *opaque, hwaddr addr, #endif return flatview_access_valid(subpage->fv, addr + subpage->base, - len, is_write); + len, is_write, attrs); } static const MemoryRegionOps subpage_ops = { @@ -2820,7 +2995,8 @@ static void readonly_mem_write(void *opaque, hwaddr addr, } static bool readonly_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return is_write; } @@ -2844,14 +3020,15 @@ static const MemoryRegionOps readonly_mem_ops = { }, }; -MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index, MemTxAttrs attrs) +MemoryRegionSection *iotlb_to_section(CPUState *cpu, + hwaddr index, MemTxAttrs attrs) { int asidx = cpu_asidx_from_attrs(cpu, attrs); CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; AddressSpaceDispatch *d = atomic_rcu_read(&cpuas->memory_dispatch); MemoryRegionSection *sections = d->map.sections; - return sections[index & ~TARGET_PAGE_MASK].mr; + return §ions[index & ~TARGET_PAGE_MASK]; } static void io_mem_init(void) @@ -2902,6 +3079,7 @@ static void tcg_commit(MemoryListener *listener) CPUAddressSpace *cpuas; AddressSpaceDispatch *d; + assert(tcg_enabled()); /* since each CPU stores ram addresses in its TLB cache, we must reset the modified entries */ cpuas = container_of(listener, CPUAddressSpace, tcg_as_listener); @@ -2999,9 +3177,7 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, } if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { assert(tcg_enabled()); - tb_lock(); tb_invalidate_phys_range(addr, addr + length); - tb_unlock(); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); @@ -3075,34 +3251,8 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, l = memory_access_size(mr, l, addr1); /* XXX: could force current_cpu to NULL to avoid potential bugs */ - switch (l) { - case 8: - /* 64 bit write access */ - val = ldq_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 8, - attrs); - break; - case 4: - /* 32 bit write access */ - val = (uint32_t)ldl_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 4, - attrs); - break; - case 2: - /* 16 bit write access */ - val = lduw_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 2, - attrs); - break; - case 1: - /* 8 bit write access */ - val = ldub_p(buf); - result |= memory_region_dispatch_write(mr, addr1, val, 1, - attrs); - break; - default: - abort(); - } + val = ldn_p(buf, l); + result |= memory_region_dispatch_write(mr, addr1, val, l, attrs); } else { /* RAM case */ ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); @@ -3124,7 +3274,7 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, } l = len; - mr = flatview_translate(fv, addr, &addr1, &l, true); + mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); } return result; @@ -3140,7 +3290,7 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, MemTxResult result = MEMTX_OK; l = len; - mr = flatview_translate(fv, addr, &addr1, &l, true); + mr = flatview_translate(fv, addr, &addr1, &l, true, attrs); result = flatview_write_continue(fv, addr, attrs, buf, len, addr1, l, mr); @@ -3163,34 +3313,8 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, /* I/O case */ release_lock |= prepare_mmio_access(mr); l = memory_access_size(mr, l, addr1); - switch (l) { - case 8: - /* 64 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 8, - attrs); - stq_p(buf, val); - break; - case 4: - /* 32 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 4, - attrs); - stl_p(buf, val); - break; - case 2: - /* 16 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 2, - attrs); - stw_p(buf, val); - break; - case 1: - /* 8 bit read access */ - result |= memory_region_dispatch_read(mr, addr1, &val, 1, - attrs); - stb_p(buf, val); - break; - default: - abort(); - } + result |= memory_region_dispatch_read(mr, addr1, &val, l, attrs); + stn_p(buf, l, val); } else { /* RAM case */ ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false); @@ -3211,7 +3335,7 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, } l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false); + mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); } return result; @@ -3226,7 +3350,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, MemoryRegion *mr; l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false); + mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); return flatview_read_continue(fv, addr, attrs, buf, len, addr1, l, mr); } @@ -3297,7 +3421,8 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as, rcu_read_lock(); while (len > 0) { l = len; - mr = address_space_translate(as, addr, &addr1, &l, true); + mr = address_space_translate(as, addr, &addr1, &l, true, + MEMTXATTRS_UNSPECIFIED); if (!(memory_region_is_ram(mr) || memory_region_is_romd(mr))) { @@ -3432,17 +3557,17 @@ static void cpu_notify_map_clients(void) } static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, - bool is_write) + bool is_write, MemTxAttrs attrs) { MemoryRegion *mr; hwaddr l, xlat; while (len > 0) { l = len; - mr = flatview_translate(fv, addr, &xlat, &l, is_write); + mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { l = memory_access_size(mr, l, addr); - if (!memory_region_access_valid(mr, xlat, l, is_write)) { + if (!memory_region_access_valid(mr, xlat, l, is_write, attrs)) { return false; } } @@ -3454,23 +3579,24 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, } bool address_space_access_valid(AddressSpace *as, hwaddr addr, - int len, bool is_write) + int len, bool is_write, + MemTxAttrs attrs) { FlatView *fv; bool result; rcu_read_lock(); fv = address_space_to_flatview(as); - result = flatview_access_valid(fv, addr, len, is_write); + result = flatview_access_valid(fv, addr, len, is_write, attrs); rcu_read_unlock(); return result; } static hwaddr flatview_extend_translation(FlatView *fv, hwaddr addr, - hwaddr target_len, - MemoryRegion *mr, hwaddr base, hwaddr len, - bool is_write) + hwaddr target_len, + MemoryRegion *mr, hwaddr base, hwaddr len, + bool is_write, MemTxAttrs attrs) { hwaddr done = 0; hwaddr xlat; @@ -3486,7 +3612,7 @@ flatview_extend_translation(FlatView *fv, hwaddr addr, len = target_len; this_mr = flatview_translate(fv, addr, &xlat, - &len, is_write); + &len, is_write, attrs); if (this_mr != mr || xlat != base + done) { return done; } @@ -3503,7 +3629,8 @@ flatview_extend_translation(FlatView *fv, hwaddr addr, void *address_space_map(AddressSpace *as, hwaddr addr, hwaddr *plen, - bool is_write) + bool is_write, + MemTxAttrs attrs) { hwaddr len = *plen; hwaddr l, xlat; @@ -3518,7 +3645,7 @@ void *address_space_map(AddressSpace *as, l = len; rcu_read_lock(); fv = address_space_to_flatview(as); - mr = flatview_translate(fv, addr, &xlat, &l, is_write); + mr = flatview_translate(fv, addr, &xlat, &l, is_write, attrs); if (!memory_access_is_direct(mr, is_write)) { if (atomic_xchg(&bounce.in_use, true)) { @@ -3546,7 +3673,7 @@ void *address_space_map(AddressSpace *as, memory_region_ref(mr); *plen = flatview_extend_translation(fv, addr, len, mr, xlat, - l, is_write); + l, is_write, attrs); ptr = qemu_ram_ptr_length(mr->ram_block, xlat, plen, true); rcu_read_unlock(); @@ -3590,7 +3717,8 @@ void *cpu_physical_memory_map(hwaddr addr, hwaddr *plen, int is_write) { - return address_space_map(&address_space_memory, addr, plen, is_write); + return address_space_map(&address_space_memory, addr, plen, is_write, + MEMTXATTRS_UNSPECIFIED); } void cpu_physical_memory_unmap(void *buffer, hwaddr len, @@ -3603,9 +3731,6 @@ void cpu_physical_memory_unmap(void *buffer, hwaddr len, #define ARG1 as #define SUFFIX #define TRANSLATE(...) address_space_translate(as, __VA_ARGS__) -#define IS_DIRECT(mr, is_write) memory_access_is_direct(mr, is_write) -#define MAP_RAM(mr, ofs) qemu_map_ram_ptr((mr)->ram_block, ofs) -#define INVALIDATE(mr, ofs, len) invalidate_and_set_dirty(mr, ofs, len) #define RCU_READ_LOCK(...) rcu_read_lock() #define RCU_READ_UNLOCK(...) rcu_read_unlock() #include "memory_ldst.inc.c" @@ -3616,33 +3741,134 @@ int64_t address_space_cache_init(MemoryRegionCache *cache, hwaddr len, bool is_write) { - cache->len = len; - cache->as = as; - cache->xlat = addr; - return len; + AddressSpaceDispatch *d; + hwaddr l; + MemoryRegion *mr; + + assert(len > 0); + + l = len; + cache->fv = address_space_get_flatview(as); + d = flatview_to_dispatch(cache->fv); + cache->mrs = *address_space_translate_internal(d, addr, &cache->xlat, &l, true); + + mr = cache->mrs.mr; + memory_region_ref(mr); + if (memory_access_is_direct(mr, is_write)) { + /* We don't care about the memory attributes here as we're only + * doing this if we found actual RAM, which behaves the same + * regardless of attributes; so UNSPECIFIED is fine. + */ + l = flatview_extend_translation(cache->fv, addr, len, mr, + cache->xlat, l, is_write, + MEMTXATTRS_UNSPECIFIED); + cache->ptr = qemu_ram_ptr_length(mr->ram_block, cache->xlat, &l, true); + } else { + cache->ptr = NULL; + } + + cache->len = l; + cache->is_write = is_write; + return l; } void address_space_cache_invalidate(MemoryRegionCache *cache, hwaddr addr, hwaddr access_len) { + assert(cache->is_write); + if (likely(cache->ptr)) { + invalidate_and_set_dirty(cache->mrs.mr, addr + cache->xlat, access_len); + } } void address_space_cache_destroy(MemoryRegionCache *cache) { - cache->as = NULL; + if (!cache->mrs.mr) { + return; + } + + if (xen_enabled()) { + xen_invalidate_map_cache_entry(cache->ptr); + } + memory_region_unref(cache->mrs.mr); + flatview_unref(cache->fv); + cache->mrs.mr = NULL; + cache->fv = NULL; +} + +/* Called from RCU critical section. This function has the same + * semantics as address_space_translate, but it only works on a + * predefined range of a MemoryRegion that was mapped with + * address_space_cache_init. + */ +static inline MemoryRegion *address_space_translate_cached( + MemoryRegionCache *cache, hwaddr addr, hwaddr *xlat, + hwaddr *plen, bool is_write, MemTxAttrs attrs) +{ + MemoryRegionSection section; + MemoryRegion *mr; + IOMMUMemoryRegion *iommu_mr; + AddressSpace *target_as; + + assert(!cache->ptr); + *xlat = addr + cache->xlat; + + mr = cache->mrs.mr; + iommu_mr = memory_region_get_iommu(mr); + if (!iommu_mr) { + /* MMIO region. */ + return mr; + } + + section = address_space_translate_iommu(iommu_mr, xlat, plen, + NULL, is_write, true, + &target_as, attrs); + return section.mr; +} + +/* Called from RCU critical section. address_space_read_cached uses this + * out of line function when the target is an MMIO or IOMMU region. + */ +void +address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr, + void *buf, int len) +{ + hwaddr addr1, l; + MemoryRegion *mr; + + l = len; + mr = address_space_translate_cached(cache, addr, &addr1, &l, false, + MEMTXATTRS_UNSPECIFIED); + flatview_read_continue(cache->fv, + addr, MEMTXATTRS_UNSPECIFIED, buf, len, + addr1, l, mr); +} + +/* Called from RCU critical section. address_space_write_cached uses this + * out of line function when the target is an MMIO or IOMMU region. + */ +void +address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, + const void *buf, int len) +{ + hwaddr addr1, l; + MemoryRegion *mr; + + l = len; + mr = address_space_translate_cached(cache, addr, &addr1, &l, true, + MEMTXATTRS_UNSPECIFIED); + flatview_write_continue(cache->fv, + addr, MEMTXATTRS_UNSPECIFIED, buf, len, + addr1, l, mr); } #define ARG1_DECL MemoryRegionCache *cache #define ARG1 cache -#define SUFFIX _cached -#define TRANSLATE(addr, ...) \ - address_space_translate(cache->as, cache->xlat + (addr), __VA_ARGS__) -#define IS_DIRECT(mr, is_write) true -#define MAP_RAM(mr, ofs) qemu_map_ram_ptr((mr)->ram_block, ofs) -#define INVALIDATE(mr, ofs, len) invalidate_and_set_dirty(mr, ofs, len) -#define RCU_READ_LOCK() rcu_read_lock() -#define RCU_READ_UNLOCK() rcu_read_unlock() +#define SUFFIX _cached_slow +#define TRANSLATE(...) address_space_translate_cached(cache, __VA_ARGS__) +#define RCU_READ_LOCK() ((void)0) +#define RCU_READ_UNLOCK() ((void)0) #include "memory_ldst.inc.c" /* virtual memory access for debug (includes writing to ROM) */ @@ -3726,7 +3952,8 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr) rcu_read_lock(); mr = address_space_translate(&address_space_memory, - phys_addr, &phys_addr, &l, false); + phys_addr, &phys_addr, &l, false, + MEMTXATTRS_UNSPECIFIED); res = !(memory_region_is_ram(mr) || memory_region_is_romd(mr)); rcu_read_unlock(); @@ -3750,6 +3977,26 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) return ret; } +int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque) +{ + RAMBlock *block; + int ret = 0; + + rcu_read_lock(); + RAMBLOCK_FOREACH(block) { + if (!qemu_ram_is_migratable(block)) { + continue; + } + ret = func(block->idstr, block->host, block->offset, + block->used_length, opaque); + if (ret) { + break; + } + } + rcu_read_unlock(); + return ret; +} + /* * Unmap pages of memory from start to start+length such that * they a) read as 0, b) Trigger whatever fault mechanism diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 27834af0de..16c0bcb6fa 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -79,82 +79,111 @@ this code that are retained. * version 2 or later. See the COPYING file in the top-level directory. */ -#if defined(TARGET_XTENSA) /* Define for architectures which deviate from IEEE in not supporting * signaling NaNs (so all NaNs are treated as quiet). */ +#if defined(TARGET_XTENSA) #define NO_SIGNALING_NANS 1 #endif -/*---------------------------------------------------------------------------- -| The pattern for a default generated half-precision NaN. -*----------------------------------------------------------------------------*/ -float16 float16_default_nan(float_status *status) +/* Define how the architecture discriminates signaling NaNs. + * This done with the most significant bit of the fraction. + * In IEEE 754-1985 this was implementation defined, but in IEEE 754-2008 + * the msb must be zero. MIPS is (so far) unique in supporting both the + * 2008 revision and backward compatibility with their original choice. + * Thus for MIPS we must make the choice at runtime. + */ +static inline flag snan_bit_is_one(float_status *status) { -#if defined(TARGET_ARM) - return const_float16(0x7E00); -#else - if (status->snan_bit_is_one) { - return const_float16(0x7DFF); - } else { #if defined(TARGET_MIPS) - return const_float16(0x7E00); + return status->snan_bit_is_one; +#elif defined(TARGET_HPPA) || defined(TARGET_UNICORE32) || defined(TARGET_SH4) + return 1; #else - return const_float16(0xFE00); -#endif - } + return 0; #endif } /*---------------------------------------------------------------------------- -| The pattern for a default generated single-precision NaN. +| For the deconstructed floating-point with fraction FRAC, return true +| if the fraction represents a signalling NaN; otherwise false. *----------------------------------------------------------------------------*/ -float32 float32_default_nan(float_status *status) + +static bool parts_is_snan_frac(uint64_t frac, float_status *status) { -#if defined(TARGET_SPARC) || defined(TARGET_M68K) - return const_float32(0x7FFFFFFF); -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_XTENSA) || defined(TARGET_S390X) || \ - defined(TARGET_TRICORE) || defined(TARGET_RISCV) - return const_float32(0x7FC00000); -#elif defined(TARGET_HPPA) - return const_float32(0x7FA00000); +#ifdef NO_SIGNALING_NANS + return false; #else - if (status->snan_bit_is_one) { - return const_float32(0x7FBFFFFF); - } else { -#if defined(TARGET_MIPS) - return const_float32(0x7FC00000); -#else - return const_float32(0xFFC00000); -#endif - } + flag msb = extract64(frac, DECOMPOSED_BINARY_POINT - 1, 1); + return msb == snan_bit_is_one(status); #endif } /*---------------------------------------------------------------------------- -| The pattern for a default generated double-precision NaN. +| The pattern for a default generated deconstructed floating-point NaN. *----------------------------------------------------------------------------*/ -float64 float64_default_nan(float_status *status) + +static FloatParts parts_default_nan(float_status *status) { + bool sign = 0; + uint64_t frac; + #if defined(TARGET_SPARC) || defined(TARGET_M68K) - return const_float64(LIT64(0x7FFFFFFFFFFFFFFF)); -#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_S390X) || defined(TARGET_RISCV) - return const_float64(LIT64(0x7FF8000000000000)); + /* !snan_bit_is_one, set all bits */ + frac = (1ULL << DECOMPOSED_BINARY_POINT) - 1; +#elif defined(TARGET_I386) || defined(TARGET_X86_64) \ + || defined(TARGET_MICROBLAZE) + /* !snan_bit_is_one, set sign and msb */ + frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); + sign = 1; #elif defined(TARGET_HPPA) - return const_float64(LIT64(0x7FF4000000000000)); + /* snan_bit_is_one, set msb-1. */ + frac = 1ULL << (DECOMPOSED_BINARY_POINT - 2); #else - if (status->snan_bit_is_one) { - return const_float64(LIT64(0x7FF7FFFFFFFFFFFF)); + /* This case is true for Alpha, ARM, MIPS, OpenRISC, PPC, RISC-V, + * S390, SH4, TriCore, and Xtensa. I cannot find documentation + * for Unicore32; the choice from the original commit is unchanged. + * Our other supported targets, CRIS, LM32, Moxie, Nios2, and Tile, + * do not have floating-point. + */ + if (snan_bit_is_one(status)) { + /* set all bits other than msb */ + frac = (1ULL << (DECOMPOSED_BINARY_POINT - 1)) - 1; } else { -#if defined(TARGET_MIPS) - return const_float64(LIT64(0x7FF8000000000000)); -#else - return const_float64(LIT64(0xFFF8000000000000)); -#endif + /* set msb */ + frac = 1ULL << (DECOMPOSED_BINARY_POINT - 1); } #endif + + return (FloatParts) { + .cls = float_class_qnan, + .sign = sign, + .exp = INT_MAX, + .frac = frac + }; +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN from a signalling NaN for the deconstructed +| floating-point parts. +*----------------------------------------------------------------------------*/ + +static FloatParts parts_silence_nan(FloatParts a, float_status *status) +{ +#ifdef NO_SIGNALING_NANS + g_assert_not_reached(); +#elif defined(TARGET_HPPA) + a.frac &= ~(1ULL << (DECOMPOSED_BINARY_POINT - 1)); + a.frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 2); +#else + if (snan_bit_is_one(status)) { + return parts_default_nan(status); + } else { + a.frac |= 1ULL << (DECOMPOSED_BINARY_POINT - 1); + } +#endif + a.cls = float_class_qnan; + return a; } /*---------------------------------------------------------------------------- @@ -163,17 +192,16 @@ float64 float64_default_nan(float_status *status) floatx80 floatx80_default_nan(float_status *status) { floatx80 r; + + /* None of the targets that have snan_bit_is_one use floatx80. */ + assert(!snan_bit_is_one(status)); #if defined(TARGET_M68K) r.low = LIT64(0xFFFFFFFFFFFFFFFF); r.high = 0x7FFF; #else - if (status->snan_bit_is_one) { - r.low = LIT64(0xBFFFFFFFFFFFFFFF); - r.high = 0x7FFF; - } else { - r.low = LIT64(0xC000000000000000); - r.high = 0xFFFF; - } + /* X86 */ + r.low = LIT64(0xC000000000000000); + r.high = 0xFFFF; #endif return r; } @@ -192,27 +220,6 @@ floatx80 floatx80_default_nan(float_status *status) const floatx80 floatx80_infinity = make_floatx80_init(floatx80_infinity_high, floatx80_infinity_low); -/*---------------------------------------------------------------------------- -| The pattern for a default generated quadruple-precision NaN. -*----------------------------------------------------------------------------*/ -float128 float128_default_nan(float_status *status) -{ - float128 r; - - if (status->snan_bit_is_one) { - r.low = LIT64(0xFFFFFFFFFFFFFFFF); - r.high = LIT64(0x7FFF7FFFFFFFFFFF); - } else { - r.low = LIT64(0x0000000000000000); -#if defined(TARGET_S390X) || defined(TARGET_PPC) || defined(TARGET_RISCV) - r.high = LIT64(0x7FFF800000000000); -#else - r.high = LIT64(0xFFFF800000000000); -#endif - } - return r; -} - /*---------------------------------------------------------------------------- | Raises the exceptions specified by `flags'. Floating-point traps can be | defined here if desired. It is currently not possible for such a trap @@ -233,17 +240,6 @@ typedef struct { uint64_t high, low; } commonNaNT; -#ifdef NO_SIGNALING_NANS -int float16_is_quiet_nan(float16 a_, float_status *status) -{ - return float16_is_any_nan(a_); -} - -int float16_is_signaling_nan(float16 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the half-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -251,12 +247,16 @@ int float16_is_signaling_nan(float16 a_, float_status *status) int float16_is_quiet_nan(float16 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return float16_is_any_nan(a_); +#else uint16_t a = float16_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); } else { return ((a & ~0x8000) >= 0x7C80); } +#endif } /*---------------------------------------------------------------------------- @@ -266,84 +266,18 @@ int float16_is_quiet_nan(float16 a_, float_status *status) int float16_is_signaling_nan(float16 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return 0; +#else uint16_t a = float16_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return ((a & ~0x8000) >= 0x7C80); } else { return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); } -} #endif - -/*---------------------------------------------------------------------------- -| Returns a quiet NaN if the half-precision floating point value `a' is a -| signaling NaN; otherwise returns `a'. -*----------------------------------------------------------------------------*/ -float16 float16_maybe_silence_nan(float16 a_, float_status *status) -{ - if (float16_is_signaling_nan(a_, status)) { - if (status->snan_bit_is_one) { - return float16_default_nan(status); - } else { - uint16_t a = float16_val(a_); - a |= (1 << 9); - return make_float16(a); - } - } - return a_; } -/*---------------------------------------------------------------------------- -| Returns the result of converting the half-precision floating-point NaN -| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid -| exception is raised. -*----------------------------------------------------------------------------*/ - -static commonNaNT float16ToCommonNaN(float16 a, float_status *status) -{ - commonNaNT z; - - if (float16_is_signaling_nan(a, status)) { - float_raise(float_flag_invalid, status); - } - z.sign = float16_val(a) >> 15; - z.low = 0; - z.high = ((uint64_t) float16_val(a)) << 54; - return z; -} - -/*---------------------------------------------------------------------------- -| Returns the result of converting the canonical NaN `a' to the half- -| precision floating-point format. -*----------------------------------------------------------------------------*/ - -static float16 commonNaNToFloat16(commonNaNT a, float_status *status) -{ - uint16_t mantissa = a.high >> 54; - - if (status->default_nan_mode) { - return float16_default_nan(status); - } - - if (mantissa) { - return make_float16(((((uint16_t) a.sign) << 15) - | (0x1F << 10) | mantissa)); - } else { - return float16_default_nan(status); - } -} - -#ifdef NO_SIGNALING_NANS -int float32_is_quiet_nan(float32 a_, float_status *status) -{ - return float32_is_any_nan(a_); -} - -int float32_is_signaling_nan(float32 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the single-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -351,12 +285,16 @@ int float32_is_signaling_nan(float32 a_, float_status *status) int float32_is_quiet_nan(float32 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return float32_is_any_nan(a_); +#else uint32_t a = float32_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF); } else { return ((uint32_t)(a << 1) >= 0xFF800000); } +#endif } /*---------------------------------------------------------------------------- @@ -366,39 +304,16 @@ int float32_is_quiet_nan(float32 a_, float_status *status) int float32_is_signaling_nan(float32 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return 0; +#else uint32_t a = float32_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return ((uint32_t)(a << 1) >= 0xFF800000); } else { return (((a >> 22) & 0x1FF) == 0x1FE) && (a & 0x003FFFFF); } -} #endif - -/*---------------------------------------------------------------------------- -| Returns a quiet NaN if the single-precision floating point value `a' is a -| signaling NaN; otherwise returns `a'. -*----------------------------------------------------------------------------*/ - -float32 float32_maybe_silence_nan(float32 a_, float_status *status) -{ - if (float32_is_signaling_nan(a_, status)) { - if (status->snan_bit_is_one) { -#ifdef TARGET_HPPA - uint32_t a = float32_val(a_); - a &= ~0x00400000; - a |= 0x00200000; - return make_float32(a); -#else - return float32_default_nan(status); -#endif - } else { - uint32_t a = float32_val(a_); - a |= (1 << 22); - return make_float32(a); - } - } - return a_; } /*---------------------------------------------------------------------------- @@ -448,7 +363,7 @@ static float32 commonNaNToFloat32(commonNaNT a, float_status *status) | The routine is passed various bits of information about the | two NaNs and should return 0 to select NaN a and 1 for NaN b. | Note that signalling NaNs are always squashed to quiet NaNs -| by the caller, by calling floatXX_maybe_silence_nan() before +| by the caller, by calling floatXX_silence_nan() before | returning them. | | aIsLargerSignificand is only valid if both a and b are NaNs @@ -458,10 +373,10 @@ static float32 commonNaNToFloat32(commonNaNT a, float_status *status) | tie-break rule. *----------------------------------------------------------------------------*/ -#if defined(TARGET_ARM) -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, +static int pickNaN(FloatClass a_cls, FloatClass b_cls, flag aIsLargerSignificand) { +#if defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_HPPA) /* ARM mandated NaN propagation rules (see FPProcessNaNs()), take * the first of: * 1. A if it is signaling @@ -470,23 +385,9 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, * 4. B (quiet) * A signaling NaN is always quietened before returning it. */ - if (aIsSNaN) { - return 0; - } else if (bIsSNaN) { - return 1; - } else if (aIsQNaN) { - return 0; - } else { - return 1; - } -} -#elif defined(TARGET_MIPS) || defined(TARGET_HPPA) -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag aIsLargerSignificand) -{ /* According to MIPS specifications, if one of the two operands is * a sNaN, a new qNaN has to be generated. This is done in - * floatXX_maybe_silence_nan(). For qNaN inputs the specifications + * floatXX_silence_nan(). For qNaN inputs the specifications * says: "When possible, this QNaN result is one of the operand QNaN * values." In practice it seems that most implementations choose * the first operand if both operands are qNaN. In short this gives @@ -497,35 +398,21 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, * 4. B (quiet) * A signaling NaN is always silenced before returning it. */ - if (aIsSNaN) { + if (is_snan(a_cls)) { return 0; - } else if (bIsSNaN) { + } else if (is_snan(b_cls)) { return 1; - } else if (aIsQNaN) { + } else if (is_qnan(a_cls)) { return 0; } else { return 1; } -} -#elif defined(TARGET_PPC) || defined(TARGET_XTENSA) -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag aIsLargerSignificand) -{ +#elif defined(TARGET_PPC) || defined(TARGET_XTENSA) || defined(TARGET_M68K) /* PowerPC propagation rules: * 1. A if it sNaN or qNaN * 2. B if it sNaN or qNaN * A signaling NaN is always silenced before returning it. */ - if (aIsSNaN || aIsQNaN) { - return 0; - } else { - return 1; - } -} -#elif defined(TARGET_M68K) -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag aIsLargerSignificand) -{ /* M68000 FAMILY PROGRAMMER'S REFERENCE MANUAL * 3.4 FLOATING-POINT INSTRUCTION DETAILS * If either operand, but not both operands, of an operation is a @@ -540,16 +427,12 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, * a nonsignaling NaN. The operation then continues as described in the * preceding paragraph for nonsignaling NaNs. */ - if (aIsQNaN || aIsSNaN) { /* a is the destination operand */ - return 0; /* return the destination operand */ + if (is_nan(a_cls)) { + return 0; } else { - return 1; /* return b */ + return 1; } -} #else -static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag aIsLargerSignificand) -{ /* This implements x87 NaN propagation rules: * SNaN + QNaN => return the QNaN * two SNaNs => return the one with the larger significand, silenced @@ -560,13 +443,13 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, * If we get down to comparing significands and they are the same, * return the NaN with the positive sign bit (if any). */ - if (aIsSNaN) { - if (bIsSNaN) { + if (is_snan(a_cls)) { + if (is_snan(b_cls)) { return aIsLargerSignificand ? 0 : 1; } - return bIsQNaN ? 1 : 0; - } else if (aIsQNaN) { - if (bIsSNaN || !bIsQNaN) { + return is_qnan(b_cls) ? 1 : 0; + } else if (is_qnan(a_cls)) { + if (is_snan(b_cls) || !is_qnan(b_cls)) { return 0; } else { return aIsLargerSignificand ? 0 : 1; @@ -574,8 +457,8 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, } else { return 1; } -} #endif +} /*---------------------------------------------------------------------------- | Select which NaN to propagate for a three-input operation. @@ -583,15 +466,14 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, | information. | Return values : 0 : a; 1 : b; 2 : c; 3 : default-NaN *----------------------------------------------------------------------------*/ -#if defined(TARGET_ARM) -static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero, - float_status *status) +static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, + bool infzero, float_status *status) { +#if defined(TARGET_ARM) /* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns * the default NaN */ - if (infzero && cIsQNaN) { + if (infzero && is_qnan(c_cls)) { float_raise(float_flag_invalid, status); return 3; } @@ -599,25 +481,20 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, /* This looks different from the ARM ARM pseudocode, because the ARM ARM * puts the operands to a fused mac operation (a*b)+c in the order c,a,b. */ - if (cIsSNaN) { + if (is_snan(c_cls)) { return 2; - } else if (aIsSNaN) { + } else if (is_snan(a_cls)) { return 0; - } else if (bIsSNaN) { + } else if (is_snan(b_cls)) { return 1; - } else if (cIsQNaN) { + } else if (is_qnan(c_cls)) { return 2; - } else if (aIsQNaN) { + } else if (is_qnan(a_cls)) { return 0; } else { return 1; } -} #elif defined(TARGET_MIPS) -static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero, - float_status *status) -{ /* For MIPS, the (inf,zero,qnan) case sets InvalidOp and returns * the default NaN */ @@ -626,43 +503,38 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, return 3; } - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { /* Prefer sNaN over qNaN, in the a, b, c order. */ - if (aIsSNaN) { + if (is_snan(a_cls)) { return 0; - } else if (bIsSNaN) { + } else if (is_snan(b_cls)) { return 1; - } else if (cIsSNaN) { + } else if (is_snan(c_cls)) { return 2; - } else if (aIsQNaN) { + } else if (is_qnan(a_cls)) { return 0; - } else if (bIsQNaN) { + } else if (is_qnan(b_cls)) { return 1; } else { return 2; } } else { /* Prefer sNaN over qNaN, in the c, a, b order. */ - if (cIsSNaN) { + if (is_snan(c_cls)) { return 2; - } else if (aIsSNaN) { + } else if (is_snan(a_cls)) { return 0; - } else if (bIsSNaN) { + } else if (is_snan(b_cls)) { return 1; - } else if (cIsQNaN) { + } else if (is_qnan(c_cls)) { return 2; - } else if (aIsQNaN) { + } else if (is_qnan(a_cls)) { return 0; } else { return 1; } } -} #elif defined(TARGET_PPC) -static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero, - float_status *status) -{ /* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer * to return an input NaN if we have one (ie c) rather than generating * a default NaN @@ -675,31 +547,26 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, /* If fRA is a NaN return it; otherwise if fRB is a NaN return it; * otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB */ - if (aIsSNaN || aIsQNaN) { + if (is_nan(a_cls)) { return 0; - } else if (cIsSNaN || cIsQNaN) { + } else if (is_nan(c_cls)) { return 2; } else { return 1; } -} #else -/* A default implementation: prefer a to b to c. - * This is unlikely to actually match any real implementation. - */ -static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, - flag cIsQNaN, flag cIsSNaN, flag infzero, - float_status *status) -{ - if (aIsSNaN || aIsQNaN) { + /* A default implementation: prefer a to b to c. + * This is unlikely to actually match any real implementation. + */ + if (is_nan(a_cls)) { return 0; - } else if (bIsSNaN || bIsQNaN) { + } else if (is_nan(b_cls)) { return 1; } else { return 2; } -} #endif +} /*---------------------------------------------------------------------------- | Takes two single-precision floating-point values `a' and `b', one of which @@ -709,18 +576,26 @@ static int pickNaNMulAdd(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, static float32 propagateFloat32NaN(float32 a, float32 b, float_status *status) { - flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; uint32_t av, bv; + FloatClass a_cls, b_cls; + + /* This is not complete, but is good enough for pickNaN. */ + a_cls = (!float32_is_any_nan(a) + ? float_class_normal + : float32_is_signaling_nan(a, status) + ? float_class_snan + : float_class_qnan); + b_cls = (!float32_is_any_nan(b) + ? float_class_normal + : float32_is_signaling_nan(b, status) + ? float_class_snan + : float_class_qnan); - aIsQuietNaN = float32_is_quiet_nan(a, status); - aIsSignalingNaN = float32_is_signaling_nan(a, status); - bIsQuietNaN = float32_is_quiet_nan(b, status); - bIsSignalingNaN = float32_is_signaling_nan(b, status); av = float32_val(a); bv = float32_val(b); - if (aIsSignalingNaN | bIsSignalingNaN) { + if (is_snan(a_cls) || is_snan(b_cls)) { float_raise(float_flag_invalid, status); } @@ -736,25 +611,19 @@ static float32 propagateFloat32NaN(float32 a, float32 b, float_status *status) aIsLargerSignificand = (av < bv) ? 1 : 0; } - if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - aIsLargerSignificand)) { - return float32_maybe_silence_nan(b, status); + if (pickNaN(a_cls, b_cls, aIsLargerSignificand)) { + if (is_snan(b_cls)) { + return float32_silence_nan(b, status); + } + return b; } else { - return float32_maybe_silence_nan(a, status); + if (is_snan(a_cls)) { + return float32_silence_nan(a, status); + } + return a; } } -#ifdef NO_SIGNALING_NANS -int float64_is_quiet_nan(float64 a_, float_status *status) -{ - return float64_is_any_nan(a_); -} - -int float64_is_signaling_nan(float64 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the double-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -762,13 +631,17 @@ int float64_is_signaling_nan(float64 a_, float_status *status) int float64_is_quiet_nan(float64 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return float64_is_any_nan(a_); +#else uint64_t a = float64_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return (((a >> 51) & 0xFFF) == 0xFFE) && (a & 0x0007FFFFFFFFFFFFULL); } else { return ((a << 1) >= 0xFFF0000000000000ULL); } +#endif } /*---------------------------------------------------------------------------- @@ -778,40 +651,17 @@ int float64_is_quiet_nan(float64 a_, float_status *status) int float64_is_signaling_nan(float64 a_, float_status *status) { +#ifdef NO_SIGNALING_NANS + return 0; +#else uint64_t a = float64_val(a_); - if (status->snan_bit_is_one) { + if (snan_bit_is_one(status)) { return ((a << 1) >= 0xFFF0000000000000ULL); } else { return (((a >> 51) & 0xFFF) == 0xFFE) && (a & LIT64(0x0007FFFFFFFFFFFF)); } -} #endif - -/*---------------------------------------------------------------------------- -| Returns a quiet NaN if the double-precision floating point value `a' is a -| signaling NaN; otherwise returns `a'. -*----------------------------------------------------------------------------*/ - -float64 float64_maybe_silence_nan(float64 a_, float_status *status) -{ - if (float64_is_signaling_nan(a_, status)) { - if (status->snan_bit_is_one) { -#ifdef TARGET_HPPA - uint64_t a = float64_val(a_); - a &= ~0x0008000000000000ULL; - a |= 0x0004000000000000ULL; - return make_float64(a); -#else - return float64_default_nan(status); -#endif - } else { - uint64_t a = float64_val(a_); - a |= LIT64(0x0008000000000000); - return make_float64(a); - } - } - return a_; } /*---------------------------------------------------------------------------- @@ -864,18 +714,26 @@ static float64 commonNaNToFloat64(commonNaNT a, float_status *status) static float64 propagateFloat64NaN(float64 a, float64 b, float_status *status) { - flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; uint64_t av, bv; + FloatClass a_cls, b_cls; + + /* This is not complete, but is good enough for pickNaN. */ + a_cls = (!float64_is_any_nan(a) + ? float_class_normal + : float64_is_signaling_nan(a, status) + ? float_class_snan + : float_class_qnan); + b_cls = (!float64_is_any_nan(b) + ? float_class_normal + : float64_is_signaling_nan(b, status) + ? float_class_snan + : float_class_qnan); - aIsQuietNaN = float64_is_quiet_nan(a, status); - aIsSignalingNaN = float64_is_signaling_nan(a, status); - bIsQuietNaN = float64_is_quiet_nan(b, status); - bIsSignalingNaN = float64_is_signaling_nan(b, status); av = float64_val(a); bv = float64_val(b); - if (aIsSignalingNaN | bIsSignalingNaN) { + if (is_snan(a_cls) || is_snan(b_cls)) { float_raise(float_flag_invalid, status); } @@ -891,25 +749,19 @@ static float64 propagateFloat64NaN(float64 a, float64 b, float_status *status) aIsLargerSignificand = (av < bv) ? 1 : 0; } - if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - aIsLargerSignificand)) { - return float64_maybe_silence_nan(b, status); + if (pickNaN(a_cls, b_cls, aIsLargerSignificand)) { + if (is_snan(b_cls)) { + return float64_silence_nan(b, status); + } + return b; } else { - return float64_maybe_silence_nan(a, status); + if (is_snan(a_cls)) { + return float64_silence_nan(a, status); + } + return a; } } -#ifdef NO_SIGNALING_NANS -int floatx80_is_quiet_nan(floatx80 a_, float_status *status) -{ - return floatx80_is_any_nan(a_); -} - -int floatx80_is_signaling_nan(floatx80 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a | quiet NaN; otherwise returns 0. This slightly differs from the same @@ -918,7 +770,10 @@ int floatx80_is_signaling_nan(floatx80 a_, float_status *status) int floatx80_is_quiet_nan(floatx80 a, float_status *status) { - if (status->snan_bit_is_one) { +#ifdef NO_SIGNALING_NANS + return floatx80_is_any_nan(a); +#else + if (snan_bit_is_one(status)) { uint64_t aLow; aLow = a.low & ~0x4000000000000000ULL; @@ -929,6 +784,7 @@ int floatx80_is_quiet_nan(floatx80 a, float_status *status) return ((a.high & 0x7FFF) == 0x7FFF) && (LIT64(0x8000000000000000) <= ((uint64_t)(a.low << 1))); } +#endif } /*---------------------------------------------------------------------------- @@ -939,7 +795,10 @@ int floatx80_is_quiet_nan(floatx80 a, float_status *status) int floatx80_is_signaling_nan(floatx80 a, float_status *status) { - if (status->snan_bit_is_one) { +#ifdef NO_SIGNALING_NANS + return 0; +#else + if (snan_bit_is_one(status)) { return ((a.high & 0x7FFF) == 0x7FFF) && ((a.low << 1) >= 0x8000000000000000ULL); } else { @@ -950,24 +809,19 @@ int floatx80_is_signaling_nan(floatx80 a, float_status *status) && (uint64_t)(aLow << 1) && (a.low == aLow); } -} #endif +} /*---------------------------------------------------------------------------- -| Returns a quiet NaN if the extended double-precision floating point value -| `a' is a signaling NaN; otherwise returns `a'. +| Returns a quiet NaN from a signalling NaN for the extended double-precision +| floating point value `a'. *----------------------------------------------------------------------------*/ -floatx80 floatx80_maybe_silence_nan(floatx80 a, float_status *status) +floatx80 floatx80_silence_nan(floatx80 a, float_status *status) { - if (floatx80_is_signaling_nan(a, status)) { - if (status->snan_bit_is_one) { - a = floatx80_default_nan(status); - } else { - a.low |= LIT64(0xC000000000000000); - return a; - } - } + /* None of the targets that have snan_bit_is_one use floatx80. */ + assert(!snan_bit_is_one(status)); + a.low |= LIT64(0xC000000000000000); return a; } @@ -1028,15 +882,22 @@ static floatx80 commonNaNToFloatx80(commonNaNT a, float_status *status) floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) { - flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; + FloatClass a_cls, b_cls; - aIsQuietNaN = floatx80_is_quiet_nan(a, status); - aIsSignalingNaN = floatx80_is_signaling_nan(a, status); - bIsQuietNaN = floatx80_is_quiet_nan(b, status); - bIsSignalingNaN = floatx80_is_signaling_nan(b, status); + /* This is not complete, but is good enough for pickNaN. */ + a_cls = (!floatx80_is_any_nan(a) + ? float_class_normal + : floatx80_is_signaling_nan(a, status) + ? float_class_snan + : float_class_qnan); + b_cls = (!floatx80_is_any_nan(b) + ? float_class_normal + : floatx80_is_signaling_nan(b, status) + ? float_class_snan + : float_class_qnan); - if (aIsSignalingNaN | bIsSignalingNaN) { + if (is_snan(a_cls) || is_snan(b_cls)) { float_raise(float_flag_invalid, status); } @@ -1052,25 +913,19 @@ floatx80 propagateFloatx80NaN(floatx80 a, floatx80 b, float_status *status) aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - aIsLargerSignificand)) { - return floatx80_maybe_silence_nan(b, status); + if (pickNaN(a_cls, b_cls, aIsLargerSignificand)) { + if (is_snan(b_cls)) { + return floatx80_silence_nan(b, status); + } + return b; } else { - return floatx80_maybe_silence_nan(a, status); + if (is_snan(a_cls)) { + return floatx80_silence_nan(a, status); + } + return a; } } -#ifdef NO_SIGNALING_NANS -int float128_is_quiet_nan(float128 a_, float_status *status) -{ - return float128_is_any_nan(a_); -} - -int float128_is_signaling_nan(float128 a_, float_status *status) -{ - return 0; -} -#else /*---------------------------------------------------------------------------- | Returns 1 if the quadruple-precision floating-point value `a' is a quiet | NaN; otherwise returns 0. @@ -1078,13 +933,17 @@ int float128_is_signaling_nan(float128 a_, float_status *status) int float128_is_quiet_nan(float128 a, float_status *status) { - if (status->snan_bit_is_one) { +#ifdef NO_SIGNALING_NANS + return float128_is_any_nan(a); +#else + if (snan_bit_is_one(status)) { return (((a.high >> 47) & 0xFFFF) == 0xFFFE) && (a.low || (a.high & 0x00007FFFFFFFFFFFULL)); } else { return ((a.high << 1) >= 0xFFFF000000000000ULL) && (a.low || (a.high & 0x0000FFFFFFFFFFFFULL)); } +#endif } /*---------------------------------------------------------------------------- @@ -1094,32 +953,36 @@ int float128_is_quiet_nan(float128 a, float_status *status) int float128_is_signaling_nan(float128 a, float_status *status) { - if (status->snan_bit_is_one) { +#ifdef NO_SIGNALING_NANS + return 0; +#else + if (snan_bit_is_one(status)) { return ((a.high << 1) >= 0xFFFF000000000000ULL) && (a.low || (a.high & 0x0000FFFFFFFFFFFFULL)); } else { return (((a.high >> 47) & 0xFFFF) == 0xFFFE) && (a.low || (a.high & LIT64(0x00007FFFFFFFFFFF))); } -} #endif +} /*---------------------------------------------------------------------------- -| Returns a quiet NaN if the quadruple-precision floating point value `a' is -| a signaling NaN; otherwise returns `a'. +| Returns a quiet NaN from a signalling NaN for the quadruple-precision +| floating point value `a'. *----------------------------------------------------------------------------*/ -float128 float128_maybe_silence_nan(float128 a, float_status *status) +float128 float128_silence_nan(float128 a, float_status *status) { - if (float128_is_signaling_nan(a, status)) { - if (status->snan_bit_is_one) { - a = float128_default_nan(status); - } else { - a.high |= LIT64(0x0000800000000000); - return a; - } +#ifdef NO_SIGNALING_NANS + g_assert_not_reached(); +#else + if (snan_bit_is_one(status)) { + return float128_default_nan(status); + } else { + a.high |= LIT64(0x0000800000000000); + return a; } - return a; +#endif } /*---------------------------------------------------------------------------- @@ -1167,15 +1030,22 @@ static float128 commonNaNToFloat128(commonNaNT a, float_status *status) static float128 propagateFloat128NaN(float128 a, float128 b, float_status *status) { - flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; flag aIsLargerSignificand; + FloatClass a_cls, b_cls; - aIsQuietNaN = float128_is_quiet_nan(a, status); - aIsSignalingNaN = float128_is_signaling_nan(a, status); - bIsQuietNaN = float128_is_quiet_nan(b, status); - bIsSignalingNaN = float128_is_signaling_nan(b, status); + /* This is not complete, but is good enough for pickNaN. */ + a_cls = (!float128_is_any_nan(a) + ? float_class_normal + : float128_is_signaling_nan(a, status) + ? float_class_snan + : float_class_qnan); + b_cls = (!float128_is_any_nan(b) + ? float_class_normal + : float128_is_signaling_nan(b, status) + ? float_class_snan + : float_class_qnan); - if (aIsSignalingNaN | bIsSignalingNaN) { + if (is_snan(a_cls) || is_snan(b_cls)) { float_raise(float_flag_invalid, status); } @@ -1191,10 +1061,15 @@ static float128 propagateFloat128NaN(float128 a, float128 b, aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, - aIsLargerSignificand)) { - return float128_maybe_silence_nan(b, status); + if (pickNaN(a_cls, b_cls, aIsLargerSignificand)) { + if (is_snan(b_cls)) { + return float128_silence_nan(b, status); + } + return b; } else { - return float128_maybe_silence_nan(a, status); + if (is_snan(a_cls)) { + return float128_silence_nan(a, status); + } + return a; } } diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 70e0c40a1c..8cd2400081 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -95,16 +95,6 @@ this code that are retained. *----------------------------------------------------------------------------*/ #include "fpu/softfloat-macros.h" -/*---------------------------------------------------------------------------- -| Functions and definitions to determine: (1) whether tininess for underflow -| is detected before or after rounding by default, (2) what (if anything) -| happens when exceptions are raised, (3) how signaling NaNs are distinguished -| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs -| are propagated from function inputs to output. These details are target- -| specific. -*----------------------------------------------------------------------------*/ -#include "softfloat-specialize.h" - /*---------------------------------------------------------------------------- | Returns the fraction bits of the half-precision floating-point value `a'. *----------------------------------------------------------------------------*/ @@ -123,15 +113,6 @@ static inline int extractFloat16Exp(float16 a) return (float16_val(a) >> 10) & 0x1f; } -/*---------------------------------------------------------------------------- -| Returns the sign bit of the single-precision floating-point value `a'. -*----------------------------------------------------------------------------*/ - -static inline flag extractFloat16Sign(float16 a) -{ - return float16_val(a)>>15; -} - /*---------------------------------------------------------------------------- | Returns the fraction bits of the single-precision floating-point value `a'. *----------------------------------------------------------------------------*/ @@ -198,10 +179,24 @@ typedef enum __attribute__ ((__packed__)) { float_class_inf, float_class_qnan, /* all NaNs from here */ float_class_snan, - float_class_dnan, - float_class_msnan, /* maybe silenced */ } FloatClass; +/* Simple helpers for checking if, or what kind of, NaN we have */ +static inline __attribute__((unused)) bool is_nan(FloatClass c) +{ + return unlikely(c >= float_class_qnan); +} + +static inline __attribute__((unused)) bool is_snan(FloatClass c) +{ + return c == float_class_snan; +} + +static inline __attribute__((unused)) bool is_qnan(FloatClass c) +{ + return c == float_class_qnan; +} + /* * Structure holding all of the decomposed parts of a float. The * exponent is unbiased and the fraction is normalized. All @@ -232,8 +227,10 @@ typedef struct { * frac_shift: shift to normalise the fraction with DECOMPOSED_BINARY_POINT * The following are computed based the size of fraction * frac_lsb: least significant bit of fraction - * fram_lsbm1: the bit bellow the least significant bit (for rounding) + * frac_lsbm1: the bit below the least significant bit (for rounding) * round_mask/roundeven_mask: masks used for rounding + * The following optional modifiers are available: + * arm_althp: handle ARM Alternative Half Precision */ typedef struct { int exp_size; @@ -245,6 +242,7 @@ typedef struct { uint64_t frac_lsbm1; uint64_t round_mask; uint64_t roundeven_mask; + bool arm_althp; } FloatFmt; /* Expand fields based on the size of exponent and fraction */ @@ -263,6 +261,11 @@ static const FloatFmt float16_params = { FLOAT_PARAMS(5, 10) }; +static const FloatFmt float16_params_ahp = { + FLOAT_PARAMS(5, 10), + .arm_althp = true +}; + static const FloatFmt float32_params = { FLOAT_PARAMS(8, 23) }; @@ -322,24 +325,27 @@ static inline float64 float64_pack_raw(FloatParts p) return make_float64(pack_raw(float64_params, p)); } +/*---------------------------------------------------------------------------- +| Functions and definitions to determine: (1) whether tininess for underflow +| is detected before or after rounding by default, (2) what (if anything) +| happens when exceptions are raised, (3) how signaling NaNs are distinguished +| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs +| are propagated from function inputs to output. These details are target- +| specific. +*----------------------------------------------------------------------------*/ +#include "softfloat-specialize.h" + /* Canonicalize EXP and FRAC, setting CLS. */ static FloatParts canonicalize(FloatParts part, const FloatFmt *parm, float_status *status) { - if (part.exp == parm->exp_max) { + if (part.exp == parm->exp_max && !parm->arm_althp) { if (part.frac == 0) { part.cls = float_class_inf; } else { -#ifdef NO_SIGNALING_NANS - part.cls = float_class_qnan; -#else - int64_t msb = part.frac << (parm->frac_shift + 2); - if ((msb < 0) == status->snan_bit_is_one) { - part.cls = float_class_snan; - } else { - part.cls = float_class_qnan; - } -#endif + part.frac <<= parm->frac_shift; + part.cls = (parts_is_snan_frac(part.frac, status) + ? float_class_snan : float_class_qnan); } } else if (part.exp == 0) { if (likely(part.frac == 0)) { @@ -422,7 +428,15 @@ static FloatParts round_canonical(FloatParts p, float_status *s, } frac >>= frac_shift; - if (unlikely(exp >= exp_max)) { + if (parm->arm_althp) { + /* ARM Alt HP eschews Inf and NaN for a wider exponent. */ + if (unlikely(exp > exp_max)) { + /* Overflow. Return the maximum normal. */ + flags = float_flag_invalid; + exp = exp_max; + frac = -1; + } + } else if (unlikely(exp >= exp_max)) { flags |= float_flag_overflow | float_flag_inexact; if (overflow_norm) { exp = exp_max - 1; @@ -473,13 +487,16 @@ static FloatParts round_canonical(FloatParts p, float_status *s, case float_class_inf: do_inf: + assert(!parm->arm_althp); exp = exp_max; frac = 0; break; case float_class_qnan: case float_class_snan: + assert(!parm->arm_althp); exp = exp_max; + frac >>= parm->frac_shift; break; default: @@ -492,22 +509,27 @@ static FloatParts round_canonical(FloatParts p, float_status *s, return p; } +/* Explicit FloatFmt version */ +static FloatParts float16a_unpack_canonical(float16 f, float_status *s, + const FloatFmt *params) +{ + return canonicalize(float16_unpack_raw(f), params, s); +} + static FloatParts float16_unpack_canonical(float16 f, float_status *s) { - return canonicalize(float16_unpack_raw(f), &float16_params, s); + return float16a_unpack_canonical(f, s, &float16_params); +} + +static float16 float16a_round_pack_canonical(FloatParts p, float_status *s, + const FloatFmt *params) +{ + return float16_pack_raw(round_canonical(p, s, params)); } static float16 float16_round_pack_canonical(FloatParts p, float_status *s) { - switch (p.cls) { - case float_class_dnan: - return float16_default_nan(s); - case float_class_msnan: - return float16_maybe_silence_nan(float16_pack_raw(p), s); - default: - p = round_canonical(p, s, &float16_params); - return float16_pack_raw(p); - } + return float16a_round_pack_canonical(p, s, &float16_params); } static FloatParts float32_unpack_canonical(float32 f, float_status *s) @@ -517,15 +539,7 @@ static FloatParts float32_unpack_canonical(float32 f, float_status *s) static float32 float32_round_pack_canonical(FloatParts p, float_status *s) { - switch (p.cls) { - case float_class_dnan: - return float32_default_nan(s); - case float_class_msnan: - return float32_maybe_silence_nan(float32_pack_raw(p), s); - default: - p = round_canonical(p, s, &float32_params); - return float32_pack_raw(p); - } + return float32_pack_raw(round_canonical(p, s, &float32_params)); } static FloatParts float64_unpack_canonical(float64 f, float_status *s) @@ -535,29 +549,7 @@ static FloatParts float64_unpack_canonical(float64 f, float_status *s) static float64 float64_round_pack_canonical(FloatParts p, float_status *s) { - switch (p.cls) { - case float_class_dnan: - return float64_default_nan(s); - case float_class_msnan: - return float64_maybe_silence_nan(float64_pack_raw(p), s); - default: - p = round_canonical(p, s, &float64_params); - return float64_pack_raw(p); - } -} - -/* Simple helpers for checking if what NaN we have */ -static bool is_nan(FloatClass c) -{ - return unlikely(c >= float_class_qnan); -} -static bool is_snan(FloatClass c) -{ - return c == float_class_snan; -} -static bool is_qnan(FloatClass c) -{ - return c == float_class_qnan; + return float64_pack_raw(round_canonical(p, s, &float64_params)); } static FloatParts return_nan(FloatParts a, float_status *s) @@ -565,11 +557,11 @@ static FloatParts return_nan(FloatParts a, float_status *s) switch (a.cls) { case float_class_snan: s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_msnan; + a = parts_silence_nan(a, s); /* fall through */ case float_class_qnan: if (s->default_nan_mode) { - a.cls = float_class_dnan; + return parts_default_nan(s); } break; @@ -586,15 +578,16 @@ static FloatParts pick_nan(FloatParts a, FloatParts b, float_status *s) } if (s->default_nan_mode) { - a.cls = float_class_dnan; + return parts_default_nan(s); } else { - if (pickNaN(is_qnan(a.cls), is_snan(a.cls), - is_qnan(b.cls), is_snan(b.cls), + if (pickNaN(a.cls, b.cls, a.frac > b.frac || (a.frac == b.frac && a.sign < b.sign))) { a = b; } - a.cls = float_class_msnan; + if (is_snan(a.cls)) { + return parts_silence_nan(a, s); + } } return a; } @@ -602,33 +595,38 @@ static FloatParts pick_nan(FloatParts a, FloatParts b, float_status *s) static FloatParts pick_nan_muladd(FloatParts a, FloatParts b, FloatParts c, bool inf_zero, float_status *s) { + int which; + if (is_snan(a.cls) || is_snan(b.cls) || is_snan(c.cls)) { s->float_exception_flags |= float_flag_invalid; } - if (s->default_nan_mode) { - a.cls = float_class_dnan; - } else { - switch (pickNaNMulAdd(is_qnan(a.cls), is_snan(a.cls), - is_qnan(b.cls), is_snan(b.cls), - is_qnan(c.cls), is_snan(c.cls), - inf_zero, s)) { - case 0: - break; - case 1: - a = b; - break; - case 2: - a = c; - break; - case 3: - a.cls = float_class_dnan; - return a; - default: - g_assert_not_reached(); - } + which = pickNaNMulAdd(a.cls, b.cls, c.cls, inf_zero, s); - a.cls = float_class_msnan; + if (s->default_nan_mode) { + /* Note that this check is after pickNaNMulAdd so that function + * has an opportunity to set the Invalid flag. + */ + which = 3; + } + + switch (which) { + case 0: + break; + case 1: + a = b; + break; + case 2: + a = c; + break; + case 3: + return parts_default_nan(s); + default: + g_assert_not_reached(); + } + + if (is_snan(a.cls)) { + return parts_silence_nan(a, s); } return a; } @@ -677,7 +675,7 @@ static FloatParts addsub_floats(FloatParts a, FloatParts b, bool subtract, if (a.cls == float_class_inf) { if (b.cls == float_class_inf) { float_raise(float_flag_invalid, s); - a.cls = float_class_dnan; + return parts_default_nan(s); } return a; } @@ -823,9 +821,7 @@ static FloatParts mul_floats(FloatParts a, FloatParts b, float_status *s) if ((a.cls == float_class_inf && b.cls == float_class_zero) || (a.cls == float_class_zero && b.cls == float_class_inf)) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; - a.sign = sign; - return a; + return parts_default_nan(s); } /* Multiply by 0 or Inf */ if (a.cls == float_class_inf || a.cls == float_class_zero) { @@ -903,8 +899,7 @@ static FloatParts muladd_floats(FloatParts a, FloatParts b, FloatParts c, if (inf_zero) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; - return a; + return parts_default_nan(s); } if (flags & float_muladd_negate_c) { @@ -928,12 +923,12 @@ static FloatParts muladd_floats(FloatParts a, FloatParts b, FloatParts c, if (c.cls == float_class_inf) { if (p_class == float_class_inf && p_sign != c.sign) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; + return parts_default_nan(s); } else { a.cls = float_class_inf; a.sign = c.sign ^ sign_flip; + return a; } - return a; } if (p_class == float_class_inf) { @@ -1143,8 +1138,7 @@ static FloatParts div_floats(FloatParts a, FloatParts b, float_status *s) && (a.cls == float_class_inf || a.cls == float_class_zero)) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; - return a; + return parts_default_nan(s); } /* Inf / x or 0 / x */ if (a.cls == float_class_inf || a.cls == float_class_zero) { @@ -1194,6 +1188,104 @@ float64 float64_div(float64 a, float64 b, float_status *status) return float64_round_pack_canonical(pr, status); } +/* + * Float to Float conversions + * + * Returns the result of converting one float format to another. The + * conversion is performed according to the IEC/IEEE Standard for + * Binary Floating-Point Arithmetic. + * + * The float_to_float helper only needs to take care of raising + * invalid exceptions and handling the conversion on NaNs. + */ + +static FloatParts float_to_float(FloatParts a, const FloatFmt *dstf, + float_status *s) +{ + if (dstf->arm_althp) { + switch (a.cls) { + case float_class_qnan: + case float_class_snan: + /* There is no NaN in the destination format. Raise Invalid + * and return a zero with the sign of the input NaN. + */ + s->float_exception_flags |= float_flag_invalid; + a.cls = float_class_zero; + a.frac = 0; + a.exp = 0; + break; + + case float_class_inf: + /* There is no Inf in the destination format. Raise Invalid + * and return the maximum normal with the correct sign. + */ + s->float_exception_flags |= float_flag_invalid; + a.cls = float_class_normal; + a.exp = dstf->exp_max; + a.frac = ((1ull << dstf->frac_size) - 1) << dstf->frac_shift; + break; + + default: + break; + } + } else if (is_nan(a.cls)) { + if (is_snan(a.cls)) { + s->float_exception_flags |= float_flag_invalid; + a = parts_silence_nan(a, s); + } + if (s->default_nan_mode) { + return parts_default_nan(s); + } + } + return a; +} + +float32 float16_to_float32(float16 a, bool ieee, float_status *s) +{ + const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; + FloatParts p = float16a_unpack_canonical(a, s, fmt16); + FloatParts pr = float_to_float(p, &float32_params, s); + return float32_round_pack_canonical(pr, s); +} + +float64 float16_to_float64(float16 a, bool ieee, float_status *s) +{ + const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; + FloatParts p = float16a_unpack_canonical(a, s, fmt16); + FloatParts pr = float_to_float(p, &float64_params, s); + return float64_round_pack_canonical(pr, s); +} + +float16 float32_to_float16(float32 a, bool ieee, float_status *s) +{ + const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; + FloatParts p = float32_unpack_canonical(a, s); + FloatParts pr = float_to_float(p, fmt16, s); + return float16a_round_pack_canonical(pr, s, fmt16); +} + +float64 float32_to_float64(float32 a, float_status *s) +{ + FloatParts p = float32_unpack_canonical(a, s); + FloatParts pr = float_to_float(p, &float64_params, s); + return float64_round_pack_canonical(pr, s); +} + +float16 float64_to_float16(float64 a, bool ieee, float_status *s) +{ + const FloatFmt *fmt16 = ieee ? &float16_params : &float16_params_ahp; + FloatParts p = float64_unpack_canonical(a, s); + FloatParts pr = float_to_float(p, fmt16, s); + return float16a_round_pack_canonical(pr, s, fmt16); +} + +float32 float64_to_float32(float64 a, float_status *s) +{ + FloatParts p = float64_unpack_canonical(a, s); + FloatParts pr = float_to_float(p, &float32_params, s); + return float32_round_pack_canonical(pr, s); +} + /* * Rounds the floating-point value `a' to an integer, and returns the * result as a floating-point value. The operation is performed @@ -1342,8 +1434,6 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode, switch (p.cls) { case float_class_snan: case float_class_qnan: - case float_class_dnan: - case float_class_msnan: s->float_exception_flags = orig_flags | float_flag_invalid; return max; case float_class_inf: @@ -1360,14 +1450,14 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode, r = UINT64_MAX; } if (p.sign) { - if (r < -(uint64_t) min) { + if (r <= -(uint64_t) min) { return -r; } else { s->float_exception_flags = orig_flags | float_flag_invalid; return min; } } else { - if (r < max) { + if (r <= max) { return r; } else { s->float_exception_flags = orig_flags | float_flag_invalid; @@ -1434,8 +1524,6 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, switch (p.cls) { case float_class_snan: case float_class_qnan: - case float_class_dnan: - case float_class_msnan: s->float_exception_flags = orig_flags | float_flag_invalid; return max; case float_class_inf: @@ -1517,7 +1605,7 @@ FLOAT_TO_UINT(64, 64) static FloatParts int_to_float(int64_t a, float_status *status) { - FloatParts r; + FloatParts r = {}; if (a == 0) { r.cls = float_class_zero; r.sign = false; @@ -1935,8 +2023,7 @@ static FloatParts sqrt_float(FloatParts a, float_status *s, const FloatFmt *p) } if (a.sign) { s->float_exception_flags |= float_flag_invalid; - a.cls = float_class_dnan; - return a; + return parts_default_nan(s); } if (a.cls == float_class_inf) { return a; /* sqrt(+inf) = +inf */ @@ -2005,6 +2092,78 @@ float64 __attribute__((flatten)) float64_sqrt(float64 a, float_status *status) return float64_round_pack_canonical(pr, status); } +/*---------------------------------------------------------------------------- +| The pattern for a default generated NaN. +*----------------------------------------------------------------------------*/ + +float16 float16_default_nan(float_status *status) +{ + FloatParts p = parts_default_nan(status); + p.frac >>= float16_params.frac_shift; + return float16_pack_raw(p); +} + +float32 float32_default_nan(float_status *status) +{ + FloatParts p = parts_default_nan(status); + p.frac >>= float32_params.frac_shift; + return float32_pack_raw(p); +} + +float64 float64_default_nan(float_status *status) +{ + FloatParts p = parts_default_nan(status); + p.frac >>= float64_params.frac_shift; + return float64_pack_raw(p); +} + +float128 float128_default_nan(float_status *status) +{ + FloatParts p = parts_default_nan(status); + float128 r; + + /* Extrapolate from the choices made by parts_default_nan to fill + * in the quad-floating format. If the low bit is set, assume we + * want to set all non-snan bits. + */ + r.low = -(p.frac & 1); + r.high = p.frac >> (DECOMPOSED_BINARY_POINT - 48); + r.high |= LIT64(0x7FFF000000000000); + r.high |= (uint64_t)p.sign << 63; + + return r; +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN from a signalling NaN for the floating point value `a'. +*----------------------------------------------------------------------------*/ + +float16 float16_silence_nan(float16 a, float_status *status) +{ + FloatParts p = float16_unpack_raw(a); + p.frac <<= float16_params.frac_shift; + p = parts_silence_nan(p, status); + p.frac >>= float16_params.frac_shift; + return float16_pack_raw(p); +} + +float32 float32_silence_nan(float32 a, float_status *status) +{ + FloatParts p = float32_unpack_raw(a); + p.frac <<= float32_params.frac_shift; + p = parts_silence_nan(p, status); + p.frac >>= float32_params.frac_shift; + return float32_pack_raw(p); +} + +float64 float64_silence_nan(float64 a, float_status *status) +{ + FloatParts p = float64_unpack_raw(a); + p.frac <<= float64_params.frac_shift; + p = parts_silence_nan(p, status); + p.frac >>= float64_params.frac_shift; + return float64_pack_raw(p); +} /*---------------------------------------------------------------------------- | Takes a 64-bit fixed-point value `absZ' with binary point between bits 6 @@ -3139,42 +3298,7 @@ float128 uint64_to_float128(uint64_t a, float_status *status) if (a == 0) { return float128_zero; } - return normalizeRoundAndPackFloat128(0, 0x406E, a, 0, status); -} - - - - -/*---------------------------------------------------------------------------- -| Returns the result of converting the single-precision floating-point value -| `a' to the double-precision floating-point format. The conversion is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. -*----------------------------------------------------------------------------*/ - -float64 float32_to_float64(float32 a, float_status *status) -{ - flag aSign; - int aExp; - uint32_t aSig; - a = float32_squash_input_denormal(a, status); - - aSig = extractFloat32Frac( a ); - aExp = extractFloat32Exp( a ); - aSign = extractFloat32Sign( a ); - if ( aExp == 0xFF ) { - if (aSig) { - return commonNaNToFloat64(float32ToCommonNaN(a, status), status); - } - return packFloat64( aSign, 0x7FF, 0 ); - } - if ( aExp == 0 ) { - if ( aSig == 0 ) return packFloat64( aSign, 0, 0 ); - normalizeFloat32Subnormal( aSig, &aExp, &aSig ); - --aExp; - } - return packFloat64( aSign, aExp + 0x380, ( (uint64_t) aSig )<<29 ); - + return normalizeRoundAndPackFloat128(0, 0x406E, 0, a, status); } /*---------------------------------------------------------------------------- @@ -3695,173 +3819,6 @@ int float32_unordered_quiet(float32 a, float32 b, float_status *status) return 0; } - -/*---------------------------------------------------------------------------- -| Returns the result of converting the double-precision floating-point value -| `a' to the single-precision floating-point format. The conversion is -| performed according to the IEC/IEEE Standard for Binary Floating-Point -| Arithmetic. -*----------------------------------------------------------------------------*/ - -float32 float64_to_float32(float64 a, float_status *status) -{ - flag aSign; - int aExp; - uint64_t aSig; - uint32_t zSig; - a = float64_squash_input_denormal(a, status); - - aSig = extractFloat64Frac( a ); - aExp = extractFloat64Exp( a ); - aSign = extractFloat64Sign( a ); - if ( aExp == 0x7FF ) { - if (aSig) { - return commonNaNToFloat32(float64ToCommonNaN(a, status), status); - } - return packFloat32( aSign, 0xFF, 0 ); - } - shift64RightJamming( aSig, 22, &aSig ); - zSig = aSig; - if ( aExp || zSig ) { - zSig |= 0x40000000; - aExp -= 0x381; - } - return roundAndPackFloat32(aSign, aExp, zSig, status); - -} - - -/*---------------------------------------------------------------------------- -| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a -| half-precision floating-point value, returning the result. After being -| shifted into the proper positions, the three fields are simply added -| together to form the result. This means that any integer portion of `zSig' -| will be added into the exponent. Since a properly normalized significand -| will have an integer portion equal to 1, the `zExp' input should be 1 less -| than the desired result exponent whenever `zSig' is a complete, normalized -| significand. -*----------------------------------------------------------------------------*/ -static float16 packFloat16(flag zSign, int zExp, uint16_t zSig) -{ - return make_float16( - (((uint32_t)zSign) << 15) + (((uint32_t)zExp) << 10) + zSig); -} - -/*---------------------------------------------------------------------------- -| Takes an abstract floating-point value having sign `zSign', exponent `zExp', -| and significand `zSig', and returns the proper half-precision floating- -| point value corresponding to the abstract input. Ordinarily, the abstract -| value is simply rounded and packed into the half-precision format, with -| the inexact exception raised if the abstract input cannot be represented -| exactly. However, if the abstract value is too large, the overflow and -| inexact exceptions are raised and an infinity or maximal finite value is -| returned. If the abstract value is too small, the input value is rounded to -| a subnormal number, and the underflow and inexact exceptions are raised if -| the abstract input cannot be represented exactly as a subnormal half- -| precision floating-point number. -| The `ieee' flag indicates whether to use IEEE standard half precision, or -| ARM-style "alternative representation", which omits the NaN and Inf -| encodings in order to raise the maximum representable exponent by one. -| The input significand `zSig' has its binary point between bits 22 -| and 23, which is 13 bits to the left of the usual location. This shifted -| significand must be normalized or smaller. If `zSig' is not normalized, -| `zExp' must be 0; in that case, the result returned is a subnormal number, -| and it must not require rounding. In the usual case that `zSig' is -| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent. -| Note the slightly odd position of the binary point in zSig compared with the -| other roundAndPackFloat functions. This should probably be fixed if we -| need to implement more float16 routines than just conversion. -| The handling of underflow and overflow follows the IEC/IEEE Standard for -| Binary Floating-Point Arithmetic. -*----------------------------------------------------------------------------*/ - -static float16 roundAndPackFloat16(flag zSign, int zExp, - uint32_t zSig, flag ieee, - float_status *status) -{ - int maxexp = ieee ? 29 : 30; - uint32_t mask; - uint32_t increment; - bool rounding_bumps_exp; - bool is_tiny = false; - - /* Calculate the mask of bits of the mantissa which are not - * representable in half-precision and will be lost. - */ - if (zExp < 1) { - /* Will be denormal in halfprec */ - mask = 0x00ffffff; - if (zExp >= -11) { - mask >>= 11 + zExp; - } - } else { - /* Normal number in halfprec */ - mask = 0x00001fff; - } - - switch (status->float_rounding_mode) { - case float_round_nearest_even: - increment = (mask + 1) >> 1; - if ((zSig & mask) == increment) { - increment = zSig & (increment << 1); - } - break; - case float_round_ties_away: - increment = (mask + 1) >> 1; - break; - case float_round_up: - increment = zSign ? 0 : mask; - break; - case float_round_down: - increment = zSign ? mask : 0; - break; - default: /* round_to_zero */ - increment = 0; - break; - } - - rounding_bumps_exp = (zSig + increment >= 0x01000000); - - if (zExp > maxexp || (zExp == maxexp && rounding_bumps_exp)) { - if (ieee) { - float_raise(float_flag_overflow | float_flag_inexact, status); - return packFloat16(zSign, 0x1f, 0); - } else { - float_raise(float_flag_invalid, status); - return packFloat16(zSign, 0x1f, 0x3ff); - } - } - - if (zExp < 0) { - /* Note that flush-to-zero does not affect half-precision results */ - is_tiny = - (status->float_detect_tininess == float_tininess_before_rounding) - || (zExp < -1) - || (!rounding_bumps_exp); - } - if (zSig & mask) { - float_raise(float_flag_inexact, status); - if (is_tiny) { - float_raise(float_flag_underflow, status); - } - } - - zSig += increment; - if (rounding_bumps_exp) { - zSig >>= 1; - zExp++; - } - - if (zExp < -10) { - return packFloat16(zSign, 0, 0); - } - if (zExp < 0) { - zSig >>= -zExp; - zExp = 0; - } - return packFloat16(zSign, zExp, zSig >> 13); -} - /*---------------------------------------------------------------------------- | If `a' is denormal and we are in flush-to-zero mode then set the | input-denormal exception and return zero. Otherwise just return the value. @@ -3877,163 +3834,6 @@ float16 float16_squash_input_denormal(float16 a, float_status *status) return a; } -static void normalizeFloat16Subnormal(uint32_t aSig, int *zExpPtr, - uint32_t *zSigPtr) -{ - int8_t shiftCount = countLeadingZeros32(aSig) - 21; - *zSigPtr = aSig << shiftCount; - *zExpPtr = 1 - shiftCount; -} - -/* Half precision floats come in two formats: standard IEEE and "ARM" format. - The latter gains extra exponent range by omitting the NaN/Inf encodings. */ - -float32 float16_to_float32(float16 a, flag ieee, float_status *status) -{ - flag aSign; - int aExp; - uint32_t aSig; - - aSign = extractFloat16Sign(a); - aExp = extractFloat16Exp(a); - aSig = extractFloat16Frac(a); - - if (aExp == 0x1f && ieee) { - if (aSig) { - return commonNaNToFloat32(float16ToCommonNaN(a, status), status); - } - return packFloat32(aSign, 0xff, 0); - } - if (aExp == 0) { - if (aSig == 0) { - return packFloat32(aSign, 0, 0); - } - - normalizeFloat16Subnormal(aSig, &aExp, &aSig); - aExp--; - } - return packFloat32( aSign, aExp + 0x70, aSig << 13); -} - -float16 float32_to_float16(float32 a, flag ieee, float_status *status) -{ - flag aSign; - int aExp; - uint32_t aSig; - - a = float32_squash_input_denormal(a, status); - - aSig = extractFloat32Frac( a ); - aExp = extractFloat32Exp( a ); - aSign = extractFloat32Sign( a ); - if ( aExp == 0xFF ) { - if (aSig) { - /* Input is a NaN */ - if (!ieee) { - float_raise(float_flag_invalid, status); - return packFloat16(aSign, 0, 0); - } - return commonNaNToFloat16( - float32ToCommonNaN(a, status), status); - } - /* Infinity */ - if (!ieee) { - float_raise(float_flag_invalid, status); - return packFloat16(aSign, 0x1f, 0x3ff); - } - return packFloat16(aSign, 0x1f, 0); - } - if (aExp == 0 && aSig == 0) { - return packFloat16(aSign, 0, 0); - } - /* Decimal point between bits 22 and 23. Note that we add the 1 bit - * even if the input is denormal; however this is harmless because - * the largest possible single-precision denormal is still smaller - * than the smallest representable half-precision denormal, and so we - * will end up ignoring aSig and returning via the "always return zero" - * codepath. - */ - aSig |= 0x00800000; - aExp -= 0x71; - - return roundAndPackFloat16(aSign, aExp, aSig, ieee, status); -} - -float64 float16_to_float64(float16 a, flag ieee, float_status *status) -{ - flag aSign; - int aExp; - uint32_t aSig; - - aSign = extractFloat16Sign(a); - aExp = extractFloat16Exp(a); - aSig = extractFloat16Frac(a); - - if (aExp == 0x1f && ieee) { - if (aSig) { - return commonNaNToFloat64( - float16ToCommonNaN(a, status), status); - } - return packFloat64(aSign, 0x7ff, 0); - } - if (aExp == 0) { - if (aSig == 0) { - return packFloat64(aSign, 0, 0); - } - - normalizeFloat16Subnormal(aSig, &aExp, &aSig); - aExp--; - } - return packFloat64(aSign, aExp + 0x3f0, ((uint64_t)aSig) << 42); -} - -float16 float64_to_float16(float64 a, flag ieee, float_status *status) -{ - flag aSign; - int aExp; - uint64_t aSig; - uint32_t zSig; - - a = float64_squash_input_denormal(a, status); - - aSig = extractFloat64Frac(a); - aExp = extractFloat64Exp(a); - aSign = extractFloat64Sign(a); - if (aExp == 0x7FF) { - if (aSig) { - /* Input is a NaN */ - if (!ieee) { - float_raise(float_flag_invalid, status); - return packFloat16(aSign, 0, 0); - } - return commonNaNToFloat16( - float64ToCommonNaN(a, status), status); - } - /* Infinity */ - if (!ieee) { - float_raise(float_flag_invalid, status); - return packFloat16(aSign, 0x1f, 0x3ff); - } - return packFloat16(aSign, 0x1f, 0); - } - shift64RightJamming(aSig, 29, &aSig); - zSig = aSig; - if (aExp == 0 && zSig == 0) { - return packFloat16(aSign, 0, 0); - } - /* Decimal point between bits 22 and 23. Note that we add the 1 bit - * even if the input is denormal; however this is harmless because - * the largest possible single-precision denormal is still smaller - * than the smallest representable half-precision denormal, and so we - * will end up ignoring aSig and returning via the "always return zero" - * codepath. - */ - zSig |= 0x00800000; - aExp -= 0x3F1; - - return roundAndPackFloat16(aSign, aExp, zSig, ieee, status); -} - /*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point value | `a' to the extended double-precision floating-point format. The conversion diff --git a/gdbstub.c b/gdbstub.c index 3c3807358c..d6ab95006c 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -675,6 +675,16 @@ static const char *get_feature_xml(const char *p, const char **newp, } return target_xml; } + if (cc->gdb_get_dynamic_xml) { + CPUState *cpu = first_cpu; + char *xmlname = g_strndup(p, len); + const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname); + + g_free(xmlname); + if (xml) { + return xml; + } + } for (i = 0; ; i++) { name = xml_builtin[i][0]; if (!name || (strncmp(name, p, len) == 0 && strlen(name) == len)) @@ -1548,6 +1558,12 @@ void gdb_do_syscallv(gdb_syscall_complete_cb cb, const char *fmt, va_list va) *p = 0; #ifdef CONFIG_USER_ONLY put_packet(s, s->syscall_buf); + /* Return control to gdb for it to process the syscall request. + * Since the protocol requires that gdb hands control back to us + * using a "here are the results" F packet, we don't need to check + * gdb_handlesig's return value (which is the signal to deliver if + * execution was resumed via a continue packet). + */ gdb_handlesig(s->c_cpu, 0); #else /* In this case wait to send the syscall packet until notification that @@ -1804,7 +1820,7 @@ void gdb_signalled(CPUArchState *env, int sig) put_packet(s, buf); } -static void gdb_accept(void) +static bool gdb_accept(void) { GDBState *s; struct sockaddr_in sockaddr; @@ -1816,17 +1832,19 @@ static void gdb_accept(void) fd = accept(gdbserver_fd, (struct sockaddr *)&sockaddr, &len); if (fd < 0 && errno != EINTR) { perror("accept"); - return; + return false; } else if (fd >= 0) { -#ifndef _WIN32 - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif + qemu_set_cloexec(fd); break; } } /* set short latency */ - socket_set_nodelay(fd); + if (socket_set_nodelay(fd)) { + perror("setsockopt"); + close(fd); + return false; + } s = g_malloc0(sizeof(GDBState)); s->c_cpu = first_cpu; @@ -1835,6 +1853,7 @@ static void gdb_accept(void) gdb_has_xml = false; gdbserver_state = s; + return true; } static int gdbserver_open(int port) @@ -1847,9 +1866,7 @@ static int gdbserver_open(int port) perror("socket"); return -1; } -#ifndef _WIN32 - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif + qemu_set_cloexec(fd); socket_set_fast_reuse(fd); @@ -1877,7 +1894,11 @@ int gdbserver_start(int port) if (gdbserver_fd < 0) return -1; /* accept connections */ - gdb_accept(); + if (!gdb_accept()) { + close(gdbserver_fd); + gdbserver_fd = -1; + return -1; + } return 0; } diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index ddfcd5adcc..70639f656a 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -19,6 +19,7 @@ ETEXI .params = "", .help = "show the version of QEMU", .cmd = hmp_info_version, + .flags = "p", }, STEXI @@ -47,6 +48,7 @@ ETEXI .params = "", .help = "show the character devices", .cmd = hmp_info_chardev, + .flags = "p", }, STEXI @@ -165,6 +167,7 @@ ETEXI .params = "", .help = "show the command line history", .cmd = hmp_info_history, + .flags = "p", }, STEXI @@ -198,7 +201,7 @@ ETEXI STEXI @item info pic @findex info pic -Show i8259 (PIC) state. +Show PIC state. ETEXI { @@ -250,10 +253,11 @@ ETEXI { .name = "mtree", - .args_type = "flatview:-f,dispatch_tree:-d", - .params = "[-f][-d]", + .args_type = "flatview:-f,dispatch_tree:-d,owner:-o", + .params = "[-f][-d][-o]", .help = "show memory tree (-f: dump flat view for address spaces;" - "-d: dump dispatch tree, valid with -f only)", + "-d: dump dispatch tree, valid with -f only);" + "-o: dump region owners/parents", .cmd = hmp_info_mtree, }, @@ -399,6 +403,7 @@ ETEXI .params = "", .help = "show the current VM status (running|paused)", .cmd = hmp_info_status, + .flags = "p", }, STEXI @@ -421,6 +426,7 @@ STEXI Show which guest mouse is receiving events. ETEXI +#if defined(CONFIG_VNC) { .name = "vnc", .args_type = "", @@ -428,6 +434,7 @@ ETEXI .help = "show the vnc server status", .cmd = hmp_info_vnc, }, +#endif STEXI @item info vnc @@ -457,6 +464,7 @@ ETEXI .params = "", .help = "show the current VM name", .cmd = hmp_info_name, + .flags = "p", }, STEXI @@ -471,6 +479,7 @@ ETEXI .params = "", .help = "show the current VM UUID", .cmd = hmp_info_uuid, + .flags = "p", }, STEXI @@ -613,6 +622,7 @@ ETEXI .params = "[path]", .help = "show QOM composition tree", .cmd = hmp_info_qom_tree, + .flags = "p", }, STEXI @@ -671,6 +681,7 @@ ETEXI .params = "", .help = "show memory backends", .cmd = hmp_info_memdev, + .flags = "p", }, STEXI @@ -699,6 +710,7 @@ ETEXI .params = "", .help = "show iothreads", .cmd = hmp_info_iothreads, + .flags = "p", }, STEXI @@ -829,6 +841,7 @@ ETEXI .params = "", .help = "Show information about hotpluggable CPUs", .cmd = hmp_hotpluggable_cpus, + .flags = "p", }, STEXI diff --git a/hmp-commands.hx b/hmp-commands.hx index 35d862a5d2..91dfe51c37 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -15,6 +15,7 @@ ETEXI .params = "[cmd]", .help = "show the help", .cmd = do_help_cmd, + .flags = "p", }, STEXI @@ -54,6 +55,25 @@ STEXI @item q or quit @findex quit Quit the emulator. +ETEXI + + { + .name = "exit_preconfig", + .args_type = "", + .params = "", + .help = "exit the preconfig state", + .cmd = hmp_exit_preconfig, + .flags = "p", + }, + +STEXI +@item exit_preconfig +@findex exit_preconfig +This command makes QEMU exit the preconfig state and proceed with +VM initialization using configuration data provided on the command line +and via the QMP monitor during the preconfig state. The command is only +available during the preconfig state (i.e. when the --preconfig command +line option was in use). ETEXI { @@ -897,13 +917,14 @@ ETEXI { .name = "migrate", - .args_type = "detach:-d,blk:-b,inc:-i,uri:s", - .params = "[-d] [-b] [-i] uri", + .args_type = "detach:-d,blk:-b,inc:-i,resume:-r,uri:s", + .params = "[-d] [-b] [-i] [-r] uri", .help = "migrate to URI (using -d to not wait for completion)" "\n\t\t\t -b for migration without shared storage with" " full copy of disk\n\t\t\t -i for migration without " "shared storage with incremental copy of disk " - "(base image shared between src and destination)", + "(base image shared between src and destination)" + "\n\t\t\t -r to resume a paused migration", .cmd = hmp_migrate, }, @@ -956,7 +977,34 @@ STEXI @findex migrate_incoming Continue an incoming migration using the @var{uri} (that has the same syntax as the -incoming option). +ETEXI + { + .name = "migrate_recover", + .args_type = "uri:s", + .params = "uri", + .help = "Continue a paused incoming postcopy migration", + .cmd = hmp_migrate_recover, + }, + +STEXI +@item migrate_recover @var{uri} +@findex migrate_recover +Continue a paused incoming postcopy migration using the @var{uri}. +ETEXI + + { + .name = "migrate_pause", + .args_type = "", + .params = "", + .help = "Pause an ongoing migration (postcopy-only)", + .cmd = hmp_migrate_pause, + }, + +STEXI +@item migrate_pause +@findex migrate_pause +Pause an ongoing migration. Currently it only supports postcopy. ETEXI { @@ -1088,30 +1136,33 @@ ETEXI { .name = "dump-guest-memory", - .args_type = "paging:-p,detach:-d,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:i?,length:i?", - .params = "[-p] [-d] [-z|-l|-s] filename [begin length]", + .args_type = "paging:-p,detach:-d,windmp:-w,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:l?,length:l?", + .params = "[-p] [-d] [-z|-l|-s|-w] filename [begin length]", .help = "dump guest memory into file 'filename'.\n\t\t\t" "-p: do paging to get guest's memory mapping.\n\t\t\t" "-d: return immediately (do not wait for completion).\n\t\t\t" "-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t" "-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t" "-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t" + "-w: dump in Windows crashdump format (can be used instead of ELF-dump converting),\n\t\t\t" + " for Windows x64 guests with vmcoreinfo driver only.\n\t\t\t" "begin: the starting physical address.\n\t\t\t" "length: the memory size, in bytes.", .cmd = hmp_dump_guest_memory, }, - STEXI @item dump-guest-memory [-p] @var{filename} @var{begin} @var{length} -@item dump-guest-memory [-z|-l|-s] @var{filename} +@item dump-guest-memory [-z|-l|-s|-w] @var{filename} @findex dump-guest-memory Dump guest memory to @var{protocol}. The file can be processed with crash or -gdb. Without -z|-l|-s, the dump format is ELF. +gdb. Without -z|-l|-s|-w, the dump format is ELF. -p: do paging to get guest's memory mapping. -z: dump in kdump-compressed format, with zlib compression. -l: dump in kdump-compressed format, with lzo compression. -s: dump in kdump-compressed format, with snappy compression. + -w: dump in Windows crashdump format (can be used instead of ELF-dump converting), + for Windows x64 guests with vmcoreinfo driver only filename: dump file name. begin: the starting physical address. It's optional, and should be specified together with length. @@ -1647,7 +1698,8 @@ ETEXI STEXI @item block_set_io_throttle @var{device} @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} @findex block_set_io_throttle -Change I/O throttle limits for a block drive to @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr} +Change I/O throttle limits for a block drive to @var{bps} @var{bps_rd} @var{bps_wr} @var{iops} @var{iops_rd} @var{iops_wr}. +@var{device} can be a block device name, a qdev ID or a QOM path. ETEXI { @@ -1799,6 +1851,7 @@ ETEXI .params = "path", .help = "list QOM properties", .cmd = hmp_qom_list, + .flags = "p", }, STEXI @@ -1812,6 +1865,7 @@ ETEXI .params = "path property value", .help = "set QOM property", .cmd = hmp_qom_set, + .flags = "p", }, STEXI @@ -1826,6 +1880,7 @@ ETEXI .help = "show various information about the system state", .cmd = hmp_info_help, .sub_table = info_cmds, + .flags = "p", }, STEXI diff --git a/hmp.c b/hmp.c index a25c7bd9a8..2aafb50e8e 100644 --- a/hmp.c +++ b/hmp.c @@ -234,6 +234,8 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->ram->dirty_sync_count); monitor_printf(mon, "page size: %" PRIu64 " kbytes\n", info->ram->page_size >> 10); + monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n", + info->ram->multifd_bytes >> 10); if (info->ram->dirty_pages_rate) { monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", @@ -274,6 +276,21 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->cpu_throttle_percentage); } + if (info->has_postcopy_blocktime) { + monitor_printf(mon, "postcopy blocktime: %u\n", + info->postcopy_blocktime); + } + + if (info->has_postcopy_vcpu_blocktime) { + Visitor *v; + char *str; + v = string_output_visitor_new(false, &str); + visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, NULL); + visit_complete(v, &str); + monitor_printf(mon, "postcopy vcpu blocktime: %s\n", str); + g_free(str); + visit_free(v); + } qapi_free_MigrationInfo(info); qapi_free_MigrationCapabilityStatusList(caps); } @@ -355,6 +372,9 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %" PRIu64 "\n", MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), params->xbzrle_cache_size); + monitor_printf(mon, "%s: %" PRIu64 "\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), + params->max_postcopy_bandwidth); } qapi_free_MigrationParameters(params); @@ -596,6 +616,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict) qapi_free_BlockStatsList(stats_list); } +#ifdef CONFIG_VNC /* Helper for hmp_info_vnc_clients, _servers */ static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info, const char *name) @@ -683,6 +704,7 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict) qapi_free_VncInfo2List(info2l); } +#endif #ifdef CONFIG_SPICE void hmp_info_spice(Monitor *mon, const QDict *qdict) @@ -1050,6 +1072,14 @@ void hmp_system_powerdown(Monitor *mon, const QDict *qdict) qmp_system_powerdown(NULL); } +void hmp_exit_preconfig(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_x_exit_preconfig(&err); + hmp_handle_error(mon, &err); +} + void hmp_cpu(Monitor *mon, const QDict *qdict) { int64_t cpu_index; @@ -1502,6 +1532,25 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +void hmp_migrate_recover(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + const char *uri = qdict_get_str(qdict, "uri"); + + qmp_migrate_recover(uri, &err); + + hmp_handle_error(mon, &err); +} + +void hmp_migrate_pause(Monitor *mon, const QDict *qdict) +{ + Error *err = NULL; + + qmp_migrate_pause(&err); + + hmp_handle_error(mon, &err); +} + /* Kept for backwards compatibility */ void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict) { @@ -1642,6 +1691,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) } p->xbzrle_cache_size = cache_size; break; + case MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH: + p->has_max_postcopy_bandwidth = true; + visit_type_size(v, param, &p->max_postcopy_bandwidth, &err); + break; default: assert(0); } @@ -1721,12 +1774,14 @@ void hmp_eject(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &err); } +#ifdef CONFIG_VNC static void hmp_change_read_arg(void *opaque, const char *password, void *readline_opaque) { qmp_change_vnc_password(password, NULL); monitor_read_command(opaque, 1); } +#endif void hmp_change(Monitor *mon, const QDict *qdict) { @@ -1737,6 +1792,7 @@ void hmp_change(Monitor *mon, const QDict *qdict) BlockdevChangeReadOnlyMode read_only_mode = 0; Error *err = NULL; +#ifdef CONFIG_VNC if (strcmp(device, "vnc") == 0) { if (read_only) { monitor_printf(mon, @@ -1751,7 +1807,9 @@ void hmp_change(Monitor *mon, const QDict *qdict) } } qmp_change("vnc", target, !!arg, arg, &err); - } else { + } else +#endif + { if (read_only) { read_only_mode = qapi_enum_parse(&BlockdevChangeReadOnlyMode_lookup, @@ -1774,9 +1832,8 @@ void hmp_change(Monitor *mon, const QDict *qdict) void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) { Error *err = NULL; + char *device = (char *) qdict_get_str(qdict, "device"); BlockIOThrottle throttle = { - .has_device = true, - .device = (char *) qdict_get_str(qdict, "device"), .bps = qdict_get_int(qdict, "bps"), .bps_rd = qdict_get_int(qdict, "bps_rd"), .bps_wr = qdict_get_int(qdict, "bps_wr"), @@ -1785,6 +1842,17 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) .iops_wr = qdict_get_int(qdict, "iops_wr"), }; + /* qmp_block_set_io_throttle has separate parameters for the + * (deprecated) block device name and the qdev ID but the HMP + * version has only one, so we must decide which one to pass. */ + if (blk_by_name(device)) { + throttle.has_device = true; + throttle.device = device; + } else { + throttle.has_id = true; + throttle.id = device; + } + qmp_block_set_io_throttle(&throttle, &err); hmp_handle_error(mon, &err); } @@ -1904,10 +1972,12 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) bool detach = qdict_get_try_bool(qdict, "detach", false); bool blk = qdict_get_try_bool(qdict, "blk", false); bool inc = qdict_get_try_bool(qdict, "inc", false); + bool resume = qdict_get_try_bool(qdict, "resume", false); const char *uri = qdict_get_str(qdict, "uri"); Error *err = NULL; - qmp_migrate(uri, !!blk, blk, !!inc, inc, false, false, &err); + qmp_migrate(uri, !!blk, blk, !!inc, inc, + false, false, true, resume, &err); if (err) { hmp_handle_error(mon, &err); return; @@ -1951,6 +2021,7 @@ void hmp_device_del(Monitor *mon, const QDict *qdict) void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) { Error *err = NULL; + bool win_dmp = qdict_get_try_bool(qdict, "windmp", false); bool paging = qdict_get_try_bool(qdict, "paging", false); bool zlib = qdict_get_try_bool(qdict, "zlib", false); bool lzo = qdict_get_try_bool(qdict, "lzo", false); @@ -1965,12 +2036,16 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) enum DumpGuestMemoryFormat dump_format = DUMP_GUEST_MEMORY_FORMAT_ELF; char *prot; - if (zlib + lzo + snappy > 1) { - error_setg(&err, "only one of '-z|-l|-s' can be set"); + if (zlib + lzo + snappy + win_dmp > 1) { + error_setg(&err, "only one of '-z|-l|-s|-w' can be set"); hmp_handle_error(mon, &err); return; } + if (win_dmp) { + dump_format = DUMP_GUEST_MEMORY_FORMAT_WIN_DMP; + } + if (zlib) { dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB; } @@ -2077,12 +2152,12 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) int has_hold_time = qdict_haskey(qdict, "hold-time"); int hold_time = qdict_get_try_int(qdict, "hold-time", -1); Error *err = NULL; - char *separator; + const char *separator; int keyname_len; while (1) { - separator = strchr(keys, '-'); - keyname_len = separator ? separator - keys : strlen(keys); + separator = qemu_strchrnul(keys, '-'); + keyname_len = separator - keys; /* Be compatible with old interface, convert user inputted "<" */ if (keys[0] == '<' && keyname_len == 1) { @@ -2119,7 +2194,7 @@ void hmp_sendkey(Monitor *mon, const QDict *qdict) keylist->value->u.qcode.data = idx; } - if (!separator) { + if (!*separator) { break; } keys = separator + 1; diff --git a/hmp.h b/hmp.h index 4e2ec375b0..33354f1bdd 100644 --- a/hmp.h +++ b/hmp.h @@ -44,6 +44,7 @@ void hmp_quit(Monitor *mon, const QDict *qdict); void hmp_stop(Monitor *mon, const QDict *qdict); void hmp_system_reset(Monitor *mon, const QDict *qdict); void hmp_system_powerdown(Monitor *mon, const QDict *qdict); +void hmp_exit_preconfig(Monitor *mon, const QDict *qdict); void hmp_cpu(Monitor *mon, const QDict *qdict); void hmp_memsave(Monitor *mon, const QDict *qdict); void hmp_pmemsave(Monitor *mon, const QDict *qdict); @@ -68,6 +69,8 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict); void hmp_migrate_cancel(Monitor *mon, const QDict *qdict); void hmp_migrate_continue(Monitor *mon, const QDict *qdict); void hmp_migrate_incoming(Monitor *mon, const QDict *qdict); +void hmp_migrate_recover(Monitor *mon, const QDict *qdict); +void hmp_migrate_pause(Monitor *mon, const QDict *qdict); void hmp_migrate_set_downtime(Monitor *mon, const QDict *qdict); void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict); void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict); diff --git a/hw/9pfs/9p-handle.c b/hw/9pfs/9p-handle.c index 4dc0d2bed1..f3641dbe4a 100644 --- a/hw/9pfs/9p-handle.c +++ b/hw/9pfs/9p-handle.c @@ -559,19 +559,13 @@ static int handle_unlinkat(FsContext *ctx, V9fsPath *dir, { int dirfd, ret; HandleData *data = (HandleData *) ctx->private; - int rflags; dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); if (dirfd < 0) { return dirfd; } - rflags = 0; - if (flags & P9_DOTL_AT_REMOVEDIR) { - rflags |= AT_REMOVEDIR; - } - - ret = unlinkat(dirfd, name, rflags); + ret = unlinkat(dirfd, name, flags); close(dirfd); return ret; diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index b37b1db453..c30f4f26bd 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -65,7 +65,7 @@ int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags, assert(*path != '/'); head = g_strdup(path); - c = strchrnul(path, '/'); + c = qemu_strchrnul(path, '/'); if (*c) { /* Intermediate path element */ head[c - path] = 0; @@ -308,7 +308,7 @@ update_map_file: if (credp->fc_gid != -1) { gid = credp->fc_gid; } - if (credp->fc_mode != -1) { + if (credp->fc_mode != (mode_t)-1) { mode = credp->fc_mode; } if (credp->fc_rdev != -1) { @@ -414,7 +414,7 @@ static int local_set_xattrat(int dirfd, const char *path, FsCred *credp) return err; } } - if (credp->fc_mode != -1) { + if (credp->fc_mode != (mode_t)-1) { uint32_t tmp_mode = cpu_to_le32(credp->fc_mode); err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0); @@ -1373,10 +1373,10 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir, return ret; } +#ifdef FS_IOC_GETVERSION static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, mode_t st_mode, uint64_t *st_gen) { -#ifdef FS_IOC_GETVERSION int err; V9fsFidOpenState fid_open; @@ -1395,30 +1395,21 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); local_close(ctx, &fid_open); return err; -#else - errno = ENOTTY; - return -1; -#endif } +#endif -static int local_init(FsContext *ctx, Error **errp) +static int local_ioc_getversion_init(FsContext *ctx, LocalData *data, Error **errp) { - struct statfs stbuf; - LocalData *data = g_malloc(sizeof(*data)); - - data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); - if (data->mountfd == -1) { - error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); - goto err; - } - #ifdef FS_IOC_GETVERSION + struct statfs stbuf; + /* * use ioc_getversion only if the ioctl is definied */ if (fstatfs(data->mountfd, &stbuf) < 0) { - close_preserve_errno(data->mountfd); - goto err; + error_setg_errno(errp, errno, + "failed to stat file system at '%s'", ctx->fs_root); + return -1; } switch (stbuf.f_type) { case EXT2_SUPER_MAGIC: @@ -1429,6 +1420,23 @@ static int local_init(FsContext *ctx, Error **errp) break; } #endif + return 0; +} + +static int local_init(FsContext *ctx, Error **errp) +{ + LocalData *data = g_malloc(sizeof(*data)); + + data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); + if (data->mountfd == -1) { + error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root); + goto err; + } + + if (local_ioc_getversion_init(ctx, data, errp) < 0) { + close(data->mountfd); + goto err; + } if (ctx->export_flags & V9FS_SM_PASSTHROUGH) { ctx->xops = passthrough_xattr_ops; diff --git a/hw/9pfs/9p-proxy.c b/hw/9pfs/9p-proxy.c index e2e03292de..47a94e088d 100644 --- a/hw/9pfs/9p-proxy.c +++ b/hw/9pfs/9p-proxy.c @@ -1088,7 +1088,7 @@ static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path, static int connect_namedsocket(const char *path, Error **errp) { - int sockfd, size; + int sockfd; struct sockaddr_un helper; if (strlen(path) >= sizeof(helper.sun_path)) { @@ -1102,8 +1102,7 @@ static int connect_namedsocket(const char *path, Error **errp) } strcpy(helper.sun_path, path); helper.sun_family = AF_UNIX; - size = strlen(helper.sun_path) + sizeof(helper.sun_family); - if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) { + if (connect(sockfd, (struct sockaddr *)&helper, sizeof(helper)) < 0) { error_setg_errno(errp, errno, "failed to connect to '%s'", path); close(sockfd); return -1; diff --git a/hw/9pfs/9p-util.c b/hw/9pfs/9p-util.c index f709c27a1f..614b7fc34d 100644 --- a/hw/9pfs/9p-util.c +++ b/hw/9pfs/9p-util.c @@ -24,3 +24,36 @@ ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name, g_free(proc_path); return ret; } + +ssize_t flistxattrat_nofollow(int dirfd, const char *filename, + char *list, size_t size) +{ + char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); + int ret; + + ret = llistxattr(proc_path, list, size); + g_free(proc_path); + return ret; +} + +ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, + const char *name) +{ + char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); + int ret; + + ret = lremovexattr(proc_path, name); + g_free(proc_path); + return ret; +} + +int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name, + void *value, size_t size, int flags) +{ + char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); + int ret; + + ret = lsetxattr(proc_path, name, value, size, flags); + g_free(proc_path); + return ret; +} diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index dc0d2e29aa..79ed6b233e 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -60,5 +60,9 @@ ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, void *value, size_t size); int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, void *value, size_t size, int flags); +ssize_t flistxattrat_nofollow(int dirfd, const char *filename, + char *list, size_t size); +ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, + const char *name); #endif diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c index d05c1a1c1d..c696d8f846 100644 --- a/hw/9pfs/9p-xattr.c +++ b/hw/9pfs/9p-xattr.c @@ -60,17 +60,6 @@ ssize_t pt_listxattr(FsContext *ctx, const char *path, return name_size; } -static ssize_t flistxattrat_nofollow(int dirfd, const char *filename, - char *list, size_t size) -{ - char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); - int ret; - - ret = llistxattr(proc_path, list, size); - g_free(proc_path); - return ret; -} - /* * Get the list and pass to each layer to find out whether * to send the data or not @@ -196,17 +185,6 @@ ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name, return local_getxattr_nofollow(ctx, path, name, value, size); } -int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name, - void *value, size_t size, int flags) -{ - char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); - int ret; - - ret = lsetxattr(proc_path, name, value, size, flags); - g_free(proc_path); - return ret; -} - ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path, const char *name, void *value, size_t size, int flags) @@ -235,17 +213,6 @@ int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value, return local_setxattr_nofollow(ctx, path, name, value, size, flags); } -static ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, - const char *name) -{ - char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename); - int ret; - - ret = lremovexattr(proc_path, name); - g_free(proc_path); - return ret; -} - ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path, const char *name) { diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 48fa48e720..eef289e394 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1195,6 +1195,10 @@ static void coroutine_fn v9fs_setattr(void *opaque) goto out_nofid; } + trace_v9fs_setattr(pdu->tag, pdu->id, fid, + v9iattr.valid, v9iattr.mode, v9iattr.uid, v9iattr.gid, + v9iattr.size, v9iattr.atime_sec, v9iattr.mtime_sec); + fidp = get_fid(pdu, fid); if (fidp == NULL) { err = -EINVAL; @@ -1259,6 +1263,7 @@ static void coroutine_fn v9fs_setattr(void *opaque) } } err = offset; + trace_v9fs_setattr_return(pdu->tag, pdu->id); out: put_fid(pdu, fidp); out_nofid: @@ -2517,7 +2522,7 @@ static void coroutine_fn v9fs_unlinkat(void *opaque) { int err = 0; V9fsString name; - int32_t dfid, flags; + int32_t dfid, flags, rflags = 0; size_t offset = 7; V9fsPath path; V9fsFidState *dfidp; @@ -2544,6 +2549,15 @@ static void coroutine_fn v9fs_unlinkat(void *opaque) goto out_nofid; } + if (flags & ~P9_DOTL_AT_REMOVEDIR) { + err = -EINVAL; + goto out_nofid; + } + + if (flags & P9_DOTL_AT_REMOVEDIR) { + rflags |= AT_REMOVEDIR; + } + dfidp = get_fid(pdu, dfid); if (dfidp == NULL) { err = -EINVAL; @@ -2562,7 +2576,7 @@ static void coroutine_fn v9fs_unlinkat(void *opaque) if (err < 0) { goto out_err; } - err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, flags); + err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, rflags); if (!err) { err = offset; } @@ -3251,8 +3265,8 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) xattr_fidp->fs.xattr.len = size; xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; + xattr_fidp->fs.xattr.value = g_malloc0(size); if (size) { - xattr_fidp->fs.xattr.value = g_malloc0(size); err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, xattr_fidp->fs.xattr.value, xattr_fidp->fs.xattr.len); @@ -3284,8 +3298,8 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) xattr_fidp->fs.xattr.len = size; xattr_fidp->fid_type = P9_FID_XATTR; xattr_fidp->fs.xattr.xattrwalk_fid = true; + xattr_fidp->fs.xattr.value = g_malloc0(size); if (size) { - xattr_fidp->fs.xattr.value = g_malloc0(size); err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, &name, xattr_fidp->fs.xattr.value, xattr_fidp->fs.xattr.len); @@ -3313,7 +3327,7 @@ out_nofid: static void coroutine_fn v9fs_xattrcreate(void *opaque) { - int flags; + int flags, rflags = 0; int32_t fid; uint64_t size; ssize_t err = 0; @@ -3330,6 +3344,19 @@ static void coroutine_fn v9fs_xattrcreate(void *opaque) } trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags); + if (flags & ~(P9_XATTR_CREATE | P9_XATTR_REPLACE)) { + err = -EINVAL; + goto out_nofid; + } + + if (flags & P9_XATTR_CREATE) { + rflags |= XATTR_CREATE; + } + + if (flags & P9_XATTR_REPLACE) { + rflags |= XATTR_REPLACE; + } + if (size > XATTR_SIZE_MAX) { err = -E2BIG; goto out_nofid; @@ -3351,7 +3378,7 @@ static void coroutine_fn v9fs_xattrcreate(void *opaque) xattr_fidp->fs.xattr.copied_len = 0; xattr_fidp->fs.xattr.xattrwalk_fid = false; xattr_fidp->fs.xattr.len = size; - xattr_fidp->fs.xattr.flags = flags; + xattr_fidp->fs.xattr.flags = rflags; v9fs_string_init(&xattr_fidp->fs.xattr.name); v9fs_string_copy(&xattr_fidp->fs.xattr.name, &name); xattr_fidp->fs.xattr.value = g_malloc0(size); diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index bad8ee719c..8883761b2c 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -169,6 +169,10 @@ typedef struct V9fsConf char *fsdev_id; } V9fsConf; +/* 9p2000.L xattr flags (matches Linux values) */ +#define P9_XATTR_CREATE 1 +#define P9_XATTR_REPLACE 2 + typedef struct V9fsXattr { uint64_t copied_len; diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs index fd90b62900..e3fa673665 100644 --- a/hw/9pfs/Makefile.objs +++ b/hw/9pfs/Makefile.objs @@ -1,3 +1,4 @@ +ifeq ($(call lor,$(CONFIG_VIRTIO_9P),$(CONFIG_XEN)),y) common-obj-y = 9p.o 9p-util.o common-obj-y += 9p-local.o 9p-xattr.o common-obj-y += 9p-xattr-user.o 9p-posix-acl.o @@ -5,6 +6,7 @@ common-obj-y += coth.o cofs.o codir.o cofile.o common-obj-y += coxattr.o 9p-synth.o common-obj-$(CONFIG_OPEN_BY_HANDLE) += 9p-handle.o common-obj-y += 9p-proxy.o -common-obj-$(CONFIG_XEN) += xen-9p-backend.o +endif -obj-$(CONFIG_VIRTIO) += virtio-9p-device.o +common-obj-$(CONFIG_XEN) += xen-9p-backend.o +obj-$(CONFIG_VIRTIO_9P) += virtio-9p-device.o diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events index 1aee350c42..881e4c4dd8 100644 --- a/hw/9pfs/trace-events +++ b/hw/9pfs/trace-events @@ -46,3 +46,5 @@ v9fs_xattrwalk_return(uint16_t tag, uint8_t id, int64_t size) "tag %d id %d size v9fs_xattrcreate(uint16_t tag, uint8_t id, int32_t fid, char* name, uint64_t size, int flags) "tag %d id %d fid %d name %s size %"PRIu64" flags %d" v9fs_readlink(uint16_t tag, uint8_t id, int32_t fid) "tag %d id %d fid %d" v9fs_readlink_return(uint16_t tag, uint8_t id, char* target) "tag %d id %d name %s" +v9fs_setattr(uint16_t tag, uint8_t id, int32_t fid, int32_t valid, int32_t mode, int32_t uid, int32_t gid, int64_t size, int64_t atime_sec, int64_t mtime_sec) "tag %u id %u fid %d iattr={valid %d mode %d uid %d gid %d size %"PRId64" atime=%"PRId64" mtime=%"PRId64" }" +v9fs_setattr_return(uint16_t tag, uint8_t id) "tag %u id %u" diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 95e50c4dfc..6026780f95 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -331,14 +331,14 @@ static int xen_9pfs_free(struct XenDevice *xendev) for (i = 0; i < xen_9pdev->num_rings; i++) { if (xen_9pdev->rings[i].data != NULL) { - xengnttab_unmap(xen_9pdev->xendev.gnttabdev, - xen_9pdev->rings[i].data, - (1 << xen_9pdev->rings[i].ring_order)); + xen_be_unmap_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].data, + (1 << xen_9pdev->rings[i].ring_order)); } if (xen_9pdev->rings[i].intf != NULL) { - xengnttab_unmap(xen_9pdev->xendev.gnttabdev, - xen_9pdev->rings[i].intf, - 1); + xen_be_unmap_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].intf, + 1); } if (xen_9pdev->rings[i].bh != NULL) { qemu_bh_delete(xen_9pdev->rings[i].bh); @@ -390,11 +390,10 @@ static int xen_9pfs_connect(struct XenDevice *xendev) } g_free(str); - xen_9pdev->rings[i].intf = xengnttab_map_grant_ref( - xen_9pdev->xendev.gnttabdev, - xen_9pdev->xendev.dom, - xen_9pdev->rings[i].ref, - PROT_READ | PROT_WRITE); + xen_9pdev->rings[i].intf = + xen_be_map_grant_ref(&xen_9pdev->xendev, + xen_9pdev->rings[i].ref, + PROT_READ | PROT_WRITE); if (!xen_9pdev->rings[i].intf) { goto out; } @@ -403,12 +402,11 @@ static int xen_9pfs_connect(struct XenDevice *xendev) goto out; } xen_9pdev->rings[i].ring_order = ring_order; - xen_9pdev->rings[i].data = xengnttab_map_domain_grant_refs( - xen_9pdev->xendev.gnttabdev, - (1 << ring_order), - xen_9pdev->xendev.dom, - xen_9pdev->rings[i].intf->ref, - PROT_READ | PROT_WRITE); + xen_9pdev->rings[i].data = + xen_be_map_grant_refs(&xen_9pdev->xendev, + xen_9pdev->rings[i].intf->ref, + (1 << ring_order), + PROT_READ | PROT_WRITE); if (!xen_9pdev->rings[i].data) { goto out; } diff --git a/hw/Makefile.objs b/hw/Makefile.objs index f068a3d491..312b0eb4d9 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -23,7 +23,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += nvram/ devices-dirs-$(CONFIG_SOFTMMU) += pci/ devices-dirs-$(CONFIG_PCI) += pci-bridge/ pci-host/ devices-dirs-$(CONFIG_SOFTMMU) += pcmcia/ -devices-dirs-$(CONFIG_SOFTMMU) += scsi/ +devices-dirs-$(CONFIG_SCSI) += scsi/ devices-dirs-$(CONFIG_SOFTMMU) += sd/ devices-dirs-$(CONFIG_SOFTMMU) += ssi/ devices-dirs-$(CONFIG_SOFTMMU) += timer/ diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 3fa557cea1..1e43cd736d 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -627,7 +627,7 @@ Aml *aml_notify(Aml *arg1, Aml *arg2) return var; } -/* helper to call method with 1 argument */ +/* helper to call method without argument */ Aml *aml_call0(const char *method) { Aml *var = aml_alloc(); diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 59d6e4254c..27eeb6609f 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -169,6 +169,21 @@ struct NvdimmNfitControlRegion { } QEMU_PACKED; typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion; +/* + * NVDIMM Platform Capabilities Structure + * + * Defined in section 5.2.25.9 of ACPI 6.2 Errata A, September 2017 + */ +struct NvdimmNfitPlatformCaps { + uint16_t type; + uint16_t length; + uint8_t highest_cap; + uint8_t reserved[3]; + uint32_t capabilities; + uint8_t reserved2[4]; +} QEMU_PACKED; +typedef struct NvdimmNfitPlatformCaps NvdimmNfitPlatformCaps; + /* * Module serial number is a unique number for each device. We use the * slot id of NVDIMM device to generate this number so that each device @@ -351,7 +366,23 @@ static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) JEDEC Annex L Release 3. */); } -static GArray *nvdimm_build_device_structure(void) +/* + * ACPI 6.2 Errata A: 5.2.25.9 NVDIMM Platform Capabilities Structure + */ +static void +nvdimm_build_structure_caps(GArray *structures, uint32_t capabilities) +{ + NvdimmNfitPlatformCaps *nfit_caps; + + nfit_caps = acpi_data_push(structures, sizeof(*nfit_caps)); + + nfit_caps->type = cpu_to_le16(7 /* NVDIMM Platform Capabilities */); + nfit_caps->length = cpu_to_le16(sizeof(*nfit_caps)); + nfit_caps->highest_cap = 31 - clz32(capabilities); + nfit_caps->capabilities = cpu_to_le32(capabilities); +} + +static GArray *nvdimm_build_device_structure(AcpiNVDIMMState *state) { GSList *device_list = nvdimm_get_device_list(); GArray *structures = g_array_new(false, true /* clear */, 1); @@ -373,6 +404,10 @@ static GArray *nvdimm_build_device_structure(void) } g_slist_free(device_list); + if (state->persistence) { + nvdimm_build_structure_caps(structures, state->persistence); + } + return structures; } @@ -381,16 +416,18 @@ static void nvdimm_init_fit_buffer(NvdimmFitBuffer *fit_buf) fit_buf->fit = g_array_new(false, true /* clear */, 1); } -static void nvdimm_build_fit_buffer(NvdimmFitBuffer *fit_buf) +static void nvdimm_build_fit_buffer(AcpiNVDIMMState *state) { + NvdimmFitBuffer *fit_buf = &state->fit_buf; + g_array_free(fit_buf->fit, true); - fit_buf->fit = nvdimm_build_device_structure(); + fit_buf->fit = nvdimm_build_device_structure(state); fit_buf->dirty = true; } void nvdimm_plug(AcpiNVDIMMState *state) { - nvdimm_build_fit_buffer(&state->fit_buf); + nvdimm_build_fit_buffer(state); } static void nvdimm_build_nfit(AcpiNVDIMMState *state, GArray *table_offsets, diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 91c82fdc7a..80d42e12ff 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -32,7 +32,6 @@ #include "hw/pci/pci.h" #include "hw/acpi/acpi.h" #include "sysemu/sysemu.h" -#include "exec/ioport.h" #include "exec/address-spaces.h" #include "hw/pci/pci_bus.h" #include "qapi/error.h" diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 8b703455b7..6404af5f33 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -28,7 +28,6 @@ #include "sysemu/sysemu.h" #include "qapi/error.h" #include "qemu/range.h" -#include "exec/ioport.h" #include "hw/nvram/fw_cfg.h" #include "exec/address-spaces.h" #include "hw/acpi/piix4.h" diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 6a40869488..d74b5b55e1 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" #include "hw/hw.h" @@ -666,7 +667,8 @@ static bool window_translate(TyphoonWindow *win, hwaddr addr, Pchip and generate a machine check interrupt. */ static IOMMUTLBEntry typhoon_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, + int iommu_idx) { TyphoonPchip *pchip = container_of(iommu, TyphoonPchip, iommu); IOMMUTLBEntry ret; @@ -812,8 +814,6 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, qemu_irq *p_rtc_irq, AlphaCPU *cpus[4], pci_map_irq_fn sys_map_irq) { - const uint64_t MB = 1024 * 1024; - const uint64_t GB = 1024 * MB; MemoryRegion *addr_space = get_system_memory(); DeviceState *dev; TyphoonState *s; @@ -854,30 +854,30 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Pchip0 CSRs, 0x801.8000.0000, 256MB. */ memory_region_init_io(&s->pchip.region, OBJECT(s), &pchip_ops, s, "pchip0", - 256*MB); + 256 * MiB); memory_region_add_subregion(addr_space, 0x80180000000ULL, &s->pchip.region); /* Cchip CSRs, 0x801.A000.0000, 256MB. */ memory_region_init_io(&s->cchip.region, OBJECT(s), &cchip_ops, s, "cchip0", - 256*MB); + 256 * MiB); memory_region_add_subregion(addr_space, 0x801a0000000ULL, &s->cchip.region); /* Dchip CSRs, 0x801.B000.0000, 256MB. */ memory_region_init_io(&s->dchip_region, OBJECT(s), &dchip_ops, s, "dchip0", - 256*MB); + 256 * MiB); memory_region_add_subregion(addr_space, 0x801b0000000ULL, &s->dchip_region); /* Pchip0 PCI memory, 0x800.0000.0000, 4GB. */ - memory_region_init(&s->pchip.reg_mem, OBJECT(s), "pci0-mem", 4*GB); + memory_region_init(&s->pchip.reg_mem, OBJECT(s), "pci0-mem", 4 * GiB); memory_region_add_subregion(addr_space, 0x80000000000ULL, &s->pchip.reg_mem); /* Pchip0 PCI I/O, 0x801.FC00.0000, 32MB. */ memory_region_init_io(&s->pchip.reg_io, OBJECT(s), &alpha_pci_ignore_ops, - NULL, "pci0-io", 32*MB); + NULL, "pci0-io", 32 * MiB); memory_region_add_subregion(addr_space, 0x801fc000000ULL, &s->pchip.reg_io); @@ -898,13 +898,13 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, /* Pchip0 PCI special/interrupt acknowledge, 0x801.F800.0000, 64MB. */ memory_region_init_io(&s->pchip.reg_iack, OBJECT(s), &alpha_pci_iack_ops, - b, "pci0-iack", 64*MB); + b, "pci0-iack", 64 * MiB); memory_region_add_subregion(addr_space, 0x801f8000000ULL, &s->pchip.reg_iack); /* Pchip0 PCI configuration, 0x801.FE00.0000, 16MB. */ memory_region_init_io(&s->pchip.reg_conf, OBJECT(s), &alpha_pci_conf1_ops, - b, "pci0-conf", 16*MB); + b, "pci0-conf", 16 * MiB); memory_region_add_subregion(addr_space, 0x801fe000000ULL, &s->pchip.reg_conf); diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 2885e3e234..d51fcecaf2 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -35,3 +35,4 @@ obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o obj-$(CONFIG_IOTKIT) += iotkit.o obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o +obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 5dbbacb7e8..9fe875cdb5 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -27,20 +27,19 @@ static void aw_a10_init(Object *obj) { AwA10State *s = AW_A10(obj); - object_initialize(&s->cpu, sizeof(s->cpu), "cortex-a8-" TYPE_ARM_CPU); - object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); + object_initialize_child(obj, "cpu", &s->cpu, sizeof(s->cpu), + "cortex-a8-" TYPE_ARM_CPU, &error_abort, NULL); - object_initialize(&s->intc, sizeof(s->intc), TYPE_AW_A10_PIC); - qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default()); + sysbus_init_child_obj(obj, "intc", &s->intc, sizeof(s->intc), + TYPE_AW_A10_PIC); - object_initialize(&s->timer, sizeof(s->timer), TYPE_AW_A10_PIT); - qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); + sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer), + TYPE_AW_A10_PIT); - object_initialize(&s->emac, sizeof(s->emac), TYPE_AW_EMAC); - qdev_set_parent_bus(DEVICE(&s->emac), sysbus_get_default()); + sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac), TYPE_AW_EMAC); - object_initialize(&s->sata, sizeof(s->sata), TYPE_ALLWINNER_AHCI); - qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default()); + sysbus_init_child_obj(obj, "sata", &s->sata, sizeof(s->sata), + TYPE_ALLWINNER_AHCI); } static void aw_a10_realize(DeviceState *dev, Error **errp) @@ -108,9 +107,9 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(&s->sata), 0, AW_A10_SATA_BASE); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, s->irq[56]); - /* FIXME use a qdev chardev prop instead of serial_hds[] */ + /* FIXME use a qdev chardev prop instead of serial_hd() */ serial_mm_init(get_system_memory(), AW_A10_UART0_REG_BASE, 2, s->irq[1], - 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); } static void aw_a10_class_init(ObjectClass *oc, void *data) diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index f123cc7d3d..6b07666057 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -134,14 +134,13 @@ static void armv7m_instance_init(Object *obj) memory_region_init(&s->container, obj, "armv7m-container", UINT64_MAX); - object_initialize(&s->nvic, sizeof(s->nvic), TYPE_NVIC); - qdev_set_parent_bus(DEVICE(&s->nvic), sysbus_get_default()); + sysbus_init_child_obj(obj, "nvnic", &s->nvic, sizeof(s->nvic), TYPE_NVIC); object_property_add_alias(obj, "num-irq", OBJECT(&s->nvic), "num-irq", &error_abort); for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { - object_initialize(&s->bitband[i], sizeof(s->bitband[i]), TYPE_BITBAND); - qdev_set_parent_bus(DEVICE(&s->bitband[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "bitband[*]", &s->bitband[i], + sizeof(s->bitband[i]), TYPE_BITBAND); } } @@ -178,6 +177,12 @@ static void armv7m_realize(DeviceState *dev, Error **errp) return; } } + + /* Tell the CPU where the NVIC is; it will fail realize if it doesn't + * have one. + */ + s->cpu->env.nvic = &s->nvic; + object_property_set_bool(OBJECT(s->cpu), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); @@ -202,7 +207,6 @@ static void armv7m_realize(DeviceState *dev, Error **errp) sbd = SYS_BUS_DEVICE(&s->nvic); sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ)); - s->cpu->env.nvic = &s->nvic; memory_region_add_subregion(&s->container, 0xe000e000, sysbus_mmio_get_region(sbd, 0)); @@ -261,27 +265,6 @@ static void armv7m_reset(void *opaque) cpu_reset(CPU(cpu)); } -/* Init CPU and memory for a v7-M based board. - mem_size is in bytes. - Returns the ARMv7M device. */ - -DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, - const char *kernel_filename, const char *cpu_type) -{ - DeviceState *armv7m; - - armv7m = qdev_create(NULL, TYPE_ARMV7M); - qdev_prop_set_uint32(armv7m, "num-irq", num_irq); - qdev_prop_set_string(armv7m, "cpu-type", cpu_type); - object_property_set_link(OBJECT(armv7m), OBJECT(get_system_memory()), - "memory", &error_abort); - /* This will exit with an error if the user passed us a bad cpu_type */ - qdev_init_nofail(armv7m); - - armv7m_load_kernel(ARM_CPU(first_cpu), kernel_filename, mem_size); - return armv7m; -} - void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) { int image_size; diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 7088c907bd..bb9d33848d 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -17,9 +17,9 @@ #include "hw/arm/arm.h" #include "hw/arm/aspeed_soc.h" #include "hw/boards.h" +#include "hw/i2c/smbus.h" #include "qemu/log.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "hw/loader.h" #include "qemu/error-report.h" @@ -46,6 +46,7 @@ enum { PALMETTO_BMC, AST2500_EVB, ROMULUS_BMC, + WITHERSPOON_BMC, }; /* Palmetto hardware value: 0x120CE416 */ @@ -83,8 +84,13 @@ enum { SCU_AST2500_HW_STRAP_ACPI_ENABLE | \ SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER)) +/* Witherspoon hardware value: 0xF10AD216 (but use romulus definition) */ +#define WITHERSPOON_BMC_HW_STRAP1 ROMULUS_BMC_HW_STRAP1 + static void palmetto_bmc_i2c_init(AspeedBoardState *bmc); static void ast2500_evb_i2c_init(AspeedBoardState *bmc); +static void romulus_bmc_i2c_init(AspeedBoardState *bmc); +static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc); static const AspeedBoardConfig aspeed_boards[] = { [PALMETTO_BMC] = { @@ -109,6 +115,15 @@ static const AspeedBoardConfig aspeed_boards[] = { .fmc_model = "n25q256a", .spi_model = "mx66l1g45g", .num_cs = 2, + .i2c_init = romulus_bmc_i2c_init, + }, + [WITHERSPOON_BMC] = { + .soc_name = "ast2500-a1", + .hw_strap1 = WITHERSPOON_BMC_HW_STRAP1, + .fmc_model = "mx25l25635e", + .spi_model = "mx66l1g45g", + .num_cs = 2, + .i2c_init = witherspoon_bmc_i2c_init, }, }; @@ -225,7 +240,7 @@ static void aspeed_board_init(MachineState *machine, * SoC and 128MB for the AST2500 SoC, which is twice as big as * needed by the flash modules of the Aspeed machines. */ - memory_region_init_rom_nomigrate(boot_rom, OBJECT(bmc), "aspeed.boot_rom", + memory_region_init_rom(boot_rom, OBJECT(bmc), "aspeed.boot_rom", fl->size, &error_abort); memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR, boot_rom); @@ -249,11 +264,15 @@ static void palmetto_bmc_i2c_init(AspeedBoardState *bmc) { AspeedSoCState *soc = &bmc->soc; DeviceState *dev; + uint8_t *eeprom_buf = g_malloc0(32 * 1024); /* The palmetto platform expects a ds3231 RTC but a ds1338 is * enough to provide basic RTC features. Alarms will be missing */ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 0), "ds1338", 0x68); + smbus_eeprom_init_one(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 0), 0x50, + eeprom_buf); + /* add a TMP423 temperature sensor */ dev = i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 2), "tmp423", 0x4c); @@ -279,7 +298,6 @@ static void palmetto_bmc_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; - mc->ignore_memory_transaction_failures = true; } static const TypeInfo palmetto_bmc_type = { @@ -291,9 +309,17 @@ static const TypeInfo palmetto_bmc_type = { static void ast2500_evb_i2c_init(AspeedBoardState *bmc) { AspeedSoCState *soc = &bmc->soc; + uint8_t *eeprom_buf = g_malloc0(8 * 1024); + + smbus_eeprom_init_one(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 3), 0x50, + eeprom_buf); /* The AST2500 EVB expects a LM75 but a TMP105 is compatible */ i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 7), "tmp105", 0x4d); + + /* The AST2500 EVB does not have an RTC. Let's pretend that one is + * plugged on the I2C bus header */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32); } static void ast2500_evb_init(MachineState *machine) @@ -312,7 +338,6 @@ static void ast2500_evb_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; - mc->ignore_memory_transaction_failures = true; } static const TypeInfo ast2500_evb_type = { @@ -321,6 +346,15 @@ static const TypeInfo ast2500_evb_type = { .class_init = ast2500_evb_class_init, }; +static void romulus_bmc_i2c_init(AspeedBoardState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + + /* The romulus board expects Epson RX8900 I2C RTC but a ds1338 is + * good enough */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32); +} + static void romulus_bmc_init(MachineState *machine) { aspeed_board_init(machine, &aspeed_boards[ROMULUS_BMC]); @@ -337,7 +371,6 @@ static void romulus_bmc_class_init(ObjectClass *oc, void *data) mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; - mc->ignore_memory_transaction_failures = true; } static const TypeInfo romulus_bmc_type = { @@ -346,11 +379,59 @@ static const TypeInfo romulus_bmc_type = { .class_init = romulus_bmc_class_init, }; +static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) +{ + AspeedSoCState *soc = &bmc->soc; + uint8_t *eeprom_buf = g_malloc0(8 * 1024); + + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 3), "pca9552", 0x60); + + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 4), "tmp423", 0x4c); + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 5), "tmp423", 0x4c); + + /* The Witherspoon expects a TMP275 but a TMP105 is compatible */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 9), "tmp105", 0x4a); + + /* The witherspoon board expects Epson RX8900 I2C RTC but a ds1338 is + * good enough */ + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32); + + smbus_eeprom_init_one(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), 0x51, + eeprom_buf); + i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "pca9552", + 0x60); +} + +static void witherspoon_bmc_init(MachineState *machine) +{ + aspeed_board_init(machine, &aspeed_boards[WITHERSPOON_BMC]); +} + +static void witherspoon_bmc_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "OpenPOWER Witherspoon BMC (ARM1176)"; + mc->init = witherspoon_bmc_init; + mc->max_cpus = 1; + mc->no_sdcard = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->no_parallel = 1; +} + +static const TypeInfo witherspoon_bmc_type = { + .name = MACHINE_TYPE_NAME("witherspoon-bmc"), + .parent = TYPE_MACHINE, + .class_init = witherspoon_bmc_class_init, +}; + static void aspeed_machine_init(void) { type_register_static(&palmetto_bmc_type); type_register_static(&ast2500_evb_type); type_register_static(&romulus_bmc_type); + type_register_static(&witherspoon_bmc_type); } type_init(aspeed_machine_init) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 30d25f8b06..e68911af0f 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -109,18 +109,6 @@ static void aspeed_soc_init(Object *obj) object_initialize(&s->cpu, sizeof(s->cpu), sc->info->cpu_type); object_property_add_child(obj, "cpu", OBJECT(&s->cpu), NULL); - object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC); - object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL); - qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default()); - - object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER); - object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL); - qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default()); - - object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C); - object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL); - qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default()); - object_initialize(&s->scu, sizeof(s->scu), TYPE_ASPEED_SCU); object_property_add_child(obj, "scu", OBJECT(&s->scu), NULL); qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); @@ -133,6 +121,20 @@ static void aspeed_soc_init(Object *obj) object_property_add_alias(obj, "hw-prot-key", OBJECT(&s->scu), "hw-prot-key", &error_abort); + object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC); + object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL); + qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default()); + + object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER); + object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL); + object_property_add_const_link(OBJECT(&s->timerctrl), "scu", + OBJECT(&s->scu), &error_abort); + qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default()); + + object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C); + object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL); + qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default()); + object_initialize(&s->fmc, sizeof(s->fmc), sc->info->fmc_typename); object_property_add_child(obj, "fmc", OBJECT(&s->fmc), NULL); qdev_set_parent_bus(DEVICE(&s->fmc), sysbus_get_default()); @@ -186,16 +188,23 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) } /* SRAM */ - memory_region_init_ram_nomigrate(&s->sram, OBJECT(dev), "aspeed.sram", + memory_region_init_ram(&s->sram, OBJECT(dev), "aspeed.sram", sc->info->sram_size, &err); if (err) { error_propagate(errp, err); return; } - vmstate_register_ram_global(&s->sram); memory_region_add_subregion(get_system_memory(), ASPEED_SOC_SRAM_BASE, &s->sram); + /* SCU */ + object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, ASPEED_SOC_SCU_BASE); + /* VIC */ object_property_set_bool(OBJECT(&s->vic), true, "realized", &err); if (err) { @@ -220,20 +229,12 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq); } - /* SCU */ - object_property_set_bool(OBJECT(&s->scu), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, ASPEED_SOC_SCU_BASE); - /* UART - attach an 8250 to the IO space as our UART5 */ - if (serial_hds[0]) { + if (serial_hd(0)) { qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]); serial_mm_init(get_system_memory(), ASPEED_SOC_IOMEM_BASE + ASPEED_SOC_UART_5_BASE, 2, - uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN); + uart5, 38400, serial_hd(0), DEVICE_LITTLE_ENDIAN); } /* I2C */ diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index 13b63970d7..6be7660e8c 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -166,7 +166,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) sysbus_pass_irq(SYS_BUS_DEVICE(s), SYS_BUS_DEVICE(&s->ic)); /* UART0 */ - qdev_prop_set_chr(DEVICE(s->uart0), "chardev", serial_hds[0]); + qdev_prop_set_chr(DEVICE(s->uart0), "chardev", serial_hd(0)); object_property_set_bool(OBJECT(s->uart0), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -179,7 +179,7 @@ static void bcm2835_peripherals_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in_named(DEVICE(&s->ic), BCM2835_IC_GPU_IRQ, INTERRUPT_UART)); /* AUX / UART1 */ - qdev_prop_set_chr(DEVICE(&s->aux), "chardev", serial_hds[1]); + qdev_prop_set_chr(DEVICE(&s->aux), "chardev", serial_hd(1)); object_property_set_bool(OBJECT(&s->aux), true, "realized", &err); if (err) { diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 3c4b44a53e..6a09c339d3 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -15,7 +15,6 @@ #include "hw/arm/bcm2836.h" #include "hw/arm/raspi_platform.h" #include "hw/sysbus.h" -#include "exec/address-spaces.h" /* Peripheral base address seen by the CPU */ #define BCM2836_PERI_BASE 0x3F000000 @@ -52,25 +51,19 @@ static void bcm2836_init(Object *obj) int n; for (n = 0; n < BCM283X_NCPUS; n++) { - object_initialize(&s->cpus[n], sizeof(s->cpus[n]), - info->cpu_type); - object_property_add_child(obj, "cpu[*]", OBJECT(&s->cpus[n]), - &error_abort); + object_initialize_child(obj, "cpu[*]", &s->cpus[n], sizeof(s->cpus[n]), + info->cpu_type, &error_abort, NULL); } - object_initialize(&s->control, sizeof(s->control), TYPE_BCM2836_CONTROL); - object_property_add_child(obj, "control", OBJECT(&s->control), NULL); - qdev_set_parent_bus(DEVICE(&s->control), sysbus_get_default()); + sysbus_init_child_obj(obj, "control", &s->control, sizeof(s->control), + TYPE_BCM2836_CONTROL); - object_initialize(&s->peripherals, sizeof(s->peripherals), - TYPE_BCM2835_PERIPHERALS); - object_property_add_child(obj, "peripherals", OBJECT(&s->peripherals), - &error_abort); + sysbus_init_child_obj(obj, "peripherals", &s->peripherals, + sizeof(s->peripherals), TYPE_BCM2835_PERIPHERALS); object_property_add_alias(obj, "board-rev", OBJECT(&s->peripherals), "board-rev", &error_abort); object_property_add_alias(obj, "vcram-size", OBJECT(&s->peripherals), "vcram-size", &error_abort); - qdev_set_parent_bus(DEVICE(&s->peripherals), sysbus_get_default()); } static void bcm2836_realize(DeviceState *dev, Error **errp) @@ -186,6 +179,8 @@ static void bcm283x_class_init(ObjectClass *oc, void *data) bc->info = data; dc->realize = bcm2836_realize; dc->props = bcm2836_props; + /* Reason: Must be wired up in code (see raspi_init() function) */ + dc->user_creatable = false; } static const TypeInfo bcm283x_type_info = { diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 26184bcd7c..e09201cc97 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -36,8 +36,8 @@ #define ARM64_TEXT_OFFSET_OFFSET 8 #define ARM64_MAGIC_OFFSET 56 -static AddressSpace *arm_boot_address_space(ARMCPU *cpu, - const struct arm_boot_info *info) +AddressSpace *arm_boot_address_space(ARMCPU *cpu, + const struct arm_boot_info *info) { /* Return the address space to use for bootloader reads and writes. * We prefer the secure address space if the CPU has it and we're @@ -486,36 +486,17 @@ static void fdt_add_psci_node(void *fdt) qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn); } -/** - * load_dtb() - load a device tree binary image into memory - * @addr: the address to load the image at - * @binfo: struct describing the boot environment - * @addr_limit: upper limit of the available memory area at @addr - * @as: address space to load image to - * - * Load a device tree supplied by the machine or by the user with the - * '-dtb' command line option, and put it at offset @addr in target - * memory. - * - * If @addr_limit contains a meaningful value (i.e., it is strictly greater - * than @addr), the device tree is only loaded if its size does not exceed - * the limit. - * - * Returns: the size of the device tree image on success, - * 0 if the image size exceeds the limit, - * -1 on errors. - * - * Note: Must not be called unless have_dtb(binfo) is true. - */ -static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, - hwaddr addr_limit, AddressSpace *as) +int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, + hwaddr addr_limit, AddressSpace *as) { void *fdt = NULL; - int size, rc; + int size, rc, n = 0; uint32_t acells, scells; char *nodename; unsigned int i; hwaddr mem_base, mem_len; + char **node_path; + Error *err = NULL; if (binfo->dtb_filename) { char *filename; @@ -567,12 +548,21 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, goto fail; } + /* nop all root nodes matching /memory or /memory@unit-address */ + node_path = qemu_fdt_node_unit_path(fdt, "memory", &err); + if (err) { + error_report_err(err); + goto fail; + } + while (node_path[n]) { + if (g_str_has_prefix(node_path[n], "/memory")) { + qemu_fdt_nop_node(fdt, node_path[n]); + } + n++; + } + g_strfreev(node_path); + if (nb_numa_nodes > 0) { - /* - * Turn the /memory node created before into a NOP node, then create - * /memory@addr nodes for all numa nodes respectively. - */ - qemu_fdt_nop_node(fdt, "/memory"); mem_base = binfo->loader_start; for (i = 0; i < nb_numa_nodes; i++) { mem_len = numa_info[i].node_mem; @@ -593,24 +583,18 @@ static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo, g_free(nodename); } } else { - Error *err = NULL; + nodename = g_strdup_printf("/memory@%" PRIx64, binfo->loader_start); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); - rc = fdt_path_offset(fdt, "/memory"); - if (rc < 0) { - qemu_fdt_add_subnode(fdt, "/memory"); - } - - if (!qemu_fdt_getprop(fdt, "/memory", "device_type", NULL, &err)) { - qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); - } - - rc = qemu_fdt_setprop_sized_cells(fdt, "/memory", "reg", + rc = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", acells, binfo->loader_start, scells, binfo->ram_size); if (rc < 0) { - fprintf(stderr, "couldn't set /memory/reg\n"); + fprintf(stderr, "couldn't set %s reg\n", nodename); goto fail; } + g_free(nodename); } rc = fdt_path_offset(fdt, "/chosen"); @@ -935,7 +919,7 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, return size; } -static void arm_load_kernel_notify(Notifier *notifier, void *data) +void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) { CPUState *cs; int kernel_size; @@ -945,13 +929,17 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) int elf_machine; hwaddr entry; static const ARMInsnFixup *primary_loader; - ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier, - notifier, notifier); - ARMCPU *cpu = n->cpu; - struct arm_boot_info *info = - container_of(n, struct arm_boot_info, load_kernel_notifier); AddressSpace *as = arm_boot_address_space(cpu, info); + /* CPU objects (unlike devices) are not automatically reset on system + * reset, so we must always register a handler to do so. If we're + * actually loading a kernel, the handler is also responsible for + * arranging that we start it correctly. + */ + for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { + qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); + } + /* The board code is not supposed to set secure_board_setup unless * running its code in secure mode is actually possible, and KVM * doesn't support secure. @@ -959,6 +947,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) assert(!(info->secure_board_setup && kvm_enabled())); info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); + info->dtb_limit = 0; /* Load the kernel. */ if (!info->kernel_filename || info->firmware_loaded) { @@ -968,9 +957,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) * the kernel is supposed to be loaded by the bootloader), copy the * DTB to the base of RAM for the bootloader to pick up. */ - if (load_dtb(info->loader_start, info, 0, as) < 0) { - exit(1); - } + info->dtb_start = info->loader_start; } if (info->kernel_filename) { @@ -1050,15 +1037,14 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) */ if (elf_low_addr > info->loader_start || elf_high_addr < info->loader_start) { - /* Pass elf_low_addr as address limit to load_dtb if it may be + /* Set elf_low_addr as address limit for arm_load_dtb if it may be * pointing into RAM, otherwise pass '0' (no limit) */ if (elf_low_addr < info->loader_start) { elf_low_addr = 0; } - if (load_dtb(info->loader_start, info, elf_low_addr, as) < 0) { - exit(1); - } + info->dtb_start = info->loader_start; + info->dtb_limit = elf_low_addr; } } entry = elf_entry; @@ -1116,7 +1102,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) */ if (have_dtb(info)) { hwaddr align; - hwaddr dtb_start; if (elf_machine == EM_AARCH64) { /* @@ -1136,11 +1121,9 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) } /* Place the DTB after the initrd in memory with alignment. */ - dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, align); - if (load_dtb(dtb_start, info, 0, as) < 0) { - exit(1); - } - fixupcontext[FIXUP_ARGPTR] = dtb_start; + info->dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, + align); + fixupcontext[FIXUP_ARGPTR] = info->dtb_start; } else { fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR; if (info->ram_size >= (1ULL << 32)) { @@ -1170,26 +1153,14 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data) } info->is_linux = is_linux; - for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) { + for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { ARM_CPU(cs)->env.boot_info = info; } -} -void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) -{ - CPUState *cs; - - info->load_kernel_notifier.cpu = cpu; - info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify; - qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier); - - /* CPU objects (unlike devices) are not automatically reset on system - * reset, so we must always register a handler to do so. If we're - * actually loading a kernel, the handler is also responsible for - * arranging that we start it correctly. - */ - for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) { - qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); + if (!info->skip_dtb_autoload && have_dtb(info)) { + if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as) < 0) { + exit(1); + } } } diff --git a/hw/arm/collie.c b/hw/arm/collie.c index f8c566e2e5..48b732c176 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -16,7 +16,6 @@ #include "strongarm.h" #include "hw/arm/arm.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "cpu.h" diff --git a/hw/arm/digic.c b/hw/arm/digic.c index 6184020985..726abb9b48 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -85,7 +85,7 @@ static void digic_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(sbd, 0, DIGIC4_TIMER_BASE(i)); } - qdev_prop_set_chr(DEVICE(&s->uart), "chardev", serial_hds[0]); + qdev_prop_set_chr(DEVICE(&s->uart), "chardev", serial_hd(0)); object_property_set_bool(OBJECT(&s->uart), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 06f9d1ffa4..b7463a71ec 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -156,12 +156,8 @@ void exynos4210_write_secondary(ARMCPU *cpu, static uint64_t exynos4210_calc_affinity(int cpu) { - uint64_t mp_affinity; - /* Exynos4210 has 0x9 as cluster ID */ - mp_affinity = (0x9 << ARM_AFF1_SHIFT) | cpu; - - return mp_affinity; + return (0x9 << ARM_AFF1_SHIFT) | cpu; } Exynos4210State *exynos4210_init(MemoryRegion *system_mem) diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index cb988a6c25..bd07040a4a 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -39,38 +39,36 @@ static void fsl_imx25_init(Object *obj) object_initialize(&s->cpu, sizeof(s->cpu), "arm926-" TYPE_ARM_CPU); - object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); - qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); + sysbus_init_child_obj(obj, "avic", &s->avic, sizeof(s->avic), + TYPE_IMX_AVIC); - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM); - qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), TYPE_IMX25_CCM); for (i = 0; i < FSL_IMX25_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "uart[*]", &s->uart[i], sizeof(s->uart[i]), + TYPE_IMX_SERIAL); } for (i = 0; i < FSL_IMX25_NUM_GPTS; i++) { - object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX25_GPT); - qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gpt[*]", &s->gpt[i], sizeof(s->gpt[i]), + TYPE_IMX25_GPT); } for (i = 0; i < FSL_IMX25_NUM_EPITS; i++) { - object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); - qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "epit[*]", &s->epit[i], sizeof(s->epit[i]), + TYPE_IMX_EPIT); } - object_initialize(&s->fec, sizeof(s->fec), TYPE_IMX_FEC); - qdev_set_parent_bus(DEVICE(&s->fec), sysbus_get_default()); + sysbus_init_child_obj(obj, "fec", &s->fec, sizeof(s->fec), TYPE_IMX_FEC); for (i = 0; i < FSL_IMX25_NUM_I2CS; i++) { - object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); - qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "i2c[*]", &s->i2c[i], sizeof(s->i2c[i]), + TYPE_IMX_I2C); } for (i = 0; i < FSL_IMX25_NUM_GPIOS; i++) { - object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); - qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gpio[*]", &s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); } } @@ -117,19 +115,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) { FSL_IMX25_UART5_ADDR, FSL_IMX25_UART5_IRQ } }; - if (i < MAX_SERIAL_PORTS) { - Chardev *chr; - - chr = serial_hds[i]; - - if (!chr) { - char label[20]; - snprintf(label, sizeof(label), "imx31.uart%d", i); - chr = qemu_chr_new(label, "null"); - } - - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); - } + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 3eee83d547..ec8239a967 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -36,33 +36,31 @@ static void fsl_imx31_init(Object *obj) object_initialize(&s->cpu, sizeof(s->cpu), "arm1136-" TYPE_ARM_CPU); - object_initialize(&s->avic, sizeof(s->avic), TYPE_IMX_AVIC); - qdev_set_parent_bus(DEVICE(&s->avic), sysbus_get_default()); + sysbus_init_child_obj(obj, "avic", &s->avic, sizeof(s->avic), + TYPE_IMX_AVIC); - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM); - qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), TYPE_IMX31_CCM); for (i = 0; i < FSL_IMX31_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "uart[*]", &s->uart[i], sizeof(s->uart[i]), + TYPE_IMX_SERIAL); } - object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX31_GPT); - qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); + sysbus_init_child_obj(obj, "gpt", &s->gpt, sizeof(s->gpt), TYPE_IMX31_GPT); for (i = 0; i < FSL_IMX31_NUM_EPITS; i++) { - object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); - qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "epit[*]", &s->epit[i], sizeof(s->epit[i]), + TYPE_IMX_EPIT); } for (i = 0; i < FSL_IMX31_NUM_I2CS; i++) { - object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); - qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "i2c[*]", &s->i2c[i], sizeof(s->i2c[i]), + TYPE_IMX_I2C); } for (i = 0; i < FSL_IMX31_NUM_GPIOS; i++) { - object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); - qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gpio[*]", &s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); } } @@ -106,19 +104,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) { FSL_IMX31_UART2_ADDR, FSL_IMX31_UART2_IRQ }, }; - if (i < MAX_SERIAL_PORTS) { - Chardev *chr; - - chr = serial_hds[i]; - - if (!chr) { - char label[20]; - snprintf(label, sizeof(label), "imx31.uart%d", i); - chr = qemu_chr_new(label, "null"); - } - - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); - } + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index 9dfbc9a8c4..7b7b97f74c 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -38,73 +38,57 @@ static void fsl_imx6_init(Object *obj) int i; for (i = 0; i < MIN(smp_cpus, FSL_IMX6_NUM_CPUS); i++) { - object_initialize(&s->cpu[i], sizeof(s->cpu[i]), - "cortex-a9-" TYPE_ARM_CPU); snprintf(name, NAME_SIZE, "cpu%d", i); - object_property_add_child(obj, name, OBJECT(&s->cpu[i]), NULL); + object_initialize_child(obj, name, &s->cpu[i], sizeof(s->cpu[i]), + "cortex-a9-" TYPE_ARM_CPU, &error_abort, NULL); } - object_initialize(&s->a9mpcore, sizeof(s->a9mpcore), TYPE_A9MPCORE_PRIV); - qdev_set_parent_bus(DEVICE(&s->a9mpcore), sysbus_get_default()); - object_property_add_child(obj, "a9mpcore", OBJECT(&s->a9mpcore), NULL); + sysbus_init_child_obj(obj, "a9mpcore", &s->a9mpcore, sizeof(s->a9mpcore), + TYPE_A9MPCORE_PRIV); - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX6_CCM); - qdev_set_parent_bus(DEVICE(&s->ccm), sysbus_get_default()); - object_property_add_child(obj, "ccm", OBJECT(&s->ccm), NULL); + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), TYPE_IMX6_CCM); - object_initialize(&s->src, sizeof(s->src), TYPE_IMX6_SRC); - qdev_set_parent_bus(DEVICE(&s->src), sysbus_get_default()); - object_property_add_child(obj, "src", OBJECT(&s->src), NULL); + sysbus_init_child_obj(obj, "src", &s->src, sizeof(s->src), TYPE_IMX6_SRC); for (i = 0; i < FSL_IMX6_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "uart%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->uart[i]), NULL); + sysbus_init_child_obj(obj, name, &s->uart[i], sizeof(s->uart[i]), + TYPE_IMX_SERIAL); } - object_initialize(&s->gpt, sizeof(s->gpt), TYPE_IMX6_GPT); - qdev_set_parent_bus(DEVICE(&s->gpt), sysbus_get_default()); - object_property_add_child(obj, "gpt", OBJECT(&s->gpt), NULL); + sysbus_init_child_obj(obj, "gpt", &s->gpt, sizeof(s->gpt), TYPE_IMX6_GPT); for (i = 0; i < FSL_IMX6_NUM_EPITS; i++) { - object_initialize(&s->epit[i], sizeof(s->epit[i]), TYPE_IMX_EPIT); - qdev_set_parent_bus(DEVICE(&s->epit[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "epit%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->epit[i]), NULL); + sysbus_init_child_obj(obj, name, &s->epit[i], sizeof(s->epit[i]), + TYPE_IMX_EPIT); } for (i = 0; i < FSL_IMX6_NUM_I2CS; i++) { - object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); - qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "i2c%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL); + sysbus_init_child_obj(obj, name, &s->i2c[i], sizeof(s->i2c[i]), + TYPE_IMX_I2C); } for (i = 0; i < FSL_IMX6_NUM_GPIOS; i++) { - object_initialize(&s->gpio[i], sizeof(s->gpio[i]), TYPE_IMX_GPIO); - qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "gpio%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->gpio[i]), NULL); + sysbus_init_child_obj(obj, name, &s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); } for (i = 0; i < FSL_IMX6_NUM_ESDHCS; i++) { - object_initialize(&s->esdhc[i], sizeof(s->esdhc[i]), TYPE_IMX_USDHC); - qdev_set_parent_bus(DEVICE(&s->esdhc[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "sdhc%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->esdhc[i]), NULL); + sysbus_init_child_obj(obj, name, &s->esdhc[i], sizeof(s->esdhc[i]), + TYPE_IMX_USDHC); } for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "spi%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL); + sysbus_init_child_obj(obj, name, &s->spi[i], sizeof(s->spi[i]), + TYPE_IMX_SPI); } - object_initialize(&s->eth, sizeof(s->eth), TYPE_IMX_ENET); - qdev_set_parent_bus(DEVICE(&s->eth), sysbus_get_default()); - object_property_add_child(obj, "eth", OBJECT(&s->eth), NULL); + sysbus_init_child_obj(obj, "eth", &s->eth, sizeof(s->eth), TYPE_IMX_ENET); } static void fsl_imx6_realize(DeviceState *dev, Error **errp) @@ -188,20 +172,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) { FSL_IMX6_UART5_ADDR, FSL_IMX6_UART5_IRQ }, }; - if (i < MAX_SERIAL_PORTS) { - Chardev *chr; - - chr = serial_hds[i]; - - if (!chr) { - char *label = g_strdup_printf("imx6.uart%d", i + 1); - chr = qemu_chr_new(label, "null"); - g_free(label); - serial_hds[i] = chr; - } - - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", chr); - } + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { @@ -449,7 +420,7 @@ static void fsl_imx6_class_init(ObjectClass *oc, void *data) dc->realize = fsl_imx6_realize; dc->desc = "i.MX6 SOC"; - /* Reason: Uses serial_hds[] in the realize() function */ + /* Reason: Uses serial_hd() in the realize() function */ dc->user_creatable = false; } diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 390b4310e6..d5e26855a5 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -30,157 +30,126 @@ static void fsl_imx7_init(Object *obj) { - BusState *sysbus = sysbus_get_default(); FslIMX7State *s = FSL_IMX7(obj); char name[NAME_SIZE]; int i; for (i = 0; i < MIN(smp_cpus, FSL_IMX7_NUM_CPUS); i++) { - object_initialize(&s->cpu[i], sizeof(s->cpu[i]), - ARM_CPU_TYPE_NAME("cortex-a7")); snprintf(name, NAME_SIZE, "cpu%d", i); - object_property_add_child(obj, name, OBJECT(&s->cpu[i]), - &error_fatal); + object_initialize_child(obj, name, &s->cpu[i], sizeof(s->cpu[i]), + ARM_CPU_TYPE_NAME("cortex-a7"), &error_abort, + NULL); } /* * A7MPCORE */ - object_initialize(&s->a7mpcore, sizeof(s->a7mpcore), TYPE_A15MPCORE_PRIV); - qdev_set_parent_bus(DEVICE(&s->a7mpcore), sysbus); - object_property_add_child(obj, "a7mpcore", - OBJECT(&s->a7mpcore), &error_fatal); + sysbus_init_child_obj(obj, "a7mpcore", &s->a7mpcore, sizeof(s->a7mpcore), + TYPE_A15MPCORE_PRIV); /* * GPIOs 1 to 7 */ for (i = 0; i < FSL_IMX7_NUM_GPIOS; i++) { - object_initialize(&s->gpio[i], sizeof(s->gpio[i]), - TYPE_IMX_GPIO); - qdev_set_parent_bus(DEVICE(&s->gpio[i]), sysbus); snprintf(name, NAME_SIZE, "gpio%d", i); - object_property_add_child(obj, name, - OBJECT(&s->gpio[i]), &error_fatal); + sysbus_init_child_obj(obj, name, &s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); } /* * GPT1, 2, 3, 4 */ for (i = 0; i < FSL_IMX7_NUM_GPTS; i++) { - object_initialize(&s->gpt[i], sizeof(s->gpt[i]), TYPE_IMX7_GPT); - qdev_set_parent_bus(DEVICE(&s->gpt[i]), sysbus); snprintf(name, NAME_SIZE, "gpt%d", i); - object_property_add_child(obj, name, OBJECT(&s->gpt[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->gpt[i], sizeof(s->gpt[i]), + TYPE_IMX7_GPT); } /* * CCM */ - object_initialize(&s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM); - qdev_set_parent_bus(DEVICE(&s->ccm), sysbus); - object_property_add_child(obj, "ccm", OBJECT(&s->ccm), &error_fatal); + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), TYPE_IMX7_CCM); /* * Analog */ - object_initialize(&s->analog, sizeof(s->analog), TYPE_IMX7_ANALOG); - qdev_set_parent_bus(DEVICE(&s->analog), sysbus); - object_property_add_child(obj, "analog", OBJECT(&s->analog), &error_fatal); + sysbus_init_child_obj(obj, "analog", &s->analog, sizeof(s->analog), + TYPE_IMX7_ANALOG); /* * GPCv2 */ - object_initialize(&s->gpcv2, sizeof(s->gpcv2), TYPE_IMX_GPCV2); - qdev_set_parent_bus(DEVICE(&s->gpcv2), sysbus); - object_property_add_child(obj, "gpcv2", OBJECT(&s->gpcv2), &error_fatal); + sysbus_init_child_obj(obj, "gpcv2", &s->gpcv2, sizeof(s->gpcv2), + TYPE_IMX_GPCV2); for (i = 0; i < FSL_IMX7_NUM_ECSPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), TYPE_IMX_SPI); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "spi%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->spi[i]), NULL); + sysbus_init_child_obj(obj, name, &s->spi[i], sizeof(s->spi[i]), + TYPE_IMX_SPI); } for (i = 0; i < FSL_IMX7_NUM_I2CS; i++) { - object_initialize(&s->i2c[i], sizeof(s->i2c[i]), TYPE_IMX_I2C); - qdev_set_parent_bus(DEVICE(&s->i2c[i]), sysbus_get_default()); snprintf(name, NAME_SIZE, "i2c%d", i + 1); - object_property_add_child(obj, name, OBJECT(&s->i2c[i]), NULL); + sysbus_init_child_obj(obj, name, &s->i2c[i], sizeof(s->i2c[i]), + TYPE_IMX_I2C); } /* * UART */ for (i = 0; i < FSL_IMX7_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_IMX_SERIAL); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus); snprintf(name, NAME_SIZE, "uart%d", i); - object_property_add_child(obj, name, OBJECT(&s->uart[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->uart[i], sizeof(s->uart[i]), + TYPE_IMX_SERIAL); } /* * Ethernet */ for (i = 0; i < FSL_IMX7_NUM_ETHS; i++) { - object_initialize(&s->eth[i], sizeof(s->eth[i]), TYPE_IMX_ENET); - qdev_set_parent_bus(DEVICE(&s->eth[i]), sysbus); snprintf(name, NAME_SIZE, "eth%d", i); - object_property_add_child(obj, name, OBJECT(&s->eth[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->eth[i], sizeof(s->eth[i]), + TYPE_IMX_ENET); } /* * SDHCI */ for (i = 0; i < FSL_IMX7_NUM_USDHCS; i++) { - object_initialize(&s->usdhc[i], sizeof(s->usdhc[i]), - TYPE_IMX_USDHC); - qdev_set_parent_bus(DEVICE(&s->usdhc[i]), sysbus); snprintf(name, NAME_SIZE, "usdhc%d", i); - object_property_add_child(obj, name, OBJECT(&s->usdhc[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->usdhc[i], sizeof(s->usdhc[i]), + TYPE_IMX_USDHC); } /* * SNVS */ - object_initialize(&s->snvs, sizeof(s->snvs), TYPE_IMX7_SNVS); - qdev_set_parent_bus(DEVICE(&s->snvs), sysbus); - object_property_add_child(obj, "snvs", OBJECT(&s->snvs), &error_fatal); + sysbus_init_child_obj(obj, "snvs", &s->snvs, sizeof(s->snvs), + TYPE_IMX7_SNVS); /* * Watchdog */ for (i = 0; i < FSL_IMX7_NUM_WDTS; i++) { - object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_IMX2_WDT); - qdev_set_parent_bus(DEVICE(&s->wdt[i]), sysbus); snprintf(name, NAME_SIZE, "wdt%d", i); - object_property_add_child(obj, name, OBJECT(&s->wdt[i]), - &error_fatal); + sysbus_init_child_obj(obj, name, &s->wdt[i], sizeof(s->wdt[i]), + TYPE_IMX2_WDT); } /* * GPR */ - object_initialize(&s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR); - qdev_set_parent_bus(DEVICE(&s->gpr), sysbus); - object_property_add_child(obj, "gpr", OBJECT(&s->gpr), &error_fatal); + sysbus_init_child_obj(obj, "gpr", &s->gpr, sizeof(s->gpr), TYPE_IMX7_GPR); - object_initialize(&s->pcie, sizeof(s->pcie), TYPE_DESIGNWARE_PCIE_HOST); - qdev_set_parent_bus(DEVICE(&s->pcie), sysbus); - object_property_add_child(obj, "pcie", OBJECT(&s->pcie), &error_fatal); + sysbus_init_child_obj(obj, "pcie", &s->pcie, sizeof(s->pcie), + TYPE_DESIGNWARE_PCIE_HOST); for (i = 0; i < FSL_IMX7_NUM_USBS; i++) { - object_initialize(&s->usb[i], - sizeof(s->usb[i]), TYPE_CHIPIDEA); - qdev_set_parent_bus(DEVICE(&s->usb[i]), sysbus); snprintf(name, NAME_SIZE, "usb%d", i); - object_property_add_child(obj, name, - OBJECT(&s->usb[i]), &error_fatal); + sysbus_init_child_obj(obj, name, &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); } } @@ -324,7 +293,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_ECSPI4_ADDR, }; - static const hwaddr FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = { + static const int FSL_IMX7_SPIn_IRQ[FSL_IMX7_NUM_ECSPIS] = { FSL_IMX7_ECSPI1_IRQ, FSL_IMX7_ECSPI2_IRQ, FSL_IMX7_ECSPI3_IRQ, @@ -349,7 +318,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_I2C4_ADDR, }; - static const hwaddr FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = { + static const int FSL_IMX7_I2Cn_IRQ[FSL_IMX7_NUM_I2CS] = { FSL_IMX7_I2C1_IRQ, FSL_IMX7_I2C2_IRQ, FSL_IMX7_I2C3_IRQ, @@ -390,9 +359,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) }; - if (i < MAX_SERIAL_PORTS) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); - } + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &error_abort); @@ -461,7 +428,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) /* * SRC */ - create_unimplemented_device("sdma", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE); + create_unimplemented_device("src", FSL_IMX7_SRC_ADDR, FSL_IMX7_SRC_SIZE); /* * Watchdog @@ -517,7 +484,7 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_USB3_ADDR, }; - static const hwaddr FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = { + static const int FSL_IMX7_USBn_IRQ[FSL_IMX7_NUM_USBS] = { FSL_IMX7_USB1_IRQ, FSL_IMX7_USB2_IRQ, FSL_IMX7_USB3_IRQ, diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c index ea2a3c532d..56cb763c4e 100644 --- a/hw/arm/gumstix.c +++ b/hw/arm/gumstix.c @@ -42,7 +42,6 @@ #include "hw/block/flash.h" #include "hw/devices.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" #include "cpu.h" diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 1742cf6f6c..6d42fce2c3 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -291,7 +291,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) memory_region_add_subregion(sysmem, 0, dram); sysram = g_new(MemoryRegion, 1); - memory_region_init_ram_nomigrate(sysram, NULL, "highbank.sysram", 0x8000, + memory_region_init_ram(sysram, NULL, "highbank.sysram", 0x8000, &error_fatal); memory_region_add_subregion(sysmem, 0xfff88000, sysram); if (bios_name != NULL) { @@ -342,7 +342,7 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0xfff34000); sysbus_connect_irq(busdev, 0, pic[18]); - pl011_create(0xfff36000, pic[20], serial_hds[0]); + pl011_create(0xfff36000, pic[20], serial_hd(0)); dev = qdev_create(NULL, TYPE_HIGHBANK_REGISTERS); qdev_init_nofail(dev); diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 58b40efc19..4eceebb9ea 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -631,8 +631,8 @@ static void integratorcp_init(MachineState *machine) sysbus_create_varargs("integrator_pit", 0x13000000, pic[5], pic[6], pic[7], NULL); sysbus_create_simple("pl031", 0x15000000, pic[8]); - pl011_create(0x16000000, pic[1], serial_hds[0]); - pl011_create(0x17000000, pic[2], serial_hds[1]); + pl011_create(0x16000000, pic[1], serial_hd(0)); + pl011_create(0x17000000, pic[2], serial_hd(1)); icp = sysbus_create_simple(TYPE_ICP_CONTROL_REGS, 0xcb000000, qdev_get_gpio_in(sic, 3)); sysbus_create_simple("pl050_keyboard", 0x18000000, pic[3]); diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c index c5f0a5b98a..8cadc8b160 100644 --- a/hw/arm/iotkit.c +++ b/hw/arm/iotkit.c @@ -30,15 +30,6 @@ static void make_alias(IoTKit *s, MemoryRegion *mr, const char *name, memory_region_add_subregion_overlap(&s->container, base, mr, -1500); } -static void init_sysbus_child(Object *parent, const char *childname, - void *child, size_t childsize, - const char *childtype) -{ - object_initialize(child, childsize, childtype); - object_property_add_child(parent, childname, OBJECT(child), &error_abort); - qdev_set_parent_bus(DEVICE(child), sysbus_get_default()); -} - static void irq_status_forwarder(void *opaque, int n, int level) { qemu_irq destirq = opaque; @@ -119,40 +110,52 @@ static void iotkit_init(Object *obj) memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX); - init_sysbus_child(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), - TYPE_ARMV7M); + sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), + TYPE_ARMV7M); qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", ARM_CPU_TYPE_NAME("cortex-m33")); - init_sysbus_child(obj, "secctl", &s->secctl, sizeof(s->secctl), - TYPE_IOTKIT_SECCTL); - init_sysbus_child(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), - TYPE_TZ_PPC); - init_sysbus_child(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), - TYPE_TZ_PPC); - init_sysbus_child(obj, "timer0", &s->timer0, sizeof(s->timer0), - TYPE_CMSDK_APB_TIMER); - init_sysbus_child(obj, "timer1", &s->timer1, sizeof(s->timer1), - TYPE_CMSDK_APB_TIMER); - init_sysbus_child(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), - TYPE_UNIMPLEMENTED_DEVICE); - object_initialize(&s->ppc_irq_orgate, sizeof(s->ppc_irq_orgate), - TYPE_OR_IRQ); - object_property_add_child(obj, "ppc-irq-orgate", - OBJECT(&s->ppc_irq_orgate), &error_abort); - object_initialize(&s->sec_resp_splitter, sizeof(s->sec_resp_splitter), - TYPE_SPLIT_IRQ); - object_property_add_child(obj, "sec-resp-splitter", - OBJECT(&s->sec_resp_splitter), &error_abort); + sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), + TYPE_IOTKIT_SECCTL); + sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), + TYPE_TZ_PPC); + sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), + TYPE_TZ_PPC); + sysbus_init_child_obj(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); + object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate, + sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + + for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) { + char *name = g_strdup_printf("mpc-irq-splitter-%d", i); + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); + } + sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, + sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter, + sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ, + &error_abort, NULL); for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { char *name = g_strdup_printf("ppc-irq-splitter-%d", i); SplitIRQ *splitter = &s->ppc_irq_splitter[i]; - object_initialize(splitter, sizeof(*splitter), TYPE_SPLIT_IRQ); - object_property_add_child(obj, name, OBJECT(splitter), &error_abort); + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); } - init_sysbus_child(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), - TYPE_UNIMPLEMENTED_DEVICE); + sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), + TYPE_UNIMPLEMENTED_DEVICE); } static void iotkit_exp_irq(void *opaque, int n, int level) @@ -162,6 +165,12 @@ static void iotkit_exp_irq(void *opaque, int n, int level) qemu_set_irq(s->exp_irqs[n], level); } +static void iotkit_mpcexp_status(void *opaque, int n, int level) +{ + IoTKit *s = IOTKIT(opaque); + qemu_set_irq(s->mpcexp_status_in[n], level); +} + static void iotkit_realize(DeviceState *dev, Error **errp) { IoTKit *s = IOTKIT(dev); @@ -266,15 +275,6 @@ static void iotkit_realize(DeviceState *dev, Error **errp) */ make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); - /* This RAM should be behind a Memory Protection Controller, but we - * don't implement that yet. - */ - memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); - if (err) { - error_propagate(errp, err); - return; - } - memory_region_add_subregion(&s->container, 0x20000000, &s->sram0); /* Security controller */ object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); @@ -310,6 +310,48 @@ static void iotkit_realize(DeviceState *dev, Error **errp) qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, qdev_get_gpio_in(dev_splitter, 0)); + /* This RAM lives behind the Memory Protection Controller */ + memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_link(OBJECT(&s->mpc), OBJECT(&s->sram0), + "downstream", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* Map the upstream end of the MPC into the right place... */ + memory_region_add_subregion(&s->container, 0x20000000, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), + 1)); + /* ...and its register interface */ + memory_region_add_subregion(&s->container, 0x50083000, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), + 0)); + + /* We must OR together lines from the MPC splitters to go to the NVIC */ + object_property_set_int(OBJECT(&s->mpc_irq_orgate), + IOTS_NUM_EXP_MPC + IOTS_NUM_MPC, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, + qdev_get_gpio_in(DEVICE(&s->armv7m), 9)); + /* Devices behind APB PPC0: * 0x40000000: timer0 * 0x40001000: timer1 @@ -340,7 +382,7 @@ static void iotkit_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); + qdev_get_gpio_in(DEVICE(&s->armv7m), 4)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); if (err) { @@ -473,8 +515,6 @@ static void iotkit_realize(DeviceState *dev, Error **errp) create_unimplemented_device("NS watchdog", 0x40081000, 0x1000); create_unimplemented_device("S watchdog", 0x50081000, 0x1000); - create_unimplemented_device("SRAM0 MPC", 0x50083000, 0x1000); - for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); @@ -517,8 +557,49 @@ static void iotkit_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, qdev_get_gpio_in(devs, 0)); + g_free(gpioname); } + /* Wire up the splitters for the MPC IRQs */ + for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) { + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + DeviceState *dev_splitter = DEVICE(splitter); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(splitter), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (i < IOTS_NUM_EXP_MPC) { + /* Splitter input is from GPIO input line */ + s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpcexp_status", i)); + } else { + /* Splitter input is from our own MPC */ + qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0, + qdev_get_gpio_in(dev_splitter, 0)); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpc_status", 0)); + } + + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); + } + /* Create GPIO inputs which will pass the line state for our + * mpcexp_irq inputs to the correct splitter devices. + */ + qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status", + IOTS_NUM_EXP_MPC); + iotkit_forward_sec_resp_cfg(s); system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index f9c2228e31..864c7bd411 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -121,10 +121,10 @@ static void kzm_init(MachineState *machine) qdev_get_gpio_in(DEVICE(&s->soc.avic), 52)); } - if (serial_hds[2]) { /* touchscreen */ + if (serial_hd(2)) { /* touchscreen */ serial_mm_init(get_system_memory(), KZM_FPGA_ADDR+0x10, 0, qdev_get_gpio_in(DEVICE(&s->soc.avic), 52), - 14745600, serial_hds[2], DEVICE_NATIVE_ENDIAN); + 14745600, serial_hd(2), DEVICE_NATIVE_ENDIAN); } kzm_binfo.ram_size = machine->ram_size; diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c index 4215c025fc..0beb5c426b 100644 --- a/hw/arm/mainstone.c +++ b/hw/arm/mainstone.c @@ -21,7 +21,6 @@ #include "hw/devices.h" #include "hw/boards.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" diff --git a/hw/arm/mcimx7d-sabre.c b/hw/arm/mcimx7d-sabre.c index 95fb409d9c..9c5f0e70c3 100644 --- a/hw/arm/mcimx7d-sabre.c +++ b/hw/arm/mcimx7d-sabre.c @@ -18,10 +18,8 @@ #include "hw/arm/fsl-imx7.h" #include "hw/boards.h" #include "sysemu/sysemu.h" -#include "sysemu/device_tree.h" #include "qemu/error-report.h" #include "sysemu/qtest.h" -#include "net/net.h" typedef struct { FslIMX7State soc; diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 8c86cffa9e..22180c56fb 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -44,6 +44,7 @@ #include "hw/timer/cmsdk-apb-timer.h" #include "hw/misc/mps2-scc.h" #include "hw/misc/mps2-fpgaio.h" +#include "hw/misc/tz-mpc.h" #include "hw/arm/iotkit.h" #include "hw/devices.h" #include "net/net.h" @@ -64,22 +65,22 @@ typedef struct { IoTKit iotkit; MemoryRegion psram; - MemoryRegion ssram1; + MemoryRegion ssram[3]; MemoryRegion ssram1_m; - MemoryRegion ssram23; MPS2SCC scc; MPS2FPGAIO fpgaio; TZPPC ppc[5]; - UnimplementedDeviceState ssram_mpc[3]; + TZMPC ssram_mpc[3]; UnimplementedDeviceState spi[5]; UnimplementedDeviceState i2c[4]; UnimplementedDeviceState i2s_audio; - UnimplementedDeviceState gpio[5]; + UnimplementedDeviceState gpio[4]; UnimplementedDeviceState dma[4]; UnimplementedDeviceState gfx; CMSDKAPBUART uart[5]; SplitIRQ sec_resp_splitter; qemu_or_irq uart_irq_orgate; + DeviceState *lan9118; } MPS2TZMachineState; #define TYPE_MPS2TZ_MACHINE "mps2tz" @@ -95,16 +96,6 @@ typedef struct { /* Main SYSCLK frequency in Hz */ #define SYSCLK_FRQ 20000000 -/* Initialize the auxiliary RAM region @mr and map it into - * the memory map at @base. - */ -static void make_ram(MemoryRegion *mr, const char *name, - hwaddr base, hwaddr size) -{ - memory_region_init_ram(mr, NULL, name, size, &error_fatal); - memory_region_add_subregion(get_system_memory(), base, mr); -} - /* Create an alias of an entire original MemoryRegion @orig * located at @base in the memory map. */ @@ -172,7 +163,6 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, { CMSDKAPBUART *uart = opaque; int i = uart - &mms->uart[0]; - Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL; int rxirqno = i * 2; int txirqno = i * 2 + 1; int combirqno = i + 10; @@ -182,7 +172,7 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, init_sysbus_child(OBJECT(mms), name, uart, sizeof(mms->uart[0]), TYPE_CMSDK_APB_UART); - qdev_prop_set_chr(DEVICE(uart), "chardev", uartchr); + qdev_prop_set_chr(DEVICE(uart), "chardev", serial_hd(i)); qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", SYSCLK_FRQ); object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal); s = SYS_BUS_DEVICE(uart); @@ -225,6 +215,64 @@ static MemoryRegion *make_fpgaio(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(SYS_BUS_DEVICE(fpgaio), 0); } +static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + SysBusDevice *s; + DeviceState *iotkitdev = DEVICE(&mms->iotkit); + NICInfo *nd = &nd_table[0]; + + /* In hardware this is a LAN9220; the LAN9118 is software compatible + * except that it doesn't support the checksum-offload feature. + */ + qemu_check_nic_model(nd, "lan9118"); + mms->lan9118 = qdev_create(NULL, "lan9118"); + qdev_set_nic_properties(mms->lan9118, nd); + qdev_init_nofail(mms->lan9118); + + s = SYS_BUS_DEVICE(mms->lan9118); + sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16)); + return sysbus_mmio_get_region(s, 0); +} + +static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + TZMPC *mpc = opaque; + int i = mpc - &mms->ssram_mpc[0]; + MemoryRegion *ssram = &mms->ssram[i]; + MemoryRegion *upstream; + char *mpcname = g_strdup_printf("%s-mpc", name); + static uint32_t ramsize[] = { 0x00400000, 0x00200000, 0x00200000 }; + static uint32_t rambase[] = { 0x00000000, 0x28000000, 0x28200000 }; + + memory_region_init_ram(ssram, NULL, name, ramsize[i], &error_fatal); + + init_sysbus_child(OBJECT(mms), mpcname, mpc, + sizeof(mms->ssram_mpc[0]), TYPE_TZ_MPC); + object_property_set_link(OBJECT(mpc), OBJECT(ssram), + "downstream", &error_fatal); + object_property_set_bool(OBJECT(mpc), true, "realized", &error_fatal); + /* Map the upstream end of the MPC into system memory */ + upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 1); + memory_region_add_subregion(get_system_memory(), rambase[i], upstream); + /* and connect its interrupt to the IoTKit */ + qdev_connect_gpio_out_named(DEVICE(mpc), "irq", 0, + qdev_get_gpio_in_named(DEVICE(&mms->iotkit), + "mpcexp_status", i)); + + /* The first SSRAM is a special case as it has an alias; accesses to + * the alias region at 0x00400000 must also go to the MPC upstream. + */ + if (i == 0) { + make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", upstream, 0x00400000); + } + + g_free(mpcname); + /* Return the register interface MR for our caller to map behind the PPC */ + return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); @@ -286,14 +334,6 @@ static void mps2tz_common_init(MachineState *machine) NULL, "mps.ram", 0x01000000); memory_region_add_subregion(system_memory, 0x80000000, &mms->psram); - /* The SSRAM memories should all be behind Memory Protection Controllers, - * but we don't implement that yet. - */ - make_ram(&mms->ssram1, "mps.ssram1", 0x00000000, 0x00400000); - make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x00400000); - - make_ram(&mms->ssram23, "mps.ssram23", 0x28000000, 0x00400000); - /* The overflow IRQs for all UARTs are ORed together. * Tx, Rx and "combined" IRQs are sent to the NVIC separately. * Create the OR gate for this. @@ -323,12 +363,9 @@ static void mps2tz_common_init(MachineState *machine) const PPCInfo ppcs[] = { { .name = "apb_ppcexp0", .ports = { - { "ssram-mpc0", make_unimp_dev, &mms->ssram_mpc[0], - 0x58007000, 0x1000 }, - { "ssram-mpc1", make_unimp_dev, &mms->ssram_mpc[1], - 0x58008000, 0x1000 }, - { "ssram-mpc2", make_unimp_dev, &mms->ssram_mpc[2], - 0x58009000, 0x1000 }, + { "ssram-0", make_mpc, &mms->ssram_mpc[0], 0x58007000, 0x1000 }, + { "ssram-1", make_mpc, &mms->ssram_mpc[1], 0x58008000, 0x1000 }, + { "ssram-2", make_mpc, &mms->ssram_mpc[2], 0x58009000, 0x1000 }, }, }, { .name = "apb_ppcexp1", @@ -364,7 +401,7 @@ static void mps2tz_common_init(MachineState *machine) { "gpio1", make_unimp_dev, &mms->gpio[1], 0x40101000, 0x1000 }, { "gpio2", make_unimp_dev, &mms->gpio[2], 0x40102000, 0x1000 }, { "gpio3", make_unimp_dev, &mms->gpio[3], 0x40103000, 0x1000 }, - { "gpio4", make_unimp_dev, &mms->gpio[4], 0x40104000, 0x1000 }, + { "eth", make_eth_dev, NULL, 0x42000000, 0x100000 }, }, }, { .name = "ahb_ppcexp1", @@ -448,13 +485,6 @@ static void mps2tz_common_init(MachineState *machine) "cfg_sec_resp", 0)); } - /* In hardware this is a LAN9220; the LAN9118 is software compatible - * except that it doesn't support the checksum-offload feature. - * The ethernet controller is not behind a PPC. - */ - lan9118_init(&nd_table[0], 0x42000000, - qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16)); - create_unimplemented_device("FPGA NS PC", 0x48007000, 0x1000); armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000); diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 694fb36866..c3946da317 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -230,7 +230,6 @@ static void mps2_common_init(MachineState *machine) static const hwaddr uartbase[] = {0x40004000, 0x40005000, 0x40006000, 0x40007000, 0x40009000}; - Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL; /* RX irq number; TX irq is always one greater */ static const int uartirq[] = {0, 2, 4, 18, 20}; qemu_irq txovrint = NULL, rxovrint = NULL; @@ -245,7 +244,7 @@ static void mps2_common_init(MachineState *machine) qdev_get_gpio_in(armv7m, uartirq[i]), txovrint, rxovrint, NULL, - uartchr, SYSCLK_FRQ); + serial_hd(i), SYSCLK_FRQ); } break; } @@ -270,7 +269,6 @@ static void mps2_common_init(MachineState *machine) static const hwaddr uartbase[] = {0x40004000, 0x40005000, 0x4002c000, 0x4002d000, 0x4002e000}; - Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL; Object *txrx_orgate; DeviceState *txrx_orgate_dev; @@ -287,7 +285,7 @@ static void mps2_common_init(MachineState *machine) qdev_get_gpio_in(orgate_dev, i * 2), qdev_get_gpio_in(orgate_dev, i * 2 + 1), NULL, - uartchr, SYSCLK_FRQ); + serial_hd(i), SYSCLK_FRQ); } break; } diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index f68df56b97..dbefade644 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -23,13 +23,13 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "hw/arm/arm.h" #include "exec/address-spaces.h" #include "hw/char/serial.h" #include "hw/boards.h" -#include "qemu/cutils.h" #include "hw/arm/msf2-soc.h" #include "hw/misc/unimp.h" @@ -40,14 +40,14 @@ #define SRAM_BASE_ADDRESS 0x20000000 -#define MSF2_ENVM_MAX_SIZE (512 * K_BYTE) +#define MSF2_ENVM_MAX_SIZE (512 * KiB) /* * eSRAM max size is 80k without SECDED(Single error correction and * dual error detection) feature and 64k with SECDED. * We do not support SECDED now. */ -#define MSF2_ESRAM_MAX_SIZE (80 * K_BYTE) +#define MSF2_ESRAM_MAX_SIZE (80 * KiB) static const uint32_t spi_addr[MSF2_NUM_SPIS] = { 0x40001000 , 0x40011000 }; static const uint32_t uart_addr[MSF2_NUM_UARTS] = { 0x40000000 , 0x40010000 }; @@ -68,19 +68,18 @@ static void m2sxxx_soc_initfn(Object *obj) MSF2State *s = MSF2_SOC(obj); int i; - object_initialize(&s->armv7m, sizeof(s->armv7m), TYPE_ARMV7M); - qdev_set_parent_bus(DEVICE(&s->armv7m), sysbus_get_default()); + sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), + TYPE_ARMV7M); - object_initialize(&s->sysreg, sizeof(s->sysreg), TYPE_MSF2_SYSREG); - qdev_set_parent_bus(DEVICE(&s->sysreg), sysbus_get_default()); + sysbus_init_child_obj(obj, "sysreg", &s->sysreg, sizeof(s->sysreg), + TYPE_MSF2_SYSREG); - object_initialize(&s->timer, sizeof(s->timer), TYPE_MSS_TIMER); - qdev_set_parent_bus(DEVICE(&s->timer), sysbus_get_default()); + sysbus_init_child_obj(obj, "timer", &s->timer, sizeof(s->timer), + TYPE_MSS_TIMER); for (i = 0; i < MSF2_NUM_SPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), + sysbus_init_child_obj(obj, "spi[*]", &s->spi[i], sizeof(s->spi[i]), TYPE_MSS_SPI); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); } } @@ -138,10 +137,10 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) system_clock_scale = NANOSECONDS_PER_SECOND / s->m3clk; for (i = 0; i < MSF2_NUM_UARTS; i++) { - if (serial_hds[i]) { + if (serial_hd(i)) { serial_mm_init(get_system_memory(), uart_addr[i], 2, qdev_get_gpio_in(armv7m, uart_irq[i]), - 115200, serial_hds[i], DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(i), DEVICE_NATIVE_ENDIAN); } } diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 0795a3a3a1..2432b5e935 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -23,20 +23,20 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/boards.h" #include "hw/arm/arm.h" #include "exec/address-spaces.h" -#include "qemu/cutils.h" #include "hw/arm/msf2-soc.h" #include "cpu.h" #define DDR_BASE_ADDRESS 0xA0000000 -#define DDR_SIZE (64 * M_BYTE) +#define DDR_SIZE (64 * MiB) -#define M2S010_ENVM_SIZE (256 * K_BYTE) -#define M2S010_ESRAM_SIZE (64 * K_BYTE) +#define M2S010_ENVM_SIZE (256 * KiB) +#define M2S010_ESRAM_SIZE (64 * KiB) static void emcraft_sf2_s2s010_init(MachineState *machine) { diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 38d7322a19..c807010e83 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -1610,13 +1610,13 @@ static void musicpal_init(MachineState *machine) pic[MP_TIMER2_IRQ], pic[MP_TIMER3_IRQ], pic[MP_TIMER4_IRQ], NULL); - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(address_space_mem, MP_UART1_BASE, 2, pic[MP_UART1_IRQ], - 1825000, serial_hds[0], DEVICE_NATIVE_ENDIAN); + 1825000, serial_hd(0), DEVICE_NATIVE_ENDIAN); } - if (serial_hds[1]) { + if (serial_hd(1)) { serial_mm_init(address_space_mem, MP_UART2_BASE, 2, pic[MP_UART2_IRQ], - 1825000, serial_hds[1], DEVICE_NATIVE_ENDIAN); + 1825000, serial_hd(1), DEVICE_NATIVE_ENDIAN); } /* Register flash */ diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 32687afced..906b7ca22d 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -35,7 +35,6 @@ #include "hw/hw.h" #include "hw/bt.h" #include "hw/loader.h" -#include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "qemu/log.h" #include "exec/address-spaces.h" diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index b3a23a83d1..539d29ef9c 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -28,19 +28,24 @@ #include "hw/arm/omap.h" #include "sysemu/sysemu.h" #include "hw/arm/soc_dma.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "sysemu/qtest.h" #include "qemu/range.h" #include "hw/sysbus.h" #include "qemu/cutils.h" #include "qemu/bcd.h" +static inline void omap_log_badwidth(const char *funcname, hwaddr addr, int sz) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: %d-bit register %#08" HWADDR_PRIx "\n", + funcname, 8 * sz, addr); +} + /* Should signal the TCMI/GPMC */ uint32_t omap_badwidth_read8(void *opaque, hwaddr addr) { uint8_t ret; - OMAP_8B_REG(addr); + omap_log_badwidth(__func__, addr, 1); cpu_physical_memory_read(addr, &ret, 1); return ret; } @@ -50,7 +55,7 @@ void omap_badwidth_write8(void *opaque, hwaddr addr, { uint8_t val8 = value; - OMAP_8B_REG(addr); + omap_log_badwidth(__func__, addr, 1); cpu_physical_memory_write(addr, &val8, 1); } @@ -58,7 +63,7 @@ uint32_t omap_badwidth_read16(void *opaque, hwaddr addr) { uint16_t ret; - OMAP_16B_REG(addr); + omap_log_badwidth(__func__, addr, 2); cpu_physical_memory_read(addr, &ret, 2); return ret; } @@ -68,7 +73,7 @@ void omap_badwidth_write16(void *opaque, hwaddr addr, { uint16_t val16 = value; - OMAP_16B_REG(addr); + omap_log_badwidth(__func__, addr, 2); cpu_physical_memory_write(addr, &val16, 2); } @@ -76,7 +81,7 @@ uint32_t omap_badwidth_read32(void *opaque, hwaddr addr) { uint32_t ret; - OMAP_32B_REG(addr); + omap_log_badwidth(__func__, addr, 4); cpu_physical_memory_read(addr, &ret, 4); return ret; } @@ -84,7 +89,7 @@ uint32_t omap_badwidth_read32(void *opaque, hwaddr addr) void omap_badwidth_write32(void *opaque, hwaddr addr, uint32_t value) { - OMAP_32B_REG(addr); + omap_log_badwidth(__func__, addr, 4); cpu_physical_memory_write(addr, &value, 4); } @@ -3963,21 +3968,21 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, omap_findclk(s, "uart1_ck"), s->drq[OMAP_DMA_UART1_TX], s->drq[OMAP_DMA_UART1_RX], "uart1", - serial_hds[0]); + serial_hd(0)); s->uart[1] = omap_uart_init(0xfffb0800, qdev_get_gpio_in(s->ih[1], OMAP_INT_UART2), omap_findclk(s, "uart2_ck"), omap_findclk(s, "uart2_ck"), s->drq[OMAP_DMA_UART2_TX], s->drq[OMAP_DMA_UART2_RX], "uart2", - serial_hds[0] ? serial_hds[1] : NULL); + serial_hd(0) ? serial_hd(1) : NULL); s->uart[2] = omap_uart_init(0xfffb9800, qdev_get_gpio_in(s->ih[0], OMAP_INT_UART3), omap_findclk(s, "uart3_ck"), omap_findclk(s, "uart3_ck"), s->drq[OMAP_DMA_UART3_TX], s->drq[OMAP_DMA_UART3_RX], "uart3", - serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL); + serial_hd(0) && serial_hd(1) ? serial_hd(2) : NULL); s->dpll[0] = omap_dpll_init(system_memory, 0xfffecf00, omap_findclk(s, "dpll1")); @@ -3987,12 +3992,11 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, omap_findclk(s, "dpll3")); dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - error_report("missing SecureDigital device"); - exit(1); + if (!dinfo && !qtest_enabled()) { + warn_report("missing SecureDigital device"); } s->mmc = omap_mmc_init(0xfffb7800, system_memory, - blk_by_legacy_dinfo(dinfo), + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN), &s->drq[OMAP_DMA_MMC_TX], omap_findclk(s, "mmc_ck")); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 647b119ba9..3c7d1364a9 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -23,8 +23,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "sysemu/qtest.h" #include "hw/boards.h" #include "hw/hw.h" #include "hw/arm/arm.h" @@ -2349,7 +2348,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->drq[OMAP24XX_DMA_UART1_TX], s->drq[OMAP24XX_DMA_UART1_RX], "uart1", - serial_hds[0]); + serial_hd(0)); s->uart[1] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 20), qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_UART2_IRQ), @@ -2358,7 +2357,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->drq[OMAP24XX_DMA_UART2_TX], s->drq[OMAP24XX_DMA_UART2_RX], "uart2", - serial_hds[0] ? serial_hds[1] : NULL); + serial_hd(0) ? serial_hd(1) : NULL); s->uart[2] = omap2_uart_init(sysmem, omap_l4ta(s->l4, 21), qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_UART3_IRQ), @@ -2367,7 +2366,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->drq[OMAP24XX_DMA_UART3_TX], s->drq[OMAP24XX_DMA_UART3_RX], "uart3", - serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL); + serial_hd(0) && serial_hd(1) ? serial_hd(2) : NULL); s->gptimer[0] = omap_gp_timer_init(omap_l4ta(s->l4, 7), qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_GPTIMER1), @@ -2486,12 +2485,11 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->drq[OMAP24XX_DMA_GPMC]); dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - error_report("missing SecureDigital device"); - exit(1); + if (!dinfo && !qtest_enabled()) { + warn_report("missing SecureDigital device"); } s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9), - blk_by_legacy_dinfo(dinfo), + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MMC_IRQ), &s->drq[OMAP24XX_DMA_MMC1_TX], omap_findclk(s, "mmc_fclk"), omap_findclk(s, "mmc_iclk")); @@ -2519,8 +2517,8 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, omap_sti_init(omap_l4ta(s->l4, 18), sysmem, 0x54000000, qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_STI), omap_findclk(s, "emul_ck"), - serial_hds[0] && serial_hds[1] && serial_hds[2] ? - serial_hds[3] : NULL); + serial_hd(0) && serial_hd(1) && serial_hd(2) ? + serial_hd(3) : NULL); s->eac = omap_eac_init(omap_l4ta(s->l4, 32), qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_EAC_IRQ), diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index eccc19c77b..84550f0236 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -33,7 +33,6 @@ #include "hw/boards.h" #include "hw/arm/arm.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "sysemu/qtest.h" #include "exec/address-spaces.h" #include "cpu.h" diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 5805a2c858..b67b0cefb6 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -19,8 +19,8 @@ #include "hw/i2c/i2c.h" #include "hw/ssi/ssi.h" #include "chardev/char-fe.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" +#include "sysemu/qtest.h" #include "qemu/cutils.h" static struct { @@ -2095,32 +2095,31 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 121); dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - error_report("missing SecureDigital device"); - exit(1); + if (!dinfo && !qtest_enabled()) { + warn_report("missing SecureDigital device"); } s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, - blk_by_legacy_dinfo(dinfo), + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); for (i = 0; pxa270_serial[i].io_base; i++) { - if (serial_hds[i]) { + if (serial_hd(i)) { serial_mm_init(address_space, pxa270_serial[i].io_base, 2, qdev_get_gpio_in(s->pic, pxa270_serial[i].irqn), - 14857000 / 16, serial_hds[i], + 14857000 / 16, serial_hd(i), DEVICE_NATIVE_ENDIAN); } else { break; } } - if (serial_hds[i]) + if (serial_hd(i)) s->fir = pxa2xx_fir_init(address_space, 0x40800000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), - serial_hds[i]); + serial_hd(i)); s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); @@ -2220,32 +2219,31 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 85); dinfo = drive_get(IF_SD, 0, 0); - if (!dinfo) { - error_report("missing SecureDigital device"); - exit(1); + if (!dinfo && !qtest_enabled()) { + warn_report("missing SecureDigital device"); } s->mmc = pxa2xx_mmci_init(address_space, 0x41100000, - blk_by_legacy_dinfo(dinfo), + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI)); for (i = 0; pxa255_serial[i].io_base; i++) { - if (serial_hds[i]) { + if (serial_hd(i)) { serial_mm_init(address_space, pxa255_serial[i].io_base, 2, qdev_get_gpio_in(s->pic, pxa255_serial[i].irqn), - 14745600 / 16, serial_hds[i], + 14745600 / 16, serial_hd(i), DEVICE_NATIVE_ENDIAN); } else { break; } } - if (serial_hds[i]) + if (serial_hd(i)) s->fir = pxa2xx_fir_init(address_space, 0x40800000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_ICP), qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_ICP), qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_ICP), - serial_hds[i]); + serial_hd(i)); s->lcd = pxa2xx_lcdc_init(address_space, 0x44000000, qdev_get_gpio_in(s->pic, PXA2XX_PIC_LCD)); diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 955a7c4e80..66899c28dc 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -226,7 +226,6 @@ static void raspi2_machine_init(MachineClass *mc) mc->no_parallel = 1; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a7"); mc->max_cpus = BCM283X_NCPUS; mc->min_cpus = BCM283X_NCPUS; mc->default_cpus = BCM283X_NCPUS; @@ -249,7 +248,6 @@ static void raspi3_machine_init(MachineClass *mc) mc->no_parallel = 1; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"); mc->max_cpus = BCM283X_NCPUS; mc->min_cpus = BCM283X_NCPUS; mc->default_cpus = BCM283X_NCPUS; diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 2139a62e25..cd585d9469 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -195,10 +195,10 @@ static void realview_init(MachineState *machine, sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]); sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]); - pl011_create(0x10009000, pic[12], serial_hds[0]); - pl011_create(0x1000a000, pic[13], serial_hds[1]); - pl011_create(0x1000b000, pic[14], serial_hds[2]); - pl011_create(0x1000c000, pic[15], serial_hds[3]); + pl011_create(0x10009000, pic[12], serial_hd(0)); + pl011_create(0x1000a000, pic[13], serial_hd(1)); + pl011_create(0x1000b000, pic[14], serial_hd(2)); + pl011_create(0x1000c000, pic[15], serial_hd(3)); /* DMA controller is optional, apparently. */ sysbus_create_simple("pl081", 0x10030000, pic[24]); diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c new file mode 100644 index 0000000000..55c75d65d2 --- /dev/null +++ b/hw/arm/smmu-common.c @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2014-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * Author: Prem Mallappa + * + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "trace.h" +#include "exec/target_page.h" +#include "qom/cpu.h" +#include "hw/qdev-properties.h" +#include "qapi/error.h" +#include "qemu/jhash.h" + +#include "qemu/error-report.h" +#include "hw/arm/smmu-common.h" +#include "smmu-internal.h" + +/* IOTLB Management */ + +inline void smmu_iotlb_inv_all(SMMUState *s) +{ + trace_smmu_iotlb_inv_all(); + g_hash_table_remove_all(s->iotlb); +} + +static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value, + gpointer user_data) +{ + uint16_t asid = *(uint16_t *)user_data; + SMMUIOTLBKey *iotlb_key = (SMMUIOTLBKey *)key; + + return iotlb_key->asid == asid; +} + +inline void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova) +{ + SMMUIOTLBKey key = {.asid = asid, .iova = iova}; + + trace_smmu_iotlb_inv_iova(asid, iova); + g_hash_table_remove(s->iotlb, &key); +} + +inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid) +{ + trace_smmu_iotlb_inv_asid(asid); + g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid, &asid); +} + +/* VMSAv8-64 Translation */ + +/** + * get_pte - Get the content of a page table entry located at + * @base_addr[@index] + */ +static int get_pte(dma_addr_t baseaddr, uint32_t index, uint64_t *pte, + SMMUPTWEventInfo *info) +{ + int ret; + dma_addr_t addr = baseaddr + index * sizeof(*pte); + + /* TODO: guarantee 64-bit single-copy atomicity */ + ret = dma_memory_read(&address_space_memory, addr, + (uint8_t *)pte, sizeof(*pte)); + + if (ret != MEMTX_OK) { + info->type = SMMU_PTW_ERR_WALK_EABT; + info->addr = addr; + return -EINVAL; + } + trace_smmu_get_pte(baseaddr, index, addr, *pte); + return 0; +} + +/* VMSAv8-64 Translation Table Format Descriptor Decoding */ + +/** + * get_page_pte_address - returns the L3 descriptor output address, + * ie. the page frame + * ARM ARM spec: Figure D4-17 VMSAv8-64 level 3 descriptor format + */ +static inline hwaddr get_page_pte_address(uint64_t pte, int granule_sz) +{ + return PTE_ADDRESS(pte, granule_sz); +} + +/** + * get_table_pte_address - return table descriptor output address, + * ie. address of next level table + * ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor formats + */ +static inline hwaddr get_table_pte_address(uint64_t pte, int granule_sz) +{ + return PTE_ADDRESS(pte, granule_sz); +} + +/** + * get_block_pte_address - return block descriptor output address and block size + * ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor formats + */ +static inline hwaddr get_block_pte_address(uint64_t pte, int level, + int granule_sz, uint64_t *bsz) +{ + int n = level_shift(level, granule_sz); + + *bsz = 1ULL << n; + return PTE_ADDRESS(pte, n); +} + +SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) +{ + bool tbi = extract64(iova, 55, 1) ? TBI1(cfg->tbi) : TBI0(cfg->tbi); + uint8_t tbi_byte = tbi * 8; + + if (cfg->tt[0].tsz && + !extract64(iova, 64 - cfg->tt[0].tsz, cfg->tt[0].tsz - tbi_byte)) { + /* there is a ttbr0 region and we are in it (high bits all zero) */ + return &cfg->tt[0]; + } else if (cfg->tt[1].tsz && + !extract64(iova, 64 - cfg->tt[1].tsz, cfg->tt[1].tsz - tbi_byte)) { + /* there is a ttbr1 region and we are in it (high bits all one) */ + return &cfg->tt[1]; + } else if (!cfg->tt[0].tsz) { + /* ttbr0 region is "everything not in the ttbr1 region" */ + return &cfg->tt[0]; + } else if (!cfg->tt[1].tsz) { + /* ttbr1 region is "everything not in the ttbr0 region" */ + return &cfg->tt[1]; + } + /* in the gap between the two regions, this is a Translation fault */ + return NULL; +} + +/** + * smmu_ptw_64 - VMSAv8-64 Walk of the page tables for a given IOVA + * @cfg: translation config + * @iova: iova to translate + * @perm: access type + * @tlbe: IOMMUTLBEntry (out) + * @info: handle to an error info + * + * Return 0 on success, < 0 on error. In case of error, @info is filled + * and tlbe->perm is set to IOMMU_NONE. + * Upon success, @tlbe is filled with translated_addr and entry + * permission rights. + */ +static int smmu_ptw_64(SMMUTransCfg *cfg, + dma_addr_t iova, IOMMUAccessFlags perm, + IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +{ + dma_addr_t baseaddr, indexmask; + int stage = cfg->stage; + SMMUTransTableInfo *tt = select_tt(cfg, iova); + uint8_t level, granule_sz, inputsize, stride; + + if (!tt || tt->disabled) { + info->type = SMMU_PTW_ERR_TRANSLATION; + goto error; + } + + granule_sz = tt->granule_sz; + stride = granule_sz - 3; + inputsize = 64 - tt->tsz; + level = 4 - (inputsize - 4) / stride; + indexmask = (1ULL << (inputsize - (stride * (4 - level)))) - 1; + baseaddr = extract64(tt->ttb, 0, 48); + baseaddr &= ~indexmask; + + tlbe->iova = iova; + tlbe->addr_mask = (1 << granule_sz) - 1; + + while (level <= 3) { + uint64_t subpage_size = 1ULL << level_shift(level, granule_sz); + uint64_t mask = subpage_size - 1; + uint32_t offset = iova_level_offset(iova, inputsize, level, granule_sz); + uint64_t pte; + dma_addr_t pte_addr = baseaddr + offset * sizeof(pte); + uint8_t ap; + + if (get_pte(baseaddr, offset, &pte, info)) { + goto error; + } + trace_smmu_ptw_level(level, iova, subpage_size, + baseaddr, offset, pte); + + if (is_invalid_pte(pte) || is_reserved_pte(pte, level)) { + trace_smmu_ptw_invalid_pte(stage, level, baseaddr, + pte_addr, offset, pte); + info->type = SMMU_PTW_ERR_TRANSLATION; + goto error; + } + + if (is_page_pte(pte, level)) { + uint64_t gpa = get_page_pte_address(pte, granule_sz); + + ap = PTE_AP(pte); + if (is_permission_fault(ap, perm)) { + info->type = SMMU_PTW_ERR_PERMISSION; + goto error; + } + + tlbe->translated_addr = gpa + (iova & mask); + tlbe->perm = PTE_AP_TO_PERM(ap); + trace_smmu_ptw_page_pte(stage, level, iova, + baseaddr, pte_addr, pte, gpa); + return 0; + } + if (is_block_pte(pte, level)) { + uint64_t block_size; + hwaddr gpa = get_block_pte_address(pte, level, granule_sz, + &block_size); + + ap = PTE_AP(pte); + if (is_permission_fault(ap, perm)) { + info->type = SMMU_PTW_ERR_PERMISSION; + goto error; + } + + trace_smmu_ptw_block_pte(stage, level, baseaddr, + pte_addr, pte, iova, gpa, + block_size >> 20); + + tlbe->translated_addr = gpa + (iova & mask); + tlbe->perm = PTE_AP_TO_PERM(ap); + return 0; + } + + /* table pte */ + ap = PTE_APTABLE(pte); + + if (is_permission_fault(ap, perm)) { + info->type = SMMU_PTW_ERR_PERMISSION; + goto error; + } + baseaddr = get_table_pte_address(pte, granule_sz); + level++; + } + + info->type = SMMU_PTW_ERR_TRANSLATION; + +error: + tlbe->perm = IOMMU_NONE; + return -EINVAL; +} + +/** + * smmu_ptw - Walk the page tables for an IOVA, according to @cfg + * + * @cfg: translation configuration + * @iova: iova to translate + * @perm: tentative access type + * @tlbe: returned entry + * @info: ptw event handle + * + * return 0 on success + */ +inline int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, + IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +{ + if (!cfg->aa64) { + /* + * This code path is not entered as we check this while decoding + * the configuration data in the derived SMMU model. + */ + g_assert_not_reached(); + } + + return smmu_ptw_64(cfg, iova, perm, tlbe, info); +} + +/** + * The bus number is used for lookup when SID based invalidation occurs. + * In that case we lazily populate the SMMUPciBus array from the bus hash + * table. At the time the SMMUPciBus is created (smmu_find_add_as), the bus + * numbers may not be always initialized yet. + */ +SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num) +{ + SMMUPciBus *smmu_pci_bus = s->smmu_pcibus_by_bus_num[bus_num]; + + if (!smmu_pci_bus) { + GHashTableIter iter; + + g_hash_table_iter_init(&iter, s->smmu_pcibus_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&smmu_pci_bus)) { + if (pci_bus_num(smmu_pci_bus->bus) == bus_num) { + s->smmu_pcibus_by_bus_num[bus_num] = smmu_pci_bus; + return smmu_pci_bus; + } + } + } + return smmu_pci_bus; +} + +static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) +{ + SMMUState *s = opaque; + SMMUPciBus *sbus = g_hash_table_lookup(s->smmu_pcibus_by_busptr, bus); + SMMUDevice *sdev; + + if (!sbus) { + sbus = g_malloc0(sizeof(SMMUPciBus) + + sizeof(SMMUDevice *) * SMMU_PCI_DEVFN_MAX); + sbus->bus = bus; + g_hash_table_insert(s->smmu_pcibus_by_busptr, bus, sbus); + } + + sdev = sbus->pbdev[devfn]; + if (!sdev) { + char *name = g_strdup_printf("%s-%d-%d", + s->mrtypename, + pci_bus_num(bus), devfn); + sdev = sbus->pbdev[devfn] = g_new0(SMMUDevice, 1); + + sdev->smmu = s; + sdev->bus = bus; + sdev->devfn = devfn; + + memory_region_init_iommu(&sdev->iommu, sizeof(sdev->iommu), + s->mrtypename, + OBJECT(s), name, 1ULL << SMMU_MAX_VA_BITS); + address_space_init(&sdev->as, + MEMORY_REGION(&sdev->iommu), name); + trace_smmu_add_mr(name); + g_free(name); + } + + return &sdev->as; +} + +IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid) +{ + uint8_t bus_n, devfn; + SMMUPciBus *smmu_bus; + SMMUDevice *smmu; + + bus_n = PCI_BUS_NUM(sid); + smmu_bus = smmu_find_smmu_pcibus(s, bus_n); + if (smmu_bus) { + devfn = SMMU_PCI_DEVFN(sid); + smmu = smmu_bus->pbdev[devfn]; + if (smmu) { + return &smmu->iommu; + } + } + return NULL; +} + +static guint smmu_iotlb_key_hash(gconstpointer v) +{ + SMMUIOTLBKey *key = (SMMUIOTLBKey *)v; + uint32_t a, b, c; + + /* Jenkins hash */ + a = b = c = JHASH_INITVAL + sizeof(*key); + a += key->asid; + b += extract64(key->iova, 0, 32); + c += extract64(key->iova, 32, 32); + + __jhash_mix(a, b, c); + __jhash_final(a, b, c); + + return c; +} + +static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2) +{ + const SMMUIOTLBKey *k1 = v1; + const SMMUIOTLBKey *k2 = v2; + + return (k1->asid == k2->asid) && (k1->iova == k2->iova); +} + +/* Unmap the whole notifier's range */ +static void smmu_unmap_notifier_range(IOMMUNotifier *n) +{ + IOMMUTLBEntry entry; + + entry.target_as = &address_space_memory; + entry.iova = n->start; + entry.perm = IOMMU_NONE; + entry.addr_mask = n->end - n->start; + + memory_region_notify_one(n, &entry); +} + +/* Unmap all notifiers attached to @mr */ +inline void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr) +{ + IOMMUNotifier *n; + + trace_smmu_inv_notifiers_mr(mr->parent_obj.name); + IOMMU_NOTIFIER_FOREACH(n, mr) { + smmu_unmap_notifier_range(n); + } +} + +/* Unmap all notifiers of all mr's */ +void smmu_inv_notifiers_all(SMMUState *s) +{ + SMMUNotifierNode *node; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + smmu_inv_notifiers_mr(&node->sdev->iommu); + } +} + +static void smmu_base_realize(DeviceState *dev, Error **errp) +{ + SMMUState *s = ARM_SMMU(dev); + SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev); + Error *local_err = NULL; + + sbc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + s->configs = g_hash_table_new_full(NULL, NULL, NULL, g_free); + s->iotlb = g_hash_table_new_full(smmu_iotlb_key_hash, smmu_iotlb_key_equal, + g_free, g_free); + s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); + + if (s->primary_bus) { + pci_setup_iommu(s->primary_bus, smmu_find_add_as, s); + } else { + error_setg(errp, "SMMU is not attached to any PCI bus!"); + } +} + +static void smmu_base_reset(DeviceState *dev) +{ + SMMUState *s = ARM_SMMU(dev); + + g_hash_table_remove_all(s->configs); + g_hash_table_remove_all(s->iotlb); +} + +static Property smmu_dev_properties[] = { + DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), + DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, "PCI", PCIBus *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void smmu_base_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SMMUBaseClass *sbc = ARM_SMMU_CLASS(klass); + + dc->props = smmu_dev_properties; + device_class_set_parent_realize(dc, smmu_base_realize, + &sbc->parent_realize); + dc->reset = smmu_base_reset; +} + +static const TypeInfo smmu_base_info = { + .name = TYPE_ARM_SMMU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SMMUState), + .class_data = NULL, + .class_size = sizeof(SMMUBaseClass), + .class_init = smmu_base_class_init, + .abstract = true, +}; + +static void smmu_base_register_types(void) +{ + type_register_static(&smmu_base_info); +} + +type_init(smmu_base_register_types) + diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h new file mode 100644 index 0000000000..7794d6d394 --- /dev/null +++ b/hw/arm/smmu-internal.h @@ -0,0 +1,99 @@ +/* + * ARM SMMU support - Internal API + * + * Copyright (c) 2017 Red Hat, Inc. + * Copyright (C) 2014-2016 Broadcom Corporation + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 + * 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 . + */ + +#ifndef HW_ARM_SMMU_INTERNAL_H +#define HW_ARM_SMMU_INTERNAL_H + +#define TBI0(tbi) ((tbi) & 0x1) +#define TBI1(tbi) ((tbi) & 0x2 >> 1) + +/* PTE Manipulation */ + +#define ARM_LPAE_PTE_TYPE_SHIFT 0 +#define ARM_LPAE_PTE_TYPE_MASK 0x3 + +#define ARM_LPAE_PTE_TYPE_BLOCK 1 +#define ARM_LPAE_PTE_TYPE_TABLE 3 + +#define ARM_LPAE_L3_PTE_TYPE_RESERVED 1 +#define ARM_LPAE_L3_PTE_TYPE_PAGE 3 + +#define ARM_LPAE_PTE_VALID (1 << 0) + +#define PTE_ADDRESS(pte, shift) \ + (extract64(pte, shift, 47 - shift + 1) << shift) + +#define is_invalid_pte(pte) (!(pte & ARM_LPAE_PTE_VALID)) + +#define is_reserved_pte(pte, level) \ + ((level == 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_L3_PTE_TYPE_RESERVED)) + +#define is_block_pte(pte, level) \ + ((level < 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_PTE_TYPE_BLOCK)) + +#define is_table_pte(pte, level) \ + ((level < 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_PTE_TYPE_TABLE)) + +#define is_page_pte(pte, level) \ + ((level == 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_L3_PTE_TYPE_PAGE)) + +/* access permissions */ + +#define PTE_AP(pte) \ + (extract64(pte, 6, 2)) + +#define PTE_APTABLE(pte) \ + (extract64(pte, 61, 2)) + +/* + * TODO: At the moment all transactions are considered as privileged (EL1) + * as IOMMU translation callback does not pass user/priv attributes. + */ +#define is_permission_fault(ap, perm) \ + (((perm) & IOMMU_WO) && ((ap) & 0x2)) + +#define PTE_AP_TO_PERM(ap) \ + (IOMMU_ACCESS_FLAG(true, !((ap) & 0x2))) + +/* Level Indexing */ + +static inline int level_shift(int level, int granule_sz) +{ + return granule_sz + (3 - level) * (granule_sz - 3); +} + +static inline uint64_t level_page_mask(int level, int granule_sz) +{ + return ~(MAKE_64BIT_MASK(0, level_shift(level, granule_sz))); +} + +static inline +uint64_t iova_level_offset(uint64_t iova, int inputsize, + int level, int gsz) +{ + return ((iova & MAKE_64BIT_MASK(0, inputsize)) >> level_shift(level, gsz)) & + MAKE_64BIT_MASK(0, gsz - 3); +} + +#endif diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h new file mode 100644 index 0000000000..bab25d640e --- /dev/null +++ b/hw/arm/smmuv3-internal.h @@ -0,0 +1,629 @@ +/* + * ARM SMMUv3 support - Internal API + * + * Copyright (C) 2014-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#ifndef HW_ARM_SMMU_V3_INTERNAL_H +#define HW_ARM_SMMU_V3_INTERNAL_H + +#include "hw/arm/smmu-common.h" + +typedef enum SMMUTranslationStatus { + SMMU_TRANS_DISABLE, + SMMU_TRANS_ABORT, + SMMU_TRANS_BYPASS, + SMMU_TRANS_ERROR, + SMMU_TRANS_SUCCESS, +} SMMUTranslationStatus; + +/* MMIO Registers */ + +REG32(IDR0, 0x0) + FIELD(IDR0, S1P, 1 , 1) + FIELD(IDR0, TTF, 2 , 2) + FIELD(IDR0, COHACC, 4 , 1) + FIELD(IDR0, ASID16, 12, 1) + FIELD(IDR0, TTENDIAN, 21, 2) + FIELD(IDR0, STALL_MODEL, 24, 2) + FIELD(IDR0, TERM_MODEL, 26, 1) + FIELD(IDR0, STLEVEL, 27, 2) + +REG32(IDR1, 0x4) + FIELD(IDR1, SIDSIZE, 0 , 6) + FIELD(IDR1, EVENTQS, 16, 5) + FIELD(IDR1, CMDQS, 21, 5) + +#define SMMU_IDR1_SIDSIZE 16 +#define SMMU_CMDQS 19 +#define SMMU_EVENTQS 19 + +REG32(IDR2, 0x8) +REG32(IDR3, 0xc) +REG32(IDR4, 0x10) +REG32(IDR5, 0x14) + FIELD(IDR5, OAS, 0, 3); + FIELD(IDR5, GRAN4K, 4, 1); + FIELD(IDR5, GRAN16K, 5, 1); + FIELD(IDR5, GRAN64K, 6, 1); + +#define SMMU_IDR5_OAS 4 + +REG32(IIDR, 0x1c) +REG32(CR0, 0x20) + FIELD(CR0, SMMU_ENABLE, 0, 1) + FIELD(CR0, EVENTQEN, 2, 1) + FIELD(CR0, CMDQEN, 3, 1) + +#define SMMU_CR0_RESERVED 0xFFFFFC20 + +REG32(CR0ACK, 0x24) +REG32(CR1, 0x28) +REG32(CR2, 0x2c) +REG32(STATUSR, 0x40) +REG32(IRQ_CTRL, 0x50) + FIELD(IRQ_CTRL, GERROR_IRQEN, 0, 1) + FIELD(IRQ_CTRL, PRI_IRQEN, 1, 1) + FIELD(IRQ_CTRL, EVENTQ_IRQEN, 2, 1) + +REG32(IRQ_CTRL_ACK, 0x54) +REG32(GERROR, 0x60) + FIELD(GERROR, CMDQ_ERR, 0, 1) + FIELD(GERROR, EVENTQ_ABT_ERR, 2, 1) + FIELD(GERROR, PRIQ_ABT_ERR, 3, 1) + FIELD(GERROR, MSI_CMDQ_ABT_ERR, 4, 1) + FIELD(GERROR, MSI_EVENTQ_ABT_ERR, 5, 1) + FIELD(GERROR, MSI_PRIQ_ABT_ERR, 6, 1) + FIELD(GERROR, MSI_GERROR_ABT_ERR, 7, 1) + FIELD(GERROR, MSI_SFM_ERR, 8, 1) + +REG32(GERRORN, 0x64) + +#define A_GERROR_IRQ_CFG0 0x68 /* 64b */ +REG32(GERROR_IRQ_CFG1, 0x70) +REG32(GERROR_IRQ_CFG2, 0x74) + +#define A_STRTAB_BASE 0x80 /* 64b */ + +#define SMMU_BASE_ADDR_MASK 0xffffffffffe0 + +REG32(STRTAB_BASE_CFG, 0x88) + FIELD(STRTAB_BASE_CFG, FMT, 16, 2) + FIELD(STRTAB_BASE_CFG, SPLIT, 6 , 5) + FIELD(STRTAB_BASE_CFG, LOG2SIZE, 0 , 6) + +#define A_CMDQ_BASE 0x90 /* 64b */ +REG32(CMDQ_PROD, 0x98) +REG32(CMDQ_CONS, 0x9c) + FIELD(CMDQ_CONS, ERR, 24, 7) + +#define A_EVENTQ_BASE 0xa0 /* 64b */ +REG32(EVENTQ_PROD, 0xa8) +REG32(EVENTQ_CONS, 0xac) + +#define A_EVENTQ_IRQ_CFG0 0xb0 /* 64b */ +REG32(EVENTQ_IRQ_CFG1, 0xb8) +REG32(EVENTQ_IRQ_CFG2, 0xbc) + +#define A_IDREGS 0xfd0 + +static inline int smmu_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->cr[0], CR0, SMMU_ENABLE); +} + +/* Command Queue Entry */ +typedef struct Cmd { + uint32_t word[4]; +} Cmd; + +/* Event Queue Entry */ +typedef struct Evt { + uint32_t word[8]; +} Evt; + +static inline uint32_t smmuv3_idreg(int regoffset) +{ + /* + * Return the value of the Primecell/Corelink ID registers at the + * specified offset from the first ID register. + * These value indicate an ARM implementation of MMU600 p1 + */ + static const uint8_t smmuv3_ids[] = { + 0x04, 0, 0, 0, 0x84, 0xB4, 0xF0, 0x10, 0x0D, 0xF0, 0x05, 0xB1 + }; + return smmuv3_ids[regoffset / 4]; +} + +static inline bool smmuv3_eventq_irq_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, EVENTQ_IRQEN); +} + +static inline bool smmuv3_gerror_irq_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, GERROR_IRQEN); +} + +/* Queue Handling */ + +#define Q_BASE(q) ((q)->base & SMMU_BASE_ADDR_MASK) +#define WRAP_MASK(q) (1 << (q)->log2size) +#define INDEX_MASK(q) (((1 << (q)->log2size)) - 1) +#define WRAP_INDEX_MASK(q) ((1 << ((q)->log2size + 1)) - 1) + +#define Q_CONS(q) ((q)->cons & INDEX_MASK(q)) +#define Q_PROD(q) ((q)->prod & INDEX_MASK(q)) + +#define Q_CONS_ENTRY(q) (Q_BASE(q) + (q)->entry_size * Q_CONS(q)) +#define Q_PROD_ENTRY(q) (Q_BASE(q) + (q)->entry_size * Q_PROD(q)) + +#define Q_CONS_WRAP(q) (((q)->cons & WRAP_MASK(q)) >> (q)->log2size) +#define Q_PROD_WRAP(q) (((q)->prod & WRAP_MASK(q)) >> (q)->log2size) + +static inline bool smmuv3_q_full(SMMUQueue *q) +{ + return ((q->cons ^ q->prod) & WRAP_INDEX_MASK(q)) == WRAP_MASK(q); +} + +static inline bool smmuv3_q_empty(SMMUQueue *q) +{ + return (q->cons & WRAP_INDEX_MASK(q)) == (q->prod & WRAP_INDEX_MASK(q)); +} + +static inline void queue_prod_incr(SMMUQueue *q) +{ + q->prod = (q->prod + 1) & WRAP_INDEX_MASK(q); +} + +static inline void queue_cons_incr(SMMUQueue *q) +{ + /* + * We have to use deposit for the CONS registers to preserve + * the ERR field in the high bits. + */ + q->cons = deposit32(q->cons, 0, q->log2size + 1, q->cons + 1); +} + +static inline bool smmuv3_cmdq_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->cr[0], CR0, CMDQEN); +} + +static inline bool smmuv3_eventq_enabled(SMMUv3State *s) +{ + return FIELD_EX32(s->cr[0], CR0, EVENTQEN); +} + +static inline void smmu_write_cmdq_err(SMMUv3State *s, uint32_t err_type) +{ + s->cmdq.cons = FIELD_DP32(s->cmdq.cons, CMDQ_CONS, ERR, err_type); +} + +/* Commands */ + +typedef enum SMMUCommandType { + SMMU_CMD_NONE = 0x00, + SMMU_CMD_PREFETCH_CONFIG , + SMMU_CMD_PREFETCH_ADDR, + SMMU_CMD_CFGI_STE, + SMMU_CMD_CFGI_STE_RANGE, + SMMU_CMD_CFGI_CD, + SMMU_CMD_CFGI_CD_ALL, + SMMU_CMD_CFGI_ALL, + SMMU_CMD_TLBI_NH_ALL = 0x10, + SMMU_CMD_TLBI_NH_ASID, + SMMU_CMD_TLBI_NH_VA, + SMMU_CMD_TLBI_NH_VAA, + SMMU_CMD_TLBI_EL3_ALL = 0x18, + SMMU_CMD_TLBI_EL3_VA = 0x1a, + SMMU_CMD_TLBI_EL2_ALL = 0x20, + SMMU_CMD_TLBI_EL2_ASID, + SMMU_CMD_TLBI_EL2_VA, + SMMU_CMD_TLBI_EL2_VAA, + SMMU_CMD_TLBI_S12_VMALL = 0x28, + SMMU_CMD_TLBI_S2_IPA = 0x2a, + SMMU_CMD_TLBI_NSNH_ALL = 0x30, + SMMU_CMD_ATC_INV = 0x40, + SMMU_CMD_PRI_RESP, + SMMU_CMD_RESUME = 0x44, + SMMU_CMD_STALL_TERM, + SMMU_CMD_SYNC, +} SMMUCommandType; + +static const char *cmd_stringify[] = { + [SMMU_CMD_PREFETCH_CONFIG] = "SMMU_CMD_PREFETCH_CONFIG", + [SMMU_CMD_PREFETCH_ADDR] = "SMMU_CMD_PREFETCH_ADDR", + [SMMU_CMD_CFGI_STE] = "SMMU_CMD_CFGI_STE", + [SMMU_CMD_CFGI_STE_RANGE] = "SMMU_CMD_CFGI_STE_RANGE", + [SMMU_CMD_CFGI_CD] = "SMMU_CMD_CFGI_CD", + [SMMU_CMD_CFGI_CD_ALL] = "SMMU_CMD_CFGI_CD_ALL", + [SMMU_CMD_CFGI_ALL] = "SMMU_CMD_CFGI_ALL", + [SMMU_CMD_TLBI_NH_ALL] = "SMMU_CMD_TLBI_NH_ALL", + [SMMU_CMD_TLBI_NH_ASID] = "SMMU_CMD_TLBI_NH_ASID", + [SMMU_CMD_TLBI_NH_VA] = "SMMU_CMD_TLBI_NH_VA", + [SMMU_CMD_TLBI_NH_VAA] = "SMMU_CMD_TLBI_NH_VAA", + [SMMU_CMD_TLBI_EL3_ALL] = "SMMU_CMD_TLBI_EL3_ALL", + [SMMU_CMD_TLBI_EL3_VA] = "SMMU_CMD_TLBI_EL3_VA", + [SMMU_CMD_TLBI_EL2_ALL] = "SMMU_CMD_TLBI_EL2_ALL", + [SMMU_CMD_TLBI_EL2_ASID] = "SMMU_CMD_TLBI_EL2_ASID", + [SMMU_CMD_TLBI_EL2_VA] = "SMMU_CMD_TLBI_EL2_VA", + [SMMU_CMD_TLBI_EL2_VAA] = "SMMU_CMD_TLBI_EL2_VAA", + [SMMU_CMD_TLBI_S12_VMALL] = "SMMU_CMD_TLBI_S12_VMALL", + [SMMU_CMD_TLBI_S2_IPA] = "SMMU_CMD_TLBI_S2_IPA", + [SMMU_CMD_TLBI_NSNH_ALL] = "SMMU_CMD_TLBI_NSNH_ALL", + [SMMU_CMD_ATC_INV] = "SMMU_CMD_ATC_INV", + [SMMU_CMD_PRI_RESP] = "SMMU_CMD_PRI_RESP", + [SMMU_CMD_RESUME] = "SMMU_CMD_RESUME", + [SMMU_CMD_STALL_TERM] = "SMMU_CMD_STALL_TERM", + [SMMU_CMD_SYNC] = "SMMU_CMD_SYNC", +}; + +static inline const char *smmu_cmd_string(SMMUCommandType type) +{ + if (type > SMMU_CMD_NONE && type < ARRAY_SIZE(cmd_stringify)) { + return cmd_stringify[type] ? cmd_stringify[type] : "UNKNOWN"; + } else { + return "INVALID"; + } +} + +/* CMDQ fields */ + +typedef enum { + SMMU_CERROR_NONE = 0, + SMMU_CERROR_ILL, + SMMU_CERROR_ABT, + SMMU_CERROR_ATC_INV_SYNC, +} SMMUCmdError; + +enum { /* Command completion notification */ + CMD_SYNC_SIG_NONE, + CMD_SYNC_SIG_IRQ, + CMD_SYNC_SIG_SEV, +}; + +#define CMD_TYPE(x) extract32((x)->word[0], 0 , 8) +#define CMD_SSEC(x) extract32((x)->word[0], 10, 1) +#define CMD_SSV(x) extract32((x)->word[0], 11, 1) +#define CMD_RESUME_AC(x) extract32((x)->word[0], 12, 1) +#define CMD_RESUME_AB(x) extract32((x)->word[0], 13, 1) +#define CMD_SYNC_CS(x) extract32((x)->word[0], 12, 2) +#define CMD_SSID(x) extract32((x)->word[0], 12, 20) +#define CMD_SID(x) ((x)->word[1]) +#define CMD_VMID(x) extract32((x)->word[1], 0 , 16) +#define CMD_ASID(x) extract32((x)->word[1], 16, 16) +#define CMD_RESUME_STAG(x) extract32((x)->word[2], 0 , 16) +#define CMD_RESP(x) extract32((x)->word[2], 11, 2) +#define CMD_LEAF(x) extract32((x)->word[2], 0 , 1) +#define CMD_STE_RANGE(x) extract32((x)->word[2], 0 , 5) +#define CMD_ADDR(x) ({ \ + uint64_t high = (uint64_t)(x)->word[3]; \ + uint64_t low = extract32((x)->word[2], 12, 20); \ + uint64_t addr = high << 32 | (low << 12); \ + addr; \ + }) + +#define SMMU_FEATURE_2LVL_STE (1 << 0) + +/* Events */ + +typedef enum SMMUEventType { + SMMU_EVT_NONE = 0x00, + SMMU_EVT_F_UUT , + SMMU_EVT_C_BAD_STREAMID , + SMMU_EVT_F_STE_FETCH , + SMMU_EVT_C_BAD_STE , + SMMU_EVT_F_BAD_ATS_TREQ , + SMMU_EVT_F_STREAM_DISABLED , + SMMU_EVT_F_TRANS_FORBIDDEN , + SMMU_EVT_C_BAD_SUBSTREAMID , + SMMU_EVT_F_CD_FETCH , + SMMU_EVT_C_BAD_CD , + SMMU_EVT_F_WALK_EABT , + SMMU_EVT_F_TRANSLATION = 0x10, + SMMU_EVT_F_ADDR_SIZE , + SMMU_EVT_F_ACCESS , + SMMU_EVT_F_PERMISSION , + SMMU_EVT_F_TLB_CONFLICT = 0x20, + SMMU_EVT_F_CFG_CONFLICT , + SMMU_EVT_E_PAGE_REQ = 0x24, +} SMMUEventType; + +static const char *event_stringify[] = { + [SMMU_EVT_NONE] = "no recorded event", + [SMMU_EVT_F_UUT] = "SMMU_EVT_F_UUT", + [SMMU_EVT_C_BAD_STREAMID] = "SMMU_EVT_C_BAD_STREAMID", + [SMMU_EVT_F_STE_FETCH] = "SMMU_EVT_F_STE_FETCH", + [SMMU_EVT_C_BAD_STE] = "SMMU_EVT_C_BAD_STE", + [SMMU_EVT_F_BAD_ATS_TREQ] = "SMMU_EVT_F_BAD_ATS_TREQ", + [SMMU_EVT_F_STREAM_DISABLED] = "SMMU_EVT_F_STREAM_DISABLED", + [SMMU_EVT_F_TRANS_FORBIDDEN] = "SMMU_EVT_F_TRANS_FORBIDDEN", + [SMMU_EVT_C_BAD_SUBSTREAMID] = "SMMU_EVT_C_BAD_SUBSTREAMID", + [SMMU_EVT_F_CD_FETCH] = "SMMU_EVT_F_CD_FETCH", + [SMMU_EVT_C_BAD_CD] = "SMMU_EVT_C_BAD_CD", + [SMMU_EVT_F_WALK_EABT] = "SMMU_EVT_F_WALK_EABT", + [SMMU_EVT_F_TRANSLATION] = "SMMU_EVT_F_TRANSLATION", + [SMMU_EVT_F_ADDR_SIZE] = "SMMU_EVT_F_ADDR_SIZE", + [SMMU_EVT_F_ACCESS] = "SMMU_EVT_F_ACCESS", + [SMMU_EVT_F_PERMISSION] = "SMMU_EVT_F_PERMISSION", + [SMMU_EVT_F_TLB_CONFLICT] = "SMMU_EVT_F_TLB_CONFLICT", + [SMMU_EVT_F_CFG_CONFLICT] = "SMMU_EVT_F_CFG_CONFLICT", + [SMMU_EVT_E_PAGE_REQ] = "SMMU_EVT_E_PAGE_REQ", +}; + +static inline const char *smmu_event_string(SMMUEventType type) +{ + if (type < ARRAY_SIZE(event_stringify)) { + return event_stringify[type] ? event_stringify[type] : "UNKNOWN"; + } else { + return "INVALID"; + } +} + +/* Encode an event record */ +typedef struct SMMUEventInfo { + SMMUEventType type; + uint32_t sid; + bool recorded; + bool record_trans_faults; + union { + struct { + uint32_t ssid; + bool ssv; + dma_addr_t addr; + bool rnw; + bool pnu; + bool ind; + } f_uut; + struct SSIDInfo { + uint32_t ssid; + bool ssv; + } c_bad_streamid; + struct SSIDAddrInfo { + uint32_t ssid; + bool ssv; + dma_addr_t addr; + } f_ste_fetch; + struct SSIDInfo c_bad_ste; + struct { + dma_addr_t addr; + bool rnw; + } f_transl_forbidden; + struct { + uint32_t ssid; + } c_bad_substream; + struct SSIDAddrInfo f_cd_fetch; + struct SSIDInfo c_bad_cd; + struct FullInfo { + bool stall; + uint16_t stag; + uint32_t ssid; + bool ssv; + bool s2; + dma_addr_t addr; + bool rnw; + bool pnu; + bool ind; + uint8_t class; + dma_addr_t addr2; + } f_walk_eabt; + struct FullInfo f_translation; + struct FullInfo f_addr_size; + struct FullInfo f_access; + struct FullInfo f_permission; + struct SSIDInfo f_cfg_conflict; + /** + * not supported yet: + * F_BAD_ATS_TREQ + * F_BAD_ATS_TREQ + * F_TLB_CONFLICT + * E_PAGE_REQUEST + * IMPDEF_EVENTn + */ + } u; +} SMMUEventInfo; + +/* EVTQ fields */ + +#define EVT_Q_OVERFLOW (1 << 31) + +#define EVT_SET_TYPE(x, v) deposit32((x)->word[0], 0 , 8 , v) +#define EVT_SET_SSV(x, v) deposit32((x)->word[0], 11, 1 , v) +#define EVT_SET_SSID(x, v) deposit32((x)->word[0], 12, 20, v) +#define EVT_SET_SID(x, v) ((x)->word[1] = v) +#define EVT_SET_STAG(x, v) deposit32((x)->word[2], 0 , 16, v) +#define EVT_SET_STALL(x, v) deposit32((x)->word[2], 31, 1 , v) +#define EVT_SET_PNU(x, v) deposit32((x)->word[3], 1 , 1 , v) +#define EVT_SET_IND(x, v) deposit32((x)->word[3], 2 , 1 , v) +#define EVT_SET_RNW(x, v) deposit32((x)->word[3], 3 , 1 , v) +#define EVT_SET_S2(x, v) deposit32((x)->word[3], 7 , 1 , v) +#define EVT_SET_CLASS(x, v) deposit32((x)->word[3], 8 , 2 , v) +#define EVT_SET_ADDR(x, addr) \ + do { \ + (x)->word[5] = (uint32_t)(addr >> 32); \ + (x)->word[4] = (uint32_t)(addr & 0xffffffff); \ + } while (0) +#define EVT_SET_ADDR2(x, addr) \ + do { \ + deposit32((x)->word[7], 3, 29, addr >> 16); \ + deposit32((x)->word[7], 0, 16, addr & 0xffff);\ + } while (0) + +void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event); + +/* Configuration Data */ + +/* STE Level 1 Descriptor */ +typedef struct STEDesc { + uint32_t word[2]; +} STEDesc; + +/* CD Level 1 Descriptor */ +typedef struct CDDesc { + uint32_t word[2]; +} CDDesc; + +/* Stream Table Entry(STE) */ +typedef struct STE { + uint32_t word[16]; +} STE; + +/* Context Descriptor(CD) */ +typedef struct CD { + uint32_t word[16]; +} CD; + +/* STE fields */ + +#define STE_VALID(x) extract32((x)->word[0], 0, 1) + +#define STE_CONFIG(x) extract32((x)->word[0], 1, 3) +#define STE_CFG_S1_ENABLED(config) (config & 0x1) +#define STE_CFG_S2_ENABLED(config) (config & 0x2) +#define STE_CFG_ABORT(config) (!(config & 0x4)) +#define STE_CFG_BYPASS(config) (config == 0x4) + +#define STE_S1FMT(x) extract32((x)->word[0], 4 , 2) +#define STE_S1CDMAX(x) extract32((x)->word[1], 27, 5) +#define STE_S1STALLD(x) extract32((x)->word[2], 27, 1) +#define STE_EATS(x) extract32((x)->word[2], 28, 2) +#define STE_STRW(x) extract32((x)->word[2], 30, 2) +#define STE_S2VMID(x) extract32((x)->word[4], 0 , 16) +#define STE_S2T0SZ(x) extract32((x)->word[5], 0 , 6) +#define STE_S2SL0(x) extract32((x)->word[5], 6 , 2) +#define STE_S2TG(x) extract32((x)->word[5], 14, 2) +#define STE_S2PS(x) extract32((x)->word[5], 16, 3) +#define STE_S2AA64(x) extract32((x)->word[5], 19, 1) +#define STE_S2HD(x) extract32((x)->word[5], 24, 1) +#define STE_S2HA(x) extract32((x)->word[5], 25, 1) +#define STE_S2S(x) extract32((x)->word[5], 26, 1) +#define STE_CTXPTR(x) \ + ({ \ + unsigned long addr; \ + addr = (uint64_t)extract32((x)->word[1], 0, 16) << 32; \ + addr |= (uint64_t)((x)->word[0] & 0xffffffc0); \ + addr; \ + }) + +#define STE_S2TTB(x) \ + ({ \ + unsigned long addr; \ + addr = (uint64_t)extract32((x)->word[7], 0, 16) << 32; \ + addr |= (uint64_t)((x)->word[6] & 0xfffffff0); \ + addr; \ + }) + +static inline int oas2bits(int oas_field) +{ + switch (oas_field) { + case 0: + return 32; + case 1: + return 36; + case 2: + return 40; + case 3: + return 42; + case 4: + return 44; + case 5: + return 48; + } + return -1; +} + +static inline int pa_range(STE *ste) +{ + int oas_field = MIN(STE_S2PS(ste), SMMU_IDR5_OAS); + + if (!STE_S2AA64(ste)) { + return 40; + } + + return oas2bits(oas_field); +} + +#define MAX_PA(ste) ((1 << pa_range(ste)) - 1) + +/* CD fields */ + +#define CD_VALID(x) extract32((x)->word[0], 30, 1) +#define CD_ASID(x) extract32((x)->word[1], 16, 16) +#define CD_TTB(x, sel) \ + ({ \ + uint64_t hi, lo; \ + hi = extract32((x)->word[(sel) * 2 + 3], 0, 19); \ + hi <<= 32; \ + lo = (x)->word[(sel) * 2 + 2] & ~0xfULL; \ + hi | lo; \ + }) + +#define CD_TSZ(x, sel) extract32((x)->word[0], (16 * (sel)) + 0, 6) +#define CD_TG(x, sel) extract32((x)->word[0], (16 * (sel)) + 6, 2) +#define CD_EPD(x, sel) extract32((x)->word[0], (16 * (sel)) + 14, 1) +#define CD_ENDI(x) extract32((x)->word[0], 15, 1) +#define CD_IPS(x) extract32((x)->word[1], 0 , 3) +#define CD_TBI(x) extract32((x)->word[1], 6 , 2) +#define CD_HD(x) extract32((x)->word[1], 10 , 1) +#define CD_HA(x) extract32((x)->word[1], 11 , 1) +#define CD_S(x) extract32((x)->word[1], 12, 1) +#define CD_R(x) extract32((x)->word[1], 13, 1) +#define CD_A(x) extract32((x)->word[1], 14, 1) +#define CD_AARCH64(x) extract32((x)->word[1], 9 , 1) + +#define CDM_VALID(x) ((x)->word[0] & 0x1) + +static inline int is_cd_valid(SMMUv3State *s, STE *ste, CD *cd) +{ + return CD_VALID(cd); +} + +/** + * tg2granule - Decodes the CD translation granule size field according + * to the ttbr in use + * @bits: TG0/1 fields + * @ttbr: ttbr index in use + */ +static inline int tg2granule(int bits, int ttbr) +{ + switch (bits) { + case 0: + return ttbr ? 0 : 12; + case 1: + return ttbr ? 14 : 16; + case 2: + return ttbr ? 12 : 14; + case 3: + return ttbr ? 16 : 0; + default: + return 0; + } +} + +static inline uint64_t l1std_l2ptr(STEDesc *desc) +{ + uint64_t hi, lo; + + hi = desc->word[1]; + lo = desc->word[0] & ~0x1fULL; + return hi << 32 | lo; +} + +#define L1STD_SPAN(stm) (extract32((stm)->word[0], 0, 4)) + +#endif diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c new file mode 100644 index 0000000000..bb6a24e9b8 --- /dev/null +++ b/hw/arm/smmuv3.c @@ -0,0 +1,1538 @@ +/* + * Copyright (C) 2014-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/qdev-core.h" +#include "hw/pci/pci.h" +#include "exec/address-spaces.h" +#include "cpu.h" +#include "trace.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" + +#include "hw/arm/smmuv3.h" +#include "smmuv3-internal.h" + +/** + * smmuv3_trigger_irq - pulse @irq if enabled and update + * GERROR register in case of GERROR interrupt + * + * @irq: irq type + * @gerror_mask: mask of gerrors to toggle (relevant if @irq is GERROR) + */ +static void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, + uint32_t gerror_mask) +{ + + bool pulse = false; + + switch (irq) { + case SMMU_IRQ_EVTQ: + pulse = smmuv3_eventq_irq_enabled(s); + break; + case SMMU_IRQ_PRIQ: + qemu_log_mask(LOG_UNIMP, "PRI not yet supported\n"); + break; + case SMMU_IRQ_CMD_SYNC: + pulse = true; + break; + case SMMU_IRQ_GERROR: + { + uint32_t pending = s->gerror ^ s->gerrorn; + uint32_t new_gerrors = ~pending & gerror_mask; + + if (!new_gerrors) { + /* only toggle non pending errors */ + return; + } + s->gerror ^= new_gerrors; + trace_smmuv3_write_gerror(new_gerrors, s->gerror); + + pulse = smmuv3_gerror_irq_enabled(s); + break; + } + } + if (pulse) { + trace_smmuv3_trigger_irq(irq); + qemu_irq_pulse(s->irq[irq]); + } +} + +static void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn) +{ + uint32_t pending = s->gerror ^ s->gerrorn; + uint32_t toggled = s->gerrorn ^ new_gerrorn; + + if (toggled & ~pending) { + qemu_log_mask(LOG_GUEST_ERROR, + "guest toggles non pending errors = 0x%x\n", + toggled & ~pending); + } + + /* + * We do not raise any error in case guest toggles bits corresponding + * to not active IRQs (CONSTRAINED UNPREDICTABLE) + */ + s->gerrorn = new_gerrorn; + + trace_smmuv3_write_gerrorn(toggled & pending, s->gerrorn); +} + +static inline MemTxResult queue_read(SMMUQueue *q, void *data) +{ + dma_addr_t addr = Q_CONS_ENTRY(q); + + return dma_memory_read(&address_space_memory, addr, data, q->entry_size); +} + +static MemTxResult queue_write(SMMUQueue *q, void *data) +{ + dma_addr_t addr = Q_PROD_ENTRY(q); + MemTxResult ret; + + ret = dma_memory_write(&address_space_memory, addr, data, q->entry_size); + if (ret != MEMTX_OK) { + return ret; + } + + queue_prod_incr(q); + return MEMTX_OK; +} + +static MemTxResult smmuv3_write_eventq(SMMUv3State *s, Evt *evt) +{ + SMMUQueue *q = &s->eventq; + MemTxResult r; + + if (!smmuv3_eventq_enabled(s)) { + return MEMTX_ERROR; + } + + if (smmuv3_q_full(q)) { + return MEMTX_ERROR; + } + + r = queue_write(q, evt); + if (r != MEMTX_OK) { + return r; + } + + if (smmuv3_q_empty(q)) { + smmuv3_trigger_irq(s, SMMU_IRQ_EVTQ, 0); + } + return MEMTX_OK; +} + +void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info) +{ + Evt evt = {}; + MemTxResult r; + + if (!smmuv3_eventq_enabled(s)) { + return; + } + + EVT_SET_TYPE(&evt, info->type); + EVT_SET_SID(&evt, info->sid); + + switch (info->type) { + case SMMU_EVT_NONE: + return; + case SMMU_EVT_F_UUT: + EVT_SET_SSID(&evt, info->u.f_uut.ssid); + EVT_SET_SSV(&evt, info->u.f_uut.ssv); + EVT_SET_ADDR(&evt, info->u.f_uut.addr); + EVT_SET_RNW(&evt, info->u.f_uut.rnw); + EVT_SET_PNU(&evt, info->u.f_uut.pnu); + EVT_SET_IND(&evt, info->u.f_uut.ind); + break; + case SMMU_EVT_C_BAD_STREAMID: + EVT_SET_SSID(&evt, info->u.c_bad_streamid.ssid); + EVT_SET_SSV(&evt, info->u.c_bad_streamid.ssv); + break; + case SMMU_EVT_F_STE_FETCH: + EVT_SET_SSID(&evt, info->u.f_ste_fetch.ssid); + EVT_SET_SSV(&evt, info->u.f_ste_fetch.ssv); + EVT_SET_ADDR(&evt, info->u.f_ste_fetch.addr); + break; + case SMMU_EVT_C_BAD_STE: + EVT_SET_SSID(&evt, info->u.c_bad_ste.ssid); + EVT_SET_SSV(&evt, info->u.c_bad_ste.ssv); + break; + case SMMU_EVT_F_STREAM_DISABLED: + break; + case SMMU_EVT_F_TRANS_FORBIDDEN: + EVT_SET_ADDR(&evt, info->u.f_transl_forbidden.addr); + EVT_SET_RNW(&evt, info->u.f_transl_forbidden.rnw); + break; + case SMMU_EVT_C_BAD_SUBSTREAMID: + EVT_SET_SSID(&evt, info->u.c_bad_substream.ssid); + break; + case SMMU_EVT_F_CD_FETCH: + EVT_SET_SSID(&evt, info->u.f_cd_fetch.ssid); + EVT_SET_SSV(&evt, info->u.f_cd_fetch.ssv); + EVT_SET_ADDR(&evt, info->u.f_cd_fetch.addr); + break; + case SMMU_EVT_C_BAD_CD: + EVT_SET_SSID(&evt, info->u.c_bad_cd.ssid); + EVT_SET_SSV(&evt, info->u.c_bad_cd.ssv); + break; + case SMMU_EVT_F_WALK_EABT: + case SMMU_EVT_F_TRANSLATION: + case SMMU_EVT_F_ADDR_SIZE: + case SMMU_EVT_F_ACCESS: + case SMMU_EVT_F_PERMISSION: + EVT_SET_STALL(&evt, info->u.f_walk_eabt.stall); + EVT_SET_STAG(&evt, info->u.f_walk_eabt.stag); + EVT_SET_SSID(&evt, info->u.f_walk_eabt.ssid); + EVT_SET_SSV(&evt, info->u.f_walk_eabt.ssv); + EVT_SET_S2(&evt, info->u.f_walk_eabt.s2); + EVT_SET_ADDR(&evt, info->u.f_walk_eabt.addr); + EVT_SET_RNW(&evt, info->u.f_walk_eabt.rnw); + EVT_SET_PNU(&evt, info->u.f_walk_eabt.pnu); + EVT_SET_IND(&evt, info->u.f_walk_eabt.ind); + EVT_SET_CLASS(&evt, info->u.f_walk_eabt.class); + EVT_SET_ADDR2(&evt, info->u.f_walk_eabt.addr2); + break; + case SMMU_EVT_F_CFG_CONFLICT: + EVT_SET_SSID(&evt, info->u.f_cfg_conflict.ssid); + EVT_SET_SSV(&evt, info->u.f_cfg_conflict.ssv); + break; + /* rest is not implemented */ + case SMMU_EVT_F_BAD_ATS_TREQ: + case SMMU_EVT_F_TLB_CONFLICT: + case SMMU_EVT_E_PAGE_REQ: + default: + g_assert_not_reached(); + } + + trace_smmuv3_record_event(smmu_event_string(info->type), info->sid); + r = smmuv3_write_eventq(s, &evt); + if (r != MEMTX_OK) { + smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_EVENTQ_ABT_ERR_MASK); + } + info->recorded = true; +} + +static void smmuv3_init_regs(SMMUv3State *s) +{ + /** + * IDR0: stage1 only, AArch64 only, coherent access, 16b ASID, + * multi-level stream table + */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1); /* stage 1 supported */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTF, 2); /* AArch64 PTW only */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, COHACC, 1); /* IO coherent */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ASID16, 1); /* 16-bit ASID */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTENDIAN, 2); /* little endian */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, STALL_MODEL, 1); /* No stall */ + /* terminated transaction will always be aborted/error returned */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TERM_MODEL, 1); + /* 2-level stream table supported */ + s->idr[0] = FIELD_DP32(s->idr[0], IDR0, STLEVEL, 1); + + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, SIDSIZE, SMMU_IDR1_SIDSIZE); + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, EVENTQS, SMMU_EVENTQS); + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS, SMMU_CMDQS); + + /* 4K and 64K granule support */ + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN4K, 1); + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN64K, 1); + s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS); /* 44 bits */ + + s->cmdq.base = deposit64(s->cmdq.base, 0, 5, SMMU_CMDQS); + s->cmdq.prod = 0; + s->cmdq.cons = 0; + s->cmdq.entry_size = sizeof(struct Cmd); + s->eventq.base = deposit64(s->eventq.base, 0, 5, SMMU_EVENTQS); + s->eventq.prod = 0; + s->eventq.cons = 0; + s->eventq.entry_size = sizeof(struct Evt); + + s->features = 0; + s->sid_split = 0; +} + +static int smmu_get_ste(SMMUv3State *s, dma_addr_t addr, STE *buf, + SMMUEventInfo *event) +{ + int ret; + + trace_smmuv3_get_ste(addr); + /* TODO: guarantee 64-bit single-copy atomicity */ + ret = dma_memory_read(&address_space_memory, addr, + (void *)buf, sizeof(*buf)); + if (ret != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "Cannot fetch pte at address=0x%"PRIx64"\n", addr); + event->type = SMMU_EVT_F_STE_FETCH; + event->u.f_ste_fetch.addr = addr; + return -EINVAL; + } + return 0; + +} + +/* @ssid > 0 not supported yet */ +static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid, + CD *buf, SMMUEventInfo *event) +{ + dma_addr_t addr = STE_CTXPTR(ste); + int ret; + + trace_smmuv3_get_cd(addr); + /* TODO: guarantee 64-bit single-copy atomicity */ + ret = dma_memory_read(&address_space_memory, addr, + (void *)buf, sizeof(*buf)); + if (ret != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "Cannot fetch pte at address=0x%"PRIx64"\n", addr); + event->type = SMMU_EVT_F_CD_FETCH; + event->u.f_ste_fetch.addr = addr; + return -EINVAL; + } + return 0; +} + +/* Returns < 0 in case of invalid STE, 0 otherwise */ +static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg, + STE *ste, SMMUEventInfo *event) +{ + uint32_t config; + + if (!STE_VALID(ste)) { + goto bad_ste; + } + + config = STE_CONFIG(ste); + + if (STE_CFG_ABORT(config)) { + cfg->aborted = true; + return 0; + } + + if (STE_CFG_BYPASS(config)) { + cfg->bypassed = true; + return 0; + } + + if (STE_CFG_S2_ENABLED(config)) { + qemu_log_mask(LOG_UNIMP, "SMMUv3 does not support stage 2 yet\n"); + goto bad_ste; + } + + if (STE_S1CDMAX(ste) != 0) { + qemu_log_mask(LOG_UNIMP, + "SMMUv3 does not support multiple context descriptors yet\n"); + goto bad_ste; + } + + if (STE_S1STALLD(ste)) { + qemu_log_mask(LOG_UNIMP, + "SMMUv3 S1 stalling fault model not allowed yet\n"); + goto bad_ste; + } + return 0; + +bad_ste: + event->type = SMMU_EVT_C_BAD_STE; + return -EINVAL; +} + +/** + * smmu_find_ste - Return the stream table entry associated + * to the sid + * + * @s: smmuv3 handle + * @sid: stream ID + * @ste: returned stream table entry + * @event: handle to an event info + * + * Supports linear and 2-level stream table + * Return 0 on success, -EINVAL otherwise + */ +static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, + SMMUEventInfo *event) +{ + dma_addr_t addr; + int ret; + + trace_smmuv3_find_ste(sid, s->features, s->sid_split); + /* Check SID range */ + if (sid > (1 << SMMU_IDR1_SIDSIZE)) { + event->type = SMMU_EVT_C_BAD_STREAMID; + return -EINVAL; + } + if (s->features & SMMU_FEATURE_2LVL_STE) { + int l1_ste_offset, l2_ste_offset, max_l2_ste, span; + dma_addr_t strtab_base, l1ptr, l2ptr; + STEDesc l1std; + + strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK; + l1_ste_offset = sid >> s->sid_split; + l2_ste_offset = sid & ((1 << s->sid_split) - 1); + l1ptr = (dma_addr_t)(strtab_base + l1_ste_offset * sizeof(l1std)); + /* TODO: guarantee 64-bit single-copy atomicity */ + ret = dma_memory_read(&address_space_memory, l1ptr, + (uint8_t *)&l1std, sizeof(l1std)); + if (ret != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "Could not read L1PTR at 0X%"PRIx64"\n", l1ptr); + event->type = SMMU_EVT_F_STE_FETCH; + event->u.f_ste_fetch.addr = l1ptr; + return -EINVAL; + } + + span = L1STD_SPAN(&l1std); + + if (!span) { + /* l2ptr is not valid */ + qemu_log_mask(LOG_GUEST_ERROR, + "invalid sid=%d (L1STD span=0)\n", sid); + event->type = SMMU_EVT_C_BAD_STREAMID; + return -EINVAL; + } + max_l2_ste = (1 << span) - 1; + l2ptr = l1std_l2ptr(&l1std); + trace_smmuv3_find_ste_2lvl(s->strtab_base, l1ptr, l1_ste_offset, + l2ptr, l2_ste_offset, max_l2_ste); + if (l2_ste_offset > max_l2_ste) { + qemu_log_mask(LOG_GUEST_ERROR, + "l2_ste_offset=%d > max_l2_ste=%d\n", + l2_ste_offset, max_l2_ste); + event->type = SMMU_EVT_C_BAD_STE; + return -EINVAL; + } + addr = l2ptr + l2_ste_offset * sizeof(*ste); + } else { + addr = s->strtab_base + sid * sizeof(*ste); + } + + if (smmu_get_ste(s, addr, ste, event)) { + return -EINVAL; + } + + return 0; +} + +static int decode_cd(SMMUTransCfg *cfg, CD *cd, SMMUEventInfo *event) +{ + int ret = -EINVAL; + int i; + + if (!CD_VALID(cd) || !CD_AARCH64(cd)) { + goto bad_cd; + } + if (!CD_A(cd)) { + goto bad_cd; /* SMMU_IDR0.TERM_MODEL == 1 */ + } + if (CD_S(cd)) { + goto bad_cd; /* !STE_SECURE && SMMU_IDR0.STALL_MODEL == 1 */ + } + if (CD_HA(cd) || CD_HD(cd)) { + goto bad_cd; /* HTTU = 0 */ + } + + /* we support only those at the moment */ + cfg->aa64 = true; + cfg->stage = 1; + + cfg->oas = oas2bits(CD_IPS(cd)); + cfg->oas = MIN(oas2bits(SMMU_IDR5_OAS), cfg->oas); + cfg->tbi = CD_TBI(cd); + cfg->asid = CD_ASID(cd); + + trace_smmuv3_decode_cd(cfg->oas); + + /* decode data dependent on TT */ + for (i = 0; i <= 1; i++) { + int tg, tsz; + SMMUTransTableInfo *tt = &cfg->tt[i]; + + cfg->tt[i].disabled = CD_EPD(cd, i); + if (cfg->tt[i].disabled) { + continue; + } + + tsz = CD_TSZ(cd, i); + if (tsz < 16 || tsz > 39) { + goto bad_cd; + } + + tg = CD_TG(cd, i); + tt->granule_sz = tg2granule(tg, i); + if ((tt->granule_sz != 12 && tt->granule_sz != 16) || CD_ENDI(cd)) { + goto bad_cd; + } + + tt->tsz = tsz; + tt->ttb = CD_TTB(cd, i); + if (tt->ttb & ~(MAKE_64BIT_MASK(0, cfg->oas))) { + goto bad_cd; + } + trace_smmuv3_decode_cd_tt(i, tt->tsz, tt->ttb, tt->granule_sz); + } + + event->record_trans_faults = CD_R(cd); + + return 0; + +bad_cd: + event->type = SMMU_EVT_C_BAD_CD; + return ret; +} + +/** + * smmuv3_decode_config - Prepare the translation configuration + * for the @mr iommu region + * @mr: iommu memory region the translation config must be prepared for + * @cfg: output translation configuration which is populated through + * the different configuration decoding steps + * @event: must be zero'ed by the caller + * + * return < 0 in case of config decoding error (@event is filled + * accordingly). Return 0 otherwise. + */ +static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg, + SMMUEventInfo *event) +{ + SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); + uint32_t sid = smmu_get_sid(sdev); + SMMUv3State *s = sdev->smmu; + int ret; + STE ste; + CD cd; + + ret = smmu_find_ste(s, sid, &ste, event); + if (ret) { + return ret; + } + + ret = decode_ste(s, cfg, &ste, event); + if (ret) { + return ret; + } + + if (cfg->aborted || cfg->bypassed) { + return 0; + } + + ret = smmu_get_cd(s, &ste, 0 /* ssid */, &cd, event); + if (ret) { + return ret; + } + + return decode_cd(cfg, &cd, event); +} + +/** + * smmuv3_get_config - Look up for a cached copy of configuration data for + * @sdev and on cache miss performs a configuration structure decoding from + * guest RAM. + * + * @sdev: SMMUDevice handle + * @event: output event info + * + * The configuration cache contains data resulting from both STE and CD + * decoding under the form of an SMMUTransCfg struct. The hash table is indexed + * by the SMMUDevice handle. + */ +static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event) +{ + SMMUv3State *s = sdev->smmu; + SMMUState *bc = &s->smmu_state; + SMMUTransCfg *cfg; + + cfg = g_hash_table_lookup(bc->configs, sdev); + if (cfg) { + sdev->cfg_cache_hits++; + trace_smmuv3_config_cache_hit(smmu_get_sid(sdev), + sdev->cfg_cache_hits, sdev->cfg_cache_misses, + 100 * sdev->cfg_cache_hits / + (sdev->cfg_cache_hits + sdev->cfg_cache_misses)); + } else { + sdev->cfg_cache_misses++; + trace_smmuv3_config_cache_miss(smmu_get_sid(sdev), + sdev->cfg_cache_hits, sdev->cfg_cache_misses, + 100 * sdev->cfg_cache_hits / + (sdev->cfg_cache_hits + sdev->cfg_cache_misses)); + cfg = g_new0(SMMUTransCfg, 1); + + if (!smmuv3_decode_config(&sdev->iommu, cfg, event)) { + g_hash_table_insert(bc->configs, sdev, cfg); + } else { + g_free(cfg); + cfg = NULL; + } + } + return cfg; +} + +static void smmuv3_flush_config(SMMUDevice *sdev) +{ + SMMUv3State *s = sdev->smmu; + SMMUState *bc = &s->smmu_state; + + trace_smmuv3_config_cache_inv(smmu_get_sid(sdev)); + g_hash_table_remove(bc->configs, sdev); +} + +static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr, + IOMMUAccessFlags flag, int iommu_idx) +{ + SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); + SMMUv3State *s = sdev->smmu; + uint32_t sid = smmu_get_sid(sdev); + SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid}; + SMMUPTWEventInfo ptw_info = {}; + SMMUTranslationStatus status; + SMMUState *bs = ARM_SMMU(s); + uint64_t page_mask, aligned_addr; + IOMMUTLBEntry *cached_entry = NULL; + SMMUTransTableInfo *tt; + SMMUTransCfg *cfg = NULL; + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = addr, + .addr_mask = ~(hwaddr)0, + .perm = IOMMU_NONE, + }; + SMMUIOTLBKey key, *new_key; + + qemu_mutex_lock(&s->mutex); + + if (!smmu_enabled(s)) { + status = SMMU_TRANS_DISABLE; + goto epilogue; + } + + cfg = smmuv3_get_config(sdev, &event); + if (!cfg) { + status = SMMU_TRANS_ERROR; + goto epilogue; + } + + if (cfg->aborted) { + status = SMMU_TRANS_ABORT; + goto epilogue; + } + + if (cfg->bypassed) { + status = SMMU_TRANS_BYPASS; + goto epilogue; + } + + tt = select_tt(cfg, addr); + if (!tt) { + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_TRANSLATION; + event.u.f_translation.addr = addr; + event.u.f_translation.rnw = flag & 0x1; + } + status = SMMU_TRANS_ERROR; + goto epilogue; + } + + page_mask = (1ULL << (tt->granule_sz)) - 1; + aligned_addr = addr & ~page_mask; + + key.asid = cfg->asid; + key.iova = aligned_addr; + + cached_entry = g_hash_table_lookup(bs->iotlb, &key); + if (cached_entry) { + cfg->iotlb_hits++; + trace_smmu_iotlb_cache_hit(cfg->asid, aligned_addr, + cfg->iotlb_hits, cfg->iotlb_misses, + 100 * cfg->iotlb_hits / + (cfg->iotlb_hits + cfg->iotlb_misses)); + if ((flag & IOMMU_WO) && !(cached_entry->perm & IOMMU_WO)) { + status = SMMU_TRANS_ERROR; + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_PERMISSION; + event.u.f_permission.addr = addr; + event.u.f_permission.rnw = flag & 0x1; + } + } else { + status = SMMU_TRANS_SUCCESS; + } + goto epilogue; + } + + cfg->iotlb_misses++; + trace_smmu_iotlb_cache_miss(cfg->asid, addr & ~page_mask, + cfg->iotlb_hits, cfg->iotlb_misses, + 100 * cfg->iotlb_hits / + (cfg->iotlb_hits + cfg->iotlb_misses)); + + if (g_hash_table_size(bs->iotlb) >= SMMU_IOTLB_MAX_SIZE) { + smmu_iotlb_inv_all(bs); + } + + cached_entry = g_new0(IOMMUTLBEntry, 1); + + if (smmu_ptw(cfg, aligned_addr, flag, cached_entry, &ptw_info)) { + g_free(cached_entry); + switch (ptw_info.type) { + case SMMU_PTW_ERR_WALK_EABT: + event.type = SMMU_EVT_F_WALK_EABT; + event.u.f_walk_eabt.addr = addr; + event.u.f_walk_eabt.rnw = flag & 0x1; + event.u.f_walk_eabt.class = 0x1; + event.u.f_walk_eabt.addr2 = ptw_info.addr; + break; + case SMMU_PTW_ERR_TRANSLATION: + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_TRANSLATION; + event.u.f_translation.addr = addr; + event.u.f_translation.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_ADDR_SIZE: + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_ADDR_SIZE; + event.u.f_addr_size.addr = addr; + event.u.f_addr_size.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_ACCESS: + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_ACCESS; + event.u.f_access.addr = addr; + event.u.f_access.rnw = flag & 0x1; + } + break; + case SMMU_PTW_ERR_PERMISSION: + if (event.record_trans_faults) { + event.type = SMMU_EVT_F_PERMISSION; + event.u.f_permission.addr = addr; + event.u.f_permission.rnw = flag & 0x1; + } + break; + default: + g_assert_not_reached(); + } + status = SMMU_TRANS_ERROR; + } else { + new_key = g_new0(SMMUIOTLBKey, 1); + new_key->asid = cfg->asid; + new_key->iova = aligned_addr; + g_hash_table_insert(bs->iotlb, new_key, cached_entry); + status = SMMU_TRANS_SUCCESS; + } + +epilogue: + qemu_mutex_unlock(&s->mutex); + switch (status) { + case SMMU_TRANS_SUCCESS: + entry.perm = flag; + entry.translated_addr = cached_entry->translated_addr + + (addr & page_mask); + entry.addr_mask = cached_entry->addr_mask; + trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr, + entry.translated_addr, entry.perm); + break; + case SMMU_TRANS_DISABLE: + entry.perm = flag; + entry.addr_mask = ~TARGET_PAGE_MASK; + trace_smmuv3_translate_disable(mr->parent_obj.name, sid, addr, + entry.perm); + break; + case SMMU_TRANS_BYPASS: + entry.perm = flag; + entry.addr_mask = ~TARGET_PAGE_MASK; + trace_smmuv3_translate_bypass(mr->parent_obj.name, sid, addr, + entry.perm); + break; + case SMMU_TRANS_ABORT: + /* no event is recorded on abort */ + trace_smmuv3_translate_abort(mr->parent_obj.name, sid, addr, + entry.perm); + break; + case SMMU_TRANS_ERROR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s translation failed for iova=0x%"PRIx64"(%s)\n", + mr->parent_obj.name, addr, smmu_event_string(event.type)); + smmuv3_record_event(s, &event); + break; + } + + return entry; +} + +/** + * smmuv3_notify_iova - call the notifier @n for a given + * @asid and @iova tuple. + * + * @mr: IOMMU mr region handle + * @n: notifier to be called + * @asid: address space ID or negative value if we don't care + * @iova: iova + */ +static void smmuv3_notify_iova(IOMMUMemoryRegion *mr, + IOMMUNotifier *n, + int asid, + dma_addr_t iova) +{ + SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu); + SMMUEventInfo event = {}; + SMMUTransTableInfo *tt; + SMMUTransCfg *cfg; + IOMMUTLBEntry entry; + + cfg = smmuv3_get_config(sdev, &event); + if (!cfg) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s error decoding the configuration for iommu mr=%s\n", + __func__, mr->parent_obj.name); + return; + } + + if (asid >= 0 && cfg->asid != asid) { + return; + } + + tt = select_tt(cfg, iova); + if (!tt) { + return; + } + + entry.target_as = &address_space_memory; + entry.iova = iova; + entry.addr_mask = (1 << tt->granule_sz) - 1; + entry.perm = IOMMU_NONE; + + memory_region_notify_one(n, &entry); +} + +/* invalidate an asid/iova tuple in all mr's */ +static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova) +{ + SMMUNotifierNode *node; + + QLIST_FOREACH(node, &s->notifiers_list, next) { + IOMMUMemoryRegion *mr = &node->sdev->iommu; + IOMMUNotifier *n; + + trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova); + + IOMMU_NOTIFIER_FOREACH(n, mr) { + smmuv3_notify_iova(mr, n, asid, iova); + } + } +} + +static int smmuv3_cmdq_consume(SMMUv3State *s) +{ + SMMUState *bs = ARM_SMMU(s); + SMMUCmdError cmd_error = SMMU_CERROR_NONE; + SMMUQueue *q = &s->cmdq; + SMMUCommandType type = 0; + + if (!smmuv3_cmdq_enabled(s)) { + return 0; + } + /* + * some commands depend on register values, typically CR0. In case those + * register values change while handling the command, spec says it + * is UNPREDICTABLE whether the command is interpreted under the new + * or old value. + */ + + while (!smmuv3_q_empty(q)) { + uint32_t pending = s->gerror ^ s->gerrorn; + Cmd cmd; + + trace_smmuv3_cmdq_consume(Q_PROD(q), Q_CONS(q), + Q_PROD_WRAP(q), Q_CONS_WRAP(q)); + + if (FIELD_EX32(pending, GERROR, CMDQ_ERR)) { + break; + } + + if (queue_read(q, &cmd) != MEMTX_OK) { + cmd_error = SMMU_CERROR_ABT; + break; + } + + type = CMD_TYPE(&cmd); + + trace_smmuv3_cmdq_opcode(smmu_cmd_string(type)); + + qemu_mutex_lock(&s->mutex); + switch (type) { + case SMMU_CMD_SYNC: + if (CMD_SYNC_CS(&cmd) & CMD_SYNC_SIG_IRQ) { + smmuv3_trigger_irq(s, SMMU_IRQ_CMD_SYNC, 0); + } + break; + case SMMU_CMD_PREFETCH_CONFIG: + case SMMU_CMD_PREFETCH_ADDR: + break; + case SMMU_CMD_CFGI_STE: + { + uint32_t sid = CMD_SID(&cmd); + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); + SMMUDevice *sdev; + + if (CMD_SSEC(&cmd)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + + if (!mr) { + break; + } + + trace_smmuv3_cmdq_cfgi_ste(sid); + sdev = container_of(mr, SMMUDevice, iommu); + smmuv3_flush_config(sdev); + + break; + } + case SMMU_CMD_CFGI_STE_RANGE: /* same as SMMU_CMD_CFGI_ALL */ + { + uint32_t start = CMD_SID(&cmd), end, i; + uint8_t range = CMD_STE_RANGE(&cmd); + + if (CMD_SSEC(&cmd)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + + end = start + (1 << (range + 1)) - 1; + trace_smmuv3_cmdq_cfgi_ste_range(start, end); + + for (i = start; i <= end; i++) { + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, i); + SMMUDevice *sdev; + + if (!mr) { + continue; + } + sdev = container_of(mr, SMMUDevice, iommu); + smmuv3_flush_config(sdev); + } + break; + } + case SMMU_CMD_CFGI_CD: + case SMMU_CMD_CFGI_CD_ALL: + { + uint32_t sid = CMD_SID(&cmd); + IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid); + SMMUDevice *sdev; + + if (CMD_SSEC(&cmd)) { + cmd_error = SMMU_CERROR_ILL; + break; + } + + if (!mr) { + break; + } + + trace_smmuv3_cmdq_cfgi_cd(sid); + sdev = container_of(mr, SMMUDevice, iommu); + smmuv3_flush_config(sdev); + break; + } + case SMMU_CMD_TLBI_NH_ASID: + { + uint16_t asid = CMD_ASID(&cmd); + + trace_smmuv3_cmdq_tlbi_nh_asid(asid); + smmu_inv_notifiers_all(&s->smmu_state); + smmu_iotlb_inv_asid(bs, asid); + break; + } + case SMMU_CMD_TLBI_NH_ALL: + case SMMU_CMD_TLBI_NSNH_ALL: + trace_smmuv3_cmdq_tlbi_nh(); + smmu_inv_notifiers_all(&s->smmu_state); + smmu_iotlb_inv_all(bs); + break; + case SMMU_CMD_TLBI_NH_VAA: + { + dma_addr_t addr = CMD_ADDR(&cmd); + uint16_t vmid = CMD_VMID(&cmd); + + trace_smmuv3_cmdq_tlbi_nh_vaa(vmid, addr); + smmuv3_inv_notifiers_iova(bs, -1, addr); + smmu_iotlb_inv_all(bs); + break; + } + case SMMU_CMD_TLBI_NH_VA: + { + uint16_t asid = CMD_ASID(&cmd); + uint16_t vmid = CMD_VMID(&cmd); + dma_addr_t addr = CMD_ADDR(&cmd); + bool leaf = CMD_LEAF(&cmd); + + trace_smmuv3_cmdq_tlbi_nh_va(vmid, asid, addr, leaf); + smmuv3_inv_notifiers_iova(bs, asid, addr); + smmu_iotlb_inv_iova(bs, asid, addr); + break; + } + case SMMU_CMD_TLBI_EL3_ALL: + case SMMU_CMD_TLBI_EL3_VA: + case SMMU_CMD_TLBI_EL2_ALL: + case SMMU_CMD_TLBI_EL2_ASID: + case SMMU_CMD_TLBI_EL2_VA: + case SMMU_CMD_TLBI_EL2_VAA: + case SMMU_CMD_TLBI_S12_VMALL: + case SMMU_CMD_TLBI_S2_IPA: + case SMMU_CMD_ATC_INV: + case SMMU_CMD_PRI_RESP: + case SMMU_CMD_RESUME: + case SMMU_CMD_STALL_TERM: + trace_smmuv3_unhandled_cmd(type); + break; + default: + cmd_error = SMMU_CERROR_ILL; + qemu_log_mask(LOG_GUEST_ERROR, + "Illegal command type: %d\n", CMD_TYPE(&cmd)); + break; + } + qemu_mutex_unlock(&s->mutex); + if (cmd_error) { + break; + } + /* + * We only increment the cons index after the completion of + * the command. We do that because the SYNC returns immediately + * and does not check the completion of previous commands + */ + queue_cons_incr(q); + } + + if (cmd_error) { + trace_smmuv3_cmdq_consume_error(smmu_cmd_string(type), cmd_error); + smmu_write_cmdq_err(s, cmd_error); + smmuv3_trigger_irq(s, SMMU_IRQ_GERROR, R_GERROR_CMDQ_ERR_MASK); + } + + trace_smmuv3_cmdq_consume_out(Q_PROD(q), Q_CONS(q), + Q_PROD_WRAP(q), Q_CONS_WRAP(q)); + + return 0; +} + +static MemTxResult smmu_writell(SMMUv3State *s, hwaddr offset, + uint64_t data, MemTxAttrs attrs) +{ + switch (offset) { + case A_GERROR_IRQ_CFG0: + s->gerror_irq_cfg0 = data; + return MEMTX_OK; + case A_STRTAB_BASE: + s->strtab_base = data; + return MEMTX_OK; + case A_CMDQ_BASE: + s->cmdq.base = data; + s->cmdq.log2size = extract64(s->cmdq.base, 0, 5); + if (s->cmdq.log2size > SMMU_CMDQS) { + s->cmdq.log2size = SMMU_CMDQS; + } + return MEMTX_OK; + case A_EVENTQ_BASE: + s->eventq.base = data; + s->eventq.log2size = extract64(s->eventq.base, 0, 5); + if (s->eventq.log2size > SMMU_EVENTQS) { + s->eventq.log2size = SMMU_EVENTQS; + } + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG0: + s->eventq_irq_cfg0 = data; + return MEMTX_OK; + default: + qemu_log_mask(LOG_UNIMP, + "%s Unexpected 64-bit access to 0x%"PRIx64" (WI)\n", + __func__, offset); + return MEMTX_OK; + } +} + +static MemTxResult smmu_writel(SMMUv3State *s, hwaddr offset, + uint64_t data, MemTxAttrs attrs) +{ + switch (offset) { + case A_CR0: + s->cr[0] = data; + s->cr0ack = data & ~SMMU_CR0_RESERVED; + /* in case the command queue has been enabled */ + smmuv3_cmdq_consume(s); + return MEMTX_OK; + case A_CR1: + s->cr[1] = data; + return MEMTX_OK; + case A_CR2: + s->cr[2] = data; + return MEMTX_OK; + case A_IRQ_CTRL: + s->irq_ctrl = data; + return MEMTX_OK; + case A_GERRORN: + smmuv3_write_gerrorn(s, data); + /* + * By acknowledging the CMDQ_ERR, SW may notify cmds can + * be processed again + */ + smmuv3_cmdq_consume(s); + return MEMTX_OK; + case A_GERROR_IRQ_CFG0: /* 64b */ + s->gerror_irq_cfg0 = deposit64(s->gerror_irq_cfg0, 0, 32, data); + return MEMTX_OK; + case A_GERROR_IRQ_CFG0 + 4: + s->gerror_irq_cfg0 = deposit64(s->gerror_irq_cfg0, 32, 32, data); + return MEMTX_OK; + case A_GERROR_IRQ_CFG1: + s->gerror_irq_cfg1 = data; + return MEMTX_OK; + case A_GERROR_IRQ_CFG2: + s->gerror_irq_cfg2 = data; + return MEMTX_OK; + case A_STRTAB_BASE: /* 64b */ + s->strtab_base = deposit64(s->strtab_base, 0, 32, data); + return MEMTX_OK; + case A_STRTAB_BASE + 4: + s->strtab_base = deposit64(s->strtab_base, 32, 32, data); + return MEMTX_OK; + case A_STRTAB_BASE_CFG: + s->strtab_base_cfg = data; + if (FIELD_EX32(data, STRTAB_BASE_CFG, FMT) == 1) { + s->sid_split = FIELD_EX32(data, STRTAB_BASE_CFG, SPLIT); + s->features |= SMMU_FEATURE_2LVL_STE; + } + return MEMTX_OK; + case A_CMDQ_BASE: /* 64b */ + s->cmdq.base = deposit64(s->cmdq.base, 0, 32, data); + s->cmdq.log2size = extract64(s->cmdq.base, 0, 5); + if (s->cmdq.log2size > SMMU_CMDQS) { + s->cmdq.log2size = SMMU_CMDQS; + } + return MEMTX_OK; + case A_CMDQ_BASE + 4: /* 64b */ + s->cmdq.base = deposit64(s->cmdq.base, 32, 32, data); + return MEMTX_OK; + case A_CMDQ_PROD: + s->cmdq.prod = data; + smmuv3_cmdq_consume(s); + return MEMTX_OK; + case A_CMDQ_CONS: + s->cmdq.cons = data; + return MEMTX_OK; + case A_EVENTQ_BASE: /* 64b */ + s->eventq.base = deposit64(s->eventq.base, 0, 32, data); + s->eventq.log2size = extract64(s->eventq.base, 0, 5); + if (s->eventq.log2size > SMMU_EVENTQS) { + s->eventq.log2size = SMMU_EVENTQS; + } + return MEMTX_OK; + case A_EVENTQ_BASE + 4: + s->eventq.base = deposit64(s->eventq.base, 32, 32, data); + return MEMTX_OK; + case A_EVENTQ_PROD: + s->eventq.prod = data; + return MEMTX_OK; + case A_EVENTQ_CONS: + s->eventq.cons = data; + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG0: /* 64b */ + s->eventq_irq_cfg0 = deposit64(s->eventq_irq_cfg0, 0, 32, data); + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG0 + 4: + s->eventq_irq_cfg0 = deposit64(s->eventq_irq_cfg0, 32, 32, data); + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG1: + s->eventq_irq_cfg1 = data; + return MEMTX_OK; + case A_EVENTQ_IRQ_CFG2: + s->eventq_irq_cfg2 = data; + return MEMTX_OK; + default: + qemu_log_mask(LOG_UNIMP, + "%s Unexpected 32-bit access to 0x%"PRIx64" (WI)\n", + __func__, offset); + return MEMTX_OK; + } +} + +static MemTxResult smmu_write_mmio(void *opaque, hwaddr offset, uint64_t data, + unsigned size, MemTxAttrs attrs) +{ + SMMUState *sys = opaque; + SMMUv3State *s = ARM_SMMUV3(sys); + MemTxResult r; + + /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */ + offset &= ~0x10000; + + switch (size) { + case 8: + r = smmu_writell(s, offset, data, attrs); + break; + case 4: + r = smmu_writel(s, offset, data, attrs); + break; + default: + r = MEMTX_ERROR; + break; + } + + trace_smmuv3_write_mmio(offset, data, size, r); + return r; +} + +static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + switch (offset) { + case A_GERROR_IRQ_CFG0: + *data = s->gerror_irq_cfg0; + return MEMTX_OK; + case A_STRTAB_BASE: + *data = s->strtab_base; + return MEMTX_OK; + case A_CMDQ_BASE: + *data = s->cmdq.base; + return MEMTX_OK; + case A_EVENTQ_BASE: + *data = s->eventq.base; + return MEMTX_OK; + default: + *data = 0; + qemu_log_mask(LOG_UNIMP, + "%s Unexpected 64-bit access to 0x%"PRIx64" (RAZ)\n", + __func__, offset); + return MEMTX_OK; + } +} + +static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset, + uint64_t *data, MemTxAttrs attrs) +{ + switch (offset) { + case A_IDREGS ... A_IDREGS + 0x1f: + *data = smmuv3_idreg(offset - A_IDREGS); + return MEMTX_OK; + case A_IDR0 ... A_IDR5: + *data = s->idr[(offset - A_IDR0) / 4]; + return MEMTX_OK; + case A_IIDR: + *data = s->iidr; + return MEMTX_OK; + case A_CR0: + *data = s->cr[0]; + return MEMTX_OK; + case A_CR0ACK: + *data = s->cr0ack; + return MEMTX_OK; + case A_CR1: + *data = s->cr[1]; + return MEMTX_OK; + case A_CR2: + *data = s->cr[2]; + return MEMTX_OK; + case A_STATUSR: + *data = s->statusr; + return MEMTX_OK; + case A_IRQ_CTRL: + case A_IRQ_CTRL_ACK: + *data = s->irq_ctrl; + return MEMTX_OK; + case A_GERROR: + *data = s->gerror; + return MEMTX_OK; + case A_GERRORN: + *data = s->gerrorn; + return MEMTX_OK; + case A_GERROR_IRQ_CFG0: /* 64b */ + *data = extract64(s->gerror_irq_cfg0, 0, 32); + return MEMTX_OK; + case A_GERROR_IRQ_CFG0 + 4: + *data = extract64(s->gerror_irq_cfg0, 32, 32); + return MEMTX_OK; + case A_GERROR_IRQ_CFG1: + *data = s->gerror_irq_cfg1; + return MEMTX_OK; + case A_GERROR_IRQ_CFG2: + *data = s->gerror_irq_cfg2; + return MEMTX_OK; + case A_STRTAB_BASE: /* 64b */ + *data = extract64(s->strtab_base, 0, 32); + return MEMTX_OK; + case A_STRTAB_BASE + 4: /* 64b */ + *data = extract64(s->strtab_base, 32, 32); + return MEMTX_OK; + case A_STRTAB_BASE_CFG: + *data = s->strtab_base_cfg; + return MEMTX_OK; + case A_CMDQ_BASE: /* 64b */ + *data = extract64(s->cmdq.base, 0, 32); + return MEMTX_OK; + case A_CMDQ_BASE + 4: + *data = extract64(s->cmdq.base, 32, 32); + return MEMTX_OK; + case A_CMDQ_PROD: + *data = s->cmdq.prod; + return MEMTX_OK; + case A_CMDQ_CONS: + *data = s->cmdq.cons; + return MEMTX_OK; + case A_EVENTQ_BASE: /* 64b */ + *data = extract64(s->eventq.base, 0, 32); + return MEMTX_OK; + case A_EVENTQ_BASE + 4: /* 64b */ + *data = extract64(s->eventq.base, 32, 32); + return MEMTX_OK; + case A_EVENTQ_PROD: + *data = s->eventq.prod; + return MEMTX_OK; + case A_EVENTQ_CONS: + *data = s->eventq.cons; + return MEMTX_OK; + default: + *data = 0; + qemu_log_mask(LOG_UNIMP, + "%s unhandled 32-bit access at 0x%"PRIx64" (RAZ)\n", + __func__, offset); + return MEMTX_OK; + } +} + +static MemTxResult smmu_read_mmio(void *opaque, hwaddr offset, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + SMMUState *sys = opaque; + SMMUv3State *s = ARM_SMMUV3(sys); + MemTxResult r; + + /* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */ + offset &= ~0x10000; + + switch (size) { + case 8: + r = smmu_readll(s, offset, data, attrs); + break; + case 4: + r = smmu_readl(s, offset, data, attrs); + break; + default: + r = MEMTX_ERROR; + break; + } + + trace_smmuv3_read_mmio(offset, *data, size, r); + return r; +} + +static const MemoryRegionOps smmu_mem_ops = { + .read_with_attrs = smmu_read_mmio, + .write_with_attrs = smmu_write_mmio, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 8, + }, +}; + +static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(dev, &s->irq[i]); + } +} + +static void smmu_reset(DeviceState *dev) +{ + SMMUv3State *s = ARM_SMMUV3(dev); + SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s); + + c->parent_reset(dev); + + smmuv3_init_regs(s); +} + +static void smmu_realize(DeviceState *d, Error **errp) +{ + SMMUState *sys = ARM_SMMU(d); + SMMUv3State *s = ARM_SMMUV3(sys); + SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s); + SysBusDevice *dev = SYS_BUS_DEVICE(d); + Error *local_err = NULL; + + c->parent_realize(d, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + qemu_mutex_init(&s->mutex); + + memory_region_init_io(&sys->iomem, OBJECT(s), + &smmu_mem_ops, sys, TYPE_ARM_SMMUV3, 0x20000); + + sys->mrtypename = TYPE_SMMUV3_IOMMU_MEMORY_REGION; + + sysbus_init_mmio(dev, &sys->iomem); + + smmu_init_irq(s, dev); +} + +static const VMStateDescription vmstate_smmuv3_queue = { + .name = "smmuv3_queue", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(base, SMMUQueue), + VMSTATE_UINT32(prod, SMMUQueue), + VMSTATE_UINT32(cons, SMMUQueue), + VMSTATE_UINT8(log2size, SMMUQueue), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_smmuv3 = { + .name = "smmuv3", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(features, SMMUv3State), + VMSTATE_UINT8(sid_size, SMMUv3State), + VMSTATE_UINT8(sid_split, SMMUv3State), + + VMSTATE_UINT32_ARRAY(cr, SMMUv3State, 3), + VMSTATE_UINT32(cr0ack, SMMUv3State), + VMSTATE_UINT32(statusr, SMMUv3State), + VMSTATE_UINT32(irq_ctrl, SMMUv3State), + VMSTATE_UINT32(gerror, SMMUv3State), + VMSTATE_UINT32(gerrorn, SMMUv3State), + VMSTATE_UINT64(gerror_irq_cfg0, SMMUv3State), + VMSTATE_UINT32(gerror_irq_cfg1, SMMUv3State), + VMSTATE_UINT32(gerror_irq_cfg2, SMMUv3State), + VMSTATE_UINT64(strtab_base, SMMUv3State), + VMSTATE_UINT32(strtab_base_cfg, SMMUv3State), + VMSTATE_UINT64(eventq_irq_cfg0, SMMUv3State), + VMSTATE_UINT32(eventq_irq_cfg1, SMMUv3State), + VMSTATE_UINT32(eventq_irq_cfg2, SMMUv3State), + + VMSTATE_STRUCT(cmdq, SMMUv3State, 0, vmstate_smmuv3_queue, SMMUQueue), + VMSTATE_STRUCT(eventq, SMMUv3State, 0, vmstate_smmuv3_queue, SMMUQueue), + + VMSTATE_END_OF_LIST(), + }, +}; + +static void smmuv3_instance_init(Object *obj) +{ + /* Nothing much to do here as of now */ +} + +static void smmuv3_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SMMUv3Class *c = ARM_SMMUV3_CLASS(klass); + + dc->vmsd = &vmstate_smmuv3; + device_class_set_parent_reset(dc, smmu_reset, &c->parent_reset); + c->parent_realize = dc->realize; + dc->realize = smmu_realize; +} + +static void smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, + IOMMUNotifierFlag old, + IOMMUNotifierFlag new) +{ + SMMUDevice *sdev = container_of(iommu, SMMUDevice, iommu); + SMMUv3State *s3 = sdev->smmu; + SMMUState *s = &(s3->smmu_state); + SMMUNotifierNode *node = NULL; + SMMUNotifierNode *next_node = NULL; + + if (new & IOMMU_NOTIFIER_MAP) { + int bus_num = pci_bus_num(sdev->bus); + PCIDevice *pcidev = pci_find_device(sdev->bus, bus_num, sdev->devfn); + + warn_report("SMMUv3 does not support notification on MAP: " + "device %s will not function properly", pcidev->name); + } + + if (old == IOMMU_NOTIFIER_NONE) { + trace_smmuv3_notify_flag_add(iommu->parent_obj.name); + node = g_malloc0(sizeof(*node)); + node->sdev = sdev; + QLIST_INSERT_HEAD(&s->notifiers_list, node, next); + return; + } + + /* update notifier node with new flags */ + QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { + if (node->sdev == sdev) { + if (new == IOMMU_NOTIFIER_NONE) { + trace_smmuv3_notify_flag_del(iommu->parent_obj.name); + QLIST_REMOVE(node, next); + g_free(node); + } + return; + } + } +} + +static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = smmuv3_translate; + imrc->notify_flag_changed = smmuv3_notify_flag_changed; +} + +static const TypeInfo smmuv3_type_info = { + .name = TYPE_ARM_SMMUV3, + .parent = TYPE_ARM_SMMU, + .instance_size = sizeof(SMMUv3State), + .instance_init = smmuv3_instance_init, + .class_size = sizeof(SMMUv3Class), + .class_init = smmuv3_class_init, +}; + +static const TypeInfo smmuv3_iommu_memory_region_info = { + .parent = TYPE_IOMMU_MEMORY_REGION, + .name = TYPE_SMMUV3_IOMMU_MEMORY_REGION, + .class_init = smmuv3_iommu_memory_region_class_init, +}; + +static void smmuv3_register_types(void) +{ + type_register(&smmuv3_type_info); + type_register(&smmuv3_iommu_memory_region_info); +} + +type_init(smmuv3_register_types) + diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index e419e3c00e..c4bc3deedf 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -27,7 +27,6 @@ #include "hw/audio/wm8750.h" #include "audio/audio.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "cpu.h" @@ -170,16 +169,22 @@ static void sl_nand_init(Object *obj) { SLNANDState *s = SL_NAND(obj); SysBusDevice *dev = SYS_BUS_DEVICE(obj); - DriveInfo *nand; s->ctl = 0; + + memory_region_init_io(&s->iomem, obj, &sl_ops, s, "sl", 0x40); + sysbus_init_mmio(dev, &s->iomem); +} + +static void sl_nand_realize(DeviceState *dev, Error **errp) +{ + SLNANDState *s = SL_NAND(dev); + DriveInfo *nand; + /* FIXME use a qdev drive property instead of drive_get() */ nand = drive_get(IF_MTD, 0, 0); s->nand = nand_init(nand ? blk_by_legacy_dinfo(nand) : NULL, s->manf_id, s->chip_id); - - memory_region_init_io(&s->iomem, obj, &sl_ops, s, "sl", 0x40); - sysbus_init_mmio(dev, &s->iomem); } /* Spitz Keyboard */ @@ -1080,6 +1085,7 @@ static void sl_nand_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_sl_nand_info; dc->props = sl_nand_properties; + dc->realize = sl_nand_realize; /* Reason: init() method uses drive_get() */ dc->user_creatable = false; } diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index de7c0fc4a6..dc521b4a5a 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -20,6 +20,7 @@ #include "qemu/log.h" #include "exec/address-spaces.h" #include "sysemu/sysemu.h" +#include "hw/arm/armv7m.h" #include "hw/char/pl011.h" #include "hw/misc/unimp.h" #include "cpu.h" @@ -203,15 +204,16 @@ static uint64_t gptm_read(void *opaque, hwaddr offset, return s->rtc; } qemu_log_mask(LOG_UNIMP, - "GPTM: read of TAR but timer read not supported"); + "GPTM: read of TAR but timer read not supported\n"); return 0; case 0x4c: /* TBR */ qemu_log_mask(LOG_UNIMP, - "GPTM: read of TBR but timer read not supported"); + "GPTM: read of TBR but timer read not supported\n"); return 0; default: qemu_log_mask(LOG_GUEST_ERROR, - "GPTM: read at bad offset 0x%x\n", (int)offset); + "GPTM: read at bad offset 0x02%" HWADDR_PRIx "\n", + offset); return 0; } } @@ -293,7 +295,8 @@ static void gptm_write(void *opaque, hwaddr offset, break; default: qemu_log_mask(LOG_GUEST_ERROR, - "GPTM: read at bad offset 0x%x\n", (int)offset); + "GPTM: write at bad offset 0x02%" HWADDR_PRIx "\n", + offset); } gptm_update_irq(s); } @@ -559,7 +562,7 @@ static void ssys_write(void *opaque, hwaddr offset, case 0x040: /* SRCR0 */ case 0x044: /* SRCR1 */ case 0x048: /* SRCR2 */ - fprintf(stderr, "Peripheral reset not implemented\n"); + qemu_log_mask(LOG_UNIMP, "Peripheral reset not implemented\n"); break; case 0x054: /* IMC */ s->int_mask = value & 0x7f; @@ -836,11 +839,12 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset, break; case 0x20: /* MCR */ if (value & 1) { - qemu_log_mask(LOG_UNIMP, "stellaris_i2c: Loopback not implemented"); + qemu_log_mask(LOG_UNIMP, + "stellaris_i2c: Loopback not implemented\n"); } if (value & 0x20) { qemu_log_mask(LOG_UNIMP, - "stellaris_i2c: Slave mode not implemented"); + "stellaris_i2c: Slave mode not implemented\n"); } s->mcr = value & 0x31; break; @@ -1124,7 +1128,7 @@ static void stellaris_adc_write(void *opaque, hwaddr offset, s->sspri = value; break; case 0x28: /* PSSI */ - qemu_log_mask(LOG_UNIMP, "ADC: sample initiate unimplemented"); + qemu_log_mask(LOG_UNIMP, "ADC: sample initiate unimplemented\n"); break; case 0x30: /* SAC */ s->sac = value; @@ -1297,8 +1301,13 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) &error_fatal); memory_region_add_subregion(system_memory, 0x20000000, sram); - nvic = armv7m_init(system_memory, flash_size, NUM_IRQ_LINES, - ms->kernel_filename, ms->cpu_type); + nvic = qdev_create(NULL, TYPE_ARMV7M); + qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES); + qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type); + object_property_set_link(OBJECT(nvic), OBJECT(get_system_memory()), + "memory", &error_abort); + /* This will exit with an error if the user passed us a bad cpu_type */ + qdev_init_nofail(nvic); qdev_connect_gpio_out_named(nvic, "SYSRESETREQ", 0, qemu_allocate_irq(&do_sys_reset, NULL, 0)); @@ -1353,7 +1362,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) if (board->dc2 & (1 << i)) { pl011_luminary_create(0x4000c000 + i * 0x1000, qdev_get_gpio_in(nvic, uart_irq[i]), - serial_hds[i]); + serial_hd(i)); } } if (board->dc2 & (1 << 4)) { @@ -1430,6 +1439,8 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) create_unimplemented_device("analogue-comparator", 0x4003c000, 0x1000); create_unimplemented_device("hibernation", 0x400fc000, 0x1000); create_unimplemented_device("flash-control", 0x400fd000, 0x1000); + + armv7m_load_kernel(ARM_CPU(first_cpu), ms->kernel_filename, flash_size); } /* FIXME: Figure out how to generate these from stellaris_boards. */ diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 1cd6374e07..c486d06a8b 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -49,36 +49,32 @@ static void stm32f205_soc_initfn(Object *obj) STM32F205State *s = STM32F205_SOC(obj); int i; - object_initialize(&s->armv7m, sizeof(s->armv7m), TYPE_ARMV7M); - qdev_set_parent_bus(DEVICE(&s->armv7m), sysbus_get_default()); + sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), + TYPE_ARMV7M); - object_initialize(&s->syscfg, sizeof(s->syscfg), TYPE_STM32F2XX_SYSCFG); - qdev_set_parent_bus(DEVICE(&s->syscfg), sysbus_get_default()); + sysbus_init_child_obj(obj, "syscfg", &s->syscfg, sizeof(s->syscfg), + TYPE_STM32F2XX_SYSCFG); for (i = 0; i < STM_NUM_USARTS; i++) { - object_initialize(&s->usart[i], sizeof(s->usart[i]), - TYPE_STM32F2XX_USART); - qdev_set_parent_bus(DEVICE(&s->usart[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "usart[*]", &s->usart[i], + sizeof(s->usart[i]), TYPE_STM32F2XX_USART); } for (i = 0; i < STM_NUM_TIMERS; i++) { - object_initialize(&s->timer[i], sizeof(s->timer[i]), - TYPE_STM32F2XX_TIMER); - qdev_set_parent_bus(DEVICE(&s->timer[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "timer[*]", &s->timer[i], + sizeof(s->timer[i]), TYPE_STM32F2XX_TIMER); } s->adc_irqs = OR_IRQ(object_new(TYPE_OR_IRQ)); for (i = 0; i < STM_NUM_ADCS; i++) { - object_initialize(&s->adc[i], sizeof(s->adc[i]), - TYPE_STM32F2XX_ADC); - qdev_set_parent_bus(DEVICE(&s->adc[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "adc[*]", &s->adc[i], sizeof(s->adc[i]), + TYPE_STM32F2XX_ADC); } for (i = 0; i < STM_NUM_SPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), - TYPE_STM32F2XX_SPI); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "spi[*]", &s->spi[i], sizeof(s->spi[i]), + TYPE_STM32F2XX_SPI); } } @@ -135,8 +131,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) /* Attach UART (uses USART registers) and USART controllers */ for (i = 0; i < STM_NUM_USARTS; i++) { dev = DEVICE(&(s->usart[i])); - qdev_prop_set_chr(dev, "chardev", - i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->usart[i]), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index 4cdb3a670b..ec2627374d 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -1622,7 +1622,7 @@ StrongARMState *sa1110_init(MemoryRegion *sysmem, for (i = 0; sa_serial[i].io_base; i++) { DeviceState *dev = qdev_create(NULL, TYPE_STRONGARM_UART); - qdev_prop_set_chr(dev, "chardev", serial_hds[i]); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, sa_serial[i].io_base); diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c index d68e3dcdbd..43d6a7bb48 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/arm/sysbus-fdt.c @@ -36,6 +36,7 @@ #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" #include "hw/vfio/vfio-amd-xgbe.h" +#include "hw/display/ramfb.h" #include "hw/arm/fdt.h" /* @@ -49,15 +50,6 @@ typedef struct PlatformBusFDTData { PlatformBusDevice *pbus; } PlatformBusFDTData; -/* - * struct used when calling the machine init done notifier - * that constructs the fdt nodes of platform bus devices - */ -typedef struct PlatformBusFDTNotifierParams { - Notifier notifier; - ARMPlatformBusFDTParams *fdt_params; -} PlatformBusFDTNotifierParams; - /* struct that associates a device type name and a node creation function */ typedef struct NodeCreationPair { const char *typename; @@ -100,17 +92,22 @@ static void copy_properties_from_host(HostProperty *props, int nb_props, r = qemu_fdt_getprop(host_fdt, node_path, props[i].name, &prop_len, - props[i].optional ? &err : &error_fatal); + &err); if (r) { qemu_fdt_setprop(guest_fdt, nodename, props[i].name, r, prop_len); } else { - if (prop_len != -FDT_ERR_NOTFOUND) { - /* optional property not returned although property exists */ - error_report_err(err); - } else { + if (props[i].optional && prop_len == -FDT_ERR_NOTFOUND) { + /* optional property does not exist */ error_free(err); + } else { + error_report_err(err); } + if (!props[i].optional) { + /* mandatory property not found: bail out */ + exit(1); + } + err = NULL; } } } @@ -146,9 +143,9 @@ static void fdt_build_clock_node(void *host_fdt, void *guest_fdt, node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle); if (node_offset <= 0) { - error_setg(&error_fatal, - "not able to locate clock handle %d in host device tree", - host_phandle); + error_report("not able to locate clock handle %d in host device tree", + host_phandle); + exit(1); } node_path = g_malloc(path_len); while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len)) @@ -157,16 +154,16 @@ static void fdt_build_clock_node(void *host_fdt, void *guest_fdt, node_path = g_realloc(node_path, path_len); } if (ret < 0) { - error_setg(&error_fatal, - "not able to retrieve node path for clock handle %d", - host_phandle); + error_report("not able to retrieve node path for clock handle %d", + host_phandle); + exit(1); } r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len, &error_fatal); if (strcmp(r, "fixed-clock")) { - error_setg(&error_fatal, - "clock handle %d is not a fixed clock", host_phandle); + error_report("clock handle %d is not a fixed clock", host_phandle); + exit(1); } nodename = strrchr(node_path, '/'); @@ -309,34 +306,37 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) dt_name = sysfs_to_dt_name(vbasedev->name); if (!dt_name) { - error_setg(&error_fatal, "%s incorrect sysfs device name %s", - __func__, vbasedev->name); + error_report("%s incorrect sysfs device name %s", + __func__, vbasedev->name); + exit(1); } node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat, &error_fatal); if (!node_path || !node_path[0]) { - error_setg(&error_fatal, "%s unable to retrieve node path for %s/%s", - __func__, dt_name, vdev->compat); + error_report("%s unable to retrieve node path for %s/%s", + __func__, dt_name, vdev->compat); + exit(1); } if (node_path[1]) { - error_setg(&error_fatal, "%s more than one node matching %s/%s!", - __func__, dt_name, vdev->compat); + error_report("%s more than one node matching %s/%s!", + __func__, dt_name, vdev->compat); + exit(1); } g_free(dt_name); if (vbasedev->num_regions != 5) { - error_setg(&error_fatal, "%s Does the host dt node combine XGBE/PHY?", - __func__); + error_report("%s Does the host dt node combine XGBE/PHY?", __func__); + exit(1); } /* generate nodes for DMA_CLK and PTP_CLK */ r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks", &prop_len, &error_fatal); if (prop_len != 8) { - error_setg(&error_fatal, "%s clocks property should contain 2 handles", - __func__); + error_report("%s clocks property should contain 2 handles", __func__); + exit(1); } host_clock_phandles = (uint32_t *)r; guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt); @@ -415,12 +415,18 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) #endif /* CONFIG_LINUX */ +static int no_fdt_node(SysBusDevice *sbdev, void *opaque) +{ + return 0; +} + /* list of supported dynamic sysbus devices */ static const NodeCreationPair add_fdt_node_functions[] = { #ifdef CONFIG_LINUX {TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node}, {TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node}, #endif + {TYPE_RAMFB_DEVICE, no_fdt_node}, {"", NULL}, /* last element */ }; @@ -453,42 +459,17 @@ static void add_fdt_node(SysBusDevice *sbdev, void *opaque) exit(1); } -/** - * add_all_platform_bus_fdt_nodes - create all the platform bus nodes - * - * builds the parent platform bus node and all the nodes of dynamic - * sysbus devices attached to it. - */ -static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params) +void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr, + hwaddr bus_size, int irq_start) { const char platcomp[] = "qemu,platform\0simple-bus"; PlatformBusDevice *pbus; DeviceState *dev; gchar *node; - uint64_t addr, size; - int irq_start, dtb_size; - struct arm_boot_info *info = fdt_params->binfo; - const ARMPlatformBusSystemParams *params = fdt_params->system_params; - const char *intc = fdt_params->intc; - void *fdt = info->get_dtb(info, &dtb_size); - - /* - * If the user provided a dtb, we assume the dynamic sysbus nodes - * already are integrated there. This corresponds to a use case where - * the dynamic sysbus nodes are complex and their generation is not yet - * supported. In that case the user can take charge of the guest dt - * while qemu takes charge of the qom stuff. - */ - if (info->dtb_filename) { - return; - } assert(fdt); - node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base); - addr = params->platform_bus_base; - size = params->platform_bus_size; - irq_start = params->platform_bus_first_irq; + node = g_strdup_printf("/platform@%"PRIx64, addr); /* Create a /platform node that we can put all devices into */ qemu_fdt_add_subnode(fdt, node); @@ -499,16 +480,13 @@ static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params) */ qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1); qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1); - qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, size); + qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, bus_size); qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc); dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); pbus = PLATFORM_BUS_DEVICE(dev); - /* We can only create dt nodes for dynamic devices when they're ready */ - assert(pbus->done_gathering); - PlatformBusFDTData data = { .fdt = fdt, .irq_start = irq_start, @@ -521,22 +499,3 @@ static void add_all_platform_bus_fdt_nodes(ARMPlatformBusFDTParams *fdt_params) g_free(node); } - -static void platform_bus_fdt_notify(Notifier *notifier, void *data) -{ - PlatformBusFDTNotifierParams *p = DO_UPCAST(PlatformBusFDTNotifierParams, - notifier, notifier); - - add_all_platform_bus_fdt_nodes(p->fdt_params); - g_free(p->fdt_params); - g_free(p); -} - -void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params) -{ - PlatformBusFDTNotifierParams *p = g_new(PlatformBusFDTNotifierParams, 1); - - p->fdt_params = fdt_params; - p->notifier.notify = platform_bus_fdt_notify; - qemu_add_machine_init_done_notifier(&p->notifier); -} diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 193063ed99..27b11d655d 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -2,3 +2,61 @@ # hw/arm/virt-acpi-build.c virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." + +# hw/arm/smmu-common.c +smmu_add_mr(const char *name) "%s" +smmu_page_walk(int stage, uint64_t baseaddr, int first_level, uint64_t start, uint64_t end) "stage=%d, baseaddr=0x%"PRIx64", first level=%d, start=0x%"PRIx64", end=0x%"PRIx64 +smmu_lookup_table(int level, uint64_t baseaddr, int granule_sz, uint64_t start, uint64_t end, int flags, uint64_t subpage_size) "level=%d baseaddr=0x%"PRIx64" granule=%d, start=0x%"PRIx64" end=0x%"PRIx64" flags=%d subpage_size=0x%"PRIx64 +smmu_ptw_level(int level, uint64_t iova, size_t subpage_size, uint64_t baseaddr, uint32_t offset, uint64_t pte) "level=%d iova=0x%"PRIx64" subpage_sz=0x%zx baseaddr=0x%"PRIx64" offset=%d => pte=0x%"PRIx64 +smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint32_t offset, uint64_t pte) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" offset=%d pte=0x%"PRIx64 +smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t address) "stage=%d level=%d iova=0x%"PRIx64" base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" page address = 0x%"PRIx64 +smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB" +smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64 +smmu_iotlb_cache_hit(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" +smmu_iotlb_cache_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" +smmu_iotlb_inv_all(void) "IOTLB invalidate all" +smmu_iotlb_inv_asid(uint16_t asid) "IOTLB invalidate asid=%d" +smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 +smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" + +#hw/arm/smmuv3.c +smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" +smmuv3_trigger_irq(int irq) "irq=%d" +smmuv3_write_gerror(uint32_t toggled, uint32_t gerror) "toggled=0x%x, new GERROR=0x%x" +smmuv3_write_gerrorn(uint32_t acked, uint32_t gerrorn) "acked=0x%x, new GERRORN=0x%x" +smmuv3_unhandled_cmd(uint32_t type) "Unhandled command type=%d" +smmuv3_cmdq_consume(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod=%d cons=%d prod.wrap=%d cons.wrap=%d" +smmuv3_cmdq_opcode(const char *opcode) "<--- %s" +smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d " +smmuv3_cmdq_consume_error(const char *cmd_name, uint8_t cmd_error) "Error on %s command execution: %d" +smmuv3_update(bool is_empty, uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "q empty:%d prod:%d cons:%d p.wrap:%d p.cons:%d" +smmuv3_update_check_cmd(int error) "cmdq not enabled or error :0x%x" +smmuv3_write_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" +smmuv3_write_mmio_idr(uint64_t addr, uint64_t val) "write to RO/Unimpl reg 0x%"PRIx64" val64:0x%"PRIx64 +smmuv3_write_mmio_evtq_cons_bef_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "Before clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" +smmuv3_write_mmio_evtq_cons_after_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "after clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" +smmuv3_record_event(const char *type, uint32_t sid) "%s sid=%d" +smmuv3_find_ste(uint16_t sid, uint32_t features, uint16_t sid_split) "SID:0x%x features:0x%x, sid_split:0x%x" +smmuv3_find_ste_2lvl(uint64_t strtab_base, uint64_t l1ptr, int l1_ste_offset, uint64_t l2ptr, int l2_ste_offset, int max_l2_ste) "strtab_base:0x%"PRIx64" l1ptr:0x%"PRIx64" l1_off:0x%x, l2ptr:0x%"PRIx64" l2_off:0x%x max_l2_ste:%d" +smmuv3_get_ste(uint64_t addr) "STE addr: 0x%"PRIx64 +smmuv3_translate_disable(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d bypass (smmu disabled) iova:0x%"PRIx64" is_write=%d" +smmuv3_translate_bypass(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d STE bypass iova:0x%"PRIx64" is_write=%d" +smmuv3_translate_abort(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d abort on iova:0x%"PRIx64" is_write=%d" +smmuv3_translate_success(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=%d iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x" +smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64 +smmuv3_decode_cd(uint32_t oas) "oas=%d" +smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d" +smmuv3_cmdq_cfgi_ste(int streamid) "streamid =%d" +smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%d - end=0x%d" +smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %d" +smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid %d (hits=%d, misses=%d, hit rate=%d)" +smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid %d (hits=%d, misses=%d, hit rate=%d)" +smmuv3_cmdq_tlbi_nh_va(int vmid, int asid, uint64_t addr, bool leaf) "vmid =%d asid =%d addr=0x%"PRIx64" leaf=%d" +smmuv3_cmdq_tlbi_nh_vaa(int vmid, uint64_t addr) "vmid =%d addr=0x%"PRIx64 +smmuv3_cmdq_tlbi_nh(void) "" +smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" +smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d" +smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" +smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" +smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova) "iommu mr=%s asid=%d iova=0x%"PRIx64 + diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index 418792cd02..a5a06b6d40 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -19,7 +19,6 @@ #include "hw/pci/pci.h" #include "hw/i2c/i2c.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "hw/block/flash.h" #include "qemu/error-report.h" @@ -283,10 +282,10 @@ static void versatile_init(MachineState *machine, int board_id) n--; } - pl011_create(0x101f1000, pic[12], serial_hds[0]); - pl011_create(0x101f2000, pic[13], serial_hds[1]); - pl011_create(0x101f3000, pic[14], serial_hds[2]); - pl011_create(0x10009000, sic[6], serial_hds[3]); + pl011_create(0x101f1000, pic[12], serial_hd(0)); + pl011_create(0x101f2000, pic[13], serial_hd(1)); + pl011_create(0x101f3000, pic[14], serial_hd(2)); + pl011_create(0x10009000, sic[6], serial_hd(3)); sysbus_create_simple("pl080", 0x10130000, pic[17]); sysbus_create_simple("sp804", 0x101e2000, pic[4]); diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 9fad79177a..5bfe2e4348 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -35,7 +35,6 @@ #include "hw/boards.h" #include "hw/loader.h" #include "exec/address-spaces.h" -#include "sysemu/block-backend.h" #include "hw/block/flash.h" #include "sysemu/device_tree.h" #include "qemu/error-report.h" @@ -622,10 +621,10 @@ static void vexpress_common_init(MachineState *machine) sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]); sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]); - pl011_create(map[VE_UART0], pic[5], serial_hds[0]); - pl011_create(map[VE_UART1], pic[6], serial_hds[1]); - pl011_create(map[VE_UART2], pic[7], serial_hds[2]); - pl011_create(map[VE_UART3], pic[8], serial_hds[3]); + pl011_create(map[VE_UART0], pic[5], serial_hd(0)); + pl011_create(map[VE_UART1], pic[6], serial_hd(1)); + pl011_create(map[VE_UART2], pic[7], serial_hd(2)); + pl011_create(map[VE_UART3], pic[8], serial_hd(3)); sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]); sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]); diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index c7c6a57ec5..6ea47e2588 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -150,16 +150,17 @@ static void acpi_dsdt_add_virtio(Aml *scope, } static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, - uint32_t irq, bool use_highmem) + uint32_t irq, bool use_highmem, bool highmem_ecam) { + int ecam_id = VIRT_ECAM_ID(highmem_ecam); Aml *method, *crs, *ifctx, *UUID, *ifctx1, *elsectx, *buf; int i, bus_no; hwaddr base_mmio = memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = memmap[VIRT_PCIE_MMIO].size; hwaddr base_pio = memmap[VIRT_PCIE_PIO].base; hwaddr size_pio = memmap[VIRT_PCIE_PIO].size; - hwaddr base_ecam = memmap[VIRT_PCIE_ECAM].base; - hwaddr size_ecam = memmap[VIRT_PCIE_ECAM].size; + hwaddr base_ecam = memmap[ecam_id].base; + hwaddr size_ecam = memmap[ecam_id].size; int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; Aml *dev = aml_device("%s", "PCI0"); @@ -173,7 +174,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_append(dev, aml_name_decl("_CCA", aml_int(1))); /* Declare the PCI Routing Table. */ - Aml *rt_pkg = aml_package(nr_pcie_buses * PCI_NUM_PINS); + Aml *rt_pkg = aml_varpackage(nr_pcie_buses * PCI_NUM_PINS); for (bus_no = 0; bus_no < nr_pcie_buses; bus_no++) { for (i = 0; i < PCI_NUM_PINS; i++) { int gsi = (i + bus_no) % PCI_NUM_PINS; @@ -316,7 +317,10 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, Aml *dev_res0 = aml_device("%s", "RES0"); aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); crs = aml_resource_template(); - aml_append(crs, aml_memory32_fixed(base_ecam, size_ecam, AML_READ_WRITE)); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, 0x0000, base_ecam, + base_ecam + size_ecam - 1, 0x0000, size_ecam)); aml_append(dev_res0, aml_name_decl("_CRS", crs)); aml_append(dev, dev_res0); aml_append(scope, dev); @@ -393,20 +397,32 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned xsdt_tbl_offset) } static void -build_iort(GArray *table_data, BIOSLinker *linker) +build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { - int iort_start = table_data->len; + int nb_nodes, iort_start = table_data->len; AcpiIortIdMapping *idmap; AcpiIortItsGroup *its; AcpiIortTable *iort; - size_t node_size, iort_length; + AcpiIortSmmu3 *smmu; + size_t node_size, iort_node_offset, iort_length, smmu_offset = 0; AcpiIortRC *rc; iort = acpi_data_push(table_data, sizeof(*iort)); + if (vms->iommu == VIRT_IOMMU_SMMUV3) { + nb_nodes = 3; /* RC, ITS, SMMUv3 */ + } else { + nb_nodes = 2; /* RC, ITS */ + } + iort_length = sizeof(*iort); - iort->node_count = cpu_to_le32(2); /* RC and ITS nodes */ - iort->node_offset = cpu_to_le32(sizeof(*iort)); + iort->node_count = cpu_to_le32(nb_nodes); + /* + * Use a copy in case table_data->data moves during acpi_data_push + * operations. + */ + iort_node_offset = sizeof(*iort); + iort->node_offset = cpu_to_le32(iort_node_offset); /* ITS group node */ node_size = sizeof(*its) + sizeof(uint32_t); @@ -418,6 +434,34 @@ build_iort(GArray *table_data, BIOSLinker *linker) its->its_count = cpu_to_le32(1); its->identifiers[0] = 0; /* MADT translation_id */ + if (vms->iommu == VIRT_IOMMU_SMMUV3) { + int irq = vms->irqmap[VIRT_SMMU]; + + /* SMMUv3 node */ + smmu_offset = iort_node_offset + node_size; + node_size = sizeof(*smmu) + sizeof(*idmap); + iort_length += node_size; + smmu = acpi_data_push(table_data, node_size); + + smmu->type = ACPI_IORT_NODE_SMMU_V3; + smmu->length = cpu_to_le16(node_size); + smmu->mapping_count = cpu_to_le32(1); + smmu->mapping_offset = cpu_to_le32(sizeof(*smmu)); + smmu->base_address = cpu_to_le64(vms->memmap[VIRT_SMMU].base); + smmu->event_gsiv = cpu_to_le32(irq); + smmu->pri_gsiv = cpu_to_le32(irq + 1); + smmu->gerr_gsiv = cpu_to_le32(irq + 2); + smmu->sync_gsiv = cpu_to_le32(irq + 3); + + /* Identity RID mapping covering the whole input RID range */ + idmap = &smmu->id_mapping_array[0]; + idmap->input_base = 0; + idmap->id_count = cpu_to_le32(0xFFFF); + idmap->output_base = 0; + /* output IORT node is the ITS group node (the first node) */ + idmap->output_reference = cpu_to_le32(iort_node_offset); + } + /* Root Complex Node */ node_size = sizeof(*rc) + sizeof(*idmap); iort_length += node_size; @@ -438,9 +482,20 @@ build_iort(GArray *table_data, BIOSLinker *linker) idmap->input_base = 0; idmap->id_count = cpu_to_le32(0xFFFF); idmap->output_base = 0; - /* output IORT node is the ITS group node (the first node) */ - idmap->output_reference = cpu_to_le32(iort->node_offset); + if (vms->iommu == VIRT_IOMMU_SMMUV3) { + /* output IORT node is the smmuv3 node */ + idmap->output_reference = cpu_to_le32(smmu_offset); + } else { + /* output IORT node is the ITS group node (the first node) */ + idmap->output_reference = cpu_to_le32(iort_node_offset); + } + + /* + * Update the pointer address in case table_data->data moves during above + * acpi_data_push operations. + */ + iort = (AcpiIortTable *)(table_data->data + iort_start); iort->length = cpu_to_le32(iort_length); build_header(linker, table_data, (void *)(table_data->data + iort_start), @@ -522,16 +577,17 @@ build_mcfg(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { AcpiTableMcfg *mcfg; const MemMapEntry *memmap = vms->memmap; + int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam); int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]); int mcfg_start = table_data->len; mcfg = acpi_data_push(table_data, len); - mcfg->allocation[0].address = cpu_to_le64(memmap[VIRT_PCIE_ECAM].base); + mcfg->allocation[0].address = cpu_to_le64(memmap[ecam_id].base); /* Only a single allocation so no need to play with segments */ mcfg->allocation[0].pci_segment = cpu_to_le16(0); mcfg->allocation[0].start_bus_number = 0; - mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size + mcfg->allocation[0].end_bus_number = (memmap[ecam_id].size / PCIE_MMCFG_SIZE_MIN) - 1; build_header(linker, table_data, (void *)(table_data->data + mcfg_start), @@ -619,6 +675,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (vms->gic_version == 3) { AcpiMadtGenericTranslator *gic_its; + int nb_redist_regions = virt_gicv3_redist_region_count(vms); AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data, sizeof *gicr); @@ -627,6 +684,14 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base); gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size); + if (nb_redist_regions == 2) { + gicr = acpi_data_push(table_data, sizeof(*gicr)); + gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR; + gicr->length = sizeof(*gicr); + gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST2].base); + gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST2].size); + } + if (its_class_name() && !vmc->no_its) { gic_its = acpi_data_push(table_data, sizeof *gic_its); gic_its->type = ACPI_APIC_GENERIC_TRANSLATOR; @@ -706,7 +771,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) 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, vms->highmem_ecam); acpi_dsdt_add_gpio(scope, &memmap[VIRT_GPIO], (irqmap[VIRT_GPIO] + ARM_SPI_BASE)); acpi_dsdt_add_power_button(scope); @@ -777,7 +842,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) if (its_class_name() && !vmc->no_its) { acpi_add_table(table_offsets, tables_blob); - build_iort(tables_blob, tables->linker); + build_iort(tables_blob, tables->linker, vms); } /* XSDT is pointed to by RSDP */ diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 94dcb125d3..281ddcdf6e 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -36,9 +36,9 @@ #include "hw/arm/virt.h" #include "hw/vfio/vfio-calxeda-xgmac.h" #include "hw/vfio/vfio-amd-xgbe.h" +#include "hw/display/ramfb.h" #include "hw/devices.h" #include "net/net.h" -#include "sysemu/block-backend.h" #include "sysemu/device_tree.h" #include "sysemu/numa.h" #include "sysemu/sysemu.h" @@ -58,6 +58,7 @@ #include "hw/smbios/smbios.h" #include "qapi/visitor.h" #include "standard-headers/linux/input.h" +#include "hw/arm/smmuv3.h" #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ @@ -93,8 +94,6 @@ #define PLATFORM_BUS_NUM_IRQS 64 -static ARMPlatformBusSystemParams platform_bus_params; - /* RAM limit in GB. Since VIRT_MEM starts at the 1GB mark, this means * RAM can go up to the 256GB mark, leaving 256GB of the physical * address space unallocated and free for future use between 256G and 512G. @@ -141,6 +140,7 @@ static const MemMapEntry a15memmap[] = { [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, [VIRT_GPIO] = { 0x09030000, 0x00001000 }, [VIRT_SECURE_UART] = { 0x09040000, 0x00001000 }, + [VIRT_SMMU] = { 0x09050000, 0x00020000 }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, @@ -149,6 +149,9 @@ static const MemMapEntry a15memmap[] = { [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES }, + /* Additional 64 MB redist region (can contain up to 512 redistributors) */ + [VIRT_GIC_REDIST2] = { 0x4000000000ULL, 0x4000000 }, + [VIRT_PCIE_ECAM_HIGH] = { 0x4010000000ULL, 0x10000000 }, /* Second PCIe window, 512GB wide at the 512GB boundary */ [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL }, }; @@ -161,6 +164,7 @@ static const int a15irqmap[] = { [VIRT_SECURE_UART] = 8, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ + [VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */ [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */ }; @@ -200,13 +204,8 @@ static void create_fdt(VirtMachineState *vms) qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2); qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2); - /* - * /chosen and /memory nodes must exist for load_dtb - * to fill in necessary properties later - */ + /* /chosen must exist for load_dtb to fill in necessary properties later */ qemu_fdt_add_subnode(fdt, "/chosen"); - qemu_fdt_add_subnode(fdt, "/memory"); - qemu_fdt_setprop_string(fdt, "/memory", "device_type", "memory"); /* Clock node, for the benefit of the UART. The kernel device tree * binding documentation claims the PL011 node clock properties are @@ -365,66 +364,98 @@ static void fdt_add_cpu_nodes(const VirtMachineState *vms) static void fdt_add_its_gic_node(VirtMachineState *vms) { + char *nodename; + vms->msi_phandle = qemu_fdt_alloc_phandle(vms->fdt); - qemu_fdt_add_subnode(vms->fdt, "/intc/its"); - qemu_fdt_setprop_string(vms->fdt, "/intc/its", "compatible", + nodename = g_strdup_printf("/intc/its@%" PRIx64, + vms->memmap[VIRT_GIC_ITS].base); + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "arm,gic-v3-its"); - qemu_fdt_setprop(vms->fdt, "/intc/its", "msi-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc/its", "reg", + qemu_fdt_setprop(vms->fdt, nodename, "msi-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_ITS].base, 2, vms->memmap[VIRT_GIC_ITS].size); - qemu_fdt_setprop_cell(vms->fdt, "/intc/its", "phandle", vms->msi_phandle); + qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->msi_phandle); + g_free(nodename); } static void fdt_add_v2m_gic_node(VirtMachineState *vms) { + char *nodename; + + nodename = g_strdup_printf("/intc/v2m@%" PRIx64, + vms->memmap[VIRT_GIC_V2M].base); vms->msi_phandle = qemu_fdt_alloc_phandle(vms->fdt); - qemu_fdt_add_subnode(vms->fdt, "/intc/v2m"); - qemu_fdt_setprop_string(vms->fdt, "/intc/v2m", "compatible", + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "arm,gic-v2m-frame"); - qemu_fdt_setprop(vms->fdt, "/intc/v2m", "msi-controller", NULL, 0); - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc/v2m", "reg", + qemu_fdt_setprop(vms->fdt, nodename, "msi-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_V2M].base, 2, vms->memmap[VIRT_GIC_V2M].size); - qemu_fdt_setprop_cell(vms->fdt, "/intc/v2m", "phandle", vms->msi_phandle); + qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->msi_phandle); + g_free(nodename); } static void fdt_add_gic_node(VirtMachineState *vms) { + char *nodename; + vms->gic_phandle = qemu_fdt_alloc_phandle(vms->fdt); qemu_fdt_setprop_cell(vms->fdt, "/", "interrupt-parent", vms->gic_phandle); - qemu_fdt_add_subnode(vms->fdt, "/intc"); - qemu_fdt_setprop_cell(vms->fdt, "/intc", "#interrupt-cells", 3); - qemu_fdt_setprop(vms->fdt, "/intc", "interrupt-controller", NULL, 0); - qemu_fdt_setprop_cell(vms->fdt, "/intc", "#address-cells", 0x2); - qemu_fdt_setprop_cell(vms->fdt, "/intc", "#size-cells", 0x2); - qemu_fdt_setprop(vms->fdt, "/intc", "ranges", NULL, 0); + nodename = g_strdup_printf("/intc@%" PRIx64, + vms->memmap[VIRT_GIC_DIST].base); + qemu_fdt_add_subnode(vms->fdt, nodename); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#interrupt-cells", 3); + qemu_fdt_setprop(vms->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 0x2); + qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 0x2); + qemu_fdt_setprop(vms->fdt, nodename, "ranges", NULL, 0); if (vms->gic_version == 3) { - qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", + int nb_redist_regions = virt_gicv3_redist_region_count(vms); + + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "arm,gic-v3"); - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", - 2, vms->memmap[VIRT_GIC_DIST].base, - 2, vms->memmap[VIRT_GIC_DIST].size, - 2, vms->memmap[VIRT_GIC_REDIST].base, - 2, vms->memmap[VIRT_GIC_REDIST].size); + + qemu_fdt_setprop_cell(vms->fdt, nodename, + "#redistributor-regions", nb_redist_regions); + + if (nb_redist_regions == 1) { + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_REDIST].base, + 2, vms->memmap[VIRT_GIC_REDIST].size); + } else { + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_REDIST].base, + 2, vms->memmap[VIRT_GIC_REDIST].size, + 2, vms->memmap[VIRT_GIC_REDIST2].base, + 2, vms->memmap[VIRT_GIC_REDIST2].size); + } + if (vms->virt) { - qemu_fdt_setprop_cells(vms->fdt, "/intc", "interrupts", + qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", GIC_FDT_IRQ_TYPE_PPI, ARCH_GICV3_MAINT_IRQ, GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } else { /* 'cortex-a15-gic' means 'GIC v2' */ - qemu_fdt_setprop_string(vms->fdt, "/intc", "compatible", + qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "arm,cortex-a15-gic"); - qemu_fdt_setprop_sized_cells(vms->fdt, "/intc", "reg", + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, vms->memmap[VIRT_GIC_DIST].base, 2, vms->memmap[VIRT_GIC_DIST].size, 2, vms->memmap[VIRT_GIC_CPU].base, 2, vms->memmap[VIRT_GIC_CPU].size); } - qemu_fdt_setprop_cell(vms->fdt, "/intc", "phandle", vms->gic_phandle); + qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->gic_phandle); + g_free(nodename); } static void fdt_add_pmu_nodes(const VirtMachineState *vms) @@ -509,6 +540,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) SysBusDevice *gicbusdev; const char *gictype; int type = vms->gic_version, i; + uint32_t nb_redist_regions = 0; gictype = (type == 3) ? gicv3_class_name() : gic_class_name(); @@ -522,11 +554,34 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) if (!kvm_irqchip_in_kernel()) { qdev_prop_set_bit(gicdev, "has-security-extensions", vms->secure); } + + if (type == 3) { + uint32_t redist0_capacity = + vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + uint32_t redist0_count = MIN(smp_cpus, redist0_capacity); + + nb_redist_regions = virt_gicv3_redist_region_count(vms); + + qdev_prop_set_uint32(gicdev, "len-redist-region-count", + nb_redist_regions); + qdev_prop_set_uint32(gicdev, "redist-region-count[0]", redist0_count); + + if (nb_redist_regions == 2) { + uint32_t redist1_capacity = + vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE; + + qdev_prop_set_uint32(gicdev, "redist-region-count[1]", + MIN(smp_cpus - redist0_count, redist1_capacity)); + } + } qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base); if (type == 3) { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base); + if (nb_redist_regions == 2) { + sysbus_mmio_map(gicbusdev, 2, vms->memmap[VIRT_GIC_REDIST2].base); + } } else { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_CPU].base); } @@ -942,7 +997,57 @@ static void create_pcie_irq_map(const VirtMachineState *vms, 0x7 /* PCI irq */); } -static void create_pcie(const VirtMachineState *vms, qemu_irq *pic) +static void create_smmu(const VirtMachineState *vms, qemu_irq *pic, + PCIBus *bus) +{ + char *node; + const char compat[] = "arm,smmu-v3"; + int irq = vms->irqmap[VIRT_SMMU]; + int i; + hwaddr base = vms->memmap[VIRT_SMMU].base; + hwaddr size = vms->memmap[VIRT_SMMU].size; + const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror"; + DeviceState *dev; + + if (vms->iommu != VIRT_IOMMU_SMMUV3 || !vms->iommu_phandle) { + return; + } + + dev = qdev_create(NULL, "arm-smmuv3"); + + object_property_set_link(OBJECT(dev), OBJECT(bus), "primary-bus", + &error_abort); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + for (i = 0; i < NUM_SMMU_IRQS; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]); + } + + node = g_strdup_printf("/smmuv3@%" PRIx64, base); + qemu_fdt_add_subnode(vms->fdt, node); + qemu_fdt_setprop(vms->fdt, node, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(vms->fdt, node, "reg", 2, base, 2, size); + + qemu_fdt_setprop_cells(vms->fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + + qemu_fdt_setprop(vms->fdt, node, "interrupt-names", irq_names, + sizeof(irq_names)); + + qemu_fdt_setprop_cell(vms->fdt, node, "clocks", vms->clock_phandle); + qemu_fdt_setprop_string(vms->fdt, node, "clock-names", "apb_pclk"); + qemu_fdt_setprop(vms->fdt, node, "dma-coherent", NULL, 0); + + qemu_fdt_setprop_cell(vms->fdt, node, "#iommu-cells", 1); + + qemu_fdt_setprop_cell(vms->fdt, node, "phandle", vms->iommu_phandle); + g_free(node); +} + +static void create_pcie(VirtMachineState *vms, qemu_irq *pic) { hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = vms->memmap[VIRT_PCIE_MMIO].size; @@ -950,10 +1055,9 @@ static void create_pcie(const VirtMachineState *vms, qemu_irq *pic) hwaddr size_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].size; hwaddr base_pio = vms->memmap[VIRT_PCIE_PIO].base; hwaddr size_pio = vms->memmap[VIRT_PCIE_PIO].size; - hwaddr base_ecam = vms->memmap[VIRT_PCIE_ECAM].base; - hwaddr size_ecam = vms->memmap[VIRT_PCIE_ECAM].size; + hwaddr base_ecam, size_ecam; hwaddr base = base_mmio; - int nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; + int nr_pcie_buses; int irq = vms->irqmap[VIRT_PCIE]; MemoryRegion *mmio_alias; MemoryRegion *mmio_reg; @@ -961,12 +1065,16 @@ static void create_pcie(const VirtMachineState *vms, qemu_irq *pic) MemoryRegion *ecam_reg; DeviceState *dev; char *nodename; - int i; + int i, ecam_id; PCIHostState *pci; dev = qdev_create(NULL, TYPE_GPEX_HOST); qdev_init_nofail(dev); + ecam_id = VIRT_ECAM_ID(vms->highmem_ecam); + base_ecam = vms->memmap[ecam_id].base; + size_ecam = vms->memmap[ecam_id].size; + nr_pcie_buses = size_ecam / PCIE_MMCFG_SIZE_MIN; /* Map only the first size_ecam bytes of ECAM space */ ecam_alias = g_new0(MemoryRegion, 1); ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); @@ -1023,6 +1131,7 @@ static void create_pcie(const VirtMachineState *vms, qemu_irq *pic) qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "pci"); qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 3); qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 2); + qemu_fdt_setprop_cell(vms->fdt, nodename, "linux,pci-domain", 0); qemu_fdt_setprop_cells(vms->fdt, nodename, "bus-range", 0, nr_pcie_buses - 1); qemu_fdt_setprop(vms->fdt, nodename, "dma-coherent", NULL, 0); @@ -1055,6 +1164,15 @@ static void create_pcie(const VirtMachineState *vms, qemu_irq *pic) qemu_fdt_setprop_cell(vms->fdt, nodename, "#interrupt-cells", 1); create_pcie_irq_map(vms, vms->gic_phandle, irq, nodename); + if (vms->iommu) { + vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt); + + create_smmu(vms, pic, pci->bus); + + qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, 0x10000); + } + g_free(nodename); } @@ -1063,39 +1181,23 @@ static void create_platform_bus(VirtMachineState *vms, qemu_irq *pic) DeviceState *dev; SysBusDevice *s; int i; - ARMPlatformBusFDTParams *fdt_params = g_new(ARMPlatformBusFDTParams, 1); MemoryRegion *sysmem = get_system_memory(); - platform_bus_params.platform_bus_base = vms->memmap[VIRT_PLATFORM_BUS].base; - platform_bus_params.platform_bus_size = vms->memmap[VIRT_PLATFORM_BUS].size; - platform_bus_params.platform_bus_first_irq = vms->irqmap[VIRT_PLATFORM_BUS]; - platform_bus_params.platform_bus_num_irqs = PLATFORM_BUS_NUM_IRQS; - - fdt_params->system_params = &platform_bus_params; - fdt_params->binfo = &vms->bootinfo; - fdt_params->intc = "/intc"; - /* - * register a machine init done notifier that creates the device tree - * nodes of the platform bus and its children dynamic sysbus devices - */ - arm_register_platform_bus_fdt_creator(fdt_params); - dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE); dev->id = TYPE_PLATFORM_BUS_DEVICE; - qdev_prop_set_uint32(dev, "num_irqs", - platform_bus_params.platform_bus_num_irqs); - qdev_prop_set_uint32(dev, "mmio_size", - platform_bus_params.platform_bus_size); + qdev_prop_set_uint32(dev, "num_irqs", PLATFORM_BUS_NUM_IRQS); + qdev_prop_set_uint32(dev, "mmio_size", vms->memmap[VIRT_PLATFORM_BUS].size); qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); + vms->platform_bus_dev = dev; - for (i = 0; i < platform_bus_params.platform_bus_num_irqs; i++) { - int irqn = platform_bus_params.platform_bus_first_irq + i; + s = SYS_BUS_DEVICE(dev); + for (i = 0; i < PLATFORM_BUS_NUM_IRQS; i++) { + int irqn = vms->irqmap[VIRT_PLATFORM_BUS] + i; sysbus_connect_irq(s, i, pic[irqn]); } memory_region_add_subregion(sysmem, - platform_bus_params.platform_bus_base, + vms->memmap[VIRT_PLATFORM_BUS].base, sysbus_mmio_get_region(s, 0)); } @@ -1166,6 +1268,26 @@ void virt_machine_done(Notifier *notifier, void *data) { VirtMachineState *vms = container_of(notifier, VirtMachineState, machine_done); + ARMCPU *cpu = ARM_CPU(first_cpu); + struct arm_boot_info *info = &vms->bootinfo; + AddressSpace *as = arm_boot_address_space(cpu, info); + + /* + * If the user provided a dtb, we assume the dynamic sysbus nodes + * already are integrated there. This corresponds to a use case where + * the dynamic sysbus nodes are complex and their generation is not yet + * supported. In that case the user can take charge of the guest dt + * while qemu takes charge of the qom stuff. + */ + if (info->dtb_filename == NULL) { + platform_bus_add_all_fdt_nodes(vms->fdt, "/intc", + vms->memmap[VIRT_PLATFORM_BUS].base, + vms->memmap[VIRT_PLATFORM_BUS].size, + vms->irqmap[VIRT_PLATFORM_BUS]); + } + if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as) < 0) { + exit(1); + } virt_acpi_setup(vms); virt_build_smbios(vms); @@ -1206,6 +1328,7 @@ static void machvirt_init(MachineState *machine) int n, virt_max_cpus; MemoryRegion *ram = g_new(MemoryRegion, 1); bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); + bool aarch64 = true; /* We can probe only here because during property set * KVM is not available yet @@ -1257,7 +1380,8 @@ static void machvirt_init(MachineState *machine) * many redistributors we can fit into the memory map. */ if (vms->gic_version == 3) { - virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / 0x20000; + virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + virt_max_cpus += vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE; } else { virt_max_cpus = GIC_NCPU; } @@ -1320,6 +1444,8 @@ static void machvirt_init(MachineState *machine) numa_cpu_pre_plug(&possible_cpus->cpus[cs->cpu_index], DEVICE(cpuobj), &error_fatal); + aarch64 &= object_property_get_bool(cpuobj, "aarch64", NULL); + if (!vms->secure) { object_property_set_bool(cpuobj, false, "has_el3", NULL); } @@ -1371,13 +1497,15 @@ static void machvirt_init(MachineState *machine) fdt_add_pmu_nodes(vms); - create_uart(vms, pic, VIRT_UART, sysmem, serial_hds[0]); + create_uart(vms, pic, VIRT_UART, sysmem, serial_hd(0)); if (vms->secure) { create_secure_ram(vms, secure_sysmem); - create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hds[1]); + create_uart(vms, pic, VIRT_SECURE_UART, secure_sysmem, serial_hd(1)); } + vms->highmem_ecam &= vms->highmem && (!firmware_loaded || aarch64); + create_rtc(vms, pic); create_pcie(vms, pic); @@ -1393,8 +1521,7 @@ static void machvirt_init(MachineState *machine) vms->fw_cfg = create_fw_cfg(vms, &address_space_memory); rom_set_fw(vms->fw_cfg); - vms->machine_done.notify = virt_machine_done; - qemu_add_machine_init_done_notifier(&vms->machine_done); + create_platform_bus(vms, pic); vms->bootinfo.ram_size = machine->ram_size; vms->bootinfo.kernel_filename = machine->kernel_filename; @@ -1404,16 +1531,12 @@ static void machvirt_init(MachineState *machine) vms->bootinfo.board_id = -1; vms->bootinfo.loader_start = vms->memmap[VIRT_MEM].base; vms->bootinfo.get_dtb = machvirt_dtb; + vms->bootinfo.skip_dtb_autoload = true; vms->bootinfo.firmware_loaded = firmware_loaded; arm_load_kernel(ARM_CPU(first_cpu), &vms->bootinfo); - /* - * arm_load_kernel machine init done notifier registration must - * happen before the platform_bus_create call. In this latter, - * another notifier is registered which adds platform bus nodes. - * Notifiers are executed in registration reverse order. - */ - create_platform_bus(vms, pic); + vms->machine_done.notify = virt_machine_done; + qemu_add_machine_init_done_notifier(&vms->machine_done); } static bool virt_get_secure(Object *obj, Error **errp) @@ -1498,6 +1621,34 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp) } } +static char *virt_get_iommu(Object *obj, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + switch (vms->iommu) { + case VIRT_IOMMU_NONE: + return g_strdup("none"); + case VIRT_IOMMU_SMMUV3: + return g_strdup("smmuv3"); + default: + g_assert_not_reached(); + } +} + +static void virt_set_iommu(Object *obj, const char *value, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(obj); + + if (!strcmp(value, "smmuv3")) { + vms->iommu = VIRT_IOMMU_SMMUV3; + } else if (!strcmp(value, "none")) { + vms->iommu = VIRT_IOMMU_NONE; + } else { + error_setg(errp, "Invalid iommu value"); + error_append_hint(errp, "Valid values are none, smmuv3.\n"); + } +} + static CpuInstanceProperties virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { @@ -1536,18 +1687,43 @@ static const CPUArchIdList *virt_possible_cpu_arch_ids(MachineState *ms) return ms->possible_cpus; } +static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + VirtMachineState *vms = VIRT_MACHINE(hotplug_dev); + + if (vms->platform_bus_dev) { + if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) { + platform_bus_link_device(PLATFORM_BUS_DEVICE(vms->platform_bus_dev), + SYS_BUS_DEVICE(dev)); + } + } +} + +static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, + DeviceState *dev) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) { + return HOTPLUG_HANDLER(machine); + } + + return NULL; +} + static void virt_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); mc->init = machvirt_init; - /* Start max_cpus at the maximum QEMU supports. We'll further restrict - * it later in machvirt_init, where we have more information about the + /* Start with max_cpus set to 512, which is the maximum supported by KVM. + * The value may be reduced later when we have more information about the * configuration of the particular instance. */ - mc->max_cpus = 255; + mc->max_cpus = 512; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->pci_allow_0_address = true; @@ -1557,6 +1733,9 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = virt_cpu_index_to_props; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; + assert(!mc->get_hotplug_handler); + mc->get_hotplug_handler = virt_machine_get_hotplug_handler; + hc->plug = virt_machine_device_plug_cb; } static const TypeInfo virt_machine_info = { @@ -1566,6 +1745,10 @@ static const TypeInfo virt_machine_info = { .instance_size = sizeof(VirtMachineState), .class_size = sizeof(VirtMachineClass), .class_init = virt_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, }; static void machvirt_machine_init(void) @@ -1574,7 +1757,10 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -static void virt_2_12_instance_init(Object *obj) +#define VIRT_COMPAT_2_12 \ + HW_COMPAT_2_12 + +static void virt_3_0_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); @@ -1617,6 +1803,8 @@ static void virt_2_12_instance_init(Object *obj) "Set GIC version. " "Valid values are 2, 3 and host", NULL); + vms->highmem_ecam = !vmc->no_highmem_ecam; + if (vmc->no_its) { vms->its = false; } else { @@ -1630,14 +1818,38 @@ static void virt_2_12_instance_init(Object *obj) NULL); } + /* Default disallows iommu instantiation */ + vms->iommu = VIRT_IOMMU_NONE; + object_property_add_str(obj, "iommu", virt_get_iommu, virt_set_iommu, NULL); + object_property_set_description(obj, "iommu", + "Set the IOMMU type. " + "Valid values are none and smmuv3", + NULL); + vms->memmap = a15memmap; vms->irqmap = a15irqmap; } -static void virt_machine_2_12_options(MachineClass *mc) +static void virt_machine_3_0_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(2, 12) +DEFINE_VIRT_MACHINE_AS_LATEST(3, 0) + +static void virt_2_12_instance_init(Object *obj) +{ + virt_3_0_instance_init(obj); +} + +static void virt_machine_2_12_options(MachineClass *mc) +{ + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + + virt_machine_3_0_options(mc); + SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); + vmc->no_highmem_ecam = true; + mc->max_cpus = 255; +} +DEFINE_VIRT_MACHINE(2, 12) #define VIRT_COMPAT_2_11 \ HW_COMPAT_2_11 diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 0f76333770..f1496d2927 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -26,7 +26,6 @@ #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "hw/loader.h" #include "hw/misc/zynq-xadc.h" #include "hw/ssi/ssi.h" @@ -236,8 +235,8 @@ static void zynq_init(MachineState *machine) sysbus_create_simple("xlnx,ps7-usb", 0xE0002000, pic[53-IRQ_OFFSET]); sysbus_create_simple("xlnx,ps7-usb", 0xE0003000, pic[76-IRQ_OFFSET]); - cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hds[0]); - cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hds[1]); + cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hd(0)); + cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hd(1)); sysbus_create_varargs("cadence_ttc", 0xF8001000, pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL); diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index b126cf148b..b6bc6a93b8 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -22,7 +22,6 @@ #include "hw/arm/xlnx-zynqmp.h" #include "hw/boards.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" #include "qemu/log.h" #include "sysemu/qtest.h" @@ -40,10 +39,6 @@ typedef struct XlnxZCU102 { #define ZCU102_MACHINE(obj) \ OBJECT_CHECK(XlnxZCU102, (obj), TYPE_ZCU102_MACHINE) -#define TYPE_EP108_MACHINE MACHINE_TYPE_NAME("xlnx-ep108") -#define EP108_MACHINE(obj) \ - OBJECT_CHECK(XlnxZCU102, (obj), TYPE_EP108_MACHINE) - static struct arm_boot_info xlnx_zcu102_binfo; static bool zcu102_get_secure(Object *obj, Error **errp) @@ -74,8 +69,9 @@ static void zcu102_set_virt(Object *obj, bool value, Error **errp) s->virt = value; } -static void xlnx_zynqmp_init(XlnxZCU102 *s, MachineState *machine) +static void xlnx_zcu102_init(MachineState *machine) { + XlnxZCU102 *s = ZCU102_MACHINE(machine); int i; uint64_t ram_size = machine->ram_size; @@ -184,60 +180,6 @@ static void xlnx_zynqmp_init(XlnxZCU102 *s, MachineState *machine) arm_load_kernel(s->soc.boot_cpu_ptr, &xlnx_zcu102_binfo); } -static void xlnx_ep108_init(MachineState *machine) -{ - XlnxZCU102 *s = EP108_MACHINE(machine); - - if (!qtest_enabled()) { - info_report("The Xilinx EP108 machine is deprecated, please use the " - "ZCU102 machine (which has the same features) instead."); - } - - xlnx_zynqmp_init(s, machine); -} - -static void xlnx_ep108_machine_instance_init(Object *obj) -{ - XlnxZCU102 *s = EP108_MACHINE(obj); - - /* EP108, we don't support setting secure or virt */ - s->secure = false; - s->virt = false; -} - -static void xlnx_ep108_machine_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Xilinx ZynqMP EP108 board (Deprecated, please use xlnx-zcu102)"; - mc->init = xlnx_ep108_init; - mc->block_default_type = IF_IDE; - mc->units_per_default_bus = 1; - mc->ignore_memory_transaction_failures = true; - mc->max_cpus = XLNX_ZYNQMP_NUM_APU_CPUS + XLNX_ZYNQMP_NUM_RPU_CPUS; - mc->default_cpus = XLNX_ZYNQMP_NUM_APU_CPUS; -} - -static const TypeInfo xlnx_ep108_machine_init_typeinfo = { - .name = MACHINE_TYPE_NAME("xlnx-ep108"), - .parent = TYPE_MACHINE, - .class_init = xlnx_ep108_machine_class_init, - .instance_init = xlnx_ep108_machine_instance_init, - .instance_size = sizeof(XlnxZCU102), -}; - -static void xlnx_ep108_machine_init_register_types(void) -{ - type_register_static(&xlnx_ep108_machine_init_typeinfo); -} - -static void xlnx_zcu102_init(MachineState *machine) -{ - XlnxZCU102 *s = ZCU102_MACHINE(machine); - - xlnx_zynqmp_init(s, machine); -} - static void xlnx_zcu102_machine_instance_init(Object *obj) { XlnxZCU102 *s = ZCU102_MACHINE(obj); @@ -266,7 +208,7 @@ static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->desc = "Xilinx ZynqMP ZCU102 board with 4xA53s and 2xR5s based on " \ + mc->desc = "Xilinx ZynqMP ZCU102 board with 4xA53s and 2xR5Fs based on " \ "the value of smp"; mc->init = xlnx_zcu102_init; mc->block_default_type = IF_IDE; @@ -290,4 +232,3 @@ static void xlnx_zcu102_machine_init_register_types(void) } type_init(xlnx_zcu102_machine_init_register_types) -type_init(xlnx_ep108_machine_init_register_types) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 465796e97c..8de4868eb9 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -90,6 +90,24 @@ static const int spi_intr[XLNX_ZYNQMP_NUM_SPIS] = { 19, 20, }; +static const uint64_t gdma_ch_addr[XLNX_ZYNQMP_NUM_GDMA_CH] = { + 0xFD500000, 0xFD510000, 0xFD520000, 0xFD530000, + 0xFD540000, 0xFD550000, 0xFD560000, 0xFD570000 +}; + +static const int gdma_ch_intr[XLNX_ZYNQMP_NUM_GDMA_CH] = { + 124, 125, 126, 127, 128, 129, 130, 131 +}; + +static const uint64_t adma_ch_addr[XLNX_ZYNQMP_NUM_ADMA_CH] = { + 0xFFA80000, 0xFFA90000, 0xFFAA0000, 0xFFAB0000, + 0xFFAC0000, 0xFFAD0000, 0xFFAE0000, 0xFFAF0000 +}; + +static const int adma_ch_intr[XLNX_ZYNQMP_NUM_ADMA_CH] = { + 77, 78, 79, 80, 81, 82, 83, 84 +}; + typedef struct XlnxZynqMPGICRegion { int region_index; uint32_t address; @@ -116,7 +134,7 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu, char *name; object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]), - "cortex-r5-" TYPE_ARM_CPU); + "cortex-r5f-" TYPE_ARM_CPU); object_property_add_child(OBJECT(s), "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]), &error_abort); @@ -148,55 +166,60 @@ static void xlnx_zynqmp_init(Object *obj) int num_apus = MIN(smp_cpus, XLNX_ZYNQMP_NUM_APU_CPUS); for (i = 0; i < num_apus; i++) { - object_initialize(&s->apu_cpu[i], sizeof(s->apu_cpu[i]), - "cortex-a53-" TYPE_ARM_CPU); - object_property_add_child(obj, "apu-cpu[*]", OBJECT(&s->apu_cpu[i]), - &error_abort); + object_initialize_child(obj, "apu-cpu[*]", &s->apu_cpu[i], + sizeof(s->apu_cpu[i]), + "cortex-a53-" TYPE_ARM_CPU, &error_abort, NULL); } - object_initialize(&s->gic, sizeof(s->gic), gic_class_name()); - qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), + gic_class_name()); for (i = 0; i < XLNX_ZYNQMP_NUM_GEMS; i++) { - object_initialize(&s->gem[i], sizeof(s->gem[i]), TYPE_CADENCE_GEM); - qdev_set_parent_bus(DEVICE(&s->gem[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gem[*]", &s->gem[i], sizeof(s->gem[i]), + TYPE_CADENCE_GEM); } for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { - object_initialize(&s->uart[i], sizeof(s->uart[i]), TYPE_CADENCE_UART); - qdev_set_parent_bus(DEVICE(&s->uart[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "uart[*]", &s->uart[i], sizeof(s->uart[i]), + TYPE_CADENCE_UART); } - object_initialize(&s->sata, sizeof(s->sata), TYPE_SYSBUS_AHCI); - qdev_set_parent_bus(DEVICE(&s->sata), sysbus_get_default()); + sysbus_init_child_obj(obj, "sata", &s->sata, sizeof(s->sata), + TYPE_SYSBUS_AHCI); for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { - object_initialize(&s->sdhci[i], sizeof(s->sdhci[i]), - TYPE_SYSBUS_SDHCI); - qdev_set_parent_bus(DEVICE(&s->sdhci[i]), - sysbus_get_default()); + sysbus_init_child_obj(obj, "sdhci[*]", &s->sdhci[i], + sizeof(s->sdhci[i]), TYPE_SYSBUS_SDHCI); } for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) { - object_initialize(&s->spi[i], sizeof(s->spi[i]), - TYPE_XILINX_SPIPS); - qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "spi[*]", &s->spi[i], sizeof(s->spi[i]), + TYPE_XILINX_SPIPS); } - object_initialize(&s->qspi, sizeof(s->qspi), TYPE_XLNX_ZYNQMP_QSPIPS); - qdev_set_parent_bus(DEVICE(&s->qspi), sysbus_get_default()); + sysbus_init_child_obj(obj, "qspi", &s->qspi, sizeof(s->qspi), + TYPE_XLNX_ZYNQMP_QSPIPS); - object_initialize(&s->dp, sizeof(s->dp), TYPE_XLNX_DP); - qdev_set_parent_bus(DEVICE(&s->dp), sysbus_get_default()); + sysbus_init_child_obj(obj, "xxxdp", &s->dp, sizeof(s->dp), TYPE_XLNX_DP); - object_initialize(&s->dpdma, sizeof(s->dpdma), TYPE_XLNX_DPDMA); - qdev_set_parent_bus(DEVICE(&s->dpdma), sysbus_get_default()); + sysbus_init_child_obj(obj, "dp-dma", &s->dpdma, sizeof(s->dpdma), + TYPE_XLNX_DPDMA); - object_initialize(&s->ipi, sizeof(s->ipi), TYPE_XLNX_ZYNQMP_IPI); - qdev_set_parent_bus(DEVICE(&s->ipi), sysbus_get_default()); + sysbus_init_child_obj(obj, "ipi", &s->ipi, sizeof(s->ipi), + TYPE_XLNX_ZYNQMP_IPI); - object_initialize(&s->rtc, sizeof(s->rtc), TYPE_XLNX_ZYNQMP_RTC); - qdev_set_parent_bus(DEVICE(&s->rtc), sysbus_get_default()); + sysbus_init_child_obj(obj, "rtc", &s->rtc, sizeof(s->rtc), + TYPE_XLNX_ZYNQMP_RTC); + + for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { + sysbus_init_child_obj(obj, "gdma[*]", &s->gdma[i], sizeof(s->gdma[i]), + TYPE_XLNX_ZDMA); + } + + for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA_CH; i++) { + sysbus_init_child_obj(obj, "adma[*]", &s->adma[i], sizeof(s->adma[i]), + TYPE_XLNX_ZDMA); + } } static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) @@ -374,7 +397,7 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } for (i = 0; i < XLNX_ZYNQMP_NUM_UARTS; i++) { - qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hds[i]); + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", &err); if (err) { error_propagate(errp, err); @@ -492,6 +515,31 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) } sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, RTC_ADDR); sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]); + + for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA_CH; i++) { + object_property_set_uint(OBJECT(&s->gdma[i]), 128, "bus-width", &err); + object_property_set_bool(OBJECT(&s->gdma[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gdma[i]), 0, gdma_ch_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gdma[i]), 0, + gic_spi[gdma_ch_intr[i]]); + } + + for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA_CH; i++) { + object_property_set_bool(OBJECT(&s->adma[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->adma[i]), 0, adma_ch_addr[i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->adma[i]), 0, + gic_spi[adma_ch_intr[i]]); + } } static Property xlnx_zynqmp_props[] = { diff --git a/hw/arm/z2.c b/hw/arm/z2.c index 730a5392e9..697a822f1e 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -21,7 +21,6 @@ #include "hw/boards.h" #include "sysemu/sysemu.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "ui/console.h" #include "hw/audio/wm8750.h" #include "audio/audio.h" diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index e8aa7842e6..617a1c1016 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -23,6 +23,7 @@ #include "intel-hda.h" #include "intel-hda-defs.h" #include "audio/audio.h" +#include "trace.h" /* -------------------------------------------------------------------------- */ @@ -126,6 +127,10 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) #define PARAM nomixemu #include "hda-codec-common.h" +#define HDA_TIMER_TICKS (SCALE_MS) +#define B_SIZE sizeof(st->buf) +#define B_MASK (sizeof(st->buf) - 1) + /* -------------------------------------------------------------------------- */ static const char *fmt2name[] = { @@ -154,8 +159,13 @@ struct HDAAudioStream { SWVoiceIn *in; SWVoiceOut *out; } voice; - uint8_t buf[HDA_BUFFER_SIZE]; - uint32_t bpos; + uint8_t compat_buf[HDA_BUFFER_SIZE]; + uint32_t compat_bpos; + uint8_t buf[8192]; /* size must be power of two */ + int64_t rpos; + int64_t wpos; + QEMUTimer *buft; + int64_t buft_start; }; #define TYPE_HDA_AUDIO "hda-audio" @@ -174,55 +184,220 @@ struct HDAAudioState { /* properties */ uint32_t debug; bool mixer; + bool use_timer; }; +static inline int64_t hda_bytes_per_second(HDAAudioStream *st) +{ + return 2LL * st->as.nchannels * st->as.freq; +} + +static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos) +{ + int64_t limit = B_SIZE / 8; + int64_t corr = 0; + + if (target_pos > limit) { + corr = HDA_TIMER_TICKS; + } + if (target_pos < -limit) { + corr = -HDA_TIMER_TICKS; + } + if (target_pos < -(2 * limit)) { + corr = -(4 * HDA_TIMER_TICKS); + } + if (corr == 0) { + return; + } + + trace_hda_audio_adjust(st->node->name, target_pos); + st->buft_start += corr; +} + +static void hda_audio_input_timer(void *opaque) +{ + HDAAudioStream *st = opaque; + + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + int64_t buft_start = st->buft_start; + int64_t wpos = st->wpos; + int64_t rpos = st->rpos; + + int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start) + / NANOSECONDS_PER_SECOND; + wanted_rpos &= -4; /* IMPORTANT! clip to frames */ + + if (wanted_rpos <= rpos) { + /* we already transmitted the data */ + goto out_timer; + } + + int64_t to_transfer = audio_MIN(wpos - rpos, wanted_rpos - rpos); + while (to_transfer) { + uint32_t start = (rpos & B_MASK); + uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer); + int rc = hda_codec_xfer( + &st->state->hda, st->stream, false, st->buf + start, chunk); + if (!rc) { + break; + } + rpos += chunk; + to_transfer -= chunk; + st->rpos += chunk; + } + +out_timer: + + if (st->running) { + timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); + } +} + static void hda_audio_input_cb(void *opaque, int avail) { HDAAudioStream *st = opaque; - int recv = 0; - int len; - bool rc; - while (avail - recv >= sizeof(st->buf)) { - if (st->bpos != sizeof(st->buf)) { - len = AUD_read(st->voice.in, st->buf + st->bpos, - sizeof(st->buf) - st->bpos); - st->bpos += len; - recv += len; - if (st->bpos != sizeof(st->buf)) { - break; - } + int64_t wpos = st->wpos; + int64_t rpos = st->rpos; + + int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail); + + hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1))); + + while (to_transfer) { + uint32_t start = (uint32_t) (wpos & B_MASK); + uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer); + uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk); + wpos += read; + to_transfer -= read; + st->wpos += read; + if (chunk != read) { + break; } - rc = hda_codec_xfer(&st->state->hda, st->stream, false, - st->buf, sizeof(st->buf)); + } +} + +static void hda_audio_output_timer(void *opaque) +{ + HDAAudioStream *st = opaque; + + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + + int64_t buft_start = st->buft_start; + int64_t wpos = st->wpos; + int64_t rpos = st->rpos; + + int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start) + / NANOSECONDS_PER_SECOND; + wanted_wpos &= -4; /* IMPORTANT! clip to frames */ + + if (wanted_wpos <= wpos) { + /* we already received the data */ + goto out_timer; + } + + int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos); + while (to_transfer) { + uint32_t start = (wpos & B_MASK); + uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer); + int rc = hda_codec_xfer( + &st->state->hda, st->stream, true, st->buf + start, chunk); if (!rc) { break; } - st->bpos = 0; + wpos += chunk; + to_transfer -= chunk; + st->wpos += chunk; + } + +out_timer: + + if (st->running) { + timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); } } static void hda_audio_output_cb(void *opaque, int avail) +{ + HDAAudioStream *st = opaque; + + int64_t wpos = st->wpos; + int64_t rpos = st->rpos; + + int64_t to_transfer = audio_MIN(wpos - rpos, avail); + + if (wpos - rpos == B_SIZE) { + /* drop buffer, reset timer adjust */ + st->rpos = 0; + st->wpos = 0; + st->buft_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + trace_hda_audio_overrun(st->node->name); + return; + } + + hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1)); + + while (to_transfer) { + uint32_t start = (uint32_t) (rpos & B_MASK); + uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer); + uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk); + rpos += written; + to_transfer -= written; + st->rpos += written; + if (chunk != written) { + break; + } + } +} + +static void hda_audio_compat_input_cb(void *opaque, int avail) +{ + HDAAudioStream *st = opaque; + int recv = 0; + int len; + bool rc; + + while (avail - recv >= sizeof(st->compat_buf)) { + if (st->compat_bpos != sizeof(st->compat_buf)) { + len = AUD_read(st->voice.in, st->compat_buf + st->compat_bpos, + sizeof(st->compat_buf) - st->compat_bpos); + st->compat_bpos += len; + recv += len; + if (st->compat_bpos != sizeof(st->compat_buf)) { + break; + } + } + rc = hda_codec_xfer(&st->state->hda, st->stream, false, + st->compat_buf, sizeof(st->compat_buf)); + if (!rc) { + break; + } + st->compat_bpos = 0; + } +} + +static void hda_audio_compat_output_cb(void *opaque, int avail) { HDAAudioStream *st = opaque; int sent = 0; int len; bool rc; - while (avail - sent >= sizeof(st->buf)) { - if (st->bpos == sizeof(st->buf)) { + while (avail - sent >= sizeof(st->compat_buf)) { + if (st->compat_bpos == sizeof(st->compat_buf)) { rc = hda_codec_xfer(&st->state->hda, st->stream, true, - st->buf, sizeof(st->buf)); + st->compat_buf, sizeof(st->compat_buf)); if (!rc) { break; } - st->bpos = 0; + st->compat_bpos = 0; } - len = AUD_write(st->voice.out, st->buf + st->bpos, - sizeof(st->buf) - st->bpos); - st->bpos += len; + len = AUD_write(st->voice.out, st->compat_buf + st->compat_bpos, + sizeof(st->compat_buf) - st->compat_bpos); + st->compat_bpos += len; sent += len; - if (st->bpos != sizeof(st->buf)) { + if (st->compat_bpos != sizeof(st->compat_buf)) { break; } } @@ -237,8 +412,18 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running) return; } st->running = running; - dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name, - st->running ? "on" : "off", st->stream); + trace_hda_audio_running(st->node->name, st->stream, st->running); + if (st->state->use_timer) { + if (running) { + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + st->rpos = 0; + st->wpos = 0; + st->buft_start = now; + timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS); + } else { + timer_del(st->buft); + } + } if (st->output) { AUD_set_active_out(st->voice.out, st->running); } else { @@ -274,22 +459,36 @@ static void hda_audio_set_amp(HDAAudioStream *st) static void hda_audio_setup(HDAAudioStream *st) { + bool use_timer = st->state->use_timer; + audio_callback_fn cb; + if (st->node == NULL) { return; } - dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n", - st->node->name, st->as.nchannels, - fmt2name[st->as.fmt], st->as.freq); + trace_hda_audio_format(st->node->name, st->as.nchannels, + fmt2name[st->as.fmt], st->as.freq); if (st->output) { + if (use_timer) { + cb = hda_audio_output_cb; + st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, + hda_audio_output_timer, st); + } else { + cb = hda_audio_compat_output_cb; + } st->voice.out = AUD_open_out(&st->state->card, st->voice.out, - st->node->name, st, - hda_audio_output_cb, &st->as); + st->node->name, st, cb, &st->as); } else { + if (use_timer) { + cb = hda_audio_input_cb; + st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, + hda_audio_input_timer, st); + } else { + cb = hda_audio_compat_input_cb; + } st->voice.in = AUD_open_in(&st->state->card, st->voice.in, - st->node->name, st, - hda_audio_input_cb, &st->as); + st->node->name, st, cb, &st->as); } } @@ -505,7 +704,7 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) /* unmute output by default */ st->gain_left = QEMU_HDA_AMP_STEPS; st->gain_right = QEMU_HDA_AMP_STEPS; - st->bpos = sizeof(st->buf); + st->compat_bpos = sizeof(st->compat_buf); st->output = true; } else { st->output = false; @@ -532,6 +731,9 @@ static void hda_audio_exit(HDACodecDevice *hda) if (st->node == NULL) { continue; } + if (a->use_timer) { + timer_del(st->buft); + } if (st->output) { AUD_close_out(&a->card, st->voice.out); } else { @@ -581,6 +783,26 @@ static void hda_audio_reset(DeviceState *dev) } } +static bool vmstate_hda_audio_stream_buf_needed(void *opaque) +{ + HDAAudioStream *st = opaque; + return st->state && st->state->use_timer; +} + +static const VMStateDescription vmstate_hda_audio_stream_buf = { + .name = "hda-audio-stream/buffer", + .version_id = 1, + .needed = vmstate_hda_audio_stream_buf_needed, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(buf, HDAAudioStream), + VMSTATE_INT64(rpos, HDAAudioStream), + VMSTATE_INT64(wpos, HDAAudioStream), + VMSTATE_TIMER_PTR(buft, HDAAudioStream), + VMSTATE_INT64(buft_start, HDAAudioStream), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_hda_audio_stream = { .name = "hda-audio-stream", .version_id = 1, @@ -592,9 +814,13 @@ static const VMStateDescription vmstate_hda_audio_stream = { VMSTATE_UINT32(gain_right, HDAAudioStream), VMSTATE_BOOL(mute_left, HDAAudioStream), VMSTATE_BOOL(mute_right, HDAAudioStream), - VMSTATE_UINT32(bpos, HDAAudioStream), - VMSTATE_BUFFER(buf, HDAAudioStream), + VMSTATE_UINT32(compat_bpos, HDAAudioStream), + VMSTATE_BUFFER(compat_buf, HDAAudioStream), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_hda_audio_stream_buf, + NULL } }; @@ -615,6 +841,7 @@ static const VMStateDescription vmstate_hda_audio = { static Property hda_audio_properties[] = { DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), + DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 948268afd8..23a2cf6484 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -407,13 +407,6 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, if (st->bpl == NULL) { return false; } - if (st->ctl & (1 << 26)) { - /* - * Wait with the next DMA xfer until the guest - * has acked the buffer completion interrupt - */ - return false; - } left = len; s = st->bentries; diff --git a/hw/audio/trace-events b/hw/audio/trace-events index fa1646d169..5891b4e2b9 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -17,3 +17,9 @@ milkymist_ac97_in_cb(int avail, uint32_t remaining) "avail %d remaining %u" milkymist_ac97_in_cb_transferred(int transferred) "transferred %d" milkymist_ac97_out_cb(int free, uint32_t remaining) "free %d remaining %u" milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" + +# hw/audio/hda-codec.c +hda_audio_running(const char *stream, int nr, bool running) "st %s, nr %d, run %d" +hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz" +hda_audio_adjust(const char *stream, int pos) "st %s, pos %d" +hda_audio_overrun(const char *stream) "st %s" diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index 416a78e869..f4aa838f62 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -617,14 +617,12 @@ static const VMStateDescription vmstate_wm8750 = { } }; -static int wm8750_init(I2CSlave *i2c) +static void wm8750_realize(DeviceState *dev, Error **errp) { - WM8750State *s = WM8750(i2c); + WM8750State *s = WM8750(dev); AUD_register_card(CODEC, &s->card); wm8750_reset(I2C_SLAVE(s)); - - return 0; } #if 0 @@ -707,7 +705,7 @@ static void wm8750_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - sc->init = wm8750_init; + dc->realize = wm8750_realize; sc->event = wm8750_event; sc->recv = wm8750_rx; sc->send = wm8750_tx; diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs index 4c19a583c8..53ce5751ae 100644 --- a/hw/block/Makefile.objs +++ b/hw/block/Makefile.objs @@ -11,8 +11,6 @@ common-obj-$(CONFIG_NVME_PCI) += nvme.o obj-$(CONFIG_SH4) += tc58128.o -obj-$(CONFIG_VIRTIO) += virtio-blk.o -obj-$(CONFIG_VIRTIO) += dataplane/ -ifeq ($(CONFIG_VIRTIO),y) +obj-$(CONFIG_VIRTIO_BLK) += virtio-blk.o +obj-$(CONFIG_VIRTIO_BLK) += dataplane/ obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk.o -endif diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 101f32cf66..8c37bd314a 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -19,7 +19,6 @@ #include "qemu/thread.h" #include "qemu/error-report.h" #include "hw/virtio/virtio-access.h" -#include "sysemu/block-backend.h" #include "hw/virtio/virtio-blk.h" #include "virtio-blk.h" #include "block/aio.h" @@ -191,8 +190,8 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev) /* Set up guest notifier (irq) */ r = k->set_guest_notifiers(qbus->parent, nvqs, true); if (r != 0) { - fprintf(stderr, "virtio-blk failed to set guest notifier (%d), " - "ensure -enable-kvm is set\n", r); + error_report("virtio-blk failed to set guest notifier (%d), " + "ensure -accel kvm is set.", r); goto fail_guest_notifiers; } diff --git a/hw/block/fdc.c b/hw/block/fdc.c index cd29e27d8f..2e9c1e1e2f 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -40,6 +40,7 @@ #include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "qemu/log.h" +#include "trace.h" /********************************************************/ /* debug Floppy devices */ @@ -396,16 +397,9 @@ static int pick_geometry(FDrive *drv) nb_sectors, FloppyDriveType_str(parse->drive)); } + assert(type_match != -1 && "misconfigured fd_format"); match = type_match; } - - /* No match of any kind found -- fd_format is misconfigured, abort. */ - if (match == -1) { - error_setg(&error_abort, "No candidate geometries present in table " - " for floppy drive type '%s'", - FloppyDriveType_str(drv->drive)); - } - parse = &(fd_formats[match]); out: @@ -934,7 +928,7 @@ static uint32_t fdctrl_read (void *opaque, uint32_t reg) retval = (uint32_t)(-1); break; } - FLOPPY_DPRINTF("read reg%d: 0x%02x\n", reg & 7, retval); + trace_fdc_ioport_read(reg, retval); return retval; } @@ -943,9 +937,8 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) { FDCtrl *fdctrl = opaque; - FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); - reg &= 7; + trace_fdc_ioport_write(reg, value); switch (reg) { case FD_REG_DOR: fdctrl_write_dor(fdctrl, value); diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index b49c8e9caa..e8dfa14b33 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -22,9 +22,9 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "hw/ssi/ssi.h" #include "qemu/bitops.h" #include "qemu/log.h" @@ -542,12 +542,12 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) switch (cmd) { case ERASE_4K: case ERASE4_4K: - len = 4 << 10; + len = 4 * KiB; capa_to_assert = ER_4K; break; case ERASE_32K: case ERASE4_32K: - len = 32 << 10; + len = 32 * KiB; capa_to_assert = ER_32K; break; case ERASE_SECTOR: @@ -699,6 +699,7 @@ static void complete_collecting_data(Flash *s) case MAN_MACRONIX: s->quad_enable = extract32(s->data[0], 6, 1); if (s->len > 1) { + s->volatile_cfg = s->data[1]; s->four_bytes_address_mode = extract32(s->data[1], 5, 1); } break; diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 85d2406400..5e508ab1b3 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -18,13 +18,15 @@ * Usage: add options: * -drive file=,if=none,id= * -device nvme,drive=,serial=,id=, \ - * cmb_size_mb= + * cmb_size_mb=, \ + * num_queues= * * Note cmb_size_mb denotes size of CMB in MB. CMB is assumed to be at * offset 0 in BAR2 and supports only WDS, RDS and SQS for now. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/block/block.h" #include "hw/hw.h" #include "hw/pci/msix.h" @@ -35,6 +37,7 @@ #include "sysemu/block-backend.h" #include "qemu/log.h" +#include "qemu/cutils.h" #include "trace.h" #include "nvme.h" @@ -647,7 +650,7 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c) static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c) { - static const int data_len = 4096; + static const int data_len = 4 * KiB; uint32_t min_nsid = le32_to_cpu(c->nsid); uint64_t prp1 = le64_to_cpu(c->prp1); uint64_t prp2 = le64_to_cpu(c->prp2); @@ -1232,7 +1235,6 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) pcie_endpoint_cap_init(&n->parent_obj, 0x80); n->num_namespaces = 1; - n->num_queues = 64; n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4); n->ns_size = bs_size / (uint64_t)n->num_namespaces; @@ -1341,6 +1343,7 @@ static Property nvme_props[] = { DEFINE_BLOCK_PROPERTIES(NvmeCtrl, conf), DEFINE_PROP_STRING("serial", NvmeCtrl, serial), DEFINE_PROP_UINT32("cmb_size_mb", NvmeCtrl, cmb_size_mb, 0), + DEFINE_PROP_UINT32("num_queues", NvmeCtrl, num_queues, 64), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/nvme.h b/hw/block/nvme.h index 8f3981121d..cabcf20c32 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -1,6 +1,5 @@ #ifndef HW_NVME_H #define HW_NVME_H -#include "qemu/cutils.h" #include "block/nvme.h" typedef struct NvmeAsyncEvent { diff --git a/hw/block/onenand.c b/hw/block/onenand.c index ed77f859e9..0cb8d7fa13 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -25,9 +25,7 @@ #include "hw/block/flash.h" #include "hw/irq.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "exec/memory.h" -#include "exec/address-spaces.h" #include "hw/sysbus.h" #include "qemu/error-report.h" diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 2e8284001d..bffb4c40e7 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -43,11 +43,11 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "qemu/bitops.h" -#include "exec/address-spaces.h" #include "qemu/host-utils.h" #include "qemu/log.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" +#include "trace.h" #define PFLASH_BUG(fmt, ...) \ do { \ @@ -121,7 +121,7 @@ static void pflash_timer (void *opaque) { pflash_t *pfl = opaque; - DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); + trace_pflash_timer_expired(pfl->cmd); /* Reset flash */ pfl->status ^= 0x80; memory_region_rom_device_set_romd(&pfl->mem, true); @@ -219,15 +219,14 @@ static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) switch (boff & 0xFF) { case 0: resp = pfl->ident0; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, resp); + trace_pflash_manufacturer_id(resp); break; case 1: resp = pfl->ident1; - DPRINTF("%s: Device ID Code %04x\n", __func__, resp); + trace_pflash_device_id(resp); break; default: - DPRINTF("%s: Read Device Information offset=%x\n", __func__, - (unsigned)offset); + trace_pflash_device_info(offset); return 0; break; } @@ -252,8 +251,7 @@ static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, switch (width) { case 1: ret = p[offset]; - DPRINTF("%s: data offset " TARGET_FMT_plx " %02x\n", - __func__, offset, ret); + trace_pflash_data_read8(offset, ret); break; case 2: if (be) { @@ -263,8 +261,7 @@ static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, ret = p[offset]; ret |= p[offset + 1] << 8; } - DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n", - __func__, offset, ret); + trace_pflash_data_read16(offset, ret); break; case 4: if (be) { @@ -278,8 +275,7 @@ static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, ret |= p[offset + 2] << 16; ret |= p[offset + 3] << 24; } - DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n", - __func__, offset, ret); + trace_pflash_data_read32(offset, ret); break; default: DPRINTF("BUG in %s\n", __func__); @@ -295,11 +291,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, uint32_t ret; ret = -1; - -#if 0 - DPRINTF("%s: reading offset " TARGET_FMT_plx " under cmd %02x width %d\n", - __func__, offset, pfl->cmd, width); -#endif + trace_pflash_read(offset, pfl->cmd, width, pfl->wcycle); switch (pfl->cmd) { default: /* This should never happen : reset state & treat it as a read */ @@ -350,15 +342,14 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, switch (boff) { case 0: ret = pfl->ident0 << 8 | pfl->ident1; - DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); + trace_pflash_manufacturer_id(ret); break; case 1: ret = pfl->ident2 << 8 | pfl->ident3; - DPRINTF("%s: Device ID Code %04x\n", __func__, ret); + trace_pflash_device_id(ret); break; default: - DPRINTF("%s: Read Device Information boff=%x\n", __func__, - (unsigned)boff); + trace_pflash_device_info(boff); ret = 0; break; } @@ -426,9 +417,7 @@ static inline void pflash_data_write(pflash_t *pfl, hwaddr offset, { uint8_t *p = pfl->storage; - DPRINTF("%s: block write offset " TARGET_FMT_plx - " value %x counter %016" PRIx64 "\n", - __func__, offset, value, pfl->counter); + trace_pflash_data_write(offset, value, width, pfl->counter); switch (width) { case 1: p[offset] = value; @@ -467,9 +456,7 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, cmd = value; - DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n", - __func__, offset, value, width, pfl->wcycle); - + trace_pflash_write(offset, value, width, pfl->wcycle); if (!pfl->wcycle) { /* Set the device in I/O access mode */ memory_region_rom_device_set_romd(&pfl->mem, false); @@ -657,8 +644,8 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, "\n", __func__, offset, pfl->wcycle, pfl->cmd, value); reset_flash: + trace_pflash_reset(); memory_region_rom_device_set_romd(&pfl->mem, true); - pfl->wcycle = 0; pfl->cmd = 0; } diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 75d1ae1026..0f8b7b8c7b 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -41,9 +41,9 @@ #include "qapi/error.h" #include "qemu/timer.h" #include "sysemu/block-backend.h" -#include "exec/address-spaces.h" #include "qemu/host-utils.h" #include "hw/sysbus.h" +#include "trace.h" //#define PFLASH_DEBUG #ifdef PFLASH_DEBUG @@ -125,7 +125,7 @@ static void pflash_timer (void *opaque) { pflash_t *pfl = opaque; - DPRINTF("%s: command %02x done\n", __func__, pfl->cmd); + trace_pflash_timer_expired(pfl->cmd); /* Reset flash */ pfl->status ^= 0x80; if (pfl->bypass) { @@ -144,8 +144,8 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, uint32_t ret; uint8_t *p; - DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset); ret = -1; + trace_pflash_read(offset, pfl->cmd, width, pfl->wcycle); /* Lazy reset to ROMD mode after a certain amount of read accesses */ if (!pfl->rom_mode && pfl->wcycle == 0 && ++pfl->read_counter > PFLASH_LAZY_ROMD_THRESHOLD) { @@ -173,7 +173,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, switch (width) { case 1: ret = p[offset]; -// DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret); + trace_pflash_data_read8(offset, ret); break; case 2: if (be) { @@ -183,7 +183,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, ret = p[offset]; ret |= p[offset + 1] << 8; } -// DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret); + trace_pflash_data_read16(offset, ret); break; case 4: if (be) { @@ -197,7 +197,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, ret |= p[offset + 2] << 16; ret |= p[offset + 3] << 24; } -// DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret); + trace_pflash_data_read32(offset, ret); break; } break; @@ -275,8 +275,7 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, #endif goto reset_flash; } - DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__, - offset, value, width, pfl->wcycle); + trace_pflash_write(offset, value, width, pfl->wcycle); offset &= pfl->chip_len - 1; DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__, @@ -346,8 +345,7 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, /* We need another unlock sequence */ goto check_unlock0; case 0xA0: - DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n", - __func__, offset, value, width); + trace_pflash_data_write(offset, value, width, 0); p = pfl->storage; if (!pfl->ro) { switch (width) { @@ -484,6 +482,7 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, /* Reset flash */ reset_flash: + trace_pflash_reset(); pfl->bypass = 0; pfl->wcycle = 0; pfl->cmd = 0; @@ -494,102 +493,41 @@ static void pflash_write (pflash_t *pfl, hwaddr offset, pfl->cmd = 0; } - -static uint32_t pflash_readb_be(void *opaque, hwaddr addr) +static uint64_t pflash_be_readfn(void *opaque, hwaddr addr, unsigned size) { - return pflash_read(opaque, addr, 1, 1); + return pflash_read(opaque, addr, size, 1); } -static uint32_t pflash_readb_le(void *opaque, hwaddr addr) +static void pflash_be_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { - return pflash_read(opaque, addr, 1, 0); + pflash_write(opaque, addr, value, size, 1); } -static uint32_t pflash_readw_be(void *opaque, hwaddr addr) +static uint64_t pflash_le_readfn(void *opaque, hwaddr addr, unsigned size) { - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 1); + return pflash_read(opaque, addr, size, 0); } -static uint32_t pflash_readw_le(void *opaque, hwaddr addr) +static void pflash_le_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 2, 0); -} - -static uint32_t pflash_readl_be(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 4, 1); -} - -static uint32_t pflash_readl_le(void *opaque, hwaddr addr) -{ - pflash_t *pfl = opaque; - - return pflash_read(pfl, addr, 4, 0); -} - -static void pflash_writeb_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 1); -} - -static void pflash_writeb_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_write(opaque, addr, value, 1, 0); -} - -static void pflash_writew_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 1); -} - -static void pflash_writew_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 2, 0); -} - -static void pflash_writel_be(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 4, 1); -} - -static void pflash_writel_le(void *opaque, hwaddr addr, - uint32_t value) -{ - pflash_t *pfl = opaque; - - pflash_write(pfl, addr, value, 4, 0); + pflash_write(opaque, addr, value, size, 0); } static const MemoryRegionOps pflash_cfi02_ops_be = { - .old_mmio = { - .read = { pflash_readb_be, pflash_readw_be, pflash_readl_be, }, - .write = { pflash_writeb_be, pflash_writew_be, pflash_writel_be, }, - }, + .read = pflash_be_readfn, + .write = pflash_be_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; static const MemoryRegionOps pflash_cfi02_ops_le = { - .old_mmio = { - .read = { pflash_readb_le, pflash_readw_le, pflash_readl_le, }, - .write = { pflash_writeb_le, pflash_writew_le, pflash_writel_le, }, - }, + .read = pflash_le_readfn, + .write = pflash_le_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/block/tc58128.c b/hw/block/tc58128.c index 1d9f7ee000..808ad76ba6 100644 --- a/hw/block/tc58128.c +++ b/hw/block/tc58128.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/sh4/sh.h" #include "hw/loader.h" @@ -26,7 +27,7 @@ typedef struct { static tc58128_dev tc58128_devs[2]; -#define FLASH_SIZE (16*1024*1024) +#define FLASH_SIZE (16 * MiB) static void init_dev(tc58128_dev * dev, const char *filename) { diff --git a/hw/block/trace-events b/hw/block/trace-events index 6b9e733412..335c092450 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -1,5 +1,22 @@ # See docs/devel/tracing.txt for syntax documentation. +# hw/block/fdc.c +fdc_ioport_read(uint8_t reg, uint8_t value) "read reg 0x%02x val 0x%02x" +fdc_ioport_write(uint8_t reg, uint8_t value) "write reg 0x%02x val 0x%02x" + +# hw/block/pflash_cfi0?.c +pflash_reset(void) "reset" +pflash_read(uint64_t offset, uint8_t cmd, int width, uint8_t wcycle) "offset:0x%04"PRIx64" cmd:0x%02x width:%d wcycle:%u" +pflash_write(uint64_t offset, uint32_t value, int width, uint8_t wcycle) "offset:0x%04"PRIx64" value:0x%03x width:%d wcycle:%u" +pflash_timer_expired(uint8_t cmd) "command 0x%02x done" +pflash_data_read8(uint64_t offset, uint32_t value) "data offset:0x%04"PRIx64" value:0x%02x" +pflash_data_read16(uint64_t offset, uint32_t value) "data offset:0x%04"PRIx64" value:0x%04x" +pflash_data_read32(uint64_t offset, uint32_t value) "data offset:0x%04"PRIx64" value:0x%08x" +pflash_data_write(uint64_t offset, uint32_t value, int width, uint64_t counter) "data offset:0x%04"PRIx64" value:0x%08x width:%d counter:0x%016"PRIx64 +pflash_manufacturer_id(uint16_t id) "Read Manufacturer ID: 0x%04x" +pflash_device_id(uint16_t id) "Read Device ID: 0x%04x" +pflash_device_info(uint64_t offset) "Read Device Information offset:0x%04"PRIx64 + # hw/block/virtio-blk.c virtio_blk_req_complete(void *vdev, void *req, int status) "vdev %p req %p status %d" virtio_blk_rw_complete(void *vdev, void *req, int ret) "vdev %p req %p ret %d" diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 262baca432..d755223643 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -196,7 +196,6 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, Error **errp) { VHostUserBlk *s = VHOST_USER_BLK(vdev); - uint64_t get_features; /* Turn on pre-defined features */ virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); @@ -204,20 +203,16 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, virtio_add_feature(&features, VIRTIO_BLK_F_TOPOLOGY); virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH); + virtio_add_feature(&features, VIRTIO_BLK_F_RO); if (s->config_wce) { virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); } - if (s->config_ro) { - virtio_add_feature(&features, VIRTIO_BLK_F_RO); - } if (s->num_queues > 1) { virtio_add_feature(&features, VIRTIO_BLK_F_MQ); } - get_features = vhost_get_features(&s->dev, user_feature_bits, features); - - return get_features; + return vhost_get_features(&s->dev, user_feature_bits, features); } static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) @@ -229,6 +224,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBlk *s = VHOST_USER_BLK(vdev); + VhostUserState *user; int i, ret; if (!s->chardev.chr) { @@ -246,6 +242,15 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) return; } + user = vhost_user_init(); + if (!user) { + error_setg(errp, "vhost-user-blk: failed to init vhost_user"); + return; + } + + user->chr = &s->chardev; + s->vhost_user = user; + virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config)); @@ -261,7 +266,7 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) vhost_dev_set_config_notifier(&s->dev, &blk_ops); - ret = vhost_dev_init(&s->dev, &s->chardev, VHOST_BACKEND_TYPE_USER, 0); + ret = vhost_dev_init(&s->dev, s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); if (ret < 0) { error_setg(errp, "vhost-user-blk: vhost initialization failed: %s", strerror(-ret)); @@ -286,6 +291,10 @@ vhost_err: virtio_err: g_free(s->dev.vqs); virtio_cleanup(vdev); + + vhost_user_cleanup(user); + g_free(user); + s->vhost_user = NULL; } static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) @@ -297,6 +306,12 @@ static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) vhost_dev_cleanup(&s->dev); g_free(s->dev.vqs); virtio_cleanup(vdev); + + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } } static void vhost_user_blk_instance_init(Object *obj) @@ -322,7 +337,6 @@ static Property vhost_user_blk_properties[] = { DEFINE_PROP_UINT16("num-queues", VHostUserBlk, num_queues, 1), DEFINE_PROP_UINT32("queue-size", VHostUserBlk, queue_size, 128), DEFINE_PROP_BIT("config-wce", VHostUserBlk, config_wce, 0, true), - DEFINE_PROP_BIT("config-ro", VHostUserBlk, config_ro, 0, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index b1532e4e91..50b5c869e3 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -18,7 +18,6 @@ #include "qemu/error-report.h" #include "trace.h" #include "hw/block/block.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/virtio/virtio-blk.h" #include "dataplane/virtio-blk.h" diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index f74fcd42d1..36eff94f84 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -20,6 +20,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include #include @@ -36,27 +37,9 @@ /* ------------------------------------------------------------- */ -static int batch_maps = 0; - -/* ------------------------------------------------------------- */ - #define BLOCK_SIZE 512 #define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) -struct PersistentGrant { - void *page; - struct XenBlkDev *blkdev; -}; - -typedef struct PersistentGrant PersistentGrant; - -struct PersistentRegion { - void *addr; - int num; -}; - -typedef struct PersistentRegion PersistentRegion; - struct ioreq { blkif_request_t req; int16_t status; @@ -64,16 +47,9 @@ struct ioreq { /* parsed request */ off_t start; QEMUIOVector v; + void *buf; + size_t size; int presync; - uint8_t mapped; - - /* grant mapping */ - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int prot; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *pages; - int num_unmap; /* aio status */ int aio_inflight; @@ -104,7 +80,6 @@ struct XenBlkDev { int protocol; blkif_back_rings_t rings; int more_work; - int cnt_map; /* request lists */ QLIST_HEAD(inflight_head, ioreq) inflight; @@ -115,13 +90,7 @@ struct XenBlkDev { int requests_finished; unsigned int max_requests; - /* Persistent grants extension */ gboolean feature_discard; - gboolean feature_persistent; - GTree *persistent_gnts; - GSList *persistent_regions; - unsigned int persistent_gnt_count; - unsigned int max_grants; /* qemu block driver */ DriveInfo *dinfo; @@ -139,14 +108,9 @@ static void ioreq_reset(struct ioreq *ioreq) memset(&ioreq->req, 0, sizeof(ioreq->req)); ioreq->status = 0; ioreq->start = 0; + ioreq->buf = NULL; + ioreq->size = 0; ioreq->presync = 0; - ioreq->mapped = 0; - - memset(ioreq->domids, 0, sizeof(ioreq->domids)); - memset(ioreq->refs, 0, sizeof(ioreq->refs)); - ioreq->prot = 0; - memset(ioreq->page, 0, sizeof(ioreq->page)); - ioreq->pages = NULL; ioreq->aio_inflight = 0; ioreq->aio_errors = 0; @@ -158,46 +122,6 @@ static void ioreq_reset(struct ioreq *ioreq) qemu_iovec_reset(&ioreq->v); } -static gint int_cmp(gconstpointer a, gconstpointer b, gpointer user_data) -{ - uint ua = GPOINTER_TO_UINT(a); - uint ub = GPOINTER_TO_UINT(b); - return (ua > ub) - (ua < ub); -} - -static void destroy_grant(gpointer pgnt) -{ - PersistentGrant *grant = pgnt; - xengnttab_handle *gnt = grant->blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, grant->page, 1) != 0) { - xen_pv_printf(&grant->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - grant->blkdev->persistent_gnt_count--; - xen_pv_printf(&grant->blkdev->xendev, 3, - "unmapped grant %p\n", grant->page); - g_free(grant); -} - -static void remove_persistent_region(gpointer data, gpointer dev) -{ - PersistentRegion *region = data; - struct XenBlkDev *blkdev = dev; - xengnttab_handle *gnt = blkdev->xendev.gnttabdev; - - if (xengnttab_unmap(gnt, region->addr, region->num) != 0) { - xen_pv_printf(&blkdev->xendev, 0, - "xengnttab_unmap region %p failed: %s\n", - region->addr, strerror(errno)); - } - xen_pv_printf(&blkdev->xendev, 3, - "unmapped grant region %p with %d pages\n", - region->addr, region->num); - g_free(region); -} - static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) { struct ioreq *ioreq = NULL; @@ -210,7 +134,7 @@ static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) ioreq = g_malloc0(sizeof(*ioreq)); ioreq->blkdev = blkdev; blkdev->requests_total++; - qemu_iovec_init(&ioreq->v, BLKIF_MAX_SEGMENTS_PER_REQUEST); + qemu_iovec_init(&ioreq->v, 1); } else { /* get one from freelist */ ioreq = QLIST_FIRST(&blkdev->freelist); @@ -255,17 +179,16 @@ static void ioreq_release(struct ioreq *ioreq, bool finish) static int ioreq_parse(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - uintptr_t mem; + struct XenDevice *xendev = &blkdev->xendev; size_t len; int i; - xen_pv_printf(&blkdev->xendev, 3, + xen_pv_printf(xendev, 3, "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", ioreq->req.operation, ioreq->req.nr_segments, ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); switch (ioreq->req.operation) { case BLKIF_OP_READ: - ioreq->prot = PROT_WRITE; /* to memory */ break; case BLKIF_OP_FLUSH_DISKCACHE: ioreq->presync = 1; @@ -274,45 +197,40 @@ static int ioreq_parse(struct ioreq *ioreq) } /* fall through */ case BLKIF_OP_WRITE: - ioreq->prot = PROT_READ; /* from memory */ break; case BLKIF_OP_DISCARD: return 0; default: - xen_pv_printf(&blkdev->xendev, 0, "error: unknown operation (%d)\n", + xen_pv_printf(xendev, 0, "error: unknown operation (%d)\n", ioreq->req.operation); goto err; }; if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { - xen_pv_printf(&blkdev->xendev, 0, "error: write req for ro device\n"); + xen_pv_printf(xendev, 0, "error: write req for ro device\n"); goto err; } ioreq->start = ioreq->req.sector_number * blkdev->file_blk; for (i = 0; i < ioreq->req.nr_segments; i++) { if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_pv_printf(&blkdev->xendev, 0, "error: nr_segments too big\n"); + xen_pv_printf(xendev, 0, "error: nr_segments too big\n"); goto err; } if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_pv_printf(&blkdev->xendev, 0, "error: first > last sector\n"); + xen_pv_printf(xendev, 0, "error: first > last sector\n"); goto err; } if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_pv_printf(&blkdev->xendev, 0, "error: page crossing\n"); + xen_pv_printf(xendev, 0, "error: page crossing\n"); goto err; } - ioreq->domids[i] = blkdev->xendev.dom; - ioreq->refs[i] = ioreq->req.seg[i].gref; - - mem = ioreq->req.seg[i].first_sect * blkdev->file_blk; len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; - qemu_iovec_add(&ioreq->v, (void*)mem, len); + ioreq->size += len; } - if (ioreq->start + ioreq->v.size > blkdev->file_size) { - xen_pv_printf(&blkdev->xendev, 0, "error: access beyond end of file\n"); + if (ioreq->start + ioreq->size > blkdev->file_size) { + xen_pv_printf(xendev, 0, "error: access beyond end of file\n"); goto err; } return 0; @@ -322,279 +240,48 @@ err: return -1; } -static void ioreq_unmap(struct ioreq *ioreq) -{ - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - int i; - - if (ioreq->num_unmap == 0 || ioreq->mapped == 0) { - return; - } - if (batch_maps) { - if (!ioreq->pages) { - return; - } - if (xengnttab_unmap(gnt, ioreq->pages, ioreq->num_unmap) != 0) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - ioreq->blkdev->cnt_map -= ioreq->num_unmap; - ioreq->pages = NULL; - } else { - for (i = 0; i < ioreq->num_unmap; i++) { - if (!ioreq->page[i]) { - continue; - } - if (xengnttab_unmap(gnt, ioreq->page[i], 1) != 0) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "xengnttab_unmap failed: %s\n", - strerror(errno)); - } - ioreq->blkdev->cnt_map--; - ioreq->page[i] = NULL; - } - } - ioreq->mapped = 0; -} - -static int ioreq_map(struct ioreq *ioreq) -{ - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - uint32_t domids[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - uint32_t refs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - void *page[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int i, j, new_maps = 0; - PersistentGrant *grant; - PersistentRegion *region; - /* domids and refs variables will contain the information necessary - * to map the grants that are needed to fulfill this request. - * - * After mapping the needed grants, the page array will contain the - * memory address of each granted page in the order specified in ioreq - * (disregarding if it's a persistent grant or not). - */ - - if (ioreq->v.niov == 0 || ioreq->mapped == 1) { - return 0; - } - if (ioreq->blkdev->feature_persistent) { - for (i = 0; i < ioreq->v.niov; i++) { - grant = g_tree_lookup(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(ioreq->refs[i])); - - if (grant != NULL) { - page[i] = grant->page; - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "using persistent-grant %" PRIu32 "\n", - ioreq->refs[i]); - } else { - /* Add the grant to the list of grants that - * should be mapped - */ - domids[new_maps] = ioreq->domids[i]; - refs[new_maps] = ioreq->refs[i]; - page[i] = NULL; - new_maps++; - } - } - /* Set the protection to RW, since grants may be reused later - * with a different protection than the one needed for this request - */ - ioreq->prot = PROT_WRITE | PROT_READ; - } else { - /* All grants in the request should be mapped */ - memcpy(refs, ioreq->refs, sizeof(refs)); - memcpy(domids, ioreq->domids, sizeof(domids)); - memset(page, 0, sizeof(page)); - new_maps = ioreq->v.niov; - } - - if (batch_maps && new_maps) { - ioreq->pages = xengnttab_map_grant_refs - (gnt, new_maps, domids, refs, ioreq->prot); - if (ioreq->pages == NULL) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "can't map %d grant refs (%s, %d maps)\n", - new_maps, strerror(errno), ioreq->blkdev->cnt_map); - return -1; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->pages + (j++) * XC_PAGE_SIZE; - } - } - ioreq->blkdev->cnt_map += new_maps; - } else if (new_maps) { - for (i = 0; i < new_maps; i++) { - ioreq->page[i] = xengnttab_map_grant_ref - (gnt, domids[i], refs[i], ioreq->prot); - if (ioreq->page[i] == NULL) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, - "can't map grant ref %d (%s, %d maps)\n", - refs[i], strerror(errno), ioreq->blkdev->cnt_map); - ioreq->mapped = 1; - ioreq_unmap(ioreq); - return -1; - } - ioreq->blkdev->cnt_map++; - } - for (i = 0, j = 0; i < ioreq->v.niov; i++) { - if (page[i] == NULL) { - page[i] = ioreq->page[j++]; - } - } - } - if (ioreq->blkdev->feature_persistent && new_maps != 0 && - (!batch_maps || (ioreq->blkdev->persistent_gnt_count + new_maps <= - ioreq->blkdev->max_grants))) { - /* - * If we are using persistent grants and batch mappings only - * add the new maps to the list of persistent grants if the whole - * area can be persistently mapped. - */ - if (batch_maps) { - region = g_malloc0(sizeof(*region)); - region->addr = ioreq->pages; - region->num = new_maps; - ioreq->blkdev->persistent_regions = g_slist_append( - ioreq->blkdev->persistent_regions, - region); - } - while ((ioreq->blkdev->persistent_gnt_count < ioreq->blkdev->max_grants) - && new_maps) { - /* Go through the list of newly mapped grants and add as many - * as possible to the list of persistently mapped grants. - * - * Since we start at the end of ioreq->page(s), we only need - * to decrease new_maps to prevent this granted pages from - * being unmapped in ioreq_unmap. - */ - grant = g_malloc0(sizeof(*grant)); - new_maps--; - if (batch_maps) { - grant->page = ioreq->pages + (new_maps) * XC_PAGE_SIZE; - } else { - grant->page = ioreq->page[new_maps]; - } - grant->blkdev = ioreq->blkdev; - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "adding grant %" PRIu32 " page: %p\n", - refs[new_maps], grant->page); - g_tree_insert(ioreq->blkdev->persistent_gnts, - GUINT_TO_POINTER(refs[new_maps]), - grant); - ioreq->blkdev->persistent_gnt_count++; - } - assert(!batch_maps || new_maps == 0); - } - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->v.iov[i].iov_base += (uintptr_t)page[i]; - } - ioreq->mapped = 1; - ioreq->num_unmap = new_maps; - return 0; -} - -#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 - -static void ioreq_free_copy_buffers(struct ioreq *ioreq) -{ - int i; - - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = NULL; - } - - qemu_vfree(ioreq->pages); -} - -static int ioreq_init_copy_buffers(struct ioreq *ioreq) -{ - int i; - - if (ioreq->v.niov == 0) { - return 0; - } - - ioreq->pages = qemu_memalign(XC_PAGE_SIZE, ioreq->v.niov * XC_PAGE_SIZE); - - for (i = 0; i < ioreq->v.niov; i++) { - ioreq->page[i] = ioreq->pages + i * XC_PAGE_SIZE; - ioreq->v.iov[i].iov_base = ioreq->page[i]; - } - - return 0; -} - static int ioreq_grant_copy(struct ioreq *ioreq) { - xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; - xengnttab_grant_copy_segment_t segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; + XenGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; int i, count, rc; - int64_t file_blk = ioreq->blkdev->file_blk; + int64_t file_blk = blkdev->file_blk; + bool to_domain = (ioreq->req.operation == BLKIF_OP_READ); + void *virt = ioreq->buf; - if (ioreq->v.niov == 0) { + if (ioreq->req.nr_segments == 0) { return 0; } - count = ioreq->v.niov; + count = ioreq->req.nr_segments; for (i = 0; i < count; i++) { - if (ioreq->req.operation == BLKIF_OP_READ) { - segs[i].flags = GNTCOPY_dest_gref; - segs[i].dest.foreign.ref = ioreq->refs[i]; - segs[i].dest.foreign.domid = ioreq->domids[i]; + if (to_domain) { + segs[i].dest.foreign.ref = ioreq->req.seg[i].gref; segs[i].dest.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].source.virt = ioreq->v.iov[i].iov_base; + segs[i].source.virt = virt; } else { - segs[i].flags = GNTCOPY_source_gref; - segs[i].source.foreign.ref = ioreq->refs[i]; - segs[i].source.foreign.domid = ioreq->domids[i]; + segs[i].source.foreign.ref = ioreq->req.seg[i].gref; segs[i].source.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].dest.virt = ioreq->v.iov[i].iov_base; + segs[i].dest.virt = virt; } segs[i].len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * file_blk; + virt += segs[i].len; } - rc = xengnttab_grant_copy(gnt, count, segs); + rc = xen_be_copy_grant_refs(xendev, to_domain, segs, count); if (rc) { - xen_pv_printf(&ioreq->blkdev->xendev, 0, + xen_pv_printf(xendev, 0, "failed to copy data %d\n", rc); ioreq->aio_errors++; return -1; } - for (i = 0; i < count; i++) { - if (segs[i].status != GNTST_okay) { - xen_pv_printf(&ioreq->blkdev->xendev, 3, - "failed to copy data %d for gref %d, domid %d\n", - segs[i].status, ioreq->refs[i], ioreq->domids[i]); - ioreq->aio_errors++; - rc = -1; - } - } - return rc; } -#else -static void ioreq_free_copy_buffers(struct ioreq *ioreq) -{ - abort(); -} - -static int ioreq_init_copy_buffers(struct ioreq *ioreq) -{ - abort(); -} - -static int ioreq_grant_copy(struct ioreq *ioreq) -{ - abort(); -} -#endif static int ioreq_runio_qemu_aio(struct ioreq *ioreq); @@ -602,11 +289,12 @@ static void qemu_aio_complete(void *opaque, int ret) { struct ioreq *ioreq = opaque; struct XenBlkDev *blkdev = ioreq->blkdev; + struct XenDevice *xendev = &blkdev->xendev; aio_context_acquire(blkdev->ctx); if (ret != 0) { - xen_pv_printf(&blkdev->xendev, 0, "%s I/O error\n", + xen_pv_printf(xendev, 0, "%s I/O error\n", ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); ioreq->aio_errors++; } @@ -621,32 +309,28 @@ static void qemu_aio_complete(void *opaque, int ret) goto done; } - if (xen_feature_grant_copy) { - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - /* in case of failure ioreq->aio_errors is increased */ - if (ret == 0) { - ioreq_grant_copy(ioreq); - } - ioreq_free_copy_buffers(ioreq); - break; - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - ioreq_free_copy_buffers(ioreq); - break; - default: + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + /* in case of failure ioreq->aio_errors is increased */ + if (ret == 0) { + ioreq_grant_copy(ioreq); + } + qemu_vfree(ioreq->buf); + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_FLUSH_DISKCACHE: + if (!ioreq->req.nr_segments) { break; } + qemu_vfree(ioreq->buf); + break; + default: + break; } ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; - if (!xen_feature_grant_copy) { - ioreq_unmap(ioreq); - } ioreq_finish(ioreq); + switch (ioreq->req.operation) { case BLKIF_OP_WRITE: case BLKIF_OP_FLUSH_DISKCACHE: @@ -706,18 +390,13 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - if (xen_feature_grant_copy) { - ioreq_init_copy_buffers(ioreq); - if (ioreq->req.nr_segments && (ioreq->req.operation == BLKIF_OP_WRITE || - ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && - ioreq_grant_copy(ioreq)) { - ioreq_free_copy_buffers(ioreq); - goto err; - } - } else { - if (ioreq->req.nr_segments && ioreq_map(ioreq)) { - goto err; - } + ioreq->buf = qemu_memalign(XC_PAGE_SIZE, ioreq->size); + if (ioreq->req.nr_segments && + (ioreq->req.operation == BLKIF_OP_WRITE || + ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && + ioreq_grant_copy(ioreq)) { + qemu_vfree(ioreq->buf); + goto err; } ioreq->aio_inflight++; @@ -728,6 +407,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) switch (ioreq->req.operation) { case BLKIF_OP_READ: + qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, ioreq->v.size, BLOCK_ACCT_READ); ioreq->aio_inflight++; @@ -740,6 +420,7 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) break; } + qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, ioreq->v.size, ioreq->req.operation == BLKIF_OP_WRITE ? @@ -758,9 +439,6 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) } default: /* unknown operation (shouldn't happen -- parse catches this) */ - if (!xen_feature_grant_copy) { - ioreq_unmap(ioreq); - } goto err; } @@ -946,24 +624,21 @@ static void blk_alloc(struct XenDevice *xendev) blkdev->ctx = iothread_get_aio_context(blkdev->iothread); blkdev->bh = aio_bh_new(blkdev->ctx, blk_bh, blkdev); - - if (xen_mode != XEN_EMULATE) { - batch_maps = 1; - } } static void blk_parse_discard(struct XenBlkDev *blkdev) { + struct XenDevice *xendev = &blkdev->xendev; int enable; blkdev->feature_discard = true; - if (xenstore_read_be_int(&blkdev->xendev, "discard-enable", &enable) == 0) { + if (xenstore_read_be_int(xendev, "discard-enable", &enable) == 0) { blkdev->feature_discard = !!enable; } if (blkdev->feature_discard) { - xenstore_write_be_int(&blkdev->xendev, "feature-discard", 1); + xenstore_write_be_int(xendev, "feature-discard", 1); } } @@ -978,7 +653,7 @@ static int blk_init(struct XenDevice *xendev) /* read xenstore entries */ if (blkdev->params == NULL) { char *h = NULL; - blkdev->params = xenstore_read_be_str(&blkdev->xendev, "params"); + blkdev->params = xenstore_read_be_str(xendev, "params"); if (blkdev->params != NULL) { h = strchr(blkdev->params, ':'); } @@ -998,18 +673,18 @@ static int blk_init(struct XenDevice *xendev) blkdev->fileproto = "vpc"; } if (blkdev->mode == NULL) { - blkdev->mode = xenstore_read_be_str(&blkdev->xendev, "mode"); + blkdev->mode = xenstore_read_be_str(xendev, "mode"); } if (blkdev->type == NULL) { - blkdev->type = xenstore_read_be_str(&blkdev->xendev, "type"); + blkdev->type = xenstore_read_be_str(xendev, "type"); } if (blkdev->dev == NULL) { - blkdev->dev = xenstore_read_be_str(&blkdev->xendev, "dev"); + blkdev->dev = xenstore_read_be_str(xendev, "dev"); } if (blkdev->devtype == NULL) { - blkdev->devtype = xenstore_read_be_str(&blkdev->xendev, "device-type"); + blkdev->devtype = xenstore_read_be_str(xendev, "device-type"); } - directiosafe = xenstore_read_be_str(&blkdev->xendev, "direct-io-safe"); + directiosafe = xenstore_read_be_str(xendev, "direct-io-safe"); blkdev->directiosafe = (directiosafe && atoi(directiosafe)); /* do we have all we need? */ @@ -1032,18 +707,13 @@ static int blk_init(struct XenDevice *xendev) blkdev->file_blk = BLOCK_SIZE; - xen_pv_printf(&blkdev->xendev, 3, "grant copy operation %s\n", - xen_feature_grant_copy ? "enabled" : "disabled"); - /* fill info * blk_connect supplies sector-size and sectors */ - xenstore_write_be_int(&blkdev->xendev, "feature-flush-cache", 1); - xenstore_write_be_int(&blkdev->xendev, "feature-persistent", - !xen_feature_grant_copy); - xenstore_write_be_int(&blkdev->xendev, "info", info); + xenstore_write_be_int(xendev, "feature-flush-cache", 1); + xenstore_write_be_int(xendev, "info", info); - xenstore_write_be_int(&blkdev->xendev, "max-ring-page-order", + xenstore_write_be_int(xendev, "max-ring-page-order", MAX_RING_PAGE_ORDER); blk_parse_discard(blkdev); @@ -1067,25 +737,15 @@ out_error: return -1; } -/* - * We need to account for the grant allocations requiring contiguous - * chunks; the worst case number would be - * max_req * max_seg + (max_req - 1) * (max_seg - 1) + 1, - * but in order to keep things simple just use - * 2 * max_req * max_seg. - */ -#define MAX_GRANTS(max_req, max_seg) (2 * (max_req) * (max_seg)) - static int blk_connect(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int pers, index, qflags; + int index, qflags; bool readonly = true; bool writethrough = true; int order, ring_ref; unsigned int ring_size, max_grants; unsigned int i; - uint32_t *domids; trace_xen_disk_connect(xendev->name); @@ -1105,7 +765,7 @@ static int blk_connect(struct XenDevice *xendev) } /* init qemu block driver */ - index = (blkdev->xendev.dev - 202 * 256) / 16; + index = (xendev->dev - 202 * 256) / 16; blkdev->dinfo = drive_get(IF_XEN, 0, index); if (!blkdev->dinfo) { Error *local_err = NULL; @@ -1117,11 +777,11 @@ static int blk_connect(struct XenDevice *xendev) } /* setup via xenbus -> create new block driver instance */ - xen_pv_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); + xen_pv_printf(xendev, 2, "create new bdrv (xenbus setup)\n"); blkdev->blk = blk_new_open(blkdev->filename, NULL, options, qflags, &local_err); if (!blkdev->blk) { - xen_pv_printf(&blkdev->xendev, 0, "error: %s\n", + xen_pv_printf(xendev, 0, "error: %s\n", error_get_pretty(local_err)); error_free(local_err); return -1; @@ -1129,11 +789,11 @@ static int blk_connect(struct XenDevice *xendev) blk_set_enable_write_cache(blkdev->blk, !writethrough); } else { /* setup via qemu cmdline -> already setup for us */ - xen_pv_printf(&blkdev->xendev, 2, + xen_pv_printf(xendev, 2, "get configured bdrv (cmdline setup)\n"); blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo); if (blk_is_read_only(blkdev->blk) && !readonly) { - xen_pv_printf(&blkdev->xendev, 0, "Unexpected read-only drive"); + xen_pv_printf(xendev, 0, "Unexpected read-only drive"); blkdev->blk = NULL; return -1; } @@ -1146,7 +806,7 @@ static int blk_connect(struct XenDevice *xendev) if (blkdev->file_size < 0) { BlockDriverState *bs = blk_bs(blkdev->blk); const char *drv_name = bs ? bdrv_get_format_name(bs) : NULL; - xen_pv_printf(&blkdev->xendev, 1, "blk_getlength: %d (%s) | drv %s\n", + xen_pv_printf(xendev, 1, "blk_getlength: %d (%s) | drv %s\n", (int)blkdev->file_size, strerror(-blkdev->file_size), drv_name ?: "-"); blkdev->file_size = 0; @@ -1155,18 +815,18 @@ static int blk_connect(struct XenDevice *xendev) xen_pv_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," " size %" PRId64 " (%" PRId64 " MB)\n", blkdev->type, blkdev->fileproto, blkdev->filename, - blkdev->file_size, blkdev->file_size >> 20); + blkdev->file_size, blkdev->file_size / MiB); /* Fill in number of sector size and number of sectors */ - xenstore_write_be_int(&blkdev->xendev, "sector-size", blkdev->file_blk); - xenstore_write_be_int64(&blkdev->xendev, "sectors", + xenstore_write_be_int(xendev, "sector-size", blkdev->file_blk); + xenstore_write_be_int64(xendev, "sectors", blkdev->file_size / blkdev->file_blk); - if (xenstore_read_fe_int(&blkdev->xendev, "ring-page-order", + if (xenstore_read_fe_int(xendev, "ring-page-order", &order) == -1) { blkdev->nr_ring_ref = 1; - if (xenstore_read_fe_int(&blkdev->xendev, "ring-ref", + if (xenstore_read_fe_int(xendev, "ring-ref", &ring_ref) == -1) { return -1; } @@ -1183,7 +843,7 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, key, + if (xenstore_read_fe_int(xendev, key, &ring_ref) == -1) { g_free(key); return -1; @@ -1198,23 +858,18 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, "event-channel", - &blkdev->xendev.remote_port) == -1) { + if (xenstore_read_fe_int(xendev, "event-channel", + &xendev->remote_port) == -1) { return -1; } - if (xenstore_read_fe_int(&blkdev->xendev, "feature-persistent", &pers)) { - blkdev->feature_persistent = FALSE; - } else { - blkdev->feature_persistent = !!pers; - } - if (!blkdev->xendev.protocol) { + if (!xendev->protocol) { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_32; - } else if (strcmp(blkdev->xendev.protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { + } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { blkdev->protocol = BLKIF_PROTOCOL_X86_64; } else { blkdev->protocol = BLKIF_PROTOCOL_NATIVE; @@ -1241,43 +896,17 @@ static int blk_connect(struct XenDevice *xendev) return -1; } - /* Calculate the maximum number of grants needed by ioreqs */ - max_grants = MAX_GRANTS(blkdev->max_requests, - BLKIF_MAX_SEGMENTS_PER_REQUEST); /* Add on the number needed for the ring pages */ - max_grants += blkdev->nr_ring_ref; - - blkdev->xendev.gnttabdev = xengnttab_open(NULL, 0); - if (blkdev->xendev.gnttabdev == NULL) { - xen_pv_printf(xendev, 0, "xengnttab_open failed: %s\n", - strerror(errno)); - return -1; - } - if (xengnttab_set_max_grants(blkdev->xendev.gnttabdev, max_grants)) { - xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - return -1; - } - - domids = g_new0(uint32_t, blkdev->nr_ring_ref); - for (i = 0; i < blkdev->nr_ring_ref; i++) { - domids[i] = blkdev->xendev.dom; - } - - blkdev->sring = xengnttab_map_grant_refs(blkdev->xendev.gnttabdev, - blkdev->nr_ring_ref, - domids, - blkdev->ring_ref, - PROT_READ | PROT_WRITE); - - g_free(domids); + max_grants = blkdev->nr_ring_ref; + xen_be_set_max_grant_refs(xendev, max_grants); + blkdev->sring = xen_be_map_grant_refs(xendev, blkdev->ring_ref, + blkdev->nr_ring_ref, + PROT_READ | PROT_WRITE); if (!blkdev->sring) { return -1; } - blkdev->cnt_map++; - switch (blkdev->protocol) { case BLKIF_PROTOCOL_NATIVE: { @@ -1301,27 +930,14 @@ static int blk_connect(struct XenDevice *xendev) } } - if (blkdev->feature_persistent) { - /* Init persistent grants */ - blkdev->max_grants = blkdev->max_requests * - BLKIF_MAX_SEGMENTS_PER_REQUEST; - blkdev->persistent_gnts = g_tree_new_full((GCompareDataFunc)int_cmp, - NULL, NULL, - batch_maps ? - (GDestroyNotify)g_free : - (GDestroyNotify)destroy_grant); - blkdev->persistent_regions = NULL; - blkdev->persistent_gnt_count = 0; - } - blk_set_aio_context(blkdev->blk, blkdev->ctx); - xen_be_bind_evtchn(&blkdev->xendev); + xen_be_bind_evtchn(xendev); - xen_pv_printf(&blkdev->xendev, 1, "ok: proto %s, nr-ring-ref %u, " + xen_pv_printf(xendev, 1, "ok: proto %s, nr-ring-ref %u, " "remote port %d, local port %d\n", - blkdev->xendev.protocol, blkdev->nr_ring_ref, - blkdev->xendev.remote_port, blkdev->xendev.local_port); + xendev->protocol, blkdev->nr_ring_ref, + xendev->remote_port, xendev->local_port); return 0; } @@ -1339,41 +955,15 @@ static void blk_disconnect(struct XenDevice *xendev) blk_unref(blkdev->blk); blkdev->blk = NULL; } - xen_pv_unbind_evtchn(&blkdev->xendev); + xen_pv_unbind_evtchn(xendev); aio_context_release(blkdev->ctx); if (blkdev->sring) { - xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, - blkdev->nr_ring_ref); - blkdev->cnt_map--; + xen_be_unmap_grant_refs(xendev, blkdev->sring, + blkdev->nr_ring_ref); blkdev->sring = NULL; } - - /* - * Unmap persistent grants before switching to the closed state - * so the frontend can free them. - * - * In the !batch_maps case g_tree_destroy will take care of unmapping - * the grant, but in the batch_maps case we need to iterate over every - * region in persistent_regions and unmap it. - */ - if (blkdev->feature_persistent) { - g_tree_destroy(blkdev->persistent_gnts); - assert(batch_maps || blkdev->persistent_gnt_count == 0); - if (batch_maps) { - blkdev->persistent_gnt_count = 0; - g_slist_foreach(blkdev->persistent_regions, - (GFunc)remove_persistent_region, blkdev); - g_slist_free(blkdev->persistent_regions); - } - blkdev->feature_persistent = false; - } - - if (blkdev->xendev.gnttabdev) { - xengnttab_close(blkdev->xendev.gnttabdev); - blkdev->xendev.gnttabdev = NULL; - } } static int blk_free(struct XenDevice *xendev) @@ -1410,10 +1000,11 @@ static void blk_event(struct XenDevice *xendev) } struct XenDevOps xen_blkdev_ops = { + .flags = DEVOPS_FLAG_NEED_GNTDEV, .size = sizeof(struct XenBlkDev), .alloc = blk_alloc, .init = blk_init, - .initialise = blk_connect, + .initialise = blk_connect, .disconnect = blk_disconnect, .event = blk_event, .free = blk_free, diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index 1b979100b7..b570531291 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -6,7 +6,7 @@ common-obj-$(CONFIG_PL011) += pl011.o common-obj-$(CONFIG_SERIAL) += serial.o common-obj-$(CONFIG_SERIAL_ISA) += serial-isa.o common-obj-$(CONFIG_SERIAL_PCI) += serial-pci.o -common-obj-$(CONFIG_VIRTIO) += virtio-console.o +common-obj-$(CONFIG_VIRTIO_SERIAL) += virtio-console.o common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o common-obj-$(CONFIG_XEN) += xen_console.o common-obj-$(CONFIG_CADENCE) += cadence_uart.o diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 370dc7e296..0364596c55 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -39,8 +39,8 @@ #define AUX_MU_BAUD_REG 0x68 /* bits in IER/IIR registers */ -#define TX_INT 0x1 -#define RX_INT 0x2 +#define RX_INT 0x1 +#define TX_INT 0x2 static void bcm2835_aux_update(BCM2835AuxState *s) { diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c index 9c0929d8a2..ddfbb25c24 100644 --- a/hw/char/cmsdk-apb-uart.c +++ b/hw/char/cmsdk-apb-uart.c @@ -157,6 +157,7 @@ static uint64_t uart_read(void *opaque, hwaddr offset, unsigned size) r = s->rxbuf; s->state &= ~R_STATE_RXFULL_MASK; cmsdk_apb_uart_update(s); + qemu_chr_fe_accept_input(&s->chr); break; case A_STATE: r = s->state; diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index 6ebcb87a40..ccc75eaa4d 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -60,7 +60,7 @@ static uint64_t digic_uart_read(void *opaque, hwaddr addr, default: qemu_log_mask(LOG_UNIMP, "digic-uart: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); + TARGET_FMT_plx "\n", addr << 2); } return ret; @@ -98,7 +98,7 @@ static void digic_uart_write(void *opaque, hwaddr addr, uint64_t value, default: qemu_log_mask(LOG_UNIMP, "digic-uart: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); + TARGET_FMT_plx "\n", addr << 2); } } diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 3957e78abf..a5a285655f 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -589,28 +589,8 @@ DeviceState *exynos4210_uart_create(hwaddr addr, DeviceState *dev; SysBusDevice *bus; - const char chr_name[] = "serial"; - char label[ARRAY_SIZE(chr_name) + 1]; - dev = qdev_create(NULL, TYPE_EXYNOS4210_UART); - if (!chr) { - if (channel >= MAX_SERIAL_PORTS) { - error_report("Only %d serial ports are supported by QEMU", - MAX_SERIAL_PORTS); - exit(1); - } - chr = serial_hds[channel]; - if (!chr) { - snprintf(label, ARRAY_SIZE(label), "%s%d", chr_name, channel); - chr = qemu_chr_new(label, "null"); - if (!(chr)) { - error_report("Can't assign serial port to UART%d", channel); - exit(1); - } - } - } - qdev_prop_set_chr(dev, "chardev", chr); qdev_prop_set_uint32(dev, "channel", channel); qdev_prop_set_uint32(dev, "rx-size", fifo_size); diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index faae083e78..787f985db6 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -10,7 +10,6 @@ #include "hw/sysbus.h" #include "hw/m68k/mcf.h" #include "chardev/char-fe.h" -#include "exec/address-spaces.h" typedef struct { SysBusDevice parent_obj; diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 1542d62201..a80da47ecf 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -30,6 +30,7 @@ #include "hw/isa/isa.h" #include "hw/char/parallel.h" #include "sysemu/sysemu.h" +#include "trace.h" //#define DEBUG_PARALLEL @@ -110,9 +111,8 @@ parallel_ioport_write_sw(void *opaque, uint32_t addr, uint32_t val) { ParallelState *s = opaque; - pdebug("write addr=0x%02x val=0x%02x\n", addr, val); - addr &= 7; + trace_parallel_ioport_write("SW", addr, val); switch(addr) { case PARA_REG_DATA: s->dataw = val; @@ -157,6 +157,7 @@ static void parallel_ioport_write_hw(void *opaque, uint32_t addr, uint32_t val) s->last_read_offset = ~0U; addr &= 7; + trace_parallel_ioport_write("HW", addr, val); switch(addr) { case PARA_REG_DATA: if (s->dataw == val) @@ -230,6 +231,8 @@ parallel_ioport_eppdata_write_hw2(void *opaque, uint32_t addr, uint32_t val) struct ParallelIOArg ioarg = { .buffer = &eppdata, .count = sizeof(eppdata) }; + + trace_parallel_ioport_write("EPP", addr, val); if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) { /* Controls not correct for EPP data cycle, so do nothing */ pdebug("we%04x s\n", val); @@ -253,6 +256,8 @@ parallel_ioport_eppdata_write_hw4(void *opaque, uint32_t addr, uint32_t val) struct ParallelIOArg ioarg = { .buffer = &eppdata, .count = sizeof(eppdata) }; + + trace_parallel_ioport_write("EPP", addr, val); if ((s->control & (PARA_CTR_DIR|PARA_CTR_SIGNAL)) != PARA_CTR_INIT) { /* Controls not correct for EPP data cycle, so do nothing */ pdebug("we%08x s\n", val); @@ -299,7 +304,7 @@ static uint32_t parallel_ioport_read_sw(void *opaque, uint32_t addr) ret = s->control; break; } - pdebug("read addr=0x%02x val=0x%02x\n", addr, ret); + trace_parallel_ioport_read("SW", addr, ret); return ret; } @@ -371,6 +376,7 @@ static uint32_t parallel_ioport_read_hw(void *opaque, uint32_t addr) } break; } + trace_parallel_ioport_read("HW", addr, ret); s->last_read_offset = addr; return ret; } @@ -399,6 +405,7 @@ parallel_ioport_eppdata_read_hw2(void *opaque, uint32_t addr) } else pdebug("re%04x\n", ret); + trace_parallel_ioport_read("EPP", addr, ret); return ret; } @@ -426,11 +433,13 @@ parallel_ioport_eppdata_read_hw4(void *opaque, uint32_t addr) } else pdebug("re%08x\n", ret); + trace_parallel_ioport_read("EPP", addr, ret); return ret; } static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val) { + trace_parallel_ioport_write("ECP", addr & 7, val); pdebug("wecp%d=%02x\n", addr & 7, val); } @@ -438,6 +447,7 @@ static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr) { uint8_t ret = 0xff; + trace_parallel_ioport_read("ECP", addr & 7, ret); pdebug("recp%d:%02x\n", addr & 7, ret); return ret; } @@ -554,56 +564,28 @@ static void parallel_isa_realizefn(DeviceState *dev, Error **errp) } /* Memory mapped interface */ -static uint32_t parallel_mm_readb (void *opaque, hwaddr addr) +static uint64_t parallel_mm_readfn(void *opaque, hwaddr addr, unsigned size) { ParallelState *s = opaque; - return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFF; + return parallel_ioport_read_sw(s, addr >> s->it_shift) & + MAKE_64BIT_MASK(0, size * 8); } -static void parallel_mm_writeb (void *opaque, - hwaddr addr, uint32_t value) +static void parallel_mm_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { ParallelState *s = opaque; - parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFF); -} - -static uint32_t parallel_mm_readw (void *opaque, hwaddr addr) -{ - ParallelState *s = opaque; - - return parallel_ioport_read_sw(s, addr >> s->it_shift) & 0xFFFF; -} - -static void parallel_mm_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - ParallelState *s = opaque; - - parallel_ioport_write_sw(s, addr >> s->it_shift, value & 0xFFFF); -} - -static uint32_t parallel_mm_readl (void *opaque, hwaddr addr) -{ - ParallelState *s = opaque; - - return parallel_ioport_read_sw(s, addr >> s->it_shift); -} - -static void parallel_mm_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - ParallelState *s = opaque; - - parallel_ioport_write_sw(s, addr >> s->it_shift, value); + parallel_ioport_write_sw(s, addr >> s->it_shift, + value & MAKE_64BIT_MASK(0, size * 8)); } static const MemoryRegionOps parallel_mm_ops = { - .old_mmio = { - .read = { parallel_mm_readb, parallel_mm_readw, parallel_mm_readl }, - .write = { parallel_mm_writeb, parallel_mm_writew, parallel_mm_writel }, - }, + .read = parallel_mm_readfn, + .write = parallel_mm_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index d7c5cc11fe..116b7b2e69 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -39,10 +39,10 @@ typedef struct ISASerialState { SerialState state; } ISASerialState; -static const int isa_serial_io[MAX_SERIAL_PORTS] = { +static const int isa_serial_io[MAX_ISA_SERIAL_PORTS] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; -static const int isa_serial_irq[MAX_SERIAL_PORTS] = { +static const int isa_serial_irq[MAX_ISA_SERIAL_PORTS] = { 4, 3, 4, 3 }; @@ -56,9 +56,9 @@ static void serial_isa_realizefn(DeviceState *dev, Error **errp) if (isa->index == -1) { isa->index = index; } - if (isa->index >= MAX_SERIAL_PORTS) { + if (isa->index >= MAX_ISA_SERIAL_PORTS) { error_setg(errp, "Max. supported number of ISA serial ports is %d.", - MAX_SERIAL_PORTS); + MAX_ISA_SERIAL_PORTS); return; } if (isa->iobase == -1) { @@ -138,11 +138,11 @@ void serial_hds_isa_init(ISABus *bus, int from, int to) int i; assert(from >= 0); - assert(to <= MAX_SERIAL_PORTS); + assert(to <= MAX_ISA_SERIAL_PORTS); for (i = from; i < to; ++i) { - if (serial_hds[i]) { - serial_isa_init(bus, i, serial_hds[i]); + if (serial_hd(i)) { + serial_isa_init(bus, i, serial_hd(i)); } } } diff --git a/hw/char/serial.c b/hw/char/serial.c index eb72191ee7..251f40fdac 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -28,8 +28,8 @@ #include "chardev/char-serial.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "exec/address-spaces.h" #include "qemu/error-report.h" +#include "trace.h" //#define DEBUG_SERIAL @@ -150,13 +150,10 @@ static void serial_update_irq(SerialState *s) static void serial_update_parameters(SerialState *s) { - int speed, parity, data_bits, stop_bits, frame_size; + float speed; + int parity, data_bits, stop_bits, frame_size; QEMUSerialSetParams ssp; - if (s->divider == 0 || s->divider > s->baudbase) { - return; - } - /* Start bit. */ frame_size = 1; if (s->lcr & 0x08) { @@ -169,14 +166,16 @@ static void serial_update_parameters(SerialState *s) } else { parity = 'N'; } - if (s->lcr & 0x04) + if (s->lcr & 0x04) { stop_bits = 2; - else + } else { stop_bits = 1; + } data_bits = (s->lcr & 0x03) + 5; frame_size += data_bits + stop_bits; - speed = s->baudbase / s->divider; + /* Zero divisor should give about 3500 baud */ + speed = (s->divider == 0) ? 3500 : (float) s->baudbase / s->divider; ssp.speed = speed; ssp.parity = parity; ssp.data_bits = data_bits; @@ -184,7 +183,7 @@ static void serial_update_parameters(SerialState *s) s->char_transmit_time = (NANOSECONDS_PER_SECOND / speed) * frame_size; qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); - DPRINTF("speed=%d parity=%c data=%d stop=%d\n", + DPRINTF("speed=%.2f parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits); } @@ -261,15 +260,20 @@ static void serial_xmit(SerialState *s) if (s->mcr & UART_MCR_LOOP) { /* in loopback mode, say that we just received a char */ serial_receive1(s, &s->tsr, 1); - } else if (qemu_chr_fe_write(&s->chr, &s->tsr, 1) != 1 && - s->tsr_retry < MAX_XMIT_RETRY) { - assert(s->watch_tag == 0); - s->watch_tag = - qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, - serial_watch_cb, s); - if (s->watch_tag > 0) { - s->tsr_retry++; - return; + } else { + int rc = qemu_chr_fe_write(&s->chr, &s->tsr, 1); + + if ((rc == 0 || + (rc == -1 && errno == EAGAIN)) && + s->tsr_retry < MAX_XMIT_RETRY) { + assert(s->watch_tag == 0); + s->watch_tag = + qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + serial_watch_cb, s); + if (s->watch_tag > 0) { + s->tsr_retry++; + return; + } } } s->tsr_retry = 0; @@ -336,12 +340,16 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, SerialState *s = opaque; addr &= 7; - DPRINTF("write addr=0x%" HWADDR_PRIx " val=0x%" PRIx64 "\n", addr, val); + trace_serial_ioport_write(addr, val); switch(addr) { default: case 0: if (s->lcr & UART_LCR_DLAB) { - s->divider = (s->divider & 0xff00) | val; + if (size == 2) { + s->divider = (s->divider & 0xff00) | val; + } else if (size == 4) { + s->divider = val; + } serial_update_parameters(s); } else { s->thr = (uint8_t) val; @@ -549,7 +557,7 @@ static uint64_t serial_ioport_read(void *opaque, hwaddr addr, unsigned size) ret = s->scr; break; } - DPRINTF("read addr=0x%" HWADDR_PRIx " val=0x%02x\n", addr, ret); + trace_serial_ioport_read(addr, ret); return ret; } @@ -923,11 +931,6 @@ static int serial_be_change(void *opaque) void serial_realize_core(SerialState *s, Error **errp) { - if (!qemu_chr_fe_backend_connected(&s->chr)) { - error_setg(errp, "Can't create serial device, empty char device"); - return; - } - s->modem_status_poll = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) serial_update_msl, s); s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) fifo_timeout_int, s); diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 835b5378a0..373a40595f 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -28,7 +28,6 @@ #include "hw/hw.h" #include "hw/sh4/sh.h" #include "chardev/char-fe.h" -#include "exec/address-spaces.h" #include "qapi/error.h" //#define DEBUG_SERIAL diff --git a/hw/char/trace-events b/hw/char/trace-events index ebd8a92450..b64213d4dd 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -1,5 +1,13 @@ # See docs/devel/tracing.txt for syntax documentation. +# hw/char/parallel.c +parallel_ioport_read(const char *desc, uint16_t addr, uint8_t value) "read [%s] addr 0x%02x val 0x%02x" +parallel_ioport_write(const char *desc, uint16_t addr, uint8_t value) "write [%s] addr 0x%02x val 0x%02x" + +# hw/char/serial.c +serial_ioport_read(uint16_t addr, uint8_t value) "read addr 0x%02x val 0x%02x" +serial_ioport_write(uint16_t addr, uint8_t value) "write addr 0x%02x val 0x%02x" + # hw/char/virtio-serial-bus.c virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t value) "port %u, event %u, value %u" virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, throttle %d" diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 5e68326c19..8b4b4bf523 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -201,7 +201,7 @@ static int con_init(struct XenDevice *xendev) /* no Xen override, use qemu output device */ if (output == NULL) { if (con->xendev.dev) { - qemu_chr_fe_init(&con->chr, serial_hds[con->xendev.dev], + qemu_chr_fe_init(&con->chr, serial_hd(con->xendev.dev), &error_abort); } } else { @@ -233,12 +233,11 @@ static int con_initialise(struct XenDevice *xendev) if (!xendev->dev) { xen_pfn_t mfn = con->ring_ref; con->sring = xenforeignmemory_map(xen_fmem, con->xendev.dom, - PROT_READ|PROT_WRITE, + PROT_READ | PROT_WRITE, 1, &mfn, NULL); } else { - con->sring = xengnttab_map_grant_ref(xendev->gnttabdev, con->xendev.dom, - con->ring_ref, - PROT_READ|PROT_WRITE); + con->sring = xen_be_map_grant_ref(xendev, con->ring_ref, + PROT_READ | PROT_WRITE); } if (!con->sring) return -1; @@ -267,7 +266,7 @@ static void con_disconnect(struct XenDevice *xendev) if (!xendev->dev) { xenforeignmemory_unmap(xen_fmem, con->sring, 1); } else { - xengnttab_unmap(xendev->gnttabdev, con->sring, 1); + xen_be_unmap_grant_ref(xendev, con->sring); } con->sring = NULL; } diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c index 17ac986685..2253072d0e 100644 --- a/hw/core/hotplug.c +++ b/hw/core/hotplug.c @@ -35,6 +35,16 @@ void hotplug_handler_plug(HotplugHandler *plug_handler, } } +void hotplug_handler_post_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->post_plug) { + hdc->post_plug(plug_handler, plugged_dev); + } +} + void hotplug_handler_unplug_request(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 0c4a7207f4..447f60857d 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "qemu/units.h" #include "exec/memory.h" #include "hw/loader.h" #include "hw/loader-fit.h" @@ -195,7 +195,7 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, err = fit_image_addr(itb, img_off, "load", &load_addr); if (err == -ENOENT) { - load_addr = ROUND_UP(kernel_end, 64 * K_BYTE) + (10 * M_BYTE); + load_addr = ROUND_UP(kernel_end, 64 * KiB) + (10 * MiB); } else if (err) { ret = err; goto out; diff --git a/hw/core/loader.c b/hw/core/loader.c index 06bdbca537..bbb6e65bb5 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -191,7 +191,7 @@ void pstrcpy_targphys(const char *name, hwaddr dest, int buf_size, rom_add_blob_fixed(name, source, (nulp - source) + 1, dest); } else { rom_add_blob_fixed(name, source, buf_size, dest); - ptr = rom_ptr(dest + buf_size - 1); + ptr = rom_ptr(dest + buf_size - 1, sizeof(*ptr)); *ptr = 0; } } @@ -1165,7 +1165,7 @@ void rom_reset_order_override(void) fw_cfg_reset_order_override(fw_cfg); } -static Rom *find_rom(hwaddr addr) +static Rom *find_rom(hwaddr addr, size_t size) { Rom *rom; @@ -1179,7 +1179,7 @@ static Rom *find_rom(hwaddr addr) if (rom->addr > addr) { continue; } - if (rom->addr + rom->romsize < addr) { + if (rom->addr + rom->romsize < addr + size) { continue; } return rom; @@ -1249,11 +1249,11 @@ int rom_copy(uint8_t *dest, hwaddr addr, size_t size) return (d + l) - dest; } -void *rom_ptr(hwaddr addr) +void *rom_ptr(hwaddr addr, size_t size) { Rom *rom; - rom = find_rom(addr); + rom = find_rom(addr, size); if (!rom || !rom->data) return NULL; return rom->data + (addr - rom->addr); diff --git a/hw/core/machine.c b/hw/core/machine.c index 2040177664..a9aeb22f03 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/boards.h" #include "qapi/error.h" #include "qapi/qapi-visit-common.h" @@ -19,7 +20,6 @@ #include "sysemu/sysemu.h" #include "sysemu/numa.h" #include "qemu/error-report.h" -#include "qemu/cutils.h" #include "sysemu/qtest.h" static char *machine_get_accel(Object *obj, Error **errp) @@ -522,7 +522,7 @@ static void machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); /* Default 128 MB as guest ram size */ - mc->default_ram_size = 128 * M_BYTE; + mc->default_ram_size = 128 * MiB; mc->rom_file_has_mr = true; /* numa node memory size aligned on 8MB by default. @@ -674,6 +674,7 @@ static void machine_finalize(Object *obj) g_free(ms->dumpdtb); g_free(ms->dt_compatible); g_free(ms->firmware); + g_free(ms->device_memory); } bool machine_usb(MachineState *machine) @@ -737,7 +738,7 @@ static char *cpu_slot_to_string(const CPUArchId *cpu) return g_string_free(s, false); } -static void machine_numa_finish_init(MachineState *machine) +static void machine_numa_finish_cpu_init(MachineState *machine) { int i; bool default_mapping; @@ -791,9 +792,9 @@ void machine_run_board_init(MachineState *machine) { MachineClass *machine_class = MACHINE_GET_CLASS(machine); - if (nb_numa_nodes) { - machine_numa_finish_init(machine); - } + numa_complete_configuration(machine); + if (nb_numa_nodes) + machine_numa_finish_cpu_init(machine); /* If the machine supports the valid_cpu_types check and the user * specified a CPU with -cpu check here that the user CPU is supported. diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index f9d76c4641..a86901b673 100644 --- a/hw/core/or-irq.c +++ b/hw/core/or-irq.c @@ -66,14 +66,49 @@ static void or_irq_init(Object *obj) qdev_init_gpio_out(DEVICE(obj), &s->out_irq, 1); } +/* The original version of this device had a fixed 16 entries in its + * VMState array; devices with more inputs than this need to + * migrate the extra lines via a subsection. + * The subsection migrates as much of the levels[] array as is needed + * (including repeating the first 16 elements), to avoid the awkwardness + * of splitting it in two to meet the requirements of VMSTATE_VARRAY_UINT16. + */ +#define OLD_MAX_OR_LINES 16 +#if MAX_OR_LINES < OLD_MAX_OR_LINES +#error MAX_OR_LINES must be at least 16 for migration compatibility +#endif + +static bool vmstate_extras_needed(void *opaque) +{ + qemu_or_irq *s = OR_IRQ(opaque); + + return s->num_lines >= OLD_MAX_OR_LINES; +} + +static const VMStateDescription vmstate_or_irq_extras = { + .name = "or-irq-extras", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_extras_needed, + .fields = (VMStateField[]) { + VMSTATE_VARRAY_UINT16_UNSAFE(levels, qemu_or_irq, num_lines, 0, + vmstate_info_bool, bool), + VMSTATE_END_OF_LIST(), + }, +}; + static const VMStateDescription vmstate_or_irq = { .name = TYPE_OR_IRQ, .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_BOOL_ARRAY(levels, qemu_or_irq, MAX_OR_LINES), + VMSTATE_BOOL_SUB_ARRAY(levels, qemu_or_irq, 0, OLD_MAX_OR_LINES), VMSTATE_END_OF_LIST(), - } + }, + .subsections = (const VMStateDescription*[]) { + &vmstate_or_irq_extras, + NULL + }, }; static Property or_irq_properties[] = { diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index 33d32fbf22..e473a44746 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "hw/platform-bus.h" -#include "exec/address-spaces.h" #include "qemu/error-report.h" #include "sysemu/sysemu.h" @@ -103,7 +102,6 @@ static void plaform_bus_refresh_irqs(PlatformBusDevice *pbus) { bitmap_zero(pbus->used_irqs, pbus->num_irqs); foreach_dynamic_sysbus_device(platform_bus_count_irqs, pbus); - pbus->done_gathering = true; } static void platform_bus_map_irq(PlatformBusDevice *pbus, SysBusDevice *sbdev, @@ -163,12 +161,11 @@ static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, } /* - * For each sysbus device, look for unassigned IRQ lines as well as - * unassociated MMIO regions. Connect them to the platform bus if available. + * Look for unassigned IRQ lines as well as unassociated MMIO regions. + * Connect them to the platform bus if available. */ -static void link_sysbus_device(SysBusDevice *sbdev, void *opaque) +void platform_bus_link_device(PlatformBusDevice *pbus, SysBusDevice *sbdev) { - PlatformBusDevice *pbus = opaque; int i; for (i = 0; sysbus_has_irq(sbdev, i); i++) { @@ -180,19 +177,6 @@ static void link_sysbus_device(SysBusDevice *sbdev, void *opaque) } } -static void platform_bus_init_notify(Notifier *notifier, void *data) -{ - PlatformBusDevice *pb = container_of(notifier, PlatformBusDevice, notifier); - - /* - * Generate a bitmap of used IRQ lines, as the user might have specified - * them on the command line. - */ - plaform_bus_refresh_irqs(pb); - - foreach_dynamic_sysbus_device(link_sysbus_device, pb); -} - static void platform_bus_realize(DeviceState *dev, Error **errp) { PlatformBusDevice *pbus; @@ -211,12 +195,8 @@ static void platform_bus_realize(DeviceState *dev, Error **errp) sysbus_init_irq(d, &pbus->irqs[i]); } - /* - * Register notifier that allows us to gather dangling devices once the - * machine is completely assembled - */ - pbus->notifier.notify = platform_bus_init_notify; - qemu_add_machine_init_done_notifier(&pbus->notifier); + /* some devices might be initialized before so update used IRQs map */ + plaform_bus_refresh_irqs(pbus); } static Property platform_bus_properties[] = { diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index 7221c68a98..170fd34d8b 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -45,8 +45,20 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust) uint32_t period_frac = s->period_frac; uint64_t period = s->period; uint64_t delta = s->delta; + bool suppress_trigger = false; - if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { + /* + * Note that if delta_adjust is 0 then we must be here because of + * a count register write or timer start, not because of timer expiry. + * In that case the policy might require us to suppress the timer trigger + * that we would otherwise generate for a zero delta. + */ + if (delta_adjust == 0 && + (s->policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT)) { + suppress_trigger = true; + } + if (delta == 0 && !(s->policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER) + && !suppress_trigger) { ptimer_trigger(s); } @@ -353,6 +365,14 @@ ptimer_state *ptimer_init(QEMUBH *bh, uint8_t policy_mask) s->bh = bh; s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ptimer_tick, s); s->policy_mask = policy_mask; + + /* + * These two policies are incompatible -- trigger-on-decrement implies + * a timer trigger when the count becomes 0, but no-immediate-trigger + * implies a trigger when the count stops being 0. + */ + assert(!((policy_mask & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) && + (policy_mask & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER))); return s; } diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 1d3ba722fa..8b22fb51c9 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -321,86 +321,6 @@ const PropertyInfo qdev_prop_netdev = { .set = set_netdev, }; -/* --- vlan --- */ - -static int print_vlan(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); - - if (*ptr) { - int id; - if (!net_hub_id_for_client(*ptr, &id)) { - return snprintf(dest, len, "%d", id); - } - } - - return snprintf(dest, len, ""); -} - -static void get_vlan(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - NetClientState **ptr = qdev_get_prop_ptr(dev, prop); - int32_t id = -1; - - if (*ptr) { - int hub_id; - if (!net_hub_id_for_client(*ptr, &hub_id)) { - id = hub_id; - } - } - - visit_type_int32(v, name, &id, errp); -} - -static void set_vlan(Object *obj, Visitor *v, const char *name, void *opaque, - Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - NICPeers *peers_ptr = qdev_get_prop_ptr(dev, prop); - NetClientState **ptr = &peers_ptr->ncs[0]; - Error *local_err = NULL; - int32_t id; - NetClientState *hubport; - - if (dev->realized) { - qdev_prop_set_after_realize(dev, name, errp); - return; - } - - visit_type_int32(v, name, &id, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (id == -1) { - *ptr = NULL; - return; - } - if (*ptr) { - error_set_from_qdev_prop_error(errp, -EINVAL, dev, prop, name); - return; - } - - hubport = net_hub_port_find(id); - if (!hubport) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name, prop->info->name); - return; - } - *ptr = hubport; -} - -const PropertyInfo qdev_prop_vlan = { - .name = "int32", - .description = "Integer VLAN id to connect to", - .print = print_vlan, - .get = get_vlan, - .set = set_vlan, -}; void qdev_prop_set_drive(DeviceState *dev, const char *name, BlockBackend *value, Error **errp) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 5bbc2d98b5..35072dec1e 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -5,7 +5,6 @@ #include "hw/pci/pci.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" -#include "sysemu/block-backend.h" #include "hw/block/block.h" #include "net/hub.h" #include "qapi/visitor.h" @@ -1309,7 +1308,7 @@ static void create_link_property(Object *obj, Property *prop, Error **errp) object_property_add_link(obj, prop->name, prop->link_type, child, qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, errp); } diff --git a/hw/core/qdev.c b/hw/core/qdev.c index f6f92473b8..529b82de18 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -208,32 +208,6 @@ void device_listener_unregister(DeviceListener *listener) QTAILQ_REMOVE(&device_listeners, listener, link); } -static void device_realize(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dc->init) { - int rc = dc->init(dev); - if (rc < 0) { - error_setg(errp, "Device initialization failed."); - return; - } - } -} - -static void device_unrealize(DeviceState *dev, Error **errp) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - if (dc->exit) { - int rc = dc->exit(dev); - if (rc < 0) { - error_setg(errp, "Device exit failed."); - return; - } - } -} - void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version) { @@ -435,7 +409,7 @@ void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins, object_property_add_link(OBJECT(dev), propname, TYPE_IRQ, (Object **)&pins[i], object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); g_free(propname); } @@ -893,6 +867,10 @@ static void device_set_realized(Object *obj, bool value, Error **errp) device_reset(dev); } dev->pending_deleted_event = false; + + if (hotplug_ctrl) { + hotplug_handler_post_plug(hotplug_ctrl, dev); + } } else if (!value && dev->realized) { Error **local_errp = NULL; QLIST_FOREACH(bus, &dev->child_bus, sibling) { @@ -1065,8 +1043,6 @@ static void device_class_init(ObjectClass *class, void *data) DeviceClass *dc = DEVICE_CLASS(class); class->unparent = device_unparent; - dc->realize = device_realize; - dc->unrealize = device_unrealize; /* by default all devices were considered as hotpluggable, * so with intent to check it in generic qdev_unplug() / diff --git a/hw/core/register.c b/hw/core/register.c index 0741a1af32..d2d1636250 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -96,7 +96,7 @@ void register_write(RegisterInfo *reg, uint64_t val, uint64_t we, if (test) { qemu_log_mask(LOG_UNIMP, "%s:%s writing %#" PRIx64 " to unimplemented bits:" \ - " %#" PRIx64 "", + " %#" PRIx64 "\n", prefix, reg->access->name, val, ac->unimp); } diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 5d0887f499..3c8e53b188 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "monitor/monitor.h" #include "exec/address-spaces.h" @@ -200,15 +201,18 @@ void sysbus_init_ioports(SysBusDevice *dev, uint32_t ioport, uint32_t size) } } -static int sysbus_device_init(DeviceState *dev) +/* TODO remove once all sysbus devices have been converted to realize */ +static void sysbus_realize(DeviceState *dev, Error **errp) { SysBusDevice *sd = SYS_BUS_DEVICE(dev); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(sd); if (!sbc->init) { - return 0; + return; + } + if (sbc->init(sd) < 0) { + error_setg(errp, "Device initialization failed"); } - return sbc->init(sd); } DeviceState *sysbus_create_varargs(const char *name, @@ -324,7 +328,7 @@ MemoryRegion *sysbus_address_space(SysBusDevice *dev) static void sysbus_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); - k->init = sysbus_device_init; + k->realize = sysbus_realize; k->bus_type = TYPE_SYSTEM_BUS; /* * device_add plugs devices into a suitable bus. For "real" buses, @@ -372,6 +376,14 @@ BusState *sysbus_get_default(void) return main_system_bus; } +void sysbus_init_child_obj(Object *parent, const char *childname, void *child, + size_t childsize, const char *childtype) +{ + object_initialize_child(parent, childname, child, childsize, childtype, + &error_abort, NULL); + qdev_set_parent_bus(DEVICE(child), sysbus_get_default()); +} + static void sysbus_register_types(void) { type_register_static(&system_bus_info); diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index bc05152fd3..43c1079493 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -35,15 +35,13 @@ static void a15mp_priv_initfn(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); A15MPPrivState *s = A15MPCORE_PRIV(obj); - DeviceState *gicdev; memory_region_init(&s->container, obj, "a15mp-priv-container", 0x8000); sysbus_init_mmio(sbd, &s->container); - object_initialize(&s->gic, sizeof(s->gic), gic_class_name()); - gicdev = DEVICE(&s->gic); - qdev_set_parent_bus(gicdev, sysbus_get_default()); - qdev_prop_set_uint32(gicdev, "revision", 2); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), + gic_class_name()); + qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 2); } static void a15mp_priv_realize(DeviceState *dev, Error **errp) diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index f17f292090..a5b867872c 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -27,20 +27,18 @@ static void a9mp_priv_initfn(Object *obj) memory_region_init(&s->container, obj, "a9mp-priv-container", 0x2000); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->container); - object_initialize(&s->scu, sizeof(s->scu), TYPE_A9_SCU); - qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); + sysbus_init_child_obj(obj, "scu", &s->scu, sizeof(s->scu), TYPE_A9_SCU); - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); - qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), TYPE_ARM_GIC); - object_initialize(&s->gtimer, sizeof(s->gtimer), TYPE_A9_GTIMER); - qdev_set_parent_bus(DEVICE(&s->gtimer), sysbus_get_default()); + sysbus_init_child_obj(obj, "gtimer", &s->gtimer, sizeof(s->gtimer), + TYPE_A9_GTIMER); - object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); + sysbus_init_child_obj(obj, "mptimer", &s->mptimer, sizeof(s->mptimer), + TYPE_ARM_MPTIMER); - object_initialize(&s->wdt, sizeof(s->wdt), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->wdt), sysbus_get_default()); + sysbus_init_child_obj(obj, "wdt", &s->wdt, sizeof(s->wdt), + TYPE_ARM_MPTIMER); } static void a9mp_priv_realize(DeviceState *dev, Error **errp) diff --git a/hw/cpu/arm11mpcore.c b/hw/cpu/arm11mpcore.c index eb244658b9..8aead3794e 100644 --- a/hw/cpu/arm11mpcore.c +++ b/hw/cpu/arm11mpcore.c @@ -121,19 +121,17 @@ static void mpcore_priv_initfn(Object *obj) "mpcore-priv-container", 0x2000); sysbus_init_mmio(sbd, &s->container); - object_initialize(&s->scu, sizeof(s->scu), TYPE_ARM11_SCU); - qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default()); + sysbus_init_child_obj(obj, "scu", &s->scu, sizeof(s->scu), TYPE_ARM11_SCU); - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); - qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default()); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), TYPE_ARM_GIC); /* Request the legacy 11MPCore GIC behaviour: */ qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 0); - object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default()); + sysbus_init_child_obj(obj, "mptimer", &s->mptimer, sizeof(s->mptimer), + TYPE_ARM_MPTIMER); - object_initialize(&s->wdtimer, sizeof(s->wdtimer), TYPE_ARM_MPTIMER); - qdev_set_parent_bus(DEVICE(&s->wdtimer), sysbus_get_default()); + sysbus_init_child_obj(obj, "wdtimer", &s->wdtimer, sizeof(s->wdtimer), + TYPE_ARM_MPTIMER); } static Property mpcore_priv_properties[] = { diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c index 39d4ebeb1d..9d3f8378fb 100644 --- a/hw/cpu/realview_mpcore.c +++ b/hw/cpu/realview_mpcore.c @@ -101,14 +101,14 @@ static void mpcore_rirq_init(Object *obj) SysBusDevice *privbusdev; int i; - object_initialize(&s->priv, sizeof(s->priv), TYPE_ARM11MPCORE_PRIV); - qdev_set_parent_bus(DEVICE(&s->priv), sysbus_get_default()); + sysbus_init_child_obj(obj, "a11priv", &s->priv, sizeof(s->priv), + TYPE_ARM11MPCORE_PRIV); privbusdev = SYS_BUS_DEVICE(&s->priv); sysbus_init_mmio(sbd, sysbus_mmio_get_region(privbusdev, 0)); for (i = 0; i < 4; i++) { - object_initialize(&s->gic[i], sizeof(s->gic[i]), TYPE_REALVIEW_GIC); - qdev_set_parent_bus(DEVICE(&s->gic[i]), sysbus_get_default()); + sysbus_init_child_obj(obj, "gic[*]", &s->gic[i], sizeof(s->gic[i]), + TYPE_REALVIEW_GIC); } } diff --git a/hw/cris/axis_dev88.c b/hw/cris/axis_dev88.c index 9ccc4350a5..191292eebf 100644 --- a/hw/cris/axis_dev88.c +++ b/hw/cris/axis_dev88.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -34,7 +35,6 @@ #include "hw/loader.h" #include "elf.h" #include "boot.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" #include "sysemu/sysemu.h" @@ -243,7 +243,7 @@ static const MemoryRegionOps gpio_ops = { }, }; -#define INTMEM_SIZE (128 * 1024) +#define INTMEM_SIZE (128 * KiB) static struct cris_load_info li; @@ -337,7 +337,7 @@ void axisdev88_init(MachineState *machine) sysbus_create_varargs("etraxfs,timer", 0x3005e000, irq[0x1b], nmi[1], NULL); for (i = 0; i < 4; i++) { - etraxfs_ser_create(0x30026000 + i * 0x2000, irq[0x14 + i], serial_hds[i]); + etraxfs_ser_create(0x30026000 + i * 0x2000, irq[0x14 + i], serial_hd(i)); } if (kernel_filename) { diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index 3c7c75b94d..fb8408c6d0 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -1,3 +1,6 @@ +common-obj-y += ramfb.o +common-obj-y += ramfb-standalone.o + common-obj-$(CONFIG_ADS7846) += ads7846.o common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o common-obj-$(CONFIG_G364FB) += g364fb.o @@ -9,6 +12,7 @@ common-obj-$(CONFIG_SSD0323) += ssd0323.o common-obj-$(CONFIG_XEN) += xenfb.o common-obj-$(CONFIG_VGA_PCI) += vga-pci.o +common-obj-$(CONFIG_VGA_PCI) += bochs-display.o common-obj-$(CONFIG_VGA_ISA) += vga-isa.o common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o @@ -20,6 +24,8 @@ common-obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o common-obj-$(CONFIG_ZAURUS) += tc6393xb.o common-obj-$(CONFIG_MILKYMIST_TMU2) += milkymist-tmu2.o +milkymist-tmu2.o-cflags := $(X11_CFLAGS) +milkymist-tmu2.o-libs := $(X11_LIBS) obj-$(CONFIG_OMAP) += omap_dss.o obj-$(CONFIG_OMAP) += omap_lcdc.o @@ -33,8 +39,8 @@ obj-$(CONFIG_VGA) += vga.o common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o -obj-$(CONFIG_VIRTIO) += virtio-gpu.o virtio-gpu-3d.o -obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o +obj-$(CONFIG_VIRTIO_GPU) += virtio-gpu.o virtio-gpu-3d.o +obj-$(call land,$(CONFIG_VIRTIO_GPU),$(CONFIG_VIRTIO_PCI)) += virtio-gpu-pci.o obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o virtio-gpu.o-cflags := $(VIRGL_CFLAGS) virtio-gpu.o-libs += $(VIRGL_LIBS) diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 7eab927652..3355f4c131 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/display/bcm2835_fb.h" -#include "hw/display/framebuffer.h" +#include "framebuffer.h" #include "ui/pixel_ops.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "qemu/log.h" diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c new file mode 100644 index 0000000000..09d8944a1b --- /dev/null +++ b/hw/display/bochs-display.c @@ -0,0 +1,366 @@ +/* + * QEMU PCI bochs display adapter. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hw/display/bochs-vbe.h" + +#include "qapi/error.h" + +#include "ui/console.h" +#include "ui/qemu-pixman.h" + +typedef struct BochsDisplayMode { + pixman_format_code_t format; + uint32_t bytepp; + uint32_t width; + uint32_t height; + uint32_t stride; + uint64_t offset; + uint64_t size; +} BochsDisplayMode; + +typedef struct BochsDisplayState { + /* parent */ + PCIDevice pci; + + /* device elements */ + QemuConsole *con; + MemoryRegion vram; + MemoryRegion mmio; + MemoryRegion vbe; + MemoryRegion qext; + + /* device config */ + uint64_t vgamem; + + /* device registers */ + uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; + bool big_endian_fb; + + /* device state */ + BochsDisplayMode mode; +} BochsDisplayState; + +#define TYPE_BOCHS_DISPLAY "bochs-display" +#define BOCHS_DISPLAY(obj) OBJECT_CHECK(BochsDisplayState, (obj), \ + TYPE_BOCHS_DISPLAY) + +static const VMStateDescription vmstate_bochs_display = { + .name = "bochs-display", + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pci, BochsDisplayState), + VMSTATE_UINT16_ARRAY(vbe_regs, BochsDisplayState, VBE_DISPI_INDEX_NB), + VMSTATE_BOOL(big_endian_fb, BochsDisplayState), + VMSTATE_END_OF_LIST() + } +}; + +static uint64_t bochs_display_vbe_read(void *ptr, hwaddr addr, + unsigned size) +{ + BochsDisplayState *s = ptr; + unsigned int index = addr >> 1; + + switch (index) { + case VBE_DISPI_INDEX_ID: + return VBE_DISPI_ID5; + case VBE_DISPI_INDEX_VIDEO_MEMORY_64K: + return s->vgamem / (64 * KiB); + } + + if (index >= ARRAY_SIZE(s->vbe_regs)) { + return -1; + } + return s->vbe_regs[index]; +} + +static void bochs_display_vbe_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + BochsDisplayState *s = ptr; + unsigned int index = addr >> 1; + + if (index >= ARRAY_SIZE(s->vbe_regs)) { + return; + } + s->vbe_regs[index] = val; +} + +static const MemoryRegionOps bochs_display_vbe_ops = { + .read = bochs_display_vbe_read, + .write = bochs_display_vbe_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 2, + .impl.max_access_size = 2, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t bochs_display_qext_read(void *ptr, hwaddr addr, + unsigned size) +{ + BochsDisplayState *s = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_SIZE: + return PCI_VGA_QEXT_SIZE; + case PCI_VGA_QEXT_REG_BYTEORDER: + return s->big_endian_fb ? + PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN; + default: + return 0; + } +} + +static void bochs_display_qext_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + BochsDisplayState *s = ptr; + + switch (addr) { + case PCI_VGA_QEXT_REG_BYTEORDER: + if (val == PCI_VGA_QEXT_BIG_ENDIAN) { + s->big_endian_fb = true; + } + if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) { + s->big_endian_fb = false; + } + break; + } +} + +static const MemoryRegionOps bochs_display_qext_ops = { + .read = bochs_display_qext_read, + .write = bochs_display_qext_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int bochs_display_get_mode(BochsDisplayState *s, + BochsDisplayMode *mode) +{ + uint16_t *vbe = s->vbe_regs; + uint32_t virt_width; + + if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { + return -1; + } + + memset(mode, 0, sizeof(*mode)); + switch (vbe[VBE_DISPI_INDEX_BPP]) { + case 16: + /* best effort: support native endianess only */ + mode->format = PIXMAN_r5g6b5; + mode->bytepp = 2; + break; + case 32: + mode->format = s->big_endian_fb + ? PIXMAN_BE_x8r8g8b8 + : PIXMAN_LE_x8r8g8b8; + mode->bytepp = 4; + break; + default: + return -1; + } + + mode->width = vbe[VBE_DISPI_INDEX_XRES]; + mode->height = vbe[VBE_DISPI_INDEX_YRES]; + virt_width = vbe[VBE_DISPI_INDEX_VIRT_WIDTH]; + if (virt_width < mode->width) { + virt_width = mode->width; + } + mode->stride = virt_width * mode->bytepp; + mode->size = (uint64_t)mode->stride * mode->height; + mode->offset = ((uint64_t)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp + + (uint64_t)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride); + + if (mode->width < 64 || mode->height < 64) { + return -1; + } + if (mode->offset + mode->size > s->vgamem) { + return -1; + } + return 0; +} + +static void bochs_display_update(void *opaque) +{ + BochsDisplayState *s = opaque; + DirtyBitmapSnapshot *snap = NULL; + bool full_update = false; + BochsDisplayMode mode; + DisplaySurface *ds; + uint8_t *ptr; + bool dirty; + int y, ys, ret; + + ret = bochs_display_get_mode(s, &mode); + if (ret < 0) { + /* no (valid) video mode */ + return; + } + + if (memcmp(&s->mode, &mode, sizeof(mode)) != 0) { + /* video mode switch */ + s->mode = mode; + ptr = memory_region_get_ram_ptr(&s->vram); + ds = qemu_create_displaysurface_from(mode.width, + mode.height, + mode.format, + mode.stride, + ptr + mode.offset); + dpy_gfx_replace_surface(s->con, ds); + full_update = true; + } + + if (full_update) { + dpy_gfx_update_full(s->con); + } else { + snap = memory_region_snapshot_and_clear_dirty(&s->vram, + mode.offset, mode.size, + DIRTY_MEMORY_VGA); + ys = -1; + for (y = 0; y < mode.height; y++) { + dirty = memory_region_snapshot_get_dirty(&s->vram, snap, + mode.offset + mode.stride * y, + mode.stride); + if (dirty && ys < 0) { + ys = y; + } + if (!dirty && ys >= 0) { + dpy_gfx_update(s->con, 0, ys, + mode.width, y - ys); + ys = -1; + } + } + if (ys >= 0) { + dpy_gfx_update(s->con, 0, ys, + mode.width, y - ys); + } + } +} + +static const GraphicHwOps bochs_display_gfx_ops = { + .gfx_update = bochs_display_update, +}; + +static void bochs_display_realize(PCIDevice *dev, Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(dev); + Object *obj = OBJECT(dev); + int ret; + + s->con = graphic_console_init(DEVICE(dev), 0, &bochs_display_gfx_ops, s); + + if (s->vgamem < 4 * MiB) { + error_setg(errp, "bochs-display: video memory too small"); + } + if (s->vgamem > 256 * MiB) { + error_setg(errp, "bochs-display: video memory too big"); + } + s->vgamem = pow2ceil(s->vgamem); + + memory_region_init_ram(&s->vram, obj, "bochs-display-vram", s->vgamem, + &error_fatal); + memory_region_init_io(&s->vbe, obj, &bochs_display_vbe_ops, s, + "bochs dispi interface", PCI_VGA_BOCHS_SIZE); + memory_region_init_io(&s->qext, obj, &bochs_display_qext_ops, s, + "qemu extended regs", PCI_VGA_QEXT_SIZE); + + memory_region_init(&s->mmio, obj, "bochs-display-mmio", + PCI_VGA_MMIO_SIZE); + memory_region_add_subregion(&s->mmio, PCI_VGA_BOCHS_OFFSET, &s->vbe); + memory_region_add_subregion(&s->mmio, PCI_VGA_QEXT_OFFSET, &s->qext); + + pci_set_byte(&s->pci.config[PCI_REVISION_ID], 2); + pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); + pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + + if (pci_bus_is_express(pci_get_bus(dev))) { + dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + ret = pcie_endpoint_cap_init(dev, 0x80); + assert(ret > 0); + } + + memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); +} + +static bool bochs_display_get_big_endian_fb(Object *obj, Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(obj); + + return s->big_endian_fb; +} + +static void bochs_display_set_big_endian_fb(Object *obj, bool value, + Error **errp) +{ + BochsDisplayState *s = BOCHS_DISPLAY(obj); + + s->big_endian_fb = value; +} + +static void bochs_display_init(Object *obj) +{ + /* Expose framebuffer byteorder via QOM */ + object_property_add_bool(obj, "big-endian-framebuffer", + bochs_display_get_big_endian_fb, + bochs_display_set_big_endian_fb, + NULL); +} + +static void bochs_display_exit(PCIDevice *dev) +{ + BochsDisplayState *s = BOCHS_DISPLAY(dev); + + graphic_console_close(s->con); +} + +static Property bochs_display_properties[] = { + DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bochs_display_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->class_id = PCI_CLASS_DISPLAY_OTHER; + k->vendor_id = PCI_VENDOR_ID_QEMU; + k->device_id = PCI_DEVICE_ID_QEMU_VGA; + + k->realize = bochs_display_realize; + k->romfile = "vgabios-bochs-display.bin"; + k->exit = bochs_display_exit; + dc->vmsd = &vmstate_bochs_display; + dc->props = bochs_display_properties; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); +} + +static const TypeInfo bochs_display_type_info = { + .name = TYPE_BOCHS_DISPLAY, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(BochsDisplayState), + .instance_init = bochs_display_init, + .class_init = bochs_display_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void bochs_display_register_types(void) +{ + type_register_static(&bochs_display_type_info); +} + +type_init(bochs_display_register_types) diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 138ae961b9..7583b18c29 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -27,6 +27,7 @@ * available at http://home.worldonline.dk/~finth/ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "trace.h" #include "hw/hw.h" @@ -2218,7 +2219,7 @@ static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s) uint32_t content; int y, y_min, y_max; - src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024; + src = s->vga.vram_ptr + s->real_vram_size - 16 * KiB; if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { src += (s->vga.sr[0x13] & 0x3c) * 256; y_min = 64; @@ -2347,7 +2348,7 @@ static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) return; } - src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024; + src = s->vga.vram_ptr + s->real_vram_size - 16 * KiB; if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { src += (s->vga.sr[0x13] & 0x3c) * 256; src += (scr_y - s->vga.hw_cursor_y) * 16; @@ -2995,8 +2996,7 @@ static void cirrus_init_common(CirrusVGAState *s, Object *owner, /* I/O handler for LFB */ memory_region_init_io(&s->cirrus_linear_io, owner, &cirrus_linear_io_ops, s, - "cirrus-linear-io", s->vga.vram_size_mb - * 1024 * 1024); + "cirrus-linear-io", s->vga.vram_size_mb * MiB); memory_region_set_flush_coalesced(&s->cirrus_linear_io); /* I/O handler for LFB */ @@ -3013,7 +3013,7 @@ static void cirrus_init_common(CirrusVGAState *s, Object *owner, memory_region_set_flush_coalesced(&s->cirrus_mmio_io); s->real_vram_size = - (s->device_id == CIRRUS_ID_CLGD5446) ? 4096 * 1024 : 2048 * 1024; + (s->device_id == CIRRUS_ID_CLGD5446) ? 4 * MiB : 2 * MiB; /* XXX: s->vga.vram_size must be a power of two */ s->cirrus_addr_mask = s->real_vram_size - 1; @@ -3048,7 +3048,8 @@ static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) s->vram_size_mb); return; } - vga_common_init(s, OBJECT(dev), true); + s->global_vmstate = true; + vga_common_init(s, OBJECT(dev)); cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0, isa_address_space(isadev), isa_address_space_io(isadev)); @@ -3062,7 +3063,7 @@ static Property isa_cirrus_vga_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState, - cirrus_vga.enable_blitter, true), + cirrus_vga.enable_blitter, true), DEFINE_PROP_END_OF_LIST(), }; @@ -3105,7 +3106,7 @@ static void pci_cirrus_vga_realize(PCIDevice *dev, Error **errp) return; } /* setup VGA */ - vga_common_init(&s->vga, OBJECT(dev), true); + vga_common_init(&s->vga, OBJECT(dev)); cirrus_init_common(s, OBJECT(dev), device_id, 1, pci_address_space(dev), pci_address_space_io(dev)); s->vga.con = graphic_console_init(DEVICE(dev), 0, s->vga.hw_ops, &s->vga); @@ -3134,6 +3135,8 @@ static Property pci_vga_cirrus_properties[] = { cirrus_vga.vga.vram_size_mb, 4), DEFINE_PROP_BOOL("blitter", struct PCICirrusVGAState, cirrus_vga.enable_blitter, true), + DEFINE_PROP_BOOL("global-vmstate", struct PCICirrusVGAState, + cirrus_vga.vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 3d75394e77..fbc2b2422d 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "qemu/error-report.h" #include "ui/console.h" @@ -510,8 +511,7 @@ static void g364fb_sysbus_reset(DeviceState *d) } static Property g364fb_sysbus_properties[] = { - DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size, - 8 * 1024 * 1024), + DEFINE_PROP_UINT32("vram_size", G364SysBusState, g364.vram_size, 8 * MiB), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index e7ac4f8789..c62b9a5e75 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -169,7 +169,8 @@ void qxl_render_update(PCIQXLDevice *qxl) qemu_mutex_lock(&qxl->ssd.lock); - if (!runstate_is_running() || !qxl->guest_primary.commands) { + if (!runstate_is_running() || !qxl->guest_primary.commands || + qxl->mode == QXL_MODE_UNDEFINED) { qxl_render_update_area_unlocked(qxl); qemu_mutex_unlock(&qxl->ssd.lock); return; diff --git a/hw/display/qxl.c b/hw/display/qxl.c index a71714ccb4..830c392c53 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include #include "qapi/error.h" @@ -2012,11 +2013,11 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) if (qxl->vgamem_size_mb > 256) { qxl->vgamem_size_mb = 256; } - qxl->vgamem_size = qxl->vgamem_size_mb * 1024 * 1024; + qxl->vgamem_size = qxl->vgamem_size_mb * MiB; /* vga ram (bar 0, total) */ if (qxl->ram_size_mb != -1) { - qxl->vga.vram_size = qxl->ram_size_mb * 1024 * 1024; + qxl->vga.vram_size = qxl->ram_size_mb * MiB; } if (qxl->vga.vram_size < qxl->vgamem_size * 2) { qxl->vga.vram_size = qxl->vgamem_size * 2; @@ -2024,7 +2025,7 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) /* vram32 (surfaces, 32bit, bar 1) */ if (qxl->vram32_size_mb != -1) { - qxl->vram32_size = qxl->vram32_size_mb * 1024 * 1024; + qxl->vram32_size = qxl->vram32_size_mb * MiB; } if (qxl->vram32_size < 4096) { qxl->vram32_size = 4096; @@ -2032,7 +2033,7 @@ static void qxl_init_ramsize(PCIQXLDevice *qxl) /* vram (surfaces, 64bit, bar 4+5) */ if (qxl->vram_size_mb != -1) { - qxl->vram_size = (uint64_t)qxl->vram_size_mb * 1024 * 1024; + qxl->vram_size = (uint64_t)qxl->vram_size_mb * MiB; } if (qxl->vram_size < qxl->vram32_size) { qxl->vram_size = qxl->vram32_size; @@ -2134,13 +2135,12 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) } /* print pci bar details */ - dprint(qxl, 1, "ram/%s: %d MB [region 0]\n", - qxl->id == 0 ? "pri" : "sec", - qxl->vga.vram_size / (1024*1024)); - dprint(qxl, 1, "vram/32: %" PRIx64 "d MB [region 1]\n", - qxl->vram32_size / (1024*1024)); - dprint(qxl, 1, "vram/64: %" PRIx64 "d MB %s\n", - qxl->vram_size / (1024*1024), + dprint(qxl, 1, "ram/%s: %" PRId64 " MB [region 0]\n", + qxl->id == 0 ? "pri" : "sec", qxl->vga.vram_size / MiB); + dprint(qxl, 1, "vram/32: %" PRIx64 " MB [region 1]\n", + qxl->vram32_size / MiB); + dprint(qxl, 1, "vram/64: %" PRIx64 " MB %s\n", + qxl->vram_size / MiB, qxl->vram32_size < qxl->vram_size ? "[region 4]" : "[unmapped]"); qxl->ssd.qxl.base.sif = &qxl_interface.base; @@ -2167,8 +2167,8 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp) qxl->id = 0; qxl_init_ramsize(qxl); vga->vbe_size = qxl->vgamem_size; - vga->vram_size_mb = qxl->vga.vram_size >> 20; - vga_common_init(vga, OBJECT(dev), true); + vga->vram_size_mb = qxl->vga.vram_size / MiB; + vga_common_init(vga, OBJECT(dev)); vga_init(vga, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), false); portio_list_init(&qxl->vga_port_list, OBJECT(dev), qxl_vga_portio_list, @@ -2391,10 +2391,8 @@ static VMStateDescription qxl_vmstate = { }; static Property qxl_properties[] = { - DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, - 64 * 1024 * 1024), - DEFINE_PROP_UINT64("vram_size", PCIQXLDevice, vram32_size, - 64 * 1024 * 1024), + DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * MiB), + DEFINE_PROP_UINT64("vram_size", PCIQXLDevice, vram32_size, 64 * MiB), DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, QXL_DEFAULT_REVISION), DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), @@ -2410,6 +2408,7 @@ static Property qxl_properties[] = { #endif DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0), DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0), + DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c new file mode 100644 index 0000000000..c0d241ba01 --- /dev/null +++ b/hw/display/ramfb-standalone.c @@ -0,0 +1,62 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/loader.h" +#include "hw/isa/isa.h" +#include "hw/display/ramfb.h" +#include "ui/console.h" +#include "sysemu/sysemu.h" + +#define RAMFB(obj) OBJECT_CHECK(RAMFBStandaloneState, (obj), TYPE_RAMFB_DEVICE) + +typedef struct RAMFBStandaloneState { + SysBusDevice parent_obj; + QemuConsole *con; + RAMFBState *state; +} RAMFBStandaloneState; + +static void display_update_wrapper(void *dev) +{ + RAMFBStandaloneState *ramfb = RAMFB(dev); + + if (0 /* native driver active */) { + /* non-standalone device would run native display update here */; + } else { + ramfb_display_update(ramfb->con, ramfb->state); + } +} + +static const GraphicHwOps wrapper_ops = { + .gfx_update = display_update_wrapper, +}; + +static void ramfb_realizefn(DeviceState *dev, Error **errp) +{ + RAMFBStandaloneState *ramfb = RAMFB(dev); + + ramfb->con = graphic_console_init(dev, 0, &wrapper_ops, dev); + ramfb->state = ramfb_setup(errp); +} + +static void ramfb_class_initfn(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + dc->realize = ramfb_realizefn; + dc->desc = "ram framebuffer standalone device"; + dc->user_creatable = true; +} + +static const TypeInfo ramfb_info = { + .name = TYPE_RAMFB_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(RAMFBStandaloneState), + .class_init = ramfb_class_initfn, +}; + +static void ramfb_register_types(void) +{ + type_register_static(&ramfb_info); +} + +type_init(ramfb_register_types) diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c new file mode 100644 index 0000000000..25c8ad7c25 --- /dev/null +++ b/hw/display/ramfb.c @@ -0,0 +1,96 @@ +/* + * early boot framebuffer in guest ram + * configured using fw_cfg + * + * Copyright Red Hat, Inc. 2017 + * + * Author: + * Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/loader.h" +#include "hw/display/ramfb.h" +#include "ui/console.h" +#include "sysemu/sysemu.h" + +struct QEMU_PACKED RAMFBCfg { + uint64_t addr; + uint32_t fourcc; + uint32_t flags; + uint32_t width; + uint32_t height; + uint32_t stride; +}; + +struct RAMFBState { + DisplaySurface *ds; + uint32_t width, height; + struct RAMFBCfg cfg; +}; + +static void ramfb_fw_cfg_write(void *dev, off_t offset, size_t len) +{ + RAMFBState *s = dev; + void *framebuffer; + uint32_t fourcc, format; + hwaddr stride, addr, length; + + s->width = be32_to_cpu(s->cfg.width); + s->height = be32_to_cpu(s->cfg.height); + stride = be32_to_cpu(s->cfg.stride); + fourcc = be32_to_cpu(s->cfg.fourcc); + addr = be64_to_cpu(s->cfg.addr); + length = stride * s->height; + format = qemu_drm_format_to_pixman(fourcc); + + fprintf(stderr, "%s: %dx%d @ 0x%" PRIx64 "\n", __func__, + s->width, s->height, addr); + framebuffer = address_space_map(&address_space_memory, + addr, &length, false, + MEMTXATTRS_UNSPECIFIED); + if (!framebuffer || length < stride * s->height) { + s->width = 0; + s->height = 0; + return; + } + s->ds = qemu_create_displaysurface_from(s->width, s->height, + format, stride, framebuffer); +} + +void ramfb_display_update(QemuConsole *con, RAMFBState *s) +{ + if (!s->width || !s->height) { + return; + } + + if (s->ds) { + dpy_gfx_replace_surface(con, s->ds); + s->ds = NULL; + } + + /* simple full screen update */ + dpy_gfx_update_full(con); +} + +RAMFBState *ramfb_setup(Error **errp) +{ + FWCfgState *fw_cfg = fw_cfg_find(); + RAMFBState *s; + + if (!fw_cfg || !fw_cfg->dma_enabled) { + error_setg(errp, "ramfb device requires fw_cfg with DMA"); + return NULL; + } + + s = g_new0(RAMFBState, 1); + + rom_add_vga("vgabios-ramfb.bin"); + fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", + NULL, ramfb_fw_cfg_write, s, + &s->cfg, sizeof(s->cfg), false); + return s; +} diff --git a/hw/display/sm501.c b/hw/display/sm501.c index f4bb33c279..874260a143 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -24,8 +24,9 @@ */ #include "qemu/osdep.h" -#include "qemu/cutils.h" +#include "qemu/units.h" #include "qapi/error.h" +#include "qemu/log.h" #include "qemu-common.h" #include "cpu.h" #include "hw/hw.h" @@ -34,9 +35,10 @@ #include "hw/devices.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/i2c-ddc.h" #include "qemu/range.h" #include "ui/pixel_ops.h" -#include "exec/address-spaces.h" /* * Status: 2010/05/07 @@ -217,6 +219,14 @@ #define SM501_I2C_SLAVE_ADDRESS (0x03) #define SM501_I2C_DATA (0x04) +#define SM501_I2C_CONTROL_START (1 << 2) +#define SM501_I2C_CONTROL_ENABLE (1 << 0) + +#define SM501_I2C_STATUS_COMPLETE (1 << 3) +#define SM501_I2C_STATUS_ERROR (1 << 2) + +#define SM501_I2C_RESET_ERROR (1 << 2) + /* SSP base */ #define SM501_SSP (0x020000) @@ -453,12 +463,12 @@ /* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ static const uint32_t sm501_mem_local_size[] = { - [0] = 4 * M_BYTE, - [1] = 8 * M_BYTE, - [2] = 16 * M_BYTE, - [3] = 32 * M_BYTE, - [4] = 64 * M_BYTE, - [5] = 2 * M_BYTE, + [0] = 4 * MiB, + [1] = 8 * MiB, + [2] = 16 * MiB, + [3] = 32 * MiB, + [4] = 64 * MiB, + [5] = 2 * MiB, }; #define get_local_mem_size(s) sm501_mem_local_size[(s)->local_mem_size_index] @@ -472,10 +482,13 @@ typedef struct SM501State { MemoryRegion local_mem_region; MemoryRegion mmio_region; MemoryRegion system_config_region; + MemoryRegion i2c_region; MemoryRegion disp_ctrl_region; MemoryRegion twoD_engine_region; uint32_t last_width; uint32_t last_height; + bool do_full_update; /* perform a full update next time */ + I2CBus *i2c_bus; /* mmio registers */ uint32_t system_control; @@ -488,6 +501,11 @@ typedef struct SM501State { uint32_t misc_timing; uint32_t power_mode_control; + uint8_t i2c_byte_count; + uint8_t i2c_status; + uint8_t i2c_addr; + uint8_t i2c_data[16]; + uint32_t uart0_ier; uint32_t uart0_lcr; uint32_t uart0_mcr; @@ -568,6 +586,11 @@ static uint32_t get_local_mem_size_index(uint32_t size) return index; } +static ram_addr_t get_fb_addr(SM501State *s, int crt) +{ + return (crt ? s->dc_crt_fb_addr : s->dc_panel_fb_addr) & 0x3FFFFF0; +} + static inline int get_width(SM501State *s, int crt) { int width = crt ? s->dc_crt_h_total : s->dc_panel_h_total; @@ -653,9 +676,9 @@ static inline void get_hwc_palette(SM501State *state, int crt, uint8_t *palette) } else { rgb565 = color_reg & 0xFFFF; } - palette[i * 3 + 0] = (rgb565 << 3) & 0xf8; /* red */ - palette[i * 3 + 1] = (rgb565 >> 3) & 0xfc; /* green */ - palette[i * 3 + 2] = (rgb565 >> 8) & 0xf8; /* blue */ + palette[i * 3 + 0] = ((rgb565 >> 11) * 527 + 23) >> 6; /* r */ + palette[i * 3 + 1] = (((rgb565 >> 5) & 0x3f) * 259 + 33) >> 6; /* g */ + palette[i * 3 + 2] = ((rgb565 & 0x1f) * 527 + 23) >> 6; /* b */ } } @@ -670,7 +693,8 @@ static inline void hwc_invalidate(SM501State *s, int crt) start *= w * bpp; end *= w * bpp; - memory_region_set_dirty(&s->local_mem_region, start, end - start); + memory_region_set_dirty(&s->local_mem_region, + get_fb_addr(s, crt) + start, end - start); } static void sm501_2d_operation(SM501State *s) @@ -687,18 +711,47 @@ static void sm501_2d_operation(SM501State *s) uint32_t color = s->twoD_foreground; int format_flags = (s->twoD_stretch >> 20) & 0x3; int addressing = (s->twoD_stretch >> 16) & 0xF; + int rop_mode = (s->twoD_control >> 15) & 0x1; /* 1 for rop2, else rop3 */ + /* 1 if rop2 source is the pattern, otherwise the source is the bitmap */ + int rop2_source_is_pattern = (s->twoD_control >> 14) & 0x1; + int rop = s->twoD_control & 0xFF; + uint32_t src_base = s->twoD_source_base & 0x03FFFFFF; + uint32_t dst_base = s->twoD_destination_base & 0x03FFFFFF; /* get frame buffer info */ - uint8_t *src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); - uint8_t *dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); - int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1; - int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1; + uint8_t *src = s->local_mem + src_base; + uint8_t *dst = s->local_mem + dst_base; + int src_width = s->twoD_pitch & 0x1FFF; + int dst_width = (s->twoD_pitch >> 16) & 0x1FFF; + int crt = (s->dc_crt_control & SM501_DC_CRT_CONTROL_SEL) ? 1 : 0; + int fb_len = get_width(s, crt) * get_height(s, crt) * get_bpp(s, crt); if (addressing != 0x0) { printf("%s: only XY addressing is supported.\n", __func__); abort(); } + if (rop_mode == 0) { + if (rop != 0xcc) { + /* Anything other than plain copies are not supported */ + qemu_log_mask(LOG_UNIMP, "sm501: rop3 mode with rop %x is not " + "supported.\n", rop); + } + } else { + if (rop2_source_is_pattern && rop != 0x5) { + /* For pattern source, we support only inverse dest */ + qemu_log_mask(LOG_UNIMP, "sm501: rop2 source being the pattern and " + "rop %x is not supported.\n", rop); + } else { + if (rop != 0x5 && rop != 0xc) { + /* Anything other than plain copies or inverse dest is not + * supported */ + qemu_log_mask(LOG_UNIMP, "sm501: rop mode %x is not " + "supported.\n", rop); + } + } + } + if ((s->twoD_source_base & 0x08000000) || (s->twoD_destination_base & 0x08000000)) { printf("%s: only local memory is supported.\n", __func__); @@ -711,6 +764,8 @@ static void sm501_2d_operation(SM501State *s) int y, x, index_d, index_s; \ for (y = 0; y < operation_height; y++) { \ for (x = 0; x < operation_width; x++) { \ + _pixel_type val; \ + \ if (rtl) { \ index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ @@ -718,7 +773,13 @@ static void sm501_2d_operation(SM501State *s) index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ } \ - *(_pixel_type *)&dst[index_d] = *(_pixel_type *)&src[index_s];\ + if (rop_mode == 1 && rop == 5) { \ + /* Invert dest */ \ + val = ~*(_pixel_type *)&dst[index_d]; \ + } else { \ + val = *(_pixel_type *)&src[index_s]; \ + } \ + *(_pixel_type *)&dst[index_d] = val; \ } \ } \ } @@ -764,6 +825,15 @@ static void sm501_2d_operation(SM501State *s) abort(); break; } + + if (dst_base >= get_fb_addr(s, crt) && + dst_base <= get_fb_addr(s, crt) + fb_len) { + int dst_len = MIN(fb_len, ((dst_y + operation_height - 1) * dst_width + + dst_x + operation_width) * (1 << format_flags)); + if (dst_len) { + memory_region_set_dirty(&s->local_mem_region, dst_base, dst_len); + } + } } static uint64_t sm501_system_config_read(void *opaque, hwaddr addr, @@ -837,27 +907,30 @@ static void sm501_system_config_write(void *opaque, hwaddr addr, switch (addr) { case SM501_SYSTEM_CONTROL: - s->system_control = value & 0xE300B8F7; + s->system_control &= 0x10DB0000; + s->system_control |= value & 0xEF00B8F7; break; case SM501_MISC_CONTROL: - s->misc_control = value & 0xFF7FFF20; + s->misc_control &= 0xEF; + s->misc_control |= value & 0xFF7FFF10; break; case SM501_GPIO31_0_CONTROL: s->gpio_31_0_control = value; break; case SM501_GPIO63_32_CONTROL: - s->gpio_63_32_control = value; + s->gpio_63_32_control = value & 0xFF80FFFF; break; case SM501_DRAM_CONTROL: s->local_mem_size_index = (value >> 13) & 0x7; /* TODO : check validity of size change */ - s->dram_control |= value & 0x7FFFFFC3; + s->dram_control &= 0x80000000; + s->dram_control |= value & 0x7FFFFFC3; break; case SM501_ARBTRTN_CONTROL: - s->arbitration_control = value & 0x37777777; + s->arbitration_control = value & 0x37777777; break; case SM501_IRQ_MASK: - s->irq_mask = value; + s->irq_mask = value & 0xFFDF3F5F; break; case SM501_MISC_TIMING: s->misc_timing = value & 0xF31F1FFF; @@ -895,6 +968,109 @@ static const MemoryRegionOps sm501_system_config_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static uint64_t sm501_i2c_read(void *opaque, hwaddr addr, unsigned size) +{ + SM501State *s = (SM501State *)opaque; + uint8_t ret = 0; + + switch (addr) { + case SM501_I2C_BYTE_COUNT: + ret = s->i2c_byte_count; + break; + case SM501_I2C_STATUS: + ret = s->i2c_status; + break; + case SM501_I2C_SLAVE_ADDRESS: + ret = s->i2c_addr; + break; + case SM501_I2C_DATA ... SM501_I2C_DATA + 15: + ret = s->i2c_data[addr - SM501_I2C_DATA]; + break; + default: + qemu_log_mask(LOG_UNIMP, "sm501 i2c : not implemented register read." + " addr=0x%" HWADDR_PRIx "\n", addr); + } + + SM501_DPRINTF("sm501 i2c regs : read addr=%" HWADDR_PRIx " val=%x\n", + addr, ret); + return ret; +} + +static void sm501_i2c_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + SM501State *s = (SM501State *)opaque; + SM501_DPRINTF("sm501 i2c regs : write addr=%" HWADDR_PRIx + " val=%" PRIx64 "\n", addr, value); + + switch (addr) { + case SM501_I2C_BYTE_COUNT: + s->i2c_byte_count = value & 0xf; + break; + case SM501_I2C_CONTROL: + if (value & SM501_I2C_CONTROL_ENABLE) { + if (value & SM501_I2C_CONTROL_START) { + int res = i2c_start_transfer(s->i2c_bus, + s->i2c_addr >> 1, + s->i2c_addr & 1); + s->i2c_status |= (res ? SM501_I2C_STATUS_ERROR : 0); + if (!res) { + int i; + SM501_DPRINTF("sm501 i2c : transferring %d bytes to 0x%x\n", + s->i2c_byte_count + 1, s->i2c_addr >> 1); + for (i = 0; i <= s->i2c_byte_count; i++) { + res = i2c_send_recv(s->i2c_bus, &s->i2c_data[i], + !(s->i2c_addr & 1)); + if (res) { + SM501_DPRINTF("sm501 i2c : transfer failed" + " i=%d, res=%d\n", i, res); + s->i2c_status |= SM501_I2C_STATUS_ERROR; + return; + } + } + if (i) { + SM501_DPRINTF("sm501 i2c : transferred %d bytes\n", i); + s->i2c_status = SM501_I2C_STATUS_COMPLETE; + } + } + } else { + SM501_DPRINTF("sm501 i2c : end transfer\n"); + i2c_end_transfer(s->i2c_bus); + s->i2c_status &= ~SM501_I2C_STATUS_ERROR; + } + } + break; + case SM501_I2C_RESET: + if ((value & SM501_I2C_RESET_ERROR) == 0) { + s->i2c_status &= ~SM501_I2C_STATUS_ERROR; + } + break; + case SM501_I2C_SLAVE_ADDRESS: + s->i2c_addr = value & 0xff; + break; + case SM501_I2C_DATA ... SM501_I2C_DATA + 15: + s->i2c_data[addr - SM501_I2C_DATA] = value & 0xff; + break; + default: + qemu_log_mask(LOG_UNIMP, "sm501 i2c : not implemented register write. " + "addr=0x%" HWADDR_PRIx " val=%" PRIx64 "\n", addr, value); + } +} + +static const MemoryRegionOps sm501_i2c_ops = { + .read = sm501_i2c_read, + .write = sm501_i2c_write, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static uint32_t sm501_palette_read(void *opaque, hwaddr addr) { SM501State *s = (SM501State *)opaque; @@ -919,6 +1095,7 @@ static void sm501_palette_write(void *opaque, hwaddr addr, assert(range_covers_byte(0, 0x400 * 3, addr)); *(uint32_t *)&s->dc_palette[addr] = value; + s->do_full_update = true; } static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr, @@ -1055,6 +1232,10 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, break; case SM501_DC_PANEL_FB_ADDR: s->dc_panel_fb_addr = value & 0x8FFFFFF0; + if (value & 0x8000000) { + qemu_log_mask(LOG_UNIMP, "Panel external memory not supported\n"); + } + s->do_full_update = true; break; case SM501_DC_PANEL_FB_OFFSET: s->dc_panel_fb_offset = value & 0x3FF03FF0; @@ -1115,6 +1296,10 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr, break; case SM501_DC_CRT_FB_ADDR: s->dc_crt_fb_addr = value & 0x8FFFFFF0; + if (value & 0x8000000) { + qemu_log_mask(LOG_UNIMP, "CRT external memory not supported\n"); + } + s->do_full_update = true; break; case SM501_DC_CRT_FB_OFFSET: s->dc_crt_fb_offset = value & 0x3FF03FF0; @@ -1457,7 +1642,7 @@ static void sm501_update_display(void *opaque) draw_hwc_line_func *draw_hwc_line = NULL; int full_update = 0; int y_start = -1; - ram_addr_t offset = 0; + ram_addr_t offset; uint32_t *palette; uint8_t hwc_palette[3 * 3]; uint8_t *hwc_src = NULL; @@ -1507,10 +1692,17 @@ static void sm501_update_display(void *opaque) full_update = 1; } + /* someone else requested a full update */ + if (s->do_full_update) { + s->do_full_update = false; + full_update = 1; + } + /* draw each line according to conditions */ + offset = get_fb_addr(s, crt); snap = memory_region_snapshot_and_clear_dirty(&s->local_mem_region, offset, width * height * src_bpp, DIRTY_MEMORY_VGA); - for (y = 0, offset = 0; y < height; y++, offset += width * src_bpp) { + for (y = 0; y < height; y++, offset += width * src_bpp) { int update, update_hwc; /* check if hardware cursor is enabled and we're within its range */ @@ -1575,6 +1767,10 @@ static void sm501_reset(SM501State *s) s->irq_mask = 0; s->misc_timing = 0; s->power_mode_control = 0; + s->i2c_byte_count = 0; + s->i2c_status = 0; + s->i2c_addr = 0; + memset(s->i2c_data, 0, 16); s->dc_panel_control = 0x00010000; /* FIFO level 3 */ s->dc_video_control = 0; s->dc_crt_control = 0x00010000; @@ -1613,6 +1809,12 @@ static void sm501_init(SM501State *s, DeviceState *dev, memory_region_set_log(&s->local_mem_region, true, DIRTY_MEMORY_VGA); s->local_mem = memory_region_get_ram_ptr(&s->local_mem_region); + /* i2c */ + s->i2c_bus = i2c_init_bus(dev, "sm501.i2c"); + /* ddc */ + I2CDDCState *ddc = I2CDDC(qdev_create(BUS(s->i2c_bus), TYPE_I2CDDC)); + i2c_set_slave_address(I2C_SLAVE(ddc), 0x50); + /* mmio */ memory_region_init(&s->mmio_region, OBJECT(dev), "sm501.mmio", MMIO_SIZE); memory_region_init_io(&s->system_config_region, OBJECT(dev), @@ -1620,6 +1822,9 @@ static void sm501_init(SM501State *s, DeviceState *dev, "sm501-system-config", 0x6c); memory_region_add_subregion(&s->mmio_region, SM501_SYS_CONFIG, &s->system_config_region); + memory_region_init_io(&s->i2c_region, OBJECT(dev), &sm501_i2c_ops, s, + "sm501-i2c", 0x14); + memory_region_add_subregion(&s->mmio_region, SM501_I2C, &s->i2c_region); memory_region_init_io(&s->disp_ctrl_region, OBJECT(dev), &sm501_disp_ctrl_ops, s, "sm501-disp-ctrl", 0x1000); @@ -1703,6 +1908,11 @@ static const VMStateDescription vmstate_sm501_state = { VMSTATE_UINT32(twoD_destination_base, SM501State), VMSTATE_UINT32(twoD_alpha, SM501State), VMSTATE_UINT32(twoD_wrap, SM501State), + /* Added in version 2 */ + VMSTATE_UINT8(i2c_byte_count, SM501State), + VMSTATE_UINT8(i2c_status, SM501State), + VMSTATE_UINT8(i2c_addr, SM501State), + VMSTATE_UINT8_ARRAY(i2c_data, SM501State, 16), VMSTATE_END_OF_LIST() } }; @@ -1768,8 +1978,8 @@ static void sm501_reset_sysbus(DeviceState *dev) static const VMStateDescription vmstate_sm501_sysbus = { .name = TYPE_SYSBUS_SM501, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_STRUCT(state, SM501SysBusState, 1, vmstate_sm501_state, SM501State), @@ -1827,7 +2037,7 @@ static void sm501_realize_pci(PCIDevice *dev, Error **errp) } static Property sm501_pci_properties[] = { - DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * M_BYTE), + DEFINE_PROP_UINT32("vram-size", SM501PCIState, vram_size, 64 * MiB), DEFINE_PROP_END_OF_LIST(), }; @@ -1841,8 +2051,8 @@ static void sm501_reset_pci(DeviceState *dev) static const VMStateDescription vmstate_sm501_pci = { .name = TYPE_PCI_SM501, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, SM501PCIState), VMSTATE_STRUCT(state, SM501PCIState, 1, diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index 68a80b9d64..eb90ba26be 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -297,13 +297,12 @@ static const GraphicHwOps ssd0303_ops = { .gfx_update = ssd0303_update_display, }; -static int ssd0303_init(I2CSlave *i2c) +static void ssd0303_realize(DeviceState *dev, Error **errp) { - ssd0303_state *s = SSD0303(i2c); + ssd0303_state *s = SSD0303(dev); - s->con = graphic_console_init(DEVICE(i2c), 0, &ssd0303_ops, s); + s->con = graphic_console_init(dev, 0, &ssd0303_ops, s); qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY); - return 0; } static void ssd0303_class_init(ObjectClass *klass, void *data) @@ -311,7 +310,7 @@ static void ssd0303_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = ssd0303_init; + dc->realize = ssd0303_realize; k->event = ssd0303_event; k->recv = ssd0303_recv; k->send = ssd0303_send; diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c index 464465b7c2..8392e59493 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -18,7 +18,6 @@ #include "hw/block/flash.h" #include "ui/console.h" #include "ui/pixel_ops.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #define IRQ_TC6393_NAND 0 diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c index e887b45651..232216cad0 100644 --- a/hw/display/vga-isa-mm.c +++ b/hw/display/vga-isa-mm.c @@ -22,12 +22,13 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/display/vga.h" #include "vga_int.h" #include "ui/pixel_ops.h" -#define VGA_RAM_SIZE (8192 * 1024) +#define VGA_RAM_SIZE (8 * MiB) typedef struct ISAVGAMMState { VGACommonState vga; @@ -130,8 +131,9 @@ int isa_vga_mm_init(hwaddr vram_base, s = g_malloc0(sizeof(*s)); - s->vga.vram_size_mb = VGA_RAM_SIZE >> 20; - vga_common_init(&s->vga, NULL, true); + 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); diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 469834add5..fa44242e0d 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -58,7 +58,8 @@ static void vga_isa_realizefn(DeviceState *dev, Error **errp) MemoryRegion *vga_io_memory; const MemoryRegionPortio *vga_ports, *vbe_ports; - vga_common_init(s, OBJECT(dev), true); + s->global_vmstate = true; + vga_common_init(s, OBJECT(dev)); s->legacy_address_space = isa_address_space(isadev); vga_io_memory = vga_init_io(s, OBJECT(dev), &vga_ports, &vbe_ports); isa_register_portio_list(isadev, &d->portio_vga, diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index f312930664..e9e62eac70 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -31,19 +31,6 @@ #include "qemu/timer.h" #include "hw/loader.h" -#define PCI_VGA_IOPORT_OFFSET 0x400 -#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) -#define PCI_VGA_BOCHS_OFFSET 0x500 -#define PCI_VGA_BOCHS_SIZE (0x0b * 2) -#define PCI_VGA_QEXT_OFFSET 0x600 -#define PCI_VGA_QEXT_SIZE (2 * 4) -#define PCI_VGA_MMIO_SIZE 0x1000 - -#define PCI_VGA_QEXT_REG_SIZE (0 * 4) -#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) -#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e -#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe - enum vga_pci_flags { PCI_VGA_FLAG_ENABLE_MMIO = 1, PCI_VGA_FLAG_ENABLE_QEXT = 2, @@ -205,22 +192,23 @@ static const MemoryRegionOps pci_vga_qext_ops = { }; void pci_std_vga_mmio_region_init(VGACommonState *s, + Object *owner, MemoryRegion *parent, MemoryRegion *subs, bool qext) { - memory_region_init_io(&subs[0], NULL, &pci_vga_ioport_ops, s, + memory_region_init_io(&subs[0], owner, &pci_vga_ioport_ops, s, "vga ioports remapped", PCI_VGA_IOPORT_SIZE); memory_region_add_subregion(parent, PCI_VGA_IOPORT_OFFSET, &subs[0]); - memory_region_init_io(&subs[1], NULL, &pci_vga_bochs_ops, s, + memory_region_init_io(&subs[1], owner, &pci_vga_bochs_ops, s, "bochs dispi interface", PCI_VGA_BOCHS_SIZE); memory_region_add_subregion(parent, PCI_VGA_BOCHS_OFFSET, &subs[1]); if (qext) { - memory_region_init_io(&subs[2], NULL, &pci_vga_qext_ops, s, + memory_region_init_io(&subs[2], owner, &pci_vga_qext_ops, s, "qemu extended regs", PCI_VGA_QEXT_SIZE); memory_region_add_subregion(parent, PCI_VGA_QEXT_OFFSET, &subs[2]); @@ -234,7 +222,7 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) bool qext = false; /* vga + console init */ - vga_common_init(s, OBJECT(dev), true); + vga_common_init(s, OBJECT(dev)); vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev), true); @@ -245,13 +233,14 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) /* mmio bar for vga register access */ if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_MMIO)) { - memory_region_init(&d->mmio, NULL, "vga.mmio", 4096); + memory_region_init(&d->mmio, NULL, "vga.mmio", + PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); } - pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext); + pci_std_vga_mmio_region_init(s, OBJECT(dev), &d->mmio, d->mrs, qext); pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); } @@ -276,17 +265,18 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) bool qext = false; /* vga + console init */ - vga_common_init(s, OBJECT(dev), false); + vga_common_init(s, OBJECT(dev)); s->con = graphic_console_init(DEVICE(dev), 0, s->hw_ops, s); /* mmio bar */ - memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", 4096); + memory_region_init(&d->mmio, OBJECT(dev), "vga.mmio", + PCI_VGA_MMIO_SIZE); if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { qext = true; pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); } - pci_std_vga_mmio_region_init(s, &d->mmio, d->mrs, qext); + pci_std_vga_mmio_region_init(s, OBJECT(dev), &d->mmio, d->mrs, qext); pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); @@ -318,6 +308,7 @@ static Property vga_pci_properties[] = { DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true), DEFINE_PROP_BIT("qemu-extended-regs", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), + DEFINE_PROP_BOOL("global-vmstate", PCIVGAState, vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga.c b/hw/display/vga.c index 72181330b8..802cfd47db 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/display/vga.h" @@ -721,7 +722,7 @@ uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr) val = s->vbe_regs[s->vbe_index]; } } else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) { - val = s->vbe_size / (64 * 1024); + val = s->vbe_size / (64 * KiB); } else { val = 0; } @@ -1480,13 +1481,28 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) s->get_resolution(s, &width, &height); disp_width = width; + depth = s->get_bpp(s); region_start = (s->start_addr * 4); region_end = region_start + (ram_addr_t)s->line_offset * height; - region_end += width * s->get_bpp(s) / 8; /* scanline length */ + region_end += width * depth / 8; /* scanline length */ region_end -= s->line_offset; - if (region_end > s->vbe_size) { - /* wraps around (can happen with cirrus vbe modes) */ + if (region_end > s->vbe_size || depth == 0 || depth == 15) { + /* + * We land here on: + * - wraps around (can happen with cirrus vbe modes) + * - depth == 0 (256 color palette video mode) + * - depth == 15 + * + * Take the safe and slow route: + * - create a dirty bitmap snapshot for all vga memory. + * - force shadowing (so all vga memory access goes + * through vga_read_*() helpers). + * + * Given this affects only vga features which are pretty much + * unused by modern guests there should be no performance + * impact. + */ region_start = 0; region_end = s->vbe_size; force_shadow = true; @@ -1520,8 +1536,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } } - depth = s->get_bpp(s); - /* * Check whether we can share the surface with the backend * or whether we need a shadow surface. We share native @@ -1535,12 +1549,31 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) } else { share_surface = false; } + if (s->line_offset != s->last_line_offset || disp_width != s->last_width || height != s->last_height || s->last_depth != depth || s->last_byteswap != byteswap || share_surface != is_buffer_shared(surface)) { + /* display parameters changed -> need new display surface */ + s->last_scr_width = disp_width; + s->last_scr_height = height; + s->last_width = disp_width; + s->last_height = height; + s->last_line_offset = s->line_offset; + s->last_depth = depth; + s->last_byteswap = byteswap; + full_update = 1; + } + if (surface_data(surface) != s->vram_ptr + (s->start_addr * 4) + && is_buffer_shared(surface)) { + /* base address changed (page flip) -> shared display surfaces + * must be updated with the new base address */ + full_update = 1; + } + + if (full_update) { if (share_surface) { surface = qemu_create_displaysurface_from(disp_width, height, format, s->line_offset, @@ -1550,23 +1583,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) qemu_console_resize(s->con, disp_width, height); surface = qemu_console_surface(s->con); } - s->last_scr_width = disp_width; - s->last_scr_height = height; - s->last_width = disp_width; - s->last_height = height; - s->last_line_offset = s->line_offset; - s->last_depth = depth; - s->last_byteswap = byteswap; - full_update = 1; - } else if (is_buffer_shared(surface) && - (full_update || surface_data(surface) != s->vram_ptr - + (s->start_addr * 4))) { - pixman_format_code_t format = - qemu_default_pixman_format(depth, !byteswap); - surface = qemu_create_displaysurface_from(disp_width, - height, format, s->line_offset, - s->vram_ptr + (s->start_addr * 4)); - dpy_gfx_replace_surface(s->con, surface); } if (shift_control == 0) { @@ -2148,7 +2164,7 @@ static inline uint32_t uint_clamp(uint32_t val, uint32_t vmin, uint32_t vmax) return val; } -void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) +void vga_common_init(VGACommonState *s, Object *obj) { int i, j, v, b; @@ -2177,7 +2193,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) s->vram_size_mb = uint_clamp(s->vram_size_mb, 1, 512); s->vram_size_mb = pow2ceil(s->vram_size_mb); - s->vram_size = s->vram_size_mb << 20; + s->vram_size = s->vram_size_mb * MiB; if (!s->vbe_size) { s->vbe_size = s->vram_size; @@ -2187,7 +2203,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) s->is_vbe_vmstate = 1; memory_region_init_ram_nomigrate(&s->vram, obj, "vga.vram", s->vram_size, &error_fatal); - vmstate_register_ram(&s->vram, global_vmstate ? NULL : DEVICE(obj)); + vmstate_register_ram(&s->vram, s->global_vmstate ? NULL : DEVICE(obj)); xen_register_framebuffer(&s->vram); s->vram_ptr = memory_region_get_ram_ptr(&s->vram); s->get_bpp = vga_get_bpp; diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index fe23b81442..339661bc01 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -29,42 +29,11 @@ #include "exec/memory.h" #include "ui/console.h" +#include "hw/display/bochs-vbe.h" + #define ST01_V_RETRACE 0x08 #define ST01_DISP_ENABLE 0x01 -#define VBE_DISPI_MAX_XRES 16000 -#define VBE_DISPI_MAX_YRES 12000 -#define VBE_DISPI_MAX_BPP 32 - -#define VBE_DISPI_INDEX_ID 0x0 -#define VBE_DISPI_INDEX_XRES 0x1 -#define VBE_DISPI_INDEX_YRES 0x2 -#define VBE_DISPI_INDEX_BPP 0x3 -#define VBE_DISPI_INDEX_ENABLE 0x4 -#define VBE_DISPI_INDEX_BANK 0x5 -#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 -#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 -#define VBE_DISPI_INDEX_X_OFFSET 0x8 -#define VBE_DISPI_INDEX_Y_OFFSET 0x9 -#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ -#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ - -#define VBE_DISPI_ID0 0xB0C0 -#define VBE_DISPI_ID1 0xB0C1 -#define VBE_DISPI_ID2 0xB0C2 -#define VBE_DISPI_ID3 0xB0C3 -#define VBE_DISPI_ID4 0xB0C4 -#define VBE_DISPI_ID5 0xB0C5 - -#define VBE_DISPI_DISABLED 0x00 -#define VBE_DISPI_ENABLED 0x01 -#define VBE_DISPI_GETCAPS 0x02 -#define VBE_DISPI_8BIT_DAC 0x20 -#define VBE_DISPI_LFB_ENABLED 0x40 -#define VBE_DISPI_NOCLEARMEM 0x80 - -#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 - #define CH_ATTR_SIZE (160 * 100) #define VGA_MAX_HEIGHT 2048 @@ -164,6 +133,7 @@ typedef struct VGACommonState { bool full_update_gfx; bool big_endian_fb; bool default_endian_fb; + bool global_vmstate; /* hardware mouse cursor support */ uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32]; uint32_t hw_cursor_x; @@ -188,7 +158,7 @@ static inline int c6_to_8(int v) return (v << 2) | (b << 1) | b; } -void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate); +void vga_common_init(VGACommonState *s, Object *obj); void vga_init(VGACommonState *s, Object *obj, MemoryRegion *address_space, MemoryRegion *address_space_io, bool init_vga_ports); MemoryRegion *vga_init_io(VGACommonState *s, Object *obj, @@ -224,6 +194,7 @@ extern const MemoryRegionOps vga_mem_ops; /* vga-pci.c */ void pci_std_vga_mmio_region_init(VGACommonState *s, + Object *owner, MemoryRegion *parent, MemoryRegion *subs, bool qext); diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 2dd3c3481a..3ddd29c0de 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "qemu/iov.h" #include "ui/console.h" @@ -399,9 +400,52 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, g->hostmem += res->hostmem; } +static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) +{ + struct virtio_gpu_scanout *scanout = &g->scanout[scanout_id]; + struct virtio_gpu_simple_resource *res; + DisplaySurface *ds = NULL; + + if (scanout->resource_id == 0) { + return; + } + + res = virtio_gpu_find_resource(g, scanout->resource_id); + if (res) { + res->scanout_bitmask &= ~(1 << scanout_id); + } + + if (scanout_id == 0) { + /* primary head */ + ds = qemu_create_message_surface(scanout->width ?: 640, + scanout->height ?: 480, + "Guest disabled display."); + } + + if (g->disable_scanout) { + g->disable_scanout(g, scanout_id); + } + + dpy_gfx_replace_surface(scanout->con, ds); + scanout->resource_id = 0; + scanout->ds = NULL; + scanout->width = 0; + scanout->height = 0; +} + static void virtio_gpu_resource_destroy(VirtIOGPU *g, struct virtio_gpu_simple_resource *res) { + int i; + + if (res->scanout_bitmask) { + for (i = 0; i < g->conf.max_outputs; i++) { + if (res->scanout_bitmask & (1 << i)) { + virtio_gpu_disable_scanout(g, i); + } + } + } + pixman_image_unref(res->image); virtio_gpu_cleanup_mapping(res); QTAILQ_REMOVE(&g->reslist, res, next); @@ -562,7 +606,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data) static void virtio_gpu_set_scanout(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd) { - struct virtio_gpu_simple_resource *res; + struct virtio_gpu_simple_resource *res, *ores; struct virtio_gpu_scanout *scanout; pixman_format_code_t format; uint32_t offset; @@ -583,24 +627,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, g->enable = 1; if (ss.resource_id == 0) { - scanout = &g->scanout[ss.scanout_id]; - if (scanout->resource_id) { - res = virtio_gpu_find_resource(g, scanout->resource_id); - if (res) { - res->scanout_bitmask &= ~(1 << ss.scanout_id); - } - } - if (ss.scanout_id == 0) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: illegal scanout id specified %d", - __func__, ss.scanout_id); - cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID; - return; - } - dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL); - scanout->ds = NULL; - scanout->width = 0; - scanout->height = 0; + virtio_gpu_disable_scanout(g, ss.scanout_id); return; } @@ -653,6 +680,11 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, scanout->ds); } + ores = virtio_gpu_find_resource(g, scanout->resource_id); + if (ores) { + ores->scanout_bitmask &= ~(1 << ss.scanout_id); + } + res->scanout_bitmask |= (1 << ss.scanout_id); scanout->resource_id = ss.resource_id; scanout->x = ss.r.x; @@ -1314,8 +1346,7 @@ static const VMStateDescription vmstate_virtio_gpu = { static Property virtio_gpu_properties[] = { DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1), - DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf.max_hostmem, - 256 * 1024 * 1024), + DEFINE_PROP_SIZE("max_hostmem", VirtIOGPU, conf.max_hostmem, 256 * MiB), #ifdef CONFIG_VIRGL DEFINE_PROP_BIT("virgl", VirtIOGPU, conf.flags, VIRTIO_GPU_FLAG_VIRGL_ENABLED, true), diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index baa74ba82c..701d980872 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -75,6 +75,16 @@ static void virtio_vga_gl_block(void *opaque, bool block) } } +static void virtio_vga_disable_scanout(VirtIOGPU *g, int scanout_id) +{ + VirtIOVGA *vvga = container_of(g, VirtIOVGA, vdev); + + if (scanout_id == 0) { + /* reset surface if needed */ + vvga->vga.graphic_mode = -1; + } +} + static const GraphicHwOps virtio_vga_ops = { .invalidate = virtio_vga_invalidate_display, .gfx_update = virtio_vga_update_display, @@ -106,7 +116,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) /* init vga compat bits */ vga->vram_size_mb = 8; - vga_common_init(vga, OBJECT(vpci_dev), false); + vga_common_init(vga, OBJECT(vpci_dev)); vga_init(vga, OBJECT(vpci_dev), pci_address_space(&vpci_dev->pci_dev), pci_address_space_io(&vpci_dev->pci_dev), true); pci_register_bar(&vpci_dev->pci_dev, 0, @@ -152,10 +162,11 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } /* add stdvga mmio regions */ - pci_std_vga_mmio_region_init(vga, &vpci_dev->modern_bar, + pci_std_vga_mmio_region_init(vga, OBJECT(vvga), &vpci_dev->modern_bar, vvga->vga_mrs, true); vga->con = g->scanout[0].con; + g->disable_scanout = virtio_vga_disable_scanout; graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga); for (i = 0; i < g->conf.max_outputs; i++) { diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index bd3e8b3586..0bbb78b9a6 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/loader.h" @@ -565,7 +566,7 @@ static inline int vmsvga_fifo_length(struct vmsvga_state_s *s) s->fifo_next >= SVGA_FIFO_SIZE) { return 0; } - if (s->fifo_max < s->fifo_min + 10 * 1024) { + if (s->fifo_max < s->fifo_min + 10 * KiB) { return 0; } @@ -1241,7 +1242,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, &error_fatal); s->fifo_ptr = memory_region_get_ram_ptr(&s->fifo_ram); - vga_common_init(&s->vga, OBJECT(dev), true); + vga_common_init(&s->vga, OBJECT(dev)); vga_init(&s->vga, OBJECT(dev), address_space, io, true); vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); s->new_depth = 32; @@ -1321,6 +1322,8 @@ static void pci_vmsvga_realize(PCIDevice *dev, Error **errp) static Property vga_vmware_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", struct pci_vmsvga_state_s, chip.vga.vram_size_mb, 16), + DEFINE_PROP_BOOL("global-vmstate", struct pci_vmsvga_state_s, + chip.vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index f5afcc0358..0330dc6f61 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -25,6 +25,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "ui/input.h" @@ -525,8 +526,8 @@ static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim, int width, int height, int depth, size_t fb_len, int offset, int row_stride) { - size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); - size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; + size_t mfn_sz = sizeof_field(struct xenfb_page, pd[0]); + size_t pd_len = sizeof_field(struct xenfb_page, pd) / mfn_sz; size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; size_t fb_len_max = fb_pages * XC_PAGE_SIZE; int max_width, max_height; @@ -889,7 +890,7 @@ static int fb_initialise(struct XenDevice *xendev) return rc; fb_page = fb->c.page; - rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U, + rc = xenfb_configure_fb(fb, videoram * MiB, fb_page->width, fb_page->height, fb_page->depth, fb_page->mem_length, 0, fb_page->line_length); if (rc != 0) diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 6715b9cc2b..6439bd05ef 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1074,7 +1074,9 @@ static void xlnx_dp_avbufm_write(void *opaque, hwaddr offset, uint64_t value, case AV_BUF_STC_SNAPSHOT1: case AV_BUF_HCOUNT_VCOUNT_INT0: case AV_BUF_HCOUNT_VCOUNT_INT1: - qemu_log_mask(LOG_UNIMP, "avbufm: unimplmented"); + qemu_log_mask(LOG_UNIMP, "avbufm: unimplemented register 0x%04" + PRIx64 "\n", + offset << 2); break; default: s->avbufm_registers[offset] = value; @@ -1221,7 +1223,7 @@ static void xlnx_dp_init(Object *obj) object_property_add_link(obj, "dpdma", TYPE_XLNX_DPDMA, (Object **) &s->dpdma, xlnx_dp_set_dpdma, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); /* @@ -1232,9 +1234,12 @@ static void xlnx_dp_init(Object *obj) /* * Initialize DPCD and EDID.. */ - s->dpcd = DPCD(aux_create_slave(s->aux_bus, "dpcd", 0x00000)); + s->dpcd = DPCD(aux_create_slave(s->aux_bus, "dpcd")); + object_property_add_child(OBJECT(s), "dpcd", OBJECT(s->dpcd), NULL); + s->edid = I2CDDC(qdev_create(BUS(aux_get_i2c_bus(s->aux_bus)), "i2c-ddc")); i2c_set_slave_address(I2C_SLAVE(s->edid), 0x50); + object_property_add_child(OBJECT(s), "edid", OBJECT(s->edid), NULL); fifo8_create(&s->rx_fifo, 16); fifo8_create(&s->tx_fifo, 16); @@ -1246,6 +1251,9 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp) DisplaySurface *surface; struct audsettings as; + qdev_init_nofail(DEVICE(s->dpcd)); + aux_map_slave(AUX_SLAVE(s->dpcd), 0x0000); + s->console = graphic_console_init(dev, 0, &xlnx_dp_gfx_ops, s); surface = qemu_console_surface(s->console); xlnx_dpdma_set_host_data_location(s->dpdma, DP_GRAPHIC_DMA_CHANNEL, diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs index c2afecbf73..79affecc39 100644 --- a/hw/dma/Makefile.objs +++ b/hw/dma/Makefile.objs @@ -10,6 +10,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o common-obj-$(CONFIG_STP2000) += sparc32_dma.o obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dpdma.o obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dpdma.o +common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index abd18c67ea..cbb920f31d 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -18,6 +18,7 @@ * with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qemu-common.h" #include "qemu/timer.h" #include "hw/arm/omap.h" @@ -878,15 +879,18 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s, ch->burst[0] = (value & 0x0180) >> 7; ch->pack[0] = (value & 0x0040) >> 6; ch->port[0] = (enum omap_dma_port) ((value & 0x003c) >> 2); - if (ch->port[0] >= __omap_dma_port_last) - printf("%s: invalid DMA port %i\n", __func__, - ch->port[0]); - if (ch->port[1] >= __omap_dma_port_last) - printf("%s: invalid DMA port %i\n", __func__, - ch->port[1]); + if (ch->port[0] >= __omap_dma_port_last) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid DMA port %i\n", + __func__, ch->port[0]); + } + if (ch->port[1] >= __omap_dma_port_last) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid DMA port %i\n", + __func__, ch->port[1]); + } ch->data_type = 1 << (value & 3); if ((value & 3) == 3) { - printf("%s: bad data_type for DMA channel\n", __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad data_type for DMA channel\n", __func__); ch->data_type >>= 1; } break; @@ -1439,8 +1443,9 @@ static int omap_dma_sys_read(struct omap_dma_s *s, int offset, case 0x480: /* DMA_PCh0_SR */ case 0x482: /* DMA_PCh1_SR */ case 0x4c0: /* DMA_PChD_SR_0 */ - printf("%s: Physical Channel Status Registers not implemented.\n", - __func__); + qemu_log_mask(LOG_UNIMP, + "%s: Physical Channel Status Registers not implemented\n", + __func__); *ret = 0xff; break; @@ -1897,14 +1902,18 @@ static void omap_dma4_write(void *opaque, hwaddr addr, if (value & 2) /* SOFTRESET */ omap_dma_reset(s->dma); s->ocp = value & 0x3321; - if (((s->ocp >> 12) & 3) == 3) /* MIDLEMODE */ - fprintf(stderr, "%s: invalid DMA power mode\n", __func__); + if (((s->ocp >> 12) & 3) == 3) { /* MIDLEMODE */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid DMA power mode\n", + __func__); + } return; case 0x78: /* DMA4_GCR */ s->gcr = value & 0x00ff00ff; - if ((value & 0xff) == 0x00) /* MAX_CHANNEL_FIFO_DEPTH */ - fprintf(stderr, "%s: wrong FIFO depth in GCR\n", __func__); + if ((value & 0xff) == 0x00) { /* MAX_CHANNEL_FIFO_DEPTH */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: wrong FIFO depth in GCR\n", + __func__); + } return; case 0x80 ... 0xfff: @@ -1933,9 +1942,11 @@ static void omap_dma4_write(void *opaque, hwaddr addr, case 0x00: /* DMA4_CCR */ ch->buf_disable = (value >> 25) & 1; ch->src_sync = (value >> 24) & 1; /* XXX For CamDMA must be 1 */ - if (ch->buf_disable && !ch->src_sync) - fprintf(stderr, "%s: Buffering disable is not allowed in " - "destination synchronised mode\n", __func__); + if (ch->buf_disable && !ch->src_sync) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Buffering disable is not allowed in " + "destination synchronised mode\n", __func__); + } ch->prefetch = (value >> 23) & 1; ch->bs = (value >> 18) & 1; ch->transparent_copy = (value >> 17) & 1; @@ -1945,9 +1956,11 @@ static void omap_dma4_write(void *opaque, hwaddr addr, ch->suspend = (value & 0x0100) >> 8; ch->priority = (value & 0x0040) >> 6; ch->fs = (value & 0x0020) >> 5; - if (ch->fs && ch->bs && ch->mode[0] && ch->mode[1]) - fprintf(stderr, "%s: For a packet transfer at least one port " - "must be constant-addressed\n", __func__); + if (ch->fs && ch->bs && ch->mode[0] && ch->mode[1]) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: For a packet transfer at least one port " + "must be constant-addressed\n", __func__); + } ch->sync = (value & 0x001f) | ((value >> 14) & 0x0060); /* XXX must be 0x01 for CamDMA */ @@ -1976,9 +1989,11 @@ static void omap_dma4_write(void *opaque, hwaddr addr, ch->endian_lock[0] =(value >> 20) & 1; ch->endian[1] =(value >> 19) & 1; ch->endian_lock[1] =(value >> 18) & 1; - if (ch->endian[0] != ch->endian[1]) - fprintf(stderr, "%s: DMA endianness conversion enable attempt\n", - __func__); + if (ch->endian[0] != ch->endian[1]) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DMA endianness conversion enable attempt\n", + __func__); + } ch->write_mode = (value >> 16) & 3; ch->burst[1] = (value & 0xc000) >> 14; ch->pack[1] = (value & 0x2000) >> 13; @@ -1986,12 +2001,15 @@ static void omap_dma4_write(void *opaque, hwaddr addr, ch->burst[0] = (value & 0x0180) >> 7; ch->pack[0] = (value & 0x0040) >> 6; ch->translate[0] = (value & 0x003c) >> 2; - if (ch->translate[0] | ch->translate[1]) - fprintf(stderr, "%s: bad MReqAddressTranslate sideband signal\n", - __func__); + if (ch->translate[0] | ch->translate[1]) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad MReqAddressTranslate sideband signal\n", + __func__); + } ch->data_type = 1 << (value & 3); if ((value & 3) == 3) { - printf("%s: bad data_type for DMA channel\n", __func__); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad data_type for DMA channel\n", __func__); ch->data_type >>= 1; } break; diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index 5d4833eeca..ccd8612888 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -491,7 +491,7 @@ static const MemoryRegionOps jazzio_ops = { }; static IOMMUTLBEntry rc4030_dma_translate(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { rc4030State *s = container_of(iommu, rc4030State, dma_mr); IOMMUTLBEntry ret = { diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index 9b48103574..401a328e27 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -525,12 +525,12 @@ static void xilinx_axidma_realize(DeviceState *dev, Error **errp) object_property_add_link(OBJECT(ds), "dma", TYPE_XILINX_AXI_DMA, (Object **)&ds->dma, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &local_err); object_property_add_link(OBJECT(cs), "dma", TYPE_XILINX_AXI_DMA, (Object **)&cs->dma, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &local_err); if (local_err) { goto xilinx_axidma_realize_fail; diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c new file mode 100644 index 0000000000..b6745f5bcf --- /dev/null +++ b/hw/dma/xlnx-zdma.c @@ -0,0 +1,836 @@ +/* + * QEMU model of the ZynqMP generic DMA + * + * Copyright (c) 2014 Xilinx Inc. + * Copyright (c) 2018 FEIMTECH AB + * + * Written by Edgar E. Iglesias , + * Francisco Iglesias + * + * 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/dma/xlnx-zdma.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qapi/error.h" + +#ifndef XLNX_ZDMA_ERR_DEBUG +#define XLNX_ZDMA_ERR_DEBUG 0 +#endif + +REG32(ZDMA_ERR_CTRL, 0x0) + FIELD(ZDMA_ERR_CTRL, APB_ERR_RES, 0, 1) +REG32(ZDMA_CH_ISR, 0x100) + FIELD(ZDMA_CH_ISR, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_ISR, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_ISR, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_ISR, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_ISR, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_ISR, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_ISR, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_ISR, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_ISR, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_ISR, INV_APB, 0, 1) +REG32(ZDMA_CH_IMR, 0x104) + FIELD(ZDMA_CH_IMR, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_IMR, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_IMR, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_IMR, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_IMR, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_IMR, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_IMR, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_IMR, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_IMR, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_IMR, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_IMR, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_IMR, INV_APB, 0, 1) +REG32(ZDMA_CH_IEN, 0x108) + FIELD(ZDMA_CH_IEN, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_IEN, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_IEN, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_IEN, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_IEN, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_IEN, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_IEN, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_IEN, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_IEN, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_IEN, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_IEN, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_IEN, INV_APB, 0, 1) +REG32(ZDMA_CH_IDS, 0x10c) + FIELD(ZDMA_CH_IDS, DMA_PAUSE, 11, 1) + FIELD(ZDMA_CH_IDS, DMA_DONE, 10, 1) + FIELD(ZDMA_CH_IDS, AXI_WR_DATA, 9, 1) + FIELD(ZDMA_CH_IDS, AXI_RD_DATA, 8, 1) + FIELD(ZDMA_CH_IDS, AXI_RD_DST_DSCR, 7, 1) + FIELD(ZDMA_CH_IDS, AXI_RD_SRC_DSCR, 6, 1) + FIELD(ZDMA_CH_IDS, IRQ_DST_ACCT_ERR, 5, 1) + FIELD(ZDMA_CH_IDS, IRQ_SRC_ACCT_ERR, 4, 1) + FIELD(ZDMA_CH_IDS, BYTE_CNT_OVRFL, 3, 1) + FIELD(ZDMA_CH_IDS, DST_DSCR_DONE, 2, 1) + FIELD(ZDMA_CH_IDS, SRC_DSCR_DONE, 1, 1) + FIELD(ZDMA_CH_IDS, INV_APB, 0, 1) +REG32(ZDMA_CH_CTRL0, 0x110) + FIELD(ZDMA_CH_CTRL0, OVR_FETCH, 7, 1) + FIELD(ZDMA_CH_CTRL0, POINT_TYPE, 6, 1) + FIELD(ZDMA_CH_CTRL0, MODE, 4, 2) + FIELD(ZDMA_CH_CTRL0, RATE_CTRL, 3, 1) + FIELD(ZDMA_CH_CTRL0, CONT_ADDR, 2, 1) + FIELD(ZDMA_CH_CTRL0, CONT, 1, 1) +REG32(ZDMA_CH_CTRL1, 0x114) + FIELD(ZDMA_CH_CTRL1, DST_ISSUE, 5, 5) + FIELD(ZDMA_CH_CTRL1, SRC_ISSUE, 0, 5) +REG32(ZDMA_CH_FCI, 0x118) + FIELD(ZDMA_CH_FCI, PROG_CELL_CNT, 2, 2) + FIELD(ZDMA_CH_FCI, SIDE, 1, 1) + FIELD(ZDMA_CH_FCI, EN, 0, 1) +REG32(ZDMA_CH_STATUS, 0x11c) + FIELD(ZDMA_CH_STATUS, STATE, 0, 2) +REG32(ZDMA_CH_DATA_ATTR, 0x120) + FIELD(ZDMA_CH_DATA_ATTR, ARBURST, 26, 2) + FIELD(ZDMA_CH_DATA_ATTR, ARCACHE, 22, 4) + FIELD(ZDMA_CH_DATA_ATTR, ARQOS, 18, 4) + FIELD(ZDMA_CH_DATA_ATTR, ARLEN, 14, 4) + FIELD(ZDMA_CH_DATA_ATTR, AWBURST, 12, 2) + FIELD(ZDMA_CH_DATA_ATTR, AWCACHE, 8, 4) + FIELD(ZDMA_CH_DATA_ATTR, AWQOS, 4, 4) + FIELD(ZDMA_CH_DATA_ATTR, AWLEN, 0, 4) +REG32(ZDMA_CH_DSCR_ATTR, 0x124) + FIELD(ZDMA_CH_DSCR_ATTR, AXCOHRNT, 8, 1) + FIELD(ZDMA_CH_DSCR_ATTR, AXCACHE, 4, 4) + FIELD(ZDMA_CH_DSCR_ATTR, AXQOS, 0, 4) +REG32(ZDMA_CH_SRC_DSCR_WORD0, 0x128) +REG32(ZDMA_CH_SRC_DSCR_WORD1, 0x12c) + FIELD(ZDMA_CH_SRC_DSCR_WORD1, MSB, 0, 17) +REG32(ZDMA_CH_SRC_DSCR_WORD2, 0x130) + FIELD(ZDMA_CH_SRC_DSCR_WORD2, SIZE, 0, 30) +REG32(ZDMA_CH_SRC_DSCR_WORD3, 0x134) + FIELD(ZDMA_CH_SRC_DSCR_WORD3, CMD, 3, 2) + FIELD(ZDMA_CH_SRC_DSCR_WORD3, INTR, 2, 1) + FIELD(ZDMA_CH_SRC_DSCR_WORD3, TYPE, 1, 1) + FIELD(ZDMA_CH_SRC_DSCR_WORD3, COHRNT, 0, 1) +REG32(ZDMA_CH_DST_DSCR_WORD0, 0x138) +REG32(ZDMA_CH_DST_DSCR_WORD1, 0x13c) + FIELD(ZDMA_CH_DST_DSCR_WORD1, MSB, 0, 17) +REG32(ZDMA_CH_DST_DSCR_WORD2, 0x140) + FIELD(ZDMA_CH_DST_DSCR_WORD2, SIZE, 0, 30) +REG32(ZDMA_CH_DST_DSCR_WORD3, 0x144) + FIELD(ZDMA_CH_DST_DSCR_WORD3, INTR, 2, 1) + FIELD(ZDMA_CH_DST_DSCR_WORD3, TYPE, 1, 1) + FIELD(ZDMA_CH_DST_DSCR_WORD3, COHRNT, 0, 1) +REG32(ZDMA_CH_WR_ONLY_WORD0, 0x148) +REG32(ZDMA_CH_WR_ONLY_WORD1, 0x14c) +REG32(ZDMA_CH_WR_ONLY_WORD2, 0x150) +REG32(ZDMA_CH_WR_ONLY_WORD3, 0x154) +REG32(ZDMA_CH_SRC_START_LSB, 0x158) +REG32(ZDMA_CH_SRC_START_MSB, 0x15c) + FIELD(ZDMA_CH_SRC_START_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_DST_START_LSB, 0x160) +REG32(ZDMA_CH_DST_START_MSB, 0x164) + FIELD(ZDMA_CH_DST_START_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_RATE_CTRL, 0x18c) + FIELD(ZDMA_CH_RATE_CTRL, CNT, 0, 12) +REG32(ZDMA_CH_SRC_CUR_PYLD_LSB, 0x168) +REG32(ZDMA_CH_SRC_CUR_PYLD_MSB, 0x16c) + FIELD(ZDMA_CH_SRC_CUR_PYLD_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_DST_CUR_PYLD_LSB, 0x170) +REG32(ZDMA_CH_DST_CUR_PYLD_MSB, 0x174) + FIELD(ZDMA_CH_DST_CUR_PYLD_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_SRC_CUR_DSCR_LSB, 0x178) +REG32(ZDMA_CH_SRC_CUR_DSCR_MSB, 0x17c) + FIELD(ZDMA_CH_SRC_CUR_DSCR_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_DST_CUR_DSCR_LSB, 0x180) +REG32(ZDMA_CH_DST_CUR_DSCR_MSB, 0x184) + FIELD(ZDMA_CH_DST_CUR_DSCR_MSB, ADDR, 0, 17) +REG32(ZDMA_CH_TOTAL_BYTE, 0x188) +REG32(ZDMA_CH_RATE_CNTL, 0x18c) + FIELD(ZDMA_CH_RATE_CNTL, CNT, 0, 12) +REG32(ZDMA_CH_IRQ_SRC_ACCT, 0x190) + FIELD(ZDMA_CH_IRQ_SRC_ACCT, CNT, 0, 8) +REG32(ZDMA_CH_IRQ_DST_ACCT, 0x194) + FIELD(ZDMA_CH_IRQ_DST_ACCT, CNT, 0, 8) +REG32(ZDMA_CH_DBG0, 0x198) + FIELD(ZDMA_CH_DBG0, CMN_BUF_FREE, 0, 9) +REG32(ZDMA_CH_DBG1, 0x19c) + FIELD(ZDMA_CH_DBG1, CMN_BUF_OCC, 0, 9) +REG32(ZDMA_CH_CTRL2, 0x200) + FIELD(ZDMA_CH_CTRL2, EN, 0, 1) + +enum { + PT_REG = 0, + PT_MEM = 1, +}; + +enum { + CMD_HALT = 1, + CMD_STOP = 2, +}; + +enum { + RW_MODE_RW = 0, + RW_MODE_WO = 1, + RW_MODE_RO = 2, +}; + +enum { + DTYPE_LINEAR = 0, + DTYPE_LINKED = 1, +}; + +enum { + AXI_BURST_FIXED = 0, + AXI_BURST_INCR = 1, +}; + +static void zdma_ch_imr_update_irq(XlnxZDMA *s) +{ + bool pending; + + pending = s->regs[R_ZDMA_CH_ISR] & ~s->regs[R_ZDMA_CH_IMR]; + + qemu_set_irq(s->irq_zdma_ch_imr, pending); +} + +static void zdma_ch_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + zdma_ch_imr_update_irq(s); +} + +static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + uint32_t val = val64; + + s->regs[R_ZDMA_CH_IMR] &= ~val; + zdma_ch_imr_update_irq(s); + return 0; +} + +static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + uint32_t val = val64; + + s->regs[R_ZDMA_CH_IMR] |= val; + zdma_ch_imr_update_irq(s); + return 0; +} + +static void zdma_set_state(XlnxZDMA *s, XlnxZDMAState state) +{ + s->state = state; + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, state); + + /* Signal error if we have an error condition. */ + if (s->error) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, 3); + } +} + +static void zdma_src_done(XlnxZDMA *s) +{ + unsigned int cnt; + cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT); + cnt++; + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT, cnt); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, SRC_DSCR_DONE, true); + + /* Did we overflow? */ + if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT)) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, true); + } + zdma_ch_imr_update_irq(s); +} + +static void zdma_dst_done(XlnxZDMA *s) +{ + unsigned int cnt; + cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT); + cnt++; + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT, cnt); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DST_DSCR_DONE, true); + + /* Did we overflow? */ + if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT)) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, true); + } + zdma_ch_imr_update_irq(s); +} + +static uint64_t zdma_get_regaddr64(XlnxZDMA *s, unsigned int basereg) +{ + uint64_t addr; + + addr = s->regs[basereg + 1]; + addr <<= 32; + addr |= s->regs[basereg]; + + return addr; +} + +static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg, uint64_t addr) +{ + s->regs[basereg] = addr; + s->regs[basereg + 1] = addr >> 32; +} + +static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf) +{ + /* ZDMA descriptors must be aligned to their own size. */ + if (addr % sizeof(XlnxZDMADescr)) { + qemu_log_mask(LOG_GUEST_ERROR, + "zdma: unaligned descriptor at %" PRIx64, + addr); + memset(buf, 0x0, sizeof(XlnxZDMADescr)); + s->error = true; + return false; + } + + address_space_rw(s->dma_as, addr, s->attr, + buf, sizeof(XlnxZDMADescr), false); + return true; +} + +static void zdma_load_src_descriptor(XlnxZDMA *s) +{ + uint64_t src_addr; + unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); + + if (ptype == PT_REG) { + memcpy(&s->dsc_src, &s->regs[R_ZDMA_CH_SRC_DSCR_WORD0], + sizeof(s->dsc_src)); + return; + } + + src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB); + + if (!zdma_load_descriptor(s, src_addr, &s->dsc_src)) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_SRC_DSCR, true); + } +} + +static void zdma_load_dst_descriptor(XlnxZDMA *s) +{ + uint64_t dst_addr; + unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); + + if (ptype == PT_REG) { + memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0], + sizeof(s->dsc_dst)); + return; + } + + dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB); + + if (!zdma_load_descriptor(s, dst_addr, &s->dsc_dst)) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true); + } +} + +static uint64_t zdma_update_descr_addr(XlnxZDMA *s, bool type, + unsigned int basereg) +{ + uint64_t addr, next; + + if (type == DTYPE_LINEAR) { + next = zdma_get_regaddr64(s, basereg); + next += sizeof(s->dsc_dst); + zdma_put_regaddr64(s, basereg, next); + } else { + addr = zdma_get_regaddr64(s, basereg); + addr += sizeof(s->dsc_dst); + address_space_rw(s->dma_as, addr, s->attr, (void *) &next, 8, false); + zdma_put_regaddr64(s, basereg, next); + } + return next; +} + +static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len) +{ + uint32_t dst_size, dlen; + bool dst_intr, dst_type; + unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); + unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE); + unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR, + AWBURST); + + /* FIXED burst types are only supported in simple dma mode. */ + if (ptype != PT_REG) { + burst_type = AXI_BURST_INCR; + } + + while (len) { + dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2, + SIZE); + dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3, + TYPE); + if (dst_size == 0 && ptype == PT_MEM) { + uint64_t next; + next = zdma_update_descr_addr(s, dst_type, + R_ZDMA_CH_DST_CUR_DSCR_LSB); + zdma_load_descriptor(s, next, &s->dsc_dst); + dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2, + SIZE); + dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3, + TYPE); + } + + /* Match what hardware does by ignoring the dst_size and only using + * the src size for Simple register mode. */ + if (ptype == PT_REG && rw_mode != RW_MODE_WO) { + dst_size = len; + } + + dst_intr = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3, + INTR); + + dlen = len > dst_size ? dst_size : len; + if (burst_type == AXI_BURST_FIXED) { + if (dlen > (s->cfg.bus_width / 8)) { + dlen = s->cfg.bus_width / 8; + } + } + + address_space_rw(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen, + true); + if (burst_type == AXI_BURST_INCR) { + s->dsc_dst.addr += dlen; + } + dst_size -= dlen; + buf += dlen; + len -= dlen; + + if (dst_size == 0 && dst_intr) { + zdma_dst_done(s); + } + + /* Write back to buffered descriptor. */ + s->dsc_dst.words[2] = FIELD_DP32(s->dsc_dst.words[2], + ZDMA_CH_DST_DSCR_WORD2, + SIZE, + dst_size); + } +} + +static void zdma_process_descr(XlnxZDMA *s) +{ + uint64_t src_addr; + uint32_t src_size, len; + unsigned int src_cmd; + bool src_intr, src_type; + unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE); + unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE); + unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR, + ARBURST); + + src_addr = s->dsc_src.addr; + src_size = FIELD_EX32(s->dsc_src.words[2], ZDMA_CH_SRC_DSCR_WORD2, SIZE); + src_cmd = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, CMD); + src_type = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, TYPE); + src_intr = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, INTR); + + /* FIXED burst types and non-rw modes are only supported in + * simple dma mode. + */ + if (ptype != PT_REG) { + if (rw_mode != RW_MODE_RW) { + qemu_log_mask(LOG_GUEST_ERROR, + "zDMA: rw-mode=%d but not simple DMA mode.\n", + rw_mode); + } + if (burst_type != AXI_BURST_INCR) { + qemu_log_mask(LOG_GUEST_ERROR, + "zDMA: burst_type=%d but not simple DMA mode.\n", + burst_type); + } + burst_type = AXI_BURST_INCR; + rw_mode = RW_MODE_RW; + } + + if (rw_mode == RW_MODE_WO) { + /* In Simple DMA Write-Only, we need to push DST size bytes + * regardless of what SRC size is set to. */ + src_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2, + SIZE); + memcpy(s->buf, &s->regs[R_ZDMA_CH_WR_ONLY_WORD0], s->cfg.bus_width / 8); + } + + while (src_size) { + len = src_size > ARRAY_SIZE(s->buf) ? ARRAY_SIZE(s->buf) : src_size; + if (burst_type == AXI_BURST_FIXED) { + if (len > (s->cfg.bus_width / 8)) { + len = s->cfg.bus_width / 8; + } + } + + if (rw_mode == RW_MODE_WO) { + if (len > s->cfg.bus_width / 8) { + len = s->cfg.bus_width / 8; + } + } else { + address_space_rw(s->dma_as, src_addr, s->attr, s->buf, len, + false); + if (burst_type == AXI_BURST_INCR) { + src_addr += len; + } + } + + if (rw_mode != RW_MODE_RO) { + zdma_write_dst(s, s->buf, len); + } + + s->regs[R_ZDMA_CH_TOTAL_BYTE] += len; + src_size -= len; + } + + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, true); + + if (src_intr) { + zdma_src_done(s); + } + + /* Load next descriptor. */ + if (ptype == PT_REG || src_cmd == CMD_STOP) { + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL2, EN, 0); + zdma_set_state(s, DISABLED); + return; + } + + if (src_cmd == CMD_HALT) { + zdma_set_state(s, PAUSED); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_PAUSE, 1); + zdma_ch_imr_update_irq(s); + return; + } + + zdma_update_descr_addr(s, src_type, R_ZDMA_CH_SRC_CUR_DSCR_LSB); +} + +static void zdma_run(XlnxZDMA *s) +{ + while (s->state == ENABLED && !s->error) { + zdma_load_src_descriptor(s); + + if (s->error) { + zdma_set_state(s, DISABLED); + } else { + zdma_process_descr(s); + } + } + + zdma_ch_imr_update_irq(s); +} + +static void zdma_update_descr_addr_from_start(XlnxZDMA *s) +{ + uint64_t src_addr, dst_addr; + + src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_START_LSB); + zdma_put_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB, src_addr); + dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_START_LSB); + zdma_put_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB, dst_addr); + zdma_load_dst_descriptor(s); +} + +static void zdma_ch_ctrlx_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxZDMA *s = XLNX_ZDMA(reg->opaque); + + if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL2, EN)) { + s->error = false; + + if (s->state == PAUSED && + ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) { + if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT_ADDR) == 1) { + zdma_update_descr_addr_from_start(s); + } else { + bool src_type = FIELD_EX32(s->dsc_src.words[3], + ZDMA_CH_SRC_DSCR_WORD3, TYPE); + zdma_update_descr_addr(s, src_type, + R_ZDMA_CH_SRC_CUR_DSCR_LSB); + } + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL0, CONT, false); + zdma_set_state(s, ENABLED); + } else if (s->state == DISABLED) { + zdma_update_descr_addr_from_start(s); + zdma_set_state(s, ENABLED); + } + } else { + /* Leave Paused state? */ + if (s->state == PAUSED && + ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) { + zdma_set_state(s, DISABLED); + } + } + + zdma_run(s); +} + +static RegisterAccessInfo zdma_regs_info[] = { + { .name = "ZDMA_ERR_CTRL", .addr = A_ZDMA_ERR_CTRL, + .rsvd = 0xfffffffe, + },{ .name = "ZDMA_CH_ISR", .addr = A_ZDMA_CH_ISR, + .rsvd = 0xfffff000, + .w1c = 0xfff, + .post_write = zdma_ch_isr_postw, + },{ .name = "ZDMA_CH_IMR", .addr = A_ZDMA_CH_IMR, + .reset = 0xfff, + .rsvd = 0xfffff000, + .ro = 0xfff, + },{ .name = "ZDMA_CH_IEN", .addr = A_ZDMA_CH_IEN, + .rsvd = 0xfffff000, + .pre_write = zdma_ch_ien_prew, + },{ .name = "ZDMA_CH_IDS", .addr = A_ZDMA_CH_IDS, + .rsvd = 0xfffff000, + .pre_write = zdma_ch_ids_prew, + },{ .name = "ZDMA_CH_CTRL0", .addr = A_ZDMA_CH_CTRL0, + .reset = 0x80, + .rsvd = 0xffffff01, + .post_write = zdma_ch_ctrlx_postw, + },{ .name = "ZDMA_CH_CTRL1", .addr = A_ZDMA_CH_CTRL1, + .reset = 0x3ff, + .rsvd = 0xfffffc00, + },{ .name = "ZDMA_CH_FCI", .addr = A_ZDMA_CH_FCI, + .rsvd = 0xffffffc0, + },{ .name = "ZDMA_CH_STATUS", .addr = A_ZDMA_CH_STATUS, + .rsvd = 0xfffffffc, + .ro = 0x3, + },{ .name = "ZDMA_CH_DATA_ATTR", .addr = A_ZDMA_CH_DATA_ATTR, + .reset = 0x483d20f, + .rsvd = 0xf0000000, + },{ .name = "ZDMA_CH_DSCR_ATTR", .addr = A_ZDMA_CH_DSCR_ATTR, + .rsvd = 0xfffffe00, + },{ .name = "ZDMA_CH_SRC_DSCR_WORD0", .addr = A_ZDMA_CH_SRC_DSCR_WORD0, + },{ .name = "ZDMA_CH_SRC_DSCR_WORD1", .addr = A_ZDMA_CH_SRC_DSCR_WORD1, + .rsvd = 0xfffe0000, + },{ .name = "ZDMA_CH_SRC_DSCR_WORD2", .addr = A_ZDMA_CH_SRC_DSCR_WORD2, + .rsvd = 0xc0000000, + },{ .name = "ZDMA_CH_SRC_DSCR_WORD3", .addr = A_ZDMA_CH_SRC_DSCR_WORD3, + .rsvd = 0xffffffe0, + },{ .name = "ZDMA_CH_DST_DSCR_WORD0", .addr = A_ZDMA_CH_DST_DSCR_WORD0, + },{ .name = "ZDMA_CH_DST_DSCR_WORD1", .addr = A_ZDMA_CH_DST_DSCR_WORD1, + .rsvd = 0xfffe0000, + },{ .name = "ZDMA_CH_DST_DSCR_WORD2", .addr = A_ZDMA_CH_DST_DSCR_WORD2, + .rsvd = 0xc0000000, + },{ .name = "ZDMA_CH_DST_DSCR_WORD3", .addr = A_ZDMA_CH_DST_DSCR_WORD3, + .rsvd = 0xfffffffa, + },{ .name = "ZDMA_CH_WR_ONLY_WORD0", .addr = A_ZDMA_CH_WR_ONLY_WORD0, + },{ .name = "ZDMA_CH_WR_ONLY_WORD1", .addr = A_ZDMA_CH_WR_ONLY_WORD1, + },{ .name = "ZDMA_CH_WR_ONLY_WORD2", .addr = A_ZDMA_CH_WR_ONLY_WORD2, + },{ .name = "ZDMA_CH_WR_ONLY_WORD3", .addr = A_ZDMA_CH_WR_ONLY_WORD3, + },{ .name = "ZDMA_CH_SRC_START_LSB", .addr = A_ZDMA_CH_SRC_START_LSB, + },{ .name = "ZDMA_CH_SRC_START_MSB", .addr = A_ZDMA_CH_SRC_START_MSB, + .rsvd = 0xfffe0000, + },{ .name = "ZDMA_CH_DST_START_LSB", .addr = A_ZDMA_CH_DST_START_LSB, + },{ .name = "ZDMA_CH_DST_START_MSB", .addr = A_ZDMA_CH_DST_START_MSB, + .rsvd = 0xfffe0000, + },{ .name = "ZDMA_CH_SRC_CUR_PYLD_LSB", .addr = A_ZDMA_CH_SRC_CUR_PYLD_LSB, + .ro = 0xffffffff, + },{ .name = "ZDMA_CH_SRC_CUR_PYLD_MSB", .addr = A_ZDMA_CH_SRC_CUR_PYLD_MSB, + .rsvd = 0xfffe0000, + .ro = 0x1ffff, + },{ .name = "ZDMA_CH_DST_CUR_PYLD_LSB", .addr = A_ZDMA_CH_DST_CUR_PYLD_LSB, + .ro = 0xffffffff, + },{ .name = "ZDMA_CH_DST_CUR_PYLD_MSB", .addr = A_ZDMA_CH_DST_CUR_PYLD_MSB, + .rsvd = 0xfffe0000, + .ro = 0x1ffff, + },{ .name = "ZDMA_CH_SRC_CUR_DSCR_LSB", .addr = A_ZDMA_CH_SRC_CUR_DSCR_LSB, + .ro = 0xffffffff, + },{ .name = "ZDMA_CH_SRC_CUR_DSCR_MSB", .addr = A_ZDMA_CH_SRC_CUR_DSCR_MSB, + .rsvd = 0xfffe0000, + .ro = 0x1ffff, + },{ .name = "ZDMA_CH_DST_CUR_DSCR_LSB", .addr = A_ZDMA_CH_DST_CUR_DSCR_LSB, + .ro = 0xffffffff, + },{ .name = "ZDMA_CH_DST_CUR_DSCR_MSB", .addr = A_ZDMA_CH_DST_CUR_DSCR_MSB, + .rsvd = 0xfffe0000, + .ro = 0x1ffff, + },{ .name = "ZDMA_CH_TOTAL_BYTE", .addr = A_ZDMA_CH_TOTAL_BYTE, + .w1c = 0xffffffff, + },{ .name = "ZDMA_CH_RATE_CNTL", .addr = A_ZDMA_CH_RATE_CNTL, + .rsvd = 0xfffff000, + },{ .name = "ZDMA_CH_IRQ_SRC_ACCT", .addr = A_ZDMA_CH_IRQ_SRC_ACCT, + .rsvd = 0xffffff00, + .ro = 0xff, + .cor = 0xff, + },{ .name = "ZDMA_CH_IRQ_DST_ACCT", .addr = A_ZDMA_CH_IRQ_DST_ACCT, + .rsvd = 0xffffff00, + .ro = 0xff, + .cor = 0xff, + },{ .name = "ZDMA_CH_DBG0", .addr = A_ZDMA_CH_DBG0, + .rsvd = 0xfffffe00, + .ro = 0x1ff, + },{ .name = "ZDMA_CH_DBG1", .addr = A_ZDMA_CH_DBG1, + .rsvd = 0xfffffe00, + .ro = 0x1ff, + },{ .name = "ZDMA_CH_CTRL2", .addr = A_ZDMA_CH_CTRL2, + .rsvd = 0xfffffffe, + .post_write = zdma_ch_ctrlx_postw, + } +}; + +static void zdma_reset(DeviceState *dev) +{ + XlnxZDMA *s = XLNX_ZDMA(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + + zdma_ch_imr_update_irq(s); +} + +static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size) +{ + XlnxZDMA *s = XLNX_ZDMA(opaque); + RegisterInfo *r = &s->regs_info[addr / 4]; + + if (!r->data) { + gchar *path = object_get_canonical_path(OBJECT(s)); + qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n", + path, + addr); + g_free(path); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); + zdma_ch_imr_update_irq(s); + return 0; + } + return register_read(r, ~0, NULL, false); +} + +static void zdma_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + XlnxZDMA *s = XLNX_ZDMA(opaque); + RegisterInfo *r = &s->regs_info[addr / 4]; + + if (!r->data) { + gchar *path = object_get_canonical_path(OBJECT(s)); + qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n", + path, + addr, value); + g_free(path); + ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true); + zdma_ch_imr_update_irq(s); + return; + } + register_write(r, value, ~0, NULL, false); +} + +static const MemoryRegionOps zdma_ops = { + .read = zdma_read, + .write = zdma_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void zdma_realize(DeviceState *dev, Error **errp) +{ + XlnxZDMA *s = XLNX_ZDMA(dev); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(zdma_regs_info); ++i) { + RegisterInfo *r = &s->regs_info[zdma_regs_info[i].addr / 4]; + + *r = (RegisterInfo) { + .data = (uint8_t *)&s->regs[ + zdma_regs_info[i].addr / 4], + .data_size = sizeof(uint32_t), + .access = &zdma_regs_info[i], + .opaque = s, + }; + } + + if (s->dma_mr) { + s->dma_as = g_malloc0(sizeof(AddressSpace)); + address_space_init(s->dma_as, s->dma_mr, NULL); + } else { + s->dma_as = &address_space_memory; + } + s->attr = MEMTXATTRS_UNSPECIFIED; +} + +static void zdma_init(Object *obj) +{ + XlnxZDMA *s = XLNX_ZDMA(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &zdma_ops, s, + TYPE_XLNX_ZDMA, ZDMA_R_MAX * 4); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq_zdma_ch_imr); + + object_property_add_link(obj, "dma", TYPE_MEMORY_REGION, + (Object **)&s->dma_mr, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG, + &error_abort); +} + +static const VMStateDescription vmstate_zdma = { + .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), + VMSTATE_UINT32_ARRAY(dsc_src.words, XlnxZDMA, 4), + VMSTATE_UINT32_ARRAY(dsc_dst.words, XlnxZDMA, 4), + VMSTATE_END_OF_LIST(), + } +}; + +static Property zdma_props[] = { + DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64), + DEFINE_PROP_END_OF_LIST(), +}; + +static void zdma_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = zdma_reset; + dc->realize = zdma_realize; + dc->props = zdma_props; + dc->vmsd = &vmstate_zdma; +} + +static const TypeInfo zdma_info = { + .name = TYPE_XLNX_ZDMA, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxZDMA), + .class_init = zdma_class_init, + .instance_init = zdma_init, +}; + +static void zdma_register_types(void) +{ + type_register_static(&zdma_info); +} + +type_init(zdma_register_types) diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c index 4c203ef5c6..a560e3afd2 100644 --- a/hw/gpio/max7310.c +++ b/hw/gpio/max7310.c @@ -182,14 +182,13 @@ static void max7310_gpio_set(void *opaque, int line, int level) /* MAX7310 is SMBus-compatible (can be used with only SMBus protocols), * but also accepts sequences that are not SMBus so return an I2C device. */ -static int max7310_init(I2CSlave *i2c) +static void max7310_realize(DeviceState *dev, Error **errp) { - MAX7310State *s = MAX7310(i2c); + I2CSlave *i2c = I2C_SLAVE(dev); + MAX7310State *s = MAX7310(dev); qdev_init_gpio_in(&i2c->qdev, max7310_gpio_set, 8); qdev_init_gpio_out(&i2c->qdev, s->handler, 8); - - return 0; } static void max7310_class_init(ObjectClass *klass, void *data) @@ -197,7 +196,7 @@ static void max7310_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = max7310_init; + dc->realize = max7310_realize; k->event = max7310_event; k->recv = max7310_rx; k->send = max7310_tx; diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c index 15aefde09c..564b938e3a 100644 --- a/hw/hppa/dino.c +++ b/hw/hppa/dino.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" #include "hw/hw.h" @@ -76,7 +77,7 @@ /* #define xxx 0x200 - bit 9 not used */ #define RS232INT 0x400 -#define DINO_MEM_CHUNK_SIZE (8 * 1024 * 1024) /* 8MB */ +#define DINO_MEM_CHUNK_SIZE (8 * MiB) #define DINO_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(DinoState, (obj), TYPE_DINO_PCI_HOST_BRIDGE) @@ -137,7 +138,8 @@ static void gsc_to_pci_forwarding(DinoState *s) } static bool dino_chip_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { switch (addr) { case DINO_IAR0: @@ -403,13 +405,10 @@ static void dino_set_irq(void *opaque, int irq, int level) static int dino_pci_map_irq(PCIDevice *d, int irq_num) { int slot = d->devfn >> 3; - int local_irq; assert(irq_num >= 0 && irq_num <= 3); - local_irq = slot & 0x03; - - return local_irq; + return slot & 0x03; } static void dino_set_timer_irq(void *opaque, int irq, int level) diff --git a/hw/hppa/hppa_sys.h b/hw/hppa/hppa_sys.h index a182d1f34e..f5f983bf4c 100644 --- a/hw/hppa/hppa_sys.h +++ b/hw/hppa/hppa_sys.h @@ -3,14 +3,13 @@ #ifndef HW_HPPA_SYS_H #define HW_HPPA_SYS_H -#include "target/hppa/cpu-qom.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" #include "hw/ide.h" #include "hw/i386/pc.h" #include "hw/irq.h" -#include "hw/hppa/hppa_hardware.h" +#include "hppa_hardware.h" PCIBus *dino_init(MemoryRegion *, qemu_irq *, qemu_irq *); diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 19033e268d..cf7c61c6cc 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -16,8 +16,8 @@ #include "hw/ide.h" #include "hw/timer/i8254.h" #include "hw/char/serial.h" -#include "hw/hppa/hppa_sys.h" -#include "qemu/cutils.h" +#include "hppa_sys.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/log.h" @@ -108,10 +108,10 @@ static void machine_hppa_init(MachineState *machine) mc146818_rtc_init(isa_bus, 2000, rtc_irq); /* Serial code setup. */ - if (serial_hds[0]) { + if (serial_hd(0)) { uint32_t addr = DINO_UART_HPA + 0x800; serial_mm_init(addr_space, addr, 0, serial_irq, - 115200, serial_hds[0], DEVICE_BIG_ENDIAN); + 115200, serial_hd(0), DEVICE_BIG_ENDIAN); } /* SCSI disk setup. */ @@ -178,8 +178,8 @@ static void machine_hppa_init(MachineState *machine) } qemu_log_mask(CPU_LOG_PAGE, "Kernel loaded at 0x%08" PRIx64 "-0x%08" PRIx64 ", entry at 0x%08" PRIx64 - ", size %ld kB.\n", - kernel_low, kernel_high, kernel_entry, size / 1024); + ", size %" PRIu64 " kB\n", + kernel_low, kernel_high, kernel_entry, size / KiB); if (kernel_cmdline) { cpu[0]->env.gr[24] = 0x4000; @@ -203,8 +203,8 @@ static void machine_hppa_init(MachineState *machine) (1) Due to sign-extension problems and PDC, put the initrd no higher than 1G. (2) Reserve 64k for stack. */ - initrd_base = MIN(ram_size, 1024 * 1024 * 1024); - initrd_base = initrd_base - 64 * 1024; + initrd_base = MIN(ram_size, 1 * GiB); + initrd_base = initrd_base - 64 * KiB; initrd_base = (initrd_base - initrd_size) & TARGET_PAGE_MASK; if (initrd_base < kernel_high) { @@ -275,7 +275,7 @@ static void machine_hppa_machine_init(MachineClass *mc) mc->max_cpus = HPPA_MAX_CPUS; mc->default_cpus = 1; mc->is_default = 1; - mc->default_ram_size = 512 * M_BYTE; + mc->default_ram_size = 512 * MiB; mc->default_boot_order = "cd"; } diff --git a/hw/i2c/core.c b/hw/i2c/core.c index cfccefca3d..b54725985a 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" +#include "trace.h" #define I2C_BROADCAST 0x00 @@ -130,14 +131,16 @@ int i2c_start_transfer(I2CBus *bus, uint8_t address, int recv) } QLIST_FOREACH(node, &bus->current_devs, next) { + I2CSlave *s = node->elt; int rv; - sc = I2C_SLAVE_GET_CLASS(node->elt); + sc = I2C_SLAVE_GET_CLASS(s); /* If the bus is already busy, assume this is a repeated start condition. */ if (sc->event) { - rv = sc->event(node->elt, recv ? I2C_START_RECV : I2C_START_SEND); + trace_i2c_event("start", s->address); + rv = sc->event(s, recv ? I2C_START_RECV : I2C_START_SEND); if (rv && !bus->broadcast) { if (bus_scanned) { /* First call, terminate the transfer. */ @@ -156,9 +159,11 @@ void i2c_end_transfer(I2CBus *bus) I2CNode *node, *next; QLIST_FOREACH_SAFE(node, &bus->current_devs, next, next) { - sc = I2C_SLAVE_GET_CLASS(node->elt); + I2CSlave *s = node->elt; + sc = I2C_SLAVE_GET_CLASS(s); if (sc->event) { - sc->event(node->elt, I2C_FINISH); + trace_i2c_event("finish", s->address); + sc->event(s, I2C_FINISH); } QLIST_REMOVE(node, next); g_free(node); @@ -169,14 +174,17 @@ void i2c_end_transfer(I2CBus *bus) int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send) { I2CSlaveClass *sc; + I2CSlave *s; I2CNode *node; int ret = 0; if (send) { QLIST_FOREACH(node, &bus->current_devs, next) { - sc = I2C_SLAVE_GET_CLASS(node->elt); + s = node->elt; + sc = I2C_SLAVE_GET_CLASS(s); if (sc->send) { - ret = ret || sc->send(node->elt, *data); + trace_i2c_send(s->address, *data); + ret = ret || sc->send(s, *data); } else { ret = -1; } @@ -189,7 +197,9 @@ int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send) sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt); if (sc->recv) { - ret = sc->recv(QLIST_FIRST(&bus->current_devs)->elt); + s = QLIST_FIRST(&bus->current_devs)->elt; + ret = sc->recv(s); + trace_i2c_recv(s->address, ret); if (ret < 0) { return ret; } else { @@ -226,6 +236,7 @@ void i2c_nack(I2CBus *bus) QLIST_FOREACH(node, &bus->current_devs, next) { sc = I2C_SLAVE_GET_CLASS(node->elt); if (sc->event) { + trace_i2c_event("nack", node->elt->address); sc->event(node->elt, I2C_NACK); } } @@ -258,18 +269,6 @@ const VMStateDescription vmstate_i2c_slave = { } }; -static int i2c_slave_qdev_init(DeviceState *dev) -{ - I2CSlave *s = I2C_SLAVE(dev); - I2CSlaveClass *sc = I2C_SLAVE_GET_CLASS(s); - - if (sc->init) { - return sc->init(s); - } - - return 0; -} - DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr) { DeviceState *dev; @@ -283,7 +282,6 @@ DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr) static void i2c_slave_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); - k->init = i2c_slave_qdev_init; set_bit(DEVICE_CATEGORY_MISC, k->categories); k->bus_type = TYPE_I2C_BUS; k->props = i2c_props; diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c index 26e3e5ebf6..d02e734ea8 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -17,6 +17,7 @@ * with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/i2c/i2c.h" #include "hw/arm/omap.h" @@ -339,14 +340,15 @@ static void omap_i2c_write(void *opaque, hwaddr addr, } break; } - if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */ - fprintf(stderr, "%s: I^2C slave mode not supported\n", - __func__); + if ((value & (1 << 15)) && !(value & (1 << 10))) { /* MST */ + qemu_log_mask(LOG_UNIMP, "%s: I^2C slave mode not supported\n", + __func__); break; } - if ((value & (1 << 15)) && value & (1 << 8)) { /* XA */ - fprintf(stderr, "%s: 10-bit addressing mode not supported\n", - __func__); + if ((value & (1 << 15)) && value & (1 << 8)) { /* XA */ + qemu_log_mask(LOG_UNIMP, + "%s: 10-bit addressing mode not supported\n", + __func__); break; } if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ @@ -392,8 +394,10 @@ static void omap_i2c_write(void *opaque, hwaddr addr, s->stat |= 0x3f; omap_i2c_interrupts_update(s); } - if (value & (1 << 15)) /* ST_EN */ - fprintf(stderr, "%s: System Test not supported\n", __func__); + if (value & (1 << 15)) { /* ST_EN */ + qemu_log_mask(LOG_UNIMP, + "%s: System Test not supported\n", __func__); + } break; default: diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index ab64d196be..d6dfafab31 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -3,7 +3,7 @@ * * Copyright (c) 2007 Jocelyn Mayer * Copyright (c) 2012 François Revol - * Copyright (c) 2016 BALATON Zoltan + * Copyright (c) 2016-2018 BALATON Zoltan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,167 +30,156 @@ #include "cpu.h" #include "hw/hw.h" #include "hw/i2c/ppc4xx_i2c.h" +#include "bitbang_i2c.h" -#define PPC4xx_I2C_MEM_SIZE 0x12 +#define PPC4xx_I2C_MEM_SIZE 18 + +enum { + IIC_MDBUF = 0, + /* IIC_SDBUF = 2, */ + IIC_LMADR = 4, + IIC_HMADR, + IIC_CNTL, + IIC_MDCNTL, + IIC_STS, + IIC_EXTSTS, + IIC_LSADR, + IIC_HSADR, + IIC_CLKDIV, + IIC_INTRMSK, + IIC_XFRCNT, + IIC_XTCNTLSS, + IIC_DIRECTCNTL + /* IIC_INTR */ +}; #define IIC_CNTL_PT (1 << 0) #define IIC_CNTL_READ (1 << 1) #define IIC_CNTL_CHT (1 << 2) #define IIC_CNTL_RPST (1 << 3) +#define IIC_CNTL_AMD (1 << 6) +#define IIC_CNTL_HMT (1 << 7) + +#define IIC_MDCNTL_EINT (1 << 2) +#define IIC_MDCNTL_ESM (1 << 3) +#define IIC_MDCNTL_FMDB (1 << 6) #define IIC_STS_PT (1 << 0) +#define IIC_STS_IRQA (1 << 1) #define IIC_STS_ERR (1 << 2) +#define IIC_STS_MDBF (1 << 4) #define IIC_STS_MDBS (1 << 5) #define IIC_EXTSTS_XFRA (1 << 0) +#define IIC_EXTSTS_BCS_FREE (4 << 4) +#define IIC_EXTSTS_BCS_BUSY (5 << 4) + +#define IIC_INTRMSK_EIMTC (1 << 0) +#define IIC_INTRMSK_EITA (1 << 1) +#define IIC_INTRMSK_EIIC (1 << 2) +#define IIC_INTRMSK_EIHE (1 << 3) #define IIC_XTCNTLSS_SRST (1 << 0) +#define IIC_DIRECTCNTL_SDAC (1 << 3) +#define IIC_DIRECTCNTL_SCLC (1 << 2) +#define IIC_DIRECTCNTL_MSDA (1 << 1) +#define IIC_DIRECTCNTL_MSCL (1 << 0) + static void ppc4xx_i2c_reset(DeviceState *s) { PPC4xxI2CState *i2c = PPC4xx_I2C(s); - /* FIXME: Should also reset bus? - *if (s->address != ADDR_RESET) { - * i2c_end_transfer(s->bus); - *} - */ - - i2c->mdata = 0; - i2c->lmadr = 0; - i2c->hmadr = 0; + i2c->mdidx = -1; + memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata)); + /* [hl][ms]addr are not affected by reset */ i2c->cntl = 0; i2c->mdcntl = 0; i2c->sts = 0; - i2c->extsts = 0x8f; - i2c->sdata = 0; - i2c->lsadr = 0; - i2c->hsadr = 0; + i2c->extsts = IIC_EXTSTS_BCS_FREE; i2c->clkdiv = 0; i2c->intrmsk = 0; i2c->xfrcnt = 0; i2c->xtcntlss = 0; - i2c->directcntl = 0x0f; - i2c->intr = 0; -} - -static inline bool ppc4xx_i2c_is_master(PPC4xxI2CState *i2c) -{ - return true; + i2c->directcntl = 0xf; /* all non-reserved bits set */ } static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) { PPC4xxI2CState *i2c = PPC4xx_I2C(opaque); uint64_t ret; + int i; switch (addr) { - case 0x00: - ret = i2c->mdata; - if (ppc4xx_i2c_is_master(i2c)) { + case IIC_MDBUF: + if (i2c->mdidx < 0) { ret = 0xff; - - if (!(i2c->sts & IIC_STS_MDBS)) { - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read " - "without starting transfer\n", - TYPE_PPC4xx_I2C, __func__); - } else { - int pending = (i2c->cntl >> 4) & 3; - - /* get the next byte */ - int byte = i2c_recv(i2c->bus); - - if (byte < 0) { - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed " - "for device 0x%02x\n", TYPE_PPC4xx_I2C, - __func__, i2c->lmadr); - ret = 0xff; - } else { - ret = byte; - /* Raise interrupt if enabled */ - /*ppc4xx_i2c_raise_interrupt(i2c)*/; - } - - if (!pending) { - i2c->sts &= ~IIC_STS_MDBS; - /*i2c_end_transfer(i2c->bus);*/ - /*} else if (i2c->cntl & (IIC_CNTL_RPST | IIC_CNTL_CHT)) {*/ - } else if (pending) { - /* current smbus implementation doesn't like - multibyte xfer repeated start */ - i2c_end_transfer(i2c->bus); - if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 1)) { - /* if non zero is returned, the adress is not valid */ - i2c->sts &= ~IIC_STS_PT; - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - } else { - /*i2c->sts |= IIC_STS_PT;*/ - i2c->sts |= IIC_STS_MDBS; - i2c->sts &= ~IIC_STS_ERR; - i2c->extsts = 0; - } - } - pending--; - i2c->cntl = (i2c->cntl & 0xcf) | (pending << 4); - } - } else { - qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n", - TYPE_PPC4xx_I2C, __func__); + break; + } + ret = i2c->mdata[0]; + if (i2c->mdidx == 3) { + i2c->sts &= ~IIC_STS_MDBF; + } else if (i2c->mdidx == 0) { + i2c->sts &= ~IIC_STS_MDBS; + } + for (i = 0; i < i2c->mdidx; i++) { + i2c->mdata[i] = i2c->mdata[i + 1]; + } + if (i2c->mdidx >= 0) { + i2c->mdidx--; } break; - case 0x02: - ret = i2c->sdata; - break; - case 0x04: + case IIC_LMADR: ret = i2c->lmadr; break; - case 0x05: + case IIC_HMADR: ret = i2c->hmadr; break; - case 0x06: + case IIC_CNTL: ret = i2c->cntl; break; - case 0x07: + case IIC_MDCNTL: ret = i2c->mdcntl; break; - case 0x08: + case IIC_STS: ret = i2c->sts; break; - case 0x09: - ret = i2c->extsts; + case IIC_EXTSTS: + ret = i2c_bus_busy(i2c->bus) ? + IIC_EXTSTS_BCS_BUSY : IIC_EXTSTS_BCS_FREE; break; - case 0x0A: + case IIC_LSADR: ret = i2c->lsadr; break; - case 0x0B: + case IIC_HSADR: ret = i2c->hsadr; break; - case 0x0C: + case IIC_CLKDIV: ret = i2c->clkdiv; break; - case 0x0D: + case IIC_INTRMSK: ret = i2c->intrmsk; break; - case 0x0E: + case IIC_XFRCNT: ret = i2c->xfrcnt; break; - case 0x0F: + case IIC_XTCNTLSS: ret = i2c->xtcntlss; break; - case 0x10: + case IIC_DIRECTCNTL: ret = i2c->directcntl; break; - case 0x11: - ret = i2c->intr; - break; default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" - HWADDR_PRIx "\n", TYPE_PPC4xx_I2C, __func__, addr); + if (addr < PPC4xx_I2C_MEM_SIZE) { + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address 0x%" + HWADDR_PRIx "\n", __func__, addr); + } ret = 0; break; } - return ret; } @@ -200,111 +189,142 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, PPC4xxI2CState *i2c = opaque; switch (addr) { - case 0x00: - i2c->mdata = value; - if (!i2c_bus_busy(i2c->bus)) { - /* assume we start a write transfer */ - if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 0)) { - /* if non zero is returned, the adress is not valid */ - i2c->sts &= ~IIC_STS_PT; - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - } else { - i2c->sts |= IIC_STS_PT; - i2c->sts &= ~IIC_STS_ERR; - i2c->extsts = 0; - } + case IIC_MDBUF: + if (i2c->mdidx >= 3) { + break; } - if (i2c_bus_busy(i2c->bus)) { - if (i2c_send(i2c->bus, i2c->mdata)) { - /* if the target return non zero then end the transfer */ - i2c->sts &= ~IIC_STS_PT; - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - i2c_end_transfer(i2c->bus); - } + i2c->mdata[++i2c->mdidx] = value; + if (i2c->mdidx == 3) { + i2c->sts |= IIC_STS_MDBF; + } else if (i2c->mdidx == 0) { + i2c->sts |= IIC_STS_MDBS; } break; - case 0x02: - i2c->sdata = value; - break; - case 0x04: + case IIC_LMADR: i2c->lmadr = value; - if (i2c_bus_busy(i2c->bus)) { - i2c_end_transfer(i2c->bus); - } break; - case 0x05: + case IIC_HMADR: i2c->hmadr = value; break; - case 0x06: - i2c->cntl = value; - if (i2c->cntl & IIC_CNTL_PT) { - if (i2c->cntl & IIC_CNTL_READ) { - if (i2c_bus_busy(i2c->bus)) { - /* end previous transfer */ - i2c->sts &= ~IIC_STS_PT; - i2c_end_transfer(i2c->bus); + case IIC_CNTL: + i2c->cntl = value & ~IIC_CNTL_PT; + if (value & IIC_CNTL_AMD) { + qemu_log_mask(LOG_UNIMP, "%s: only 7 bit addresses supported\n", + __func__); + } + if (value & IIC_CNTL_HMT && i2c_bus_busy(i2c->bus)) { + i2c_end_transfer(i2c->bus); + if (i2c->mdcntl & IIC_MDCNTL_EINT && + i2c->intrmsk & IIC_INTRMSK_EIHE) { + i2c->sts |= IIC_STS_IRQA; + qemu_irq_raise(i2c->irq); + } + } else if (value & IIC_CNTL_PT) { + int recv = (value & IIC_CNTL_READ) >> 1; + int tct = value >> 4 & 3; + int i; + + if (recv && (i2c->lmadr >> 1) >= 0x50 && (i2c->lmadr >> 1) < 0x58) { + /* smbus emulation does not like multi byte reads w/o restart */ + value |= IIC_CNTL_RPST; + } + + for (i = 0; i <= tct; i++) { + if (!i2c_bus_busy(i2c->bus)) { + i2c->extsts = IIC_EXTSTS_BCS_FREE; + if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, recv)) { + i2c->sts |= IIC_STS_ERR; + i2c->extsts |= IIC_EXTSTS_XFRA; + break; + } else { + i2c->sts &= ~IIC_STS_ERR; + } } - if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 1)) { - /* if non zero is returned, the adress is not valid */ - i2c->sts &= ~IIC_STS_PT; + if (!(i2c->sts & IIC_STS_ERR) && + i2c_send_recv(i2c->bus, &i2c->mdata[i], !recv)) { i2c->sts |= IIC_STS_ERR; i2c->extsts |= IIC_EXTSTS_XFRA; - } else { - /*i2c->sts |= IIC_STS_PT;*/ - i2c->sts |= IIC_STS_MDBS; - i2c->sts &= ~IIC_STS_ERR; - i2c->extsts = 0; + break; } - } else { - /* we actually already did the write transfer... */ - i2c->sts &= ~IIC_STS_PT; + if (value & IIC_CNTL_RPST || !(value & IIC_CNTL_CHT)) { + i2c_end_transfer(i2c->bus); + } + } + i2c->xfrcnt = i; + i2c->mdidx = i - 1; + if (recv && i2c->mdidx >= 0) { + i2c->sts |= IIC_STS_MDBS; + } + if (recv && i2c->mdidx == 3) { + i2c->sts |= IIC_STS_MDBF; + } + if (i && i2c->mdcntl & IIC_MDCNTL_EINT && + i2c->intrmsk & IIC_INTRMSK_EIMTC) { + i2c->sts |= IIC_STS_IRQA; + qemu_irq_raise(i2c->irq); } } break; - case 0x07: - i2c->mdcntl = value & 0xDF; + case IIC_MDCNTL: + i2c->mdcntl = value & 0x3d; + if (value & IIC_MDCNTL_ESM) { + qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", + __func__); + } + if (value & IIC_MDCNTL_FMDB) { + i2c->mdidx = -1; + memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata)); + i2c->sts &= ~(IIC_STS_MDBF | IIC_STS_MDBS); + } break; - case 0x08: - i2c->sts &= ~(value & 0x0A); + case IIC_STS: + i2c->sts &= ~(value & 0x0a); + if (value & IIC_STS_IRQA && i2c->mdcntl & IIC_MDCNTL_EINT) { + qemu_irq_lower(i2c->irq); + } break; - case 0x09: - i2c->extsts &= ~(value & 0x8F); + case IIC_EXTSTS: + i2c->extsts &= ~(value & 0x8f); break; - case 0x0A: + case IIC_LSADR: i2c->lsadr = value; - /*i2c_set_slave_address(i2c->bus, i2c->lsadr);*/ break; - case 0x0B: + case IIC_HSADR: i2c->hsadr = value; break; - case 0x0C: + case IIC_CLKDIV: i2c->clkdiv = value; break; - case 0x0D: + case IIC_INTRMSK: i2c->intrmsk = value; break; - case 0x0E: + case IIC_XFRCNT: i2c->xfrcnt = value & 0x77; break; - case 0x0F: + case IIC_XTCNTLSS: + i2c->xtcntlss &= ~(value & 0xf0); if (value & IIC_XTCNTLSS_SRST) { /* Is it actually a full reset? U-Boot sets some regs before */ ppc4xx_i2c_reset(DEVICE(i2c)); break; } - i2c->xtcntlss = value; break; - case 0x10: - i2c->directcntl = value & 0x7; - break; - case 0x11: - i2c->intr = value; + case IIC_DIRECTCNTL: + i2c->directcntl = value & (IIC_DIRECTCNTL_SDAC & IIC_DIRECTCNTL_SCLC); + i2c->directcntl |= (value & IIC_DIRECTCNTL_SCLC ? 1 : 0); + bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SCL, + i2c->directcntl & IIC_DIRECTCNTL_MSCL); + i2c->directcntl |= bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SDA, + (value & IIC_DIRECTCNTL_SDAC) != 0) << 1; break; default: - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" - HWADDR_PRIx "\n", TYPE_PPC4xx_I2C, __func__, addr); + if (addr < PPC4xx_I2C_MEM_SIZE) { + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register 0x%" + HWADDR_PRIx "\n", __func__, addr); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address 0x%" + HWADDR_PRIx "\n", __func__, addr); + } break; } } @@ -328,6 +348,7 @@ static void ppc4xx_i2c_init(Object *o) sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); sysbus_init_irq(SYS_BUS_DEVICE(s), &s->irq); s->bus = i2c_init_bus(DEVICE(s), "i2c"); + s->bitbang = bitbang_i2c_init(s->bus); } static void ppc4xx_i2c_class_init(ObjectClass *klass, void *data) diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c index 2d1b79a689..587ce1ab7f 100644 --- a/hw/i2c/smbus.c +++ b/hw/i2c/smbus.c @@ -202,14 +202,6 @@ static int smbus_i2c_send(I2CSlave *s, uint8_t data) return 0; } -static int smbus_device_init(I2CSlave *i2c) -{ - SMBusDevice *dev = SMBUS_DEVICE(i2c); - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - - return sc->init(dev); -} - /* Master device commands. */ int smbus_quick_command(I2CBus *bus, uint8_t addr, int read) { @@ -350,7 +342,6 @@ static void smbus_device_class_init(ObjectClass *klass, void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - sc->init = smbus_device_init; sc->event = smbus_i2c_event; sc->recv = smbus_i2c_recv; sc->send = smbus_i2c_send; diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index 4062b592bb..986a2a1a30 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -97,12 +97,11 @@ static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n) return eeprom_receive_byte(dev); } -static int smbus_eeprom_initfn(SMBusDevice *dev) +static void smbus_eeprom_realize(DeviceState *dev, Error **errp) { SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev; eeprom->offset = 0; - return 0; } static Property smbus_eeprom_properties[] = { @@ -115,7 +114,7 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass); - sc->init = smbus_eeprom_initfn; + dc->realize = smbus_eeprom_realize; sc->quick_cmd = eeprom_quick_cmd; sc->send_byte = eeprom_send_byte; sc->receive_byte = eeprom_receive_byte; @@ -140,6 +139,16 @@ static void smbus_eeprom_register_types(void) type_init(smbus_eeprom_register_types) +void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf) +{ + DeviceState *dev; + + dev = qdev_create((BusState *) smbus, "smbus-eeprom"); + qdev_prop_set_uint8(dev, "address", address); + qdev_prop_set_ptr(dev, "data", eeprom_buf); + qdev_init_nofail(dev); +} + void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, const uint8_t *eeprom_spd, int eeprom_spd_size) { @@ -150,11 +159,7 @@ void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, } for (i = 0; i < nb_eeprom; i++) { - DeviceState *eeprom; - eeprom = qdev_create((BusState *)smbus, "smbus-eeprom"); - qdev_prop_set_uint8(eeprom, "address", 0x50 + i); - qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256)); - qdev_init_nofail(eeprom); + smbus_eeprom_init_one(smbus, 0x50 + i, eeprom_buf + (i * 256)); } } diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events new file mode 100644 index 0000000000..d339b61202 --- /dev/null +++ b/hw/i2c/trace-events @@ -0,0 +1,7 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# hw/i2c/core.c + +i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)" +i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x" +i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 30c6a061e6..974f223850 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -46,6 +46,7 @@ #include "hw/acpi/vmgenid.h" #include "sysemu/tpm_backend.h" #include "hw/timer/mc146818rtc_regs.h" +#include "hw/mem/memory-device.h" #include "sysemu/numa.h" /* Supported chipsets: */ @@ -201,21 +202,21 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) } else { pm->s3_disabled = false; } - qobject_decref(o); + qobject_unref(o); o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL); if (o) { pm->s4_disabled = qnum_get_uint(qobject_to(QNum, o)); } else { pm->s4_disabled = false; } - qobject_decref(o); + qobject_unref(o); o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL); if (o) { pm->s4_val = qnum_get_uint(qobject_to(QNum, o)); } else { pm->s4_val = false; } - qobject_decref(o); + qobject_unref(o); pm->pcihp_bridge_en = object_property_get_bool(obj, "acpi-pci-hotplug-with-bridge-support", @@ -573,7 +574,7 @@ static void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus, } } aml_append(parent_scope, method); - qobject_decref(bsel); + qobject_unref(bsel); } /** @@ -2250,13 +2251,13 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL, NULL); } -#define HOLE_640K_START (640 * 1024) -#define HOLE_640K_END (1024 * 1024) +#define HOLE_640K_START (640 * KiB) +#define HOLE_640K_END (1 * MiB) static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base, uint64_t len, int default_node) { - MemoryDeviceInfoList *info_list = qmp_pc_dimm_device_list(); + MemoryDeviceInfoList *info_list = qmp_memory_device_list(); MemoryDeviceInfoList *info; MemoryDeviceInfo *mi; PCDIMMDeviceInfo *di; @@ -2271,7 +2272,16 @@ static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base, numamem = acpi_data_push(table_data, sizeof *numamem); if (!info) { - build_srat_memory(numamem, cur, end - cur, default_node, + /* + * Entry is required for Windows to enable memory hotplug in OS + * and for Linux to enable SWIOTLB when booted with less than + * 4G of RAM. Windows works better if the entry sets proximity + * to the highest NUMA node in the machine at the end of the + * reserved space. + * Memory devices may override proximity set by this entry, + * providing _PXM method if necessary. + */ + build_srat_memory(numamem, end - 1, 1, default_node, MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); break; } @@ -2315,7 +2325,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine); PCMachineState *pcms = PC_MACHINE(machine); ram_addr_t hotplugabble_address_space_size = - object_property_get_int(OBJECT(pcms), PC_MACHINE_MEMHP_REGION_SIZE, + object_property_get_int(OBJECT(pcms), PC_MACHINE_DEVMEM_REGION_SIZE, NULL); srat_start = table_data->len; @@ -2394,9 +2404,12 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) mem_len = next_base - pcms->below_4g_mem_size; next_base = mem_base + mem_len; } - numamem = acpi_data_push(table_data, sizeof *numamem); - build_srat_memory(numamem, mem_base, mem_len, i - 1, - MEM_AFFINITY_ENABLED); + + if (mem_len > 0) { + numamem = acpi_data_push(table_data, sizeof *numamem); + build_srat_memory(numamem, mem_base, mem_len, i - 1, + MEM_AFFINITY_ENABLED); + } } slots = (table_data->len - numa_start) / sizeof *numamem; for (; slots < pcms->numa_nodes + 2; slots++) { @@ -2404,16 +2417,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); } - /* - * Entry is required for Windows to enable memory hotplug in OS - * and for Linux to enable SWIOTLB when booted with less than - * 4G of RAM. Windows works better if the entry sets proximity - * to the highest NUMA node in the machine. - * Memory devices may override proximity set by this entry, - * providing _PXM method if necessary. - */ if (hotplugabble_address_space_size) { - build_srat_hotpluggable_memory(table_data, pcms->hotplug_memory.base, + build_srat_hotpluggable_memory(table_data, machine->device_memory->base, hotplugabble_address_space_size, pcms->numa_nodes - 1); } @@ -2539,7 +2544,7 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker) (1UL << 7), /* PPRSup */ 1); /* IVHD length */ - build_append_int_noprefix(table_data, 0x24, 2); + build_append_int_noprefix(table_data, 28, 2); /* DeviceID */ build_append_int_noprefix(table_data, s->devid, 2); /* Capability offset */ @@ -2617,12 +2622,12 @@ static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) return false; } mcfg->mcfg_base = qnum_get_uint(qobject_to(QNum, o)); - qobject_decref(o); + qobject_unref(o); o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL); assert(o); mcfg->mcfg_size = qnum_get_uint(qobject_to(QNum, o)); - qobject_decref(o); + qobject_unref(o); return true; } diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 63d46ff6ee..1fd669fef8 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -991,7 +991,7 @@ static inline bool amdvi_is_interrupt_addr(hwaddr addr) } static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { AMDVIAddressSpace *as = container_of(iommu, AMDVIAddressSpace, iommu); AMDVIState *s = as->iommu_state; diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index aeef802364..874030582d 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -165,8 +165,8 @@ #define AMDVI_DTE_UPPER_QUAD_RESERVED 0x08f0000000000000 /* AMDVI paging mode */ -#define AMDVI_GATS_MODE (6ULL << 12) -#define AMDVI_HATS_MODE (6ULL << 10) +#define AMDVI_GATS_MODE (2ULL << 12) +#define AMDVI_HATS_MODE (2ULL << 10) /* IOTLB */ #define AMDVI_IOTLB_MAX_SIZE 1024 diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index fb31de9416..0a8cd4e9cc 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -128,6 +128,22 @@ static uint64_t vtd_set_clear_mask_quad(IntelIOMMUState *s, hwaddr addr, return new_val; } +static inline void vtd_iommu_lock(IntelIOMMUState *s) +{ + qemu_mutex_lock(&s->iommu_lock); +} + +static inline void vtd_iommu_unlock(IntelIOMMUState *s) +{ + qemu_mutex_unlock(&s->iommu_lock); +} + +/* Whether the address space needs to notify new mappings */ +static inline gboolean vtd_as_has_map_notifier(VTDAddressSpace *as) +{ + return as->notifier_flags & IOMMU_NOTIFIER_MAP; +} + /* GHashTable functions */ static gboolean vtd_uint64_equal(gconstpointer v1, gconstpointer v2) { @@ -172,9 +188,9 @@ static gboolean vtd_hash_remove_by_page(gpointer key, gpointer value, } /* Reset all the gen of VTDAddressSpace to zero and set the gen of - * IntelIOMMUState to 1. + * IntelIOMMUState to 1. Must be called with IOMMU lock held. */ -static void vtd_reset_context_cache(IntelIOMMUState *s) +static void vtd_reset_context_cache_locked(IntelIOMMUState *s) { VTDAddressSpace *vtd_as; VTDBus *vtd_bus; @@ -197,12 +213,20 @@ static void vtd_reset_context_cache(IntelIOMMUState *s) s->context_cache_gen = 1; } -static void vtd_reset_iotlb(IntelIOMMUState *s) +/* Must be called with IOMMU lock held. */ +static void vtd_reset_iotlb_locked(IntelIOMMUState *s) { assert(s->iotlb); g_hash_table_remove_all(s->iotlb); } +static void vtd_reset_iotlb(IntelIOMMUState *s) +{ + vtd_iommu_lock(s); + vtd_reset_iotlb_locked(s); + vtd_iommu_unlock(s); +} + static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id, uint32_t level) { @@ -215,6 +239,7 @@ static uint64_t vtd_get_iotlb_gfn(hwaddr addr, uint32_t level) return (addr & vtd_slpt_level_page_mask(level)) >> VTD_PAGE_SHIFT_4K; } +/* Must be called with IOMMU lock held */ static VTDIOTLBEntry *vtd_lookup_iotlb(IntelIOMMUState *s, uint16_t source_id, hwaddr addr) { @@ -235,6 +260,7 @@ out: return entry; } +/* Must be with IOMMU lock held */ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, uint16_t domain_id, hwaddr addr, uint64_t slpte, uint8_t access_flags, uint32_t level) @@ -246,7 +272,7 @@ static void vtd_update_iotlb(IntelIOMMUState *s, uint16_t source_id, trace_vtd_iotlb_page_update(source_id, addr, slpte, domain_id); if (g_hash_table_size(s->iotlb) >= VTD_IOTLB_MAX_SIZE) { trace_vtd_iotlb_reset("iotlb exceeds size limit"); - vtd_reset_iotlb(s); + vtd_reset_iotlb_locked(s); } entry->gfn = gfn; @@ -722,23 +748,117 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, typedef int (*vtd_page_walk_hook)(IOMMUTLBEntry *entry, void *private); +/** + * Constant information used during page walking + * + * @hook_fn: hook func to be called when detected page + * @private: private data to be passed into hook func + * @notify_unmap: whether we should notify invalid entries + * @as: VT-d address space of the device + * @aw: maximum address width + * @domain: domain ID of the page walk + */ +typedef struct { + VTDAddressSpace *as; + vtd_page_walk_hook hook_fn; + void *private; + bool notify_unmap; + uint8_t aw; + uint16_t domain_id; +} vtd_page_walk_info; + +static int vtd_page_walk_one(IOMMUTLBEntry *entry, vtd_page_walk_info *info) +{ + VTDAddressSpace *as = info->as; + vtd_page_walk_hook hook_fn = info->hook_fn; + void *private = info->private; + DMAMap target = { + .iova = entry->iova, + .size = entry->addr_mask, + .translated_addr = entry->translated_addr, + .perm = entry->perm, + }; + DMAMap *mapped = iova_tree_find(as->iova_tree, &target); + + if (entry->perm == IOMMU_NONE && !info->notify_unmap) { + trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); + return 0; + } + + assert(hook_fn); + + /* Update local IOVA mapped ranges */ + if (entry->perm) { + if (mapped) { + /* If it's exactly the same translation, skip */ + if (!memcmp(mapped, &target, sizeof(target))) { + trace_vtd_page_walk_one_skip_map(entry->iova, entry->addr_mask, + entry->translated_addr); + return 0; + } else { + /* + * Translation changed. Normally this should not + * happen, but it can happen when with buggy guest + * OSes. Note that there will be a small window that + * we don't have map at all. But that's the best + * effort we can do. The ideal way to emulate this is + * atomically modify the PTE to follow what has + * changed, but we can't. One example is that vfio + * driver only has VFIO_IOMMU_[UN]MAP_DMA but no + * interface to modify a mapping (meanwhile it seems + * meaningless to even provide one). Anyway, let's + * mark this as a TODO in case one day we'll have + * a better solution. + */ + IOMMUAccessFlags cache_perm = entry->perm; + int ret; + + /* Emulate an UNMAP */ + entry->perm = IOMMU_NONE; + trace_vtd_page_walk_one(info->domain_id, + entry->iova, + entry->translated_addr, + entry->addr_mask, + entry->perm); + ret = hook_fn(entry, private); + if (ret) { + return ret; + } + /* Drop any existing mapping */ + iova_tree_remove(as->iova_tree, &target); + /* Recover the correct permission */ + entry->perm = cache_perm; + } + } + iova_tree_insert(as->iova_tree, &target); + } else { + if (!mapped) { + /* Skip since we didn't map this range at all */ + trace_vtd_page_walk_one_skip_unmap(entry->iova, entry->addr_mask); + return 0; + } + iova_tree_remove(as->iova_tree, &target); + } + + trace_vtd_page_walk_one(info->domain_id, entry->iova, + entry->translated_addr, entry->addr_mask, + entry->perm); + return hook_fn(entry, private); +} + /** * vtd_page_walk_level - walk over specific level for IOVA range * * @addr: base GPA addr to start the walk * @start: IOVA range start address * @end: IOVA range end address (start <= addr < end) - * @hook_fn: hook func to be called when detected page - * @private: private data to be passed into hook func * @read: whether parent level has read permission * @write: whether parent level has write permission - * @notify_unmap: whether we should notify invalid entries - * @aw: maximum address width + * @info: constant information for the page walk */ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, - uint64_t end, vtd_page_walk_hook hook_fn, - void *private, uint32_t level, bool read, - bool write, bool notify_unmap, uint8_t aw) + uint64_t end, uint32_t level, bool read, + bool write, vtd_page_walk_info *info) { bool read_cur, write_cur, entry_valid; uint32_t offset; @@ -781,37 +901,34 @@ static int vtd_page_walk_level(dma_addr_t addr, uint64_t start, */ entry_valid = read_cur | write_cur; - if (vtd_is_last_slpte(slpte, level)) { + if (!vtd_is_last_slpte(slpte, level) && entry_valid) { + /* + * This is a valid PDE (or even bigger than PDE). We need + * to walk one further level. + */ + ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, info->aw), + iova, MIN(iova_next, end), level - 1, + read_cur, write_cur, info); + } else { + /* + * This means we are either: + * + * (1) the real page entry (either 4K page, or huge page) + * (2) the whole range is invalid + * + * In either case, we send an IOTLB notification down. + */ entry.target_as = &address_space_memory; entry.iova = iova & subpage_mask; - /* NOTE: this is only meaningful if entry_valid == true */ - entry.translated_addr = vtd_get_slpte_addr(slpte, aw); - entry.addr_mask = ~subpage_mask; entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); - if (!entry_valid && !notify_unmap) { - trace_vtd_page_walk_skip_perm(iova, iova_next); - goto next; - } - trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr, - entry.addr_mask, entry.perm); - if (hook_fn) { - ret = hook_fn(&entry, private); - if (ret < 0) { - return ret; - } - } - } else { - if (!entry_valid) { - trace_vtd_page_walk_skip_perm(iova, iova_next); - goto next; - } - ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte, aw), iova, - MIN(iova_next, end), hook_fn, private, - level - 1, read_cur, write_cur, - notify_unmap, aw); - if (ret < 0) { - return ret; - } + entry.addr_mask = ~subpage_mask; + /* NOTE: this is only meaningful if entry_valid == true */ + entry.translated_addr = vtd_get_slpte_addr(slpte, info->aw); + ret = vtd_page_walk_one(&entry, info); + } + + if (ret < 0) { + return ret; } next: @@ -827,28 +944,24 @@ next: * @ce: context entry to walk upon * @start: IOVA address to start the walk * @end: IOVA range end address (start <= addr < end) - * @hook_fn: the hook that to be called for each detected area - * @private: private data for the hook function - * @aw: maximum address width + * @info: page walking information struct */ static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end, - vtd_page_walk_hook hook_fn, void *private, - bool notify_unmap, uint8_t aw) + vtd_page_walk_info *info) { dma_addr_t addr = vtd_ce_get_slpt_base(ce); uint32_t level = vtd_ce_get_level(ce); - if (!vtd_iova_range_check(start, ce, aw)) { + if (!vtd_iova_range_check(start, ce, info->aw)) { return -VTD_FR_ADDR_BEYOND_MGAW; } - if (!vtd_iova_range_check(end, ce, aw)) { + if (!vtd_iova_range_check(end, ce, info->aw)) { /* Fix end so that it reaches the maximum */ - end = vtd_iova_limit(ce, aw); + end = vtd_iova_limit(ce, info->aw); } - return vtd_page_walk_level(addr, start, end, hook_fn, private, - level, true, true, notify_unmap, aw); + return vtd_page_walk_level(addr, start, end, level, true, true, info); } /* Map a device to its corresponding domain (context-entry) */ @@ -907,6 +1020,58 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return 0; } +static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry, + void *private) +{ + memory_region_notify_iommu((IOMMUMemoryRegion *)private, 0, *entry); + return 0; +} + +/* If context entry is NULL, we'll try to fetch it on our own. */ +static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, + VTDContextEntry *ce, + hwaddr addr, hwaddr size) +{ + IntelIOMMUState *s = vtd_as->iommu_state; + vtd_page_walk_info info = { + .hook_fn = vtd_sync_shadow_page_hook, + .private = (void *)&vtd_as->iommu, + .notify_unmap = true, + .aw = s->aw_bits, + .as = vtd_as, + }; + VTDContextEntry ce_cache; + int ret; + + if (ce) { + /* If the caller provided context entry, use it */ + ce_cache = *ce; + } else { + /* If the caller didn't provide ce, try to fetch */ + ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce_cache); + if (ret) { + /* + * This should not really happen, but in case it happens, + * we just skip the sync for this time. After all we even + * don't have the root table pointer! + */ + trace_vtd_err("Detected invalid context entry when " + "trying to sync shadow page table"); + return 0; + } + } + + info.domain_id = VTD_CONTEXT_ENTRY_DID(ce_cache.hi); + + return vtd_page_walk(&ce_cache, addr, addr + size, &info); +} + +static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) +{ + return vtd_sync_shadow_page_table_range(vtd_as, NULL, 0, UINT64_MAX); +} + /* * Fetch translation type for specific device. Returns <0 if error * happens, otherwise return the shifted type to check against @@ -1088,7 +1253,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, IntelIOMMUState *s = vtd_as->iommu_state; VTDContextEntry ce; uint8_t bus_num = pci_bus_num(bus); - VTDContextCacheEntry *cc_entry = &vtd_as->context_cache_entry; + VTDContextCacheEntry *cc_entry; uint64_t slpte, page_mask; uint32_t level; uint16_t source_id = vtd_make_source_id(bus_num, devfn); @@ -1105,6 +1270,10 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, */ assert(!vtd_is_interrupt_addr(addr)); + vtd_iommu_lock(s); + + cc_entry = &vtd_as->context_cache_entry; + /* Try to fetch slpte form IOTLB */ iotlb_entry = vtd_lookup_iotlb(s, source_id, addr); if (iotlb_entry) { @@ -1164,7 +1333,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, * IOMMU region can be swapped back. */ vtd_pt_enable_fast_path(s, source_id); - + vtd_iommu_unlock(s); return true; } @@ -1185,6 +1354,7 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte, access_flags, level); out: + vtd_iommu_unlock(s); entry->iova = addr & page_mask; entry->translated_addr = vtd_get_slpte_addr(slpte, s->aw_bits) & page_mask; entry->addr_mask = ~page_mask; @@ -1192,6 +1362,7 @@ out: return true; error: + vtd_iommu_unlock(s); entry->iova = 0; entry->translated_addr = 0; entry->addr_mask = 0; @@ -1230,20 +1401,23 @@ static void vtd_interrupt_remap_table_setup(IntelIOMMUState *s) static void vtd_iommu_replay_all(IntelIOMMUState *s) { - IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; - QLIST_FOREACH(node, &s->notifiers_list, next) { - memory_region_iommu_replay_all(&node->vtd_as->iommu); + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { + vtd_sync_shadow_page_table(vtd_as); } } static void vtd_context_global_invalidate(IntelIOMMUState *s) { trace_vtd_inv_desc_cc_global(); + /* Protects context cache */ + vtd_iommu_lock(s); s->context_cache_gen++; if (s->context_cache_gen == VTD_CONTEXT_CACHE_GEN_MAX) { - vtd_reset_context_cache(s); + vtd_reset_context_cache_locked(s); } + vtd_iommu_unlock(s); vtd_switch_address_space_all(s); /* * From VT-d spec 6.5.2.1, a global context entry invalidation @@ -1295,7 +1469,9 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, if (vtd_as && ((devfn_it & mask) == (devfn & mask))) { trace_vtd_inv_desc_cc_device(bus_n, VTD_PCI_SLOT(devfn_it), VTD_PCI_FUNC(devfn_it)); + vtd_iommu_lock(s); vtd_as->context_cache_entry.context_cache_gen = 0; + vtd_iommu_unlock(s); /* * Do switch address space when needed, in case if the * device passthrough bit is switched. @@ -1303,14 +1479,13 @@ static void vtd_context_device_invalidate(IntelIOMMUState *s, vtd_switch_address_space(vtd_as); /* * So a device is moving out of (or moving into) a - * domain, a replay() suites here to notify all the - * IOMMU_NOTIFIER_MAP registers about this change. + * domain, resync the shadow page table. * This won't bring bad even if we have no such * notifier registered - the IOMMU notification * framework will skip MAP notifications if that * happened. */ - memory_region_iommu_replay_all(&vtd_as->iommu); + vtd_sync_shadow_page_table(vtd_as); } } } @@ -1354,48 +1529,60 @@ static void vtd_iotlb_global_invalidate(IntelIOMMUState *s) static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) { - IntelIOMMUNotifierNode *node; VTDContextEntry ce; VTDAddressSpace *vtd_as; trace_vtd_inv_desc_iotlb_domain(domain_id); + vtd_iommu_lock(s); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_domain, &domain_id); + vtd_iommu_unlock(s); - QLIST_FOREACH(node, &s->notifiers_list, next) { - vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce) && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { - memory_region_iommu_replay_all(&vtd_as->iommu); + vtd_sync_shadow_page_table(vtd_as); } } } -static int vtd_page_invalidate_notify_hook(IOMMUTLBEntry *entry, - void *private) -{ - memory_region_notify_iommu((IOMMUMemoryRegion *)private, *entry); - return 0; -} - static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, uint16_t domain_id, hwaddr addr, uint8_t am) { - IntelIOMMUNotifierNode *node; + VTDAddressSpace *vtd_as; VTDContextEntry ce; int ret; + hwaddr size = (1 << am) * VTD_PAGE_SIZE; - QLIST_FOREACH(node, &(s->notifiers_list), next) { - VTDAddressSpace *vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { - vtd_page_walk(&ce, addr, addr + (1 << am) * VTD_PAGE_SIZE, - vtd_page_invalidate_notify_hook, - (void *)&vtd_as->iommu, true, s->aw_bits); + if (vtd_as_has_map_notifier(vtd_as)) { + /* + * As long as we have MAP notifications registered in + * any of our IOMMU notifiers, we need to sync the + * shadow page table. + */ + vtd_sync_shadow_page_table_range(vtd_as, &ce, addr, size); + } else { + /* + * For UNMAP-only notifiers, we don't need to walk the + * page tables. We just deliver the PSI down to + * invalidate caches. + */ + IOMMUTLBEntry entry = { + .target_as = &address_space_memory, + .iova = addr, + .translated_addr = 0, + .addr_mask = size - 1, + .perm = IOMMU_NONE, + }; + memory_region_notify_iommu(&vtd_as->iommu, 0, entry); + } } } } @@ -1411,7 +1598,9 @@ static void vtd_iotlb_page_invalidate(IntelIOMMUState *s, uint16_t domain_id, info.domain_id = domain_id; info.addr = addr; info.mask = ~((1 << am) - 1); + vtd_iommu_lock(s); g_hash_table_foreach_remove(s->iotlb, vtd_hash_remove_by_page, &info); + vtd_iommu_unlock(s); vtd_iotlb_page_invalidate_notify(s, domain_id, addr, am); } @@ -1826,7 +2015,7 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, entry.iova = addr; entry.perm = IOMMU_NONE; entry.translated_addr = 0; - memory_region_notify_iommu(&vtd_dev_as->iommu, entry); + memory_region_notify_iommu(&vtd_dev_as->iommu, 0, entry); done: return true; @@ -2282,7 +2471,7 @@ static void vtd_mem_write(void *opaque, hwaddr addr, } static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; @@ -2326,8 +2515,6 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, { VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); IntelIOMMUState *s = vtd_as->iommu_state; - IntelIOMMUNotifierNode *node = NULL; - IntelIOMMUNotifierNode *next_node = NULL; if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) { error_report("We need to set caching-mode=1 for intel-iommu to enable " @@ -2335,22 +2522,13 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, exit(1); } - if (old == IOMMU_NOTIFIER_NONE) { - node = g_malloc0(sizeof(*node)); - node->vtd_as = vtd_as; - QLIST_INSERT_HEAD(&s->notifiers_list, node, next); - return; - } + /* Update per-address-space notifier flags */ + vtd_as->notifier_flags = new; - /* update notifier node with new flags */ - QLIST_FOREACH_SAFE(node, &s->notifiers_list, next, next_node) { - if (node->vtd_as == vtd_as) { - if (new == IOMMU_NOTIFIER_NONE) { - QLIST_REMOVE(node, next); - g_free(node); - } - return; - } + if (old == IOMMU_NOTIFIER_NONE) { + QLIST_INSERT_HEAD(&s->vtd_as_with_notifiers, vtd_as, next); + } else if (new == IOMMU_NOTIFIER_NONE) { + QLIST_REMOVE(vtd_as, next); } } @@ -2719,6 +2897,7 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) vtd_dev_as->devfn = (uint8_t)devfn; vtd_dev_as->iommu_state = s; vtd_dev_as->context_cache_entry.context_cache_gen = 0; + vtd_dev_as->iova_tree = iova_tree_new(); /* * Memory region relationships looks like (Address range shows @@ -2771,6 +2950,7 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) hwaddr start = n->start; hwaddr end = n->end; IntelIOMMUState *s = as->iommu_state; + DMAMap map; /* * Note: all the codes in this function has a assumption that IOVA @@ -2815,17 +2995,19 @@ static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n) VTD_PCI_FUNC(as->devfn), entry.iova, size); + map.iova = entry.iova; + map.size = entry.addr_mask; + iova_tree_remove(as->iova_tree, &map); + memory_region_notify_one(n, &entry); } static void vtd_address_space_unmap_all(IntelIOMMUState *s) { - IntelIOMMUNotifierNode *node; VTDAddressSpace *vtd_as; IOMMUNotifier *n; - QLIST_FOREACH(node, &s->notifiers_list, next) { - vtd_as = node->vtd_as; + QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { vtd_address_space_unmap(vtd_as, n); } @@ -2857,8 +3039,19 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) PCI_FUNC(vtd_as->devfn), VTD_CONTEXT_ENTRY_DID(ce.hi), ce.hi, ce.lo); - vtd_page_walk(&ce, 0, ~0ULL, vtd_replay_hook, (void *)n, false, - s->aw_bits); + if (vtd_as_has_map_notifier(vtd_as)) { + /* This is required only for MAP typed notifiers */ + vtd_page_walk_info info = { + .hook_fn = vtd_replay_hook, + .private = (void *)n, + .notify_unmap = false, + .aw = s->aw_bits, + .as = vtd_as, + .domain_id = VTD_CONTEXT_ENTRY_DID(ce.hi), + }; + + vtd_page_walk(&ce, 0, ~0ULL, &info); + } } else { trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), PCI_FUNC(vtd_as->devfn)); @@ -2930,8 +3123,10 @@ static void vtd_init(IntelIOMMUState *s) s->cap |= VTD_CAP_CM; } - vtd_reset_context_cache(s); - vtd_reset_iotlb(s); + vtd_iommu_lock(s); + vtd_reset_context_cache_locked(s); + vtd_reset_iotlb_locked(s); + vtd_iommu_unlock(s); /* Define registers with default values and bit semantics */ vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0); @@ -3070,7 +3265,8 @@ static void vtd_realize(DeviceState *dev, Error **errp) return; } - QLIST_INIT(&s->notifiers_list); + QLIST_INIT(&s->vtd_as_with_notifiers); + qemu_mutex_init(&s->iommu_lock); memset(s->vtd_as_by_bus_num, 0, sizeof(s->vtd_as_by_bus_num)); memory_region_init_io(&s->csrmem, OBJECT(s), &vtd_mem_ops, s, "intel_iommu", DMAR_REG_SIZE); diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 7dac319403..0bf1c60a06 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include -#include +#include "standard-headers/asm-x86/kvm_para.h" #define TYPE_KVM_CLOCK "kvmclock" #define KVM_CLOCK(obj) OBJECT_CHECK(KVMClockState, (obj), TYPE_KVM_CLOCK) diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 13f20f47d9..d4d4a859f0 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -293,7 +293,7 @@ static void kvm_pit_realizefn(DeviceState *dev, Error **errp) return; } - memory_region_init_reservation(&pit->ioports, NULL, "kvm-pit", 4); + memory_region_init_io(&pit->ioports, OBJECT(dev), NULL, NULL, "kvm-pit", 4); qdev_init_gpio_in(dev, kvm_pit_irq_control, 1); diff --git a/hw/i386/kvm/i8259.c b/hw/i386/kvm/i8259.c index 05394cdb7b..83b6bfec77 100644 --- a/hw/i386/kvm/i8259.c +++ b/hw/i386/kvm/i8259.c @@ -121,8 +121,8 @@ static void kvm_pic_realize(DeviceState *dev, Error **errp) PICCommonState *s = PIC_COMMON(dev); KVMPICClass *kpc = KVM_PIC_GET_CLASS(dev); - memory_region_init_reservation(&s->base_io, NULL, "kvm-pic", 2); - memory_region_init_reservation(&s->elcr_io, NULL, "kvm-elcr", 1); + memory_region_init_io(&s->base_io, OBJECT(dev), NULL, NULL, "kvm-pic", 2); + memory_region_init_io(&s->elcr_io, OBJECT(dev), NULL, NULL, "kvm-elcr", 1); kpc->parent_realize(dev, errp); } diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 98ca480792..5b40d75439 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -112,15 +112,6 @@ static void kvm_ioapic_put(IOAPICCommonState *s) } } -void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict) -{ - IOAPICCommonState *s = IOAPIC_COMMON(object_resolve_path("ioapic", NULL)); - - assert(s); - kvm_ioapic_get(s); - ioapic_print_redtbl(mon, s); -} - static void kvm_ioapic_reset(DeviceState *dev) { IOAPICCommonState *s = IOAPIC_COMMON(dev); @@ -132,8 +123,10 @@ static void kvm_ioapic_reset(DeviceState *dev) static void kvm_ioapic_set_irq(void *opaque, int irq, int level) { KVMIOAPICState *s = opaque; + IOAPICCommonState *common = IOAPIC_COMMON(s); int delivered; + ioapic_stat_update_irq(common, irq, level); delivered = kvm_set_irq(kvm_state, s->kvm_gsi_base + irq, level); apic_report_irq_delivered(delivered); } @@ -142,7 +135,7 @@ static void kvm_ioapic_realize(DeviceState *dev, Error **errp) { IOAPICCommonState *s = IOAPIC_COMMON(dev); - memory_region_init_reservation(&s->io_memory, NULL, "kvm-ioapic", 0x1000); + memory_region_init_io(&s->io_memory, OBJECT(dev), NULL, NULL, "kvm-ioapic", 0x1000); /* * KVM ioapic only supports 0x11 now. This will only be used when * we want to dump ioapic version. diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index fc962c5fbc..70f6f26a94 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "cpu.h" -#include "exec/exec-all.h" #include "sysemu/sysemu.h" #include "sysemu/cpus.h" #include "sysemu/hw_accel.h" diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index 5bc0a2cddb..d519e206c5 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -161,6 +161,7 @@ int load_multiboot(FWCfgState *fw_cfg, uint8_t bootinfo[MBI_SIZE]; uint8_t *mb_bootinfo_data; uint32_t cmdline_len; + GList *mods = NULL; /* Ok, let's see if it is a multiboot image. The header is 12x32bit long, so the latest entry may be 8192 - 48. */ @@ -292,11 +293,15 @@ int load_multiboot(FWCfgState *fw_cfg, cmdline_len += strlen(kernel_cmdline) + 1; if (initrd_filename) { const char *r = initrd_filename; - cmdline_len += strlen(r) + 1; - mbs.mb_mods_avail = 1; - while (*(r = get_opt_value(NULL, 0, r))) { - mbs.mb_mods_avail++; - r++; + cmdline_len += strlen(initrd_filename) + 1; + while (*r) { + char *value; + r = get_opt_value(r, &value); + mbs.mb_mods_avail++; + mods = g_list_append(mods, value); + if (*r) { + r++; + } } } @@ -311,43 +316,44 @@ int load_multiboot(FWCfgState *fw_cfg, mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE; mbs.offset_bootloader = mbs.offset_cmdlines + cmdline_len; - if (initrd_filename) { - const char *next_initrd; - char not_last, tmpbuf[strlen(initrd_filename) + 1]; - + if (mods) { + GList *tmpl = mods; mbs.offset_mods = mbs.mb_buf_size; - do { + while (tmpl) { char *next_space; int mb_mod_length; uint32_t offs = mbs.mb_buf_size; + char *one_file = tmpl->data; - next_initrd = get_opt_value(tmpbuf, sizeof(tmpbuf), initrd_filename); - not_last = *next_initrd; /* if a space comes after the module filename, treat everything after that as parameters */ - hwaddr c = mb_add_cmdline(&mbs, tmpbuf); - if ((next_space = strchr(tmpbuf, ' '))) + hwaddr c = mb_add_cmdline(&mbs, one_file); + next_space = strchr(one_file, ' '); + if (next_space) { *next_space = '\0'; - mb_debug("multiboot loading module: %s", tmpbuf); - mb_mod_length = get_image_size(tmpbuf); + } + mb_debug("multiboot loading module: %s", one_file); + mb_mod_length = get_image_size(one_file); if (mb_mod_length < 0) { - error_report("Failed to open file '%s'", tmpbuf); + error_report("Failed to open file '%s'", one_file); exit(1); } mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_mod_length + mbs.mb_buf_size); mbs.mb_buf = g_realloc(mbs.mb_buf, mbs.mb_buf_size); - load_image(tmpbuf, (unsigned char *)mbs.mb_buf + offs); + load_image(one_file, (unsigned char *)mbs.mb_buf + offs); mb_add_mod(&mbs, mbs.mb_buf_phys + offs, mbs.mb_buf_phys + offs + mb_mod_length, c); mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx, (char *)mbs.mb_buf + offs, (char *)mbs.mb_buf + offs + mb_mod_length, c); - initrd_filename = next_initrd+1; - } while (not_last); + g_free(one_file); + tmpl = tmpl->next; + } + g_list_free(mods); } /* Commandline support */ diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 39d1558433..66802fbfc2 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" @@ -64,7 +65,6 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/cpu_hotplug.h" #include "hw/boards.h" -#include "hw/pci/pci_host.h" #include "acpi-build.h" #include "hw/mem/pc-dimm.h" #include "qapi/error.h" @@ -454,12 +454,12 @@ void pc_cmos_init(PCMachineState *pcms, /* memory size */ /* base memory (first MiB) */ - val = MIN(pcms->below_4g_mem_size / 1024, 640); + val = MIN(pcms->below_4g_mem_size / KiB, 640); rtc_set_memory(s, 0x15, val); rtc_set_memory(s, 0x16, val >> 8); /* extended memory (next 64MiB) */ - if (pcms->below_4g_mem_size > 1024 * 1024) { - val = (pcms->below_4g_mem_size - 1024 * 1024) / 1024; + if (pcms->below_4g_mem_size > 1 * MiB) { + val = (pcms->below_4g_mem_size - 1 * MiB) / KiB; } else { val = 0; } @@ -470,8 +470,8 @@ void pc_cmos_init(PCMachineState *pcms, rtc_set_memory(s, 0x30, val); rtc_set_memory(s, 0x31, val >> 8); /* memory between 16MiB and 4GiB */ - if (pcms->below_4g_mem_size > 16 * 1024 * 1024) { - val = (pcms->below_4g_mem_size - 16 * 1024 * 1024) / 65536; + if (pcms->below_4g_mem_size > 16 * MiB) { + val = (pcms->below_4g_mem_size - 16 * MiB) / (64 * KiB); } else { val = 0; } @@ -489,7 +489,7 @@ void pc_cmos_init(PCMachineState *pcms, TYPE_ISA_DEVICE, (Object **)&pcms->rtc, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + OBJ_PROP_LINK_STRONG, &error_abort); object_property_set_link(OBJECT(pcms), OBJECT(s), "rtc_state", &error_abort); @@ -1376,11 +1376,13 @@ void pc_memory_init(PCMachineState *pcms, exit(EXIT_FAILURE); } - /* initialize hotplug memory address space */ + /* always allocate the device memory information */ + machine->device_memory = g_malloc0(sizeof(*machine->device_memory)); + + /* initialize device memory address space */ if (pcmc->has_reserved_memory && (machine->ram_size < machine->maxram_size)) { - ram_addr_t hotplug_mem_size = - machine->maxram_size - machine->ram_size; + ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) { error_report("unsupported amount of memory slots: %"PRIu64, @@ -1395,25 +1397,25 @@ void pc_memory_init(PCMachineState *pcms, exit(EXIT_FAILURE); } - pcms->hotplug_memory.base = - ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30); + machine->device_memory->base = + ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1 * GiB); if (pcmc->enforce_aligned_dimm) { - /* size hotplug region assuming 1G page max alignment per slot */ - hotplug_mem_size += (1ULL << 30) * machine->ram_slots; + /* size device region assuming 1G page max alignment per slot */ + device_mem_size += (1 * GiB) * machine->ram_slots; } - if ((pcms->hotplug_memory.base + hotplug_mem_size) < - hotplug_mem_size) { + if ((machine->device_memory->base + device_mem_size) < + device_mem_size) { error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT, machine->maxram_size); exit(EXIT_FAILURE); } - memory_region_init(&pcms->hotplug_memory.mr, OBJECT(pcms), - "hotplug-memory", hotplug_mem_size); - memory_region_add_subregion(system_memory, pcms->hotplug_memory.base, - &pcms->hotplug_memory.mr); + memory_region_init(&machine->device_memory->mr, OBJECT(pcms), + "device-memory", device_mem_size); + memory_region_add_subregion(system_memory, machine->device_memory->base, + &machine->device_memory->mr); } /* Initialize PC system firmware */ @@ -1434,15 +1436,15 @@ void pc_memory_init(PCMachineState *pcms, rom_set_fw(fw_cfg); - if (pcmc->has_reserved_memory && pcms->hotplug_memory.base) { + if (pcmc->has_reserved_memory && machine->device_memory->base) { uint64_t *val = g_malloc(sizeof(*val)); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - uint64_t res_mem_end = pcms->hotplug_memory.base; + uint64_t res_mem_end = machine->device_memory->base; if (!pcmc->broken_reserved_end) { - res_mem_end += memory_region_size(&pcms->hotplug_memory.mr); + res_mem_end += memory_region_size(&machine->device_memory->mr); } - *val = cpu_to_le64(ROUND_UP(res_mem_end, 0x1ULL << 30)); + *val = cpu_to_le64(ROUND_UP(res_mem_end, 1 * GiB)); fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); } @@ -1467,18 +1469,19 @@ uint64_t pc_pci_hole64_start(void) { PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + MachineState *ms = MACHINE(pcms); uint64_t hole64_start = 0; - if (pcmc->has_reserved_memory && pcms->hotplug_memory.base) { - hole64_start = pcms->hotplug_memory.base; + if (pcmc->has_reserved_memory && ms->device_memory->base) { + hole64_start = ms->device_memory->base; if (!pcmc->broken_reserved_end) { - hole64_start += memory_region_size(&pcms->hotplug_memory.mr); + hole64_start += memory_region_size(&ms->device_memory->mr); } } else { hole64_start = 0x100000000ULL + pcms->above_4g_mem_size; } - return ROUND_UP(hole64_start, 1ULL << 30); + return ROUND_UP(hole64_start, 1 * GiB); } qemu_irq pc_allocate_cpu_irq(void) @@ -1529,7 +1532,7 @@ static void pc_superio_init(ISABus *isa_bus, bool create_fdctrl, bool no_vmport) qemu_irq *a20_line; ISADevice *i8042, *port92, *vmmouse; - serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); + serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS); parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS); for (i = 0; i < MAX_FD; i++) { @@ -1677,27 +1680,11 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) } } -static void pc_dimm_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) { - HotplugHandlerClass *hhc; - Error *local_err = NULL; - PCMachineState *pcms = PC_MACHINE(hotplug_dev); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr; - uint64_t align = TARGET_PAGE_SIZE; - bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - - mr = ddc->get_memory_region(dimm, &local_err); - if (local_err) { - goto out; - } - - if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) { - align = memory_region_get_alignment(mr); - } + const PCMachineState *pcms = PC_MACHINE(hotplug_dev); + const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); /* * When -no-acpi is used with Q35 machine type, no ACPI is built, @@ -1705,18 +1692,35 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, * addition to cover this case. */ if (!pcms->acpi_dev || !acpi_enabled) { - error_setg(&local_err, + error_setg(errp, "memory hotplug is not enabled: missing acpi device or acpi disabled"); - goto out; + return; } if (is_nvdimm && !pcms->acpi_nvdimm_state.is_enabled) { - error_setg(&local_err, - "nvdimm is not enabled: missing 'nvdimm' in '-M'"); - goto out; + error_setg(errp, "nvdimm is not enabled: missing 'nvdimm' in '-M'"); + return; + } +} + +static void pc_memory_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + HotplugHandlerClass *hhc; + Error *local_err = NULL; + PCMachineState *pcms = PC_MACHINE(hotplug_dev); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); + uint64_t align = TARGET_PAGE_SIZE; + bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); + + if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) { + align = memory_region_get_alignment(mr); } - pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err); + pc_dimm_plug(dev, MACHINE(pcms), align, &local_err); if (local_err) { goto out; } @@ -1731,8 +1735,8 @@ out: error_propagate(errp, local_err); } -static void pc_dimm_unplug_request(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { HotplugHandlerClass *hhc; Error *local_err = NULL; @@ -1762,21 +1766,13 @@ out: error_propagate(errp, local_err); } -static void pc_dimm_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void pc_memory_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { PCMachineState *pcms = PC_MACHINE(hotplug_dev); - PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr; HotplugHandlerClass *hhc; Error *local_err = NULL; - mr = ddc->get_memory_region(dimm, &local_err); - if (local_err) { - goto out; - } - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); @@ -1784,7 +1780,7 @@ static void pc_dimm_unplug(HotplugHandler *hotplug_dev, goto out; } - pc_dimm_memory_unplug(dev, &pcms->hotplug_memory, mr); + pc_dimm_unplug(dev, MACHINE(pcms)); object_unparent(OBJECT(dev)); out: @@ -2008,6 +2004,11 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, } cpu->thread_id = topo.smt_id; + if (cpu->hyperv_vpindex && !kvm_hv_vpindex_settable()) { + error_setg(errp, "kernel doesn't allow setting HyperV VP_INDEX"); + return; + } + cs = CPU(cpu); cs->cpu_index = idx; @@ -2017,7 +2018,9 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, static void pc_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + pc_memory_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_pre_plug(hotplug_dev, dev, errp); } } @@ -2026,7 +2029,7 @@ static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - pc_dimm_plug(hotplug_dev, dev, errp); + pc_memory_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_plug(hotplug_dev, dev, errp); } @@ -2036,7 +2039,7 @@ static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - pc_dimm_unplug_request(hotplug_dev, dev, errp); + pc_memory_unplug_request(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_unplug_request_cb(hotplug_dev, dev, errp); } else { @@ -2049,7 +2052,7 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - pc_dimm_unplug(hotplug_dev, dev, errp); + pc_memory_unplug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { pc_cpu_unplug_cb(hotplug_dev, dev, errp); } else { @@ -2061,24 +2064,21 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, static HotplugHandler *pc_get_hotpug_handler(MachineState *machine, DeviceState *dev) { - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine); - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { return HOTPLUG_HANDLER(machine); } - return pcmc->get_hotplug_handler ? - pcmc->get_hotplug_handler(machine, dev) : NULL; + return NULL; } static void -pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) +pc_machine_get_device_memory_region_size(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) { - PCMachineState *pcms = PC_MACHINE(obj); - int64_t value = memory_region_size(&pcms->hotplug_memory.mr); + MachineState *ms = MACHINE(obj); + int64_t value = memory_region_size(&ms->device_memory->mr); visit_type_int(v, name, &value, errp); } @@ -2106,7 +2106,7 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, error_propagate(errp, error); return; } - if (value > (1ULL << 32)) { + if (value > 4 * GiB) { error_setg(&error, "Machine option 'max-ram-below-4g=%"PRIu64 "' expects size less than or equal to 4G", value); @@ -2114,7 +2114,7 @@ static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, return; } - if (value < (1ULL << 20)) { + if (value < 1 * MiB) { warn_report("Only %" PRIu64 " bytes of RAM below the 4GiB boundary," "BIOS may not work with less than 1MiB", value); } @@ -2195,6 +2195,32 @@ static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp) pcms->acpi_nvdimm_state.is_enabled = value; } +static char *pc_machine_get_nvdimm_persistence(Object *obj, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + return g_strdup(pcms->acpi_nvdimm_state.persistence_string); +} + +static void pc_machine_set_nvdimm_persistence(Object *obj, const char *value, + Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + AcpiNVDIMMState *nvdimm_state = &pcms->acpi_nvdimm_state; + + if (strcmp(value, "cpu") == 0) + nvdimm_state->persistence = 3; + else if (strcmp(value, "mem-ctrl") == 0) + nvdimm_state->persistence = 2; + else { + error_report("-machine nvdimm-persistence=%s: unsupported option", value); + exit(EXIT_FAILURE); + } + + g_free(nvdimm_state->persistence_string); + nvdimm_state->persistence_string = g_strdup(value); +} + static bool pc_machine_get_smbus(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); @@ -2349,7 +2375,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); - pcmc->get_hotplug_handler = mc->get_hotplug_handler; pcmc->pci_enabled = true; pcmc->has_acpi_build = true; pcmc->rsdp_in_ram = true; @@ -2364,6 +2389,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->acpi_data_size = 0x20000 + 0x8000; pcmc->save_tsc_khz = true; pcmc->linuxboot_dma_enabled = true; + assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = pc_get_hotpug_handler; mc->cpu_index_to_instance_props = pc_cpu_index_to_props; mc->get_default_cpu_node_id = pc_get_default_cpu_node_id; @@ -2382,8 +2408,8 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) nc->nmi_monitor_handler = x86_nmi; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; - object_class_property_add(oc, PC_MACHINE_MEMHP_REGION_SIZE, "int", - pc_machine_get_hotplug_memory_region_size, NULL, + object_class_property_add(oc, PC_MACHINE_DEVMEM_REGION_SIZE, "int", + pc_machine_get_device_memory_region_size, NULL, NULL, NULL, &error_abort); object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size", @@ -2408,6 +2434,10 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm, pc_machine_set_nvdimm, &error_abort); + object_class_property_add_str(oc, PC_MACHINE_NVDIMM_PERSIST, + pc_machine_get_nvdimm_persistence, + pc_machine_set_nvdimm_persistence, &error_abort); + object_class_property_add_bool(oc, PC_MACHINE_SMBUS, pc_machine_get_smbus, pc_machine_set_smbus, &error_abort); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 729a0508aa..dc09466b3e 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -24,10 +24,12 @@ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/loader.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" +#include "hw/display/ramfb.h" #include "hw/smbios/smbios.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" @@ -130,7 +132,7 @@ static void pc_init1(MachineState *machine, if (lowmem > 0xc0000000) { lowmem = 0xc0000000; } - if (lowmem & ((1ULL << 30) - 1)) { + if (lowmem & (1 * GiB - 1)) { warn_report("Large machine and max_ram_below_4g " "(%" PRIu64 ") not a multiple of 1G; " "possible bad performance.", @@ -289,7 +291,7 @@ static void pc_init1(MachineState *machine, TYPE_HOTPLUG_HANDLER, (Object **)&pcms->acpi_dev, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + OBJ_PROP_LINK_STRONG, &error_abort); object_property_set_link(OBJECT(machine), OBJECT(piix4_pm), PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); } @@ -423,23 +425,33 @@ static void pc_i440fx_machine_options(MachineClass *m) m->desc = "Standard PC (i440FX + PIIX, 1996)"; m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; + machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); } -static void pc_i440fx_2_12_machine_options(MachineClass *m) +static void pc_i440fx_3_0_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } +DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL, + pc_i440fx_3_0_machine_options); + +static void pc_i440fx_2_12_machine_options(MachineClass *m) +{ + pc_i440fx_3_0_machine_options(m); + m->is_default = 0; + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); +} + DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", NULL, pc_i440fx_2_12_machine_options); static void pc_i440fx_2_11_machine_options(MachineClass *m) { pc_i440fx_2_12_machine_options(m); - m->is_default = 0; - m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_11); } @@ -945,6 +957,7 @@ static void pc_i440fx_0_11_machine_options(MachineClass *m) { pc_i440fx_0_12_machine_options(m); m->hw_version = "0.11"; + m->deprecation_reason = "use a newer machine type instead"; SET_MACHINE_COMPAT(m, PC_COMPAT_0_11); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 9ae916327e..532241e3f8 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/loader.h" #include "sysemu/arch_init.h" @@ -45,6 +46,7 @@ #include "hw/i386/ich9.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" +#include "hw/display/ramfb.h" #include "hw/smbios/smbios.h" #include "hw/ide/pci.h" #include "hw/ide/ahci.h" @@ -104,7 +106,7 @@ static void pc_q35_init(MachineState *machine) if (lowmem > pcms->max_ram_below_4g) { lowmem = pcms->max_ram_below_4g; if (machine->ram_size - lowmem > lowmem && - lowmem & ((1ULL << 30) - 1)) { + lowmem & (1 * GiB - 1)) { warn_report("There is possibly poor performance as the ram size " " (0x%" PRIx64 ") is more then twice the size of" " max-ram-below-4g (%"PRIu64") and" @@ -194,7 +196,7 @@ static void pc_q35_init(MachineState *machine) TYPE_HOTPLUG_HANDLER, (Object **)&pcms->acpi_dev, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort); + OBJ_PROP_LINK_STRONG, &error_abort); object_property_set_link(OBJECT(machine), OBJECT(lpc), PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); @@ -305,15 +307,26 @@ static void pc_q35_machine_options(MachineClass *m) m->no_floppy = 1; machine_class_allow_dynamic_sysbus_dev(m, TYPE_AMD_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_INTEL_IOMMU_DEVICE); + machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); m->max_cpus = 288; } -static void pc_q35_2_12_machine_options(MachineClass *m) +static void pc_q35_3_0_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } +DEFINE_Q35_MACHINE(v3_0, "pc-q35-3.0", NULL, + pc_q35_3_0_machine_options); + +static void pc_q35_2_12_machine_options(MachineClass *m) +{ + pc_q35_3_0_machine_options(m); + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); +} + DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL, pc_q35_2_12_machine_options); @@ -323,7 +336,6 @@ static void pc_q35_2_11_machine_options(MachineClass *m) pc_q35_2_12_machine_options(m); pcmc->default_nic_model = "e1000"; - m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_11); } diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 73ac783f20..091e22dd60 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -28,6 +28,7 @@ #include "sysemu/block-backend.h" #include "qemu/error-report.h" #include "qemu/option.h" +#include "qemu/units.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/i386/pc.h" @@ -56,7 +57,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, flash_size = memory_region_size(flash_mem); /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = MIN(flash_size, 128 * 1024); + isa_bios_size = MIN(flash_size, 128 * KiB); isa_bios = g_malloc(sizeof(*isa_bios)); memory_region_init_ram(isa_bios, NULL, "isa-bios", isa_bios_size, &error_fatal); @@ -83,7 +84,7 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in * size. */ -#define FLASH_MAP_BASE_MIN ((hwaddr)(0x100000000ULL - 8*1024*1024)) +#define FLASH_MAP_BASE_MIN ((hwaddr)(4 * GiB - 8 * MiB)) /* This function maps flash drives from 4G downward, in order of their unit * numbers. The mapping starts at unit#0, with unit number increments of 1, and @@ -221,10 +222,7 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) g_free(filename); /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = bios_size; - if (isa_bios_size > (128 * 1024)) { - isa_bios_size = 128 * 1024; - } + isa_bios_size = MIN(bios_size, 128 * KiB); isa_bios = g_malloc(sizeof(*isa_bios)); memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, bios_size - isa_bios_size, isa_bios_size); diff --git a/hw/i386/trace-events b/hw/i386/trace-events index 22d44648af..e14d06ec83 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -39,9 +39,10 @@ vtd_fault_disabled(void) "Fault processing disabled for context entry" vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 -vtd_page_walk_one(uint32_t level, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "detected page level 0x%"PRIx32" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" +vtd_page_walk_one_skip_map(uint64_t iova, uint64_t mask, uint64_t translated) "iova 0x%"PRIx64" mask 0x%"PRIx64" translated 0x%"PRIx64 +vtd_page_walk_one_skip_unmap(uint64_t iova, uint64_t mask) "iova 0x%"PRIx64" mask 0x%"PRIx64 vtd_page_walk_skip_read(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to unable to read" -vtd_page_walk_skip_perm(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to perm empty" vtd_page_walk_skip_reserve(uint64_t iova, uint64_t next) "Page walk skip iova 0x%"PRIx64" - 0x%"PRIx64" due to rsrv set" vtd_switch_address_space(uint8_t bus, uint8_t slot, uint8_t fn, bool on) "Device %02x:%02x.%x switching address space (iommu enabled=%d)" vtd_as_unmap_whole(uint8_t bus, uint8_t slot, uint8_t fn, uint64_t iova, uint64_t size) "Device %02x:%02x.%x start 0x%"PRIx64" size 0x%"PRIx64 diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events index 8dab7bcfe0..8a9077cd4e 100644 --- a/hw/i386/xen/trace-events +++ b/hw/i386/xen/trace-events @@ -15,6 +15,9 @@ cpu_ioreq_pio(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64 cpu_ioreq_pio_read_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio read reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" cpu_ioreq_pio_write_reg(void *req, uint64_t data, uint64_t addr, uint32_t size) "I/O=%p pio write reg data=0x%"PRIx64" port=0x%"PRIx64" size=%d" cpu_ioreq_move(void *req, uint32_t dir, uint32_t df, uint32_t data_is_ptr, uint64_t addr, uint64_t data, uint32_t count, uint32_t size) "I/O=%p copy dir=%d df=%d ptr=%d port=0x%"PRIx64" data=0x%"PRIx64" count=%d size=%d" +xen_map_resource_ioreq(uint32_t id, void *addr) "id: %u addr: %p" +cpu_ioreq_config_read(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" +cpu_ioreq_config_write(void *req, uint32_t sbdf, uint32_t reg, uint32_t size, uint32_t data) "I/O=%p sbdf=0x%x reg=%u size=%u data=0x%x" # xen-mapcache.c xen_map_cache(uint64_t phys_addr) "want 0x%"PRIx64 diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index f24b7d4923..935a3676c8 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -12,6 +12,7 @@ #include "cpu.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "hw/i386/pc.h" #include "hw/i386/apic-msidef.h" #include "hw/xen/xen_common.h" @@ -86,6 +87,14 @@ typedef struct XenPhysmap { QLIST_ENTRY(XenPhysmap) list; } XenPhysmap; +static QLIST_HEAD(, XenPhysmap) xen_physmap; + +typedef struct XenPciDevice { + PCIDevice *pci_dev; + uint32_t sbdf; + QLIST_ENTRY(XenPciDevice) entry; +} XenPciDevice; + typedef struct XenIOState { ioservid_t ioservid; shared_iopage_t *shared_page; @@ -95,7 +104,8 @@ typedef struct XenIOState { CPUState **cpu_by_vcpu_id; /* the evtchn port for polling the notification, */ evtchn_port_t *ioreq_local_port; - /* evtchn local port for buffered io */ + /* evtchn remote and local ports for buffered io */ + evtchn_port_t bufioreq_remote_port; evtchn_port_t bufioreq_local_port; /* the evtchn fd for polling */ xenevtchn_handle *xce_handle; @@ -105,8 +115,8 @@ typedef struct XenIOState { struct xs_handle *xenstore; MemoryListener memory_listener; MemoryListener io_listener; + QLIST_HEAD(, XenPciDevice) dev_list; DeviceListener device_listener; - QLIST_HEAD(, XenPhysmap) physmap; hwaddr free_phys_offset; const XenPhysmap *log_for_dirtybit; @@ -273,14 +283,13 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, g_free(pfn_list); } -static XenPhysmap *get_physmapping(XenIOState *state, - hwaddr start_addr, ram_addr_t size) +static XenPhysmap *get_physmapping(hwaddr start_addr, ram_addr_t size) { XenPhysmap *physmap = NULL; start_addr &= TARGET_PAGE_MASK; - QLIST_FOREACH(physmap, &state->physmap, list) { + QLIST_FOREACH(physmap, &xen_physmap, list) { if (range_covers_byte(physmap->start_addr, physmap->size, start_addr)) { return physmap; } @@ -288,23 +297,21 @@ static XenPhysmap *get_physmapping(XenIOState *state, return NULL; } -#ifdef XEN_COMPAT_PHYSMAP -static hwaddr xen_phys_offset_to_gaddr(hwaddr start_addr, - ram_addr_t size, void *opaque) +static hwaddr xen_phys_offset_to_gaddr(hwaddr phys_offset, ram_addr_t size) { - hwaddr addr = start_addr & TARGET_PAGE_MASK; - XenIOState *xen_io_state = opaque; + hwaddr addr = phys_offset & TARGET_PAGE_MASK; XenPhysmap *physmap = NULL; - QLIST_FOREACH(physmap, &xen_io_state->physmap, list) { + QLIST_FOREACH(physmap, &xen_physmap, list) { if (range_covers_byte(physmap->phys_offset, physmap->size, addr)) { - return physmap->start_addr; + return physmap->start_addr + (phys_offset - physmap->phys_offset); } } - return start_addr; + return phys_offset; } +#ifdef XEN_COMPAT_PHYSMAP static int xen_save_physmap(XenIOState *state, XenPhysmap *physmap) { char path[80], value[17]; @@ -347,14 +354,14 @@ static int xen_add_to_physmap(XenIOState *state, MemoryRegion *mr, hwaddr offset_within_region) { - unsigned long i = 0; + unsigned long nr_pages; int rc = 0; XenPhysmap *physmap = NULL; hwaddr pfn, start_gpfn; hwaddr phys_offset = memory_region_get_ram_addr(mr); const char *mr_name; - if (get_physmapping(state, start_addr, size)) { + if (get_physmapping(start_addr, size)) { return 0; } if (size <= 0) { @@ -383,7 +390,7 @@ go_physmap: physmap->name = mr_name; physmap->phys_offset = phys_offset; - QLIST_INSERT_HEAD(&state->physmap, physmap, list); + QLIST_INSERT_HEAD(&xen_physmap, physmap, list); if (runstate_check(RUN_STATE_INMIGRATE)) { /* Now when we have a physmap entry we can replace a dummy mapping with @@ -396,22 +403,26 @@ go_physmap: pfn = phys_offset >> TARGET_PAGE_BITS; start_gpfn = start_addr >> TARGET_PAGE_BITS; - for (i = 0; i < size >> TARGET_PAGE_BITS; i++) { - unsigned long idx = pfn + i; - xen_pfn_t gpfn = start_gpfn + i; + nr_pages = size >> TARGET_PAGE_BITS; + rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, nr_pages, pfn, + start_gpfn); + if (rc) { + int saved_errno = errno; - rc = xen_xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); - if (rc) { - DPRINTF("add_to_physmap MFN %"PRI_xen_pfn" to PFN %" - PRI_xen_pfn" failed: %d (errno: %d)\n", idx, gpfn, rc, errno); - return -rc; - } + error_report("relocate_memory %lu pages from GFN %"HWADDR_PRIx + " to GFN %"HWADDR_PRIx" failed: %s", + nr_pages, pfn, start_gpfn, strerror(saved_errno)); + errno = saved_errno; + return -1; } - xc_domain_pin_memory_cacheattr(xen_xc, xen_domid, + rc = xendevicemodel_pin_memory_cacheattr(xen_dmod, xen_domid, start_addr >> TARGET_PAGE_BITS, (start_addr + size - 1) >> TARGET_PAGE_BITS, XEN_DOMCTL_MEM_CACHEATTR_WB); + if (rc) { + error_report("pin_memory_cacheattr failed: %s", strerror(errno)); + } return xen_save_physmap(state, physmap); } @@ -419,12 +430,11 @@ static int xen_remove_from_physmap(XenIOState *state, hwaddr start_addr, ram_addr_t size) { - unsigned long i = 0; int rc = 0; XenPhysmap *physmap = NULL; hwaddr phys_offset = 0; - physmap = get_physmapping(state, start_addr, size); + physmap = get_physmapping(start_addr, size); if (physmap == NULL) { return -1; } @@ -438,16 +448,17 @@ static int xen_remove_from_physmap(XenIOState *state, size >>= TARGET_PAGE_BITS; start_addr >>= TARGET_PAGE_BITS; phys_offset >>= TARGET_PAGE_BITS; - for (i = 0; i < size; i++) { - xen_pfn_t idx = start_addr + i; - xen_pfn_t gpfn = phys_offset + i; + rc = xendevicemodel_relocate_memory(xen_dmod, xen_domid, size, start_addr, + phys_offset); + if (rc) { + int saved_errno = errno; - rc = xen_xc_domain_add_to_physmap(xen_xc, xen_domid, XENMAPSPACE_gmfn, idx, gpfn); - if (rc) { - fprintf(stderr, "add_to_physmap MFN %"PRI_xen_pfn" to PFN %" - PRI_xen_pfn" failed: %d (errno: %d)\n", idx, gpfn, rc, errno); - return -rc; - } + error_report("relocate_memory "RAM_ADDR_FMT" pages" + " from GFN %"HWADDR_PRIx + " to GFN %"HWADDR_PRIx" failed: %s", + size, start_addr, phys_offset, strerror(saved_errno)); + errno = saved_errno; + return -1; } QLIST_REMOVE(physmap, list); @@ -565,6 +576,12 @@ static void xen_device_realize(DeviceListener *listener, if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev = g_new(XenPciDevice, 1); + + xendev->pci_dev = pci_dev; + xendev->sbdf = PCI_BUILD_BDF(pci_dev_bus_num(pci_dev), + pci_dev->devfn); + QLIST_INSERT_HEAD(&state->dev_list, xendev, entry); xen_map_pcidev(xen_domid, state->ioservid, pci_dev); } @@ -577,8 +594,17 @@ static void xen_device_unrealize(DeviceListener *listener, if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pci_dev = PCI_DEVICE(dev); + XenPciDevice *xendev, *next; xen_unmap_pcidev(xen_domid, state->ioservid, pci_dev); + + QLIST_FOREACH_SAFE(xendev, &state->dev_list, entry, next) { + if (xendev->pci_dev == pci_dev) { + QLIST_REMOVE(xendev, entry); + g_free(xendev); + break; + } + } } } @@ -592,7 +618,7 @@ static void xen_sync_dirty_bitmap(XenIOState *state, int rc, i, j; const XenPhysmap *physmap = NULL; - physmap = get_physmapping(state, start_addr, size); + physmap = get_physmapping(start_addr, size); if (physmap == NULL) { /* not handled */ return; @@ -899,6 +925,62 @@ static void cpu_ioreq_move(ioreq_t *req) } } +static void cpu_ioreq_config(XenIOState *state, ioreq_t *req) +{ + uint32_t sbdf = req->addr >> 32; + uint32_t reg = req->addr; + XenPciDevice *xendev; + + if (req->size != sizeof(uint8_t) && req->size != sizeof(uint16_t) && + req->size != sizeof(uint32_t)) { + hw_error("PCI config access: bad size (%u)", req->size); + } + + if (req->count != 1) { + hw_error("PCI config access: bad count (%u)", req->count); + } + + QLIST_FOREACH(xendev, &state->dev_list, entry) { + if (xendev->sbdf != sbdf) { + continue; + } + + if (!req->data_is_ptr) { + if (req->dir == IOREQ_READ) { + req->data = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, req->data); + } else if (req->dir == IOREQ_WRITE) { + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, req->data); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->data, req->size); + } + } else { + uint32_t tmp; + + if (req->dir == IOREQ_READ) { + tmp = pci_host_config_read_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + req->size); + trace_cpu_ioreq_config_read(req, xendev->sbdf, reg, + req->size, tmp); + write_phys_req_item(req->data, req, 0, &tmp); + } else if (req->dir == IOREQ_WRITE) { + read_phys_req_item(req->data, req, 0, &tmp); + trace_cpu_ioreq_config_write(req, xendev->sbdf, reg, + req->size, tmp); + pci_host_config_write_common( + xendev->pci_dev, reg, PCI_CONFIG_SPACE_SIZE, + tmp, req->size); + } + } + } +} + static void regs_to_cpu(vmware_regs_t *vmport_regs, ioreq_t *req) { X86CPU *cpu; @@ -971,27 +1053,9 @@ static void handle_ioreq(XenIOState *state, ioreq_t *req) case IOREQ_TYPE_INVALIDATE: xen_invalidate_map_cache(); break; - case IOREQ_TYPE_PCI_CONFIG: { - uint32_t sbdf = req->addr >> 32; - uint32_t val; - - /* Fake a write to port 0xCF8 so that - * the config space access will target the - * correct device model. - */ - val = (1u << 31) | - ((req->addr & 0x0f00) << 16) | - ((sbdf & 0xffff) << 8) | - (req->addr & 0xfc); - do_outp(0xcf8, 4, val); - - /* Now issue the config space access via - * port 0xCFC - */ - req->addr = 0xcfc | (req->addr & 0x03); - cpu_ioreq_pio(req); + case IOREQ_TYPE_PCI_CONFIG: + cpu_ioreq_config(state, req); break; - } default: hw_error("Invalid ioreq type 0x%x\n", req->type); } @@ -1217,7 +1281,7 @@ static void xen_read_physmap(XenIOState *state) xen_domid, entries[i]); physmap->name = xs_read(state->xenstore, 0, path, &len); - QLIST_INSERT_HEAD(&state->physmap, physmap, list); + QLIST_INSERT_HEAD(&xen_physmap, physmap, list); } free(entries); } @@ -1232,12 +1296,88 @@ static void xen_wakeup_notifier(Notifier *notifier, void *data) xc_set_hvm_param(xen_xc, xen_domid, HVM_PARAM_ACPI_S_STATE, 0); } +static int xen_map_ioreq_server(XenIOState *state) +{ + void *addr = NULL; + xenforeignmemory_resource_handle *fres; + xen_pfn_t ioreq_pfn; + xen_pfn_t bufioreq_pfn; + evtchn_port_t bufioreq_evtchn; + int rc; + + /* + * Attempt to map using the resource API and fall back to normal + * foreign mapping if this is not supported. + */ + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_bufioreq != 0); + QEMU_BUILD_BUG_ON(XENMEM_resource_ioreq_server_frame_ioreq(0) != 1); + fres = xenforeignmemory_map_resource(xen_fmem, xen_domid, + XENMEM_resource_ioreq_server, + state->ioservid, 0, 2, + &addr, + PROT_READ | PROT_WRITE, 0); + if (fres != NULL) { + trace_xen_map_resource_ioreq(state->ioservid, addr); + state->buffered_io_page = addr; + state->shared_page = addr + TARGET_PAGE_SIZE; + } else if (errno != EOPNOTSUPP) { + error_report("failed to map ioreq server resources: error %d handle=%p", + errno, xen_xc); + return -1; + } + + rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, + (state->shared_page == NULL) ? + &ioreq_pfn : NULL, + (state->buffered_io_page == NULL) ? + &bufioreq_pfn : NULL, + &bufioreq_evtchn); + if (rc < 0) { + error_report("failed to get ioreq server info: error %d handle=%p", + errno, xen_xc); + return rc; + } + + if (state->shared_page == NULL) { + DPRINTF("shared page at pfn %lx\n", ioreq_pfn); + + state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &ioreq_pfn, NULL); + if (state->shared_page == NULL) { + error_report("map shared IO page returned error %d handle=%p", + errno, xen_xc); + } + } + + if (state->buffered_io_page == NULL) { + DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); + + state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, + PROT_READ | PROT_WRITE, + 1, &bufioreq_pfn, + NULL); + if (state->buffered_io_page == NULL) { + error_report("map buffered IO page returned error %d", errno); + return -1; + } + } + + if (state->shared_page == NULL || state->buffered_io_page == NULL) { + return -1; + } + + DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); + + state->bufioreq_remote_port = bufioreq_evtchn; + + return 0; +} + void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) { int i, rc; xen_pfn_t ioreq_pfn; - xen_pfn_t bufioreq_pfn; - evtchn_port_t bufioreq_evtchn; XenIOState *state; state = g_malloc0(sizeof (XenIOState)); @@ -1254,14 +1394,6 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - if (xen_domid_restrict) { - rc = xen_restrict(xen_domid); - if (rc < 0) { - error_report("failed to restrict: error %d", errno); - goto err; - } - } - xen_create_ioreq_server(xen_domid, &state->ioservid); state->exit.notify = xen_exit_notifier; @@ -1273,25 +1405,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) state->wakeup.notify = xen_wakeup_notifier; qemu_register_wakeup_notifier(&state->wakeup); - rc = xen_get_ioreq_server_info(xen_domid, state->ioservid, - &ioreq_pfn, &bufioreq_pfn, - &bufioreq_evtchn); + rc = xen_map_ioreq_server(state); if (rc < 0) { - error_report("failed to get ioreq server info: error %d handle=%p", - errno, xen_xc); - goto err; - } - - DPRINTF("shared page at pfn %lx\n", ioreq_pfn); - DPRINTF("buffered io page at pfn %lx\n", bufioreq_pfn); - DPRINTF("buffered io evtchn is %x\n", bufioreq_evtchn); - - state->shared_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ|PROT_WRITE, - 1, &ioreq_pfn, NULL); - if (state->shared_page == NULL) { - error_report("map shared IO page returned error %d handle=%p", - errno, xen_xc); goto err; } @@ -1312,14 +1427,6 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } - state->buffered_io_page = xenforeignmemory_map(xen_fmem, xen_domid, - PROT_READ|PROT_WRITE, - 1, &bufioreq_pfn, NULL); - if (state->buffered_io_page == NULL) { - error_report("map buffered IO page returned error %d", errno); - goto err; - } - /* Note: cpus is empty at this point in init */ state->cpu_by_vcpu_id = g_malloc0(max_cpus * sizeof(CPUState *)); @@ -1344,7 +1451,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) } rc = xenevtchn_bind_interdomain(state->xce_handle, xen_domid, - bufioreq_evtchn); + state->bufioreq_remote_port); if (rc == -1) { error_report("buffered evtchn bind error %d", errno); goto err; @@ -1362,7 +1469,6 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) qemu_add_vm_change_state_handler(xen_hvm_change_state_handler, state); state->memory_listener = xen_memory_listener; - QLIST_INIT(&state->physmap); memory_listener_register(&state->memory_listener, &address_space_memory); state->log_for_dirtybit = NULL; @@ -1370,6 +1476,7 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) memory_listener_register(&state->io_listener, &address_space_io); state->device_listener = xen_device_listener; + QLIST_INIT(&state->dev_list); device_listener_register(&state->device_listener); /* Initialize backend core & drivers */ @@ -1378,6 +1485,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) goto err; } xen_be_register_common(); + + QLIST_INIT(&xen_physmap); xen_read_physmap(state); /* Disable ACPI build because Xen handles it */ @@ -1394,13 +1503,26 @@ void destroy_hvm_domain(bool reboot) { xc_interface *xc_handle; int sts; + int rc; + + unsigned int reason = reboot ? SHUTDOWN_reboot : SHUTDOWN_poweroff; + + if (xen_dmod) { + rc = xendevicemodel_shutdown(xen_dmod, xen_domid, reason); + if (!rc) { + return; + } + if (errno != ENOTTY /* old Xen */) { + perror("xendevicemodel_shutdown failed"); + } + /* well, try the old thing then */ + } xc_handle = xc_interface_open(0, 0, 0); if (xc_handle == NULL) { fprintf(stderr, "Cannot acquire xenctrl handle\n"); } else { - sts = xc_domain_shutdown(xc_handle, xen_domid, - reboot ? SHUTDOWN_reboot : SHUTDOWN_poweroff); + sts = xc_domain_shutdown(xc_handle, xen_domid, reason); if (sts != 0) { fprintf(stderr, "xc_domain_shutdown failed to issue %s, " "sts %d, %s\n", reboot ? "reboot" : "poweroff", @@ -1436,6 +1558,8 @@ void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) int rc; ram_addr_t start_pfn, nb_pages; + start = xen_phys_offset_to_gaddr(start, length); + if (length == 0) { length = TARGET_PAGE_SIZE; } diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index efa35dc6e0..4e4f069a24 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -9,12 +9,12 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include #include "hw/xen/xen_backend.h" -#include "sysemu/blockdev.h" #include "qemu/bitmap.h" #include @@ -47,7 +47,7 @@ * From empirical tests I observed that qemu use 75MB more than the * max_mcache_size. */ -#define NON_MCACHE_MEMORY_SIZE (80 * 1024 * 1024) +#define NON_MCACHE_MEMORY_SIZE (80 * MiB) typedef struct MapCacheEntry { hwaddr paddr_index; @@ -319,7 +319,7 @@ tryagain: mapcache->last_entry = NULL; #ifdef XEN_COMPAT_PHYSMAP if (!translated && mapcache->phys_offset_to_gaddr) { - phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size, mapcache->opaque); + phys_addr = mapcache->phys_offset_to_gaddr(phys_addr, size); translated = true; goto tryagain; } diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index f748823658..a146f1883a 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -71,6 +71,16 @@ static const MemoryRegionOps xen_pv_mmio_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static const VMStateDescription vmstate_xen_pvdevice = { + .name = "xen-pvdevice", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(parent_obj, XenPVDevice), + VMSTATE_END_OF_LIST() + } +}; + static void xen_pv_realize(PCIDevice *pci_dev, Error **errp) { XenPVDevice *d = XEN_PV_DEVICE(pci_dev); @@ -120,6 +130,7 @@ static void xen_pv_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SYSTEM_OTHER; dc->desc = "Xen PV Device"; dc->props = xen_pv_props; + dc->vmsd = &vmstate_xen_pvdevice; } static const TypeInfo xen_pv_type_info = { diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c index 5397483fd8..f98e6cb3d4 100644 --- a/hw/ide/ahci-allwinner.c +++ b/hw/ide/ahci-allwinner.c @@ -20,10 +20,13 @@ #include "qemu/error-report.h" #include "sysemu/dma.h" #include "hw/ide/internal.h" -#include "hw/ide/ahci_internal.h" +#include "ahci_internal.h" #include "trace.h" +#define ALLWINNER_AHCI(obj) \ + OBJECT_CHECK(AllwinnerAHCIState, (obj), TYPE_ALLWINNER_AHCI) + #define ALLWINNER_AHCI_BISTAFR ((0xa0 - ALLWINNER_AHCI_MMIO_OFF) / 4) #define ALLWINNER_AHCI_BISTCR ((0xa4 - ALLWINNER_AHCI_MMIO_OFF) / 4) #define ALLWINNER_AHCI_BISTFCTR ((0xa8 - ALLWINNER_AHCI_MMIO_OFF) / 4) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index e22d7be05f..d700ca973b 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -27,11 +27,12 @@ #include "hw/pci/pci.h" #include "qemu/error-report.h" +#include "qemu/log.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "hw/ide/internal.h" #include "hw/ide/pci.h" -#include "hw/ide/ahci_internal.h" +#include "ahci_internal.h" #include "trace.h" @@ -46,6 +47,44 @@ static bool ahci_map_fis_address(AHCIDevice *ad); static void ahci_unmap_clb_address(AHCIDevice *ad); static void ahci_unmap_fis_address(AHCIDevice *ad); +static const char *AHCIHostReg_lookup[AHCI_HOST_REG__COUNT] = { + [AHCI_HOST_REG_CAP] = "CAP", + [AHCI_HOST_REG_CTL] = "GHC", + [AHCI_HOST_REG_IRQ_STAT] = "IS", + [AHCI_HOST_REG_PORTS_IMPL] = "PI", + [AHCI_HOST_REG_VERSION] = "VS", + [AHCI_HOST_REG_CCC_CTL] = "CCC_CTL", + [AHCI_HOST_REG_CCC_PORTS] = "CCC_PORTS", + [AHCI_HOST_REG_EM_LOC] = "EM_LOC", + [AHCI_HOST_REG_EM_CTL] = "EM_CTL", + [AHCI_HOST_REG_CAP2] = "CAP2", + [AHCI_HOST_REG_BOHC] = "BOHC", +}; + +static const char *AHCIPortReg_lookup[AHCI_PORT_REG__COUNT] = { + [AHCI_PORT_REG_LST_ADDR] = "PxCLB", + [AHCI_PORT_REG_LST_ADDR_HI] = "PxCLBU", + [AHCI_PORT_REG_FIS_ADDR] = "PxFB", + [AHCI_PORT_REG_FIS_ADDR_HI] = "PxFBU", + [AHCI_PORT_REG_IRQ_STAT] = "PxIS", + [AHCI_PORT_REG_IRQ_MASK] = "PXIE", + [AHCI_PORT_REG_CMD] = "PxCMD", + [7] = "Reserved", + [AHCI_PORT_REG_TFDATA] = "PxTFD", + [AHCI_PORT_REG_SIG] = "PxSIG", + [AHCI_PORT_REG_SCR_STAT] = "PxSSTS", + [AHCI_PORT_REG_SCR_CTL] = "PxSCTL", + [AHCI_PORT_REG_SCR_ERR] = "PxSERR", + [AHCI_PORT_REG_SCR_ACT] = "PxSACT", + [AHCI_PORT_REG_CMD_ISSUE] = "PxCI", + [AHCI_PORT_REG_SCR_NOTIF] = "PxSNTF", + [AHCI_PORT_REG_FIS_CTL] = "PxFBS", + [AHCI_PORT_REG_DEV_SLEEP] = "PxDEVSLP", + [18 ... 27] = "Reserved", + [AHCI_PORT_REG_VENDOR_1 ... + AHCI_PORT_REG_VENDOR_4] = "PxVS", +}; + static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = { [AHCI_PORT_IRQ_BIT_DHRS] = "DHRS", [AHCI_PORT_IRQ_BIT_PSS] = "PSS", @@ -68,41 +107,42 @@ static const char *AHCIPortIRQ_lookup[AHCI_PORT_IRQ__COUNT] = { [AHCI_PORT_IRQ_BIT_CPDS] = "CPDS" }; -static uint32_t ahci_port_read(AHCIState *s, int port, int offset) +static uint32_t ahci_port_read(AHCIState *s, int port, int offset) { uint32_t val; - AHCIPortRegs *pr; - pr = &s->dev[port].port_regs; + AHCIPortRegs *pr = &s->dev[port].port_regs; + enum AHCIPortReg regnum = offset / sizeof(uint32_t); + assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t))); - switch (offset) { - case PORT_LST_ADDR: + switch (regnum) { + case AHCI_PORT_REG_LST_ADDR: val = pr->lst_addr; break; - case PORT_LST_ADDR_HI: + case AHCI_PORT_REG_LST_ADDR_HI: val = pr->lst_addr_hi; break; - case PORT_FIS_ADDR: + case AHCI_PORT_REG_FIS_ADDR: val = pr->fis_addr; break; - case PORT_FIS_ADDR_HI: + case AHCI_PORT_REG_FIS_ADDR_HI: val = pr->fis_addr_hi; break; - case PORT_IRQ_STAT: + case AHCI_PORT_REG_IRQ_STAT: val = pr->irq_stat; break; - case PORT_IRQ_MASK: + case AHCI_PORT_REG_IRQ_MASK: val = pr->irq_mask; break; - case PORT_CMD: + case AHCI_PORT_REG_CMD: val = pr->cmd; break; - case PORT_TFDATA: + case AHCI_PORT_REG_TFDATA: val = pr->tfdata; break; - case PORT_SIG: + case AHCI_PORT_REG_SIG: val = pr->sig; break; - case PORT_SCR_STAT: + case AHCI_PORT_REG_SCR_STAT: if (s->dev[port].port.ifs[0].blk) { val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP | SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE; @@ -110,28 +150,29 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) val = SATA_SCR_SSTATUS_DET_NODEV; } break; - case PORT_SCR_CTL: + case AHCI_PORT_REG_SCR_CTL: val = pr->scr_ctl; break; - case PORT_SCR_ERR: + case AHCI_PORT_REG_SCR_ERR: val = pr->scr_err; break; - case PORT_SCR_ACT: + case AHCI_PORT_REG_SCR_ACT: val = pr->scr_act; break; - case PORT_CMD_ISSUE: + case AHCI_PORT_REG_CMD_ISSUE: val = pr->cmd_issue; break; - case PORT_RESERVED: default: + trace_ahci_port_read_default(s, port, AHCIPortReg_lookup[regnum], + offset); val = 0; } - trace_ahci_port_read(s, port, offset, val); + trace_ahci_port_read(s, port, AHCIPortReg_lookup[regnum], offset, val); return val; } -static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) +static void ahci_irq_raise(AHCIState *s) { DeviceState *dev_state = s->container; PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), @@ -146,7 +187,7 @@ static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) } } -static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev) +static void ahci_irq_lower(AHCIState *s) { DeviceState *dev_state = s->container; PCIDevice *pci_dev = (PCIDevice *) object_dynamic_cast(OBJECT(dev_state), @@ -174,9 +215,9 @@ static void ahci_check_irq(AHCIState *s) trace_ahci_check_irq(s, old_irq, s->control_regs.irqstatus); if (s->control_regs.irqstatus && (s->control_regs.ghc & HOST_CTL_IRQ_EN)) { - ahci_irq_raise(s, NULL); + ahci_irq_raise(s); } else { - ahci_irq_lower(s, NULL); + ahci_irq_lower(s); } } @@ -253,85 +294,88 @@ static int ahci_cond_start_engines(AHCIDevice *ad) return 0; } -static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) +static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) { AHCIPortRegs *pr = &s->dev[port].port_regs; + enum AHCIPortReg regnum = offset / sizeof(uint32_t); + assert(regnum < (AHCI_PORT_ADDR_OFFSET_LEN / sizeof(uint32_t))); + trace_ahci_port_write(s, port, AHCIPortReg_lookup[regnum], offset, val); - trace_ahci_port_write(s, port, offset, val); - switch (offset) { - case PORT_LST_ADDR: - pr->lst_addr = val; - break; - case PORT_LST_ADDR_HI: - pr->lst_addr_hi = val; - break; - case PORT_FIS_ADDR: - pr->fis_addr = val; - break; - case PORT_FIS_ADDR_HI: - pr->fis_addr_hi = val; - break; - case PORT_IRQ_STAT: - pr->irq_stat &= ~val; - ahci_check_irq(s); - break; - case PORT_IRQ_MASK: - pr->irq_mask = val & 0xfdc000ff; - ahci_check_irq(s); - break; - case PORT_CMD: - /* Block any Read-only fields from being set; - * including LIST_ON and FIS_ON. - * The spec requires to set ICC bits to zero after the ICC change - * is done. We don't support ICC state changes, therefore always - * force the ICC bits to zero. - */ - pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | - (val & ~(PORT_CMD_RO_MASK|PORT_CMD_ICC_MASK)); + switch (regnum) { + case AHCI_PORT_REG_LST_ADDR: + pr->lst_addr = val; + break; + case AHCI_PORT_REG_LST_ADDR_HI: + pr->lst_addr_hi = val; + break; + case AHCI_PORT_REG_FIS_ADDR: + pr->fis_addr = val; + break; + case AHCI_PORT_REG_FIS_ADDR_HI: + pr->fis_addr_hi = val; + break; + case AHCI_PORT_REG_IRQ_STAT: + pr->irq_stat &= ~val; + ahci_check_irq(s); + break; + case AHCI_PORT_REG_IRQ_MASK: + pr->irq_mask = val & 0xfdc000ff; + ahci_check_irq(s); + break; + case AHCI_PORT_REG_CMD: + /* Block any Read-only fields from being set; + * including LIST_ON and FIS_ON. + * The spec requires to set ICC bits to zero after the ICC change + * is done. We don't support ICC state changes, therefore always + * force the ICC bits to zero. + */ + pr->cmd = (pr->cmd & PORT_CMD_RO_MASK) | + (val & ~(PORT_CMD_RO_MASK | PORT_CMD_ICC_MASK)); - /* Check FIS RX and CLB engines */ - ahci_cond_start_engines(&s->dev[port]); + /* Check FIS RX and CLB engines */ + ahci_cond_start_engines(&s->dev[port]); - /* XXX usually the FIS would be pending on the bus here and - issuing deferred until the OS enables FIS receival. - Instead, we only submit it once - which works in most - cases, but is a hack. */ - if ((pr->cmd & PORT_CMD_FIS_ON) && - !s->dev[port].init_d2h_sent) { - ahci_init_d2h(&s->dev[port]); - } + /* XXX usually the FIS would be pending on the bus here and + issuing deferred until the OS enables FIS receival. + Instead, we only submit it once - which works in most + cases, but is a hack. */ + if ((pr->cmd & PORT_CMD_FIS_ON) && + !s->dev[port].init_d2h_sent) { + ahci_init_d2h(&s->dev[port]); + } - check_cmd(s, port); - break; - case PORT_TFDATA: - /* Read Only. */ - break; - case PORT_SIG: - /* Read Only */ - break; - case PORT_SCR_STAT: - /* Read Only */ - break; - case PORT_SCR_CTL: - if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && - ((val & AHCI_SCR_SCTL_DET) == 0)) { - ahci_reset_port(s, port); - } - pr->scr_ctl = val; - break; - case PORT_SCR_ERR: - pr->scr_err &= ~val; - break; - case PORT_SCR_ACT: - /* RW1 */ - pr->scr_act |= val; - break; - case PORT_CMD_ISSUE: - pr->cmd_issue |= val; - check_cmd(s, port); - break; - default: - break; + check_cmd(s, port); + break; + case AHCI_PORT_REG_TFDATA: + case AHCI_PORT_REG_SIG: + case AHCI_PORT_REG_SCR_STAT: + /* Read Only */ + break; + case AHCI_PORT_REG_SCR_CTL: + if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && + ((val & AHCI_SCR_SCTL_DET) == 0)) { + ahci_reset_port(s, port); + } + pr->scr_ctl = val; + break; + case AHCI_PORT_REG_SCR_ERR: + pr->scr_err &= ~val; + break; + case AHCI_PORT_REG_SCR_ACT: + /* RW1 */ + pr->scr_act |= val; + break; + case AHCI_PORT_REG_CMD_ISSUE: + pr->cmd_issue |= val; + check_cmd(s, port); + break; + default: + trace_ahci_port_write_unimpl(s, port, AHCIPortReg_lookup[regnum], + offset, val); + qemu_log_mask(LOG_UNIMP, "Attempted write to unimplemented register: " + "AHCI port %d register %s, offset 0x%x: 0x%"PRIx32, + port, AHCIPortReg_lookup[regnum], offset, val); + break; } } @@ -341,28 +385,37 @@ static uint64_t ahci_mem_read_32(void *opaque, hwaddr addr) uint32_t val = 0; if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { - switch (addr) { - case HOST_CAP: + enum AHCIHostReg regnum = addr / 4; + assert(regnum < AHCI_HOST_REG__COUNT); + + switch (regnum) { + case AHCI_HOST_REG_CAP: val = s->control_regs.cap; break; - case HOST_CTL: + case AHCI_HOST_REG_CTL: val = s->control_regs.ghc; break; - case HOST_IRQ_STAT: + case AHCI_HOST_REG_IRQ_STAT: val = s->control_regs.irqstatus; break; - case HOST_PORTS_IMPL: + case AHCI_HOST_REG_PORTS_IMPL: val = s->control_regs.impl; break; - case HOST_VERSION: + case AHCI_HOST_REG_VERSION: val = s->control_regs.version; break; + default: + trace_ahci_mem_read_32_host_default(s, AHCIHostReg_lookup[regnum], + addr); } + trace_ahci_mem_read_32_host(s, AHCIHostReg_lookup[regnum], addr, val); } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && (addr < (AHCI_PORT_REGS_START_ADDR + (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, addr & AHCI_PORT_ADDR_OFFSET_MASK); + } else { + trace_ahci_mem_read_32_default(s, addr, val); } trace_ahci_mem_read_32(s, addr, val); @@ -387,7 +440,7 @@ static uint64_t ahci_mem_read(void *opaque, hwaddr addr, unsigned size) if (ofst + size <= 4) { val = lo >> (ofst * 8); } else { - g_assert_cmpint(size, >, 1); + g_assert(size > 1); /* If the 64bit read is unaligned, we will produce undefined * results. AHCI does not support unaligned 64bit reads. */ @@ -415,38 +468,53 @@ static void ahci_mem_write(void *opaque, hwaddr addr, } if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { - switch (addr) { - case HOST_CAP: /* R/WO, RO */ - /* FIXME handle R/WO */ - break; - case HOST_CTL: /* R/W */ - if (val & HOST_CTL_RESET) { - ahci_reset(s); - } else { - s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN; - ahci_check_irq(s); - } - break; - case HOST_IRQ_STAT: /* R/WC, RO */ - s->control_regs.irqstatus &= ~val; + enum AHCIHostReg regnum = addr / 4; + assert(regnum < AHCI_HOST_REG__COUNT); + + switch (regnum) { + case AHCI_HOST_REG_CAP: /* R/WO, RO */ + /* FIXME handle R/WO */ + break; + case AHCI_HOST_REG_CTL: /* R/W */ + if (val & HOST_CTL_RESET) { + ahci_reset(s); + } else { + s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN; ahci_check_irq(s); - break; - case HOST_PORTS_IMPL: /* R/WO, RO */ - /* FIXME handle R/WO */ - break; - case HOST_VERSION: /* RO */ - /* FIXME report write? */ - break; - default: - trace_ahci_mem_write_unknown(s, size, addr, val); + } + break; + case AHCI_HOST_REG_IRQ_STAT: /* R/WC, RO */ + s->control_regs.irqstatus &= ~val; + ahci_check_irq(s); + break; + case AHCI_HOST_REG_PORTS_IMPL: /* R/WO, RO */ + /* FIXME handle R/WO */ + break; + case AHCI_HOST_REG_VERSION: /* RO */ + /* FIXME report write? */ + break; + default: + qemu_log_mask(LOG_UNIMP, + "Attempted write to unimplemented register: " + "AHCI host register %s, " + "offset 0x%"PRIx64": 0x%"PRIx64, + AHCIHostReg_lookup[regnum], addr, val); + trace_ahci_mem_write_host_unimpl(s, size, + AHCIHostReg_lookup[regnum], addr); } + trace_ahci_mem_write_host(s, size, AHCIHostReg_lookup[regnum], + addr, val); } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && (addr < (AHCI_PORT_REGS_START_ADDR + - (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { + (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, addr & AHCI_PORT_ADDR_OFFSET_MASK, val); + } else { + qemu_log_mask(LOG_UNIMP, "Attempted write to unimplemented register: " + "AHCI global register at offset 0x%"PRIx64": 0x%"PRIx64, + addr, val); + trace_ahci_mem_write_unimpl(s, size, addr, val); } - } static const MemoryRegionOps ahci_mem_ops = { @@ -532,13 +600,6 @@ static void ahci_check_cmd_bh(void *opaque) qemu_bh_delete(ad->check_bh); ad->check_bh = NULL; - if ((ad->busy_slot != -1) && - !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { - /* no longer busy */ - ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot); - ad->busy_slot = -1; - } - check_cmd(ad->hba, ad->port_no); } @@ -740,7 +801,7 @@ static void ahci_write_fis_sdb(AHCIState *s, NCQTransferState *ncq_tfs) } } -static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) +static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len, bool pio_fis_i) { AHCIPortRegs *pr = &ad->port_regs; uint8_t *pio_fis; @@ -753,7 +814,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) pio_fis = &ad->res_fis[RES_FIS_PSFIS]; pio_fis[0] = SATA_FIS_TYPE_PIO_SETUP; - pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); + pio_fis[1] = (pio_fis_i ? (1 << 6) : 0); pio_fis[2] = s->status; pio_fis[3] = s->error; @@ -781,8 +842,6 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) if (pio_fis[2] & ERR_STAT) { ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES); } - - ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS); } static bool ahci_write_fis_d2h(AHCIDevice *ad) @@ -799,7 +858,7 @@ static bool ahci_write_fis_d2h(AHCIDevice *ad) d2h_fis = &ad->res_fis[RES_FIS_RFIS]; d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H; - d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); + d2h_fis[1] = (1 << 6); /* interrupt bit */ d2h_fis[2] = s->status; d2h_fis[3] = s->error; @@ -1197,12 +1256,10 @@ static void handle_reg_h2d_fis(AHCIState *s, int port, trace_handle_reg_h2d_fis_dump(s, port, pretty_fis); g_free(pretty_fis); } - s->dev[port].done_atapi_packet = false; - /* XXX send PIO setup FIS */ } ide_state->error = 0; - + s->dev[port].done_first_drq = false; /* Reset transferred byte counter */ cmd->status = 0; @@ -1280,8 +1337,8 @@ out: return 0; } -/* DMA dev <-> ram */ -static void ahci_start_transfer(IDEDMA *dma) +/* Transfer PIO data between RAM and device */ +static void ahci_pio_transfer(IDEDMA *dma) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; @@ -1291,11 +1348,23 @@ static void ahci_start_transfer(IDEDMA *dma) int is_write = opts & AHCI_CMD_WRITE; int is_atapi = opts & AHCI_CMD_ATAPI; int has_sglist = 0; + bool pio_fis_i; - if (is_atapi && !ad->done_atapi_packet) { + /* The PIO Setup FIS is received prior to transfer, but the interrupt + * is only triggered after data is received. + * + * The device only sets the 'I' bit in the PIO Setup FIS for device->host + * requests (see "DPIOI1" in the SATA spec), or for host->device DRQs after + * the first (see "DPIOO1"). The latter is consistent with the spec's + * description of the PACKET protocol, where the command part of ATAPI requests + * ("DPKT0") has the 'I' bit clear, while the data part of PIO ATAPI requests + * ("DPKT4a" and "DPKT7") has the 'I' bit set for both directions for all DRQs. + */ + pio_fis_i = ad->done_first_drq || (!is_atapi && !is_write); + ahci_write_fis_pio(ad, size, pio_fis_i); + + if (is_atapi && !ad->done_first_drq) { /* already prepopulated iobuffer */ - ad->done_atapi_packet = true; - size = 0; goto out; } @@ -1303,9 +1372,9 @@ static void ahci_start_transfer(IDEDMA *dma) has_sglist = 1; } - trace_ahci_start_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read", - size, is_atapi ? "atapi" : "ata", - has_sglist ? "" : "o"); + trace_ahci_pio_transfer(ad->hba, ad->port_no, is_write ? "writ" : "read", + size, is_atapi ? "atapi" : "ata", + has_sglist ? "" : "o"); if (has_sglist && size) { if (is_write) { @@ -1315,18 +1384,16 @@ static void ahci_start_transfer(IDEDMA *dma) } } + /* Update number of transferred bytes, destroy sglist */ + dma_buf_commit(s, size); + out: /* declare that we processed everything */ s->data_ptr = s->data_end; - /* Update number of transferred bytes, destroy sglist */ - dma_buf_commit(s, size); - - s->end_transfer_func(s); - - if (!(s->status & DRQ_STAT)) { - /* done with PIO send/receive */ - ahci_write_fis_pio(ad, le32_to_cpu(ad->cur_cmd->status)); + ad->done_first_drq = true; + if (pio_fis_i) { + ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS); } } @@ -1425,11 +1492,16 @@ static void ahci_cmd_done(IDEDMA *dma) trace_ahci_cmd_done(ad->hba, ad->port_no); + /* no longer busy */ + if (ad->busy_slot != -1) { + ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot); + ad->busy_slot = -1; + } + /* update d2h status */ ahci_write_fis_d2h(ad); - if (!ad->check_bh) { - /* maybe we still have something to process, check later */ + if (ad->port_regs.cmd_issue && !ad->check_bh) { ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); qemu_bh_schedule(ad->check_bh); } @@ -1443,7 +1515,7 @@ static const IDEDMAOps ahci_dma_ops = { .start_dma = ahci_start_dma, .restart = ahci_restart, .restart_dma = ahci_restart_dma, - .start_transfer = ahci_start_transfer, + .pio_transfer = ahci_pio_transfer, .prepare_buf = ahci_dma_prepare_buf, .commit_buf = ahci_commit_buf, .rw_buf = ahci_dma_rw_buf, @@ -1568,7 +1640,7 @@ static const VMStateDescription vmstate_ahci_device = { VMSTATE_UINT32(port_regs.scr_err, AHCIDevice), VMSTATE_UINT32(port_regs.scr_act, AHCIDevice), VMSTATE_UINT32(port_regs.cmd_issue, AHCIDevice), - VMSTATE_BOOL(done_atapi_packet, AHCIDevice), + VMSTATE_BOOL(done_first_drq, AHCIDevice), VMSTATE_INT32(busy_slot, AHCIDevice), VMSTATE_BOOL(init_d2h_sent, AHCIDevice), VMSTATE_STRUCT_ARRAY(ncq_tfs, AHCIDevice, AHCI_MAX_CMDS, diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci_internal.h index 8c755d4ca1..9b7fa8fc7d 100644 --- a/hw/ide/ahci_internal.h +++ b/hw/ide/ahci_internal.h @@ -55,11 +55,20 @@ #define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ /* global controller registers */ -#define HOST_CAP 0x00 /* host capabilities */ -#define HOST_CTL 0x04 /* global host control */ -#define HOST_IRQ_STAT 0x08 /* interrupt status */ -#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ -#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ +enum AHCIHostReg { + AHCI_HOST_REG_CAP = 0, /* CAP: host capabilities */ + AHCI_HOST_REG_CTL = 1, /* GHC: global host control */ + AHCI_HOST_REG_IRQ_STAT = 2, /* IS: interrupt status */ + AHCI_HOST_REG_PORTS_IMPL = 3, /* PI: bitmap of implemented ports */ + AHCI_HOST_REG_VERSION = 4, /* VS: AHCI spec. version compliancy */ + AHCI_HOST_REG_CCC_CTL = 5, /* CCC_CTL: CCC Control */ + AHCI_HOST_REG_CCC_PORTS = 6, /* CCC_PORTS: CCC Ports */ + AHCI_HOST_REG_EM_LOC = 7, /* EM_LOC: Enclosure Mgmt Location */ + AHCI_HOST_REG_EM_CTL = 8, /* EM_CTL: Enclosure Mgmt Control */ + AHCI_HOST_REG_CAP2 = 9, /* CAP2: host capabilities, extended */ + AHCI_HOST_REG_BOHC = 10, /* BOHC: firmare/os handoff ctrl & status */ + AHCI_HOST_REG__COUNT = 11 +}; /* HOST_CTL bits */ #define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ @@ -75,21 +84,32 @@ #define HOST_CAP_64 (1U << 31) /* PCI DAC (64-bit DMA) support */ /* registers for each SATA port */ -#define PORT_LST_ADDR 0x00 /* command list DMA addr */ -#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ -#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ -#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ -#define PORT_IRQ_STAT 0x10 /* interrupt status */ -#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ -#define PORT_CMD 0x18 /* port command */ -#define PORT_TFDATA 0x20 /* taskfile data */ -#define PORT_SIG 0x24 /* device TF signature */ -#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ -#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ -#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */ -#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ -#define PORT_CMD_ISSUE 0x38 /* command issue */ -#define PORT_RESERVED 0x3c /* reserved */ +enum AHCIPortReg { + AHCI_PORT_REG_LST_ADDR = 0, /* PxCLB: command list DMA addr */ + AHCI_PORT_REG_LST_ADDR_HI = 1, /* PxCLBU: command list DMA addr hi */ + AHCI_PORT_REG_FIS_ADDR = 2, /* PxFB: FIS rx buf addr */ + AHCI_PORT_REG_FIS_ADDR_HI = 3, /* PxFBU: FIX rx buf addr hi */ + AHCI_PORT_REG_IRQ_STAT = 4, /* PxIS: interrupt status */ + AHCI_PORT_REG_IRQ_MASK = 5, /* PxIE: interrupt enable/disable mask */ + AHCI_PORT_REG_CMD = 6, /* PxCMD: port command */ + /* RESERVED */ + AHCI_PORT_REG_TFDATA = 8, /* PxTFD: taskfile data */ + AHCI_PORT_REG_SIG = 9, /* PxSIG: device TF signature */ + AHCI_PORT_REG_SCR_STAT = 10, /* PxSSTS: SATA phy register: SStatus */ + AHCI_PORT_REG_SCR_CTL = 11, /* PxSCTL: SATA phy register: SControl */ + AHCI_PORT_REG_SCR_ERR = 12, /* PxSERR: SATA phy register: SError */ + AHCI_PORT_REG_SCR_ACT = 13, /* PxSACT: SATA phy register: SActive */ + AHCI_PORT_REG_CMD_ISSUE = 14, /* PxCI: command issue */ + AHCI_PORT_REG_SCR_NOTIF = 15, /* PxSNTF: SATA phy register: SNotification */ + AHCI_PORT_REG_FIS_CTL = 16, /* PxFBS: Port multiplier switching ctl */ + AHCI_PORT_REG_DEV_SLEEP = 17, /* PxDEVSLP: device sleep control */ + /* RESERVED */ + AHCI_PORT_REG_VENDOR_1 = 28, /* PxVS: Vendor Specific */ + AHCI_PORT_REG_VENDOR_2 = 29, + AHCI_PORT_REG_VENDOR_3 = 30, + AHCI_PORT_REG_VENDOR_4 = 31, + AHCI_PORT_REG__COUNT = 32 +}; /* Port interrupt bit descriptors */ enum AHCIPortIRQ { @@ -198,8 +218,7 @@ enum AHCIPortIRQ { #define SATA_SIGNATURE_CDROM 0xeb140101 #define SATA_SIGNATURE_DISK 0x00000101 -#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20 - /* Shouldn't this be 0x2c? */ +#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x2c #define AHCI_PORT_REGS_START_ADDR 0x100 #define AHCI_PORT_ADDR_OFFSET_MASK 0x7f @@ -296,7 +315,7 @@ struct AHCIDevice { QEMUBH *check_bh; uint8_t *lst; uint8_t *res_fis; - bool done_atapi_packet; + bool done_first_drq; int32_t busy_slot; bool init_d2h_sent; AHCICmdHdr *cur_cmd; @@ -375,7 +394,4 @@ void ahci_reset(AHCIState *s); #define SYSBUS_AHCI(obj) OBJECT_CHECK(SysbusAHCIState, (obj), TYPE_SYSBUS_AHCI) -#define ALLWINNER_AHCI(obj) OBJECT_CHECK(AllwinnerAHCIState, (obj), \ - TYPE_ALLWINNER_AHCI) - #endif /* HW_IDE_AHCI_H */ diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index c0509c8bf5..39e473f9c2 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -245,15 +245,11 @@ static uint16_t atapi_byte_count_limit(IDEState *s) void ide_atapi_cmd_reply_end(IDEState *s) { int byte_count_limit, size, ret; - trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, - s->elementary_transfer_size, - s->io_buffer_index); - if (s->packet_transfer_size <= 0) { - /* end of transfer */ - ide_atapi_cmd_ok(s); - ide_set_irq(s->bus); - trace_ide_atapi_cmd_reply_end_eot(s, s->status); - } else { + while (s->packet_transfer_size > 0) { + trace_ide_atapi_cmd_reply_end(s, s->packet_transfer_size, + s->elementary_transfer_size, + s->io_buffer_index); + /* see if a new sector must be read */ if (s->lba != -1 && s->io_buffer_index >= s->cd_sector_size) { if (!s->elementary_transfer_size) { @@ -279,14 +275,10 @@ void ide_atapi_cmd_reply_end(IDEState *s) size = s->cd_sector_size - s->io_buffer_index; if (size > s->elementary_transfer_size) size = s->elementary_transfer_size; - s->packet_transfer_size -= size; - s->elementary_transfer_size -= size; - s->io_buffer_index += size; - ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size, - size, ide_atapi_cmd_reply_end); } else { /* a new transfer is needed */ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; + ide_set_irq(s->bus); byte_count_limit = atapi_byte_count_limit(s); trace_ide_atapi_cmd_reply_end_bcl(s, byte_count_limit); size = s->packet_transfer_size; @@ -304,15 +296,27 @@ void ide_atapi_cmd_reply_end(IDEState *s) if (size > (s->cd_sector_size - s->io_buffer_index)) size = (s->cd_sector_size - s->io_buffer_index); } - s->packet_transfer_size -= size; - s->elementary_transfer_size -= size; - s->io_buffer_index += size; - ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size, - size, ide_atapi_cmd_reply_end); - ide_set_irq(s->bus); trace_ide_atapi_cmd_reply_end_new(s, s->status); } + s->packet_transfer_size -= size; + s->elementary_transfer_size -= size; + s->io_buffer_index += size; + + /* Some adapters process PIO data right away. In that case, we need + * to avoid mutual recursion between ide_transfer_start + * and ide_atapi_cmd_reply_end. + */ + if (!ide_transfer_start_norecurse(s, + s->io_buffer + s->io_buffer_index - size, + size, ide_atapi_cmd_reply_end)) { + return; + } } + + /* end of transfer */ + trace_ide_atapi_cmd_reply_end_eot(s, s->status); + ide_atapi_cmd_ok(s); + ide_set_irq(s->bus); } /* send a reply of 'size' bytes in s->io_buffer to an ATAPI command */ diff --git a/hw/ide/core.c b/hw/ide/core.c index c1dfab4135..b37d4f6414 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" -#include "hw/pci/pci.h" #include "hw/isa/isa.h" #include "qemu/error-report.h" #include "qemu/timer.h" @@ -532,18 +531,28 @@ static void ide_clear_retry(IDEState *s) } /* prepare data transfer and tell what to do after */ -void ide_transfer_start(IDEState *s, uint8_t *buf, int size, - EndTransferFunc *end_transfer_func) +bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func) { - s->end_transfer_func = end_transfer_func; s->data_ptr = buf; s->data_end = buf + size; ide_set_retry(s); if (!(s->status & ERR_STAT)) { s->status |= DRQ_STAT; } - if (s->bus->dma->ops->start_transfer) { - s->bus->dma->ops->start_transfer(s->bus->dma); + if (!s->bus->dma->ops->pio_transfer) { + s->end_transfer_func = end_transfer_func; + return false; + } + s->bus->dma->ops->pio_transfer(s->bus->dma); + return true; +} + +void ide_transfer_start(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func) +{ + if (ide_transfer_start_norecurse(s, buf, size, end_transfer_func)) { + end_transfer_func(s); } } @@ -554,27 +563,18 @@ static void ide_cmd_done(IDEState *s) } } -static void ide_transfer_halt(IDEState *s, - void(*end_transfer_func)(IDEState *), - bool notify) +static void ide_transfer_halt(IDEState *s) { - s->end_transfer_func = end_transfer_func; + s->end_transfer_func = ide_transfer_stop; s->data_ptr = s->io_buffer; s->data_end = s->io_buffer; s->status &= ~DRQ_STAT; - if (notify) { - ide_cmd_done(s); - } } void ide_transfer_stop(IDEState *s) { - ide_transfer_halt(s, ide_transfer_stop, true); -} - -static void ide_transfer_cancel(IDEState *s) -{ - ide_transfer_halt(s, ide_transfer_cancel, false); + ide_transfer_halt(s); + ide_cmd_done(s); } int64_t ide_get_sector(IDEState *s) @@ -1371,7 +1371,7 @@ static bool cmd_nop(IDEState *s, uint8_t cmd) static bool cmd_device_reset(IDEState *s, uint8_t cmd) { /* Halt PIO (in the DRQ phase), then DMA */ - ide_transfer_cancel(s); + ide_transfer_halt(s); ide_cancel_dma_sync(s); /* Reset any PIO commands, reset signature, etc */ diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 134478ebb2..51c935a0da 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -67,7 +67,7 @@ #include "hw/isa/isa.h" #include "sysemu/dma.h" #include "hw/ide/pci.h" -#include "hw/ide/ahci_internal.h" +#include "ahci_internal.h" #define ICH9_MSI_CAP_OFFSET 0x80 #define ICH9_SATA_CAP_OFFSET 0xA8 diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 1ab0a892d0..fe1ceeb0cd 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -26,7 +26,6 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" -#include "sysemu/block-backend.h" #include "sysemu/dma.h" #include "qemu/error-report.h" #include "hw/ide/pci.h" diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 0c39cabe72..65d6f9034d 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -63,16 +63,23 @@ ide_atapi_cmd_read_dma_cb_aio(void *s, int lba, int n) "IDEState: %p; aio read: ide_atapi_cmd_packet(void *s, uint16_t limit, const char *packet) "IDEState: %p; limit=0x%x packet: %s" # hw/ide/ahci.c -ahci_port_read(void *s, int port, int offset, uint32_t ret) "ahci(%p)[%d]: port read @ 0x%x: 0x%08x" +ahci_port_read(void *s, int port, const char *reg, int offset, uint32_t ret) "ahci(%p)[%d]: port read [reg:%s] @ 0x%x: 0x%08x" +ahci_port_read_default(void *s, int port, const char *reg, int offset) "ahci(%p)[%d]: unimplemented port read [reg:%s] @ 0x%x" ahci_irq_raise(void *s) "ahci(%p): raise irq" ahci_irq_lower(void *s) "ahci(%p): lower irq" ahci_check_irq(void *s, uint32_t old, uint32_t new) "ahci(%p): check irq 0x%08x --> 0x%08x" ahci_trigger_irq(void *s, int port, const char *name, uint32_t val, uint32_t old, uint32_t new, uint32_t effective) "ahci(%p)[%d]: trigger irq +%s (0x%08x); irqstat: 0x%08x --> 0x%08x; effective: 0x%08x" -ahci_port_write(void *s, int port, int offset, uint32_t val) "ahci(%p)[%d]: port write @ 0x%x: 0x%08x" +ahci_port_write(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: port write [reg:%s] @ 0x%x: 0x%08x" +ahci_port_write_unimpl(void *s, int port, const char *reg, int offset, uint32_t val) "ahci(%p)[%d]: unimplemented port write [reg:%s] @ 0x%x: 0x%08x" ahci_mem_read_32(void *s, uint64_t addr, uint32_t val) "ahci(%p): mem read @ 0x%"PRIx64": 0x%08x" +ahci_mem_read_32_default(void *s, uint64_t addr, uint32_t val) "ahci(%p): mem read @ 0x%"PRIx64": 0x%08x" +ahci_mem_read_32_host(void *s, const char *reg, uint64_t addr, uint32_t val) "ahci(%p): mem read [reg:%s] @ 0x%"PRIx64": 0x%08x" +ahci_mem_read_32_host_default(void *s, const char *reg, uint64_t addr) "ahci(%p): unimplemented mem read [reg:%s] @ 0x%"PRIx64 ahci_mem_read(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): read%u @ 0x%"PRIx64": 0x%016"PRIx64 ahci_mem_write(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u @ 0x%"PRIx64": 0x%016"PRIx64 -ahci_mem_write_unknown(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u to unknown register 0x%"PRIx64": 0x%016"PRIx64 +ahci_mem_write_host_unimpl(void *s, unsigned size, const char *reg, uint64_t addr) "ahci(%p) unimplemented write%u [reg:%s] @ 0x%"PRIx64 +ahci_mem_write_host(void *s, unsigned size, const char *reg, uint64_t addr, uint64_t val) "ahci(%p) write%u [reg:%s] @ 0x%"PRIx64": 0x%016"PRIx64 +ahci_mem_write_unimpl(void *s, unsigned size, uint64_t addr, uint64_t val) "ahci(%p): write%u to unknown register 0x%"PRIx64": 0x%016"PRIx64 ahci_set_signature(void *s, int port, uint8_t nsector, uint8_t sector, uint8_t lcyl, uint8_t hcyl, uint32_t sig) "ahci(%p)[%d]: set signature sector:0x%02x nsector:0x%02x lcyl:0x%02x hcyl:0x%02x (cumulatively: 0x%08x)" ahci_reset_port(void *s, int port) "ahci(%p)[%d]: reset port" ahci_unmap_fis_address_null(void *s, int port) "ahci(%p)[%d]: Attempt to unmap NULL FIS address" @@ -101,15 +108,15 @@ handle_cmd_badport(void *s, int port) "ahci(%p)[%d]: guest accessed unused port" handle_cmd_badfis(void *s, int port) "ahci(%p)[%d]: guest provided an invalid cmd FIS" handle_cmd_badmap(void *s, int port, uint64_t len) "ahci(%p)[%d]: dma_memory_map failed, 0x%02"PRIx64" != 0x80" handle_cmd_unhandled_fis(void *s, int port, uint8_t b0, uint8_t b1, uint8_t b2) "ahci(%p)[%d]: unhandled FIS type. cmd_fis: 0x%02x-%02x-%02x" -ahci_start_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist" +ahci_pio_transfer(void *s, int port, const char *rw, uint32_t size, const char *tgt, const char *sgl) "ahci(%p)[%d]: %sing %d bytes on %s w/%s sglist" ahci_start_dma(void *s, int port) "ahci(%p)[%d]: start dma" ahci_dma_prepare_buf(void *s, int port, int32_t io_buffer_size, int32_t limit) "ahci(%p)[%d]: prepare buf limit=%"PRId32" prepared=%"PRId32 ahci_dma_prepare_buf_fail(void *s, int port) "ahci(%p)[%d]: sglist population failed" ahci_dma_rw_buf(void *s, int port, int l) "ahci(%p)[%d] len=0x%x" ahci_cmd_done(void *s, int port) "ahci(%p)[%d]: cmd done" ahci_reset(void *s) "ahci(%p): HBA reset" -allwinner_ahci_mem_read(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): read a=%p addr=0x%"HWADDR_PRIx" val=0x%"PRIx64", size=%d" -allwinner_ahci_mem_write(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): write a=%p addr=0x%"HWADDR_PRIx" val=0x%"PRIx64", size=%d" +allwinner_ahci_mem_read(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): read a=%p addr=0x%"PRIx64" val=0x%"PRIx64", size=%d" +allwinner_ahci_mem_write(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): write a=%p addr=0x%"PRIx64" val=0x%"PRIx64", size=%d" # Warning: Verbose handle_reg_h2d_fis_dump(void *s, int port, const char *fis) "ahci(%p)[%d]: %s" diff --git a/hw/ide/via.c b/hw/ide/via.c index 117ac4d95e..238f038d72 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -27,7 +27,6 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" -#include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs index 77e53e6883..c8b00f71ec 100644 --- a/hw/input/Makefile.objs +++ b/hw/input/Makefile.objs @@ -7,10 +7,10 @@ common-obj-y += ps2.o common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o common-obj-$(CONFIG_TSC2005) += tsc2005.o -common-obj-$(CONFIG_VIRTIO) += virtio-input.o -common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o +common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input.o +common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-hid.o ifeq ($(CONFIG_LINUX),y) -common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o +common-obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-host.o endif obj-$(CONFIG_MILKYMIST) += milkymist-softusb.o diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 50b62712c8..b026e9d49f 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -261,18 +261,21 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, trace_adb_kbd_request_change_addr(d->devaddr); break; default: - d->devaddr = buf[1] & 0xf; - /* we support handlers: - * 1: Apple Standard Keyboard - * 2: Apple Extended Keyboard (LShift = RShift) - * 3: Apple Extended Keyboard (LShift != RShift) - */ - if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { - d->handler = buf[2]; - } + if (!d->disable_direct_reg3_writes) { + d->devaddr = buf[1] & 0xf; - trace_adb_kbd_request_change_addr_and_handler(d->devaddr, - d->handler); + /* we support handlers: + * 1: Apple Standard Keyboard + * 2: Apple Extended Keyboard (LShift = RShift) + * 3: Apple Extended Keyboard (LShift != RShift) + */ + if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { + d->handler = buf[2]; + } + + trace_adb_kbd_request_change_addr_and_handler(d->devaddr, + d->handler); + } break; } } @@ -290,8 +293,8 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, olen = 2; break; case 3: - obuf[0] = d->handler; - obuf[1] = d->devaddr; + obuf[0] = d->devaddr; + obuf[1] = d->handler; olen = 2; break; } diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index 3ba6027d33..83833b0035 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -142,24 +142,27 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, trace_adb_mouse_request_change_addr(d->devaddr); break; default: - d->devaddr = buf[1] & 0xf; - /* we support handlers: - * 0x01: Classic Apple Mouse Protocol / 100 cpi operations - * 0x02: Classic Apple Mouse Protocol / 200 cpi operations - * we don't support handlers (at least): - * 0x03: Mouse systems A3 trackball - * 0x04: Extended Apple Mouse Protocol - * 0x2f: Microspeed mouse - * 0x42: Macally - * 0x5f: Microspeed mouse - * 0x66: Microspeed mouse - */ - if (buf[2] == 1 || buf[2] == 2) { - d->handler = buf[2]; - } + if (!d->disable_direct_reg3_writes) { + d->devaddr = buf[1] & 0xf; - trace_adb_mouse_request_change_addr_and_handler(d->devaddr, - d->handler); + /* we support handlers: + * 0x01: Classic Apple Mouse Protocol / 100 cpi operations + * 0x02: Classic Apple Mouse Protocol / 200 cpi operations + * we don't support handlers (at least): + * 0x03: Mouse systems A3 trackball + * 0x04: Extended Apple Mouse Protocol + * 0x2f: Microspeed mouse + * 0x42: Macally + * 0x5f: Microspeed mouse + * 0x66: Microspeed mouse + */ + if (buf[2] == 1 || buf[2] == 2) { + d->handler = buf[2]; + } + + trace_adb_mouse_request_change_addr_and_handler( + d->devaddr, d->handler); + } break; } } @@ -172,8 +175,8 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, case 1: break; case 3: - obuf[0] = d->handler; - obuf[1] = d->devaddr; + obuf[0] = d->devaddr; + obuf[1] = d->handler; olen = 2; break; } diff --git a/hw/input/adb.c b/hw/input/adb.c index 23ae6f0d75..bbb40aeef1 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -113,11 +113,18 @@ static void adb_device_realizefn(DeviceState *dev, Error **errp) bus->devices[bus->nb_devices++] = d; } +static Property adb_device_properties[] = { + DEFINE_PROP_BOOL("disable-direct-reg3-writes", ADBDevice, + disable_direct_reg3_writes, false), + DEFINE_PROP_END_OF_LIST(), +}; + static void adb_device_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = adb_device_realizefn; + dc->props = adb_device_properties; dc->bus_type = TYPE_ADB_BUS; } diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c index d39953126b..74da30d9ca 100644 --- a/hw/input/lm832x.c +++ b/hw/input/lm832x.c @@ -464,20 +464,19 @@ static const VMStateDescription vmstate_lm_kbd = { }; -static int lm8323_init(I2CSlave *i2c) +static void lm8323_realize(DeviceState *dev, Error **errp) { - LM823KbdState *s = LM8323(i2c); + LM823KbdState *s = LM8323(dev); s->model = 0x8323; s->pwm.tm[0] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm0_tick, s); s->pwm.tm[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm1_tick, s); s->pwm.tm[2] = timer_new_ns(QEMU_CLOCK_VIRTUAL, lm_kbd_pwm2_tick, s); - qdev_init_gpio_out(DEVICE(i2c), &s->nirq, 1); + qdev_init_gpio_out(dev, &s->nirq, 1); lm_kbd_reset(s); qemu_register_reset((void *) lm_kbd_reset, s); - return 0; } void lm832x_key_event(DeviceState *dev, int key, int state) @@ -505,7 +504,7 @@ static void lm8323_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = lm8323_init; + dc->realize = lm8323_realize; k->event = lm_i2c_event; k->recv = lm_i2c_rx; k->send = lm_i2c_tx; diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index f17f18e51b..07c8801387 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/isa/isa.h" #include "hw/i386/pc.h" @@ -308,7 +309,8 @@ static void kbd_write_command(void *opaque, hwaddr addr, /* ignore that */ break; default: - fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", (int)val); + qemu_log_mask(LOG_GUEST_ERROR, + "unsupported keyboard cmd=0x%02" PRIx64 "\n", val); break; } } @@ -434,7 +436,7 @@ static const VMStateDescription vmstate_kbd = { }; /* Memory mapped interface */ -static uint32_t kbd_mm_readb (void *opaque, hwaddr addr) +static uint64_t kbd_mm_readfn(void *opaque, hwaddr addr, unsigned size) { KBDState *s = opaque; @@ -444,7 +446,8 @@ static uint32_t kbd_mm_readb (void *opaque, hwaddr addr) return kbd_read_data(s, 0, 1) & 0xff; } -static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value) +static void kbd_mm_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { KBDState *s = opaque; @@ -454,12 +457,13 @@ static void kbd_mm_writeb (void *opaque, hwaddr addr, uint32_t value) kbd_write_data(s, 0, value & 0xff, 1); } + static const MemoryRegionOps i8042_mmio_ops = { + .read = kbd_mm_readfn, + .write = kbd_mm_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, - .old_mmio = { - .read = { kbd_mm_readb, kbd_mm_readb, kbd_mm_readb }, - .write = { kbd_mm_writeb, kbd_mm_writeb, kbd_mm_writeb }, - }, }; void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 06f5d2ac4a..fdfcadf9a1 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -188,16 +188,64 @@ static void ps2_reset_queue(PS2State *s) q->count = 0; } -void ps2_queue(PS2State *s, int b) +void ps2_queue_noirq(PS2State *s, int b) { PS2Queue *q = &s->queue; - if (q->count >= PS2_QUEUE_SIZE - 1) + if (q->count == PS2_QUEUE_SIZE) { return; + } + q->data[q->wptr] = b; if (++q->wptr == PS2_QUEUE_SIZE) q->wptr = 0; q->count++; +} + +void ps2_raise_irq(PS2State *s) +{ + s->update_irq(s->update_arg, 1); +} + +void ps2_queue(PS2State *s, int b) +{ + ps2_queue_noirq(s, b); + s->update_irq(s->update_arg, 1); +} + +void ps2_queue_2(PS2State *s, int b1, int b2) +{ + if (PS2_QUEUE_SIZE - s->queue.count < 2) { + return; + } + + ps2_queue_noirq(s, b1); + ps2_queue_noirq(s, b2); + s->update_irq(s->update_arg, 1); +} + +void ps2_queue_3(PS2State *s, int b1, int b2, int b3) +{ + if (PS2_QUEUE_SIZE - s->queue.count < 3) { + return; + } + + ps2_queue_noirq(s, b1); + ps2_queue_noirq(s, b2); + ps2_queue_noirq(s, b3); + s->update_irq(s->update_arg, 1); +} + +void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4) +{ + if (PS2_QUEUE_SIZE - s->queue.count < 4) { + return; + } + + ps2_queue_noirq(s, b1); + ps2_queue_noirq(s, b2); + ps2_queue_noirq(s, b3); + ps2_queue_noirq(s, b4); s->update_irq(s->update_arg, 1); } @@ -232,6 +280,11 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, uint16_t keycode = 0; int mod; + /* do not process events while disabled to prevent stream corruption */ + if (!s->scan_enabled) { + return; + } + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); assert(evt->type == INPUT_EVENT_KIND_KEY); qcode = qemu_input_key_value_to_qcode(key->key); @@ -496,13 +549,17 @@ void ps2_write_keyboard(void *opaque, int val) ps2_queue(&s->common, KBD_REPLY_RESEND); break; case KBD_CMD_GET_ID: - ps2_queue(&s->common, KBD_REPLY_ACK); /* We emulate a MF2 AT keyboard here */ - ps2_queue(&s->common, KBD_REPLY_ID); if (s->translate) - ps2_queue(&s->common, 0x41); + ps2_queue_3(&s->common, + KBD_REPLY_ACK, + KBD_REPLY_ID, + 0x41); else - ps2_queue(&s->common, 0x83); + ps2_queue_3(&s->common, + KBD_REPLY_ACK, + KBD_REPLY_ID, + 0x83); break; case KBD_CMD_ECHO: ps2_queue(&s->common, KBD_CMD_ECHO); @@ -529,8 +586,9 @@ void ps2_write_keyboard(void *opaque, int val) break; case KBD_CMD_RESET: ps2_reset_keyboard(s); - ps2_queue(&s->common, KBD_REPLY_ACK); - ps2_queue(&s->common, KBD_REPLY_POR); + ps2_queue_2(&s->common, + KBD_REPLY_ACK, + KBD_REPLY_POR); break; default: ps2_queue(&s->common, KBD_REPLY_RESEND); @@ -539,8 +597,10 @@ void ps2_write_keyboard(void *opaque, int val) break; case KBD_CMD_SCANCODE: if (val == 0) { - ps2_queue(&s->common, KBD_REPLY_ACK); - ps2_put_keycode(s, s->scancode_set); + if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) { + ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_put_keycode(s, s->scancode_set); + } } else if (val >= 1 && val <= 3) { s->scancode_set = val; ps2_queue(&s->common, KBD_REPLY_ACK); @@ -572,11 +632,16 @@ void ps2_keyboard_set_translation(void *opaque, int mode) s->translate = mode; } -static void ps2_mouse_send_packet(PS2MouseState *s) +static int ps2_mouse_send_packet(PS2MouseState *s) { + const int needed = 3 + (s->mouse_type - 2); unsigned int b; int dx1, dy1, dz1; + if (PS2_QUEUE_SIZE - s->common.queue.count < needed) { + return 0; + } + dx1 = s->mouse_dx; dy1 = s->mouse_dy; dz1 = s->mouse_dz; @@ -590,9 +655,9 @@ static void ps2_mouse_send_packet(PS2MouseState *s) else if (dy1 < -127) dy1 = -127; b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); - ps2_queue(&s->common, b); - ps2_queue(&s->common, dx1 & 0xff); - ps2_queue(&s->common, dy1 & 0xff); + ps2_queue_noirq(&s->common, b); + ps2_queue_noirq(&s->common, dx1 & 0xff); + ps2_queue_noirq(&s->common, dy1 & 0xff); /* extra byte for IMPS/2 or IMEX */ switch(s->mouse_type) { default: @@ -602,7 +667,7 @@ static void ps2_mouse_send_packet(PS2MouseState *s) dz1 = 127; else if (dz1 < -127) dz1 = -127; - ps2_queue(&s->common, dz1 & 0xff); + ps2_queue_noirq(&s->common, dz1 & 0xff); break; case 4: if (dz1 > 7) @@ -610,15 +675,19 @@ static void ps2_mouse_send_packet(PS2MouseState *s) else if (dz1 < -7) dz1 = -7; b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); - ps2_queue(&s->common, b); + ps2_queue_noirq(&s->common, b); break; } + ps2_raise_irq(&s->common); + trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b); /* update deltas */ s->mouse_dx -= dx1; s->mouse_dy -= dy1; s->mouse_dz -= dz1; + + return 1; } static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, @@ -673,14 +742,18 @@ static void ps2_mouse_sync(DeviceState *dev) { PS2MouseState *s = (PS2MouseState *)dev; + /* do not sync while disabled to prevent stream corruption */ + if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) { + return; + } + if (s->mouse_buttons) { qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); } if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) { - while (s->common.queue.count < PS2_QUEUE_SIZE - 4) { - /* if not remote, send event. Multiple events are sent if - too big deltas */ - ps2_mouse_send_packet(s); + /* 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) break; } @@ -739,8 +812,9 @@ void ps2_write_mouse(void *opaque, int val) ps2_queue(&s->common, AUX_ACK); break; case AUX_GET_TYPE: - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, s->mouse_type); + ps2_queue_2(&s->common, + AUX_ACK, + s->mouse_type); break; case AUX_SET_RES: case AUX_SET_SAMPLE: @@ -748,10 +822,11 @@ void ps2_write_mouse(void *opaque, int val) ps2_queue(&s->common, AUX_ACK); break; case AUX_GET_SCALE: - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, s->mouse_status); - ps2_queue(&s->common, s->mouse_resolution); - ps2_queue(&s->common, s->mouse_sample_rate); + ps2_queue_4(&s->common, + AUX_ACK, + s->mouse_status, + s->mouse_resolution, + s->mouse_sample_rate); break; case AUX_POLL: ps2_queue(&s->common, AUX_ACK); @@ -776,9 +851,11 @@ void ps2_write_mouse(void *opaque, int val) s->mouse_resolution = 2; s->mouse_status = 0; s->mouse_type = 0; - ps2_queue(&s->common, AUX_ACK); - ps2_queue(&s->common, 0xaa); - ps2_queue(&s->common, s->mouse_type); + ps2_reset_queue(&s->common); + ps2_queue_3(&s->common, + AUX_ACK, + 0xaa, + s->mouse_type); break; default: break; @@ -850,7 +927,7 @@ static void ps2_common_post_load(PS2State *s) /* reset rptr/wptr/count */ q->rptr = 0; - q->wptr = size; + q->wptr = (size == PS2_QUEUE_SIZE) ? 0 : size; q->count = size; s->update_irq(s->update_arg, q->count != 0); } diff --git a/hw/input/trace-events b/hw/input/trace-events index db72484a25..3965a842ae 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -41,5 +41,8 @@ milkymist_softusb_pulse_irq(void) "Pulse IRQ" hid_kbd_queue_full(void) "queue full" hid_kbd_queue_empty(void) "queue empty" +# hw/input/tsc2005.c +tsc2005_sense(const char *state) "touchscreen sense %s" + # hw/input/virtio virtio_input_queue_full(void) "queue full" diff --git a/hw/input/tsc2005.c b/hw/input/tsc2005.c index 7990954b6c..2b9108a193 100644 --- a/hw/input/tsc2005.c +++ b/hw/input/tsc2005.c @@ -19,10 +19,12 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "qemu/timer.h" #include "ui/console.h" #include "hw/devices.h" +#include "trace.h" #define TSC_CUT_RESOLUTION(value, p) ((value) >> (16 - (p ? 12 : 10))) @@ -200,17 +202,17 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) s->host_mode = (data >> 15) != 0; if (s->enabled != !(data & 0x4000)) { s->enabled = !(data & 0x4000); - fprintf(stderr, "%s: touchscreen sense %sabled\n", - __func__, s->enabled ? "en" : "dis"); + trace_tsc2005_sense(s->enabled ? "enabled" : "disabled"); if (s->busy && !s->enabled) timer_del(s->timer); s->busy = s->busy && s->enabled; } s->nextprecision = (data >> 13) & 1; s->timing[0] = data & 0x1fff; - if ((s->timing[0] >> 11) == 3) - fprintf(stderr, "%s: illegal conversion clock setting\n", - __func__); + if ((s->timing[0] >> 11) == 3) { + qemu_log_mask(LOG_GUEST_ERROR, + "tsc2005_write: illegal conversion clock setting\n"); + } break; case 0xd: /* CFR1 */ s->timing[1] = data & 0xf07; @@ -221,8 +223,9 @@ static void tsc2005_write(TSC2005State *s, int reg, uint16_t data) break; default: - fprintf(stderr, "%s: write into read-only register %x\n", - __func__, reg); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write into read-only register 0x%x\n", + __func__, reg); } } @@ -337,8 +340,7 @@ static uint8_t tsc2005_txrx_word(void *opaque, uint8_t value) s->nextprecision = (value >> 2) & 1; if (s->enabled != !(value & 1)) { s->enabled = !(value & 1); - fprintf(stderr, "%s: touchscreen sense %sabled\n", - __func__, s->enabled ? "en" : "dis"); + trace_tsc2005_sense(s->enabled ? "enabled" : "disabled"); if (s->busy && !s->enabled) timer_del(s->timer); s->busy = s->busy && s->enabled; diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index ea0323f969..34dc84ae81 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -543,7 +543,21 @@ static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs) static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) { int cm = 1 << cpu; - int group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); + int group; + + if (irq >= s->num_irq) { + /* + * This handles two cases: + * 1. If software writes the ID of a spurious interrupt [ie 1023] + * to the GICC_DIR, the GIC ignores that write. + * 2. If software writes the number of a non-existent interrupt + * this must be a subcase of "value written is not an active interrupt" + * and so this is UNPREDICTABLE. We choose to ignore it. + */ + return; + } + + group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); if (!gic_eoi_split(s, cpu, attrs)) { /* This is UNPREDICTABLE; we choose to ignore it */ @@ -737,7 +751,9 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) if (irq >= s->num_irq) { goto bad_reg; } - if (irq >= 29 && irq <= 31) { + if (irq < 29 && s->revision == REV_11MPCORE) { + res = 0; + } else if (irq < GIC_INTERNAL) { res = cm; } else { res = GIC_TARGET(irq); @@ -1000,7 +1016,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, if (irq >= s->num_irq) { goto bad_reg; } - if (irq < 29) { + if (irq < 29 && s->revision == REV_11MPCORE) { value = 0; } else if (irq < GIC_INTERNAL) { value = ALL_CPU_MASK; diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 6f467e68a8..86665080bd 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -558,7 +558,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) | KVM_VGIC_V2_ADDR_TYPE_DIST, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_DIST, - s->dev_fd); + s->dev_fd, 0); /* CPU interface for current core. Unlike arm_gic, we don't * provide the "interface for core #N" memory regions, because * cores with a VGIC don't have those. @@ -568,11 +568,10 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) | KVM_VGIC_V2_ADDR_TYPE_CPU, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V2_ADDR_TYPE_CPU, - s->dev_fd); + s->dev_fd, 0); if (kvm_has_gsi_routing()) { /* set up irq routing */ - kvm_init_irq_routing(kvm_state); for (i = 0; i < s->num_irq - GIC_INTERNAL; ++i) { kvm_irqchip_add_irq_route(kvm_state, i, 0, i); } diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 479c66733c..7044133e2d 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -373,7 +373,17 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) return; } - gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops); + if (s->nb_redist_regions != 1) { + error_setg(errp, "VGICv3 redist region number(%d) not equal to 1", + s->nb_redist_regions); + return; + } + + gicv3_init_irqs_and_mmio(s, gicv3_set_irq, gic_ops, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } gicv3_init_cpuif(s); } diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 7b54d52376..52480c3b4c 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -27,6 +27,42 @@ #include "hw/intc/arm_gicv3_common.h" #include "gicv3_internal.h" #include "hw/arm/linux-boot-if.h" +#include "sysemu/kvm.h" + + +static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs) +{ + if (cs->gicd_no_migration_shift_bug) { + return; + } + + /* Older versions of QEMU had a bug in the handling of state save/restore + * to the KVM GICv3: they got the offset in the bitmap arrays wrong, + * so that instead of the data for external interrupts 32 and up + * starting at bit position 32 in the bitmap, it started at bit + * position 64. If we're receiving data from a QEMU with that bug, + * we must move the data down into the right place. + */ + memmove(cs->group, (uint8_t *)cs->group + GIC_INTERNAL / 8, + sizeof(cs->group) - GIC_INTERNAL / 8); + memmove(cs->grpmod, (uint8_t *)cs->grpmod + GIC_INTERNAL / 8, + sizeof(cs->grpmod) - GIC_INTERNAL / 8); + memmove(cs->enabled, (uint8_t *)cs->enabled + GIC_INTERNAL / 8, + sizeof(cs->enabled) - GIC_INTERNAL / 8); + memmove(cs->pending, (uint8_t *)cs->pending + GIC_INTERNAL / 8, + sizeof(cs->pending) - GIC_INTERNAL / 8); + memmove(cs->active, (uint8_t *)cs->active + GIC_INTERNAL / 8, + sizeof(cs->active) - GIC_INTERNAL / 8); + memmove(cs->edge_trigger, (uint8_t *)cs->edge_trigger + GIC_INTERNAL / 8, + sizeof(cs->edge_trigger) - GIC_INTERNAL / 8); + + /* + * While this new version QEMU doesn't have this kind of bug as we fix it, + * so it needs to set the flag to true to indicate that and it's necessary + * for next migration to work from this new version QEMU. + */ + cs->gicd_no_migration_shift_bug = true; +} static int gicv3_pre_save(void *opaque) { @@ -45,6 +81,8 @@ static int gicv3_post_load(void *opaque, int version_id) GICv3State *s = (GICv3State *)opaque; ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s); + gicv3_gicd_no_migration_shift_bug_post_load(s); + if (c->post_load) { c->post_load(s); } @@ -72,7 +110,7 @@ static const VMStateDescription vmstate_gicv3_cpu_virt = { } }; -static int icc_sre_el1_reg_pre_load(void *opaque) +static int vmstate_gicv3_cpu_pre_load(void *opaque) { GICv3CPUState *cs = opaque; @@ -96,7 +134,6 @@ const VMStateDescription vmstate_gicv3_cpu_sre_el1 = { .name = "arm_gicv3_cpu/sre_el1", .version_id = 1, .minimum_version_id = 1, - .pre_load = icc_sre_el1_reg_pre_load, .needed = icc_sre_el1_reg_needed, .fields = (VMStateField[]) { VMSTATE_UINT64(icc_sre_el1, GICv3CPUState), @@ -108,6 +145,7 @@ static const VMStateDescription vmstate_gicv3_cpu = { .name = "arm_gicv3_cpu", .version_id = 1, .minimum_version_id = 1, + .pre_load = vmstate_gicv3_cpu_pre_load, .fields = (VMStateField[]) { VMSTATE_UINT32(level, GICv3CPUState), VMSTATE_UINT32(gicr_ctlr, GICv3CPUState), @@ -133,18 +171,54 @@ static const VMStateDescription vmstate_gicv3_cpu = { }, .subsections = (const VMStateDescription * []) { &vmstate_gicv3_cpu_virt, - NULL - }, - .subsections = (const VMStateDescription * []) { &vmstate_gicv3_cpu_sre_el1, NULL } }; +static int gicv3_pre_load(void *opaque) +{ + GICv3State *cs = opaque; + + /* + * The gicd_no_migration_shift_bug flag is used for migration compatibility + * for old version QEMU which may have the GICD bmp shift bug under KVM mode. + * Strictly, what we want to know is whether the migration source is using + * KVM. Since we don't have any way to determine that, we look at whether the + * destination is using KVM; this is close enough because for the older QEMU + * versions with this bug KVM -> TCG migration didn't work anyway. If the + * source is a newer QEMU without this bug it will transmit the migration + * subsection which sets the flag to true; otherwise it will remain set to + * the value we select here. + */ + if (kvm_enabled()) { + cs->gicd_no_migration_shift_bug = false; + } + + return 0; +} + +static bool needed_always(void *opaque) +{ + return true; +} + +const VMStateDescription vmstate_gicv3_gicd_no_migration_shift_bug = { + .name = "arm_gicv3/gicd_no_migration_shift_bug", + .version_id = 1, + .minimum_version_id = 1, + .needed = needed_always, + .fields = (VMStateField[]) { + VMSTATE_BOOL(gicd_no_migration_shift_bug, GICv3State), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gicv3 = { .name = "arm_gicv3", .version_id = 1, .minimum_version_id = 1, + .pre_load = gicv3_pre_load, .pre_save = gicv3_pre_save, .post_load = gicv3_post_load, .priority = MIG_PRI_GICV3, @@ -165,15 +239,30 @@ static const VMStateDescription vmstate_gicv3 = { VMSTATE_STRUCT_VARRAY_POINTER_UINT32(cpu, GICv3State, num_cpu, vmstate_gicv3_cpu, GICv3CPUState), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_gicv3_gicd_no_migration_shift_bug, + NULL } }; void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, - const MemoryRegionOps *ops) + const MemoryRegionOps *ops, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(s); + int rdist_capacity = 0; int i; + for (i = 0; i < s->nb_redist_regions; i++) { + rdist_capacity += s->redist_region_count[i]; + } + if (rdist_capacity < s->num_cpu) { + error_setg(errp, "Capacity of the redist regions(%d) " + "is less than number of vcpus(%d)", + rdist_capacity, s->num_cpu); + return; + } + /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. * GPIO array layout is thus: * [0..N-1] spi @@ -199,11 +288,18 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s, "gicv3_dist", 0x10000); - memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s, - "gicv3_redist", 0x20000 * s->num_cpu); - sysbus_init_mmio(sbd, &s->iomem_dist); - sysbus_init_mmio(sbd, &s->iomem_redist); + + s->iomem_redist = g_new0(MemoryRegion, s->nb_redist_regions); + for (i = 0; i < s->nb_redist_regions; i++) { + char *name = g_strdup_printf("gicv3_redist_region[%d]", i); + + memory_region_init_io(&s->iomem_redist[i], OBJECT(s), + ops ? &ops[1] : NULL, s, name, + s->redist_region_count[i] * GICV3_REDIST_SIZE); + sysbus_init_mmio(sbd, &s->iomem_redist[i]); + g_free(name); + } } static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) @@ -285,6 +381,13 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp) } } +static void arm_gicv3_finalize(Object *obj) +{ + GICv3State *s = ARM_GICV3_COMMON(obj); + + g_free(s->redist_region_count); +} + static void arm_gicv3_common_reset(DeviceState *dev) { GICv3State *s = ARM_GICV3_COMMON(dev); @@ -364,6 +467,7 @@ static void arm_gicv3_common_reset(DeviceState *dev) gicv3_gicd_group_set(s, i); } } + s->gicd_no_migration_shift_bug = true; } static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, @@ -388,6 +492,8 @@ static Property arm_gicv3_common_properties[] = { DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32), DEFINE_PROP_UINT32("revision", GICv3State, revision, 3), DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0), + DEFINE_PROP_ARRAY("redist-region-count", GICv3State, nb_redist_regions, + redist_region_count, qdev_prop_uint32, uint32_t), DEFINE_PROP_END_OF_LIST(), }; @@ -409,6 +515,7 @@ static const TypeInfo arm_gicv3_common_type = { .instance_size = sizeof(GICv3State), .class_size = sizeof(ARMGICv3CommonClass), .class_init = arm_gicv3_common_class_init, + .instance_finalize = arm_gicv3_finalize, .abstract = true, .interfaces = (InterfaceInfo []) { { TYPE_ARM_LINUX_BOOT_IF }, diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 26f5eeda94..2a60568d82 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -29,11 +29,7 @@ void gicv3_set_gicv3state(CPUState *cpu, GICv3CPUState *s) static GICv3CPUState *icc_cs_from_env(CPUARMState *env) { - /* Given the CPU, find the right GICv3CPUState struct. - * Since we registered the CPU interface with the EL change hook as - * the opaque pointer, we can just directly get from the CPU to it. - */ - return arm_get_el_change_hook_opaque(arm_env_get_cpu(env)); + return env->gicv3state; } static bool gicv3_use_ns_bank(CPUARMState *env) @@ -431,7 +427,7 @@ static uint64_t icv_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; uint64_t value = cs->ich_apr[grp][regno]; trace_gicv3_icv_ap_read(ri->crm & 1, regno, gicv3_redist_affid(cs), value); @@ -443,7 +439,7 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; trace_gicv3_icv_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); @@ -1465,7 +1461,7 @@ static uint64_t icc_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t value; int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + int grp = (ri->crm & 1) ? GICV3_G1 : GICV3_G0; if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { return icv_ap_read(env, ri); @@ -1487,7 +1483,7 @@ static void icc_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1; + int grp = (ri->crm & 1) ? GICV3_G1 : GICV3_G0; if (icv_access(env, grp == GICV3_G0 ? HCR_FMO : HCR_IMO)) { icv_ap_write(env, ri, value); @@ -1554,7 +1550,7 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, * tested in cases where we know !IsSecure is true. */ route_fiq_to_el2 = env->cp15.hcr_el2 & HCR_FMO; - route_irq_to_el2 = env->cp15.hcr_el2 & HCR_FMO; + route_irq_to_el2 = env->cp15.hcr_el2 & HCR_IMO; switch (arm_current_el(env)) { case 3: @@ -2296,7 +2292,7 @@ static uint64_t ich_ap_read(CPUARMState *env, const ARMCPRegInfo *ri) { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; uint64_t value; value = cs->ich_apr[grp][regno]; @@ -2309,7 +2305,7 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, { GICv3CPUState *cs = icc_cs_from_env(env); int regno = ri->opc2 & 3; - int grp = ri->crm & 1 ? GICV3_G0 : GICV3_G1NS; + int grp = (ri->crm & 1) ? GICV3_G1NS : GICV3_G0; trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value); @@ -2615,9 +2611,7 @@ void gicv3_init_cpuif(GICv3State *s) * it might be with code translated by CPU 0 but run by CPU 1, in * which case we'd get the wrong value. * So instead we define the regs with no ri->opaque info, and - * get back to the GICv3CPUState from the ARMCPU by reading back - * the opaque pointer from the el_change_hook, which we're going - * to need to register anyway. + * get back to the GICv3CPUState from the CPUARMState. */ define_arm_cp_regs(cpu, gicv3_cpuif_reginfo); if (arm_feature(&cpu->env, ARM_FEATURE_EL2) diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c index 93fe936862..53c55c5729 100644 --- a/hw/intc/arm_gicv3_dist.c +++ b/hw/intc/arm_gicv3_dist.c @@ -441,7 +441,8 @@ static MemTxResult gicd_readl(GICv3State *s, hwaddr offset, int i, irq = offset - GICD_IPRIORITYR; uint32_t value = 0; - for (i = irq + 3; i >= irq; i--, value <<= 8) { + for (i = irq + 3; i >= irq; i--) { + value <<= 8; value |= gicd_read_ipriorityr(s, attrs, i); } *data = value; diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index eea6a73df2..271ebe461c 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -103,7 +103,7 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp) /* register the base address */ kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd); + KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd, 0); gicv3_its_init_mmio(s, NULL); diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index ec371772b3..1e11200fe2 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -135,7 +135,14 @@ static void kvm_dist_get_priority(GICv3State *s, uint32_t offset, uint8_t *bmp) uint32_t reg, *field; int irq; - field = (uint32_t *)bmp; + /* For the KVM GICv3, affinity routing is always enabled, and the first 8 + * GICD_IPRIORITYR registers are always RAZ/WI. The corresponding + * functionality is replaced by GICR_IPRIORITYR. It doesn't need to + * sync them. So it needs to skip the field of GIC_INTERNAL irqs in bmp and + * offset. + */ + field = (uint32_t *)(bmp + GIC_INTERNAL); + offset += (GIC_INTERNAL * 8) / 8; for_each_dist_irq_reg(irq, s->num_irq, 8) { kvm_gicd_access(s, offset, ®, false); *field = reg; @@ -149,7 +156,14 @@ static void kvm_dist_put_priority(GICv3State *s, uint32_t offset, uint8_t *bmp) uint32_t reg, *field; int irq; - field = (uint32_t *)bmp; + /* For the KVM GICv3, affinity routing is always enabled, and the first 8 + * GICD_IPRIORITYR registers are always RAZ/WI. The corresponding + * functionality is replaced by GICR_IPRIORITYR. It doesn't need to + * sync them. So it needs to skip the field of GIC_INTERNAL irqs in bmp and + * offset. + */ + field = (uint32_t *)(bmp + GIC_INTERNAL); + offset += (GIC_INTERNAL * 8) / 8; for_each_dist_irq_reg(irq, s->num_irq, 8) { reg = *field; kvm_gicd_access(s, offset, ®, true); @@ -164,6 +178,14 @@ static void kvm_dist_get_edge_trigger(GICv3State *s, uint32_t offset, uint32_t reg; int irq; + /* For the KVM GICv3, affinity routing is always enabled, and the first 2 + * GICD_ICFGR registers are always RAZ/WI. The corresponding + * functionality is replaced by GICR_ICFGR. It doesn't need to sync + * them. So it should increase the offset to skip GIC_INTERNAL irqs. + * This matches the for_each_dist_irq_reg() macro which also skips the + * first GIC_INTERNAL irqs. + */ + offset += (GIC_INTERNAL * 2) / 8; for_each_dist_irq_reg(irq, s->num_irq, 2) { kvm_gicd_access(s, offset, ®, false); reg = half_unshuffle32(reg >> 1); @@ -181,6 +203,14 @@ static void kvm_dist_put_edge_trigger(GICv3State *s, uint32_t offset, uint32_t reg; int irq; + /* For the KVM GICv3, affinity routing is always enabled, and the first 2 + * GICD_ICFGR registers are always RAZ/WI. The corresponding + * functionality is replaced by GICR_ICFGR. It doesn't need to sync + * them. So it should increase the offset to skip GIC_INTERNAL irqs. + * This matches the for_each_dist_irq_reg() macro which also skips the + * first GIC_INTERNAL irqs. + */ + offset += (GIC_INTERNAL * 2) / 8; for_each_dist_irq_reg(irq, s->num_irq, 2) { reg = *gic_bmp_ptr32(bmp, irq); if (irq % 32 != 0) { @@ -222,6 +252,15 @@ static void kvm_dist_getbmp(GICv3State *s, uint32_t offset, uint32_t *bmp) uint32_t reg; int irq; + /* For the KVM GICv3, affinity routing is always enabled, and the + * GICD_IGROUPR0/GICD_IGRPMODR0/GICD_ISENABLER0/GICD_ISPENDR0/ + * GICD_ISACTIVER0 registers are always RAZ/WI. The corresponding + * functionality is replaced by the GICR registers. It doesn't need to sync + * them. So it should increase the offset to skip GIC_INTERNAL irqs. + * This matches the for_each_dist_irq_reg() macro which also skips the + * first GIC_INTERNAL irqs. + */ + offset += (GIC_INTERNAL * 1) / 8; for_each_dist_irq_reg(irq, s->num_irq, 1) { kvm_gicd_access(s, offset, ®, false); *gic_bmp_ptr32(bmp, irq) = reg; @@ -235,6 +274,19 @@ static void kvm_dist_putbmp(GICv3State *s, uint32_t offset, uint32_t reg; int irq; + /* For the KVM GICv3, affinity routing is always enabled, and the + * GICD_IGROUPR0/GICD_IGRPMODR0/GICD_ISENABLER0/GICD_ISPENDR0/ + * GICD_ISACTIVER0 registers are always RAZ/WI. The corresponding + * functionality is replaced by the GICR registers. It doesn't need to sync + * them. So it should increase the offset and clroffset to skip GIC_INTERNAL + * irqs. This matches the for_each_dist_irq_reg() macro which also skips the + * first GIC_INTERNAL irqs. + */ + offset += (GIC_INTERNAL * 1) / 8; + if (clroffset != 0) { + clroffset += (GIC_INTERNAL * 1) / 8; + } + for_each_dist_irq_reg(irq, s->num_irq, 1) { /* If this bitmap is a set/clear register pair, first write to the * clear-reg to clear all bits before using the set-reg to write @@ -243,6 +295,7 @@ static void kvm_dist_putbmp(GICv3State *s, uint32_t offset, if (clroffset != 0) { reg = 0; kvm_gicd_access(s, clroffset, ®, true); + clroffset += 4; } reg = *gic_bmp_ptr32(bmp, irq); kvm_gicd_access(s, offset, ®, true); @@ -714,6 +767,7 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { GICv3State *s = KVM_ARM_GICV3(dev); KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s); + bool multiple_redist_region_allowed; Error *local_err = NULL; int i; @@ -731,7 +785,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } - gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL); + gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } for (i = 0; i < s->num_cpu; i++) { ARMCPU *cpu = ARM_CPU(qemu_get_cpu(i)); @@ -746,6 +804,18 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } + multiple_redist_region_allowed = + kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION); + + if (!multiple_redist_region_allowed && s->nb_redist_regions > 1) { + error_setg(errp, "Multiple VGICv3 redistributor regions are not " + "supported by this host kernel"); + error_append_hint(errp, "A maximum of %d VCPUs can be used", + s->redist_region_count[0]); + return; + } + kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &s->num_irq, true, &error_abort); @@ -754,13 +824,31 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true, &error_abort); kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd); - kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd); + KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd, 0); + + if (!multiple_redist_region_allowed) { + kvm_arm_register_device(&s->iomem_redist[0], -1, + KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd, 0); + } else { + /* we register regions in reverse order as "devices" are inserted at + * the head of a QSLIST and the list is then popped from the head + * onwards by kvm_arm_machine_init_done() + */ + for (i = s->nb_redist_regions - 1; i >= 0; i--) { + /* Address mask made of the rdist region index and count */ + uint64_t addr_ormask = + i | ((uint64_t)s->redist_region_count[i] << 52); + + kvm_arm_register_device(&s->iomem_redist[i], -1, + KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, + s->dev_fd, addr_ormask); + } + } if (kvm_has_gsi_routing()) { /* set up irq routing */ - kvm_init_irq_routing(kvm_state); for (i = 0; i < s->num_irq - GIC_INTERNAL; ++i) { kvm_irqchip_add_irq_route(kvm_state, i, 0, i); } diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index 8a8684d76e..3b0ba6de1a 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -192,7 +192,8 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset, int i, irq = offset - GICR_IPRIORITYR; uint32_t value = 0; - for (i = irq + 3; i >= irq; i--, value <<= 8) { + for (i = irq + 3; i >= irq; i--) { + value <<= 8; value |= gicr_read_ipriorityr(cs, attrs, i); } *data = value; diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index c51151fa8a..cd1e7f1729 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -525,13 +525,17 @@ static void do_armv7m_nvic_set_pending(void *opaque, int irq, bool secure, NVICState *s = (NVICState *)opaque; bool banked = exc_is_banked(irq); VecInfo *vec; + bool targets_secure; assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); assert(!secure || banked); vec = (banked && secure) ? &s->sec_vectors[irq] : &s->vectors[irq]; - trace_nvic_set_pending(irq, secure, derived, vec->enabled, vec->prio); + targets_secure = banked ? secure : exc_targets_secure(s, irq); + + trace_nvic_set_pending(irq, secure, targets_secure, + derived, vec->enabled, vec->prio); if (derived) { /* Derived exceptions are always synchronous. */ @@ -611,7 +615,7 @@ static void do_armv7m_nvic_set_pending(void *opaque, int irq, bool secure, */ irq = ARMV7M_EXCP_HARD; if (arm_feature(&s->cpu->env, ARM_FEATURE_M_SECURITY) && - (secure || + (targets_secure || !(s->cpu->env.v7m.aircr & R_V7M_AIRCR_BFHFNMINS_MASK))) { vec = &s->sec_vectors[irq]; } else { @@ -2060,7 +2064,7 @@ static int nvic_security_post_load(void *opaque, int version_id) } static const VMStateDescription vmstate_nvic_security = { - .name = "nvic/m-security", + .name = "armv7m_nvic/m-security", .version_id = 1, .minimum_version_id = 1, .needed = nvic_security_needed, @@ -2183,7 +2187,11 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) int regionlen; s->cpu = ARM_CPU(qemu_get_cpu(0)); - assert(s->cpu); + + if (!s->cpu || !arm_feature(&s->cpu->env, ARM_FEATURE_M)) { + error_setg(errp, "The NVIC can only be used with a Cortex-M CPU"); + return; + } if (s->num_irq > NVIC_MAX_IRQ) { error_setg(errp, "num-irq %d exceeds NVIC maximum", s->num_irq); @@ -2292,9 +2300,8 @@ static void armv7m_nvic_instance_init(Object *obj) NVICState *nvic = NVIC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - object_initialize(&nvic->systick[M_REG_NS], - sizeof(nvic->systick[M_REG_NS]), TYPE_SYSTICK); - qdev_set_parent_bus(DEVICE(&nvic->systick[M_REG_NS]), sysbus_get_default()); + sysbus_init_child_obj(obj, "systick-reg-ns", &nvic->systick[M_REG_NS], + sizeof(nvic->systick[M_REG_NS]), TYPE_SYSTICK); /* We can't initialize the secure systick here, as we don't know * yet if we need it. */ diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index b6b00a4f58..69f9c18d73 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -281,9 +281,9 @@ static void exynos4210_gic_set_irq(void *opaque, int irq, int level) qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level); } -static void exynos4210_gic_init(Object *obj) +static void exynos4210_gic_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(obj); + Object *obj = OBJECT(dev); Exynos4210GicState *s = EXYNOS4210_GIC(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); const char cpu_prefix[] = "exynos4210-gic-alias_cpu"; @@ -347,13 +347,13 @@ static void exynos4210_gic_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->props = exynos4210_gic_properties; + dc->realize = exynos4210_gic_realize; } static const TypeInfo exynos4210_gic_info = { .name = TYPE_EXYNOS4210_GIC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210GicState), - .instance_init = exynos4210_gic_init, .class_init = exynos4210_gic_class_init, }; diff --git a/hw/intc/heathrow_pic.c b/hw/intc/heathrow_pic.c index 393fdd7326..b8b997deca 100644 --- a/hw/intc/heathrow_pic.c +++ b/hw/intc/heathrow_pic.c @@ -172,29 +172,16 @@ static void heathrow_init(Object *obj) HeathrowState *s = HEATHROW(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + /* only 1 CPU */ + qdev_init_gpio_out(DEVICE(obj), s->irqs, 1); + + qdev_init_gpio_in(DEVICE(obj), heathrow_set_irq, HEATHROW_NUM_IRQS); + memory_region_init_io(&s->mem, OBJECT(s), &heathrow_ops, s, "heathrow-pic", 0x1000); sysbus_init_mmio(sbd, &s->mem); } -DeviceState *heathrow_pic_init(int nb_cpus, qemu_irq **irqs, - qemu_irq **pic_irqs) -{ - DeviceState *d; - HeathrowState *s; - - d = qdev_create(NULL, TYPE_HEATHROW); - qdev_init_nofail(d); - - s = HEATHROW(d); - /* only 1 CPU */ - s->irqs = irqs[0]; - - *pic_irqs = qemu_allocate_irqs(heathrow_set_irq, s, HEATHROW_NUM_IRQS); - - return d; -} - static void heathrow_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 36139a4db6..b6896ac4ce 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -28,9 +28,8 @@ #include "hw/i386/apic.h" #include "hw/i386/ioapic.h" #include "hw/i386/ioapic_internal.h" -#include "include/hw/pci/msi.h" +#include "hw/pci/msi.h" #include "sysemu/kvm.h" -#include "target/i386/cpu.h" #include "hw/i386/apic-msidef.h" #include "hw/i386/x86-iommu.h" #include "trace.h" @@ -149,10 +148,11 @@ static void ioapic_set_irq(void *opaque, int vector, int level) * the cleanest way of doing it but it should work. */ trace_ioapic_set_irq(vector, level); + ioapic_stat_update_irq(s, vector, level); if (vector == 0) { vector = 2; } - if (vector >= 0 && vector < IOAPIC_NUM_PINS) { + if (vector < IOAPIC_NUM_PINS) { uint32_t mask = 1 << vector; uint64_t entry = s->ioredtbl[vector]; @@ -234,17 +234,6 @@ void ioapic_eoi_broadcast(int vector) } } -void ioapic_dump_state(Monitor *mon, const QDict *qdict) -{ - int i; - - for (i = 0; i < MAX_IOAPICS; i++) { - if (ioapics[i] != 0) { - ioapic_print_redtbl(mon, ioapics[i]); - } - } -} - static uint64_t ioapic_mem_read(void *opaque, hwaddr addr, unsigned int size) { diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index 3b3d0a7680..692dc37bb6 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -24,6 +24,7 @@ #include "monitor/monitor.h" #include "hw/i386/ioapic.h" #include "hw/i386/ioapic_internal.h" +#include "hw/intc/intc.h" #include "hw/sysbus.h" /* ioapic_no count start from 0 to MAX_IOAPICS, @@ -34,6 +35,28 @@ */ int ioapic_no; +void ioapic_stat_update_irq(IOAPICCommonState *s, int irq, int level) +{ + if (level != s->irq_level[irq]) { + s->irq_level[irq] = level; + if (level == 1) { + s->irq_count[irq]++; + } + } +} + +static bool ioapic_get_statistics(InterruptStatsProvider *obj, + uint64_t **irq_counts, + unsigned int *nb_irqs) +{ + IOAPICCommonState *s = IOAPIC_COMMON(obj); + + *irq_counts = s->irq_count; + *nb_irqs = IOAPIC_NUM_PINS; + + return true; +} + static void ioapic_irr_dump(Monitor *mon, const char *name, uint32_t bitmap) { int i; @@ -58,7 +81,7 @@ void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) uint32_t remote_irr = 0; int i; - monitor_printf(mon, "ioapic ver=0x%x id=0x%02x sel=0x%02x", + monitor_printf(mon, "ioapic0: ver=0x%x id=0x%02x sel=0x%02x", s->version, s->id, s->ioregsel); if (s->ioregsel) { monitor_printf(mon, " (redir[%u])\n", @@ -70,7 +93,7 @@ void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) uint64_t entry = s->ioredtbl[i]; uint32_t delm = (uint32_t)((entry & IOAPIC_LVT_DELIV_MODE) >> IOAPIC_LVT_DELIV_MODE_SHIFT); - monitor_printf(mon, "pin %-2u 0x%016"PRIx64" dest=%"PRIx64 + monitor_printf(mon, " pin %-2u 0x%016"PRIx64" dest=%"PRIx64 " vec=%-3"PRIu64" %s %-5s %-6s %-6s %s\n", i, entry, (entry >> IOAPIC_LVT_DEST_SHIFT) & @@ -85,8 +108,8 @@ void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s) remote_irr |= entry & IOAPIC_LVT_TRIGGER_MODE ? (entry & IOAPIC_LVT_REMOTE_IRR ? (1 << i) : 0) : 0; } - ioapic_irr_dump(mon, "IRR", s->irr); - ioapic_irr_dump(mon, "Remote IRR", remote_irr); + ioapic_irr_dump(mon, " IRR", s->irr); + ioapic_irr_dump(mon, " Remote IRR", remote_irr); } void ioapic_reset_common(DeviceState *dev) @@ -142,6 +165,15 @@ static void ioapic_common_realize(DeviceState *dev, Error **errp) ioapic_no++; } +static void ioapic_print_info(InterruptStatsProvider *obj, + Monitor *mon) +{ + IOAPICCommonState *s = IOAPIC_COMMON(obj); + + ioapic_dispatch_pre_save(s); + ioapic_print_redtbl(mon, s); +} + static const VMStateDescription vmstate_ioapic_common = { .name = "ioapic", .version_id = 3, @@ -161,9 +193,12 @@ static const VMStateDescription vmstate_ioapic_common = { static void ioapic_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); dc->realize = ioapic_common_realize; dc->vmsd = &vmstate_ioapic_common; + ic->print_info = ioapic_print_info; + ic->get_statistics = ioapic_get_statistics; } static const TypeInfo ioapic_common_type = { @@ -173,6 +208,10 @@ static const TypeInfo ioapic_common_type = { .class_size = sizeof(IOAPICCommonClass), .class_init = ioapic_common_class_init, .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_INTERRUPT_STATS_PROVIDER }, + { } + }, }; static void ioapic_common_register_types(void) diff --git a/hw/intc/realview_gic.c b/hw/intc/realview_gic.c index 50bbab66ee..7f2ff85c83 100644 --- a/hw/intc/realview_gic.c +++ b/hw/intc/realview_gic.c @@ -54,16 +54,13 @@ static void realview_gic_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); RealViewGICState *s = REALVIEW_GIC(obj); - DeviceState *gicdev; memory_region_init(&s->container, OBJECT(s), "realview-gic-container", 0x2000); sysbus_init_mmio(sbd, &s->container); - object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC); - gicdev = DEVICE(&s->gic); - qdev_set_parent_bus(gicdev, sysbus_get_default()); - qdev_prop_set_uint32(gicdev, "num-cpu", 1); + sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), TYPE_ARM_GIC); + qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", 1); } static void realview_gic_class_init(ObjectClass *oc, void *data) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 55e8c2570c..5fb18e65c9 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -177,7 +177,7 @@ nvic_set_prio(int irq, bool secure, uint8_t prio) "NVIC set irq %d secure-bank % nvic_irq_update(int vectpending, int pendprio, int exception_prio, int level) "NVIC vectpending %d pending prio %d exception_prio %d: setting irq line to %d" nvic_escalate_prio(int irq, int irqprio, int runprio) "NVIC escalating irq %d to HardFault: insufficient priority %d >= %d" nvic_escalate_disabled(int irq) "NVIC escalating irq %d to HardFault: disabled" -nvic_set_pending(int irq, bool secure, bool derived, int en, int prio) "NVIC set pending irq %d secure-bank %d derived %d (enabled: %d priority %d)" +nvic_set_pending(int irq, bool secure, bool targets_secure, bool derived, int en, int prio) "NVIC set pending irq %d secure-bank %d targets_secure %d derived %d (enabled: %d priority %d)" nvic_clear_pending(int irq, bool secure, int en, int prio) "NVIC clear pending irq %d secure-bank %d (enabled: %d priority %d)" nvic_set_pending_level(int irq) "NVIC set pending: irq %d higher prio than vectpending: setting irq line to 1" nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (prio %d)" diff --git a/hw/intc/xics.c b/hw/intc/xics.c index e73e623e3b..c90c893228 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -291,10 +291,9 @@ static const VMStateDescription vmstate_icp_server = { }, }; -static void icp_reset(void *dev) +static void icp_reset(DeviceState *dev) { ICPState *icp = ICP(dev); - ICPStateClass *icpc = ICP_GET_CLASS(icp); icp->xirr = 0; icp->pending_priority = 0xff; @@ -302,16 +301,18 @@ static void icp_reset(void *dev) /* Make all outputs are deasserted */ qemu_set_irq(icp->output, 0); +} - if (icpc->reset) { - icpc->reset(icp); - } +static void icp_reset_handler(void *dev) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + dc->reset(dev); } static void icp_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); - ICPStateClass *icpc = ICP_GET_CLASS(dev); PowerPCCPU *cpu; CPUPPCState *env; Object *obj; @@ -351,11 +352,7 @@ static void icp_realize(DeviceState *dev, Error **errp) return; } - if (icpc->realize) { - icpc->realize(icp, errp); - } - - qemu_register_reset(icp_reset, dev); + qemu_register_reset(icp_reset_handler, dev); vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); } @@ -364,7 +361,7 @@ static void icp_unrealize(DeviceState *dev, Error **errp) ICPState *icp = ICP(dev); vmstate_unregister(NULL, &vmstate_icp_server, icp); - qemu_unregister_reset(icp_reset, dev); + qemu_unregister_reset(icp_reset_handler, dev); } static void icp_class_init(ObjectClass *klass, void *data) @@ -373,6 +370,7 @@ static void icp_class_init(ObjectClass *klass, void *data) dc->realize = icp_realize; dc->unrealize = icp_unrealize; + dc->reset = icp_reset; } static const TypeInfo icp_info = { @@ -547,9 +545,61 @@ static void ics_simple_eoi(ICSState *ics, uint32_t nr) } } -static void ics_simple_reset(void *dev) +static void ics_simple_reset(DeviceState *dev) +{ + ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); + + icsc->parent_reset(dev); +} + +static void ics_simple_reset_handler(void *dev) +{ + ics_simple_reset(dev); +} + +static void ics_simple_realize(DeviceState *dev, Error **errp) { ICSState *ics = ICS_SIMPLE(dev); + ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics); + Error *local_err = NULL; + + icsc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); + + qemu_register_reset(ics_simple_reset_handler, ics); +} + +static void ics_simple_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICSStateClass *isc = ICS_BASE_CLASS(klass); + + device_class_set_parent_realize(dc, ics_simple_realize, + &isc->parent_realize); + device_class_set_parent_reset(dc, ics_simple_reset, + &isc->parent_reset); + + isc->reject = ics_simple_reject; + isc->resend = ics_simple_resend; + isc->eoi = ics_simple_eoi; +} + +static const TypeInfo ics_simple_info = { + .name = TYPE_ICS_SIMPLE, + .parent = TYPE_ICS_BASE, + .instance_size = sizeof(ICSState), + .class_init = ics_simple_class_init, + .class_size = sizeof(ICSStateClass), +}; + +static void ics_base_reset(DeviceState *dev) +{ + ICSState *ics = ICS_BASE(dev); int i; uint8_t flags[ics->nr_irqs]; @@ -566,7 +616,35 @@ static void ics_simple_reset(void *dev) } } -static int ics_simple_dispatch_pre_save(void *opaque) +static void ics_base_realize(DeviceState *dev, Error **errp) +{ + ICSState *ics = ICS_BASE(dev); + Object *obj; + Error *err = NULL; + + obj = object_property_get_link(OBJECT(dev), ICS_PROP_XICS, &err); + if (!obj) { + error_propagate(errp, err); + error_prepend(errp, "required link '" ICS_PROP_XICS "' not found: "); + return; + } + ics->xics = XICS_FABRIC(obj); + + if (!ics->nr_irqs) { + error_setg(errp, "Number of interrupts needs to be greater 0"); + return; + } + ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); +} + +static void ics_base_instance_init(Object *obj) +{ + ICSState *ics = ICS_BASE(obj); + + ics->offset = XICS_IRQ_BASE; +} + +static int ics_base_dispatch_pre_save(void *opaque) { ICSState *ics = opaque; ICSStateClass *info = ICS_BASE_GET_CLASS(ics); @@ -578,7 +656,7 @@ static int ics_simple_dispatch_pre_save(void *opaque) return 0; } -static int ics_simple_dispatch_post_load(void *opaque, int version_id) +static int ics_base_dispatch_post_load(void *opaque, int version_id) { ICSState *ics = opaque; ICSStateClass *info = ICS_BASE_GET_CLASS(ics); @@ -590,7 +668,7 @@ static int ics_simple_dispatch_post_load(void *opaque, int version_id) return 0; } -static const VMStateDescription vmstate_ics_simple_irq = { +static const VMStateDescription vmstate_ics_base_irq = { .name = "ics/irq", .version_id = 2, .minimum_version_id = 1, @@ -604,95 +682,36 @@ static const VMStateDescription vmstate_ics_simple_irq = { }, }; -static const VMStateDescription vmstate_ics_simple = { +static const VMStateDescription vmstate_ics_base = { .name = "ics", .version_id = 1, .minimum_version_id = 1, - .pre_save = ics_simple_dispatch_pre_save, - .post_load = ics_simple_dispatch_post_load, + .pre_save = ics_base_dispatch_pre_save, + .post_load = ics_base_dispatch_post_load, .fields = (VMStateField[]) { /* Sanity check */ VMSTATE_UINT32_EQUAL(nr_irqs, ICSState, NULL), VMSTATE_STRUCT_VARRAY_POINTER_UINT32(irqs, ICSState, nr_irqs, - vmstate_ics_simple_irq, + vmstate_ics_base_irq, ICSIRQState), VMSTATE_END_OF_LIST() }, }; -static void ics_simple_initfn(Object *obj) -{ - ICSState *ics = ICS_SIMPLE(obj); - - ics->offset = XICS_IRQ_BASE; -} - -static void ics_simple_realize(ICSState *ics, Error **errp) -{ - if (!ics->nr_irqs) { - error_setg(errp, "Number of interrupts needs to be greater 0"); - return; - } - ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); - ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); - - qemu_register_reset(ics_simple_reset, ics); -} - -static Property ics_simple_properties[] = { +static Property ics_base_properties[] = { DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), DEFINE_PROP_END_OF_LIST(), }; -static void ics_simple_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ICSStateClass *isc = ICS_BASE_CLASS(klass); - - isc->realize = ics_simple_realize; - dc->props = ics_simple_properties; - dc->vmsd = &vmstate_ics_simple; - isc->reject = ics_simple_reject; - isc->resend = ics_simple_resend; - isc->eoi = ics_simple_eoi; -} - -static const TypeInfo ics_simple_info = { - .name = TYPE_ICS_SIMPLE, - .parent = TYPE_ICS_BASE, - .instance_size = sizeof(ICSState), - .class_init = ics_simple_class_init, - .class_size = sizeof(ICSStateClass), - .instance_init = ics_simple_initfn, -}; - -static void ics_base_realize(DeviceState *dev, Error **errp) -{ - ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); - ICSState *ics = ICS_BASE(dev); - Object *obj; - Error *err = NULL; - - obj = object_property_get_link(OBJECT(dev), ICS_PROP_XICS, &err); - if (!obj) { - error_propagate(errp, err); - error_prepend(errp, "required link '" ICS_PROP_XICS "' not found: "); - return; - } - ics->xics = XICS_FABRIC(obj); - - - if (icsc->realize) { - icsc->realize(ics, errp); - } -} - static void ics_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ics_base_realize; + dc->props = ics_base_properties; + dc->reset = ics_base_reset; + dc->vmsd = &vmstate_ics_base; } static const TypeInfo ics_base_info = { @@ -700,6 +719,7 @@ static const TypeInfo ics_base_info = { .parent = TYPE_DEVICE, .abstract = true, .instance_size = sizeof(ICSState), + .instance_init = ics_base_instance_init, .class_init = ics_base_class_init, .class_size = sizeof(ICSStateClass), }; diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 89fb20e2c5..30c3769a20 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -56,10 +56,6 @@ static QLIST_HEAD(, KVMEnabledICP) static void icp_get_kvm_state(ICPState *icp) { uint64_t state; - struct kvm_one_reg reg = { - .id = KVM_REG_PPC_ICP_STATE, - .addr = (uintptr_t)&state, - }; int ret; /* ICP for this CPU thread is not in use, exiting */ @@ -67,7 +63,7 @@ static void icp_get_kvm_state(ICPState *icp) return; } - ret = kvm_vcpu_ioctl(icp->cs, KVM_GET_ONE_REG, ®); + ret = kvm_get_one_reg(icp->cs, KVM_REG_PPC_ICP_STATE, &state); if (ret != 0) { error_report("Unable to retrieve KVM interrupt controller state" " for CPU %ld: %s", kvm_arch_vcpu_id(icp->cs), strerror(errno)); @@ -96,10 +92,6 @@ static void icp_synchronize_state(ICPState *icp) static int icp_set_kvm_state(ICPState *icp, int version_id) { uint64_t state; - struct kvm_one_reg reg = { - .id = KVM_REG_PPC_ICP_STATE, - .addr = (uintptr_t)&state, - }; int ret; /* ICP for this CPU thread is not in use, exiting */ @@ -111,7 +103,7 @@ static int icp_set_kvm_state(ICPState *icp, int version_id) | ((uint64_t)icp->mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT) | ((uint64_t)icp->pending_priority << KVM_REG_PPC_ICP_PPRI_SHIFT); - ret = kvm_vcpu_ioctl(icp->cs, KVM_SET_ONE_REG, ®); + ret = kvm_set_one_reg(icp->cs, KVM_REG_PPC_ICP_STATE, &state); if (ret != 0) { error_report("Unable to restore KVM interrupt controller state (0x%" PRIx64 ") for CPU %ld: %s", state, kvm_arch_vcpu_id(icp->cs), @@ -122,22 +114,38 @@ static int icp_set_kvm_state(ICPState *icp, int version_id) return 0; } -static void icp_kvm_reset(ICPState *icp) +static void icp_kvm_reset(DeviceState *dev) { - icp_set_kvm_state(icp, 1); + ICPStateClass *icpc = ICP_GET_CLASS(dev); + + icpc->parent_reset(dev); + + icp_set_kvm_state(ICP(dev), 1); } -static void icp_kvm_realize(ICPState *icp, Error **errp) +static void icp_kvm_realize(DeviceState *dev, Error **errp) { - CPUState *cs = icp->cs; + ICPState *icp = ICP(dev); + ICPStateClass *icpc = ICP_GET_CLASS(icp); + Error *local_err = NULL; + CPUState *cs; KVMEnabledICP *enabled_icp; - unsigned long vcpu_id = kvm_arch_vcpu_id(cs); + unsigned long vcpu_id; int ret; if (kernel_xics_fd == -1) { abort(); } + icpc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + cs = icp->cs; + vcpu_id = kvm_arch_vcpu_id(cs); + /* * If we are reusing a parked vCPU fd corresponding to the CPU * which was hot-removed earlier we don't have to renable @@ -162,12 +170,16 @@ static void icp_kvm_realize(ICPState *icp, Error **errp) static void icp_kvm_class_init(ObjectClass *klass, void *data) { + DeviceClass *dc = DEVICE_CLASS(klass); ICPStateClass *icpc = ICP_CLASS(klass); + device_class_set_parent_realize(dc, icp_kvm_realize, + &icpc->parent_realize); + device_class_set_parent_reset(dc, icp_kvm_reset, + &icpc->parent_reset); + icpc->pre_save = icp_get_kvm_state; icpc->post_load = icp_set_kvm_state; - icpc->realize = icp_kvm_realize; - icpc->reset = icp_kvm_reset; icpc->synchronize_state = icp_synchronize_state; } @@ -185,23 +197,16 @@ static const TypeInfo icp_kvm_info = { static void ics_get_kvm_state(ICSState *ics) { uint64_t state; - struct kvm_device_attr attr = { - .flags = 0, - .group = KVM_DEV_XICS_GRP_SOURCES, - .addr = (uint64_t)(uintptr_t)&state, - }; int i; + Error *local_err = NULL; for (i = 0; i < ics->nr_irqs; i++) { ICSIRQState *irq = &ics->irqs[i]; - int ret; - attr.attr = i + ics->offset; - - ret = ioctl(kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr); - if (ret != 0) { - error_report("Unable to retrieve KVM interrupt controller state" - " for IRQ %d: %s", i + ics->offset, strerror(errno)); + kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, + i + ics->offset, &state, false, &local_err); + if (local_err) { + error_report_err(local_err); exit(1); } @@ -255,19 +260,13 @@ static void ics_synchronize_state(ICSState *ics) static int ics_set_kvm_state(ICSState *ics, int version_id) { uint64_t state; - struct kvm_device_attr attr = { - .flags = 0, - .group = KVM_DEV_XICS_GRP_SOURCES, - .addr = (uint64_t)(uintptr_t)&state, - }; int i; + Error *local_err = NULL; for (i = 0; i < ics->nr_irqs; i++) { ICSIRQState *irq = &ics->irqs[i]; int ret; - attr.attr = i + ics->offset; - state = irq->server; state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK) << KVM_XICS_PRIORITY_SHIFT; @@ -293,10 +292,10 @@ static int ics_set_kvm_state(ICSState *ics, int version_id) state |= KVM_XICS_QUEUED; } - ret = ioctl(kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr); - if (ret != 0) { - error_report("Unable to restore KVM interrupt controller state" - " for IRQs %d: %s", i + ics->offset, strerror(errno)); + ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, + i + ics->offset, &state, true, &local_err); + if (local_err) { + error_report_err(local_err); return ret; } } @@ -325,44 +324,46 @@ static void ics_kvm_set_irq(void *opaque, int srcno, int val) } } -static void ics_kvm_reset(void *dev) +static void ics_kvm_reset(DeviceState *dev) { - ICSState *ics = ICS_SIMPLE(dev); - int i; - uint8_t flags[ics->nr_irqs]; + ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); - for (i = 0; i < ics->nr_irqs; i++) { - flags[i] = ics->irqs[i].flags; - } + icsc->parent_reset(dev); - memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); - - for (i = 0; i < ics->nr_irqs; i++) { - ics->irqs[i].priority = 0xff; - ics->irqs[i].saved_priority = 0xff; - ics->irqs[i].flags = flags[i]; - } - - ics_set_kvm_state(ics, 1); + ics_set_kvm_state(ICS_KVM(dev), 1); } -static void ics_kvm_realize(ICSState *ics, Error **errp) +static void ics_kvm_reset_handler(void *dev) { - if (!ics->nr_irqs) { - error_setg(errp, "Number of interrupts needs to be greater 0"); + ics_kvm_reset(dev); +} + +static void ics_kvm_realize(DeviceState *dev, Error **errp) +{ + ICSState *ics = ICS_KVM(dev); + ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics); + Error *local_err = NULL; + + icsc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); - qemu_register_reset(ics_kvm_reset, ics); + qemu_register_reset(ics_kvm_reset_handler, ics); } static void ics_kvm_class_init(ObjectClass *klass, void *data) { ICSStateClass *icsc = ICS_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + device_class_set_parent_realize(dc, ics_kvm_realize, + &icsc->parent_realize); + device_class_set_parent_reset(dc, ics_kvm_reset, + &icsc->parent_reset); - icsc->realize = ics_kvm_realize; icsc->pre_save = ics_get_kvm_state; icsc->post_load = ics_set_kvm_state; icsc->synchronize_state = ics_synchronize_state; @@ -370,7 +371,7 @@ static void ics_kvm_class_init(ObjectClass *klass, void *data) static const TypeInfo ics_kvm_info = { .name = TYPE_ICS_KVM, - .parent = TYPE_ICS_SIMPLE, + .parent = TYPE_ICS_BASE, .instance_size = sizeof(ICSState), .class_init = ics_kvm_class_init, }; @@ -391,10 +392,6 @@ static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr, int xics_kvm_init(sPAPRMachineState *spapr, Error **errp) { int rc; - struct kvm_create_device xics_create_device = { - .type = KVM_DEV_TYPE_XICS, - .flags = 0, - }; if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) { error_setg(errp, @@ -431,20 +428,19 @@ int xics_kvm_init(sPAPRMachineState *spapr, Error **errp) goto fail; } - /* Create the kernel ICP */ - rc = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &xics_create_device); + /* Create the KVM XICS device */ + rc = kvm_create_device(kvm_state, KVM_DEV_TYPE_XICS, false); if (rc < 0) { error_setg_errno(errp, -rc, "Error on KVM_CREATE_DEVICE for XICS"); goto fail; } - kernel_xics_fd = xics_create_device.fd; - + kernel_xics_fd = rc; kvm_kernel_irqchip = true; kvm_msi_via_irqfd_allowed = true; kvm_gsi_direct_mapping = true; - return rc; + return 0; fail: kvmppc_define_rtas_kernel_token(0, "ibm,set-xive"); diff --git a/hw/intc/xics_pnv.c b/hw/intc/xics_pnv.c index c87de2189c..fa48505f36 100644 --- a/hw/intc/xics_pnv.c +++ b/hw/intc/xics_pnv.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "qemu/log.h" #include "hw/ppc/xics.h" @@ -158,9 +159,18 @@ static const MemoryRegionOps pnv_icp_ops = { }, }; -static void pnv_icp_realize(ICPState *icp, Error **errp) +static void pnv_icp_realize(DeviceState *dev, Error **errp) { + ICPState *icp = ICP(dev); PnvICPState *pnv_icp = PNV_ICP(icp); + ICPStateClass *icpc = ICP_GET_CLASS(icp); + Error *local_err = NULL; + + icpc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } memory_region_init_io(&pnv_icp->mmio, OBJECT(icp), &pnv_icp_ops, icp, "icp-thread", 0x1000); @@ -171,7 +181,8 @@ static void pnv_icp_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); ICPStateClass *icpc = ICP_CLASS(klass); - icpc->realize = pnv_icp_realize; + device_class_set_parent_realize(dc, pnv_icp_realize, + &icpc->parent_realize); dc->desc = "PowerNV ICP"; } diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index da05c8589d..cd3e79139d 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -9,6 +9,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/ipack/ipack.h" #include "hw/pci/pci.h" #include "qemu/bitops.h" @@ -597,9 +598,9 @@ static void tpci200_realize(PCIDevice *pci_dev, Error **errp) memory_region_init_io(&s->las1, OBJECT(s), &tpci200_las1_ops, s, "tpci200_las1", 1024); memory_region_init_io(&s->las2, OBJECT(s), &tpci200_las2_ops, - s, "tpci200_las2", 1024*1024*32); + s, "tpci200_las2", 32 * MiB); memory_region_init_io(&s->las3, OBJECT(s), &tpci200_las3_ops, - s, "tpci200_las3", 1024*1024*16); + s, "tpci200_las3", 16 * MiB); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io); pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->las0); diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index 9be281fd87..63c031703d 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -104,7 +104,7 @@ void ipmi_bmc_find_and_link(Object *obj, Object **bmc) { object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc, isa_ipmi_bmc_check, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); } diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index 689587b65d..a79431554a 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/ipmi/ipmi.h" @@ -422,24 +423,69 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); } -const VMStateDescription vmstate_ISAIPMIKCSDevice = { +static int ipmi_kcs_vmstate_post_load(void *opaque, int version) +{ + IPMIKCS *ik = opaque; + + /* Make sure all the values are sane. */ + if (ik->outpos >= MAX_IPMI_MSG_SIZE || ik->outlen >= MAX_IPMI_MSG_SIZE || + ik->outpos >= ik->outlen) { + qemu_log_mask(LOG_GUEST_ERROR, + "ipmi:kcs: vmstate transfer received bad out values: %d %d\n", + ik->outpos, ik->outlen); + ik->outpos = 0; + ik->outlen = 0; + } + + if (ik->inlen >= MAX_IPMI_MSG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "ipmi:kcs: vmstate transfer received bad in value: %d\n", + ik->inlen); + ik->inlen = 0; + } + + return 0; +} + +static bool vmstate_kcs_before_version2(void *opaque, int version) +{ + return version <= 1; +} + +static const VMStateDescription vmstate_IPMIKCS = { + .name = TYPE_IPMI_INTERFACE_PREFIX "kcs", + .version_id = 2, + .minimum_version_id = 1, + .post_load = ipmi_kcs_vmstate_post_load, + .fields = (VMStateField[]) { + VMSTATE_BOOL(obf_irq_set, IPMIKCS), + VMSTATE_BOOL(atn_irq_set, IPMIKCS), + VMSTATE_UNUSED_TEST(vmstate_kcs_before_version2, 1), /* Was use_irq */ + VMSTATE_BOOL(irqs_enabled, IPMIKCS), + VMSTATE_UINT32(outpos, IPMIKCS), + VMSTATE_UINT32_V(outlen, IPMIKCS, 2), + VMSTATE_UINT8_ARRAY(outmsg, IPMIKCS, MAX_IPMI_MSG_SIZE), + VMSTATE_UINT32_V(inlen, IPMIKCS, 2), + VMSTATE_UINT8_ARRAY(inmsg, IPMIKCS, MAX_IPMI_MSG_SIZE), + VMSTATE_BOOL(write_end, IPMIKCS), + VMSTATE_UINT8(status_reg, IPMIKCS), + VMSTATE_UINT8(data_out_reg, IPMIKCS), + VMSTATE_INT16(data_in_reg, IPMIKCS), + VMSTATE_INT16(cmd_reg, IPMIKCS), + VMSTATE_UINT8(waiting_rsp, IPMIKCS), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ISAIPMIKCSDevice = { .name = TYPE_IPMI_INTERFACE, - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_BOOL(kcs.obf_irq_set, ISAIPMIKCSDevice), - VMSTATE_BOOL(kcs.atn_irq_set, ISAIPMIKCSDevice), - VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice), - VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice), - VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice), - VMSTATE_UINT8_ARRAY(kcs.outmsg, ISAIPMIKCSDevice, MAX_IPMI_MSG_SIZE), - VMSTATE_UINT8_ARRAY(kcs.inmsg, ISAIPMIKCSDevice, MAX_IPMI_MSG_SIZE), - VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice), - VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice), - VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice), - VMSTATE_INT16(kcs.data_in_reg, ISAIPMIKCSDevice), - VMSTATE_INT16(kcs.cmd_reg, ISAIPMIKCSDevice), - VMSTATE_UINT8(kcs.waiting_rsp, ISAIPMIKCSDevice), + VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2, + 0, vmstate_IPMIKCS, IPMIKCS, 1), + VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS, + IPMIKCS, 2), VMSTATE_END_OF_LIST() } }; @@ -450,6 +496,11 @@ static void isa_ipmi_kcs_init(Object *obj) ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); + /* + * Version 1 had an incorrect name, it clashed with the BT + * IPMI device, so receive it, but transmit a different + * version. + */ vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); } diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index b95608a003..8bc2f69eaa 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -13,7 +13,6 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "sysemu/sysemu.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "chardev/char.h" #include "hw/isa/superio.h" @@ -43,7 +42,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) if (!k->parallel.is_enabled || k->parallel.is_enabled(sio, i)) { /* FIXME use a qdev chardev prop instead of parallel_hds[] */ chr = parallel_hds[i]; - if (chr == NULL || chr->be) { + if (chr == NULL) { name = g_strdup_printf("discarding-parallel%d", i); chr = qemu_chr_new(name, "null"); } else { @@ -81,9 +80,9 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) break; } if (!k->serial.is_enabled || k->serial.is_enabled(sio, i)) { - /* FIXME use a qdev chardev prop instead of serial_hds[] */ - chr = serial_hds[i]; - if (chr == NULL || chr->be) { + /* FIXME use a qdev chardev prop instead of serial_hd() */ + chr = serial_hd(i); + if (chr == NULL) { name = g_strdup_printf("discarding-serial%d", i); chr = qemu_chr_new(name, "null"); } else { diff --git a/hw/isa/smc37c669-superio.c b/hw/isa/smc37c669-superio.c index aa233c6967..64466a9373 100644 --- a/hw/isa/smc37c669-superio.c +++ b/hw/isa/smc37c669-superio.c @@ -37,7 +37,7 @@ static bool is_parallel_enabled(ISASuperIODevice *sio, uint8_t index) static uint16_t get_parallel_iobase(ISASuperIODevice *sio, uint8_t index) { - return 0x3bc; + return 0x378; } static unsigned int get_parallel_irq(ISASuperIODevice *sio, uint8_t index) diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index 527bcc229c..fd8eccca14 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qemu-common.h" #include "cpu.h" @@ -27,7 +28,6 @@ #include "hw/devices.h" #include "hw/boards.h" #include "hw/loader.h" -#include "sysemu/block-backend.h" #include "elf.h" #include "lm32_hwsetup.h" #include "lm32.h" @@ -88,10 +88,10 @@ static void lm32_evr_init(MachineState *machine) /* memory map */ hwaddr flash_base = 0x04000000; - size_t flash_sector_size = 256 * 1024; - size_t flash_size = 32 * 1024 * 1024; + size_t flash_sector_size = 256 * KiB; + size_t flash_size = 32 * MiB; hwaddr ram_base = 0x08000000; - size_t ram_size = 64 * 1024 * 1024; + size_t ram_size = 64 * MiB; hwaddr timer0_base = 0x80002000; hwaddr uart0_base = 0x80006000; hwaddr timer1_base = 0x8000a000; @@ -125,12 +125,12 @@ static void lm32_evr_init(MachineState *machine) irq[i] = qdev_get_gpio_in(env->pic_state, i); } - lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]); + lm32_uart_create(uart0_base, irq[uart0_irq], serial_hd(0)); sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(serial_hds[1]); + env->juart_state = lm32_juart_init(serial_hd(1)); reset_info->bootstrap_pc = flash_base; @@ -174,10 +174,10 @@ static void lm32_uclinux_init(MachineState *machine) /* memory map */ hwaddr flash_base = 0x04000000; - size_t flash_sector_size = 256 * 1024; - size_t flash_size = 32 * 1024 * 1024; + size_t flash_sector_size = 256 * KiB; + size_t flash_size = 32 * MiB; hwaddr ram_base = 0x08000000; - size_t ram_size = 64 * 1024 * 1024; + size_t ram_size = 64 * MiB; hwaddr uart0_base = 0x80000000; hwaddr timer0_base = 0x80002000; hwaddr timer1_base = 0x80010000; @@ -217,13 +217,13 @@ static void lm32_uclinux_init(MachineState *machine) irq[i] = qdev_get_gpio_in(env->pic_state, i); } - lm32_uart_create(uart0_base, irq[uart0_irq], serial_hds[0]); + lm32_uart_create(uart0_base, irq[uart0_irq], serial_hd(0)); sysbus_create_simple("lm32-timer", timer0_base, irq[timer0_irq]); sysbus_create_simple("lm32-timer", timer1_base, irq[timer1_irq]); sysbus_create_simple("lm32-timer", timer2_base, irq[timer2_irq]); /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(serial_hds[1]); + env->juart_state = lm32_juart_init(serial_hd(1)); reset_info->bootstrap_pc = flash_base; diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index 85d64fe58d..321f184595 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qemu-common.h" #include "cpu.h" @@ -30,15 +31,13 @@ #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" -#include "sysemu/block-backend.h" #include "milkymist-hw.h" #include "lm32.h" #include "exec/address-spaces.h" -#include "qemu/cutils.h" #define BIOS_FILENAME "mmone-bios.bin" #define BIOS_OFFSET 0x00860000 -#define BIOS_SIZE (512*1024) +#define BIOS_SIZE (512 * KiB) #define KERNEL_LOAD_ADDR 0x40000000 typedef struct { @@ -97,10 +96,10 @@ milkymist_init(MachineState *machine) /* memory map */ hwaddr flash_base = 0x00000000; - size_t flash_sector_size = 128 * 1024; - size_t flash_size = 32 * 1024 * 1024; + size_t flash_sector_size = 128 * KiB; + size_t flash_size = 32 * MiB; hwaddr sdram_base = 0x40000000; - size_t sdram_size = 128 * 1024 * 1024; + size_t sdram_size = 128 * MiB; hwaddr initrd_base = sdram_base + 0x1002000; hwaddr cmdline_base = sdram_base + 0x1000000; @@ -151,7 +150,7 @@ milkymist_init(MachineState *machine) } g_free(bios_filename); - milkymist_uart_create(0x60000000, irq[0], serial_hds[0]); + milkymist_uart_create(0x60000000, irq[0], serial_hd(0)); milkymist_sysctl_create(0x60001000, irq[1], irq[2], irq[3], 80000000, 0x10014d31, 0x0000041f, 0x00000001); milkymist_hpdmc_create(0x60002000); @@ -167,7 +166,7 @@ milkymist_init(MachineState *machine) 0x20000000, 0x1000, 0x20020000, 0x2000); /* make sure juart isn't the first chardev */ - env->juart_state = lm32_juart_init(serial_hds[1]); + env->juart_state = lm32_juart_init(serial_hd(1)); if (kernel_filename) { uint64_t entry; diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index bd8e993c58..d7f26d6810 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -14,7 +14,6 @@ #include "qemu/timer.h" #include "hw/ptimer.h" #include "sysemu/sysemu.h" -#include "exec/address-spaces.h" /* General purpose timer module. */ typedef struct { @@ -513,19 +512,43 @@ static void m5206_mbar_writel(void *opaque, hwaddr offset, m5206_mbar_write(s, offset, value, 4); } +static uint64_t m5206_mbar_readfn(void *opaque, hwaddr addr, unsigned size) +{ + switch (size) { + case 1: + return m5206_mbar_readb(opaque, addr); + case 2: + return m5206_mbar_readw(opaque, addr); + case 4: + return m5206_mbar_readl(opaque, addr); + default: + g_assert_not_reached(); + } +} + +static void m5206_mbar_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + switch (size) { + case 1: + m5206_mbar_writeb(opaque, addr, value); + break; + case 2: + m5206_mbar_writew(opaque, addr, value); + break; + case 4: + m5206_mbar_writel(opaque, addr, value); + break; + default: + g_assert_not_reached(); + } +} + static const MemoryRegionOps m5206_mbar_ops = { - .old_mmio = { - .read = { - m5206_mbar_readb, - m5206_mbar_readw, - m5206_mbar_readl, - }, - .write = { - m5206_mbar_writeb, - m5206_mbar_writew, - m5206_mbar_writel, - }, - }, + .read = m5206_mbar_readfn, + .write = m5206_mbar_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -543,8 +566,8 @@ qemu_irq *mcf5206_init(MemoryRegion *sysmem, uint32_t base, M68kCPU *cpu) pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); s->timer[0] = m5206_timer_init(pic[9]); s->timer[1] = m5206_timer_init(pic[10]); - s->uart[0] = mcf_uart_init(pic[12], serial_hds[0]); - s->uart[1] = mcf_uart_init(pic[13], serial_hds[1]); + s->uart[0] = mcf_uart_init(pic[12], serial_hd(0)); + s->uart[1] = mcf_uart_init(pic[13], serial_hd(1)); s->cpu = cpu; m5206_mbar_reset(s); diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index fac0d09cbc..0f2245dd81 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -6,6 +6,7 @@ * This code is licensed under the GPL */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu-common.h" @@ -241,15 +242,15 @@ static void mcf5208evb_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0x40000000, ram); /* Internal SRAM. */ - memory_region_init_ram(sram, NULL, "mcf5208.sram", 16384, &error_fatal); + memory_region_init_ram(sram, NULL, "mcf5208.sram", 16 * KiB, &error_fatal); memory_region_add_subregion(address_space_mem, 0x80000000, sram); /* Internal peripherals. */ pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); - mcf_uart_mm_init(0xfc060000, pic[26], serial_hds[0]); - mcf_uart_mm_init(0xfc064000, pic[27], serial_hds[1]); - mcf_uart_mm_init(0xfc068000, pic[28], serial_hds[2]); + mcf_uart_mm_init(0xfc060000, pic[26], serial_hd(0)); + mcf_uart_mm_init(0xfc064000, pic[27], serial_hd(1)); + mcf_uart_mm_init(0xfc068000, pic[28], serial_hd(2)); mcf5208_sys_init(address_space_mem, pic); @@ -315,7 +316,7 @@ static void mcf5208evb_init(MachineState *machine) static void mcf5208evb_machine_init(MachineClass *mc) { - mc->desc = "MCF5206EVB"; + mc->desc = "MCF5208EVB"; mc->init = mcf5208evb_init; mc->is_default = 1; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m5208"); diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 8198afac1e..393ce284a2 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -11,7 +11,6 @@ #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/m68k/mcf.h" -#include "exec/address-spaces.h" #define TYPE_MCF_INTC "mcf-intc" #define MCF_INTC(obj) OBJECT_CHECK(mcf_intc_state, (obj), TYPE_MCF_INTC) diff --git a/hw/mem/Makefile.objs b/hw/mem/Makefile.objs index f12f8b97a2..10be4df2a2 100644 --- a/hw/mem/Makefile.objs +++ b/hw/mem/Makefile.objs @@ -1,2 +1,3 @@ common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o +common-obj-$(CONFIG_MEM_HOTPLUG) += memory-device.o common-obj-$(CONFIG_NVDIMM) += nvdimm.o diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c new file mode 100644 index 0000000000..6de4f70bb4 --- /dev/null +++ b/hw/mem/memory-device.c @@ -0,0 +1,281 @@ +/* + * Memory Device Interface + * + * Copyright ProfitBricks GmbH 2012 + * Copyright (C) 2014 Red Hat Inc + * Copyright (c) 2018 Red Hat Inc + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/mem/memory-device.h" +#include "hw/qdev.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "qemu/range.h" +#include "hw/virtio/vhost.h" +#include "sysemu/kvm.h" + +static gint memory_device_addr_sort(gconstpointer a, gconstpointer b) +{ + const MemoryDeviceState *md_a = MEMORY_DEVICE(a); + const MemoryDeviceState *md_b = MEMORY_DEVICE(b); + const MemoryDeviceClass *mdc_a = MEMORY_DEVICE_GET_CLASS(a); + const MemoryDeviceClass *mdc_b = MEMORY_DEVICE_GET_CLASS(b); + const uint64_t addr_a = mdc_a->get_addr(md_a); + const uint64_t addr_b = mdc_b->get_addr(md_b); + + if (addr_a > addr_b) { + return 1; + } else if (addr_a < addr_b) { + return -1; + } + return 0; +} + +static int memory_device_build_list(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { + DeviceState *dev = DEVICE(obj); + if (dev->realized) { /* only realized memory devices matter */ + *list = g_slist_insert_sorted(*list, dev, memory_device_addr_sort); + } + } + + object_child_foreach(obj, memory_device_build_list, opaque); + return 0; +} + +static int memory_device_used_region_size(Object *obj, void *opaque) +{ + uint64_t *size = opaque; + + if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { + const DeviceState *dev = DEVICE(obj); + const MemoryDeviceState *md = MEMORY_DEVICE(obj); + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); + + if (dev->realized) { + *size += mdc->get_region_size(md); + } + } + + object_child_foreach(obj, memory_device_used_region_size, opaque); + return 0; +} + +static void memory_device_check_addable(MachineState *ms, uint64_t size, + Error **errp) +{ + uint64_t used_region_size = 0; + + /* we will need a new memory slot for kvm and vhost */ + if (kvm_enabled() && !kvm_has_free_slot(ms)) { + error_setg(errp, "hypervisor has no free memory slots left"); + return; + } + if (!vhost_has_free_slot()) { + error_setg(errp, "a used vhost backend has no free memory slots left"); + return; + } + + /* will we exceed the total amount of memory specified */ + memory_device_used_region_size(OBJECT(ms), &used_region_size); + if (used_region_size + size > ms->maxram_size - ms->ram_size) { + error_setg(errp, "not enough space, currently 0x%" PRIx64 + " in use of total hot pluggable 0x" RAM_ADDR_FMT, + used_region_size, ms->maxram_size - ms->ram_size); + return; + } + +} + +uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, + uint64_t align, uint64_t size, + Error **errp) +{ + uint64_t address_space_start, address_space_end; + GSList *list = NULL, *item; + uint64_t new_addr = 0; + + if (!ms->device_memory) { + error_setg(errp, "memory devices (e.g. for memory hotplug) are not " + "supported by the machine"); + return 0; + } + + if (!memory_region_size(&ms->device_memory->mr)) { + error_setg(errp, "memory devices (e.g. for memory hotplug) are not " + "enabled, please specify the maxmem option"); + return 0; + } + address_space_start = ms->device_memory->base; + address_space_end = address_space_start + + memory_region_size(&ms->device_memory->mr); + g_assert(address_space_end >= address_space_start); + + /* address_space_start indicates the maximum alignment we expect */ + if (QEMU_ALIGN_UP(address_space_start, align) != address_space_start) { + error_setg(errp, "the alignment (0%" PRIx64 ") is not supported", + align); + return 0; + } + + memory_device_check_addable(ms, size, errp); + if (*errp) { + return 0; + } + + if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { + error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", + align); + return 0; + } + + if (QEMU_ALIGN_UP(size, align) != size) { + error_setg(errp, "backend memory size must be multiple of 0x%" + PRIx64, align); + return 0; + } + + if (hint) { + new_addr = *hint; + if (new_addr < address_space_start) { + error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 + "] at 0x%" PRIx64, new_addr, size, address_space_start); + return 0; + } else if ((new_addr + size) > address_space_end) { + error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 + "] beyond 0x%" PRIx64, new_addr, size, + address_space_end); + return 0; + } + } else { + new_addr = address_space_start; + } + + /* find address range that will fit new memory device */ + object_child_foreach(OBJECT(ms), memory_device_build_list, &list); + for (item = list; item; item = g_slist_next(item)) { + const MemoryDeviceState *md = item->data; + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(OBJECT(md)); + uint64_t md_size, md_addr; + + md_addr = mdc->get_addr(md); + md_size = mdc->get_region_size(md); + if (*errp) { + goto out; + } + + if (ranges_overlap(md_addr, md_size, new_addr, size)) { + if (hint) { + const DeviceState *d = DEVICE(md); + error_setg(errp, "address range conflicts with '%s'", d->id); + goto out; + } + new_addr = QEMU_ALIGN_UP(md_addr + md_size, align); + } + } + + if (new_addr + size > address_space_end) { + error_setg(errp, "could not find position in guest address space for " + "memory device - memory fragmented due to alignments"); + goto out; + } +out: + g_slist_free(list); + return new_addr; +} + +MemoryDeviceInfoList *qmp_memory_device_list(void) +{ + GSList *devices = NULL, *item; + MemoryDeviceInfoList *list = NULL, *prev = NULL; + + object_child_foreach(qdev_get_machine(), memory_device_build_list, + &devices); + + for (item = devices; item; item = g_slist_next(item)) { + const MemoryDeviceState *md = MEMORY_DEVICE(item->data); + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(item->data); + MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1); + MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1); + + mdc->fill_device_info(md, info); + + elem->value = info; + elem->next = NULL; + if (prev) { + prev->next = elem; + } else { + list = elem; + } + prev = elem; + } + + g_slist_free(devices); + + return list; +} + +static int memory_device_plugged_size(Object *obj, void *opaque) +{ + uint64_t *size = opaque; + + if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { + const DeviceState *dev = DEVICE(obj); + const MemoryDeviceState *md = MEMORY_DEVICE(obj); + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); + + if (dev->realized) { + *size += mdc->get_plugged_size(md); + } + } + + object_child_foreach(obj, memory_device_plugged_size, opaque); + return 0; +} + +uint64_t get_plugged_memory_size(void) +{ + uint64_t size = 0; + + memory_device_plugged_size(qdev_get_machine(), &size); + + return size; +} + +void memory_device_plug_region(MachineState *ms, MemoryRegion *mr, + uint64_t addr) +{ + /* we expect a previous call to memory_device_get_free_addr() */ + g_assert(ms->device_memory); + + memory_region_add_subregion(&ms->device_memory->mr, + addr - ms->device_memory->base, mr); +} + +void memory_device_unplug_region(MachineState *ms, MemoryRegion *mr) +{ + /* we expect a previous call to memory_device_get_free_addr() */ + g_assert(ms->device_memory); + + memory_region_del_subregion(&ms->device_memory->mr, mr); +} + +static const TypeInfo memory_device_info = { + .name = TYPE_MEMORY_DEVICE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(MemoryDeviceClass), +}; + +static void memory_device_register_types(void) +{ + type_register_static(&memory_device_info); +} + +type_init(memory_device_register_types) diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index acb656b672..021d1c3997 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -43,7 +43,7 @@ static void nvdimm_set_label_size(Object *obj, Visitor *v, const char *name, Error *local_err = NULL; uint64_t value; - if (memory_region_size(&nvdimm->nvdimm_mr)) { + if (nvdimm->nvdimm_mr) { error_setg(&local_err, "cannot change property value"); goto out; } @@ -64,52 +64,36 @@ out: error_propagate(errp, local_err); } -static bool nvdimm_get_unarmed(Object *obj, Error **errp) -{ - NVDIMMDevice *nvdimm = NVDIMM(obj); - - return nvdimm->unarmed; -} - -static void nvdimm_set_unarmed(Object *obj, bool value, Error **errp) -{ - NVDIMMDevice *nvdimm = NVDIMM(obj); - Error *local_err = NULL; - - if (memory_region_size(&nvdimm->nvdimm_mr)) { - error_setg(&local_err, "cannot change property value"); - goto out; - } - - nvdimm->unarmed = value; - - out: - error_propagate(errp, local_err); -} - static void nvdimm_init(Object *obj) { - object_property_add(obj, NVDIMM_LABLE_SIZE_PROP, "int", + object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "int", nvdimm_get_label_size, nvdimm_set_label_size, NULL, NULL, NULL); - object_property_add_bool(obj, NVDIMM_UNARMED_PROP, - nvdimm_get_unarmed, nvdimm_set_unarmed, NULL); } -static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) +static void nvdimm_finalize(Object *obj) { - NVDIMMDevice *nvdimm = NVDIMM(dimm); + NVDIMMDevice *nvdimm = NVDIMM(obj); - return &nvdimm->nvdimm_mr; + g_free(nvdimm->nvdimm_mr); } -static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) +static void nvdimm_prepare_memory_region(NVDIMMDevice *nvdimm, Error **errp) { - MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem, errp); - NVDIMMDevice *nvdimm = NVDIMM(dimm); - uint64_t align, pmem_size, size = memory_region_size(mr); + PCDIMMDevice *dimm = PC_DIMM(nvdimm); + uint64_t align, pmem_size, size; + MemoryRegion *mr; + g_assert(!nvdimm->nvdimm_mr); + + if (!dimm->hostmem) { + error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set"); + return; + } + + mr = host_memory_backend_get_memory(dimm->hostmem); align = memory_region_get_alignment(mr); + size = memory_region_size(mr); pmem_size = size - nvdimm->label_size; nvdimm->label_data = memory_region_get_ram_ptr(mr) + pmem_size; @@ -127,9 +111,34 @@ static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) return; } - memory_region_init_alias(&nvdimm->nvdimm_mr, OBJECT(dimm), + nvdimm->nvdimm_mr = g_new(MemoryRegion, 1); + memory_region_init_alias(nvdimm->nvdimm_mr, OBJECT(dimm), "nvdimm-memory", mr, 0, pmem_size); - nvdimm->nvdimm_mr.align = align; + nvdimm->nvdimm_mr->align = align; +} + +static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) +{ + NVDIMMDevice *nvdimm = NVDIMM(dimm); + Error *local_err = NULL; + + if (!nvdimm->nvdimm_mr) { + nvdimm_prepare_memory_region(nvdimm, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return NULL; + } + } + return nvdimm->nvdimm_mr; +} + +static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) +{ + NVDIMMDevice *nvdimm = NVDIMM(dimm); + + if (!nvdimm->nvdimm_mr) { + nvdimm_prepare_memory_region(nvdimm, errp); + } } /* @@ -161,24 +170,25 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, memcpy(nvdimm->label_data + offset, buf, size); - mr = host_memory_backend_get_memory(dimm->hostmem, &error_abort); + mr = host_memory_backend_get_memory(dimm->hostmem); backend_offset = memory_region_size(mr) - nvdimm->label_size + offset; memory_region_set_dirty(mr, backend_offset, size); } -static MemoryRegion *nvdimm_get_vmstate_memory_region(PCDIMMDevice *dimm) -{ - return host_memory_backend_get_memory(dimm->hostmem, &error_abort); -} +static Property nvdimm_properties[] = { + DEFINE_PROP_BOOL(NVDIMM_UNARMED_PROP, NVDIMMDevice, unarmed, false), + DEFINE_PROP_END_OF_LIST(), +}; static void nvdimm_class_init(ObjectClass *oc, void *data) { PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); NVDIMMClass *nvc = NVDIMM_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); ddc->realize = nvdimm_realize; ddc->get_memory_region = nvdimm_get_memory_region; - ddc->get_vmstate_memory_region = nvdimm_get_vmstate_memory_region; + dc->props = nvdimm_properties; nvc->read_label_data = nvdimm_read_label_data; nvc->write_label_data = nvdimm_write_label_data; @@ -191,6 +201,7 @@ static TypeInfo nvdimm_info = { .class_init = nvdimm_class_init, .instance_size = sizeof(NVDIMMDevice), .instance_init = nvdimm_init, + .instance_finalize = nvdimm_finalize, }; static void nvdimm_register_types(void) diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 51350d9c2d..65843bc52a 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -21,30 +21,24 @@ #include "qemu/osdep.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" +#include "hw/mem/memory-device.h" #include "qapi/error.h" -#include "qemu/config-file.h" #include "qapi/visitor.h" -#include "qemu/range.h" #include "sysemu/numa.h" -#include "sysemu/kvm.h" #include "trace.h" -#include "hw/virtio/vhost.h" -typedef struct pc_dimms_capacity { - uint64_t size; - Error **errp; -} pc_dimms_capacity; +static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); -void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr, uint64_t align, Error **errp) +void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, + Error **errp) { int slot; - MachineState *machine = MACHINE(qdev_get_machine()); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); + MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, + &error_abort); + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); Error *local_err = NULL; - uint64_t existing_dimms_capacity = 0; uint64_t addr; addr = object_property_get_uint(OBJECT(dimm), @@ -53,28 +47,12 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, goto out; } - addr = pc_dimm_get_free_addr(hpms->base, - memory_region_size(&hpms->mr), - !addr ? NULL : &addr, align, - memory_region_size(mr), &local_err); + addr = memory_device_get_free_addr(machine, !addr ? NULL : &addr, align, + memory_region_size(mr), &local_err); if (local_err) { goto out; } - existing_dimms_capacity = pc_existing_dimms_capacity(&local_err); - if (local_err) { - goto out; - } - - if (existing_dimms_capacity + memory_region_size(mr) > - machine->maxram_size - machine->ram_size) { - error_setg(&local_err, "not enough space, currently 0x%" PRIx64 - " in use of total hot pluggable 0x" RAM_ADDR_FMT, - existing_dimms_capacity, - machine->maxram_size - machine->ram_size); - goto out; - } - object_property_set_uint(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); if (local_err) { goto out; @@ -97,72 +75,25 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, } trace_mhp_pc_dimm_assigned_slot(slot); - if (kvm_enabled() && !kvm_has_free_slot(machine)) { - error_setg(&local_err, "hypervisor has no free memory slots left"); - goto out; - } - - if (!vhost_has_free_slot()) { - error_setg(&local_err, "a used vhost backend has no free" - " memory slots left"); - goto out; - } - - memory_region_add_subregion(&hpms->mr, addr - hpms->base, mr); + memory_device_plug_region(machine, mr, addr); vmstate_register_ram(vmstate_mr, dev); out: error_propagate(errp, local_err); } -void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr) +void pc_dimm_unplug(DeviceState *dev, MachineState *machine) { PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); + MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, + &error_abort); + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); - memory_region_del_subregion(&hpms->mr, mr); + memory_device_unplug_region(machine, mr); vmstate_unregister_ram(vmstate_mr, dev); } -static int pc_existing_dimms_capacity_internal(Object *obj, void *opaque) -{ - pc_dimms_capacity *cap = opaque; - uint64_t *size = &cap->size; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - DeviceState *dev = DEVICE(obj); - - if (dev->realized) { - (*size) += object_property_get_uint(obj, PC_DIMM_SIZE_PROP, - cap->errp); - } - - if (cap->errp && *cap->errp) { - return 1; - } - } - object_child_foreach(obj, pc_existing_dimms_capacity_internal, opaque); - return 0; -} - -uint64_t pc_existing_dimms_capacity(Error **errp) -{ - pc_dimms_capacity cap; - - cap.size = 0; - cap.errp = errp; - - pc_existing_dimms_capacity_internal(qdev_get_machine(), &cap); - return cap.size; -} - -uint64_t get_plugged_memory_size(void) -{ - return pc_existing_dimms_capacity(&error_abort); -} - static int pc_dimm_slot2bitmap(Object *obj, void *opaque) { unsigned long *bitmap = opaque; @@ -179,11 +110,18 @@ static int pc_dimm_slot2bitmap(Object *obj, void *opaque) return 0; } -int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp) +static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp) { - unsigned long *bitmap = bitmap_new(max_slots); + unsigned long *bitmap; int slot = 0; + if (max_slots <= 0) { + error_setg(errp, "no slots where allocated, please specify " + "the 'slots' option"); + return slot; + } + + bitmap = bitmap_new(max_slots); object_child_foreach(qdev_get_machine(), pc_dimm_slot2bitmap, bitmap); /* check if requested slot is not occupied */ @@ -209,158 +147,6 @@ out: return slot; } -static gint pc_dimm_addr_sort(gconstpointer a, gconstpointer b) -{ - PCDIMMDevice *x = PC_DIMM(a); - PCDIMMDevice *y = PC_DIMM(b); - Int128 diff = int128_sub(int128_make64(x->addr), int128_make64(y->addr)); - - if (int128_lt(diff, int128_zero())) { - return -1; - } else if (int128_gt(diff, int128_zero())) { - return 1; - } - return 0; -} - -static int pc_dimm_built_list(Object *obj, void *opaque) -{ - GSList **list = opaque; - - if (object_dynamic_cast(obj, TYPE_PC_DIMM)) { - DeviceState *dev = DEVICE(obj); - if (dev->realized) { /* only realized DIMMs matter */ - *list = g_slist_insert_sorted(*list, dev, pc_dimm_addr_sort); - } - } - - object_child_foreach(obj, pc_dimm_built_list, opaque); - return 0; -} - -MemoryDeviceInfoList *qmp_pc_dimm_device_list(void) -{ - GSList *dimms = NULL, *item; - MemoryDeviceInfoList *list = NULL, *prev = NULL; - - object_child_foreach(qdev_get_machine(), pc_dimm_built_list, &dimms); - - for (item = dimms; item; item = g_slist_next(item)) { - PCDIMMDevice *dimm = PC_DIMM(item->data); - Object *obj = OBJECT(dimm); - MemoryDeviceInfoList *elem = g_new0(MemoryDeviceInfoList, 1); - MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1); - PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1); - bool is_nvdimm = object_dynamic_cast(obj, TYPE_NVDIMM); - DeviceClass *dc = DEVICE_GET_CLASS(obj); - DeviceState *dev = DEVICE(obj); - - if (dev->id) { - di->has_id = true; - di->id = g_strdup(dev->id); - } - di->hotplugged = dev->hotplugged; - di->hotpluggable = dc->hotpluggable; - di->addr = dimm->addr; - di->slot = dimm->slot; - di->node = dimm->node; - di->size = object_property_get_uint(obj, PC_DIMM_SIZE_PROP, NULL); - di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem)); - - if (!is_nvdimm) { - info->u.dimm.data = di; - info->type = MEMORY_DEVICE_INFO_KIND_DIMM; - } else { - info->u.nvdimm.data = di; - info->type = MEMORY_DEVICE_INFO_KIND_NVDIMM; - } - elem->value = info; - elem->next = NULL; - if (prev) { - prev->next = elem; - } else { - list = elem; - } - prev = elem; - } - - g_slist_free(dimms); - - return list; -} - -uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, - uint64_t address_space_size, - uint64_t *hint, uint64_t align, uint64_t size, - Error **errp) -{ - GSList *list = NULL, *item; - uint64_t new_addr, ret = 0; - uint64_t address_space_end = address_space_start + address_space_size; - - g_assert(QEMU_ALIGN_UP(address_space_start, align) == address_space_start); - - if (!address_space_size) { - error_setg(errp, "memory hotplug is not enabled, " - "please add maxmem option"); - goto out; - } - - if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { - error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", - align); - goto out; - } - - if (QEMU_ALIGN_UP(size, align) != size) { - error_setg(errp, "backend memory size must be multiple of 0x%" - PRIx64, align); - goto out; - } - - assert(address_space_end > address_space_start); - object_child_foreach(qdev_get_machine(), pc_dimm_built_list, &list); - - if (hint) { - new_addr = *hint; - } else { - new_addr = address_space_start; - } - - /* find address range that will fit new DIMM */ - for (item = list; item; item = g_slist_next(item)) { - PCDIMMDevice *dimm = item->data; - uint64_t dimm_size = object_property_get_uint(OBJECT(dimm), - PC_DIMM_SIZE_PROP, - errp); - if (errp && *errp) { - goto out; - } - - if (ranges_overlap(dimm->addr, dimm_size, new_addr, size)) { - if (hint) { - DeviceState *d = DEVICE(dimm); - error_setg(errp, "address range conflicts with '%s'", d->id); - goto out; - } - new_addr = QEMU_ALIGN_UP(dimm->addr + dimm_size, align); - } - } - ret = new_addr; - - if (new_addr < address_space_start) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] at 0x%" PRIx64, new_addr, size, address_space_start); - } else if ((new_addr + size) > address_space_end) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] beyond 0x%" PRIx64, new_addr, size, address_space_end); - } - -out: - g_slist_free(list); - return ret; -} - static Property pc_dimm_properties[] = { DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0), DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0), @@ -437,18 +223,66 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) return NULL; } - return host_memory_backend_get_memory(dimm->hostmem, errp); + return host_memory_backend_get_memory(dimm->hostmem); } -static MemoryRegion *pc_dimm_get_vmstate_memory_region(PCDIMMDevice *dimm) +static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md) { - return host_memory_backend_get_memory(dimm->hostmem, &error_abort); + const PCDIMMDevice *dimm = PC_DIMM(md); + + return dimm->addr; +} + +static uint64_t pc_dimm_md_get_region_size(const MemoryDeviceState *md) +{ + /* dropping const here is fine as we don't touch the memory region */ + PCDIMMDevice *dimm = PC_DIMM(md); + const PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(md); + MemoryRegion *mr; + + mr = ddc->get_memory_region(dimm, &error_abort); + if (!mr) { + return 0; + } + + return memory_region_size(mr); +} + +static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md, + MemoryDeviceInfo *info) +{ + PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1); + const DeviceClass *dc = DEVICE_GET_CLASS(md); + const PCDIMMDevice *dimm = PC_DIMM(md); + const DeviceState *dev = DEVICE(md); + + if (dev->id) { + di->has_id = true; + di->id = g_strdup(dev->id); + } + di->hotplugged = dev->hotplugged; + di->hotpluggable = dc->hotpluggable; + di->addr = dimm->addr; + di->slot = dimm->slot; + di->node = dimm->node; + di->size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP, + NULL); + di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem)); + + if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) { + info->u.nvdimm.data = di; + info->type = MEMORY_DEVICE_INFO_KIND_NVDIMM; + } else { + info->u.dimm.data = di; + info->type = MEMORY_DEVICE_INFO_KIND_DIMM; + } } static void pc_dimm_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); + MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); dc->realize = pc_dimm_realize; dc->unrealize = pc_dimm_unrealize; @@ -456,7 +290,13 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data) dc->desc = "DIMM memory module"; ddc->get_memory_region = pc_dimm_get_memory_region; - ddc->get_vmstate_memory_region = pc_dimm_get_vmstate_memory_region; + ddc->get_vmstate_memory_region = pc_dimm_get_memory_region; + + mdc->get_addr = pc_dimm_md_get_addr; + /* for a dimm plugged_size == region_size */ + mdc->get_plugged_size = pc_dimm_md_get_region_size; + mdc->get_region_size = pc_dimm_md_get_region_size; + mdc->fill_device_info = pc_dimm_md_fill_device_info; } static TypeInfo pc_dimm_info = { @@ -466,6 +306,10 @@ static TypeInfo pc_dimm_info = { .instance_init = pc_dimm_init, .class_init = pc_dimm_class_init, .class_size = sizeof(PCDIMMDeviceClass), + .interfaces = (InterfaceInfo[]) { + { TYPE_MEMORY_DEVICE }, + { } + }, }; static void pc_dimm_register_types(void) diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index b664dc0f9c..c730878d25 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -36,7 +37,6 @@ #include "sysemu/sysemu.h" #include "hw/devices.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "hw/char/serial.h" #include "exec/address-spaces.h" #include "hw/ssi/ssi.h" @@ -45,8 +45,8 @@ #include "hw/stream.h" -#define LMB_BRAM_SIZE (128 * 1024) -#define FLASH_SIZE (32 * 1024 * 1024) +#define LMB_BRAM_SIZE (128 * KiB) +#define FLASH_SIZE (32 * MiB) #define BINARY_DEVICE_TREE_FILE "petalogix-ml605.dtb" @@ -110,7 +110,7 @@ petalogix_ml605_init(MachineState *machine) pflash_cfi01_register(FLASH_BASEADDR, NULL, "petalogix_ml605.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), FLASH_SIZE >> 16, + 64 * KiB, FLASH_SIZE >> 16, 2, 0x89, 0x18, 0x0000, 0x0, 0); @@ -125,7 +125,7 @@ petalogix_ml605_init(MachineState *machine) } serial_mm_init(address_space_mem, UART16550_BASEADDR + 0x1000, 2, - irq[UART16550_IRQ], 115200, serial_hds[0], + irq[UART16550_IRQ], 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); /* 2 timers at irq 2 @ 100 Mhz. */ diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 5cb4deb69e..5cf7b84c79 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -34,14 +35,13 @@ #include "sysemu/sysemu.h" #include "hw/devices.h" #include "hw/boards.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "hw/char/xilinx_uartlite.h" #include "boot.h" -#define LMB_BRAM_SIZE (128 * 1024) -#define FLASH_SIZE (16 * 1024 * 1024) +#define LMB_BRAM_SIZE (128 * KiB) +#define FLASH_SIZE (16 * MiB) #define BINARY_DEVICE_TREE_FILE "petalogix-s3adsp1800.dtb" @@ -88,7 +88,7 @@ petalogix_s3adsp1800_init(MachineState *machine) pflash_cfi01_register(FLASH_BASEADDR, NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), FLASH_SIZE >> 16, + 64 * KiB, FLASH_SIZE >> 16, 1, 0x89, 0x18, 0x0000, 0x0, 1); dev = qdev_create(NULL, "xlnx.xps-intc"); @@ -103,7 +103,7 @@ petalogix_s3adsp1800_init(MachineState *machine) } xilinx_uartlite_create(UARTLITE_BASEADDR, irq[UARTLITE_IRQ], - serial_hds[0]); + serial_hd(0)); /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_create(NULL, "xlnx.xps-timer"); diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index 999a5657cf..57dc1ccd42 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -62,13 +62,11 @@ static void xlnx_zynqmp_pmu_soc_init(Object *obj) { XlnxZynqMPPMUSoCState *s = XLNX_ZYNQMP_PMU_SOC(obj); - object_initialize(&s->cpu, sizeof(s->cpu), - TYPE_MICROBLAZE_CPU); - object_property_add_child(obj, "pmu-cpu", OBJECT(&s->cpu), - &error_abort); + object_initialize_child(obj, "pmu-cpu", &s->cpu, sizeof(s->cpu), + TYPE_MICROBLAZE_CPU, &error_abort, NULL); - object_initialize(&s->intc, sizeof(s->intc), TYPE_XLNX_PMU_IO_INTC); - qdev_set_parent_bus(DEVICE(&s->intc), sysbus_get_default()); + sysbus_init_child_obj(obj, "intc", &s->intc, sizeof(s->intc), + TYPE_XLNX_PMU_IO_INTC); } static void xlnx_zynqmp_pmu_soc_realize(DeviceState *dev, Error **errp) diff --git a/hw/mips/boston.c b/hw/mips/boston.c index fb23161b33..6c9c20a93e 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "exec/address-spaces.h" @@ -32,7 +33,6 @@ #include "hw/mips/cpudevs.h" #include "hw/pci-host/xilinx-pcie.h" #include "qapi/error.h" -#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "chardev/char.h" @@ -176,7 +176,7 @@ static uint64_t boston_platreg_read(void *opaque, hwaddr addr, uint32_t gic_freq, val; if (size != 4) { - qemu_log_mask(LOG_UNIMP, "%uB platform register read", size); + qemu_log_mask(LOG_UNIMP, "%uB platform register read\n", size); return 0; } @@ -200,12 +200,12 @@ static uint64_t boston_platreg_read(void *opaque, hwaddr addr, val |= PLAT_BUILD_CFG_PCIE2_EN; return val; case PLAT_DDR_CFG: - val = s->mach->ram_size / G_BYTE; + val = s->mach->ram_size / GiB; assert(!(val & ~PLAT_DDR_CFG_SIZE)); val |= PLAT_DDR_CFG_MHZ; return val; default: - qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx, + qemu_log_mask(LOG_UNIMP, "Read platform register 0x%" HWADDR_PRIx "\n", addr & 0xffff); return 0; } @@ -215,7 +215,7 @@ static void boston_platreg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { if (size != 4) { - qemu_log_mask(LOG_UNIMP, "%uB platform register write", size); + qemu_log_mask(LOG_UNIMP, "%uB platform register write\n", size); return; } @@ -237,7 +237,7 @@ static void boston_platreg_write(void *opaque, hwaddr addr, break; default: qemu_log_mask(LOG_UNIMP, "Write platform register 0x%" HWADDR_PRIx - " = 0x%" PRIx64, addr & 0xffff, val); + " = 0x%" PRIx64 "\n", addr & 0xffff, val); break; } } @@ -355,7 +355,7 @@ static const void *boston_fdt_filter(void *opaque, const void *fdt_orig, return NULL; } - ram_low_sz = MIN(256 * M_BYTE, machine->ram_size); + ram_low_sz = MIN(256 * MiB, machine->ram_size); ram_high_sz = machine->ram_size - ram_low_sz; qemu_fdt_setprop_sized_cells(fdt, "/memory@0", "reg", 1, 0x00000000, 1, ram_low_sz, @@ -436,8 +436,8 @@ static void boston_mach_init(MachineState *machine) int fw_size, fit_err; bool is_64b; - if ((machine->ram_size % G_BYTE) || - (machine->ram_size > (2 * G_BYTE))) { + if ((machine->ram_size % GiB) || + (machine->ram_size > (2 * GiB))) { error_report("Memory size must be 1GB or 2GB"); exit(1); } @@ -471,8 +471,7 @@ static void boston_mach_init(MachineState *machine) sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->cps), 0, 0, 1); flash = g_new(MemoryRegion, 1); - memory_region_init_rom_nomigrate(flash, NULL, - "boston.flash", 128 * M_BYTE, &err); + memory_region_init_rom(flash, NULL, "boston.flash", 128 * MiB, &err); memory_region_add_subregion_overlap(sys_mem, 0x18000000, flash, 0); ddr = g_new(MemoryRegion, 1); @@ -482,22 +481,22 @@ static void boston_mach_init(MachineState *machine) ddr_low_alias = g_new(MemoryRegion, 1); memory_region_init_alias(ddr_low_alias, NULL, "boston_low.ddr", - ddr, 0, MIN(machine->ram_size, (256 * M_BYTE))); + ddr, 0, MIN(machine->ram_size, (256 * MiB))); memory_region_add_subregion_overlap(sys_mem, 0, ddr_low_alias, 0); xilinx_pcie_init(sys_mem, 0, - 0x10000000, 32 * M_BYTE, - 0x40000000, 1 * G_BYTE, + 0x10000000, 32 * MiB, + 0x40000000, 1 * GiB, get_cps_irq(s->cps, 2), false); xilinx_pcie_init(sys_mem, 1, - 0x12000000, 32 * M_BYTE, - 0x20000000, 512 * M_BYTE, + 0x12000000, 32 * MiB, + 0x20000000, 512 * MiB, get_cps_irq(s->cps, 1), false); pcie2 = xilinx_pcie_init(sys_mem, 2, - 0x14000000, 32 * M_BYTE, - 0x16000000, 1 * M_BYTE, + 0x14000000, 32 * MiB, + 0x16000000, 1 * MiB, get_cps_irq(s->cps, 0), true); platreg = g_new(MemoryRegion, 1); @@ -505,13 +504,9 @@ static void boston_mach_init(MachineState *machine) "boston-platregs", 0x1000); memory_region_add_subregion_overlap(sys_mem, 0x17ffd000, platreg, 0); - if (!serial_hds[0]) { - serial_hds[0] = qemu_chr_new("serial0", "null"); - } - s->uart = serial_mm_init(sys_mem, 0x17ffe000, 2, get_cps_irq(s->cps, 3), 10000000, - serial_hds[0], DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_NATIVE_ENDIAN); lcd = g_new(MemoryRegion, 1); memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); @@ -531,7 +526,7 @@ static void boston_mach_init(MachineState *machine) if (machine->firmware) { fw_size = load_image_targphys(machine->firmware, - 0x1fc00000, 4 * M_BYTE); + 0x1fc00000, 4 * MiB); if (fw_size == -1) { error_printf("unable to load firmware image '%s'\n", machine->firmware); @@ -557,7 +552,7 @@ static void boston_mach_class_init(MachineClass *mc) mc->desc = "MIPS Boston"; mc->init = boston_mach_init; mc->block_default_type = IF_IDE; - mc->default_ram_size = 1 * G_BYTE; + mc->default_ram_size = 1 * GiB; mc->max_cpus = 16; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("I6400"); } diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index 02fb2fdcc4..c1694c8254 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/i386/pc.h" @@ -159,7 +160,7 @@ static int64_t load_kernel (CPUMIPSState *env) /* Setup minimum environment variables */ prom_set(prom_buf, index++, "busclock=33000000"); prom_set(prom_buf, index++, "cpuclock=100000000"); - prom_set(prom_buf, index++, "memsize=%i", loaderparams.ram_size/1024/1024); + prom_set(prom_buf, index++, "memsize=%"PRIi64, loaderparams.ram_size / MiB); prom_set(prom_buf, index++, "modetty0=38400n8r"); prom_set(prom_buf, index++, NULL); @@ -303,10 +304,10 @@ static void mips_fulong2e_init(MachineState *machine) qemu_register_reset(main_cpu_reset, cpu); /* fulong 2e has 256M ram. */ - ram_size = 256 * 1024 * 1024; + ram_size = 256 * MiB; /* fulong 2e has a 1M flash.Winbond W39L040AP70Z */ - bios_size = 1024 * 1024; + bios_size = 1 * MiB; /* allocate RAM */ memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size); diff --git a/hw/mips/mips_jazz.c b/hw/mips/mips_jazz.c index 7223085547..1afbe3ce6a 100644 --- a/hw/mips/mips_jazz.c +++ b/hw/mips/mips_jazz.c @@ -145,10 +145,10 @@ static void mips_jazz_init(MachineState *machine, ISABus *isa_bus; ISADevice *pit; DriveInfo *fds[MAX_FD]; - qemu_irq esp_reset, dma_enable; MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); MemoryRegion *bios2 = g_new(MemoryRegion, 1); + SysBusESPState *sysbus_esp; ESPState *esp; /* init CPUs */ @@ -281,8 +281,21 @@ static void mips_jazz_init(MachineState *machine, } /* SCSI adapter */ - esp = esp_init(0x80002000, 0, rc4030_dma_read, rc4030_dma_write, dmas[0], - qdev_get_gpio_in(rc4030, 5), &esp_reset, &dma_enable); + dev = qdev_create(NULL, TYPE_ESP); + sysbus_esp = ESP_STATE(dev); + esp = &sysbus_esp->esp; + esp->dma_memory_read = rc4030_dma_read; + esp->dma_memory_write = rc4030_dma_write; + esp->dma_opaque = dmas[0]; + sysbus_esp->it_shift = 0; + /* XXX for now until rc4030 has been changed to use DMA enable signal */ + esp->dma_enabled = 1; + qdev_init_nofail(dev); + + sysbus = SYS_BUS_DEVICE(dev); + sysbus_connect_irq(sysbus, 0, qdev_get_gpio_in(rc4030, 5)); + sysbus_mmio_map(sysbus, 0, 0x80002000); + scsi_bus_legacy_handle_cmdline(&esp->bus); /* Floppy */ @@ -303,15 +316,15 @@ static void mips_jazz_init(MachineState *machine, memory_region_add_subregion(address_space, 0x80005000, i8042); /* Serial ports */ - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(address_space, 0x80006000, 0, qdev_get_gpio_in(rc4030, 8), 8000000/16, - serial_hds[0], DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_NATIVE_ENDIAN); } - if (serial_hds[1]) { + if (serial_hd(1)) { serial_mm_init(address_space, 0x80007000, 0, qdev_get_gpio_in(rc4030, 9), 8000000/16, - serial_hds[1], DEVICE_NATIVE_ENDIAN); + serial_hd(1), DEVICE_NATIVE_ENDIAN); } /* Parallel port */ diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index f6513a4fd5..3467451482 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "cpu.h" #include "hw/hw.h" @@ -46,7 +47,6 @@ #include "elf.h" #include "hw/timer/mc146818rtc.h" #include "hw/timer/i8254.h" -#include "sysemu/blockdev.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" /* SysBusDevice */ #include "qemu/host-utils.h" @@ -192,7 +192,7 @@ static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size) int i; /* work in terms of MB */ - ram_size >>= 20; + ram_size /= MiB; while ((ram_size >= 4) && (nbanks <= 2)) { int sz_log2 = MIN(31 - clz32(ram_size), 14); @@ -844,7 +844,8 @@ static int64_t load_kernel (void) /* The kernel allocates the bootmap memory in the low memory after the initrd. It takes at most 128kiB for 2GB RAM and 4kiB pages. */ - initrd_offset = (loaderparams.ram_low_size - initrd_size - 131072 + initrd_offset = (loaderparams.ram_low_size - initrd_size + - (128 * KiB) - ~INITRD_PAGE_MASK) & INITRD_PAGE_MASK; if (kernel_high >= initrd_offset) { error_report("memory too small for initial ram disk '%s'", @@ -1022,9 +1023,9 @@ void mips_malta_init(MachineState *machine) mips_create_cpu(s, machine->cpu_type, &cbus_irq, &i8259_irq); /* allocate RAM */ - if (ram_size > (2048u << 20)) { - error_report("Too much memory for this machine: %dMB, maximum 2048MB", - ((unsigned int)ram_size / (1 << 20))); + if (ram_size > 2 * GiB) { + error_report("Too much memory for this machine: %" PRId64 "MB," + " maximum 2048MB", ram_size / MiB); exit(1); } @@ -1035,17 +1036,18 @@ void mips_malta_init(MachineState *machine) /* alias for pre IO hole access */ memory_region_init_alias(ram_low_preio, NULL, "mips_malta_low_preio.ram", - ram_high, 0, MIN(ram_size, (256 << 20))); + ram_high, 0, MIN(ram_size, 256 * MiB)); memory_region_add_subregion(system_memory, 0, ram_low_preio); /* alias for post IO hole access, if there is enough RAM */ - if (ram_size > (512 << 20)) { + if (ram_size > 512 * MiB) { ram_low_postio = g_new(MemoryRegion, 1); memory_region_init_alias(ram_low_postio, NULL, "mips_malta_low_postio.ram", - ram_high, 512 << 20, - ram_size - (512 << 20)); - memory_region_add_subregion(system_memory, 512 << 20, ram_low_postio); + ram_high, 512 * MiB, + ram_size - 512 * MiB); + memory_region_add_subregion(system_memory, 512 * MiB, + ram_low_postio); } #ifdef TARGET_WORDS_BIGENDIAN @@ -1056,13 +1058,8 @@ void mips_malta_init(MachineState *machine) /* FPGA */ - /* Make sure the second serial port is associated with a device. */ - if (!serial_hds[2]) { - serial_hds[2] = qemu_chr_new("fpga-uart", "null"); - } - /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */ - malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hds[2]); + malta_fpga_init(system_memory, FPGA_ADDRESS, cbus_irq, serial_hd(2)); /* Load firmware in flash / BIOS. */ dinfo = drive_get(IF_PFLASH, 0, fl_idx); @@ -1082,7 +1079,7 @@ void mips_malta_init(MachineState *machine) bios = pflash_cfi01_get_memory(fl); fl_idx++; if (kernel_filename) { - ram_low_size = MIN(ram_size, 256 << 20); + ram_low_size = MIN(ram_size, 256 * MiB); /* For KVM we reserve 1MB of RAM for running bootloader */ if (kvm_enabled()) { ram_low_size -= 0x100000; @@ -1139,11 +1136,13 @@ void mips_malta_init(MachineState *machine) a neat trick which allows bi-endian firmware. */ #ifndef TARGET_WORDS_BIGENDIAN { - uint32_t *end, *addr = rom_ptr(FLASH_ADDRESS); + uint32_t *end, *addr; + const size_t swapsize = MIN(bios_size, 0x3e0000); + addr = rom_ptr(FLASH_ADDRESS, swapsize); if (!addr) { addr = memory_region_get_ram_ptr(bios); } - end = (void *)addr + MIN(bios_size, 0x3e0000); + end = (void *)addr + swapsize; while (addr < end) { bswap32s(addr); addr++; @@ -1158,7 +1157,7 @@ void mips_malta_init(MachineState *machine) * handled by an overlapping region as the resulting ROM code subpage * regions are not executable. */ - memory_region_init_ram_nomigrate(bios_copy, NULL, "bios.1fc", BIOS_SIZE, + memory_region_init_ram(bios_copy, NULL, "bios.1fc", BIOS_SIZE, &error_fatal); if (!rom_copy(memory_region_get_ram_ptr(bios_copy), FLASH_ADDRESS, BIOS_SIZE)) { diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c index e0ba5efc84..241faa1d0f 100644 --- a/hw/mips/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -213,8 +213,8 @@ mips_mipssim_init(MachineState *machine) /* A single 16450 sits at offset 0x3f8. It is attached to MIPS CPU INT2, which is interrupt 4. */ - if (serial_hds[0]) - serial_init(0x3f8, env->irq[4], 115200, serial_hds[0], + if (serial_hd(0)) + serial_init(0x3f8, env->irq[4], 115200, serial_hd(0), get_system_io()); if (nd_table[0].used) diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index aeadc4a340..d5725d0555 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -8,6 +8,7 @@ * the standard PC ISA addresses. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -30,7 +31,6 @@ #include "hw/timer/mc146818rtc.h" #include "hw/input/i8042.h" #include "hw/timer/i8254.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "sysemu/qtest.h" #include "qemu/error-report.h" @@ -80,8 +80,9 @@ typedef struct ResetData { static int64_t load_kernel(void) { + const size_t params_size = 264; int64_t entry, kernel_high; - long kernel_size, initrd_size, params_size; + long kernel_size, initrd_size; ram_addr_t initrd_offset; uint32_t *params_buf; int big_endian; @@ -129,7 +130,6 @@ static int64_t load_kernel(void) } /* Store command line. */ - params_size = 264; params_buf = g_malloc(params_size); params_buf[0] = tswap32(ram_size); @@ -144,7 +144,7 @@ static int64_t load_kernel(void) } rom_add_blob_fixed("params", params_buf, params_size, - (16 << 20) - 264); + 16 * MiB - params_size); g_free(params_buf); return entry; @@ -159,7 +159,7 @@ static void main_cpu_reset(void *opaque) env->active_tc.PC = s->vector; } -static const int sector_len = 32 * 1024; +static const int sector_len = 32 * KiB; static void mips_r4k_init(MachineState *machine) { @@ -195,9 +195,9 @@ void mips_r4k_init(MachineState *machine) qemu_register_reset(main_cpu_reset, reset_info); /* allocate RAM */ - if (ram_size > (256 << 20)) { - error_report("Too much memory for this machine: %dMB, maximum 256MB", - ((unsigned int)ram_size / (1 << 20))); + if (ram_size > 256 * MiB) { + error_report("Too much memory for this machine: %" PRId64 "MB," + " maximum 256MB", ram_size / MiB); exit(1); } memory_region_allocate_system_memory(ram, NULL, "mips_r4k.ram", ram_size); @@ -274,7 +274,7 @@ void mips_r4k_init(MachineState *machine) pit = i8254_pit_init(isa_bus, 0x40, 0, NULL); - serial_hds_isa_init(isa_bus, 0, MAX_SERIAL_PORTS); + serial_hds_isa_init(isa_bus, 0, MAX_ISA_SERIAL_PORTS); isa_vga_init(isa_bus); diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 00e834d0f0..9350900845 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -7,6 +7,7 @@ common-obj-$(CONFIG_SGA) += sga.o common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o common-obj-$(CONFIG_EDU) += edu.o +common-obj-$(CONFIG_PCA9552) += pca9552.o common-obj-y += unimp.o common-obj-$(CONFIG_FW_CFG_DMA) += vmcoreinfo.o @@ -61,6 +62,7 @@ obj-$(CONFIG_MIPS_ITU) += mips_itu.o obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o obj-$(CONFIG_MPS2_SCC) += mps2-scc.o +obj-$(CONFIG_TZ_MPC) += tz-mpc.o obj-$(CONFIG_TZ_PPC) += tz-ppc.o obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o diff --git a/hw/misc/arm_integrator_debug.c b/hw/misc/arm_integrator_debug.c index 8a5f29559d..533e6e3208 100644 --- a/hw/misc/arm_integrator_debug.c +++ b/hw/misc/arm_integrator_debug.c @@ -17,7 +17,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/sysbus.h" -#include "exec/address-spaces.h" #include "hw/misc/arm_integrator_debug.h" #include "qemu/log.h" diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 5e6d5744ee..c8217740ef 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -16,6 +16,7 @@ #include "qapi/visitor.h" #include "qemu/bitops.h" #include "qemu/log.h" +#include "crypto/random.h" #include "trace.h" #define TO_REG(offset) ((offset) >> 2) @@ -154,6 +155,40 @@ static const uint32_t ast2500_a1_resets[ASPEED_SCU_NR_REGS] = { [BMC_DEV_ID] = 0x00002402U }; +static uint32_t aspeed_scu_get_random(void) +{ + Error *err = NULL; + uint32_t num; + + if (qcrypto_random_bytes((uint8_t *)&num, sizeof(num), &err)) { + error_report_err(err); + exit(1); + } + + return num; +} + +static void aspeed_scu_set_apb_freq(AspeedSCUState *s) +{ + uint32_t apb_divider; + + switch (s->silicon_rev) { + case AST2400_A0_SILICON_REV: + case AST2400_A1_SILICON_REV: + apb_divider = 2; + break; + case AST2500_A0_SILICON_REV: + case AST2500_A1_SILICON_REV: + apb_divider = 4; + break; + default: + g_assert_not_reached(); + } + + s->apb_freq = s->hpll / (SCU_CLK_GET_PCLK_DIV(s->regs[CLK_SEL]) + 1) + / apb_divider; +} + static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) { AspeedSCUState *s = ASPEED_SCU(opaque); @@ -167,6 +202,12 @@ static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size) } switch (reg) { + case RNG_DATA: + /* On hardware, RNG_DATA works regardless of + * the state of the enable bit in RNG_CTRL + */ + s->regs[RNG_DATA] = aspeed_scu_get_random(); + break; case WAKEUP_EN: qemu_log_mask(LOG_GUEST_ERROR, "%s: Read of write-only offset 0x%" HWADDR_PRIx "\n", @@ -202,11 +243,30 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data, case PROT_KEY: s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0; return; - + case CLK_SEL: + s->regs[reg] = data; + aspeed_scu_set_apb_freq(s); + break; + case HW_STRAP1: + if (ASPEED_IS_AST2500(s->regs[SILICON_REV])) { + s->regs[HW_STRAP1] |= data; + return; + } + /* Jump to assignment below */ + break; + case SILICON_REV: + if (ASPEED_IS_AST2500(s->regs[SILICON_REV])) { + s->regs[HW_STRAP1] &= ~data; + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Write to read-only offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + /* Avoid assignment below, we've handled everything */ + return; case FREQ_CNTR_EVAL: case VGA_SCRATCH1 ... VGA_SCRATCH8: case RNG_DATA: - case SILICON_REV: case FREE_CNTR4: case FREE_CNTR4_EXT: qemu_log_mask(LOG_GUEST_ERROR, @@ -227,19 +287,93 @@ static const MemoryRegionOps aspeed_scu_ops = { .valid.unaligned = false, }; +static uint32_t aspeed_scu_get_clkin(AspeedSCUState *s) +{ + if (s->hw_strap1 & SCU_HW_STRAP_CLK_25M_IN) { + return 25000000; + } else if (s->hw_strap1 & SCU_HW_STRAP_CLK_48M_IN) { + return 48000000; + } else { + return 24000000; + } +} + +/* + * Strapped frequencies for the AST2400 in MHz. They depend on the + * clkin frequency. + */ +static const uint32_t hpll_ast2400_freqs[][4] = { + { 384, 360, 336, 408 }, /* 24MHz or 48MHz */ + { 400, 375, 350, 425 }, /* 25MHz */ +}; + +static uint32_t aspeed_scu_calc_hpll_ast2400(AspeedSCUState *s) +{ + uint32_t hpll_reg = s->regs[HPLL_PARAM]; + uint8_t freq_select; + bool clk_25m_in; + + if (hpll_reg & SCU_AST2400_H_PLL_OFF) { + return 0; + } + + if (hpll_reg & SCU_AST2400_H_PLL_PROGRAMMED) { + uint32_t multiplier = 1; + + if (!(hpll_reg & SCU_AST2400_H_PLL_BYPASS_EN)) { + uint32_t n = (hpll_reg >> 5) & 0x3f; + uint32_t od = (hpll_reg >> 4) & 0x1; + uint32_t d = hpll_reg & 0xf; + + multiplier = (2 - od) * ((n + 2) / (d + 1)); + } + + return s->clkin * multiplier; + } + + /* HW strapping */ + clk_25m_in = !!(s->hw_strap1 & SCU_HW_STRAP_CLK_25M_IN); + freq_select = SCU_AST2400_HW_STRAP_GET_H_PLL_CLK(s->hw_strap1); + + return hpll_ast2400_freqs[clk_25m_in][freq_select] * 1000000; +} + +static uint32_t aspeed_scu_calc_hpll_ast2500(AspeedSCUState *s) +{ + uint32_t hpll_reg = s->regs[HPLL_PARAM]; + uint32_t multiplier = 1; + + if (hpll_reg & SCU_H_PLL_OFF) { + return 0; + } + + if (!(hpll_reg & SCU_H_PLL_BYPASS_EN)) { + uint32_t p = (hpll_reg >> 13) & 0x3f; + uint32_t m = (hpll_reg >> 5) & 0xff; + uint32_t n = hpll_reg & 0x1f; + + multiplier = ((m + 1) / (n + 1)) / (p + 1); + } + + return s->clkin * multiplier; +} + static void aspeed_scu_reset(DeviceState *dev) { AspeedSCUState *s = ASPEED_SCU(dev); const uint32_t *reset; + uint32_t (*calc_hpll)(AspeedSCUState *s); switch (s->silicon_rev) { case AST2400_A0_SILICON_REV: case AST2400_A1_SILICON_REV: reset = ast2400_a0_resets; + calc_hpll = aspeed_scu_calc_hpll_ast2400; break; case AST2500_A0_SILICON_REV: case AST2500_A1_SILICON_REV: reset = ast2500_a1_resets; + calc_hpll = aspeed_scu_calc_hpll_ast2500; break; default: g_assert_not_reached(); @@ -250,6 +384,13 @@ static void aspeed_scu_reset(DeviceState *dev) s->regs[HW_STRAP1] = s->hw_strap1; s->regs[HW_STRAP2] = s->hw_strap2; s->regs[PROT_KEY] = s->hw_prot_key; + + /* + * All registers are set. Now compute the frequencies of the main clocks + */ + s->clkin = aspeed_scu_get_clkin(s); + s->hpll = calc_hpll(s); + aspeed_scu_set_apb_freq(s); } static uint32_t aspeed_silicon_revs[] = { diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index b4cacd664b..0e56d9a8a4 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -27,10 +27,12 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/log.h" #include "hw/misc/auxbus.h" #include "hw/i2c/i2c.h" #include "monitor/monitor.h" +#include "qapi/error.h" #ifndef DEBUG_AUX #define DEBUG_AUX 0 @@ -62,20 +64,27 @@ static void aux_bus_class_init(ObjectClass *klass, void *data) AUXBus *aux_init_bus(DeviceState *parent, const char *name) { AUXBus *bus; + Object *auxtoi2c; bus = AUX_BUS(qbus_create(TYPE_AUX_BUS, parent, name)); - bus->bridge = AUXTOI2C(qdev_create(BUS(bus), TYPE_AUXTOI2C)); + auxtoi2c = object_new_with_props(TYPE_AUXTOI2C, OBJECT(bus), "i2c", + &error_abort, NULL); + qdev_set_parent_bus(DEVICE(auxtoi2c), BUS(bus)); + + bus->bridge = AUXTOI2C(auxtoi2c); /* Memory related. */ bus->aux_io = g_malloc(sizeof(*bus->aux_io)); - memory_region_init(bus->aux_io, OBJECT(bus), "aux-io", (1 << 20)); + memory_region_init(bus->aux_io, OBJECT(bus), "aux-io", 1 * MiB); address_space_init(&bus->aux_addr_space, bus->aux_io, "aux-io"); return bus; } -static void aux_bus_map_device(AUXBus *bus, AUXSlave *dev, hwaddr addr) +void aux_map_slave(AUXSlave *aux_dev, hwaddr addr) { - memory_region_add_subregion(bus->aux_io, addr, dev->mmio); + DeviceState *dev = DEVICE(aux_dev); + AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev)); + memory_region_add_subregion(bus->aux_io, addr, aux_dev->mmio); } static bool aux_bus_is_bridge(AUXBus *bus, DeviceState *dev) @@ -259,15 +268,13 @@ static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent) memory_region_size(s->mmio)); } -DeviceState *aux_create_slave(AUXBus *bus, const char *type, uint32_t addr) +DeviceState *aux_create_slave(AUXBus *bus, const char *type) { DeviceState *dev; dev = DEVICE(object_new(type)); assert(dev); qdev_set_parent_bus(dev, &bus->qbus); - qdev_init_nofail(dev); - aux_bus_map_device(AUX_BUS(qdev_get_parent_bus(dev)), AUX_SLAVE(dev), addr); return dev; } diff --git a/hw/misc/edu.c b/hw/misc/edu.c index 34eb05d213..df26a4d046 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "qemu/timer.h" @@ -357,7 +358,7 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp) edu, QEMU_THREAD_JOINABLE); memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu, - "edu-mmio", 1 << 20); + "edu-mmio", 1 * MiB); pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio); } diff --git a/hw/misc/hyperv_testdev.c b/hw/misc/hyperv_testdev.c index dbd7cdda07..bf6bbfa8cf 100644 --- a/hw/misc/hyperv_testdev.c +++ b/hw/misc/hyperv_testdev.c @@ -57,7 +57,7 @@ static void free_sint_route_index(HypervTestDev *dev, int i) dev->sint_route[i] = NULL; } -static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id, +static int find_sint_route_index(HypervTestDev *dev, uint32_t vp_index, uint32_t sint) { HvSintRoute *sint_route; @@ -65,7 +65,7 @@ static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id, for (i = 0; i < ARRAY_SIZE(dev->sint_route); i++) { sint_route = dev->sint_route[i]; - if (sint_route && sint_route->vcpu_id == vcpu_id && + if (sint_route && sint_route->vp_index == vp_index && sint_route->sint == sint) { return i; } @@ -74,7 +74,7 @@ static int find_sint_route_index(HypervTestDev *dev, uint32_t vcpu_id, } static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl, - uint32_t vcpu_id, uint32_t sint) + uint32_t vp_index, uint32_t sint) { int i; HvSintRoute *sint_route; @@ -83,19 +83,19 @@ static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl, case HV_TEST_DEV_SINT_ROUTE_CREATE: i = alloc_sint_route_index(dev); assert(i >= 0); - sint_route = kvm_hv_sint_route_create(vcpu_id, sint, NULL); + sint_route = kvm_hv_sint_route_create(vp_index, sint, NULL); assert(sint_route); dev->sint_route[i] = sint_route; break; case HV_TEST_DEV_SINT_ROUTE_DESTROY: - i = find_sint_route_index(dev, vcpu_id, sint); + i = find_sint_route_index(dev, vp_index, sint); assert(i >= 0); sint_route = dev->sint_route[i]; kvm_hv_sint_route_destroy(sint_route); free_sint_route_index(dev, i); break; case HV_TEST_DEV_SINT_ROUTE_SET_SINT: - i = find_sint_route_index(dev, vcpu_id, sint); + i = find_sint_route_index(dev, vp_index, sint); assert(i >= 0); sint_route = dev->sint_route[i]; kvm_hv_sint_route_set_sint(sint_route); @@ -117,8 +117,8 @@ static void hv_test_dev_control(void *opaque, hwaddr addr, uint64_t data, case HV_TEST_DEV_SINT_ROUTE_DESTROY: case HV_TEST_DEV_SINT_ROUTE_SET_SINT: { uint8_t sint = data & 0xFF; - uint8_t vcpu_id = (data >> 8ULL) & 0xFF; - hv_synic_test_dev_control(dev, ctl, vcpu_id, sint); + uint8_t vp_index = (data >> 8ULL) & 0xFF; + hv_synic_test_dev_control(dev, ctl, vp_index, sint); break; } default: diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index ddd1584d34..de4fd8e36d 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -139,6 +139,9 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, case A_NSCCFG: r = s->nsccfg; break; + case A_SECMPCINTSTATUS: + r = s->mpcintstatus; + break; case A_SECPPCINTSTAT: r = s->secppcintstat; break; @@ -186,7 +189,6 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, case A_APBSPPPCEXP3: r = s->apbexp[offset_to_ppc_idx(offset)].sp; break; - case A_SECMPCINTSTATUS: case A_SECMSCINTSTAT: case A_SECMSCINTEN: case A_NSMSCEXP: @@ -572,6 +574,20 @@ static void iotkit_secctl_reset(DeviceState *dev) foreach_ppc(s, iotkit_secctl_reset_ppc); } +static void iotkit_secctl_mpc_status(void *opaque, int n, int level) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + s->mpcintstatus = deposit32(s->mpcintstatus, 0, 1, !!level); +} + +static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level); +} + static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level) { IoTKitSecCtlPPC *ppc = opaque; @@ -640,6 +656,10 @@ static void iotkit_secctl_init(Object *obj) qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1); + qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", 1); + qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status, + "mpcexp_status", IOTS_NUM_EXP_MPC); + memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops, s, "iotkit-secctl-s-regs", 0x1000); memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops, @@ -660,6 +680,16 @@ static const VMStateDescription iotkit_secctl_ppc_vmstate = { } }; +static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = { + .name = "iotkit-secctl-mpcintstatus", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(mpcintstatus, IoTKitSecCtl), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription iotkit_secctl_vmstate = { .name = "iotkit-secctl", .version_id = 1, @@ -677,7 +707,11 @@ static const VMStateDescription iotkit_secctl_vmstate = { VMSTATE_STRUCT_ARRAY(ahbexp, IoTKitSecCtl, IOTS_NUM_AHB_EXP_PPC, 1, iotkit_secctl_ppc_vmstate, IoTKitSecCtlPPC), VMSTATE_END_OF_LIST() - } + }, + .subsections = (const VMStateDescription*[]) { + &iotkit_secctl_mpcintstatus_vmstate, + NULL + }, }; static void iotkit_secctl_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 16f03701b7..6febbabcaa 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -17,6 +17,7 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "hw/hw.h" @@ -909,8 +910,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) if (s->hostmem != NULL) { IVSHMEM_DPRINTF("using hostmem\n"); - s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem, - &error_abort); + s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem); } else { Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr); assert(chr); @@ -1302,7 +1302,7 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp) } if (s->sizearg == NULL) { - s->legacy_size = 4 << 20; /* 4 MB default */ + s->legacy_size = 4 * MiB; /* 4 MB default */ } else { int ret; uint64_t size; diff --git a/hw/misc/macio/Makefile.objs b/hw/misc/macio/Makefile.objs index ef7ac249ec..07fdb320d4 100644 --- a/hw/misc/macio/Makefile.objs +++ b/hw/misc/macio/Makefile.objs @@ -1,3 +1,5 @@ common-obj-y += macio.o common-obj-$(CONFIG_CUDA) += cuda.o +common-obj-$(CONFIG_MAC_PMU) += pmu.o common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o +common-obj-$(CONFIG_MACIO_GPIO) += gpio.o diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index bd9b862034..c4f7a2f39b 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -65,7 +65,7 @@ static void cuda_receive_packet_from_host(CUDAState *s, static uint64_t cuda_get_counter_value(MOS6522State *s, MOS6522Timer *ti) { MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); - CUDAState *cs = mcs->cuda; + CUDAState *cs = container_of(mcs, CUDAState, mos6522_cuda); /* Reverse of the tb calculation algorithm that Mac OS X uses on bootup */ uint64_t tb_diff = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), @@ -78,7 +78,7 @@ static uint64_t cuda_get_counter_value(MOS6522State *s, MOS6522Timer *ti) static uint64_t cuda_get_load_time(MOS6522State *s, MOS6522Timer *ti) { MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); - CUDAState *cs = mcs->cuda; + CUDAState *cs = container_of(mcs, CUDAState, mos6522_cuda); uint64_t load_time = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), cs->tb_frequency, NANOSECONDS_PER_SECOND); @@ -88,7 +88,7 @@ static uint64_t cuda_get_load_time(MOS6522State *s, MOS6522Timer *ti) static void cuda_set_sr_int(void *opaque) { CUDAState *s = opaque; - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); @@ -97,7 +97,7 @@ static void cuda_set_sr_int(void *opaque) static void cuda_delay_set_sr_int(CUDAState *s) { - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); int64_t expire; @@ -117,7 +117,7 @@ static void cuda_delay_set_sr_int(CUDAState *s) /* NOTE: TIP and TREQ are negated */ static void cuda_update(CUDAState *s) { - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); int packet_received, len; @@ -462,7 +462,7 @@ static void cuda_receive_packet_from_host(CUDAState *s, static uint64_t mos6522_cuda_read(void *opaque, hwaddr addr, unsigned size) { CUDAState *s = opaque; - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); addr = (addr >> 9) & 0xf; @@ -473,7 +473,7 @@ static void mos6522_cuda_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { CUDAState *s = opaque; - MOS6522CUDAState *mcs = s->mos6522_cuda; + MOS6522CUDAState *mcs = &s->mos6522_cuda; MOS6522State *ms = MOS6522(mcs); addr = (addr >> 9) & 0xf; @@ -492,9 +492,11 @@ static const MemoryRegionOps mos6522_cuda_ops = { static const VMStateDescription vmstate_cuda = { .name = "cuda", - .version_id = 4, - .minimum_version_id = 4, + .version_id = 5, + .minimum_version_id = 5, .fields = (VMStateField[]) { + VMSTATE_STRUCT(mos6522_cuda.parent_obj, CUDAState, 0, vmstate_mos6522, + MOS6522State), VMSTATE_UINT8(last_b, CUDAState), VMSTATE_UINT8(last_acr, CUDAState), VMSTATE_INT32(data_in_size, CUDAState), @@ -530,12 +532,8 @@ static void cuda_realize(DeviceState *dev, Error **errp) DeviceState *d; struct tm tm; - d = qdev_create(NULL, TYPE_MOS6522_CUDA); - object_property_set_link(OBJECT(d), OBJECT(s), "cuda", errp); - qdev_init_nofail(d); - s->mos6522_cuda = MOS6522_CUDA(d); - /* Pass IRQ from 6522 */ + d = DEVICE(&s->mos6522_cuda); ms = MOS6522(d); sbd = SYS_BUS_DEVICE(s); sysbus_pass_irq(sbd, SYS_BUS_DEVICE(ms)); @@ -556,6 +554,9 @@ static void cuda_init(Object *obj) CUDAState *s = CUDA(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + sysbus_init_child_obj(obj, "mos6522-cuda", &s->mos6522_cuda, + sizeof(s->mos6522_cuda), TYPE_MOS6522_CUDA); + memory_region_init_io(&s->mem, obj, &mos6522_cuda_ops, s, "cuda", 0x2000); sysbus_init_mmio(sbd, &s->mem); @@ -590,37 +591,28 @@ static const TypeInfo cuda_type_info = { static void mos6522_cuda_portB_write(MOS6522State *s) { MOS6522CUDAState *mcs = container_of(s, MOS6522CUDAState, parent_obj); + CUDAState *cs = container_of(mcs, CUDAState, mos6522_cuda); - cuda_update(mcs->cuda); + cuda_update(cs); } -static void mos6522_cuda_realize(DeviceState *dev, Error **errp) +static void mos6522_cuda_reset(DeviceState *dev) { MOS6522State *ms = MOS6522(dev); MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); - mdc->parent_realize(dev, errp); + mdc->parent_reset(dev); ms->timers[0].frequency = CUDA_TIMER_FREQ; ms->timers[1].frequency = (SCALE_US * 6000) / 4700; } -static void mos6522_cuda_init(Object *obj) -{ - MOS6522CUDAState *s = MOS6522_CUDA(obj); - - object_property_add_link(obj, "cuda", TYPE_CUDA, - (Object **) &s->cuda, - qdev_prop_allow_set_link_before_realize, - 0, NULL); -} - static void mos6522_cuda_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); - dc->realize = mos6522_cuda_realize; + dc->reset = mos6522_cuda_reset; mdc->portB_write = mos6522_cuda_portB_write; mdc->get_timer1_counter_value = cuda_get_counter_value; mdc->get_timer2_counter_value = cuda_get_counter_value; @@ -632,7 +624,6 @@ static const TypeInfo mos6522_cuda_type_info = { .name = TYPE_MOS6522_CUDA, .parent = TYPE_MOS6522, .instance_size = sizeof(MOS6522CUDAState), - .instance_init = mos6522_cuda_init, .class_init = mos6522_cuda_class_init, }; diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c new file mode 100644 index 0000000000..9317df759c --- /dev/null +++ b/hw/misc/macio/gpio.c @@ -0,0 +1,231 @@ +/* + * PowerMac NewWorld MacIO GPIO emulation + * + * Copyright (c) 2016 Benjamin Herrenschmidt + * Copyright (c) 2018 Mark Cave-Ayland + * + * 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/hw.h" +#include "hw/ppc/mac.h" +#include "hw/misc/macio/macio.h" +#include "hw/misc/macio/gpio.h" +#include "hw/nmi.h" +#include "qemu/log.h" +#include "trace.h" + + +void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state) +{ + uint8_t new_reg; + + trace_macio_set_gpio(gpio, state); + + if (s->gpio_regs[gpio] & 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "GPIO: Setting GPIO %d while it's an output\n", gpio); + } + + new_reg = s->gpio_regs[gpio] & ~2; + if (state) { + new_reg |= 2; + } + + if (new_reg == s->gpio_regs[gpio]) { + return; + } + + s->gpio_regs[gpio] = new_reg; + + /* This is will work until we fix the binding between MacIO and + * the MPIC properly so we can route all GPIOs and avoid going + * via the top level platform code. + * + * Note that we probably need to get access to the MPIC config to + * decode polarity since qemu always use "raise" regardless. + * + * For now, we hard wire known GPIOs + */ + + switch (gpio) { + case 1: + /* Level low */ + if (!state) { + trace_macio_gpio_irq_assert(gpio); + qemu_irq_raise(s->gpio_extirqs[gpio]); + } else { + trace_macio_gpio_irq_deassert(gpio); + qemu_irq_lower(s->gpio_extirqs[gpio]); + } + break; + + case 9: + /* Edge, triggered by NMI below */ + if (state) { + trace_macio_gpio_irq_assert(gpio); + qemu_irq_raise(s->gpio_extirqs[gpio]); + } else { + trace_macio_gpio_irq_deassert(gpio); + qemu_irq_lower(s->gpio_extirqs[gpio]); + } + break; + + default: + qemu_log_mask(LOG_UNIMP, "GPIO: setting unimplemented GPIO %d", gpio); + } +} + +static void macio_gpio_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + MacIOGPIOState *s = opaque; + uint8_t ibit; + + trace_macio_gpio_write(addr, value); + + /* Levels regs are read-only */ + if (addr < 8) { + return; + } + + addr -= 8; + if (addr < 36) { + value &= ~2; + + if (value & 4) { + ibit = (value & 1) << 1; + } else { + ibit = s->gpio_regs[addr] & 2; + } + + s->gpio_regs[addr] = value | ibit; + } +} + +static uint64_t macio_gpio_read(void *opaque, hwaddr addr, unsigned size) +{ + MacIOGPIOState *s = opaque; + uint64_t val = 0; + + /* Levels regs */ + if (addr < 8) { + val = s->gpio_levels[addr]; + } else { + addr -= 8; + + if (addr < 36) { + val = s->gpio_regs[addr]; + } + } + + trace_macio_gpio_write(addr, val); + return val; +} + +static const MemoryRegionOps macio_gpio_ops = { + .read = macio_gpio_read, + .write = macio_gpio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static void macio_gpio_realize(DeviceState *dev, Error **errp) +{ + MacIOGPIOState *s = MACIO_GPIO(dev); + + s->gpio_extirqs[1] = qdev_get_gpio_in(DEVICE(s->pic), + NEWWORLD_EXTING_GPIO1); + s->gpio_extirqs[9] = qdev_get_gpio_in(DEVICE(s->pic), + NEWWORLD_EXTING_GPIO9); +} + +static void macio_gpio_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + MacIOGPIOState *s = MACIO_GPIO(obj); + + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + memory_region_init_io(&s->gpiomem, OBJECT(s), &macio_gpio_ops, obj, + "gpio", 0x30); + sysbus_init_mmio(sbd, &s->gpiomem); +} + +static const VMStateDescription vmstate_macio_gpio = { + .name = "macio_gpio", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(gpio_levels, MacIOGPIOState, 8), + VMSTATE_UINT8_ARRAY(gpio_regs, MacIOGPIOState, 36), + VMSTATE_END_OF_LIST() + } +}; + +static void macio_gpio_reset(DeviceState *dev) +{ + MacIOGPIOState *s = MACIO_GPIO(dev); + + /* GPIO 1 is up by default */ + macio_set_gpio(s, 1, true); +} + +static void macio_gpio_nmi(NMIState *n, int cpu_index, Error **errp) +{ + macio_set_gpio(MACIO_GPIO(n), 9, true); + macio_set_gpio(MACIO_GPIO(n), 9, false); +} + +static void macio_gpio_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); + + dc->realize = macio_gpio_realize; + dc->reset = macio_gpio_reset; + dc->vmsd = &vmstate_macio_gpio; + nc->nmi_monitor_handler = macio_gpio_nmi; +} + +static const TypeInfo macio_gpio_init_info = { + .name = TYPE_MACIO_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MacIOGPIOState), + .instance_init = macio_gpio_init, + .class_init = macio_gpio_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, +}; + +static void macio_gpio_register_types(void) +{ + type_register_static(&macio_gpio_init_info); +} + +type_init(macio_gpio_register_types) diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 1b2a69b3ef..87ae246d37 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -71,18 +71,19 @@ static DBDMAState *dbdma_from_ch(DBDMA_channel *ch) } #if DEBUG_DBDMA -static void dump_dbdma_cmd(dbdma_cmd *cmd) +static void dump_dbdma_cmd(DBDMA_channel *ch, dbdma_cmd *cmd) { - printf("dbdma_cmd %p\n", cmd); - printf(" req_count 0x%04x\n", le16_to_cpu(cmd->req_count)); - printf(" command 0x%04x\n", le16_to_cpu(cmd->command)); - printf(" phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr)); - printf(" cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep)); - printf(" res_count 0x%04x\n", le16_to_cpu(cmd->res_count)); - printf(" xfer_status 0x%04x\n", le16_to_cpu(cmd->xfer_status)); + DBDMA_DPRINTFCH(ch, "dbdma_cmd %p\n", cmd); + DBDMA_DPRINTFCH(ch, " req_count 0x%04x\n", le16_to_cpu(cmd->req_count)); + DBDMA_DPRINTFCH(ch, " command 0x%04x\n", le16_to_cpu(cmd->command)); + DBDMA_DPRINTFCH(ch, " phy_addr 0x%08x\n", le32_to_cpu(cmd->phy_addr)); + DBDMA_DPRINTFCH(ch, " cmd_dep 0x%08x\n", le32_to_cpu(cmd->cmd_dep)); + DBDMA_DPRINTFCH(ch, " res_count 0x%04x\n", le16_to_cpu(cmd->res_count)); + DBDMA_DPRINTFCH(ch, " xfer_status 0x%04x\n", + le16_to_cpu(cmd->xfer_status)); } #else -static void dump_dbdma_cmd(dbdma_cmd *cmd) +static void dump_dbdma_cmd(DBDMA_channel *ch, dbdma_cmd *cmd) { } #endif @@ -448,7 +449,7 @@ static void channel_run(DBDMA_channel *ch) uint32_t phy_addr; DBDMA_DPRINTFCH(ch, "channel_run\n"); - dump_dbdma_cmd(current); + dump_dbdma_cmd(ch, current); /* clear WAKE flag at command fetch */ diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index b74a6572b0..52aa3775f4 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -32,6 +32,7 @@ #include "hw/char/escc.h" #include "hw/misc/macio/macio.h" #include "hw/intc/heathrow_pic.h" +#include "trace.h" /* Note: this code is strongly inspirated from the corresponding code * in PearPC */ @@ -104,22 +105,11 @@ static void macio_common_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&s->bar, 0x08000, sysbus_mmio_get_region(sysbus_dev, 0)); - qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", - s->frequency); - object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - memory_region_add_subregion(&s->bar, 0x16000, - sysbus_mmio_get_region(sysbus_dev, 0)); - qdev_prop_set_uint32(DEVICE(&s->escc), "disabled", 0); qdev_prop_set_uint32(DEVICE(&s->escc), "frequency", ESCC_CLOCK); qdev_prop_set_uint32(DEVICE(&s->escc), "it_shift", 4); - qdev_prop_set_chr(DEVICE(&s->escc), "chrA", serial_hds[0]); - qdev_prop_set_chr(DEVICE(&s->escc), "chrB", serial_hds[1]); + qdev_prop_set_chr(DEVICE(&s->escc), "chrA", serial_hd(0)); + qdev_prop_set_chr(DEVICE(&s->escc), "chrB", serial_hd(1)); qdev_prop_set_uint32(DEVICE(&s->escc), "chnBtype", escc_serial); qdev_prop_set_uint32(DEVICE(&s->escc), "chnAtype", escc_serial); object_property_set_bool(OBJECT(&s->escc), true, "realized", &err); @@ -152,10 +142,9 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) { MacIOState *s = MACIO(d); OldWorldMacIOState *os = OLDWORLD_MACIO(d); + DeviceState *pic_dev = DEVICE(os->pic); Error *err = NULL; SysBusDevice *sysbus_dev; - int i; - int cur_irq = 0; macio_common_realize(d, &err); if (err) { @@ -163,12 +152,24 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) return; } + qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", + s->frequency); + object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + OLDWORLD_CUDA_IRQ)); sysbus_dev = SYS_BUS_DEVICE(&s->escc); - sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); - sysbus_connect_irq(sysbus_dev, 1, os->irqs[cur_irq++]); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + OLDWORLD_ESCCB_IRQ)); + sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, + OLDWORLD_ESCCA_IRQ)); object_property_set_bool(OBJECT(&os->nvram), true, "realized", &err); if (err) { @@ -186,29 +187,33 @@ static void macio_oldworld_realize(PCIDevice *d, Error **errp) sysbus_mmio_get_region(sysbus_dev, 0)); /* IDE buses */ - for (i = 0; i < ARRAY_SIZE(os->ide); i++) { - qemu_irq irq0 = os->irqs[cur_irq++]; - qemu_irq irq1 = os->irqs[cur_irq++]; + macio_realize_ide(s, &os->ide[0], + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_IRQ), + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE0_DMA_IRQ), + 0x16, &err); + if (err) { + error_propagate(errp, err); + return; + } - macio_realize_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4), &err); - if (err) { - error_propagate(errp, err); - return; - } + macio_realize_ide(s, &os->ide[1], + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_IRQ), + qdev_get_gpio_in(pic_dev, OLDWORLD_IDE1_DMA_IRQ), + 0x1a, &err); + if (err) { + error_propagate(errp, err); + return; } } static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size, int index) { - gchar *name; + gchar *name = g_strdup_printf("ide[%i]", index); - object_initialize(ide, ide_size, TYPE_MACIO_IDE); - qdev_set_parent_bus(DEVICE(ide), sysbus_get_default()); + sysbus_init_child_obj(OBJECT(s), name, ide, ide_size, TYPE_MACIO_IDE); memory_region_add_subregion(&s->bar, 0x1f000 + ((index + 1) * 0x1000), &ide->mem); - name = g_strdup_printf("ide[%i]", index); - object_property_add_child(OBJECT(s), name, OBJECT(ide), NULL); g_free(name); } @@ -219,13 +224,13 @@ static void macio_oldworld_init(Object *obj) DeviceState *dev; int i; - qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs)); - object_property_add_link(obj, "pic", TYPE_HEATHROW, (Object **) &os->pic, qdev_prop_allow_set_link_before_realize, 0, NULL); + sysbus_init_child_obj(obj, "cuda", &s->cuda, sizeof(s->cuda), TYPE_CUDA); + object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM); dev = DEVICE(&os->nvram); qdev_prop_set_uint32(dev, "size", 0x2000); @@ -239,6 +244,7 @@ static void macio_oldworld_init(Object *obj) static void timer_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { + trace_macio_timer_write(addr, size, value); } static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) @@ -259,6 +265,7 @@ static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) break; } + trace_macio_timer_read(addr, size, value); return value; } @@ -272,11 +279,10 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) { MacIOState *s = MACIO(d); NewWorldMacIOState *ns = NEWWORLD_MACIO(d); + DeviceState *pic_dev = DEVICE(ns->pic); Error *err = NULL; SysBusDevice *sysbus_dev; MemoryRegion *timer_memory = NULL; - int i; - int cur_irq = 0; macio_common_realize(d, &err); if (err) { @@ -284,12 +290,11 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) return; } - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); - sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]); - sysbus_dev = SYS_BUS_DEVICE(&s->escc); - sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]); - sysbus_connect_irq(sysbus_dev, 1, ns->irqs[cur_irq++]); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + NEWWORLD_ESCCB_IRQ)); + sysbus_connect_irq(sysbus_dev, 1, qdev_get_gpio_in(pic_dev, + NEWWORLD_ESCCA_IRQ)); /* OpenPIC */ sysbus_dev = SYS_BUS_DEVICE(ns->pic); @@ -297,15 +302,22 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) sysbus_mmio_get_region(sysbus_dev, 0)); /* IDE buses */ - for (i = 0; i < ARRAY_SIZE(ns->ide); i++) { - qemu_irq irq0 = ns->irqs[cur_irq++]; - qemu_irq irq1 = ns->irqs[cur_irq++]; + macio_realize_ide(s, &ns->ide[0], + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_IRQ), + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE0_DMA_IRQ), + 0x16, &err); + if (err) { + error_propagate(errp, err); + return; + } - macio_realize_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4), &err); - if (err) { - error_propagate(errp, err); - return; - } + macio_realize_ide(s, &ns->ide[1], + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_IRQ), + qdev_get_gpio_in(pic_dev, NEWWORLD_IDE1_DMA_IRQ), + 0x1a, &err); + if (err) { + error_propagate(errp, err); + return; } /* Timer */ @@ -313,6 +325,53 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer", 0x1000); memory_region_add_subregion(&s->bar, 0x15000, timer_memory); + + if (ns->has_pmu) { + /* GPIOs */ + sysbus_dev = SYS_BUS_DEVICE(&ns->gpio); + object_property_set_link(OBJECT(&ns->gpio), OBJECT(pic_dev), "pic", + &error_abort); + memory_region_add_subregion(&s->bar, 0x50, + sysbus_mmio_get_region(sysbus_dev, 0)); + object_property_set_bool(OBJECT(&ns->gpio), true, "realized", &err); + + /* PMU */ + object_initialize(&s->pmu, sizeof(s->pmu), TYPE_VIA_PMU); + object_property_set_link(OBJECT(&s->pmu), OBJECT(sysbus_dev), "gpio", + &error_abort); + qdev_prop_set_bit(DEVICE(&s->pmu), "has-adb", ns->has_adb); + qdev_set_parent_bus(DEVICE(&s->pmu), sysbus_get_default()); + object_property_add_child(OBJECT(s), "pmu", OBJECT(&s->pmu), NULL); + + object_property_set_bool(OBJECT(&s->pmu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_dev = SYS_BUS_DEVICE(&s->pmu); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + NEWWORLD_PMU_IRQ)); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); + } else { + /* CUDA */ + object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); + qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); + object_property_add_child(OBJECT(s), "cuda", OBJECT(&s->cuda), NULL); + qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", + s->frequency); + + object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, + NEWWORLD_CUDA_IRQ)); + memory_region_add_subregion(&s->bar, 0x16000, + sysbus_mmio_get_region(sysbus_dev, 0)); + } } static void macio_newworld_init(Object *obj) @@ -321,13 +380,14 @@ static void macio_newworld_init(Object *obj) NewWorldMacIOState *ns = NEWWORLD_MACIO(obj); int i; - qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs)); - object_property_add_link(obj, "pic", TYPE_OPENPIC, (Object **) &ns->pic, qdev_prop_allow_set_link_before_realize, 0, NULL); + sysbus_init_child_obj(obj, "gpio", &ns->gpio, sizeof(ns->gpio), + TYPE_MACIO_GPIO); + for (i = 0; i < 2; i++) { macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i); } @@ -339,17 +399,10 @@ static void macio_instance_init(Object *obj) memory_region_init(&s->bar, obj, "macio", 0x80000); - object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); - qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); - object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); + sysbus_init_child_obj(obj, "dbdma", &s->dbdma, sizeof(s->dbdma), + TYPE_MAC_DBDMA); - object_initialize(&s->dbdma, sizeof(s->dbdma), TYPE_MAC_DBDMA); - qdev_set_parent_bus(DEVICE(&s->dbdma), sysbus_get_default()); - object_property_add_child(obj, "dbdma", OBJECT(&s->dbdma), NULL); - - object_initialize(&s->escc, sizeof(s->escc), TYPE_ESCC); - qdev_set_parent_bus(DEVICE(&s->escc), sysbus_get_default()); - object_property_add_child(obj, "escc", OBJECT(&s->escc), NULL); + sysbus_init_child_obj(obj, "escc", &s->escc, sizeof(s->escc), TYPE_ESCC); } static const VMStateDescription vmstate_macio_oldworld = { @@ -382,6 +435,12 @@ static const VMStateDescription vmstate_macio_newworld = { } }; +static Property macio_newworld_properties[] = { + DEFINE_PROP_BOOL("has-pmu", NewWorldMacIOState, has_pmu, false), + DEFINE_PROP_BOOL("has-adb", NewWorldMacIOState, has_adb, false), + DEFINE_PROP_END_OF_LIST() +}; + static void macio_newworld_class_init(ObjectClass *oc, void *data) { PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); @@ -390,6 +449,7 @@ static void macio_newworld_class_init(ObjectClass *oc, void *data) pdc->realize = macio_newworld_realize; pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; dc->vmsd = &vmstate_macio_newworld; + dc->props = macio_newworld_properties; } static Property macio_properties[] = { diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c new file mode 100644 index 0000000000..d25344f888 --- /dev/null +++ b/hw/misc/macio/pmu.c @@ -0,0 +1,870 @@ +/* + * QEMU PowerMac PMU device support + * + * Copyright (c) 2016 Benjamin Herrenschmidt, IBM Corp. + * Copyright (c) 2018 Mark Cave-Ayland + * + * Based on the CUDA device by: + * + * Copyright (c) 2004-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * 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/hw.h" +#include "hw/ppc/mac.h" +#include "hw/input/adb.h" +#include "hw/misc/mos6522.h" +#include "hw/misc/macio/gpio.h" +#include "hw/misc/macio/pmu.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "qemu/cutils.h" +#include "qemu/log.h" +#include "trace.h" + + +/* Bits in B data register: all active low */ +#define TACK 0x08 /* Transfer request (input) */ +#define TREQ 0x10 /* Transfer acknowledge (output) */ + +/* PMU returns time_t's offset from Jan 1, 1904, not 1970 */ +#define RTC_OFFSET 2082844800 + +#define VIA_TIMER_FREQ (4700000 / 6) + +static void via_update_irq(PMUState *s) +{ + MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); + MOS6522State *ms = MOS6522(mps); + + bool new_state = !!(ms->ifr & ms->ier & (SR_INT | T1_INT | T2_INT)); + + if (new_state != s->via_irq_state) { + s->via_irq_state = new_state; + qemu_set_irq(s->via_irq, new_state); + } +} + +static void via_set_sr_int(void *opaque) +{ + PMUState *s = opaque; + MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); + MOS6522State *ms = MOS6522(mps); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); + + mdc->set_sr_int(ms); +} + +static void pmu_update_extirq(PMUState *s) +{ + if ((s->intbits & s->intmask) != 0) { + macio_set_gpio(s->gpio, 1, false); + } else { + macio_set_gpio(s->gpio, 1, true); + } +} + +static void pmu_adb_poll(void *opaque) +{ + PMUState *s = opaque; + int olen; + + if (!(s->intbits & PMU_INT_ADB)) { + olen = adb_poll(&s->adb_bus, s->adb_reply, s->adb_poll_mask); + trace_pmu_adb_poll(olen); + + if (olen > 0) { + s->adb_reply_size = olen; + s->intbits |= PMU_INT_ADB | PMU_INT_ADB_AUTO; + pmu_update_extirq(s); + } + } + + timer_mod(s->adb_poll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); +} + +static void pmu_one_sec_timer(void *opaque) +{ + PMUState *s = opaque; + + trace_pmu_one_sec_timer(); + + s->intbits |= PMU_INT_TICK; + pmu_update_extirq(s); + s->one_sec_target += 1000; + + timer_mod(s->one_sec_timer, s->one_sec_target); +} + +static void pmu_cmd_int_ack(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: INT_ACK command, invalid len: %d want: 0\n", + in_len); + return; + } + + /* Make appropriate reply packet */ + if (s->intbits & PMU_INT_ADB) { + if (!s->adb_reply_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "Odd, PMU_INT_ADB set with no reply in buffer\n"); + } + + memcpy(out_data + 1, s->adb_reply, s->adb_reply_size); + out_data[0] = s->intbits & (PMU_INT_ADB | PMU_INT_ADB_AUTO); + *out_len = s->adb_reply_size + 1; + s->intbits &= ~(PMU_INT_ADB | PMU_INT_ADB_AUTO); + s->adb_reply_size = 0; + } else { + out_data[0] = s->intbits; + s->intbits = 0; + *out_len = 1; + } + + pmu_update_extirq(s); +} + +static void pmu_cmd_set_int_mask(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SET_INT_MASK command, invalid len: %d want: 1\n", + in_len); + return; + } + + trace_pmu_cmd_set_int_mask(s->intmask); + s->intmask = in_data[0]; + + pmu_update_extirq(s); +} + +static void pmu_cmd_set_adb_autopoll(PMUState *s, uint16_t mask) +{ + trace_pmu_cmd_set_adb_autopoll(mask); + + if (s->autopoll_mask == mask) { + return; + } + + s->autopoll_mask = mask; + if (mask) { + timer_mod(s->adb_poll_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); + } else { + timer_del(s->adb_poll_timer); + } +} + +static void pmu_cmd_adb(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + int len, adblen; + uint8_t adb_cmd[255]; + + if (in_len < 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB PACKET, invalid len: %d want at least 2\n", + in_len); + return; + } + + *out_len = 0; + + if (!s->has_adb) { + trace_pmu_cmd_adb_nobus(); + return; + } + + /* Set autopoll is a special form of the command */ + if (in_data[0] == 0 && in_data[1] == 0x86) { + uint16_t mask = in_data[2]; + mask = (mask << 8) | in_data[3]; + if (in_len != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB Autopoll requires 4 bytes, got %d\n", + in_len); + return; + } + + pmu_cmd_set_adb_autopoll(s, mask); + return; + } + + trace_pmu_cmd_adb_request(in_len, in_data[0], in_data[1], in_data[2], + in_data[3], in_data[4]); + + *out_len = 0; + + /* Check ADB len */ + adblen = in_data[2]; + if (adblen > (in_len - 3)) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB len is %d > %d (in_len -3)...erroring\n", + adblen, in_len - 3); + len = -1; + } else if (adblen > 252) { + qemu_log_mask(LOG_GUEST_ERROR, "PMU: ADB command too big!\n"); + len = -1; + } else { + /* Format command */ + adb_cmd[0] = in_data[0]; + memcpy(&adb_cmd[1], &in_data[3], in_len - 3); + len = adb_request(&s->adb_bus, s->adb_reply + 2, adb_cmd, in_len - 2); + + trace_pmu_cmd_adb_reply(len); + } + + if (len > 0) { + /* XXX Check this */ + s->adb_reply_size = len + 2; + s->adb_reply[0] = 0x01; + s->adb_reply[1] = len; + } else { + /* XXX Check this */ + s->adb_reply_size = 1; + s->adb_reply[0] = 0x00; + } + + s->intbits |= PMU_INT_ADB; + pmu_update_extirq(s); +} + +static void pmu_cmd_adb_poll_off(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: ADB POLL OFF command, invalid len: %d want: 0\n", + in_len); + return; + } + + if (s->has_adb && s->autopoll_mask) { + timer_del(s->adb_poll_timer); + s->autopoll_mask = false; + } +} + +static void pmu_cmd_shutdown(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SHUTDOWN command, invalid len: %d want: 4\n", + in_len); + return; + } + + *out_len = 1; + out_data[0] = 0; + + if (in_data[0] != 'M' || in_data[1] != 'A' || in_data[2] != 'T' || + in_data[3] != 'T') { + + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SHUTDOWN command, Bad MATT signature\n"); + return; + } + + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); +} + +static void pmu_cmd_reset(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: RESET command, invalid len: %d want: 0\n", + in_len); + return; + } + + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); +} + +static void pmu_cmd_get_rtc(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + uint32_t ti; + + if (in_len != 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: GET_RTC command, invalid len: %d want: 0\n", + in_len); + return; + } + + ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / NANOSECONDS_PER_SECOND); + out_data[0] = ti >> 24; + out_data[1] = ti >> 16; + out_data[2] = ti >> 8; + out_data[3] = ti; + *out_len = 4; +} + +static void pmu_cmd_set_rtc(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + uint32_t ti; + + if (in_len != 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: SET_RTC command, invalid len: %d want: 4\n", + in_len); + return; + } + + ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16) + + (((uint32_t)in_data[2]) << 8) + in_data[3]; + + s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / NANOSECONDS_PER_SECOND); +} + +static void pmu_cmd_system_ready(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + /* Do nothing */ +} + +static void pmu_cmd_get_version(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + *out_len = 1; + *out_data = 1; /* ??? Check what Apple does */ +} + +static void pmu_cmd_power_events(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len < 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: POWER EVENTS command, invalid len %d, want at least 1\n", + in_len); + return; + } + + switch (in_data[0]) { + /* Dummies for now */ + case PMU_PWR_GET_POWERUP_EVENTS: + *out_len = 2; + out_data[0] = 0; + out_data[1] = 0; + break; + case PMU_PWR_SET_POWERUP_EVENTS: + case PMU_PWR_CLR_POWERUP_EVENTS: + break; + case PMU_PWR_GET_WAKEUP_EVENTS: + *out_len = 2; + out_data[0] = 0; + out_data[1] = 0; + break; + case PMU_PWR_SET_WAKEUP_EVENTS: + case PMU_PWR_CLR_WAKEUP_EVENTS: + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: POWER EVENTS unknown subcommand 0x%02x\n", + in_data[0]); + } +} + +static void pmu_cmd_get_cover(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + /* Not 100% sure here, will have to check what a real Mac + * returns other than byte 0 bit 0 is LID closed on laptops + */ + *out_len = 1; + *out_data = 0x00; +} + +static void pmu_cmd_download_status(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + /* This has to do with PMU firmware updates as far as I can tell. + * + * We return 0x62 which is what OpenPMU expects + */ + *out_len = 1; + *out_data = 0x62; +} + +static void pmu_cmd_read_pmu_ram(PMUState *s, + const uint8_t *in_data, uint8_t in_len, + uint8_t *out_data, uint8_t *out_len) +{ + if (in_len < 3) { + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: READ_PMU_RAM command, invalid len %d, expected 3\n", + in_len); + return; + } + + qemu_log_mask(LOG_GUEST_ERROR, + "PMU: Unsupported READ_PMU_RAM, args: %02x %02x %02x\n", + in_data[0], in_data[1], in_data[2]); + + *out_len = 0; +} + +/* description of commands */ +typedef struct PMUCmdHandler { + uint8_t command; + const char *name; + void (*handler)(PMUState *s, + const uint8_t *in_args, uint8_t in_len, + uint8_t *out_args, uint8_t *out_len); +} PMUCmdHandler; + +static const PMUCmdHandler PMUCmdHandlers[] = { + { PMU_INT_ACK, "INT ACK", pmu_cmd_int_ack }, + { PMU_SET_INTR_MASK, "SET INT MASK", pmu_cmd_set_int_mask }, + { PMU_ADB_CMD, "ADB COMMAND", pmu_cmd_adb }, + { PMU_ADB_POLL_OFF, "ADB POLL OFF", pmu_cmd_adb_poll_off }, + { PMU_RESET, "REBOOT", pmu_cmd_reset }, + { PMU_SHUTDOWN, "SHUTDOWN", pmu_cmd_shutdown }, + { PMU_READ_RTC, "GET RTC", pmu_cmd_get_rtc }, + { PMU_SET_RTC, "SET RTC", pmu_cmd_set_rtc }, + { PMU_SYSTEM_READY, "SYSTEM READY", pmu_cmd_system_ready }, + { PMU_GET_VERSION, "GET VERSION", pmu_cmd_get_version }, + { PMU_POWER_EVENTS, "POWER EVENTS", pmu_cmd_power_events }, + { PMU_GET_COVER, "GET_COVER", pmu_cmd_get_cover }, + { PMU_DOWNLOAD_STATUS, "DOWNLOAD STATUS", pmu_cmd_download_status }, + { PMU_READ_PMU_RAM, "READ PMGR RAM", pmu_cmd_read_pmu_ram }, +}; + +static void pmu_dispatch_cmd(PMUState *s) +{ + unsigned int i; + + /* No response by default */ + s->cmd_rsp_sz = 0; + + for (i = 0; i < ARRAY_SIZE(PMUCmdHandlers); i++) { + const PMUCmdHandler *desc = &PMUCmdHandlers[i]; + + if (desc->command != s->cmd) { + continue; + } + + trace_pmu_dispatch_cmd(desc->name); + desc->handler(s, s->cmd_buf, s->cmd_buf_pos, + s->cmd_rsp, &s->cmd_rsp_sz); + + if (s->rsplen != -1 && s->rsplen != s->cmd_rsp_sz) { + trace_pmu_debug_protocol_string("QEMU internal cmd resp mismatch!"); + } else { + trace_pmu_debug_protocol_resp_size(s->cmd_rsp_sz); + } + + return; + } + + trace_pmu_dispatch_unknown_cmd(s->cmd); + + /* Manufacture fake response with 0's */ + if (s->rsplen == -1) { + s->cmd_rsp_sz = 0; + } else { + s->cmd_rsp_sz = s->rsplen; + memset(s->cmd_rsp, 0, s->rsplen); + } +} + +static void pmu_update(PMUState *s) +{ + MOS6522PMUState *mps = &s->mos6522_pmu; + MOS6522State *ms = MOS6522(mps); + + /* Only react to changes in reg B */ + if (ms->b == s->last_b) { + return; + } + s->last_b = ms->b; + + /* Check the TREQ / TACK state */ + switch (ms->b & (TREQ | TACK)) { + case TREQ: + /* This is an ack release, handle it and bail out */ + ms->b |= TACK; + s->last_b = ms->b; + + trace_pmu_debug_protocol_string("handshake: TREQ high, setting TACK"); + return; + case TACK: + /* This is a valid request, handle below */ + break; + case TREQ | TACK: + /* This is an idle state */ + return; + default: + /* Invalid state, log and ignore */ + trace_pmu_debug_protocol_error(ms->b); + return; + } + + /* If we wanted to handle commands asynchronously, this is where + * we would delay the clearing of TACK until we are ready to send + * the response + */ + + /* We have a request, handshake TACK so we don't stay in + * an invalid state. If we were concurrent with the OS we + * should only do this after we grabbed the SR but that isn't + * a problem here. + */ + + trace_pmu_debug_protocol_clear_treq(s->cmd_state); + + ms->b &= ~TACK; + s->last_b = ms->b; + + /* Act according to state */ + switch (s->cmd_state) { + case pmu_state_idle: + if (!(ms->acr & SR_OUT)) { + trace_pmu_debug_protocol_string("protocol error! " + "state idle, ACR reading"); + break; + } + + s->cmd = ms->sr; + via_set_sr_int(s); + s->cmdlen = pmu_data_len[s->cmd][0]; + s->rsplen = pmu_data_len[s->cmd][1]; + s->cmd_buf_pos = 0; + s->cmd_rsp_pos = 0; + s->cmd_state = pmu_state_cmd; + + trace_pmu_debug_protocol_cmd(s->cmd, s->cmdlen, s->rsplen); + break; + + case pmu_state_cmd: + if (!(ms->acr & SR_OUT)) { + trace_pmu_debug_protocol_string("protocol error! " + "state cmd, ACR reading"); + break; + } + + if (s->cmdlen == -1) { + trace_pmu_debug_protocol_cmdlen(ms->sr); + + s->cmdlen = ms->sr; + if (s->cmdlen > sizeof(s->cmd_buf)) { + trace_pmu_debug_protocol_cmd_toobig(s->cmdlen); + } + } else if (s->cmd_buf_pos < sizeof(s->cmd_buf)) { + s->cmd_buf[s->cmd_buf_pos++] = ms->sr; + } + + via_set_sr_int(s); + break; + + case pmu_state_rsp: + if (ms->acr & SR_OUT) { + trace_pmu_debug_protocol_string("protocol error! " + "state resp, ACR writing"); + break; + } + + if (s->rsplen == -1) { + trace_pmu_debug_protocol_cmd_send_resp_size(s->cmd_rsp_sz); + + ms->sr = s->cmd_rsp_sz; + s->rsplen = s->cmd_rsp_sz; + } else if (s->cmd_rsp_pos < s->cmd_rsp_sz) { + trace_pmu_debug_protocol_cmd_send_resp(s->cmd_rsp_pos, s->rsplen); + + ms->sr = s->cmd_rsp[s->cmd_rsp_pos++]; + } + + via_set_sr_int(s); + break; + } + + /* Check for state completion */ + if (s->cmd_state == pmu_state_cmd && s->cmdlen == s->cmd_buf_pos) { + trace_pmu_debug_protocol_string("Command reception complete, " + "dispatching..."); + + pmu_dispatch_cmd(s); + s->cmd_state = pmu_state_rsp; + } + + if (s->cmd_state == pmu_state_rsp && s->rsplen == s->cmd_rsp_pos) { + trace_pmu_debug_protocol_cmd_resp_complete(ms->ier); + + s->cmd_state = pmu_state_idle; + } +} + +static uint64_t mos6522_pmu_read(void *opaque, hwaddr addr, unsigned size) +{ + PMUState *s = opaque; + MOS6522PMUState *mps = &s->mos6522_pmu; + MOS6522State *ms = MOS6522(mps); + + addr = (addr >> 9) & 0xf; + return mos6522_read(ms, addr, size); +} + +static void mos6522_pmu_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PMUState *s = opaque; + MOS6522PMUState *mps = &s->mos6522_pmu; + MOS6522State *ms = MOS6522(mps); + + addr = (addr >> 9) & 0xf; + mos6522_write(ms, addr, val, size); +} + +static const MemoryRegionOps mos6522_pmu_ops = { + .read = mos6522_pmu_read, + .write = mos6522_pmu_write, + .endianness = DEVICE_BIG_ENDIAN, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +static bool pmu_adb_state_needed(void *opaque) +{ + PMUState *s = opaque; + + return s->has_adb; +} + +static const VMStateDescription vmstate_pmu_adb = { + .name = "pmu/adb", + .version_id = 0, + .minimum_version_id = 0, + .needed = pmu_adb_state_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT16(adb_poll_mask, PMUState), + VMSTATE_TIMER_PTR(adb_poll_timer, PMUState), + VMSTATE_UINT8(adb_reply_size, PMUState), + VMSTATE_BUFFER(adb_reply, PMUState), + } +}; + +static const VMStateDescription vmstate_pmu = { + .name = "pmu", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_STRUCT(mos6522_pmu.parent_obj, PMUState, 0, vmstate_mos6522, + MOS6522State), + VMSTATE_UINT8(last_b, PMUState), + VMSTATE_UINT8(cmd, PMUState), + VMSTATE_UINT32(cmdlen, PMUState), + VMSTATE_UINT32(rsplen, PMUState), + VMSTATE_UINT8(cmd_buf_pos, PMUState), + VMSTATE_BUFFER(cmd_buf, PMUState), + VMSTATE_UINT8(cmd_rsp_pos, PMUState), + VMSTATE_UINT8(cmd_rsp_sz, PMUState), + VMSTATE_BUFFER(cmd_rsp, PMUState), + VMSTATE_UINT8(intbits, PMUState), + VMSTATE_UINT8(intmask, PMUState), + VMSTATE_UINT8(autopoll_rate_ms, PMUState), + VMSTATE_UINT8(autopoll_mask, PMUState), + VMSTATE_UINT32(tick_offset, PMUState), + VMSTATE_TIMER_PTR(one_sec_timer, PMUState), + VMSTATE_INT64(one_sec_target, PMUState), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_pmu_adb, + } +}; + +static void pmu_reset(DeviceState *dev) +{ + PMUState *s = VIA_PMU(dev); + + /* OpenBIOS needs to do this? MacOS 9 needs it */ + s->intmask = PMU_INT_ADB | PMU_INT_TICK; + s->intbits = 0; + + s->cmd_state = pmu_state_idle; + s->autopoll_mask = 0; +} + +static void pmu_realize(DeviceState *dev, Error **errp) +{ + PMUState *s = VIA_PMU(dev); + SysBusDevice *sbd; + MOS6522State *ms; + DeviceState *d; + struct tm tm; + + /* Pass IRQ from 6522 */ + d = DEVICE(&s->mos6522_pmu); + ms = MOS6522(d); + sbd = SYS_BUS_DEVICE(s); + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(ms)); + + qemu_get_timedate(&tm, 0); + s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; + s->one_sec_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_one_sec_timer, s); + s->one_sec_target = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000; + timer_mod(s->one_sec_timer, s->one_sec_target); + + if (s->has_adb) { + qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, + DEVICE(dev), "adb.0"); + s->adb_poll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_adb_poll, s); + s->adb_poll_mask = 0xffff; + s->autopoll_rate_ms = 20; + } +} + +static void pmu_init(Object *obj) +{ + SysBusDevice *d = SYS_BUS_DEVICE(obj); + PMUState *s = VIA_PMU(obj); + + object_property_add_link(obj, "gpio", TYPE_MACIO_GPIO, + (Object **) &s->gpio, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + sysbus_init_child_obj(obj, "mos6522-pmu", &s->mos6522_pmu, + sizeof(s->mos6522_pmu), TYPE_MOS6522_PMU); + + memory_region_init_io(&s->mem, obj, &mos6522_pmu_ops, s, "via-pmu", + 0x2000); + sysbus_init_mmio(d, &s->mem); +} + +static Property pmu_properties[] = { + DEFINE_PROP_BOOL("has-adb", PMUState, has_adb, true), + DEFINE_PROP_END_OF_LIST() +}; + +static void pmu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = pmu_realize; + dc->reset = pmu_reset; + dc->vmsd = &vmstate_pmu; + dc->props = pmu_properties; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo pmu_type_info = { + .name = TYPE_VIA_PMU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PMUState), + .instance_init = pmu_init, + .class_init = pmu_class_init, +}; + +static void mos6522_pmu_portB_write(MOS6522State *s) +{ + MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); + PMUState *ps = container_of(mps, PMUState, mos6522_pmu); + + if ((s->pcr & 0xe0) == 0x20 || (s->pcr & 0xe0) == 0x60) { + s->ifr &= ~CB2_INT; + } + s->ifr &= ~CB1_INT; + + via_update_irq(ps); + pmu_update(ps); +} + +static void mos6522_pmu_portA_write(MOS6522State *s) +{ + MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); + PMUState *ps = container_of(mps, PMUState, mos6522_pmu); + + if ((s->pcr & 0x0e) == 0x02 || (s->pcr & 0x0e) == 0x06) { + s->ifr &= ~CA2_INT; + } + s->ifr &= ~CA1_INT; + + via_update_irq(ps); +} + +static void mos6522_pmu_reset(DeviceState *dev) +{ + MOS6522State *ms = MOS6522(dev); + MOS6522PMUState *mps = container_of(ms, MOS6522PMUState, parent_obj); + PMUState *s = container_of(mps, PMUState, mos6522_pmu); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); + + mdc->parent_reset(dev); + + ms->timers[0].frequency = VIA_TIMER_FREQ; + ms->timers[1].frequency = (SCALE_US * 6000) / 4700; + + s->last_b = ms->b = TACK | TREQ; +} + +static void mos6522_pmu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); + + dc->reset = mos6522_pmu_reset; + mdc->portB_write = mos6522_pmu_portB_write; + mdc->portA_write = mos6522_pmu_portA_write; +} + +static const TypeInfo mos6522_pmu_type_info = { + .name = TYPE_MOS6522_PMU, + .parent = TYPE_MOS6522, + .instance_size = sizeof(MOS6522PMUState), + .class_init = mos6522_pmu_class_init, +}; + +static void pmu_register_types(void) +{ + type_register_static(&pmu_type_info); + type_register_static(&mos6522_pmu_type_info); +} + +type_init(pmu_register_types) diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events index 24c0a36824..05019262fa 100644 --- a/hw/misc/macio/trace-events +++ b/hw/misc/macio/trace-events @@ -9,3 +9,35 @@ cuda_packet_receive(int len) "length %d" cuda_packet_receive_data(int i, const uint8_t data) "[%d] 0x%02x" cuda_packet_send(int len) "length %d" cuda_packet_send_data(int i, const uint8_t data) "[%d] 0x%02x" + +# hw/misc/macio/macio.c +macio_timer_write(uint64_t addr, unsigned len, uint64_t val) "write addr 0x%"PRIx64 " len %d val 0x%"PRIx64 +macio_timer_read(uint64_t addr, unsigned len, uint32_t val) "read addr 0x%"PRIx64 " len %d val 0x%"PRIx32 + +# hw/misc/macio/gpio.c +macio_set_gpio(int gpio, bool state) "setting GPIO %d to %d" +macio_gpio_irq_assert(int gpio) "asserting GPIO %d" +macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d" +macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 +macio_gpio_read(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 + +# hw/misc/macio/pmu.c +pmu_adb_poll(int olen) "ADB autopoll, olen=%d" +pmu_one_sec_timer(void) "PMU one sec..." +pmu_cmd_set_int_mask(int intmask) "Setting PMU int mask to 0x%02x" +pmu_cmd_set_adb_autopoll(int mask) "ADB set autopoll, mask=0x%04x" +pmu_cmd_adb_nobus(void) "ADB PACKET with no ADB bus!" +pmu_cmd_adb_request(int inlen, int indata0, int indata1, int indata2, int indata3, int indata4) "ADB request: len=%d, cmd=0x%02x, pflags=0x%02x, adblen=%d: 0x%02x 0x%02x..." +pmu_cmd_adb_reply(int len) "ADB reply is %d bytes" +pmu_dispatch_cmd(const char *name) "handling command %s" +pmu_dispatch_unknown_cmd(int cmd) "Unknown PMU command 0x%02x" +pmu_debug_protocol_string(const char *str) "%s" +pmu_debug_protocol_resp_size(int size) "sending %d resp bytes" +pmu_debug_protocol_error(int portB) "protocol error! portB=0x%02x" +pmu_debug_protocol_clear_treq(int state) "TREQ cleared, clearing TACK, state: %d" +pmu_debug_protocol_cmd(int cmd, int cmdlen, int rsplen) "Got command byte 0x%02x, clen=%d, rlen=%d" +pmu_debug_protocol_cmdlen(int len) "got cmd length byte: %d" +pmu_debug_protocol_cmd_toobig(int len) "command too big (%d bytes)" +pmu_debug_protocol_cmd_send_resp_size(int len) "sending length byte: %d" +pmu_debug_protocol_cmd_send_resp(int pos, int len) "sending byte: %d/%d" +pmu_debug_protocol_cmd_resp_complete(int ier) "Response send complete. IER=0x%02x" diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index c84a48bbb7..43bbec46cf 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -18,13 +18,11 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/log.h" #include "qapi/error.h" #include "cpu.h" -#include "qemu/log.h" #include "exec/exec-all.h" -#include "hw/hw.h" -#include "hw/sysbus.h" -#include "sysemu/sysemu.h" #include "hw/misc/mips_itu.h" #define ITC_TAG_ADDRSPACE_SZ (ITC_ADDRESSMAP_NUM * 8) @@ -83,7 +81,7 @@ static void itc_reconfigure(MIPSITUState *tag) uint64_t *am = &tag->ITCAddressMap[0]; MemoryRegion *mr = &tag->storage_io; hwaddr address = am[0] & ITC_AM0_BASE_ADDRESS_MASK; - uint64_t size = (1 << 10) + (am[1] & ITC_AM1_ADDR_MASK_MASK); + uint64_t size = (1 * KiB) + (am[1] & ITC_AM1_ADDR_MASK_MASK); bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0; memory_region_transaction_begin(); diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 8ad9fc831e..14cff26c61 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -40,7 +40,7 @@ static void mos6522_timer_update(MOS6522State *s, MOS6522Timer *ti, static void mos6522_update_irq(MOS6522State *s) { - if (s->ifr & s->ier & (SR_INT | T1_INT | T2_INT)) { + if (s->ifr & s->ier) { qemu_irq_raise(s->irq); } else { qemu_irq_lower(s->irq); @@ -176,12 +176,8 @@ static void mos6522_set_sr_int(MOS6522State *s) static uint64_t mos6522_get_counter_value(MOS6522State *s, MOS6522Timer *ti) { - uint64_t d; - - d = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ti->load_time, - ti->frequency, NANOSECONDS_PER_SECOND); - - return d; + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ti->load_time, + ti->frequency, NANOSECONDS_PER_SECOND); } static uint64_t mos6522_get_load_time(MOS6522State *s, MOS6522Timer *ti) @@ -193,12 +189,12 @@ static uint64_t mos6522_get_load_time(MOS6522State *s, MOS6522Timer *ti) static void mos6522_portA_write(MOS6522State *s) { - qemu_log_mask(LOG_UNIMP, "portA_write unimplemented"); + qemu_log_mask(LOG_UNIMP, "portA_write unimplemented\n"); } static void mos6522_portB_write(MOS6522State *s) { - qemu_log_mask(LOG_UNIMP, "portB_write unimplemented"); + qemu_log_mask(LOG_UNIMP, "portB_write unimplemented\n"); } uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) @@ -245,7 +241,7 @@ uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size) break; case VIA_REG_SR: val = s->sr; - s->ifr &= ~(SR_INT | CB1_INT | CB2_INT); + s->ifr &= ~SR_INT; mos6522_update_irq(s); break; case VIA_REG_ACR: @@ -373,13 +369,6 @@ static const MemoryRegionOps mos6522_ops = { }, }; -static bool mos6522_timer_exist(void *opaque, int version_id) -{ - MOS6522Timer *s = opaque; - - return s->timer != NULL; -} - static const VMStateDescription vmstate_mos6522_timer = { .name = "mos6522_timer", .version_id = 0, @@ -389,12 +378,12 @@ static const VMStateDescription vmstate_mos6522_timer = { VMSTATE_UINT16(counter_value, MOS6522Timer), VMSTATE_INT64(load_time, MOS6522Timer), VMSTATE_INT64(next_irq_time, MOS6522Timer), - VMSTATE_TIMER_PTR_TEST(timer, MOS6522Timer, mos6522_timer_exist), + VMSTATE_TIMER_PTR(timer, MOS6522Timer), VMSTATE_END_OF_LIST() } }; -static const VMStateDescription vmstate_mos6522 = { +const VMStateDescription vmstate_mos6522 = { .name = "mos6522", .version_id = 0, .minimum_version_id = 0, @@ -409,7 +398,7 @@ static const VMStateDescription vmstate_mos6522 = { VMSTATE_UINT8(ifr, MOS6522State), VMSTATE_UINT8(ier, MOS6522State), VMSTATE_UINT8(anh, MOS6522State), - VMSTATE_STRUCT_ARRAY(timers, MOS6522State, 2, 1, + VMSTATE_STRUCT_ARRAY(timers, MOS6522State, 2, 0, vmstate_mos6522_timer, MOS6522Timer), VMSTATE_END_OF_LIST() } @@ -431,18 +420,12 @@ static void mos6522_reset(DeviceState *dev) /* s->ier = T1_INT | SR_INT; */ s->anh = 0; + s->timers[0].frequency = s->frequency; s->timers[0].latch = 0xffff; set_counter(s, &s->timers[0], 0xffff); - s->timers[1].latch = 0xffff; -} - -static void mos6522_realize(DeviceState *dev, Error **errp) -{ - MOS6522State *s = MOS6522(dev); - - s->timers[0].frequency = s->frequency; s->timers[1].frequency = s->frequency; + s->timers[1].latch = 0xffff; } static void mos6522_init(Object *obj) @@ -473,14 +456,14 @@ static void mos6522_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); - dc->realize = mos6522_realize; dc->reset = mos6522_reset; dc->vmsd = &vmstate_mos6522; dc->props = mos6522_properties; - mdc->parent_realize = dc->realize; + mdc->parent_reset = dc->reset; mdc->set_sr_int = mos6522_set_sr_int; mdc->portB_write = mos6522_portB_write; mdc->portA_write = mos6522_portA_write; + mdc->update_irq = mos6522_update_irq; mdc->get_timer1_counter_value = mos6522_get_counter_value; mdc->get_timer2_counter_value = mos6522_get_counter_value; mdc->get_timer1_load_time = mos6522_get_load_time; diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c new file mode 100644 index 0000000000..9775d5274a --- /dev/null +++ b/hw/misc/pca9552.c @@ -0,0 +1,240 @@ +/* + * PCA9552 I2C LED blinker + * + * https://www.nxp.com/docs/en/application-note/AN264.pdf + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/hw.h" +#include "hw/misc/pca9552.h" +#include "hw/misc/pca9552_regs.h" + +#define PCA9552_LED_ON 0x0 +#define PCA9552_LED_OFF 0x1 +#define PCA9552_LED_PWM0 0x2 +#define PCA9552_LED_PWM1 0x3 + +static uint8_t pca9552_pin_get_config(PCA9552State *s, int pin) +{ + uint8_t reg = PCA9552_LS0 + (pin / 4); + uint8_t shift = (pin % 4) << 1; + + return extract32(s->regs[reg], shift, 2); +} + +static void pca9552_update_pin_input(PCA9552State *s) +{ + int i; + + for (i = 0; i < s->nr_leds; i++) { + uint8_t input_reg = PCA9552_INPUT0 + (i / 8); + uint8_t input_shift = (i % 8); + uint8_t config = pca9552_pin_get_config(s, i); + + switch (config) { + case PCA9552_LED_ON: + s->regs[input_reg] |= 1 << input_shift; + break; + case PCA9552_LED_OFF: + s->regs[input_reg] &= ~(1 << input_shift); + break; + case PCA9552_LED_PWM0: + case PCA9552_LED_PWM1: + /* TODO */ + default: + break; + } + } +} + +static uint8_t pca9552_read(PCA9552State *s, uint8_t reg) +{ + switch (reg) { + case PCA9552_INPUT0: + case PCA9552_INPUT1: + case PCA9552_PSC0: + case PCA9552_PWM0: + case PCA9552_PSC1: + case PCA9552_PWM1: + case PCA9552_LS0: + case PCA9552_LS1: + case PCA9552_LS2: + case PCA9552_LS3: + return s->regs[reg]; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected read to register %d\n", + __func__, reg); + return 0xFF; + } +} + +static void pca9552_write(PCA9552State *s, uint8_t reg, uint8_t data) +{ + switch (reg) { + case PCA9552_PSC0: + case PCA9552_PWM0: + case PCA9552_PSC1: + case PCA9552_PWM1: + s->regs[reg] = data; + break; + + case PCA9552_LS0: + case PCA9552_LS1: + case PCA9552_LS2: + case PCA9552_LS3: + s->regs[reg] = data; + pca9552_update_pin_input(s); + break; + + case PCA9552_INPUT0: + case PCA9552_INPUT1: + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: unexpected write to register %d\n", + __func__, reg); + } +} + +/* + * When Auto-Increment is on, the register address is incremented + * after each byte is sent to or received by the device. The index + * rollovers to 0 when the maximum register address is reached. + */ +static void pca9552_autoinc(PCA9552State *s) +{ + if (s->pointer != 0xFF && s->pointer & PCA9552_AUTOINC) { + uint8_t reg = s->pointer & 0xf; + + reg = (reg + 1) % (s->max_reg + 1); + s->pointer = reg | PCA9552_AUTOINC; + } +} + +static int pca9552_recv(I2CSlave *i2c) +{ + PCA9552State *s = PCA9552(i2c); + uint8_t ret; + + ret = pca9552_read(s, s->pointer & 0xf); + + /* + * From the Specs: + * + * Important Note: When a Read sequence is initiated and the + * AI bit is set to Logic Level 1, the Read Sequence MUST + * start by a register different from 0. + * + * I don't know what should be done in this case, so throw an + * error. + */ + if (s->pointer == PCA9552_AUTOINC) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Autoincrement read starting with register 0\n", + __func__); + } + + pca9552_autoinc(s); + + return ret; +} + +static int pca9552_send(I2CSlave *i2c, uint8_t data) +{ + PCA9552State *s = PCA9552(i2c); + + /* First byte sent by is the register address */ + if (s->len == 0) { + s->pointer = data; + s->len++; + } else { + pca9552_write(s, s->pointer & 0xf, data); + + pca9552_autoinc(s); + } + + return 0; +} + +static int pca9552_event(I2CSlave *i2c, enum i2c_event event) +{ + PCA9552State *s = PCA9552(i2c); + + s->len = 0; + return 0; +} + +static const VMStateDescription pca9552_vmstate = { + .name = "PCA9552", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT8(len, PCA9552State), + VMSTATE_UINT8(pointer, PCA9552State), + VMSTATE_UINT8_ARRAY(regs, PCA9552State, PCA9552_NR_REGS), + VMSTATE_I2C_SLAVE(i2c, PCA9552State), + VMSTATE_END_OF_LIST() + } +}; + +static void pca9552_reset(DeviceState *dev) +{ + PCA9552State *s = PCA9552(dev); + + s->regs[PCA9552_PSC0] = 0xFF; + s->regs[PCA9552_PWM0] = 0x80; + s->regs[PCA9552_PSC1] = 0xFF; + s->regs[PCA9552_PWM1] = 0x80; + s->regs[PCA9552_LS0] = 0x55; /* all OFF */ + s->regs[PCA9552_LS1] = 0x55; + s->regs[PCA9552_LS2] = 0x55; + s->regs[PCA9552_LS3] = 0x55; + + pca9552_update_pin_input(s); + + s->pointer = 0xFF; + s->len = 0; +} + +static void pca9552_initfn(Object *obj) +{ + PCA9552State *s = PCA9552(obj); + + /* If support for the other PCA955X devices are implemented, these + * constant values might be part of class structure describing the + * PCA955X device + */ + s->max_reg = PCA9552_LS3; + s->nr_leds = 16; +} + +static void pca9552_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + k->event = pca9552_event; + k->recv = pca9552_recv; + k->send = pca9552_send; + dc->reset = pca9552_reset; + dc->vmsd = &pca9552_vmstate; +} + +static const TypeInfo pca9552_info = { + .name = TYPE_PCA9552, + .parent = TYPE_I2C_SLAVE, + .instance_init = pca9552_initfn, + .instance_size = sizeof(PCA9552State), + .class_init = pca9552_class_init, +}; + +static void pca9552_register_types(void) +{ + type_register_static(&pca9552_info); +} + +type_init(pca9552_register_types) diff --git a/hw/misc/sga.c b/hw/misc/sga.c index 97fd63f176..4a22a52a60 100644 --- a/hw/misc/sga.c +++ b/hw/misc/sga.c @@ -25,7 +25,7 @@ * */ #include "qemu/osdep.h" -#include "hw/pci/pci.h" +#include "hw/isa/isa.h" #include "hw/loader.h" #include "sysemu/sysemu.h" diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c index 9e22d64e36..0918f3a6ea 100644 --- a/hw/misc/tmp105.c +++ b/hw/misc/tmp105.c @@ -229,15 +229,14 @@ static void tmp105_reset(I2CSlave *i2c) tmp105_interrupt_update(s); } -static int tmp105_init(I2CSlave *i2c) +static void tmp105_realize(DeviceState *dev, Error **errp) { + I2CSlave *i2c = I2C_SLAVE(dev); TMP105State *s = TMP105(i2c); qdev_init_gpio_out(&i2c->qdev, &s->pin, 1); tmp105_reset(&s->i2c); - - return 0; } static void tmp105_initfn(Object *obj) @@ -252,7 +251,7 @@ static void tmp105_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = tmp105_init; + dc->realize = tmp105_realize; k->event = tmp105_event; k->recv = tmp105_rx; k->send = tmp105_tx; diff --git a/hw/misc/tmp421.c b/hw/misc/tmp421.c index 4a505abbce..c234044305 100644 --- a/hw/misc/tmp421.c +++ b/hw/misc/tmp421.c @@ -335,13 +335,11 @@ static void tmp421_reset(I2CSlave *i2c) s->status = 0; } -static int tmp421_init(I2CSlave *i2c) +static void tmp421_realize(DeviceState *dev, Error **errp) { - TMP421State *s = TMP421(i2c); + TMP421State *s = TMP421(dev); tmp421_reset(&s->i2c); - - return 0; } static void tmp421_initfn(Object *obj) @@ -366,7 +364,7 @@ static void tmp421_class_init(ObjectClass *klass, void *data) I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); TMP421Class *sc = TMP421_CLASS(klass); - k->init = tmp421_init; + dc->realize = tmp421_realize; k->event = tmp421_event; k->recv = tmp421_rx; k->send = tmp421_tx; diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 562d9ed005..c956e1419b 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -69,13 +69,13 @@ mps2_fpgaio_reset(void) "MPS2 FPGAIO: reset" mps2_fpgaio_leds(char led1, char led0) "MPS2 FPGAIO LEDs: %c%c" # hw/misc/msf2-sysreg.c -msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" HWADDR_PRIx " data 0x%" PRIx32 " prev 0x%" PRIx32 -msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" HWADDR_PRIx " data 0x%08" PRIx32 +msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" PRIx64 " data 0x%" PRIx32 " prev 0x%" PRIx32 +msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" PRIx64 " data 0x%08" PRIx32 msf2_sysreg_write_pll_status(void) "Invalid write to read only PLL status register" #hw/misc/imx7_gpr.c -imx7_gpr_read(uint64_t offset) "addr 0x%08" HWADDR_PRIx -imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" HWADDR_PRIx "value 0x%08" HWADDR_PRIx +imx7_gpr_read(uint64_t offset) "addr 0x%08" PRIx64 +imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx64 # hw/misc/mos6522.c mos6522_set_counter(int index, unsigned int val) "T%d.counter=%d" @@ -84,6 +84,14 @@ mos6522_set_sr_int(void) "set sr_int" mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x" +# hw/misc/tz-mpc.c +tz_mpc_reg_read(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs read: offset 0x%x data 0x%" PRIx64 " size %u" +tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs write: offset 0x%x data 0x%" PRIx64 " size %u" +tz_mpc_mem_blocked_read(uint64_t addr, unsigned size, bool secure) "TZ MPC blocked read: offset 0x%" PRIx64 " size %u secure %d" +tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secure) "TZ MPC blocked write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" +tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s" +tz_mpc_iommu_notify(uint64_t addr) "TZ MPC iommu: notifying UNMAP/MAP for 0x%" PRIx64 + # hw/misc/tz-ppc.c tz_ppc_reset(void) "TZ PPC: reset" tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d" diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c new file mode 100644 index 0000000000..e0c58ba37e --- /dev/null +++ b/hw/misc/tz-mpc.c @@ -0,0 +1,628 @@ +/* + * ARM AHB5 TrustZone Memory Protection Controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/tz-mpc.h" + +/* Our IOMMU has two IOMMU indexes, one for secure transactions and one for + * non-secure transactions. + */ +enum { + IOMMU_IDX_S, + IOMMU_IDX_NS, + IOMMU_NUM_INDEXES, +}; + +/* Config registers */ +REG32(CTRL, 0x00) + FIELD(CTRL, SEC_RESP, 4, 1) + FIELD(CTRL, AUTOINC, 8, 1) + FIELD(CTRL, LOCKDOWN, 31, 1) +REG32(BLK_MAX, 0x10) +REG32(BLK_CFG, 0x14) +REG32(BLK_IDX, 0x18) +REG32(BLK_LUT, 0x1c) +REG32(INT_STAT, 0x20) + FIELD(INT_STAT, IRQ, 0, 1) +REG32(INT_CLEAR, 0x24) + FIELD(INT_CLEAR, IRQ, 0, 1) +REG32(INT_EN, 0x28) + FIELD(INT_EN, IRQ, 0, 1) +REG32(INT_INFO1, 0x2c) +REG32(INT_INFO2, 0x30) + FIELD(INT_INFO2, HMASTER, 0, 16) + FIELD(INT_INFO2, HNONSEC, 16, 1) + FIELD(INT_INFO2, CFG_NS, 17, 1) +REG32(INT_SET, 0x34) + FIELD(INT_SET, IRQ, 0, 1) +REG32(PIDR4, 0xfd0) +REG32(PIDR5, 0xfd4) +REG32(PIDR6, 0xfd8) +REG32(PIDR7, 0xfdc) +REG32(PIDR0, 0xfe0) +REG32(PIDR1, 0xfe4) +REG32(PIDR2, 0xfe8) +REG32(PIDR3, 0xfec) +REG32(CIDR0, 0xff0) +REG32(CIDR1, 0xff4) +REG32(CIDR2, 0xff8) +REG32(CIDR3, 0xffc) + +static const uint8_t tz_mpc_idregs[] = { + 0x04, 0x00, 0x00, 0x00, + 0x60, 0xb8, 0x1b, 0x00, + 0x0d, 0xf0, 0x05, 0xb1, +}; + +static void tz_mpc_irq_update(TZMPC *s) +{ + qemu_set_irq(s->irq, s->int_stat && s->int_en); +} + +static void tz_mpc_iommu_notify(TZMPC *s, uint32_t lutidx, + uint32_t oldlut, uint32_t newlut) +{ + /* Called when the LUT word at lutidx has changed from oldlut to newlut; + * must call the IOMMU notifiers for the changed blocks. + */ + IOMMUTLBEntry entry = { + .addr_mask = s->blocksize - 1, + }; + hwaddr addr = lutidx * s->blocksize * 32; + int i; + + for (i = 0; i < 32; i++, addr += s->blocksize) { + bool block_is_ns; + + if (!((oldlut ^ newlut) & (1 << i))) { + continue; + } + /* This changes the mappings for both the S and the NS space, + * so we need to do four notifies: an UNMAP then a MAP for each. + */ + block_is_ns = newlut & (1 << i); + + trace_tz_mpc_iommu_notify(addr); + entry.iova = addr; + entry.translated_addr = addr; + + entry.perm = IOMMU_NONE; + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); + + entry.perm = IOMMU_RW; + if (block_is_ns) { + entry.target_as = &s->blocked_io_as; + } else { + entry.target_as = &s->downstream_as; + } + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_S, entry); + if (block_is_ns) { + entry.target_as = &s->downstream_as; + } else { + entry.target_as = &s->blocked_io_as; + } + memory_region_notify_iommu(&s->upstream, IOMMU_IDX_NS, entry); + } +} + +static void tz_mpc_autoinc_idx(TZMPC *s, unsigned access_size) +{ + /* Auto-increment BLK_IDX if necessary */ + if (access_size == 4 && (s->ctrl & R_CTRL_AUTOINC_MASK)) { + s->blk_idx++; + s->blk_idx %= s->blk_max; + } +} + +static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, + uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + TZMPC *s = TZ_MPC(opaque); + uint64_t r; + uint32_t offset = addr & ~0x3; + + if (!attrs.secure && offset < A_PIDR4) { + /* NS accesses can only see the ID registers */ + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register read: NS access to offset 0x%x\n", + offset); + r = 0; + goto read_out; + } + + switch (offset) { + case A_CTRL: + r = s->ctrl; + break; + case A_BLK_MAX: + r = s->blk_max; + break; + case A_BLK_CFG: + /* We are never in "init in progress state", so this just indicates + * the block size. s->blocksize == (1 << BLK_CFG + 5), so + * BLK_CFG == ctz32(s->blocksize) - 5 + */ + r = ctz32(s->blocksize) - 5; + break; + case A_BLK_IDX: + r = s->blk_idx; + break; + case A_BLK_LUT: + r = s->blk_lut[s->blk_idx]; + tz_mpc_autoinc_idx(s, size); + break; + case A_INT_STAT: + r = s->int_stat; + break; + case A_INT_EN: + r = s->int_en; + break; + case A_INT_INFO1: + r = s->int_info1; + break; + case A_INT_INFO2: + r = s->int_info2; + break; + case A_PIDR4: + case A_PIDR5: + case A_PIDR6: + case A_PIDR7: + case A_PIDR0: + case A_PIDR1: + case A_PIDR2: + case A_PIDR3: + case A_CIDR0: + case A_CIDR1: + case A_CIDR2: + case A_CIDR3: + r = tz_mpc_idregs[(offset - A_PIDR4) / 4]; + break; + case A_INT_CLEAR: + case A_INT_SET: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register read: write-only offset 0x%x\n", + offset); + r = 0; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register read: bad offset 0x%x\n", offset); + r = 0; + break; + } + + if (size != 4) { + /* None of our registers are read-sensitive (except BLK_LUT, + * which can special case the "size not 4" case), so just + * pull the right bytes out of the word read result. + */ + r = extract32(r, (addr & 3) * 8, size * 8); + } + +read_out: + trace_tz_mpc_reg_read(addr, r, size); + *pdata = r; + return MEMTX_OK; +} + +static MemTxResult tz_mpc_reg_write(void *opaque, hwaddr addr, + uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + TZMPC *s = TZ_MPC(opaque); + uint32_t offset = addr & ~0x3; + + trace_tz_mpc_reg_write(addr, value, size); + + if (!attrs.secure && offset < A_PIDR4) { + /* NS accesses can only see the ID registers */ + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register write: NS access to offset 0x%x\n", + offset); + return MEMTX_OK; + } + + if (size != 4) { + /* Expand the byte or halfword write to a full word size. + * In most cases we can do this with zeroes; the exceptions + * are CTRL, BLK_IDX and BLK_LUT. + */ + uint32_t oldval; + + switch (offset) { + case A_CTRL: + oldval = s->ctrl; + break; + case A_BLK_IDX: + oldval = s->blk_idx; + break; + case A_BLK_LUT: + oldval = s->blk_lut[s->blk_idx]; + break; + default: + oldval = 0; + break; + } + value = deposit32(oldval, (addr & 3) * 8, size * 8, value); + } + + if ((s->ctrl & R_CTRL_LOCKDOWN_MASK) && + (offset == A_CTRL || offset == A_BLK_LUT || offset == A_INT_EN)) { + /* Lockdown mode makes these three registers read-only, and + * the only way out of it is to reset the device. + */ + qemu_log_mask(LOG_GUEST_ERROR, "TZ MPC register write to offset 0x%x " + "while MPC is in lockdown mode\n", offset); + return MEMTX_OK; + } + + switch (offset) { + case A_CTRL: + /* We don't implement the 'data gating' feature so all other bits + * are reserved and we make them RAZ/WI. + */ + s->ctrl = value & (R_CTRL_SEC_RESP_MASK | + R_CTRL_AUTOINC_MASK | + R_CTRL_LOCKDOWN_MASK); + break; + case A_BLK_IDX: + s->blk_idx = value % s->blk_max; + break; + case A_BLK_LUT: + tz_mpc_iommu_notify(s, s->blk_idx, s->blk_lut[s->blk_idx], value); + s->blk_lut[s->blk_idx] = value; + tz_mpc_autoinc_idx(s, size); + break; + case A_INT_CLEAR: + if (value & R_INT_CLEAR_IRQ_MASK) { + s->int_stat = 0; + tz_mpc_irq_update(s); + } + break; + case A_INT_EN: + s->int_en = value & R_INT_EN_IRQ_MASK; + tz_mpc_irq_update(s); + break; + case A_INT_SET: + if (value & R_INT_SET_IRQ_MASK) { + s->int_stat = R_INT_STAT_IRQ_MASK; + tz_mpc_irq_update(s); + } + break; + case A_PIDR4: + case A_PIDR5: + case A_PIDR6: + case A_PIDR7: + case A_PIDR0: + case A_PIDR1: + case A_PIDR2: + case A_PIDR3: + case A_CIDR0: + case A_CIDR1: + case A_CIDR2: + case A_CIDR3: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register write: read-only offset 0x%x\n", offset); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "TZ MPC register write: bad offset 0x%x\n", offset); + break; + } + + return MEMTX_OK; +} + +static const MemoryRegionOps tz_mpc_reg_ops = { + .read_with_attrs = tz_mpc_reg_read, + .write_with_attrs = tz_mpc_reg_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, +}; + +static inline bool tz_mpc_cfg_ns(TZMPC *s, hwaddr addr) +{ + /* Return the cfg_ns bit from the LUT for the specified address */ + hwaddr blknum = addr / s->blocksize; + hwaddr blkword = blknum / 32; + uint32_t blkbit = 1U << (blknum % 32); + + /* This would imply the address was larger than the size we + * defined this memory region to be, so it can't happen. + */ + assert(blkword < s->blk_max); + return s->blk_lut[blkword] & blkbit; +} + +static MemTxResult tz_mpc_handle_block(TZMPC *s, hwaddr addr, MemTxAttrs attrs) +{ + /* Handle a blocked transaction: raise IRQ, capture info, etc */ + if (!s->int_stat) { + /* First blocked transfer: capture information into INT_INFO1 and + * INT_INFO2. Subsequent transfers are still blocked but don't + * capture information until the guest clears the interrupt. + */ + + s->int_info1 = addr; + s->int_info2 = 0; + s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HMASTER, + attrs.requester_id & 0xffff); + s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, HNONSEC, + ~attrs.secure); + s->int_info2 = FIELD_DP32(s->int_info2, INT_INFO2, CFG_NS, + tz_mpc_cfg_ns(s, addr)); + s->int_stat |= R_INT_STAT_IRQ_MASK; + tz_mpc_irq_update(s); + } + + /* Generate bus error if desired; otherwise RAZ/WI */ + return (s->ctrl & R_CTRL_SEC_RESP_MASK) ? MEMTX_ERROR : MEMTX_OK; +} + +/* Accesses only reach these read and write functions if the MPC is + * blocking them; non-blocked accesses go directly to the downstream + * memory region without passing through this code. + */ +static MemTxResult tz_mpc_mem_blocked_read(void *opaque, hwaddr addr, + uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + TZMPC *s = TZ_MPC(opaque); + + trace_tz_mpc_mem_blocked_read(addr, size, attrs.secure); + + *pdata = 0; + return tz_mpc_handle_block(s, addr, attrs); +} + +static MemTxResult tz_mpc_mem_blocked_write(void *opaque, hwaddr addr, + uint64_t value, + unsigned size, MemTxAttrs attrs) +{ + TZMPC *s = TZ_MPC(opaque); + + trace_tz_mpc_mem_blocked_write(addr, value, size, attrs.secure); + + return tz_mpc_handle_block(s, addr, attrs); +} + +static const MemoryRegionOps tz_mpc_mem_blocked_ops = { + .read_with_attrs = tz_mpc_mem_blocked_read, + .write_with_attrs = tz_mpc_mem_blocked_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 8, + .impl.min_access_size = 1, + .impl.max_access_size = 8, +}; + +static IOMMUTLBEntry tz_mpc_translate(IOMMUMemoryRegion *iommu, + hwaddr addr, IOMMUAccessFlags flags, + int iommu_idx) +{ + TZMPC *s = TZ_MPC(container_of(iommu, TZMPC, upstream)); + bool ok; + + IOMMUTLBEntry ret = { + .iova = addr & ~(s->blocksize - 1), + .translated_addr = addr & ~(s->blocksize - 1), + .addr_mask = s->blocksize - 1, + .perm = IOMMU_RW, + }; + + /* Look at the per-block configuration for this address, and + * return a TLB entry directing the transaction at either + * downstream_as or blocked_io_as, as appropriate. + * If the LUT cfg_ns bit is 1, only non-secure transactions + * may pass. If the bit is 0, only secure transactions may pass. + */ + ok = tz_mpc_cfg_ns(s, addr) == (iommu_idx == IOMMU_IDX_NS); + + trace_tz_mpc_translate(addr, flags, + iommu_idx == IOMMU_IDX_S ? "S" : "NS", + ok ? "pass" : "block"); + + ret.target_as = ok ? &s->downstream_as : &s->blocked_io_as; + return ret; +} + +static int tz_mpc_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs) +{ + /* We treat unspecified attributes like secure. Transactions with + * unspecified attributes come from places like + * cpu_physical_memory_write_rom() for initial image load, and we want + * those to pass through the from-reset "everything is secure" config. + * All the real during-emulation transactions from the CPU will + * specify attributes. + */ + return (attrs.unspecified || attrs.secure) ? IOMMU_IDX_S : IOMMU_IDX_NS; +} + +static int tz_mpc_num_indexes(IOMMUMemoryRegion *iommu) +{ + return IOMMU_NUM_INDEXES; +} + +static void tz_mpc_reset(DeviceState *dev) +{ + TZMPC *s = TZ_MPC(dev); + + s->ctrl = 0x00000100; + s->blk_idx = 0; + s->int_stat = 0; + s->int_en = 1; + s->int_info1 = 0; + s->int_info2 = 0; + + memset(s->blk_lut, 0, s->blk_max * sizeof(uint32_t)); +} + +static void tz_mpc_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + TZMPC *s = TZ_MPC(obj); + + qdev_init_gpio_out_named(dev, &s->irq, "irq", 1); +} + +static void tz_mpc_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + TZMPC *s = TZ_MPC(dev); + uint64_t size; + + /* We can't create the upstream end of the port until realize, + * as we don't know the size of the MR used as the downstream until then. + * We insist on having a downstream, to avoid complicating the code + * with handling the "don't know how big this is" case. It's easy + * enough for the user to create an unimplemented_device as downstream + * if they have nothing else to plug into this. + */ + if (!s->downstream) { + error_setg(errp, "MPC 'downstream' link not set"); + return; + } + + size = memory_region_size(s->downstream); + + memory_region_init_iommu(&s->upstream, sizeof(s->upstream), + TYPE_TZ_MPC_IOMMU_MEMORY_REGION, + obj, "tz-mpc-upstream", size); + + /* In real hardware the block size is configurable. In QEMU we could + * make it configurable but will need it to be at least as big as the + * target page size so we can execute out of the resulting MRs. Guest + * software is supposed to check the block size using the BLK_CFG + * register, so make it fixed at the page size. + */ + s->blocksize = memory_region_iommu_get_min_page_size(&s->upstream); + if (size % s->blocksize != 0) { + error_setg(errp, + "MPC 'downstream' size %" PRId64 + " is not a multiple of %" HWADDR_PRIx " bytes", + size, s->blocksize); + object_unref(OBJECT(&s->upstream)); + return; + } + + /* BLK_MAX is the max value of BLK_IDX, which indexes an array of 32-bit + * words, each bit of which indicates one block. + */ + s->blk_max = DIV_ROUND_UP(size / s->blocksize, 32); + + memory_region_init_io(&s->regmr, obj, &tz_mpc_reg_ops, + s, "tz-mpc-regs", 0x1000); + sysbus_init_mmio(sbd, &s->regmr); + + sysbus_init_mmio(sbd, MEMORY_REGION(&s->upstream)); + + /* This memory region is not exposed to users of this device as a + * sysbus MMIO region, but is instead used internally as something + * that our IOMMU translate function might direct accesses to. + */ + memory_region_init_io(&s->blocked_io, obj, &tz_mpc_mem_blocked_ops, + s, "tz-mpc-blocked-io", size); + + address_space_init(&s->downstream_as, s->downstream, + "tz-mpc-downstream"); + address_space_init(&s->blocked_io_as, &s->blocked_io, + "tz-mpc-blocked-io"); + + s->blk_lut = g_new0(uint32_t, s->blk_max); +} + +static int tz_mpc_post_load(void *opaque, int version_id) +{ + TZMPC *s = TZ_MPC(opaque); + + /* Check the incoming data doesn't point blk_idx off the end of blk_lut. */ + if (s->blk_idx >= s->blk_max) { + return -1; + } + return 0; +} + +static const VMStateDescription tz_mpc_vmstate = { + .name = "tz-mpc", + .version_id = 1, + .minimum_version_id = 1, + .post_load = tz_mpc_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ctrl, TZMPC), + VMSTATE_UINT32(blk_idx, TZMPC), + VMSTATE_UINT32(int_stat, TZMPC), + VMSTATE_UINT32(int_en, TZMPC), + VMSTATE_UINT32(int_info1, TZMPC), + VMSTATE_UINT32(int_info2, TZMPC), + VMSTATE_VARRAY_UINT32(blk_lut, TZMPC, blk_max, + 0, vmstate_info_uint32, uint32_t), + VMSTATE_END_OF_LIST() + } +}; + +static Property tz_mpc_properties[] = { + DEFINE_PROP_LINK("downstream", TZMPC, downstream, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tz_mpc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = tz_mpc_realize; + dc->vmsd = &tz_mpc_vmstate; + dc->reset = tz_mpc_reset; + dc->props = tz_mpc_properties; +} + +static const TypeInfo tz_mpc_info = { + .name = TYPE_TZ_MPC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TZMPC), + .instance_init = tz_mpc_init, + .class_init = tz_mpc_class_init, +}; + +static void tz_mpc_iommu_memory_region_class_init(ObjectClass *klass, + void *data) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); + + imrc->translate = tz_mpc_translate; + imrc->attrs_to_index = tz_mpc_attrs_to_index; + imrc->num_indexes = tz_mpc_num_indexes; +} + +static const TypeInfo tz_mpc_iommu_memory_region_info = { + .name = TYPE_TZ_MPC_IOMMU_MEMORY_REGION, + .parent = TYPE_IOMMU_MEMORY_REGION, + .class_init = tz_mpc_iommu_memory_region_class_init, +}; + +static void tz_mpc_register_types(void) +{ + type_register_static(&tz_mpc_info); + type_register_static(&tz_mpc_iommu_memory_region_info); +} + +type_init(tz_mpc_register_types); diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c index 0bbf770795..d41247dbdc 100644 --- a/hw/moxie/moxiesim.c +++ b/hw/moxie/moxiesim.c @@ -141,9 +141,9 @@ static void moxiesim_init(MachineState *machine) } /* A single 16450 sits at offset 0x3f8. */ - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(address_space_mem, 0x3f8, 0, env->irq[4], - 8000000/16, serial_hds[0], DEVICE_LITTLE_ENDIAN); + 8000000/16, serial_hd(0), DEVICE_LITTLE_ENDIAN); } } diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index ab22968641..fa461d4463 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -36,7 +36,7 @@ obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o obj-$(CONFIG_PSERIES) += spapr_llan.o obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o -obj-$(CONFIG_VIRTIO) += virtio-net.o +obj-$(CONFIG_VIRTIO_NET) += virtio-net.o obj-y += vhost_net.o obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index f2d2ce344c..b53fcaa8bc 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -887,7 +887,7 @@ static void dp8393x_realize(DeviceState *dev, Error **errp) s->watchdog = timer_new_ns(QEMU_CLOCK_VIRTUAL, dp8393x_watchdog, s); s->regs[SONIC_SR] = 0x0004; /* only revision recognized by Linux */ - memory_region_init_ram_nomigrate(&s->prom, OBJECT(dev), + memory_region_init_ram(&s->prom, OBJECT(dev), "dp8393x-prom", SONIC_PROM_SIZE, &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 16a9417a85..510ddb3897 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -34,6 +34,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "net/net.h" #include "net/tap.h" #include "qemu/range.h" @@ -41,7 +42,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" -#include "hw/net/e1000_regs.h" +#include "e1000_regs.h" #include "e1000x_common.h" #include "e1000e_core.h" @@ -81,10 +82,10 @@ typedef struct E1000EState { #define E1000E_IO_IDX 2 #define E1000E_MSIX_IDX 3 -#define E1000E_MMIO_SIZE (128 * 1024) -#define E1000E_FLASH_SIZE (128 * 1024) +#define E1000E_MMIO_SIZE (128 * KiB) +#define E1000E_FLASH_SIZE (128 * KiB) #define E1000E_IO_SIZE (32) -#define E1000E_MSIX_SIZE (16 * 1024) +#define E1000E_MSIX_SIZE (16 * KiB) #define E1000E_MSIX_TABLE (0x0000) #define E1000E_MSIX_PBA (0x2000) diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index c93c4661ed..2a221c2ef9 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -2022,11 +2022,8 @@ e1000e_msix_notify_one(E1000ECore *core, uint32_t cause, uint32_t int_cfg) effective_eiac = core->mac[EIAC] & cause; - if (effective_eiac == E1000_ICR_OTHER) { - effective_eiac |= E1000_ICR_OTHER_CAUSES; - } - core->mac[ICR] &= ~effective_eiac; + core->msi_causes_pending &= ~effective_eiac; if (!(core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { core->mac[IMS] &= ~effective_eiac; @@ -2123,6 +2120,13 @@ e1000e_send_msi(E1000ECore *core, bool msix) { uint32_t causes = core->mac[ICR] & core->mac[IMS] & ~E1000_ICR_ASSERTED; + core->msi_causes_pending &= causes; + causes ^= core->msi_causes_pending; + if (causes == 0) { + return; + } + core->msi_causes_pending |= causes; + if (msix) { e1000e_msix_notify(core, causes); } else { @@ -2160,6 +2164,9 @@ e1000e_update_interrupt_state(E1000ECore *core) core->mac[ICS] = core->mac[ICR]; interrupts_pending = (core->mac[IMS] & core->mac[ICR]) ? true : false; + if (!interrupts_pending) { + core->msi_causes_pending = 0; + } trace_e1000e_irq_pending_interrupts(core->mac[ICR] & core->mac[IMS], core->mac[ICR], core->mac[IMS]); diff --git a/hw/net/e1000e_core.h b/hw/net/e1000e_core.h index 7d8ff41890..63a15510cc 100644 --- a/hw/net/e1000e_core.h +++ b/hw/net/e1000e_core.h @@ -109,6 +109,8 @@ struct E1000Core { NICState *owner_nic; PCIDevice *owner; void (*owner_start_recv)(PCIDevice *d); + + uint32_t msi_causes_pending; }; void diff --git a/hw/net/e1000x_common.c b/hw/net/e1000x_common.c index eb0e097137..09047806f2 100644 --- a/hw/net/e1000x_common.c +++ b/hw/net/e1000x_common.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -111,7 +112,7 @@ bool e1000x_is_oversized(uint32_t *mac, size_t size) static const int maximum_ethernet_vlan_size = 1522; /* this is the size past which hardware will drop packets when setting LPE=1 */ - static const int maximum_ethernet_lpe_size = 16384; + static const int maximum_ethernet_lpe_size = 16 * KiB; if ((size > maximum_ethernet_lpe_size || (size > maximum_ethernet_vlan_size diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index a07a63247e..e761daf551 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -41,6 +41,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/pci/pci.h" #include "net/net.h" @@ -60,8 +61,6 @@ * changed to pad short packets itself. */ #define CONFIG_PAD_RECEIVED_FRAMES -#define KiB 1024 - /* Debug EEPRO100 card. */ #if 0 # define DEBUG_EEPRO100 diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c index 013c8d0a41..a6932432b1 100644 --- a/hw/net/etraxfs_eth.c +++ b/hw/net/etraxfs_eth.c @@ -27,6 +27,7 @@ #include "net/net.h" #include "hw/cris/etraxfs.h" #include "qemu/error-report.h" +#include "trace.h" #define D(x) @@ -106,7 +107,7 @@ static unsigned int tdk_read(struct qemu_phy *phy, unsigned int req) r = phy->regs[regnum]; break; } - D(printf("\n%s %x = reg[%d]\n", __func__, r, regnum)); + trace_mdio_phy_read(regnum, r); return r; } @@ -116,7 +117,7 @@ tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) int regnum; regnum = req & 0x1f; - D(printf("%s reg[%d] = %x\n", __func__, regnum, data)); + trace_mdio_phy_write(regnum, data); switch (regnum) { default: phy->regs[regnum] = data; @@ -206,8 +207,7 @@ static void mdio_cycle(struct qemu_mdio *bus) { bus->cnt++; - D(printf("mdc=%d mdio=%d state=%d cnt=%d drv=%d\n", - bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive)); + trace_mdio_bitbang(bus->mdc, bus->mdio, bus->state, bus->cnt, bus->drive); #if 0 if (bus->mdc) { printf("%d", bus->mdio); diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index 9da1932970..0b66274ce3 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -49,6 +49,28 @@ static const int debug_etsec; } \ } while (0) +/* call after any change to IEVENT or IMASK */ +void etsec_update_irq(eTSEC *etsec) +{ + uint32_t ievent = etsec->regs[IEVENT].value; + uint32_t imask = etsec->regs[IMASK].value; + uint32_t active = ievent & imask; + + int tx = !!(active & IEVENT_TX_MASK); + int rx = !!(active & IEVENT_RX_MASK); + int err = !!(active & IEVENT_ERR_MASK); + + DPRINTF("%s IRQ ievent=%"PRIx32" imask=%"PRIx32" %c%c%c", + __func__, ievent, imask, + tx ? 'T' : '_', + rx ? 'R' : '_', + err ? 'E' : '_'); + + qemu_set_irq(etsec->tx_irq, tx); + qemu_set_irq(etsec->rx_irq, rx); + qemu_set_irq(etsec->err_irq, err); +} + static uint64_t etsec_read(void *opaque, hwaddr addr, unsigned size) { eTSEC *etsec = opaque; @@ -139,31 +161,6 @@ static void write_rbasex(eTSEC *etsec, etsec->regs[RBPTR0 + (reg_index - RBASE0)].value = value & ~0x7; } -static void write_ievent(eTSEC *etsec, - eTSEC_Register *reg, - uint32_t reg_index, - uint32_t value) -{ - /* Write 1 to clear */ - reg->value &= ~value; - - if (!(reg->value & (IEVENT_TXF | IEVENT_TXF))) { - qemu_irq_lower(etsec->tx_irq); - } - if (!(reg->value & (IEVENT_RXF | IEVENT_RXF))) { - qemu_irq_lower(etsec->rx_irq); - } - - if (!(reg->value & (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | - IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | - IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | - IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | - IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | - IEVENT_MMRW))) { - qemu_irq_lower(etsec->err_irq); - } -} - static void write_dmactrl(eTSEC *etsec, eTSEC_Register *reg, uint32_t reg_index, @@ -178,9 +175,7 @@ static void write_dmactrl(eTSEC *etsec, } else { /* Graceful receive stop now */ etsec->regs[IEVENT].value |= IEVENT_GRSC; - if (etsec->regs[IMASK].value & IMASK_GRSCEN) { - qemu_irq_raise(etsec->err_irq); - } + etsec_update_irq(etsec); } } @@ -191,9 +186,7 @@ static void write_dmactrl(eTSEC *etsec, } else { /* Graceful transmit stop now */ etsec->regs[IEVENT].value |= IEVENT_GTSC; - if (etsec->regs[IMASK].value & IMASK_GTSCEN) { - qemu_irq_raise(etsec->err_irq); - } + etsec_update_irq(etsec); } } @@ -222,7 +215,16 @@ static void etsec_write(void *opaque, switch (reg_index) { case IEVENT: - write_ievent(etsec, reg, reg_index, value); + /* Write 1 to clear */ + reg->value &= ~value; + + etsec_update_irq(etsec); + break; + + case IMASK: + reg->value = value; + + etsec_update_irq(etsec); break; case DMACTRL: @@ -337,6 +339,8 @@ static void etsec_reset(DeviceState *d) MII_SR_EXTENDED_STATUS | MII_SR_100T2_HD_CAPS | MII_SR_100T2_FD_CAPS | MII_SR_10T_HD_CAPS | MII_SR_10T_FD_CAPS | MII_SR_100X_HD_CAPS | MII_SR_100X_FD_CAPS | MII_SR_100T4_CAPS; + + etsec_update_irq(etsec); } static ssize_t etsec_receive(NetClientState *nc, diff --git a/hw/net/fsl_etsec/etsec.h b/hw/net/fsl_etsec/etsec.h index 30c828e241..877988572e 100644 --- a/hw/net/fsl_etsec/etsec.h +++ b/hw/net/fsl_etsec/etsec.h @@ -163,6 +163,8 @@ DeviceState *etsec_create(hwaddr base, qemu_irq rx_irq, qemu_irq err_irq); +void etsec_update_irq(eTSEC *etsec); + void etsec_walk_tx_ring(eTSEC *etsec, int ring_nbr); void etsec_walk_rx_ring(eTSEC *etsec, int ring_nbr); ssize_t etsec_rx_ring_write(eTSEC *etsec, const uint8_t *buf, size_t size); diff --git a/hw/net/fsl_etsec/registers.h b/hw/net/fsl_etsec/registers.h index c4ed2b9d62..f085537ecd 100644 --- a/hw/net/fsl_etsec/registers.h +++ b/hw/net/fsl_etsec/registers.h @@ -74,6 +74,16 @@ extern const eTSEC_Register_Definition eTSEC_registers_def[]; #define IEVENT_RXC (1 << 30) #define IEVENT_BABR (1 << 31) +/* Mapping between interrupt pin and interrupt flags */ +#define IEVENT_RX_MASK (IEVENT_RXF | IEVENT_RXB) +#define IEVENT_TX_MASK (IEVENT_TXF | IEVENT_TXB) +#define IEVENT_ERR_MASK (IEVENT_MAG | IEVENT_GTSC | IEVENT_GRSC | IEVENT_TXC | \ + IEVENT_RXC | IEVENT_BABR | IEVENT_BABT | IEVENT_LC | \ + IEVENT_CRL | IEVENT_FGPI | IEVENT_FIR | IEVENT_FIQ | \ + IEVENT_DPE | IEVENT_PERR | IEVENT_EBERR | IEVENT_TXE | \ + IEVENT_XFUN | IEVENT_BSY | IEVENT_MSRO | IEVENT_MMRD | \ + IEVENT_MMRW) + #define IMASK_RXFEN (1 << 7) #define IMASK_GRSCEN (1 << 8) #define IMASK_RXBEN (1 << 15) diff --git a/hw/net/fsl_etsec/rings.c b/hw/net/fsl_etsec/rings.c index d0f93eebfc..337a55fc95 100644 --- a/hw/net/fsl_etsec/rings.c +++ b/hw/net/fsl_etsec/rings.c @@ -152,17 +152,7 @@ static void ievent_set(eTSEC *etsec, { etsec->regs[IEVENT].value |= flags; - if ((flags & IEVENT_TXB && etsec->regs[IMASK].value & IMASK_TXBEN) - || (flags & IEVENT_TXF && etsec->regs[IMASK].value & IMASK_TXFEN)) { - qemu_irq_raise(etsec->tx_irq); - RING_DEBUG("%s Raise Tx IRQ\n", __func__); - } - - if ((flags & IEVENT_RXB && etsec->regs[IMASK].value & IMASK_RXBEN) - || (flags & IEVENT_RXF && etsec->regs[IMASK].value & IMASK_RXFEN)) { - qemu_irq_raise(etsec->rx_irq); - RING_DEBUG("%s Raise Rx IRQ\n", __func__); - } + etsec_update_irq(etsec); } static void tx_padding_and_crc(eTSEC *etsec, uint32_t min_frame_len) diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 704f452067..909c1182ee 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -207,16 +207,18 @@ typedef struct { /* * Max frame size for the receiving buffer */ -#define FTGMAC100_MAX_FRAME_SIZE 10240 +#define FTGMAC100_MAX_FRAME_SIZE 9220 /* Limits depending on the type of the frame * * 9216 for Jumbo frames (+ 4 for VLAN) * 1518 for other frames (+ 4 for VLAN) */ -static int ftgmac100_max_frame_size(FTGMAC100State *s) +static int ftgmac100_max_frame_size(FTGMAC100State *s, uint16_t proto) { - return (s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518) + 4; + int max = (s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518); + + return max + (proto == ETH_P_VLAN ? 4 : 0); } static void ftgmac100_update_irq(FTGMAC100State *s) @@ -408,7 +410,6 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, uint8_t *ptr = s->frame; uint32_t addr = tx_descriptor; uint32_t flags = 0; - int max_frame_size = ftgmac100_max_frame_size(s); while (1) { FTGMAC100Desc bd; @@ -427,11 +428,12 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, flags = bd.des1; } - len = bd.des0 & 0x3FFF; - if (frame_size + len > max_frame_size) { + len = FTGMAC100_TXDES0_TXBUF_SIZE(bd.des0); + if (frame_size + len > sizeof(s->frame)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %d bytes\n", __func__, len); - len = max_frame_size - frame_size; + s->isr |= FTGMAC100_INT_XPKT_LOST; + len = sizeof(s->frame) - frame_size; } if (dma_memory_read(&address_space_memory, bd.des3, ptr, len)) { @@ -441,6 +443,22 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring, break; } + /* Check for VLAN */ + if (bd.des0 & FTGMAC100_TXDES0_FTS && + bd.des1 & FTGMAC100_TXDES1_INS_VLANTAG && + be16_to_cpu(PKT_GET_ETH_HDR(ptr)->h_proto) != ETH_P_VLAN) { + if (frame_size + len + 4 > sizeof(s->frame)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %d bytes\n", + __func__, len); + s->isr |= FTGMAC100_INT_XPKT_LOST; + len = sizeof(s->frame) - frame_size - 4; + } + memmove(ptr + 16, ptr + 12, len - 12); + stw_be_p(ptr + 12, ETH_P_VLAN); + stw_be_p(ptr + 14, bd.des1); + len += 4; + } + ptr += len; frame_size += len; if (bd.des0 & FTGMAC100_TXDES0_LTS) { @@ -511,7 +529,6 @@ static uint32_t ftgmac100_rxpoll(FTGMAC100State *s) uint32_t cnt = 1024 * FTGMAC100_APTC_RXPOLL_CNT(s->aptcr); uint32_t speed = (s->maccr & FTGMAC100_MACCR_FAST_MODE) ? 1 : 0; - uint32_t period; if (s->aptcr & FTGMAC100_APTC_RXPOLL_TIME_SEL) { cnt <<= 4; @@ -521,9 +538,7 @@ static uint32_t ftgmac100_rxpoll(FTGMAC100State *s) speed = 2; } - period = cnt / div[speed]; - - return period; + return cnt / div[speed]; } static void ftgmac100_reset(DeviceState *d) @@ -761,8 +776,8 @@ static int ftgmac100_filter(FTGMAC100State *s, const uint8_t *buf, size_t len) return 0; } - /* TODO: this does not seem to work for ftgmac100 */ - mcast_idx = net_crc32(buf, ETH_ALEN) >> 26; + mcast_idx = net_crc32_le(buf, ETH_ALEN); + mcast_idx = (~(mcast_idx >> 2)) & 0x3f; if (!(s->math[mcast_idx / 32] & (1 << (mcast_idx % 32)))) { return 0; } @@ -791,7 +806,8 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, uint32_t buf_len; size_t size = len; uint32_t first = FTGMAC100_RXDES0_FRS; - int max_frame_size = ftgmac100_max_frame_size(s); + uint16_t proto = be16_to_cpu(PKT_GET_ETH_HDR(buf)->h_proto); + int max_frame_size = ftgmac100_max_frame_size(s, proto); if ((s->maccr & (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) != (FTGMAC100_MACCR_RXDMA_EN | FTGMAC100_MACCR_RXMAC_EN)) { @@ -806,12 +822,6 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, return size; } - if (size < 64 && !(s->maccr & FTGMAC100_MACCR_RX_RUNT)) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: dropped runt frame of %zd bytes\n", - __func__, size); - return size; - } - if (!ftgmac100_filter(s, buf, size)) { return size; } @@ -823,9 +833,9 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, /* Huge frames are truncated. */ if (size > max_frame_size) { - size = max_frame_size; qemu_log_mask(LOG_GUEST_ERROR, "%s: frame too big : %zd bytes\n", __func__, size); + size = max_frame_size; flags |= FTGMAC100_RXDES0_FTL; } @@ -864,7 +874,20 @@ static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf, buf_len += size - 4; } buf_addr = bd.des3; - dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + if (first && proto == ETH_P_VLAN && buf_len >= 18) { + bd.des1 = lduw_be_p(buf + 14) | FTGMAC100_RXDES1_VLANTAG_AVAIL; + + if (s->maccr & FTGMAC100_MACCR_RM_VLAN) { + dma_memory_write(&address_space_memory, buf_addr, buf, 12); + dma_memory_write(&address_space_memory, buf_addr + 12, buf + 16, + buf_len - 16); + } else { + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + } + } else { + bd.des1 = 0; + dma_memory_write(&address_space_memory, buf_addr, buf, buf_len); + } buf += buf_len; if (size < 4) { dma_memory_write(&address_space_memory, buf_addr + buf_len, @@ -943,8 +966,6 @@ static void ftgmac100_realize(DeviceState *dev, Error **errp) object_get_typename(OBJECT(dev)), DEVICE(dev)->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - s->frame = g_malloc(FTGMAC100_MAX_FRAME_SIZE); } static const VMStateDescription vmstate_ftgmac100 = { diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index bfa6b4bcce..0091e4ecdd 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -14,7 +14,6 @@ #include "hw/sysbus.h" /* For crc32 */ #include -#include "exec/address-spaces.h" //#define DEBUG_FEC 1 diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 70e5c1d3d4..c7fdeb0f6c 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -27,7 +27,6 @@ #include "hw/qdev.h" #include "ne2000.h" #include "sysemu/sysemu.h" -#include "exec/address-spaces.h" #include "qapi/error.h" #include "qapi/visitor.h" diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 3a9fc89e48..07d79e317f 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -26,6 +26,7 @@ #include "net/eth.h" #include "ne2000.h" #include "sysemu/sysemu.h" +#include "trace.h" /* debug NE2000 card */ //#define DEBUG_NE2000 @@ -276,9 +277,7 @@ static void ne2000_ioport_write(void *opaque, uint32_t addr, uint32_t val) int offset, page, index; addr &= 0xf; -#ifdef DEBUG_NE2000 - printf("NE2000: write addr=0x%x val=0x%02x\n", addr, val); -#endif + trace_ne2000_ioport_write(addr, val); if (addr == E8390_CMD) { /* control register */ s->cmd = val; @@ -441,9 +440,7 @@ static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) break; } } -#ifdef DEBUG_NE2000 - printf("NE2000: read addr=0x%x val=%02x\n", addr, ret); -#endif + trace_ne2000_ioport_read(addr, ret); return ret; } @@ -662,19 +659,24 @@ static uint64_t ne2000_read(void *opaque, hwaddr addr, unsigned size) { NE2000State *s = opaque; + uint64_t val; if (addr < 0x10 && size == 1) { - return ne2000_ioport_read(s, addr); + val = ne2000_ioport_read(s, addr); } else if (addr == 0x10) { if (size <= 2) { - return ne2000_asic_ioport_read(s, addr); + val = ne2000_asic_ioport_read(s, addr); } else { - return ne2000_asic_ioport_readl(s, addr); + val = ne2000_asic_ioport_readl(s, addr); } } else if (addr == 0x1f && size == 1) { - return ne2000_reset_ioport_read(s, addr); + val = ne2000_reset_ioport_read(s, addr); + } else { + val = ((uint64_t)1 << (size * 8)) - 1; } - return ((uint64_t)1 << (size * 8)) - 1; + trace_ne2000_read(addr, val); + + return val; } static void ne2000_write(void *opaque, hwaddr addr, @@ -682,6 +684,7 @@ static void ne2000_write(void *opaque, hwaddr addr, { NE2000State *s = opaque; + trace_ne2000_write(addr, data); if (addr < 0x10 && size == 1) { ne2000_ioport_write(s, addr, data); } else if (addr == 0x10) { diff --git a/hw/net/ne2000.h b/hw/net/ne2000.h index adb8021bd1..2cd193e4c6 100644 --- a/hw/net/ne2000.h +++ b/hw/net/ne2000.h @@ -1,11 +1,12 @@ #ifndef HW_NE2000_H #define HW_NE2000_H +#include "qemu/units.h" #include "hw/hw.h" #include "net/net.h" -#define NE2000_PMEM_SIZE (32*1024) -#define NE2000_PMEM_START (16*1024) +#define NE2000_PMEM_SIZE (32 * KiB) +#define NE2000_PMEM_START (16 * KiB) #define NE2000_PMEM_END (NE2000_PMEM_SIZE+NE2000_PMEM_START) #define NE2000_MEM_SIZE NE2000_PMEM_END diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c index 27b17c890f..4aa7da79b8 100644 --- a/hw/net/rocker/rocker_fp.c +++ b/hw/net/rocker/rocker_fp.c @@ -15,7 +15,6 @@ */ #include "qemu/osdep.h" -#include "net/clients.h" #include "qapi/qapi-types-rocker.h" #include "rocker.h" #include "rocker_hw.h" diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index 60046720a5..8e347d1ee4 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -104,7 +104,7 @@ typedef struct of_dpa_flow_key { /* Width of key which includes field 'f' in u64s, rounded up */ #define FLOW_KEY_WIDTH(f) \ - DIV_ROUND_UP(offsetof(OfDpaFlowKey, f) + sizeof(((OfDpaFlowKey *)0)->f), \ + DIV_ROUND_UP(offsetof(OfDpaFlowKey, f) + sizeof_field(OfDpaFlowKey, f), \ sizeof(uint64_t)) typedef struct of_dpa_flow_action { diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 3b16dcf5a1..d2fd2040e8 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -11,6 +11,7 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/devices.h" +#include "qemu/log.h" /* For crc32 */ #include @@ -361,10 +362,14 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, SET_HIGH(gpr, value); return; case 12: /* Control */ - if (value & 1) - fprintf(stderr, "smc91c111:EEPROM store not implemented\n"); - if (value & 2) - fprintf(stderr, "smc91c111:EEPROM reload not implemented\n"); + if (value & 1) { + qemu_log_mask(LOG_UNIMP, + "smc91c111: EEPROM store not implemented\n"); + } + if (value & 2) { + qemu_log_mask(LOG_UNIMP, + "smc91c111: EEPROM reload not implemented\n"); + } value &= ~3; SET_LOW(ctr, value); return; @@ -478,7 +483,9 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, } break; } - hw_error("smc91c111_write: Bad reg %d:%x\n", s->bank, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "smc91c111_write(bank:%d) Illegal register" + " 0x%" HWADDR_PRIx " = 0x%x\n", + s->bank, offset, value); } static uint32_t smc91c111_readb(void *opaque, hwaddr offset) @@ -621,41 +628,39 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset) } break; } - hw_error("smc91c111_read: Bad reg %d:%x\n", s->bank, (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "smc91c111_read(bank:%d) Illegal register" + " 0x%" HWADDR_PRIx "\n", + s->bank, offset); return 0; } -static void smc91c111_writew(void *opaque, hwaddr offset, - uint32_t value) +static uint64_t smc91c111_readfn(void *opaque, hwaddr addr, unsigned size) { - smc91c111_writeb(opaque, offset, value & 0xff); - smc91c111_writeb(opaque, offset + 1, value >> 8); + int i; + uint32_t val = 0; + + for (i = 0; i < size; i++) { + val |= smc91c111_readb(opaque, addr + i) << (i * 8); + } + return val; } -static void smc91c111_writel(void *opaque, hwaddr offset, - uint32_t value) +static void smc91c111_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { + int i = 0; + /* 32-bit writes to offset 0xc only actually write to the bank select - register (offset 0xe) */ - if (offset != 0xc) - smc91c111_writew(opaque, offset, value & 0xffff); - smc91c111_writew(opaque, offset + 2, value >> 16); -} + * register (offset 0xe), so skip the first two bytes we would write. + */ + if (addr == 0xc && size == 4) { + i += 2; + } -static uint32_t smc91c111_readw(void *opaque, hwaddr offset) -{ - uint32_t val; - val = smc91c111_readb(opaque, offset); - val |= smc91c111_readb(opaque, offset + 1) << 8; - return val; -} - -static uint32_t smc91c111_readl(void *opaque, hwaddr offset) -{ - uint32_t val; - val = smc91c111_readw(opaque, offset); - val |= smc91c111_readw(opaque, offset + 2) << 16; - return val; + for (; i < size; i++) { + smc91c111_writeb(opaque, addr + i, + extract32(value, i * 8, 8)); + } } static int smc91c111_can_receive_nc(NetClientState *nc) @@ -747,10 +752,10 @@ static const MemoryRegionOps smc91c111_mem_ops = { /* The special case for 32 bit writes to 0xc means we can't just * set .impl.min/max_access_size to 1, unfortunately */ - .old_mmio = { - .read = { smc91c111_readb, smc91c111_readw, smc91c111_readl, }, - .write = { smc91c111_writeb, smc91c111_writew, smc91c111_writel, }, - }, + .read = smc91c111_readfn, + .write = smc91c111_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 04bd10ada3..165562d788 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "net/net.h" +#include "qemu/log.h" #include //#define DEBUG_STELLARIS_ENET 1 @@ -340,10 +341,12 @@ static uint64_t stellaris_enet_read(void *opaque, hwaddr offset, return s->np; case 0x38: /* TR */ return 0; - case 0x3c: /* Undocuented: Timestamp? */ + case 0x3c: /* Undocumented: Timestamp? */ return 0; default: - hw_error("stellaris_enet_read: Bad offset %x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "stellaris_enet_rd%d: Illegal register" + " 0x02%" HWADDR_PRIx "\n", + size * 8, offset); return 0; } } @@ -442,7 +445,9 @@ static void stellaris_enet_write(void *opaque, hwaddr offset, /* Ignored. */ break; default: - hw_error("stellaris_enet_write: Bad offset %x\n", (int)offset); + qemu_log_mask(LOG_GUEST_ERROR, "stellaris_enet_wr%d: Illegal register " + "0x02%" HWADDR_PRIx " = 0x%" PRIx64 "\n", + size * 8, offset, value); } } diff --git a/hw/net/trace-events b/hw/net/trace-events index 45c4e9fba0..663bea1b74 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -1,5 +1,10 @@ # See docs/devel/tracing.txt for syntax documentation. +# hw/net/etraxfs_eth.c +mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d value:0x%04x" +mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d value:0x%04x" +mdio_bitbang(bool mdc, bool mdio, int state, uint16_t cnt, unsigned int drive) "bitbang mdc=%u mdio=%u state=%d cnt=%u drv=%d" + # hw/net/lance.c lance_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64"val=0x%04x" lance_mem_writew(uint64_t addr, uint32_t val) "addr=0x%"PRIx64"val=0x%04x" @@ -23,6 +28,12 @@ mipsnet_read(uint64_t addr, uint32_t val) "read addr=0x%" PRIx64 " val=0x%x" mipsnet_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 mipsnet_irq(uint32_t isr, uint32_t intctl) "set irq to %d (0x%02x)" +# hw/net/ne2000.c +ne2000_read(uint64_t addr, uint64_t val) "read addr=0x%" PRIx64 " val=0x%" PRIx64 +ne2000_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 +ne2000_ioport_read(uint64_t addr, uint64_t val) "io read addr=0x%02" PRIx64 " val=0x%02" PRIx64 +ne2000_ioport_write(uint64_t addr, uint64_t val) "io write addr=0x%02" PRIx64 " val=0x%02" PRIx64 + # hw/net/opencores_eth.c open_eth_mii_write(unsigned idx, uint16_t v) "MII[0x%02x] <- 0x%04x" open_eth_mii_read(unsigned idx, uint16_t v) "MII[0x%02x] -> 0x%04x" diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 90502fca7c..f154756e85 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -46,7 +46,7 @@ * 'container'. */ #define endof(container, field) \ - (offsetof(container, field) + sizeof(((container *)0)->field)) + (offsetof(container, field) + sizeof_field(container, field)) typedef struct VirtIOFeature { uint64_t flags; diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 20c43a61b3..46a8dbfc90 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -160,9 +160,8 @@ static void net_tx_packets(struct XenNetDev *netdev) (txreq.flags & NETTXF_more_data) ? " more_data" : "", (txreq.flags & NETTXF_extra_info) ? " extra_info" : ""); - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - txreq.gref, PROT_READ); + page = xen_be_map_grant_ref(&netdev->xendev, txreq.gref, + PROT_READ); if (page == NULL) { xen_pv_printf(&netdev->xendev, 0, "error: tx gref dereference failed (%d)\n", @@ -183,7 +182,7 @@ static void net_tx_packets(struct XenNetDev *netdev) qemu_send_packet(qemu_get_queue(netdev->nic), page + txreq.offset, txreq.size); } - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); + xen_be_unmap_grant_ref(&netdev->xendev, page); net_tx_response(netdev, &txreq, NETIF_RSP_OKAY); } if (!netdev->tx_work) { @@ -254,9 +253,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq)); netdev->rx_ring.req_cons = ++rc; - page = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - rxreq.gref, PROT_WRITE); + page = xen_be_map_grant_ref(&netdev->xendev, rxreq.gref, PROT_WRITE); if (page == NULL) { xen_pv_printf(&netdev->xendev, 0, "error: rx gref dereference failed (%d)\n", @@ -265,7 +262,7 @@ static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size return -1; } memcpy(page + NET_IP_ALIGN, buf, size); - xengnttab_unmap(netdev->xendev.gnttabdev, page, 1); + xen_be_unmap_grant_ref(&netdev->xendev, page); net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0); return size; @@ -338,19 +335,17 @@ static int net_connect(struct XenDevice *xendev) return -1; } - netdev->txs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->tx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->txs = xen_be_map_grant_ref(&netdev->xendev, + netdev->tx_ring_ref, + PROT_READ | PROT_WRITE); if (!netdev->txs) { return -1; } - netdev->rxs = xengnttab_map_grant_ref(netdev->xendev.gnttabdev, - netdev->xendev.dom, - netdev->rx_ring_ref, - PROT_READ | PROT_WRITE); + netdev->rxs = xen_be_map_grant_ref(&netdev->xendev, + netdev->rx_ring_ref, + PROT_READ | PROT_WRITE); if (!netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); netdev->txs = NULL; return -1; } @@ -375,11 +370,11 @@ static void net_disconnect(struct XenDevice *xendev) xen_pv_unbind_evtchn(&netdev->xendev); if (netdev->txs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->txs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->txs); netdev->txs = NULL; } if (netdev->rxs) { - xengnttab_unmap(netdev->xendev.gnttabdev, netdev->rxs, 1); + xen_be_unmap_grant_ref(&netdev->xendev, netdev->rxs); netdev->rxs = NULL; } } diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index d4c2c89dc1..cc880a3d08 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -951,12 +951,12 @@ static void xilinx_enet_realize(DeviceState *dev, Error **errp) object_property_add_link(OBJECT(ds), "enet", "xlnx.axi-ethernet", (Object **) &ds->enet, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &local_err); object_property_add_link(OBJECT(cs), "enet", "xlnx.axi-ethernet", (Object **) &cs->enet, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &local_err); if (local_err) { goto xilinx_enet_realize_fail; diff --git a/hw/nios2/10m50_devboard.c b/hw/nios2/10m50_devboard.c index 42053b2ca9..36b49a420c 100644 --- a/hw/nios2/10m50_devboard.c +++ b/hw/nios2/10m50_devboard.c @@ -92,7 +92,7 @@ static void nios2_10m50_ghrd_init(MachineState *machine) /* Register: Altera 16550 UART */ serial_mm_init(address_space_mem, 0xf8001600, 2, irq[1], 115200, - serial_hds[0], DEVICE_NATIVE_ENDIAN); + serial_hd(0), DEVICE_NATIVE_ENDIAN); /* Register: Timer sys_clk_timer */ dev = qdev_create(NULL, "ALTR.timer"); diff --git a/hw/nios2/boot.c b/hw/nios2/boot.c index 94f436e7fb..4bb5b601d3 100644 --- a/hw/nios2/boot.c +++ b/hw/nios2/boot.c @@ -29,6 +29,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "cpu.h" #include "qemu/option.h" @@ -38,7 +39,6 @@ #include "sysemu/sysemu.h" #include "hw/loader.h" #include "elf.h" -#include "qemu/cutils.h" #include "boot.h" @@ -177,7 +177,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, high = ddr_base + kernel_size; } - high = ROUND_UP(high, 1024 * 1024); + high = ROUND_UP(high, 1 * MiB); /* If initrd is available, it goes after the kernel, aligned to 1M. */ if (initrd_filename) { @@ -213,7 +213,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, high += fdt_size; /* Kernel command is at the end, 4k aligned. */ - boot_info.cmdline = ROUND_UP(high, 4096); + boot_info.cmdline = ROUND_UP(high, 4 * KiB); if (kernel_cmdline && strlen(kernel_cmdline)) { pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); } diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 22183f5360..27cd01e615 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -116,31 +116,29 @@ int at24c_eeprom_send(I2CSlave *s, uint8_t data) return 0; } -static -int at24c_eeprom_init(I2CSlave *i2c) +static void at24c_eeprom_realize(DeviceState *dev, Error **errp) { - EEPROMState *ee = AT24C_EE(i2c); - - ee->mem = g_malloc0(ee->rsize); + EEPROMState *ee = AT24C_EE(dev); if (ee->blk) { int64_t len = blk_getlength(ee->blk); if (len != ee->rsize) { - ERR(TYPE_AT24C_EE " : Backing file size %lu != %u\n", - (unsigned long)len, (unsigned)ee->rsize); - exit(1); + error_setg(errp, "%s: Backing file size %" PRId64 " != %u", + TYPE_AT24C_EE, len, ee->rsize); + return; } if (blk_set_perm(ee->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, BLK_PERM_ALL, &error_fatal) < 0) { - ERR(TYPE_AT24C_EE - " : Backing file incorrect permission\n"); - exit(1); + error_setg(errp, "%s: Backing file incorrect permission", + TYPE_AT24C_EE); + return; } } - return 0; + + ee->mem = g_malloc0(ee->rsize); } static @@ -178,7 +176,7 @@ void at24c_eeprom_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); - k->init = &at24c_eeprom_init; + dc->realize = &at24c_eeprom_realize; k->event = &at24c_eeprom_event; k->recv = &at24c_eeprom_recv; k->send = &at24c_eeprom_send; diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 2a0739d0e9..b23e7f64a8 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -420,14 +420,16 @@ static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr, } static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return !is_write || ((size == 4 && (addr == 0 || addr == 4)) || (size == 8 && addr == 0)); } static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return addr == 0; } @@ -439,7 +441,8 @@ static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr, } static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return is_write && size == 2; } @@ -458,7 +461,8 @@ static void fw_cfg_comb_write(void *opaque, hwaddr addr, } static bool fw_cfg_comb_valid(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return (size == 1) || (is_write && size == 2); } diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 4a0aec8e1d..bed1557d83 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -47,9 +48,9 @@ typedef struct sPAPRNVRAM { #define VIO_SPAPR_NVRAM(obj) \ OBJECT_CHECK(sPAPRNVRAM, (obj), TYPE_VIO_SPAPR_NVRAM) -#define MIN_NVRAM_SIZE 8192 -#define DEFAULT_NVRAM_SIZE 65536 -#define MAX_NVRAM_SIZE 1048576 +#define MIN_NVRAM_SIZE (8 * KiB) +#define DEFAULT_NVRAM_SIZE (64 * KiB) +#define MAX_NVRAM_SIZE (1 * MiB) static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, @@ -167,7 +168,9 @@ static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) nvram->buf = g_malloc0(nvram->size); if ((nvram->size < MIN_NVRAM_SIZE) || (nvram->size > MAX_NVRAM_SIZE)) { - error_setg(errp, "spapr-nvram must be between %d and %d bytes in size", + error_setg(errp, + "spapr-nvram must be between %" PRId64 + " and %" PRId64 " bytes in size", MIN_NVRAM_SIZE, MAX_NVRAM_SIZE); return; } diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index c755f11efd..a495a84a41 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -164,7 +164,7 @@ static void openrisc_sim_init(MachineState *machine) } serial_mm_init(get_system_memory(), 0x90000000, 0, serial_irq, - 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); openrisc_load_kernel(ram_size, kernel_filename); } diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 033588b7d2..4810a4de79 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -27,6 +27,8 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/mac.h" #include "hw/pci/pci.h" +#include "hw/intc/heathrow_pic.h" +#include "qapi/error.h" #include "trace.h" #define GRACKLE_PCI_HOST_BRIDGE(obj) \ @@ -35,8 +37,11 @@ typedef struct GrackleState { PCIHostState parent_obj; + HeathrowState *pic; + qemu_irq irqs[4]; MemoryRegion pci_mmio; MemoryRegion pci_hole; + MemoryRegion pci_io; } GrackleState; /* Don't know if this matches real hardware, but it agrees with OHW. */ @@ -47,76 +52,78 @@ static int pci_grackle_map_irq(PCIDevice *pci_dev, int irq_num) static void pci_grackle_set_irq(void *opaque, int irq_num, int level) { - qemu_irq *pic = opaque; + GrackleState *s = opaque; trace_grackle_set_irq(irq_num, level); - qemu_set_irq(pic[irq_num + 0x15], level); + qemu_set_irq(s->irqs[irq_num], level); } -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) +static void grackle_init_irqs(GrackleState *s) { - DeviceState *dev; - SysBusDevice *s; - PCIHostState *phb; - GrackleState *d; + int i; - dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); - s = SYS_BUS_DEVICE(dev); - phb = PCI_HOST_BRIDGE(dev); - d = GRACKLE_PCI_HOST_BRIDGE(dev); + for (i = 0; i < ARRAY_SIZE(s->irqs); i++) { + s->irqs[i] = qdev_get_gpio_in(DEVICE(s->pic), 0x15 + i); + } +} - memory_region_init(&d->pci_mmio, OBJECT(s), "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, OBJECT(s), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x7e000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); +static void grackle_realize(DeviceState *dev, Error **errp) +{ + GrackleState *s = GRACKLE_PCI_HOST_BRIDGE(dev); + PCIHostState *phb = PCI_HOST_BRIDGE(dev); phb->bus = pci_register_root_bus(dev, NULL, pci_grackle_set_irq, pci_grackle_map_irq, - pic, - &d->pci_mmio, - address_space_io, + s, + &s->pci_mmio, + &s->pci_io, 0, 4, TYPE_PCI_BUS); pci_create_simple(phb->bus, 0, "grackle"); - qdev_init_nofail(dev); - - sysbus_mmio_map(s, 0, base); - sysbus_mmio_map(s, 1, base + 0x00200000); - - return phb->bus; + grackle_init_irqs(s); } -static int pci_grackle_init_device(SysBusDevice *dev) +static void grackle_init(Object *obj) { - PCIHostState *phb; + GrackleState *s = GRACKLE_PCI_HOST_BRIDGE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *phb = PCI_HOST_BRIDGE(obj); - phb = PCI_HOST_BRIDGE(dev); + memory_region_init(&s->pci_mmio, OBJECT(s), "pci-mmio", 0x100000000ULL); + memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj, + "pci-isa-mmio", 0x00200000); - memory_region_init_io(&phb->conf_mem, OBJECT(dev), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&phb->data_mem, OBJECT(dev), &pci_host_data_le_ops, - dev, "pci-data-idx", 0x1000); - sysbus_init_mmio(dev, &phb->conf_mem); - sysbus_init_mmio(dev, &phb->data_mem); + memory_region_init_alias(&s->pci_hole, OBJECT(s), "pci-hole", &s->pci_mmio, + 0x80000000ULL, 0x7e000000ULL); - return 0; + memory_region_init_io(&phb->conf_mem, obj, &pci_host_conf_le_ops, + DEVICE(obj), "pci-conf-idx", 0x1000); + memory_region_init_io(&phb->data_mem, obj, &pci_host_data_le_ops, + DEVICE(obj), "pci-data-idx", 0x1000); + + object_property_add_link(obj, "pic", TYPE_HEATHROW, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + sysbus_init_mmio(sbd, &phb->conf_mem); + sysbus_init_mmio(sbd, &phb->data_mem); + sysbus_init_mmio(sbd, &s->pci_hole); + sysbus_init_mmio(sbd, &s->pci_io); } -static void grackle_pci_host_realize(PCIDevice *d, Error **errp) +static void grackle_pci_realize(PCIDevice *d, Error **errp) { d->config[0x09] = 0x01; } static void grackle_pci_class_init(ObjectClass *klass, void *data) { - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->realize = grackle_pci_host_realize; + k->realize = grackle_pci_realize; k->vendor_id = PCI_VENDOR_ID_MOTOROLA; k->device_id = PCI_DEVICE_ID_MOTOROLA_MPC106; k->revision = 0x00; @@ -139,26 +146,26 @@ static const TypeInfo grackle_pci_info = { }, }; -static void pci_grackle_class_init(ObjectClass *klass, void *data) +static void grackle_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = pci_grackle_init_device; + dc->realize = grackle_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } -static const TypeInfo grackle_pci_host_info = { +static const TypeInfo grackle_host_info = { .name = TYPE_GRACKLE_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(GrackleState), - .class_init = pci_grackle_class_init, + .instance_init = grackle_init, + .class_init = grackle_class_init, }; static void grackle_register_types(void) { type_register_static(&grackle_pci_info); - type_register_static(&grackle_pci_host_info); + type_register_static(&grackle_host_info); } type_init(grackle_register_types) diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 01f67f9db1..88f035c20b 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/pci/pci.h" @@ -70,7 +71,7 @@ typedef struct PRePPCIState { int contiguous_map; } PREPPCIState; -#define BIOS_SIZE (1024 * 1024) +#define BIOS_SIZE (1 * MiB) static inline uint32_t raven_pci_io_config(hwaddr addr) { diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index a36a1195e4..02f9576588 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -535,13 +535,15 @@ static void mch_realize(PCIDevice *d, Error **errp) /* if *disabled* show SMRAM to all CPUs */ memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", - mch->pci_address_space, 0xa0000, 0x20000); - memory_region_add_subregion_overlap(mch->system_memory, 0xa0000, + mch->pci_address_space, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); + memory_region_add_subregion_overlap(mch->system_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, &mch->smram_region, 1); memory_region_set_enabled(&mch->smram_region, true); memory_region_init_alias(&mch->open_high_smram, OBJECT(mch), "smram-open-high", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_add_subregion_overlap(mch->system_memory, 0xfeda0000, &mch->open_high_smram, 1); memory_region_set_enabled(&mch->open_high_smram, false); @@ -550,11 +552,14 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_init(&mch->smram, OBJECT(mch), "smram", 1ull << 32); memory_region_set_enabled(&mch->smram, true); memory_region_init_alias(&mch->low_smram, OBJECT(mch), "smram-low", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_set_enabled(&mch->low_smram, true); - memory_region_add_subregion(&mch->smram, 0xa0000, &mch->low_smram); + memory_region_add_subregion(&mch->smram, MCH_HOST_BRIDGE_SMRAM_C_BASE, + &mch->low_smram); memory_region_init_alias(&mch->high_smram, OBJECT(mch), "smram-high", - mch->ram_memory, 0xa0000, 0x20000); + mch->ram_memory, MCH_HOST_BRIDGE_SMRAM_C_BASE, + MCH_HOST_BRIDGE_SMRAM_C_SIZE); memory_region_set_enabled(&mch->high_smram, true); memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram); diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index 341a87a702..dd7a398e96 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -18,3 +18,5 @@ unin_set_irq(int irq_num, int level) "setting INT %d = %d" unin_get_config_reg(uint32_t reg, uint32_t addr, uint32_t retval) "converted config space accessor 0x%"PRIx32 "/0x%"PRIx32 " -> 0x%"PRIx32 unin_data_write(uint64_t addr, unsigned len, uint64_t val) "write addr 0x%"PRIx64 " len %d val 0x%"PRIx64 unin_data_read(uint64_t addr, unsigned len, uint64_t val) "read addr 0x%"PRIx64 " len %d val 0x%"PRIx64 +unin_write(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 +unin_read(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 66991da975..a843aa7b36 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -26,31 +26,11 @@ #include "hw/ppc/mac.h" #include "hw/pci/pci.h" #include "hw/pci/pci_host.h" +#include "hw/pci-host/uninorth.h" #include "trace.h" static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e }; -#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" -#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" -#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" -#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost" - -#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) -#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) -#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) -#define U3_AGP_HOST_BRIDGE(obj) \ - OBJECT_CHECK(UNINState, (obj), TYPE_U3_AGP_HOST_BRIDGE) - -typedef struct UNINState { - PCIHostState parent_obj; - - MemoryRegion pci_mmio; - MemoryRegion pci_hole; -} UNINState; - static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) { return (irq_num + (pci_dev->devfn >> 3)) & 3; @@ -58,10 +38,10 @@ static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) static void pci_unin_set_irq(void *opaque, int irq_num, int level) { - qemu_irq *pic = opaque; + UNINHostState *s = opaque; trace_unin_set_irq(unin_irq_line[irq_num], level); - qemu_set_irq(pic[unin_irq_line[irq_num]], level); + qemu_set_irq(s->irqs[irq_num], level); } static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr) @@ -101,7 +81,7 @@ static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr) static void unin_data_write(void *opaque, hwaddr addr, uint64_t val, unsigned len) { - UNINState *s = opaque; + UNINHostState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); trace_unin_data_write(addr, len, val); pci_data_write(phb->bus, @@ -112,7 +92,7 @@ static void unin_data_write(void *opaque, hwaddr addr, static uint64_t unin_data_read(void *opaque, hwaddr addr, unsigned len) { - UNINState *s = opaque; + UNINHostState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); uint32_t val; @@ -129,189 +109,201 @@ static const MemoryRegionOps unin_data_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int pci_unin_main_init_device(SysBusDevice *dev) +static void pci_unin_init_irqs(UNINHostState *s) { - PCIHostState *h; + int i; - /* Use values found on a real PowerMac */ - /* Uninorth main bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, dev, - "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - - return 0; + for (i = 0; i < ARRAY_SIZE(s->irqs); i++) { + s->irqs[i] = qdev_get_gpio_in(DEVICE(s->pic), unin_irq_line[i]); + } } - -static int pci_u3_agp_init_device(SysBusDevice *dev) +static void pci_unin_main_realize(DeviceState *dev, Error **errp) { - PCIHostState *h; - - /* Uninorth U3 AGP bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, dev, - "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - - return 0; -} - -static int pci_unin_agp_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Uninorth AGP bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, - dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - return 0; -} - -static int pci_unin_internal_init_device(SysBusDevice *dev) -{ - PCIHostState *h; - - /* Uninorth internal bus */ - h = PCI_HOST_BRIDGE(dev); - - memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, - dev, "pci-conf-idx", 0x1000); - memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, - dev, "pci-conf-data", 0x1000); - sysbus_init_mmio(dev, &h->conf_mem); - sysbus_init_mmio(dev, &h->data_mem); - return 0; -} - -PCIBus *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) -{ - DeviceState *dev; - SysBusDevice *s; - PCIHostState *h; - UNINState *d; - - /* Use values found on a real PowerMac */ - /* Uninorth main bus */ - dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - h = PCI_HOST_BRIDGE(s); - d = UNI_NORTH_PCI_HOST_BRIDGE(dev); - memory_region_init(&d->pci_mmio, OBJECT(d), "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x10000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); + UNINHostState *s = UNI_NORTH_PCI_HOST_BRIDGE(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); h->bus = pci_register_root_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, - pic, - &d->pci_mmio, - address_space_io, + s, + &s->pci_mmio, + &s->pci_io, PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); -#if 0 - pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north"); -#endif - - sysbus_mmio_map(s, 0, 0xf2800000); - sysbus_mmio_map(s, 1, 0xf2c00000); + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-pci"); + pci_unin_init_irqs(s); /* DEC 21154 bridge */ #if 0 /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */ pci_create_simple(h->bus, PCI_DEVFN(12, 0), "dec-21154"); #endif - - /* Uninorth AGP bus */ - pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); - dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, 0xf0800000); - sysbus_mmio_map(s, 1, 0xf0c00000); - - /* Uninorth internal bus */ -#if 0 - /* XXX: not needed for now */ - pci_create_simple(h->bus, PCI_DEVFN(14, 0), - "uni-north-internal-pci"); - dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_mmio_map(s, 0, 0xf4800000); - sysbus_mmio_map(s, 1, 0xf4c00000); -#endif - - return h->bus; } -PCIBus *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io) +static void pci_unin_main_init(Object *obj) { - DeviceState *dev; - SysBusDevice *s; - PCIHostState *h; - UNINState *d; + UNINHostState *s = UNI_NORTH_PCI_HOST_BRIDGE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); - /* Uninorth AGP bus */ + /* Use values found on a real PowerMac */ + /* Uninorth main bus */ + memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, + obj, "unin-pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, obj, + "unin-pci-conf-data", 0x1000); - dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - h = PCI_HOST_BRIDGE(dev); - d = U3_AGP_HOST_BRIDGE(dev); + memory_region_init(&s->pci_mmio, OBJECT(s), "unin-pci-mmio", + 0x100000000ULL); + memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj, + "unin-pci-isa-mmio", 0x00800000); - memory_region_init(&d->pci_mmio, OBJECT(d), "pci-mmio", 0x100000000ULL); - memory_region_init_alias(&d->pci_hole, OBJECT(d), "pci-hole", &d->pci_mmio, - 0x80000000ULL, 0x70000000ULL); - memory_region_add_subregion(address_space_mem, 0x80000000ULL, - &d->pci_hole); + memory_region_init_alias(&s->pci_hole, OBJECT(s), + "unin-pci-hole", &s->pci_mmio, + 0x80000000ULL, 0x10000000ULL); + + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + sysbus_init_mmio(sbd, &h->conf_mem); + sysbus_init_mmio(sbd, &h->data_mem); + sysbus_init_mmio(sbd, &s->pci_hole); + sysbus_init_mmio(sbd, &s->pci_io); +} + +static void pci_u3_agp_realize(DeviceState *dev, Error **errp) +{ + UNINHostState *s = U3_AGP_HOST_BRIDGE(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); h->bus = pci_register_root_bus(dev, NULL, pci_unin_set_irq, pci_unin_map_irq, - pic, - &d->pci_mmio, - address_space_io, + s, + &s->pci_mmio, + &s->pci_io, PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); - sysbus_mmio_map(s, 0, 0xf0800000); - sysbus_mmio_map(s, 1, 0xf0c00000); + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "u3-agp"); + pci_unin_init_irqs(s); +} - pci_create_simple(h->bus, 11 << 3, "u3-agp"); +static void pci_u3_agp_init(Object *obj) +{ + UNINHostState *s = U3_AGP_HOST_BRIDGE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); - return h->bus; + /* Uninorth U3 AGP bus */ + memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, + obj, "unin-pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, OBJECT(h), &unin_data_ops, obj, + "unin-pci-conf-data", 0x1000); + + memory_region_init(&s->pci_mmio, OBJECT(s), "unin-pci-mmio", + 0x100000000ULL); + memory_region_init_io(&s->pci_io, OBJECT(s), &unassigned_io_ops, obj, + "unin-pci-isa-mmio", 0x00800000); + + memory_region_init_alias(&s->pci_hole, OBJECT(s), + "unin-pci-hole", &s->pci_mmio, + 0x80000000ULL, 0x70000000ULL); + + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + sysbus_init_mmio(sbd, &h->conf_mem); + sysbus_init_mmio(sbd, &h->data_mem); + sysbus_init_mmio(sbd, &s->pci_hole); + sysbus_init_mmio(sbd, &s->pci_io); +} + +static void pci_unin_agp_realize(DeviceState *dev, Error **errp) +{ + UNINHostState *s = UNI_NORTH_AGP_HOST_BRIDGE(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + + h->bus = pci_register_root_bus(dev, NULL, + pci_unin_set_irq, pci_unin_map_irq, + s, + &s->pci_mmio, + &s->pci_io, + PCI_DEVFN(11, 0), 4, TYPE_PCI_BUS); + + pci_create_simple(h->bus, PCI_DEVFN(11, 0), "uni-north-agp"); + pci_unin_init_irqs(s); +} + +static void pci_unin_agp_init(Object *obj) +{ + UNINHostState *s = UNI_NORTH_AGP_HOST_BRIDGE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + + /* Uninorth AGP bus */ + memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, + obj, "unin-agp-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, + obj, "unin-agp-conf-data", 0x1000); + + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + sysbus_init_mmio(sbd, &h->conf_mem); + sysbus_init_mmio(sbd, &h->data_mem); +} + +static void pci_unin_internal_realize(DeviceState *dev, Error **errp) +{ + UNINHostState *s = UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + + h->bus = pci_register_root_bus(dev, NULL, + pci_unin_set_irq, pci_unin_map_irq, + s, + &s->pci_mmio, + &s->pci_io, + PCI_DEVFN(14, 0), 4, TYPE_PCI_BUS); + + pci_create_simple(h->bus, PCI_DEVFN(14, 0), "uni-north-internal-pci"); + pci_unin_init_irqs(s); +} + +static void pci_unin_internal_init(Object *obj) +{ + UNINHostState *s = UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + PCIHostState *h = PCI_HOST_BRIDGE(obj); + + /* Uninorth internal bus */ + memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, + obj, "unin-pci-conf-idx", 0x1000); + memory_region_init_io(&h->data_mem, OBJECT(h), &pci_host_data_le_ops, + obj, "unin-pci-conf-data", 0x1000); + + object_property_add_link(obj, "pic", TYPE_OPENPIC, + (Object **) &s->pic, + qdev_prop_allow_set_link_before_realize, + 0, NULL); + + sysbus_init_mmio(sbd, &h->conf_mem); + sysbus_init_mmio(sbd, &h->data_mem); } static void unin_main_pci_host_realize(PCIDevice *d, Error **errp) { - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer -} + /* cache_line_size */ + d->config[0x0C] = 0x08; + /* latency_timer */ + d->config[0x0D] = 0x10; + /* capabilities_pointer */ + d->config[0x34] = 0x00; -static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) -{ - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - // d->config[0x34] = 0x80; // capabilities_pointer /* * Set kMacRISCPCIAddressSelect (0x48) register to indicate PCI * memory space with base 0x80000000, size 0x10000000 for Apple's @@ -323,6 +315,16 @@ static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) d->config[0x4b] = 0x1; } +static void unin_agp_pci_host_realize(PCIDevice *d, Error **errp) +{ + /* cache_line_size */ + d->config[0x0C] = 0x08; + /* latency_timer */ + d->config[0x0D] = 0x10; + /* capabilities_pointer + d->config[0x34] = 0x80; */ +} + static void u3_agp_pci_host_realize(PCIDevice *d, Error **errp) { /* cache line size */ @@ -333,9 +335,12 @@ static void u3_agp_pci_host_realize(PCIDevice *d, Error **errp) static void unin_internal_pci_host_realize(PCIDevice *d, Error **errp) { - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x10; // latency_timer - d->config[0x34] = 0x00; // capabilities_pointer + /* cache_line_size */ + d->config[0x0C] = 0x08; + /* latency_timer */ + d->config[0x0D] = 0x10; + /* capabilities_pointer */ + d->config[0x34] = 0x00; } static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) @@ -452,68 +457,123 @@ static const TypeInfo unin_internal_pci_host_info = { static void pci_unin_main_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = pci_unin_main_init_device; + dc->realize = pci_unin_main_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_unin_main_info = { .name = TYPE_UNI_NORTH_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), + .instance_size = sizeof(UNINHostState), + .instance_init = pci_unin_main_init, .class_init = pci_unin_main_class_init, }; static void pci_u3_agp_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = pci_u3_agp_init_device; + dc->realize = pci_u3_agp_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_u3_agp_info = { .name = TYPE_U3_AGP_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), + .instance_size = sizeof(UNINHostState), + .instance_init = pci_u3_agp_init, .class_init = pci_u3_agp_class_init, }; static void pci_unin_agp_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = pci_unin_agp_init_device; + dc->realize = pci_unin_agp_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_unin_agp_info = { .name = TYPE_UNI_NORTH_AGP_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), + .instance_size = sizeof(UNINHostState), + .instance_init = pci_unin_agp_init, .class_init = pci_unin_agp_class_init, }; static void pci_unin_internal_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = pci_unin_internal_init_device; + dc->realize = pci_unin_internal_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); } static const TypeInfo pci_unin_internal_info = { .name = TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(UNINState), + .instance_size = sizeof(UNINHostState), + .instance_init = pci_unin_internal_init, .class_init = pci_unin_internal_class_init, }; +/* UniN device */ +static void unin_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + trace_unin_write(addr, value); +} + +static uint64_t unin_read(void *opaque, hwaddr addr, unsigned size) +{ + uint32_t value; + + switch (addr) { + case 0: + value = UNINORTH_VERSION_10A; + break; + default: + value = 0; + } + + trace_unin_read(addr, value); + + return value; +} + +static const MemoryRegionOps unin_ops = { + .read = unin_read, + .write = unin_write, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void unin_init(Object *obj) +{ + UNINState *s = UNI_NORTH(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->mem, obj, &unin_ops, s, "unin", 0x1000); + + sysbus_init_mmio(sbd, &s->mem); +} + +static void unin_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo unin_info = { + .name = TYPE_UNI_NORTH, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(UNINState), + .instance_init = unin_init, + .class_init = unin_class_init, +}; + static void unin_register_types(void) { type_register_static(&unin_main_pci_host_info); @@ -525,6 +585,8 @@ static void unin_register_types(void) type_register_static(&pci_u3_agp_info); type_register_static(&pci_unin_agp_info); type_register_static(&pci_unin_internal_info); + + type_register_static(&unin_info); } type_init(unin_register_types) diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index d0b02bdc47..7b19078c80 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -12,7 +12,6 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" -#include "exec/address-spaces.h" #include "qemu/log.h" /* Old and buggy versions of QEMU used the wrong mapping from diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 044e312dc1..60309afe9e 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/pci/pci_bridge.h" #include "hw/pci-host/xilinx-pcie.h" @@ -120,9 +121,8 @@ static void xilinx_pcie_host_realize(DeviceState *dev, Error **errp) memory_region_init(&s->mmio, OBJECT(s), "mmio", UINT64_MAX); memory_region_set_enabled(&s->mmio, false); - /* dummy I/O region */ - memory_region_init_ram_nomigrate(&s->io, OBJECT(s), "io", 16, NULL); - memory_region_set_enabled(&s->io, false); + /* dummy PCI I/O region (not visible to the CPU) */ + memory_region_init(&s->io, OBJECT(s), "io", 16); /* interrupt out */ qdev_init_gpio_out_named(dev, &s->irq, "interrupt_out", 1); @@ -158,9 +158,9 @@ static void xilinx_pcie_host_init(Object *obj) static Property xilinx_pcie_host_props[] = { DEFINE_PROP_UINT32("bus_nr", XilinxPCIEHost, bus_nr, 0), DEFINE_PROP_SIZE("cfg_base", XilinxPCIEHost, cfg_base, 0), - DEFINE_PROP_SIZE("cfg_size", XilinxPCIEHost, cfg_size, 32 << 20), + DEFINE_PROP_SIZE("cfg_size", XilinxPCIEHost, cfg_size, 32 * MiB), DEFINE_PROP_SIZE("mmio_base", XilinxPCIEHost, mmio_base, 0), - DEFINE_PROP_SIZE("mmio_size", XilinxPCIEHost, mmio_size, 1 << 20), + DEFINE_PROP_SIZE("mmio_size", XilinxPCIEHost, mmio_size, 1 * MiB), DEFINE_PROP_BOOL("link_up", XilinxPCIEHost, link_up, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 86d82a6ec3..bcab6323b7 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -14,7 +14,8 @@ obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o # PowerPC 4xx boards obj-y += ppc4xx_devs.o ppc405_uc.o obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o -obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o sam460ex.o +obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o +obj-$(CONFIG_SAM460EX) += sam460ex.o # PReP obj-$(CONFIG_PREP) += prep.o obj-$(CONFIG_PREP) += prep_systemio.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 9a85a41362..7d19b1498c 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -15,6 +15,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "e500.h" #include "e500-ccsr.h" @@ -46,11 +47,11 @@ #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define DTC_LOAD_PAD 0x1800000 #define DTC_PAD_MASK 0xFFFFF -#define DTB_MAX_SIZE (8 * 1024 * 1024) +#define DTB_MAX_SIZE (8 * MiB) #define INITRD_LOAD_PAD 0x2000000 #define INITRD_PAD_MASK 0xFFFFFF -#define RAM_SIZES_ALIGN (64UL << 20) +#define RAM_SIZES_ALIGN (64 * MiB) /* TODO: parameterize */ #define MPC8544_CCSRBAR_SIZE 0x00100000ULL @@ -106,9 +107,9 @@ static void dt_serial_create(void *fdt, unsigned long long offset, const char *soc, const char *mpic, const char *alias, int idx, bool defcon) { - char ser[128]; + char *ser; - snprintf(ser, sizeof(ser), "%s/serial@%llx", soc, offset); + ser = g_strdup_printf("%s/serial@%llx", soc, offset); qemu_fdt_add_subnode(fdt, ser); qemu_fdt_setprop_string(fdt, ser, "device_type", "serial"); qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550"); @@ -129,6 +130,7 @@ static void dt_serial_create(void *fdt, unsigned long long offset, qemu_fdt_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", ser); } + g_free(ser); } static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic) @@ -221,16 +223,15 @@ static void sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) } } -static void platform_bus_create_devtree(PPCE500Params *params, void *fdt, - const char *mpic) +static void platform_bus_create_devtree(PPCE500MachineState *pms, + void *fdt, const char *mpic) { - gchar *node = g_strdup_printf("/platform@%"PRIx64, params->platform_bus_base); + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); + gchar *node = g_strdup_printf("/platform@%"PRIx64, pmc->platform_bus_base); const char platcomp[] = "qemu,platform\0simple-bus"; - uint64_t addr = params->platform_bus_base; - uint64_t size = params->platform_bus_size; - int irq_start = params->platform_bus_first_irq; - PlatformBusDevice *pbus; - DeviceState *dev; + uint64_t addr = pmc->platform_bus_base; + uint64_t size = pmc->platform_bus_size; + int irq_start = pmc->platform_bus_first_irq; /* Create a /platform node that we can put all devices into */ @@ -245,28 +246,22 @@ static void platform_bus_create_devtree(PPCE500Params *params, void *fdt, qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", mpic); - dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); - pbus = PLATFORM_BUS_DEVICE(dev); + /* Create dt nodes for dynamic devices */ + PlatformDevtreeData data = { + .fdt = fdt, + .mpic = mpic, + .irq_start = irq_start, + .node = node, + .pbus = pms->pbus_dev, + }; - /* We can only create dt nodes for dynamic devices when they're ready */ - if (pbus->done_gathering) { - PlatformDevtreeData data = { - .fdt = fdt, - .mpic = mpic, - .irq_start = irq_start, - .node = node, - .pbus = pbus, - }; - - /* Loop through all dynamic sysbus devices and create nodes for them */ - foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data); - } + /* Loop through all dynamic sysbus devices and create nodes for them */ + foreach_dynamic_sysbus_device(sysbus_device_create_devtree, &data); g_free(node); } -static int ppce500_load_device_tree(MachineState *machine, - PPCE500Params *params, +static int ppce500_load_device_tree(PPCE500MachineState *pms, hwaddr addr, hwaddr initrd_base, hwaddr initrd_size, @@ -274,6 +269,8 @@ static int ppce500_load_device_tree(MachineState *machine, hwaddr kernel_size, bool dry_run) { + MachineState *machine = MACHINE(pms); + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); CPUPPCState *env = first_cpu->env_ptr; int ret = -1; uint64_t mem_reg_property[] = { 0, cpu_to_be64(machine->ram_size) }; @@ -284,23 +281,23 @@ static int ppce500_load_device_tree(MachineState *machine, uint32_t tb_freq = 400000000; int i; char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; - char soc[128]; - char mpic[128]; + char *soc; + char *mpic; uint32_t mpic_ph; uint32_t msi_ph; - char gutil[128]; - char pci[128]; - char msi[128]; + char *gutil; + char *pci; + char *msi; uint32_t *pci_map = NULL; int len; uint32_t pci_ranges[14] = { - 0x2000000, 0x0, params->pci_mmio_bus_base, - params->pci_mmio_base >> 32, params->pci_mmio_base, + 0x2000000, 0x0, pmc->pci_mmio_bus_base, + pmc->pci_mmio_base >> 32, pmc->pci_mmio_base, 0x0, 0x20000000, 0x1000000, 0x0, 0x0, - params->pci_pio_base >> 32, params->pci_pio_base, + pmc->pci_pio_base >> 32, pmc->pci_pio_base, 0x0, 0x10000, }; QemuOpts *machine_opts = qemu_get_machine_opts(); @@ -390,8 +387,8 @@ static int ppce500_load_device_tree(MachineState *machine, the first node as boot node and be happy */ for (i = smp_cpus - 1; i >= 0; i--) { CPUState *cpu; - char cpu_name[128]; - uint64_t cpu_release_addr = params->spin_base + (i * 0x20); + char *cpu_name; + uint64_t cpu_release_addr = pmc->spin_base + (i * 0x20); cpu = qemu_get_cpu(i); if (cpu == NULL) { @@ -399,7 +396,7 @@ static int ppce500_load_device_tree(MachineState *machine, } env = cpu->env_ptr; - snprintf(cpu_name, sizeof(cpu_name), "/cpus/PowerPC,8544@%x", i); + cpu_name = g_strdup_printf("/cpus/PowerPC,8544@%x", i); qemu_fdt_add_subnode(fdt, cpu_name); qemu_fdt_setprop_cell(fdt, cpu_name, "clock-frequency", clock_freq); qemu_fdt_setprop_cell(fdt, cpu_name, "timebase-frequency", tb_freq); @@ -421,11 +418,12 @@ static int ppce500_load_device_tree(MachineState *machine, } else { qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay"); } + g_free(cpu_name); } qemu_fdt_add_subnode(fdt, "/aliases"); /* XXX These should go into their respective devices' code */ - snprintf(soc, sizeof(soc), "/soc@%"PRIx64, params->ccsrbar_base); + soc = g_strdup_printf("/soc@%"PRIx64, pmc->ccsrbar_base); qemu_fdt_add_subnode(fdt, soc); qemu_fdt_setprop_string(fdt, soc, "device_type", "soc"); qemu_fdt_setprop(fdt, soc, "compatible", compatible_sb, @@ -433,12 +431,12 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, soc, "#address-cells", 1); qemu_fdt_setprop_cell(fdt, soc, "#size-cells", 1); qemu_fdt_setprop_cells(fdt, soc, "ranges", 0x0, - params->ccsrbar_base >> 32, params->ccsrbar_base, + pmc->ccsrbar_base >> 32, pmc->ccsrbar_base, MPC8544_CCSRBAR_SIZE); /* XXX should contain a reasonable value */ qemu_fdt_setprop_cell(fdt, soc, "bus-frequency", 0); - snprintf(mpic, sizeof(mpic), "%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); + mpic = g_strdup_printf("%s/pic@%llx", soc, MPC8544_MPIC_REGS_OFFSET); qemu_fdt_add_subnode(fdt, mpic); qemu_fdt_setprop_string(fdt, mpic, "device_type", "open-pic"); qemu_fdt_setprop_string(fdt, mpic, "compatible", "fsl,mpic"); @@ -456,24 +454,25 @@ static int ppce500_load_device_tree(MachineState *machine, * device it finds in the dt as serial output device. And we generate * devices in reverse order to the dt. */ - if (serial_hds[1]) { + if (serial_hd(1)) { dt_serial_create(fdt, MPC8544_SERIAL1_REGS_OFFSET, soc, mpic, "serial1", 1, false); } - if (serial_hds[0]) { + if (serial_hd(0)) { dt_serial_create(fdt, MPC8544_SERIAL0_REGS_OFFSET, soc, mpic, "serial0", 0, true); } - snprintf(gutil, sizeof(gutil), "%s/global-utilities@%llx", soc, - MPC8544_UTIL_OFFSET); + gutil = g_strdup_printf("%s/global-utilities@%llx", soc, + MPC8544_UTIL_OFFSET); qemu_fdt_add_subnode(fdt, gutil); qemu_fdt_setprop_string(fdt, gutil, "compatible", "fsl,mpc8544-guts"); qemu_fdt_setprop_cells(fdt, gutil, "reg", MPC8544_UTIL_OFFSET, 0x1000); qemu_fdt_setprop(fdt, gutil, "fsl,has-rstcr", NULL, 0); + g_free(gutil); - snprintf(msi, sizeof(msi), "/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); + msi = g_strdup_printf("/%s/msi@%llx", soc, MPC8544_MSI_REGS_OFFSET); qemu_fdt_add_subnode(fdt, msi); qemu_fdt_setprop_string(fdt, msi, "compatible", "fsl,mpic-msi"); qemu_fdt_setprop_cells(fdt, msi, "reg", MPC8544_MSI_REGS_OFFSET, 0x200); @@ -491,9 +490,10 @@ static int ppce500_load_device_tree(MachineState *machine, 0xe7, 0x0); qemu_fdt_setprop_cell(fdt, msi, "phandle", msi_ph); qemu_fdt_setprop_cell(fdt, msi, "linux,phandle", msi_ph); + g_free(msi); - snprintf(pci, sizeof(pci), "/pci@%llx", - params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET); + pci = g_strdup_printf("/pci@%llx", + pmc->ccsrbar_base + MPC8544_PCI_REGS_OFFSET); qemu_fdt_add_subnode(fdt, pci); qemu_fdt_setprop_cell(fdt, pci, "cell-index", 0); qemu_fdt_setprop_string(fdt, pci, "compatible", "fsl,mpc8540-pci"); @@ -501,7 +501,7 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cells(fdt, pci, "interrupt-map-mask", 0xf800, 0x0, 0x0, 0x7); pci_map = pci_map_create(fdt, qemu_fdt_get_phandle(fdt, mpic), - params->pci_first_slot, params->pci_nr_slots, + pmc->pci_first_slot, pmc->pci_nr_slots, &len); qemu_fdt_setprop(fdt, pci, "interrupt-map", pci_map, len); qemu_fdt_setprop_phandle(fdt, pci, "interrupt-parent", mpic); @@ -513,24 +513,27 @@ static int ppce500_load_device_tree(MachineState *machine, qemu_fdt_setprop_cell(fdt, pci, "fsl,msi", msi_ph); qemu_fdt_setprop(fdt, pci, "ranges", pci_ranges, sizeof(pci_ranges)); qemu_fdt_setprop_cells(fdt, pci, "reg", - (params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET) >> 32, - (params->ccsrbar_base + MPC8544_PCI_REGS_OFFSET), + (pmc->ccsrbar_base + MPC8544_PCI_REGS_OFFSET) >> 32, + (pmc->ccsrbar_base + MPC8544_PCI_REGS_OFFSET), 0, 0x1000); qemu_fdt_setprop_cell(fdt, pci, "clock-frequency", 66666666); qemu_fdt_setprop_cell(fdt, pci, "#interrupt-cells", 1); qemu_fdt_setprop_cell(fdt, pci, "#size-cells", 2); qemu_fdt_setprop_cell(fdt, pci, "#address-cells", 3); qemu_fdt_setprop_string(fdt, "/aliases", "pci0", pci); + g_free(pci); - if (params->has_mpc8xxx_gpio) { + if (pmc->has_mpc8xxx_gpio) { create_dt_mpc8xxx_gpio(fdt, soc, mpic); } + g_free(soc); - if (params->has_platform_bus) { - platform_bus_create_devtree(params, fdt, mpic); + if (pms->pbus_dev) { + platform_bus_create_devtree(pms, fdt, mpic); } + g_free(mpic); - params->fixup_devtree(params, fdt); + pmc->fixup_devtree(fdt); if (toplevel_compat) { qemu_fdt_setprop(fdt, "/", "compatible", toplevel_compat, @@ -551,8 +554,7 @@ out: } typedef struct DeviceTreeParams { - MachineState *machine; - PPCE500Params params; + PPCE500MachineState *machine; hwaddr addr; hwaddr initrd_base; hwaddr initrd_size; @@ -564,7 +566,7 @@ typedef struct DeviceTreeParams { static void ppce500_reset_device_tree(void *opaque) { DeviceTreeParams *p = opaque; - ppce500_load_device_tree(p->machine, &p->params, p->addr, p->initrd_base, + ppce500_load_device_tree(p->machine, p->addr, p->initrd_base, p->initrd_size, p->kernel_base, p->kernel_size, false); } @@ -575,8 +577,7 @@ static void ppce500_init_notify(Notifier *notifier, void *data) ppce500_reset_device_tree(p); } -static int ppce500_prep_device_tree(MachineState *machine, - PPCE500Params *params, +static int ppce500_prep_device_tree(PPCE500MachineState *machine, hwaddr addr, hwaddr initrd_base, hwaddr initrd_size, @@ -585,7 +586,6 @@ static int ppce500_prep_device_tree(MachineState *machine, { DeviceTreeParams *p = g_new(DeviceTreeParams, 1); p->machine = machine; - p->params = *params; p->addr = addr; p->initrd_base = initrd_base; p->initrd_size = initrd_size; @@ -597,15 +597,14 @@ static int ppce500_prep_device_tree(MachineState *machine, qemu_add_machine_init_done_notifier(&p->notifier); /* Issue the device tree loader once, so that we get the size of the blob */ - return ppce500_load_device_tree(machine, params, addr, initrd_base, - initrd_size, kernel_base, kernel_size, - true); + return ppce500_load_device_tree(machine, addr, initrd_base, initrd_size, + kernel_base, kernel_size, true); } /* Create -kernel TLB entries for BookE. */ hwaddr booke206_page_size_to_tlb(uint64_t size) { - return 63 - clz64(size >> 10); + return 63 - clz64(size / KiB); } static int booke206_initial_map_tsize(CPUPPCState *env) @@ -673,7 +672,7 @@ static void ppce500_cpu_reset(void *opaque) /* Set initial guest state. */ cs->halted = 0; - env->gpr[1] = (16<<20) - 8; + env->gpr[1] = (16 * MiB) - 8; env->gpr[3] = bi->dt_base; env->gpr[4] = 0; env->gpr[5] = 0; @@ -685,17 +684,19 @@ static void ppce500_cpu_reset(void *opaque) mmubooke_create_initial_mapping(env); } -static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params, +static DeviceState *ppce500_init_mpic_qemu(PPCE500MachineState *pms, qemu_irq **irqs) { DeviceState *dev; SysBusDevice *s; int i, j, k; + MachineState *machine = MACHINE(pms); + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); dev = qdev_create(NULL, TYPE_OPENPIC); - object_property_add_child(qdev_get_machine(), "pic", OBJECT(dev), + object_property_add_child(OBJECT(machine), "pic", OBJECT(dev), &error_fatal); - qdev_prop_set_uint32(dev, "model", params->mpic_version); + qdev_prop_set_uint32(dev, "model", pmc->mpic_version); qdev_prop_set_uint32(dev, "nb_cpus", smp_cpus); qdev_init_nofail(dev); @@ -711,7 +712,7 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500Params *params, return dev; } -static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, +static DeviceState *ppce500_init_mpic_kvm(const PPCE500MachineClass *pmc, qemu_irq **irqs, Error **errp) { Error *err = NULL; @@ -719,7 +720,7 @@ static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, CPUState *cs; dev = qdev_create(NULL, TYPE_KVM_OPENPIC); - qdev_prop_set_uint32(dev, "model", params->mpic_version); + qdev_prop_set_uint32(dev, "model", pmc->mpic_version); object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { @@ -739,11 +740,12 @@ static DeviceState *ppce500_init_mpic_kvm(PPCE500Params *params, return dev; } -static DeviceState *ppce500_init_mpic(MachineState *machine, - PPCE500Params *params, +static DeviceState *ppce500_init_mpic(PPCE500MachineState *pms, MemoryRegion *ccsr, qemu_irq **irqs) { + MachineState *machine = MACHINE(pms); + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); DeviceState *dev = NULL; SysBusDevice *s; @@ -751,7 +753,7 @@ static DeviceState *ppce500_init_mpic(MachineState *machine, Error *err = NULL; if (machine_kernel_irqchip_allowed(machine)) { - dev = ppce500_init_mpic_kvm(params, irqs, &err); + dev = ppce500_init_mpic_kvm(pmc, irqs, &err); } if (machine_kernel_irqchip_required(machine) && !dev) { error_reportf_err(err, @@ -761,7 +763,7 @@ static DeviceState *ppce500_init_mpic(MachineState *machine, } if (!dev) { - dev = ppce500_init_mpic_qemu(params, irqs); + dev = ppce500_init_mpic_qemu(pms, irqs); } s = SYS_BUS_DEVICE(dev); @@ -778,10 +780,12 @@ static void ppce500_power_off(void *opaque, int line, int on) } } -void ppce500_init(MachineState *machine, PPCE500Params *params) +void ppce500_init(MachineState *machine) { MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); + PPCE500MachineState *pms = PPCE500_MACHINE(machine); + const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(machine); PCIBus *pci_bus; CPUPPCState *env = NULL; uint64_t loadaddr; @@ -835,8 +839,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; - env->mpic_iack = params->ccsrbar_base + - MPC8544_MPIC_REGS_OFFSET + 0xa0; + env->mpic_iack = pmc->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; ppc_booke_timers_init(cpu, 400000000, PPC_TIMER_E500); @@ -869,22 +872,22 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) qdev_init_nofail(dev); ccsr = CCSR(dev); ccsr_addr_space = &ccsr->ccsr_space; - memory_region_add_subregion(address_space_mem, params->ccsrbar_base, + memory_region_add_subregion(address_space_mem, pmc->ccsrbar_base, ccsr_addr_space); - mpicdev = ppce500_init_mpic(machine, params, ccsr_addr_space, irqs); + mpicdev = ppce500_init_mpic(pms, ccsr_addr_space, irqs); /* Serial */ - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(ccsr_addr_space, MPC8544_SERIAL0_REGS_OFFSET, 0, qdev_get_gpio_in(mpicdev, 42), 399193, - serial_hds[0], DEVICE_BIG_ENDIAN); + serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1]) { + if (serial_hd(1)) { serial_mm_init(ccsr_addr_space, MPC8544_SERIAL1_REGS_OFFSET, 0, qdev_get_gpio_in(mpicdev, 42), 399193, - serial_hds[1], DEVICE_BIG_ENDIAN); + serial_hd(1), DEVICE_BIG_ENDIAN); } /* General Utility device */ @@ -898,7 +901,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) dev = qdev_create(NULL, "e500-pcihost"); object_property_add_child(qdev_get_machine(), "pci-host", OBJECT(dev), &error_abort); - qdev_prop_set_uint32(dev, "first_slot", params->pci_first_slot); + qdev_prop_set_uint32(dev, "first_slot", pmc->pci_first_slot); qdev_prop_set_uint32(dev, "first_pin_irq", pci_irq_nrs[0]); qdev_init_nofail(dev); s = SYS_BUS_DEVICE(dev); @@ -921,9 +924,9 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) } /* Register spinning region */ - sysbus_create_simple("e500-spin", params->spin_base, NULL); + sysbus_create_simple("e500-spin", pmc->spin_base, NULL); - if (params->has_mpc8xxx_gpio) { + if (pmc->has_mpc8xxx_gpio) { qemu_irq poweroff_irq; dev = qdev_create(NULL, "mpc8xxx_gpio"); @@ -939,21 +942,22 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) } /* Platform Bus Device */ - if (params->has_platform_bus) { + if (pmc->has_platform_bus) { dev = qdev_create(NULL, TYPE_PLATFORM_BUS_DEVICE); dev->id = TYPE_PLATFORM_BUS_DEVICE; - qdev_prop_set_uint32(dev, "num_irqs", params->platform_bus_num_irqs); - qdev_prop_set_uint32(dev, "mmio_size", params->platform_bus_size); + qdev_prop_set_uint32(dev, "num_irqs", pmc->platform_bus_num_irqs); + qdev_prop_set_uint32(dev, "mmio_size", pmc->platform_bus_size); qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); + pms->pbus_dev = PLATFORM_BUS_DEVICE(dev); - for (i = 0; i < params->platform_bus_num_irqs; i++) { - int irqn = params->platform_bus_first_irq + i; + s = SYS_BUS_DEVICE(pms->pbus_dev); + for (i = 0; i < pmc->platform_bus_num_irqs; i++) { + int irqn = pmc->platform_bus_first_irq + i; sysbus_connect_irq(s, i, qdev_get_gpio_in(mpicdev, irqn)); } memory_region_add_subregion(address_space_mem, - params->platform_bus_base, + pmc->platform_bus_base, sysbus_mmio_get_region(s, 0)); } @@ -1009,9 +1013,9 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) } cur_base = loadaddr + payload_size; - if (cur_base < (32 * 1024 * 1024)) { + if (cur_base < 32 * MiB) { /* u-boot occupies memory up to 32MB, so load blobs above */ - cur_base = (32 * 1024 * 1024); + cur_base = 32 * MiB; } /* Load bare kernel only if no bios/u-boot has been provided */ @@ -1056,7 +1060,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) exit(1); } - dt_size = ppce500_prep_device_tree(machine, params, dt_base, + dt_size = ppce500_prep_device_tree(pms, dt_base, initrd_base, initrd_size, kernel_base, kernel_size); if (dt_size < 0) { @@ -1085,9 +1089,18 @@ static const TypeInfo e500_ccsr_info = { .instance_init = e500_ccsr_initfn, }; +static const TypeInfo ppce500_info = { + .name = TYPE_PPCE500_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(PPCE500MachineState), + .class_size = sizeof(PPCE500MachineClass), +}; + static void e500_register_types(void) { type_register_static(&e500_ccsr_info); + type_register_static(&ppce500_info); } type_init(e500_register_types) diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 70ba1d8f4f..3fd9f825ca 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -2,13 +2,27 @@ #define PPCE500_H #include "hw/boards.h" +#include "hw/platform-bus.h" -typedef struct PPCE500Params { - int pci_first_slot; - int pci_nr_slots; +typedef struct PPCE500MachineState { + /*< private >*/ + MachineState parent_obj; + + /* points to instance of TYPE_PLATFORM_BUS_DEVICE if + * board supports dynamic sysbus devices + */ + PlatformBusDevice *pbus_dev; +} PPCE500MachineState; + +typedef struct PPCE500MachineClass { + /*< private >*/ + MachineClass parent_class; /* required -- must at least add toplevel board compatible */ - void (*fixup_devtree)(struct PPCE500Params *params, void *fdt); + void (*fixup_devtree)(void *fdt); + + int pci_first_slot; + int pci_nr_slots; int mpic_version; bool has_mpc8xxx_gpio; @@ -22,10 +36,18 @@ typedef struct PPCE500Params { hwaddr pci_mmio_base; hwaddr pci_mmio_bus_base; hwaddr spin_base; -} PPCE500Params; +} PPCE500MachineClass; -void ppce500_init(MachineState *machine, PPCE500Params *params); +void ppce500_init(MachineState *machine); hwaddr booke206_page_size_to_tlb(uint64_t size); +#define TYPE_PPCE500_MACHINE "ppce500-base-machine" +#define PPCE500_MACHINE(obj) \ + OBJECT_CHECK(PPCE500MachineState, (obj), TYPE_PPCE500_MACHINE) +#define PPCE500_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PPCE500MachineClass, obj, TYPE_PPCE500_MACHINE) +#define PPCE500_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PPCE500MachineClass, klass, TYPE_PPCE500_MACHINE) + #endif diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 81d03e1038..963d429cc8 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -10,6 +10,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "e500.h" #include "hw/net/fsl_etsec/etsec.h" @@ -21,7 +22,7 @@ #include "hw/ppc/openpic.h" #include "kvm_ppc.h" -static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) +static void e500plat_fixup_devtree(void *fdt) { const char model[] = "QEMU ppce500"; const char compatible[] = "fsl,qemu-e500"; @@ -33,40 +34,86 @@ static void e500plat_fixup_devtree(PPCE500Params *params, void *fdt) static void e500plat_init(MachineState *machine) { - PPCE500Params params = { - .pci_first_slot = 0x1, - .pci_nr_slots = PCI_SLOT_MAX - 1, - .fixup_devtree = e500plat_fixup_devtree, - .mpic_version = OPENPIC_MODEL_FSL_MPIC_42, - .has_mpc8xxx_gpio = true, - .has_platform_bus = true, - .platform_bus_base = 0xf00000000ULL, - .platform_bus_size = (128ULL * 1024 * 1024), - .platform_bus_first_irq = 5, - .platform_bus_num_irqs = 10, - .ccsrbar_base = 0xFE0000000ULL, - .pci_pio_base = 0xFE1000000ULL, - .pci_mmio_base = 0xC00000000ULL, - .pci_mmio_bus_base = 0xE0000000ULL, - .spin_base = 0xFEF000000ULL, - }; - + PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(machine); /* Older KVM versions don't support EPR which breaks guests when we announce MPIC variants that support EPR. Revert to an older one for those */ if (kvm_enabled() && !kvmppc_has_cap_epr()) { - params.mpic_version = OPENPIC_MODEL_FSL_MPIC_20; + pmc->mpic_version = OPENPIC_MODEL_FSL_MPIC_20; } - ppce500_init(machine, ¶ms); + ppce500_init(machine); } -static void e500plat_machine_init(MachineClass *mc) +static void e500plat_machine_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { + PPCE500MachineState *pms = PPCE500_MACHINE(hotplug_dev); + + if (pms->pbus_dev) { + if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) { + platform_bus_link_device(pms->pbus_dev, SYS_BUS_DEVICE(dev)); + } + } +} + +static +HotplugHandler *e500plat_machine_get_hotpug_handler(MachineState *machine, + DeviceState *dev) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) { + return HOTPLUG_HANDLER(machine); + } + + return NULL; +} + +#define TYPE_E500PLAT_MACHINE MACHINE_TYPE_NAME("ppce500") + +static void e500plat_machine_class_init(ObjectClass *oc, void *data) +{ + PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + MachineClass *mc = MACHINE_CLASS(oc); + + assert(!mc->get_hotplug_handler); + mc->get_hotplug_handler = e500plat_machine_get_hotpug_handler; + hc->plug = e500plat_machine_device_plug_cb; + + pmc->pci_first_slot = 0x1; + pmc->pci_nr_slots = PCI_SLOT_MAX - 1; + pmc->fixup_devtree = e500plat_fixup_devtree; + pmc->mpic_version = OPENPIC_MODEL_FSL_MPIC_42; + pmc->has_mpc8xxx_gpio = true; + pmc->has_platform_bus = true; + pmc->platform_bus_base = 0xf00000000ULL; + pmc->platform_bus_size = 128 * MiB; + pmc->platform_bus_first_irq = 5; + pmc->platform_bus_num_irqs = 10; + pmc->ccsrbar_base = 0xFE0000000ULL; + pmc->pci_pio_base = 0xFE1000000ULL; + pmc->pci_mmio_base = 0xC00000000ULL; + pmc->pci_mmio_bus_base = 0xE0000000ULL; + pmc->spin_base = 0xFEF000000ULL; + mc->desc = "generic paravirt e500 platform"; mc->init = e500plat_init; mc->max_cpus = 32; - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON); mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); -} + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ETSEC_COMMON); + } -DEFINE_MACHINE("ppce500", e500plat_machine_init) +static const TypeInfo e500plat_info = { + .name = TYPE_E500PLAT_MACHINE, + .parent = TYPE_PPCE500_MACHINE, + .class_init = e500plat_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } +}; + +static void e500plat_register_types(void) +{ + type_register_static(&e500plat_info); +} +type_init(e500plat_register_types) diff --git a/hw/ppc/fdt.c b/hw/ppc/fdt.c index 2ffc5866e4..0828ad7254 100644 --- a/hw/ppc/fdt.c +++ b/hw/ppc/fdt.c @@ -9,19 +9,20 @@ #include "qemu/osdep.h" #include "target/ppc/cpu.h" +#include "target/ppc/mmu-hash64.h" #include "hw/ppc/fdt.h" #if defined(TARGET_PPC64) -size_t ppc_create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, - size_t maxsize) +size_t ppc_create_page_sizes_prop(PowerPCCPU *cpu, uint32_t *prop, + size_t maxsize) { size_t maxcells = maxsize / sizeof(uint32_t); int i, j, count; uint32_t *p = prop; for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; + PPCHash64SegmentPageSizes *sps = &cpu->hash64_opts->sps[i]; if (!sps->page_shift) { break; diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index a02f797598..41fd289e81 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -26,16 +26,20 @@ #ifndef PPC_MAC_H #define PPC_MAC_H +#include "qemu/units.h" #include "exec/memory.h" +#include "hw/boards.h" #include "hw/sysbus.h" #include "hw/ide/internal.h" #include "hw/input/adb.h" #include "hw/misc/mos6522.h" +#include "hw/pci/pci_host.h" +#include "hw/pci-host/uninorth.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 -#define BIOS_SIZE (1024 * 1024) +#define BIOS_SIZE (1 * MiB) #define NVRAM_SIZE 0x2000 #define PROM_FILENAME "openbios-ppc" #define PROM_ADDR 0xfff00000 @@ -45,6 +49,42 @@ #define ESCC_CLOCK 3686400 +/* Old World IRQs */ +#define OLDWORLD_CUDA_IRQ 0x12 +#define OLDWORLD_ESCCB_IRQ 0x10 +#define OLDWORLD_ESCCA_IRQ 0xf +#define OLDWORLD_IDE0_IRQ 0xd +#define OLDWORLD_IDE0_DMA_IRQ 0x2 +#define OLDWORLD_IDE1_IRQ 0xe +#define OLDWORLD_IDE1_DMA_IRQ 0x3 + +/* New World IRQs */ +#define NEWWORLD_CUDA_IRQ 0x19 +#define NEWWORLD_PMU_IRQ 0x19 +#define NEWWORLD_ESCCB_IRQ 0x24 +#define NEWWORLD_ESCCA_IRQ 0x25 +#define NEWWORLD_IDE0_IRQ 0xd +#define NEWWORLD_IDE0_DMA_IRQ 0x2 +#define NEWWORLD_IDE1_IRQ 0xe +#define NEWWORLD_IDE1_DMA_IRQ 0x3 +#define NEWWORLD_EXTING_GPIO1 0x2f +#define NEWWORLD_EXTING_GPIO9 0x37 + +/* Core99 machine */ +#define TYPE_CORE99_MACHINE MACHINE_TYPE_NAME("mac99") +#define CORE99_MACHINE(obj) OBJECT_CHECK(Core99MachineState, (obj), \ + TYPE_CORE99_MACHINE) + +#define CORE99_VIA_CONFIG_CUDA 0x0 +#define CORE99_VIA_CONFIG_PMU 0x1 +#define CORE99_VIA_CONFIG_PMU_ADB 0x2 + +typedef struct Core99MachineState { + /*< private >*/ + MachineState parent; + + uint8_t via_config; +} Core99MachineState; /* MacIO */ #define TYPE_MACIO_IDE "macio-ide" @@ -72,26 +112,8 @@ typedef struct MACIOIDEState { void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); void macio_ide_register_dma(MACIOIDEState *ide); -void macio_init(PCIDevice *dev, - MemoryRegion *pic_mem); - -/* Heathrow PIC */ -DeviceState *heathrow_pic_init(int nb_cpus, qemu_irq **irqs, - qemu_irq **pic_irqs); - /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" -PCIBus *pci_grackle_init(uint32_t base, qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); - -/* UniNorth PCI */ -PCIBus *pci_pmac_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); -PCIBus *pci_pmac_u3_init(qemu_irq *pic, - MemoryRegion *address_space_mem, - MemoryRegion *address_space_io); /* Mac NVRAM */ #define TYPE_MACIO_NVRAM "macio-nvram" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 2f5b6f651a..2ca294664b 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -71,7 +71,6 @@ #include "hw/usb.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" -#include "qemu/cutils.h" #include "trace.h" #define MAX_IDE_BUS 2 @@ -82,36 +81,6 @@ #define NDRV_VGA_FILENAME "qemu_vga.ndrv" -/* UniN device */ -static void unin_write(void *opaque, hwaddr addr, uint64_t value, - unsigned size) -{ - trace_mac99_uninorth_write(addr, value); - if (addr == 0x0) { - *(int*)opaque = value; - } -} - -static uint64_t unin_read(void *opaque, hwaddr addr, unsigned size) -{ - uint32_t value; - - value = 0; - switch (addr) { - case 0: - value = *(int*)opaque; - } - - trace_mac99_uninorth_read(addr, value); - - return value; -} - -static const MemoryRegionOps unin_ops = { - .read = unin_read, - .write = unin_write, - .endianness = DEVICE_NATIVE_ENDIAN, -}; static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) @@ -141,19 +110,19 @@ static void ppc_core99_init(MachineState *machine) const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; const char *boot_device = machine->boot_order; + Core99MachineState *core99_machine = CORE99_MACHINE(machine); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; - qemu_irq *pic, **openpic_irqs; - MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *unin_memory = g_new(MemoryRegion, 1); - MemoryRegion *unin2_memory = g_new(MemoryRegion, 1); + qemu_irq **openpic_irqs; int linux_boot, i, j, k; MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); hwaddr kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; + UNINHostState *uninorth_pci; PCIBus *pci_bus; NewWorldMacIOState *macio; + bool has_pmu, has_adb; MACIOIDEState *macio_ide; BusState *adb_bus; MacIONVRAMState *nvr; @@ -165,7 +134,6 @@ static void ppc_core99_init(MachineState *machine) int machine_arch; SysBusDevice *s; DeviceState *dev, *pic_dev; - int *token = g_new(int, 1); hwaddr nvram_addr = 0xFFF04000; uint64_t tbfreq; @@ -273,17 +241,12 @@ static void ppc_core99_init(MachineState *machine) } } - /* Register 8 MB of ISA IO space */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00800000); - memory_region_add_subregion(get_system_memory(), 0xf2000000, isa); - - /* UniN init: XXX should be a real device */ - memory_region_init_io(unin_memory, NULL, &unin_ops, token, "unin", 0x1000); - memory_region_add_subregion(get_system_memory(), 0xf8000000, unin_memory); - - memory_region_init_io(unin2_memory, NULL, &unin_ops, token, "unin", 0x1000); - memory_region_add_subregion(get_system_memory(), 0xf3000000, unin2_memory); + /* UniN init */ + dev = qdev_create(NULL, TYPE_UNI_NORTH); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + memory_region_add_subregion(get_system_memory(), 0xf8000000, + sysbus_mmio_get_region(s, 0)); openpic_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); openpic_irqs[0] = @@ -329,8 +292,6 @@ static void ppc_core99_init(MachineState *machine) } } - pic = g_new0(qemu_irq, 64); - pic_dev = qdev_create(NULL, TYPE_OPENPIC); qdev_prop_set_uint32(pic_dev, "model", OPENPIC_MODEL_KEYLARGO); qdev_init_nofail(pic_dev); @@ -342,21 +303,68 @@ static void ppc_core99_init(MachineState *machine) } } - for (i = 0; i < 64; i++) { - pic[i] = qdev_get_gpio_in(pic_dev, i); - } - if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { /* 970 gets a U3 bus */ - pci_bus = pci_pmac_u3_init(pic, get_system_memory(), get_system_io()); + /* Uninorth AGP bus */ + dev = qdev_create(NULL, TYPE_U3_AGP_HOST_BRIDGE); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); + uninorth_pci = U3_AGP_HOST_BRIDGE(dev); + s = SYS_BUS_DEVICE(dev); + /* PCI hole */ + memory_region_add_subregion(get_system_memory(), 0x80000000ULL, + sysbus_mmio_get_region(s, 2)); + /* Register 8 MB of ISA IO space */ + memory_region_add_subregion(get_system_memory(), 0xf2000000, + sysbus_mmio_get_region(s, 3)); + sysbus_mmio_map(s, 0, 0xf0800000); + sysbus_mmio_map(s, 1, 0xf0c00000); + machine_arch = ARCH_MAC99_U3; } else { - pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); + /* Use values found on a real PowerMac */ + /* Uninorth AGP bus */ + dev = qdev_create(NULL, TYPE_UNI_NORTH_AGP_HOST_BRIDGE); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, 0xf0800000); + sysbus_mmio_map(s, 1, 0xf0c00000); + + /* Uninorth internal bus */ + dev = qdev_create(NULL, TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, 0xf4800000); + sysbus_mmio_map(s, 1, 0xf4c00000); + + /* Uninorth main bus */ + dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); + uninorth_pci = UNI_NORTH_PCI_HOST_BRIDGE(dev); + s = SYS_BUS_DEVICE(dev); + /* PCI hole */ + memory_region_add_subregion(get_system_memory(), 0x80000000ULL, + sysbus_mmio_get_region(s, 2)); + /* Register 8 MB of ISA IO space */ + memory_region_add_subregion(get_system_memory(), 0xf2000000, + sysbus_mmio_get_region(s, 3)); + sysbus_mmio_map(s, 0, 0xf2800000); + sysbus_mmio_map(s, 1, 0xf2c00000); + machine_arch = ARCH_MAC99; } - object_property_set_bool(OBJECT(pci_bus), true, "realized", &error_abort); machine->usb |= defaults_enabled() && !machine->usb_disabled; + has_pmu = (core99_machine->via_config != CORE99_VIA_CONFIG_CUDA); + has_adb = (core99_machine->via_config == CORE99_VIA_CONFIG_CUDA || + core99_machine->via_config == CORE99_VIA_CONFIG_PMU_ADB); /* Timebase Frequency */ if (kvm_enabled()) { @@ -365,17 +373,15 @@ static void ppc_core99_init(MachineState *machine) tbfreq = TBFREQ; } + /* init basic PC hardware */ + pci_bus = PCI_HOST_BRIDGE(uninorth_pci)->bus; + /* MacIO */ macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); - qdev_connect_gpio_out(dev, 0, pic[0x19]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x24]); /* ESCC-B */ - qdev_connect_gpio_out(dev, 2, pic[0x25]); /* ESCC-A */ - qdev_connect_gpio_out(dev, 3, pic[0x0d]); /* IDE */ - qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE DMA */ - qdev_connect_gpio_out(dev, 5, pic[0x0e]); /* IDE */ - qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE DMA */ qdev_prop_set_uint64(dev, "frequency", tbfreq); + qdev_prop_set_bit(dev, "has-pmu", has_pmu); + qdev_prop_set_bit(dev, "has-adb", has_adb); object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", &error_abort); qdev_init_nofail(dev); @@ -391,19 +397,29 @@ static void ppc_core99_init(MachineState *machine) "ide[1]")); macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); - adb_bus = qdev_get_child_bus(dev, "adb.0"); - dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); - qdev_init_nofail(dev); - dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); - qdev_init_nofail(dev); + if (has_adb) { + if (has_pmu) { + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "pmu")); + } else { + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); + } + + adb_bus = qdev_get_child_bus(dev, "adb.0"); + dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true); + qdev_init_nofail(dev); + + dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", true); + qdev_init_nofail(dev); + } if (machine->usb) { pci_create_simple(pci_bus, -1, "pci-ohci"); /* U3 needs to use USB for input because Linux doesn't support via-cuda on PPC64 */ - if (machine_arch == ARCH_MAC99_U3) { + if (!has_adb || machine_arch == ARCH_MAC99_U3) { USBBus *usb_bus = usb_bus_find(-1); usb_create_simple(usb_bus, "usb-kbd"); @@ -459,6 +475,8 @@ static void ppc_core99_init(MachineState *machine) fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_VIACONFIG, core99_machine->via_config); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); if (kvm_enabled()) { #ifdef CONFIG_KVM @@ -507,6 +525,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_IDE; mc->max_cpus = MAX_CPUS; mc->default_boot_order = "cd"; + mc->default_display = "std"; mc->kvm_type = core99_kvm_type; #ifdef TARGET_PPC64 mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("970fx_v3.1"); @@ -515,10 +534,61 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) #endif } +static char *core99_get_via_config(Object *obj, Error **errp) +{ + Core99MachineState *cms = CORE99_MACHINE(obj); + + switch (cms->via_config) { + default: + case CORE99_VIA_CONFIG_CUDA: + return g_strdup("cuda"); + + case CORE99_VIA_CONFIG_PMU: + return g_strdup("pmu"); + + case CORE99_VIA_CONFIG_PMU_ADB: + return g_strdup("pmu-adb"); + } +} + +static void core99_set_via_config(Object *obj, const char *value, Error **errp) +{ + Core99MachineState *cms = CORE99_MACHINE(obj); + + if (!strcmp(value, "cuda")) { + cms->via_config = CORE99_VIA_CONFIG_CUDA; + } else if (!strcmp(value, "pmu")) { + cms->via_config = CORE99_VIA_CONFIG_PMU; + } else if (!strcmp(value, "pmu-adb")) { + cms->via_config = CORE99_VIA_CONFIG_PMU_ADB; + } else { + error_setg(errp, "Invalid via value"); + error_append_hint(errp, "Valid values are cuda, pmu, pmu-adb.\n"); + } +} + +static void core99_instance_init(Object *obj) +{ + Core99MachineState *cms = CORE99_MACHINE(obj); + + /* Default via_config is CORE99_VIA_CONFIG_CUDA */ + cms->via_config = CORE99_VIA_CONFIG_CUDA; + object_property_add_str(obj, "via", core99_get_via_config, + core99_set_via_config, NULL); + object_property_set_description(obj, "via", + "Set VIA configuration. " + "Valid values are cuda, pmu and pmu-adb", + NULL); + + return; +} + static const TypeInfo core99_machine_info = { .name = MACHINE_TYPE_NAME("mac99"), .parent = TYPE_MACHINE, .class_init = core99_machine_class_init, + .instance_init = core99_instance_init, + .instance_size = sizeof(Core99MachineState) }; static void mac_machine_register_types(void) diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 10e291ca22..064d7eb30a 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" @@ -34,6 +35,7 @@ #include "net/net.h" #include "hw/isa/isa.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" @@ -45,7 +47,6 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "exec/address-spaces.h" -#include "qemu/cutils.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -55,6 +56,8 @@ #define NDRV_VGA_FILENAME "qemu_vga.ndrv" +#define GRACKLE_BASE 0xfec00000 + static void fw_cfg_boot_set(void *opaque, const char *boot_device, Error **errp) { @@ -84,16 +87,15 @@ static void ppc_heathrow_init(MachineState *machine) PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; - qemu_irq *pic, **heathrow_irqs; int linux_boot, i; MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); - MemoryRegion *isa = g_new(MemoryRegion, 1); uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; PCIBus *pci_bus; OldWorldMacIOState *macio; MACIOIDEState *macio_ide; + SysBusDevice *s; DeviceState *dev, *pic_dev; BusState *adb_bus; int bios_size, ndrv_size; @@ -116,10 +118,9 @@ static void ppc_heathrow_init(MachineState *machine) } /* allocate RAM */ - if (ram_size > (2047 << 20)) { - fprintf(stderr, - "qemu: Too much memory for this machine: %d MB, maximum 2047 MB\n", - ((unsigned int)ram_size / (1 << 20))); + if (ram_size > 2047 * MiB) { + error_report("Too much memory for this machine: %" PRId64 " MB, " + "maximum 2047 MB", ram_size / MiB); exit(1); } @@ -221,22 +222,16 @@ static void ppc_heathrow_init(MachineState *machine) } } - /* Register 2 MB of ISA IO space */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00200000); - memory_region_add_subregion(sysmem, 0xfe000000, isa); - /* XXX: we register only 1 output pin for heathrow PIC */ - heathrow_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); - heathrow_irqs[0] = - g_malloc0(smp_cpus * sizeof(qemu_irq) * 1); + pic_dev = qdev_create(NULL, TYPE_HEATHROW); + qdev_init_nofail(pic_dev); + /* Connect the heathrow PIC outputs to the 6xx bus */ for (i = 0; i < smp_cpus; i++) { switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_6xx: - heathrow_irqs[i] = heathrow_irqs[0] + (i * 1); - heathrow_irqs[i][0] = - ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; + qdev_connect_gpio_out(pic_dev, 0, + ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]); break; default: error_report("Bus model not supported on OldWorld Mac machine"); @@ -256,10 +251,24 @@ static void ppc_heathrow_init(MachineState *machine) error_report("Only 6xx bus is supported on heathrow machine"); exit(1); } - pic_dev = heathrow_pic_init(1, heathrow_irqs, &pic); - pci_bus = pci_grackle_init(0xfec00000, pic, - get_system_memory(), - get_system_io()); + + /* Grackle PCI host bridge */ + dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); + object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, GRACKLE_BASE); + sysbus_mmio_map(s, 1, GRACKLE_BASE + 0x200000); + /* PCI hole */ + memory_region_add_subregion(get_system_memory(), 0x80000000ULL, + sysbus_mmio_get_region(s, 2)); + /* Register 2 MB of ISA IO space */ + memory_region_add_subregion(get_system_memory(), 0xfe000000, + sysbus_mmio_get_region(s, 3)); + + pci_bus = PCI_HOST_BRIDGE(dev)->bus; + pci_vga_init(pci_bus); for (i = 0; i < nb_nics; i++) { @@ -271,13 +280,6 @@ static void ppc_heathrow_init(MachineState *machine) /* MacIO */ macio = OLDWORLD_MACIO(pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO)); dev = DEVICE(macio); - qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x10]); /* ESCC-B */ - qdev_connect_gpio_out(dev, 2, pic[0x0F]); /* ESCC-A */ - qdev_connect_gpio_out(dev, 3, pic[0x0D]); /* IDE-0 */ - qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE-0 DMA */ - qdev_connect_gpio_out(dev, 5, pic[0x0E]); /* IDE-1 */ - qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE-1 DMA */ qdev_prop_set_uint64(dev, "frequency", tbfreq); object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", &error_abort); @@ -381,6 +383,7 @@ static void heathrow_class_init(ObjectClass *oc, void *data) mc->default_boot_order = "cd"; mc->kvm_type = heathrow_kvm_type; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("750_v3.1"); + mc->default_display = "std"; } static const TypeInfo ppc_heathrow_machine_info = { diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index 1717953ec7..ab30a2a99e 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -18,7 +18,7 @@ #include "qemu/error-report.h" #include "cpu.h" -static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) +static void mpc8544ds_fixup_devtree(void *fdt) { const char model[] = "MPC8544DS"; const char compatible[] = "MPC8544DS\0MPC85xxDS"; @@ -30,33 +30,46 @@ static void mpc8544ds_fixup_devtree(PPCE500Params *params, void *fdt) static void mpc8544ds_init(MachineState *machine) { - PPCE500Params params = { - .pci_first_slot = 0x11, - .pci_nr_slots = 2, - .fixup_devtree = mpc8544ds_fixup_devtree, - .mpic_version = OPENPIC_MODEL_FSL_MPIC_20, - .ccsrbar_base = 0xE0000000ULL, - .pci_mmio_base = 0xC0000000ULL, - .pci_mmio_bus_base = 0xC0000000ULL, - .pci_pio_base = 0xE1000000ULL, - .spin_base = 0xEF000000ULL, - }; - if (machine->ram_size > 0xc0000000) { error_report("The MPC8544DS board only supports up to 3GB of RAM"); exit(1); } - ppce500_init(machine, ¶ms); + ppce500_init(machine); } - -static void ppce500_machine_init(MachineClass *mc) +static void e500plat_machine_class_init(ObjectClass *oc, void *data) { + MachineClass *mc = MACHINE_CLASS(oc); + PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); + + pmc->pci_first_slot = 0x11; + pmc->pci_nr_slots = 2; + pmc->fixup_devtree = mpc8544ds_fixup_devtree; + pmc->mpic_version = OPENPIC_MODEL_FSL_MPIC_20; + pmc->ccsrbar_base = 0xE0000000ULL; + pmc->pci_mmio_base = 0xC0000000ULL; + pmc->pci_mmio_bus_base = 0xC0000000ULL; + pmc->pci_pio_base = 0xE1000000ULL; + pmc->spin_base = 0xEF000000ULL; + mc->desc = "mpc8544ds"; mc->init = mpc8544ds_init; mc->max_cpus = 15; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("e500v2_v30"); } -DEFINE_MACHINE("mpc8544ds", ppce500_machine_init) +#define TYPE_MPC8544DS_MACHINE MACHINE_TYPE_NAME("mpc8544ds") + +static const TypeInfo mpc8544ds_info = { + .name = TYPE_MPC8544DS_MACHINE, + .parent = TYPE_PPCE500_MACHINE, + .class_init = e500plat_machine_class_init, +}; + +static void mpc8544ds_register_types(void) +{ + type_register_static(&mpc8544ds_info); +} + +type_init(mpc8544ds_register_types) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 98ee3c607a..346f5e7aed 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "sysemu/sysemu.h" #include "sysemu/numa.h" @@ -31,11 +32,11 @@ #include "hw/ppc/pnv_core.h" #include "hw/loader.h" #include "exec/address-spaces.h" -#include "qemu/cutils.h" #include "qapi/visitor.h" #include "monitor/monitor.h" #include "hw/intc/intc.h" #include "hw/ipmi/ipmi.h" +#include "target/ppc/mmu-hash64.h" #include "hw/ppc/xics.h" #include "hw/ppc/pnv_xscom.h" @@ -120,9 +121,9 @@ static int get_cpus_node(void *fdt) */ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) { - CPUState *cs = CPU(DEVICE(pc->threads)); + PowerPCCPU *cpu = pc->threads[0]; + CPUState *cs = CPU(cpu); DeviceClass *dc = DEVICE_GET_CLASS(cs); - PowerPCCPU *cpu = POWERPC_CPU(cs); int smt_threads = CPU_CORE(pc)->nr_threads; CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); @@ -179,7 +180,7 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size))); _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); @@ -187,7 +188,7 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0))); } - if (env->mmu_model & POWERPC_MMU_1TSEG) { + if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", segs, sizeof(segs)))); } @@ -209,8 +210,8 @@ static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt) _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); } - page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop, - sizeof(page_sizes_prop)); + page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop, + sizeof(page_sizes_prop)); if (page_sizes_prop_size) { _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", page_sizes_prop, page_sizes_prop_size))); @@ -264,18 +265,6 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir, g_free(reg); } -static int pnv_chip_lpc_offset(PnvChip *chip, void *fdt) -{ - char *name; - int offset; - - name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x", - (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE); - offset = fdt_path_offset(fdt, name); - g_free(name); - return offset; -} - static void pnv_dt_chip(PnvChip *chip, void *fdt) { const char *typename = pnv_chip_core_typename(chip); @@ -284,16 +273,6 @@ static void pnv_dt_chip(PnvChip *chip, void *fdt) pnv_dt_xscom(chip, fdt, 0); - /* The default LPC bus of a multichip system is on chip 0. It's - * recognized by the firmware (skiboot) using a "primary" - * property. - */ - if (chip->chip_id == 0x0) { - int lpc_offset = pnv_chip_lpc_offset(chip, fdt); - - _FDT((fdt_setprop(fdt, lpc_offset, "primary", NULL, 0))); - } - for (i = 0; i < chip->nr_cores; i++) { PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); @@ -417,16 +396,35 @@ static int pnv_dt_isa_device(DeviceState *dev, void *opaque) return 0; } -static void pnv_dt_isa(ISABus *bus, void *fdt, int lpc_offset) +static int pnv_chip_isa_offset(PnvChip *chip, void *fdt) { + char *name; + int offset; + + name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x", + (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE); + offset = fdt_path_offset(fdt, name); + g_free(name); + return offset; +} + +/* The default LPC bus of a multichip system is on chip 0. It's + * recognized by the firmware (skiboot) using a "primary" property. + */ +static void pnv_dt_isa(PnvMachineState *pnv, void *fdt) +{ + int isa_offset = pnv_chip_isa_offset(pnv->chips[0], fdt); ForeachPopulateArgs args = { .fdt = fdt, - .offset = lpc_offset, + .offset = isa_offset, }; + _FDT((fdt_setprop(fdt, isa_offset, "primary", NULL, 0))); + /* ISA devices are not necessarily parented to the ISA bus so we * can not use object_child_foreach() */ - qbus_walk_children(BUS(bus), pnv_dt_isa_device, NULL, NULL, NULL, &args); + qbus_walk_children(BUS(pnv->isa_bus), pnv_dt_isa_device, NULL, NULL, NULL, + &args); } static void *pnv_dt_create(MachineState *machine) @@ -437,7 +435,6 @@ static void *pnv_dt_create(MachineState *machine) char *buf; int off; int i; - int lpc_offset; fdt = g_malloc0(FDT_MAX_SIZE); _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); @@ -479,8 +476,7 @@ static void *pnv_dt_create(MachineState *machine) } /* Populate ISA devices on chip 0 */ - lpc_offset = pnv_chip_lpc_offset(pnv->chips[0], fdt); - pnv_dt_isa(pnv->isa_bus, fdt, lpc_offset); + pnv_dt_isa(pnv, fdt); if (pnv->bmc) { pnv_dt_bmc_sensors(pnv->bmc, fdt); @@ -528,24 +524,26 @@ static void pnv_reset(void) cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); } -static ISABus *pnv_isa_create(PnvChip *chip) +static ISABus *pnv_chip_power8_isa_create(PnvChip *chip, Error **errp) { - PnvLpcController *lpc = &chip->lpc; - ISABus *isa_bus; - qemu_irq *irqs; - PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + Pnv8Chip *chip8 = PNV8_CHIP(chip); + return pnv_lpc_isa_create(&chip8->lpc, true, errp); +} - /* let isa_bus_new() create its own bridge on SysBus otherwise - * devices speficied on the command line won't find the bus and - * will fail to create. - */ - isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, - &error_fatal); +static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp) +{ + Pnv8Chip *chip8 = PNV8_CHIP(chip); + return pnv_lpc_isa_create(&chip8->lpc, false, errp); +} - irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS); +static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) +{ + return NULL; +} - isa_bus_irqs(isa_bus, irqs); - return isa_bus; +static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) +{ + return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); } static void pnv_init(MachineState *machine) @@ -558,7 +556,7 @@ static void pnv_init(MachineState *machine) char *chip_typename; /* allocate RAM */ - if (machine->ram_size < (1 * G_BYTE)) { + if (machine->ram_size < (1 * GiB)) { warn_report("skiboot may not work with < 1GB of RAM"); } @@ -645,10 +643,10 @@ static void pnv_init(MachineState *machine) g_free(chip_typename); /* Instantiate ISA bus on chip 0 */ - pnv->isa_bus = pnv_isa_create(pnv->chips[0]); + pnv->isa_bus = pnv_isa_create(pnv->chips[0], &error_fatal); /* Create serial port */ - serial_hds_isa_init(pnv->isa_bus, 0, MAX_SERIAL_PORTS); + serial_hds_isa_init(pnv->isa_bus, 0, MAX_ISA_SERIAL_PORTS); /* Create an RTC ISA device too */ mc146818_rtc_init(pnv->isa_bus, 2000, NULL); @@ -670,6 +668,13 @@ static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id) return (chip->chip_id << 7) | (core_id << 3); } +static Object *pnv_chip_power8_intc_create(PnvChip *chip, Object *child, + Error **errp) +{ + return icp_create(child, TYPE_PNV_ICP, XICS_FABRIC(qdev_get_machine()), + errp); +} + /* * 0:48 Reserved - Read as zeroes * 49:52 Node ID @@ -685,6 +690,12 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id) return (chip->chip_id << 8) | (core_id << 2); } +static Object *pnv_chip_power9_intc_create(PnvChip *chip, Object *child, + Error **errp) +{ + return NULL; +} + /* Allowed core identifiers on a POWER8 Processor Chip : * * @@ -711,6 +722,103 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id) */ #define POWER9_CORE_MASK (0xffffffffffffffull) +static void pnv_chip_power8_instance_init(Object *obj) +{ + Pnv8Chip *chip8 = PNV8_CHIP(obj); + + object_initialize(&chip8->psi, sizeof(chip8->psi), TYPE_PNV_PSI); + object_property_add_child(obj, "psi", OBJECT(&chip8->psi), NULL); + object_property_add_const_link(OBJECT(&chip8->psi), "xics", + OBJECT(qdev_get_machine()), &error_abort); + + object_initialize(&chip8->lpc, sizeof(chip8->lpc), TYPE_PNV_LPC); + object_property_add_child(obj, "lpc", OBJECT(&chip8->lpc), NULL); + object_property_add_const_link(OBJECT(&chip8->lpc), "psi", + OBJECT(&chip8->psi), &error_abort); + + object_initialize(&chip8->occ, sizeof(chip8->occ), TYPE_PNV_OCC); + object_property_add_child(obj, "occ", OBJECT(&chip8->occ), NULL); + object_property_add_const_link(OBJECT(&chip8->occ), "psi", + OBJECT(&chip8->psi), &error_abort); +} + +static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) + { + PnvChip *chip = PNV_CHIP(chip8); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + const char *typename = pnv_chip_core_typename(chip); + size_t typesize = object_type_get_instance_size(typename); + int i, j; + char *name; + XICSFabric *xi = XICS_FABRIC(qdev_get_machine()); + + name = g_strdup_printf("icp-%x", chip->chip_id); + memory_region_init(&chip8->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip8->icp_mmio); + g_free(name); + + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip)); + + /* Map the ICP registers for each thread */ + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + int core_hwid = CPU_CORE(pnv_core)->core_id; + + for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { + uint32_t pir = pcc->core_pir(chip, core_hwid) + j; + PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir)); + + memory_region_add_subregion(&chip8->icp_mmio, pir << 12, + &icp->mmio); + } + } +} + +static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); + PnvChip *chip = PNV_CHIP(dev); + Pnv8Chip *chip8 = PNV8_CHIP(dev); + Error *local_err = NULL; + + pcc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip8->psi), PNV_PSIHB_BASE(chip), + "bar", &error_fatal); + object_property_set_bool(OBJECT(&chip8->psi), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip8->psi.xscom_regs); + + /* Create LPC controller */ + object_property_set_bool(OBJECT(&chip8->lpc), true, "realized", + &error_fatal); + pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip8->lpc.xscom_regs); + + /* Interrupt Management Area. This is the memory region holding + * all the Interrupt Control Presenter (ICP) registers */ + pnv_chip_icp_realize(chip8, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Create the simplified OCC model */ + object_property_set_bool(OBJECT(&chip8->occ), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip8->occ.xscom_regs); +} + static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -720,8 +828,13 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */ k->cores_mask = POWER8E_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; + k->intc_create = pnv_chip_power8_intc_create; + k->isa_create = pnv_chip_power8_isa_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8E"; + + device_class_set_parent_realize(dc, pnv_chip_power8_realize, + &k->parent_realize); } static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) @@ -733,8 +846,13 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */ k->cores_mask = POWER8_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; + k->intc_create = pnv_chip_power8_intc_create; + k->isa_create = pnv_chip_power8_isa_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8"; + + device_class_set_parent_realize(dc, pnv_chip_power8_realize, + &k->parent_realize); } static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) @@ -746,8 +864,29 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */ k->cores_mask = POWER8_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; + k->intc_create = pnv_chip_power8_intc_create; + k->isa_create = pnv_chip_power8nvl_isa_create; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8NVL"; + + device_class_set_parent_realize(dc, pnv_chip_power8_realize, + &k->parent_realize); +} + +static void pnv_chip_power9_instance_init(Object *obj) +{ +} + +static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); + Error *local_err = NULL; + + pcc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } } static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) @@ -759,8 +898,13 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */ k->cores_mask = POWER9_CORE_MASK; k->core_pir = pnv_chip_core_pir_p9; + k->intc_create = pnv_chip_power9_intc_create; + k->isa_create = pnv_chip_power9_isa_create; k->xscom_base = 0x00603fc00000000ull; dc->desc = "PowerNV Chip POWER9"; + + device_class_set_parent_realize(dc, pnv_chip_power9_realize, + &k->parent_realize); } static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) @@ -793,64 +937,13 @@ static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) } } -static void pnv_chip_init(Object *obj) +static void pnv_chip_instance_init(Object *obj) { - PnvChip *chip = PNV_CHIP(obj); - PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); - - chip->xscom_base = pcc->xscom_base; - - object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC); - object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL); - - object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI); - object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL); - object_property_add_const_link(OBJECT(&chip->psi), "xics", - OBJECT(qdev_get_machine()), &error_abort); - - object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC); - object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL); - object_property_add_const_link(OBJECT(&chip->occ), "psi", - OBJECT(&chip->psi), &error_abort); - - /* The LPC controller needs PSI to generate interrupts */ - object_property_add_const_link(OBJECT(&chip->lpc), "psi", - OBJECT(&chip->psi), &error_abort); + PNV_CHIP(obj)->xscom_base = PNV_CHIP_GET_CLASS(obj)->xscom_base; } -static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) +static void pnv_chip_core_realize(PnvChip *chip, Error **errp) { - PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); - int i, j; - char *name; - XICSFabric *xi = XICS_FABRIC(qdev_get_machine()); - - name = g_strdup_printf("icp-%x", chip->chip_id); - memory_region_init(&chip->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE); - sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip->icp_mmio); - g_free(name); - - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip)); - - /* Map the ICP registers for each thread */ - for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); - int core_hwid = CPU_CORE(pnv_core)->core_id; - - for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { - uint32_t pir = pcc->core_pir(chip, core_hwid) + j; - PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir)); - - memory_region_add_subregion(&chip->icp_mmio, pir << 12, &icp->mmio); - } - } -} - -static void pnv_chip_realize(DeviceState *dev, Error **errp) -{ - PnvChip *chip = PNV_CHIP(dev); Error *error = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); const char *typename = pnv_chip_core_typename(chip); @@ -862,14 +955,6 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) return; } - /* XSCOM bridge */ - pnv_xscom_realize(chip, &error); - if (error) { - error_propagate(errp, error); - return; - } - sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip)); - /* Cores */ pnv_chip_core_sanitize(chip, &error); if (error) { @@ -900,8 +985,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) object_property_set_int(OBJECT(pnv_core), pcc->core_pir(chip, core_hwid), "pir", &error_fatal); - object_property_add_const_link(OBJECT(pnv_core), "xics", - qdev_get_machine(), &error_fatal); + object_property_add_const_link(OBJECT(pnv_core), "chip", + OBJECT(chip), &error_fatal); object_property_set_bool(OBJECT(pnv_core), true, "realized", &error_fatal); object_unref(OBJECT(pnv_core)); @@ -917,37 +1002,27 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) &PNV_CORE(pnv_core)->xscom_regs); i++; } +} - /* Create LPC controller */ - object_property_set_bool(OBJECT(&chip->lpc), true, "realized", - &error_fatal); - pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs); +static void pnv_chip_realize(DeviceState *dev, Error **errp) +{ + PnvChip *chip = PNV_CHIP(dev); + Error *error = NULL; - /* Interrupt Management Area. This is the memory region holding - * all the Interrupt Control Presenter (ICP) registers */ - pnv_chip_icp_realize(chip, &error); + /* XSCOM bridge */ + pnv_xscom_realize(chip, &error); if (error) { error_propagate(errp, error); return; } + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip)); - /* Processor Service Interface (PSI) Host Bridge */ - object_property_set_int(OBJECT(&chip->psi), PNV_PSIHB_BASE(chip), - "bar", &error_fatal); - object_property_set_bool(OBJECT(&chip->psi), true, "realized", &error); + /* Cores */ + pnv_chip_core_realize(chip, &error); if (error) { error_propagate(errp, error); return; } - pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip->psi.xscom_regs); - - /* Create the simplified OCC model */ - object_property_set_bool(OBJECT(&chip->occ), true, "realized", &error); - if (error) { - error_propagate(errp, error); - return; - } - pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip->occ.xscom_regs); } static Property pnv_chip_properties[] = { @@ -975,8 +1050,10 @@ static ICSState *pnv_ics_get(XICSFabric *xi, int irq) int i; for (i = 0; i < pnv->num_chips; i++) { - if (ics_valid_irq(&pnv->chips[i]->psi.ics, irq)) { - return &pnv->chips[i]->psi.ics; + Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); + + if (ics_valid_irq(&chip8->psi.ics, irq)) { + return &chip8->psi.ics; } } return NULL; @@ -988,7 +1065,8 @@ static void pnv_ics_resend(XICSFabric *xi) int i; for (i = 0; i < pnv->num_chips; i++) { - ics_resend(&pnv->chips[i]->psi.ics); + Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); + ics_resend(&chip8->psi.ics); } } @@ -1029,7 +1107,8 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, } for (i = 0; i < pnv->num_chips; i++) { - ics_pic_print_info(&pnv->chips[i]->psi.ics, mon); + Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); + ics_pic_print_info(&chip8->psi.ics, mon); } } @@ -1064,7 +1143,7 @@ static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name, pnv->num_chips = num_chips; } -static void pnv_machine_initfn(Object *obj) +static void pnv_machine_instance_init(Object *obj) { PnvMachineState *pnv = PNV_MACHINE(obj); pnv->num_chips = 1; @@ -1095,7 +1174,7 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) * storage */ mc->no_parallel = 1; mc->default_boot_order = NULL; - mc->default_ram_size = 1 * G_BYTE; + mc->default_ram_size = 1 * GiB; xic->icp_get = pnv_icp_get; xic->ics_get = pnv_ics_get; xic->ics_resend = pnv_ics_resend; @@ -1104,11 +1183,18 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) pnv_machine_class_props_init(oc); } -#define DEFINE_PNV_CHIP_TYPE(type, class_initfn) \ - { \ - .name = type, \ - .class_init = class_initfn, \ - .parent = TYPE_PNV_CHIP, \ +#define DEFINE_PNV8_CHIP_TYPE(type, class_initfn) \ + { \ + .name = type, \ + .class_init = class_initfn, \ + .parent = TYPE_PNV8_CHIP, \ + } + +#define DEFINE_PNV9_CHIP_TYPE(type, class_initfn) \ + { \ + .name = type, \ + .class_init = class_initfn, \ + .parent = TYPE_PNV9_CHIP, \ } static const TypeInfo types[] = { @@ -1116,7 +1202,7 @@ static const TypeInfo types[] = { .name = TYPE_PNV_MACHINE, .parent = TYPE_MACHINE, .instance_size = sizeof(PnvMachineState), - .instance_init = pnv_machine_initfn, + .instance_init = pnv_machine_instance_init, .class_init = pnv_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_XICS_FABRIC }, @@ -1128,16 +1214,36 @@ static const TypeInfo types[] = { .name = TYPE_PNV_CHIP, .parent = TYPE_SYS_BUS_DEVICE, .class_init = pnv_chip_class_init, - .instance_init = pnv_chip_init, + .instance_init = pnv_chip_instance_init, .instance_size = sizeof(PnvChip), .class_size = sizeof(PnvChipClass), .abstract = true, }, - DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER9, pnv_chip_power9_class_init), - DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8, pnv_chip_power8_class_init), - DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8E, pnv_chip_power8e_class_init), - DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8NVL, - pnv_chip_power8nvl_class_init), + + /* + * P9 chip and variants + */ + { + .name = TYPE_PNV9_CHIP, + .parent = TYPE_PNV_CHIP, + .instance_init = pnv_chip_power9_instance_init, + .instance_size = sizeof(Pnv9Chip), + }, + DEFINE_PNV9_CHIP_TYPE(TYPE_PNV_CHIP_POWER9, pnv_chip_power9_class_init), + + /* + * P8 chip and variants + */ + { + .name = TYPE_PNV8_CHIP, + .parent = TYPE_PNV_CHIP, + .instance_init = pnv_chip_power8_instance_init, + .instance_size = sizeof(Pnv8Chip), + }, + DEFINE_PNV8_CHIP_TYPE(TYPE_PNV_CHIP_POWER8, pnv_chip_power8_class_init), + DEFINE_PNV8_CHIP_TYPE(TYPE_PNV_CHIP_POWER8E, pnv_chip_power8e_class_init), + DEFINE_PNV8_CHIP_TYPE(TYPE_PNV_CHIP_POWER8NVL, + pnv_chip_power8nvl_class_init), }; DEFINE_TYPES(types) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index cbb64ad9e7..9750464bf4 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -54,28 +54,6 @@ static void pnv_cpu_reset(void *opaque) env->msr |= MSR_HVB; /* Hypervisor mode */ } -static void pnv_cpu_init(PowerPCCPU *cpu, Error **errp) -{ - CPUPPCState *env = &cpu->env; - int core_pir; - int thread_index = 0; /* TODO: TCG supports only one thread */ - ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; - - core_pir = object_property_get_uint(OBJECT(cpu), "core-pir", &error_abort); - - /* - * The PIR of a thread is the core PIR + the thread index. We will - * need to find a way to get the thread index when TCG supports - * more than 1. We could use the object name ? - */ - pir->default_value = core_pir + thread_index; - - /* Set time-base frequency to 512 MHz */ - cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); - - qemu_register_reset(pnv_cpu_reset, cpu); -} - /* * These values are read by the PowerNV HW monitors under Linux */ @@ -97,7 +75,7 @@ static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr, val = 0x24f000000000000ull; break; default: - qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx, + qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx "\n", addr); } @@ -107,7 +85,7 @@ static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr, static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned int width) { - qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx, + qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n", addr); } @@ -121,29 +99,40 @@ static const MemoryRegionOps pnv_core_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp) +static void pnv_realize_vcpu(PowerPCCPU *cpu, PnvChip *chip, Error **errp) { + CPUPPCState *env = &cpu->env; + int core_pir; + int thread_index = 0; /* TODO: TCG supports only one thread */ + ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; Error *local_err = NULL; - CPUState *cs = CPU(child); - PowerPCCPU *cpu = POWERPC_CPU(cs); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); - object_property_set_bool(child, true, "realized", &local_err); + object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); return; } - cpu->intc = icp_create(child, TYPE_PNV_ICP, xi, &local_err); + cpu->intc = pcc->intc_create(chip, OBJECT(cpu), &local_err); if (local_err) { error_propagate(errp, local_err); return; } - pnv_cpu_init(cpu, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + core_pir = object_property_get_uint(OBJECT(cpu), "core-pir", &error_abort); + + /* + * The PIR of a thread is the core PIR + the thread index. We will + * need to find a way to get the thread index when TCG supports + * more than 1. We could use the object name ? + */ + pir->default_value = core_pir + thread_index; + + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); + + qemu_register_reset(pnv_cpu_reset, cpu); } static void pnv_core_realize(DeviceState *dev, Error **errp) @@ -151,40 +140,34 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) PnvCore *pc = PNV_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(OBJECT(dev)); const char *typename = pnv_core_cpu_typename(pc); - size_t size = object_type_get_instance_size(typename); Error *local_err = NULL; void *obj; int i, j; char name[32]; - Object *xi; + Object *chip; - xi = object_property_get_link(OBJECT(dev), "xics", &local_err); - if (!xi) { - error_setg(errp, "%s: required link 'xics' not found: %s", - __func__, error_get_pretty(local_err)); + chip = object_property_get_link(OBJECT(dev), "chip", &local_err); + if (!chip) { + error_propagate(errp, local_err); + error_prepend(errp, "required link 'chip' not found: "); return; } - pc->threads = g_malloc0(size * cc->nr_threads); + pc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - obj = pc->threads + i * size; + obj = object_new(typename); - object_initialize(obj, size, typename); + pc->threads[i] = POWERPC_CPU(obj); snprintf(name, sizeof(name), "thread[%d]", i); - object_property_add_child(OBJECT(pc), name, obj, &local_err); + object_property_add_child(OBJECT(pc), name, obj, &error_abort); object_property_add_alias(obj, "core-pir", OBJECT(pc), - "pir", &local_err); - if (local_err) { - goto err; - } + "pir", &error_abort); object_unref(obj); } for (j = 0; j < cc->nr_threads; j++) { - obj = pc->threads + j * size; - - pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err); + pnv_realize_vcpu(pc->threads[j], PNV_CHIP(chip), &local_err); if (local_err) { goto err; } @@ -197,13 +180,33 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) err: while (--i >= 0) { - obj = pc->threads + i * size; + obj = OBJECT(pc->threads[i]); object_unparent(obj); } g_free(pc->threads); error_propagate(errp, local_err); } +static void pnv_unrealize_vcpu(PowerPCCPU *cpu) +{ + qemu_unregister_reset(pnv_cpu_reset, cpu); + object_unparent(cpu->intc); + cpu_remove_sync(CPU(cpu)); + object_unparent(OBJECT(cpu)); +} + +static void pnv_core_unrealize(DeviceState *dev, Error **errp) +{ + PnvCore *pc = PNV_CORE(dev); + CPUCore *cc = CPU_CORE(dev); + int i; + + for (i = 0; i < cc->nr_threads; i++) { + pnv_unrealize_vcpu(pc->threads[i]); + } + g_free(pc->threads); +} + static Property pnv_core_properties[] = { DEFINE_PROP_UINT32("pir", PnvCore, pir, 0), DEFINE_PROP_END_OF_LIST(), @@ -214,6 +217,7 @@ static void pnv_core_class_init(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); dc->realize = pnv_core_realize; + dc->unrealize = pnv_core_unrealize; dc->props = pnv_core_properties; } diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index c42b4a8f6c..d7721320a2 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -22,6 +22,7 @@ #include "target/ppc/cpu.h" #include "qapi/error.h" #include "qemu/log.h" +#include "hw/isa/isa.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_lpc.h" @@ -79,6 +80,7 @@ enum { #define ISA_IO_SIZE 0x00010000 #define ISA_MEM_SIZE 0x10000000 +#define ISA_FW_SIZE 0x10000000 #define LPC_IO_OPB_ADDR 0xd0010000 #define LPC_IO_OPB_SIZE 0x00010000 #define LPC_MEM_OPB_ADDR 0xe0010000 @@ -125,25 +127,17 @@ static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) { - bool success; - /* XXX Handle access size limits and FW read caching here */ - success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, - data, sz, false); - - return success; + return !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, false); } static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, int sz) { - bool success; - /* XXX Handle access size limits here */ - success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, - data, sz, true); - - return success; + return !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, true); } #define ECCB_CTL_READ PPC_BIT(15) @@ -437,6 +431,7 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) */ memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); + memory_region_init(&lpc->isa_fw, OBJECT(dev), "isa-fw", ISA_FW_SIZE); /* Create windows from the OPB space to the ISA space */ memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io", @@ -448,7 +443,7 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR, &lpc->opb_isa_mem); memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw", - &lpc->isa_mem, 0, LPC_FW_OPB_SIZE); + &lpc->isa_fw, 0, LPC_FW_OPB_SIZE); memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR, &lpc->opb_isa_fw); @@ -541,16 +536,35 @@ static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) } } -qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, - int nirqs) +ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) { + Error *local_err = NULL; + ISABus *isa_bus; + qemu_irq *irqs; + qemu_irq_handler handler; + + /* let isa_bus_new() create its own bridge on SysBus otherwise + * devices speficied on the command line won't find the bus and + * will fail to create. + */ + isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return NULL; + } + /* Not all variants have a working serial irq decoder. If not, * handling of LPC interrupts becomes a platform issue (some * platforms have a CPLD to do it). */ - if (chip_type == PNV_CHIP_POWER8NVL) { - return qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, nirqs); + if (use_cpld) { + handler = pnv_lpc_isa_irq_handler_cpld; } else { - return qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, lpc, nirqs); + handler = pnv_lpc_isa_irq_handler; } + + irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); + + isa_bus_irqs(isa_bus, irqs); + return isa_bus; } diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 0b658931ee..70111075b3 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -37,11 +38,10 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "hw/loader.h" -#include "sysemu/blockdev.h" #include "exec/address-spaces.h" #define BIOS_FILENAME "ppc405_rom.bin" -#define BIOS_SIZE (2048 * 1024) +#define BIOS_SIZE (2 * MiB) #define KERNEL_LOAD_ADDR 0x00000000 #define INITRD_LOAD_ADDR 0x01800000 @@ -217,14 +217,14 @@ static void ref405ep_init(MachineState *machine) memory_region_init(&ram_memories[1], NULL, "ef405ep.ram1", 0); ram_bases[1] = 0x00000000; ram_sizes[1] = 0x00000000; - ram_size = 128 * 1024 * 1024; + ram_size = 128 * MiB; #ifdef DEBUG_BOARD_INIT printf("%s: register cpu\n", __func__); #endif env = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, 33333333, &pic, kernel_filename == NULL ? 0 : 1); /* allocate SRAM */ - sram_size = 512 * 1024; + sram_size = 512 * KiB; memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size, &error_fatal); memory_region_add_subregion(sysmem, 0xFFF00000, sram); @@ -590,7 +590,7 @@ static void taihu_405ep_init(MachineState *machine) bios_size = blk_getlength(blk); /* XXX: should check that size is 32MB */ - bios_size = 32 * 1024 * 1024; + bios_size = 32 * MiB; fl_sectors = (bios_size + 65535) >> 16; #ifdef DEBUG_BOARD_INIT printf("Register parallel flash %d size %lx" diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 205ebcea93..4bd9fbcc1e 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -983,10 +984,10 @@ static void ppc405_ocm_init(CPUPPCState *env) ocm = g_malloc0(sizeof(ppc405_ocm_t)); /* XXX: Size is 4096 or 0x04000000 */ - memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4096, + memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4 * KiB, &error_fatal); - memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", &ocm->isarc_ram, - 0, 4096); + memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", + &ocm->isarc_ram, 0, 4 * KiB); qemu_register_reset(&ocm_reset, ocm); ppc_dcr_register(env, OCM0_ISARC, ocm, &dcr_read_ocm, &dcr_write_ocm); @@ -1660,14 +1661,14 @@ CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem, dma_irqs[3] = pic[23]; ppc405_dma_init(env, dma_irqs); /* Serial ports */ - if (serial_hds[0] != NULL) { + if (serial_hd(0) != NULL) { serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1] != NULL) { + if (serial_hd(1) != NULL) { serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } /* IIC controller */ @@ -2023,14 +2024,14 @@ CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, /* GPIO */ ppc405_gpio_init(0xef600700); /* Serial ports */ - if (serial_hds[0] != NULL) { + if (serial_hd(0) != NULL) { serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1] != NULL) { + if (serial_hd(1) != NULL) { serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } /* OCM */ diff --git a/hw/ppc/ppc440.h b/hw/ppc/ppc440.h index ad27db12e4..7cef936125 100644 --- a/hw/ppc/ppc440.h +++ b/hw/ppc/ppc440.h @@ -21,6 +21,7 @@ void ppc440_sdram_init(CPUPPCState *env, int nbanks, hwaddr *ram_bases, hwaddr *ram_sizes, int do_init); void ppc4xx_ahb_init(CPUPPCState *env); +void ppc4xx_dma_init(CPUPPCState *env, int dcr_base); void ppc460ex_pcie_init(CPUPPCState *env); #endif /* PPC440_H */ diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 8641986a71..3d4c43b8cc 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -12,6 +12,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qemu-common.h" #include "qemu/error-report.h" @@ -49,7 +50,7 @@ #define PPC440EP_SDRAM_NR_BANKS 4 static const unsigned int ppc440ep_sdram_bank_sizes[] = { - 256<<20, 128<<20, 64<<20, 32<<20, 16<<20, 8<<20, 0 + 256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0 }; static hwaddr entry; @@ -151,7 +152,7 @@ static void main_cpu_reset(void *opaque) CPUPPCState *env = &cpu->env; cpu_reset(CPU(cpu)); - env->gpr[1] = (16<<20) - 8; + env->gpr[1] = (16 * MiB) - 8; env->gpr[3] = FDT_ADDR; env->nip = entry; @@ -238,14 +239,14 @@ static void bamboo_init(MachineState *machine) get_system_io(), 0, PPC440EP_PCI_IOLEN); memory_region_add_subregion(get_system_memory(), PPC440EP_PCI_IO, isa); - if (serial_hds[0] != NULL) { + if (serial_hd(0) != NULL) { serial_mm_init(address_space_mem, 0xef600300, 0, pic[0], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1] != NULL) { + if (serial_hd(1) != NULL) { serial_mm_init(address_space_mem, 0xef600400, 0, pic[1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c index b1307e6477..64ed07afa6 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/ppc/ppc440_pcix.c @@ -57,7 +57,7 @@ typedef struct PPC440PCIXState { struct PLBOutMap pom[PPC440_PCIX_NR_POMS]; struct PLBInMap pim[PPC440_PCIX_NR_PIMS]; uint32_t sts; - qemu_irq irq[PCI_NUM_PINS]; + qemu_irq irq; AddressSpace bm_as; MemoryRegion bm; @@ -257,7 +257,7 @@ static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr, break; case PCIX0_PIM2SAL: s->pim[2].sa &= 0xffffffff00000000ULL; - s->pim[2].sa = val; + s->pim[2].sa |= val; ppc440_pcix_update_pim(s, 2); break; case PCIX0_PIM2LAL: @@ -418,21 +418,20 @@ static void ppc440_pcix_reset(DeviceState *dev) * This may need further refactoring for other boards. */ static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num) { - int slot = pci_dev->devfn >> 3; - trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, slot); - return slot - 1; + trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, 0); + return 0; } static void ppc440_pcix_set_irq(void *opaque, int irq_num, int level) { - qemu_irq *pci_irqs = opaque; + qemu_irq *pci_irq = opaque; trace_ppc440_pcix_set_irq(irq_num); if (irq_num < 0) { error_report("%s: PCI irq %d", __func__, irq_num); return; } - qemu_set_irq(pci_irqs[irq_num], level); + qemu_set_irq(*pci_irq, level); } static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) @@ -471,19 +470,15 @@ static int ppc440_pcix_initfn(SysBusDevice *dev) { PPC440PCIXState *s; PCIHostState *h; - int i; h = PCI_HOST_BRIDGE(dev); s = PPC440_PCIX_HOST_BRIDGE(dev); - for (i = 0; i < ARRAY_SIZE(s->irq); i++) { - sysbus_init_irq(dev, &s->irq[i]); - } - + sysbus_init_irq(dev, &s->irq); memory_region_init(&s->busmem, OBJECT(dev), "pci bus memory", UINT64_MAX); h->bus = pci_register_root_bus(DEVICE(dev), NULL, ppc440_pcix_set_irq, - ppc440_pcix_map_irq, s->irq, &s->busmem, - get_system_io(), PCI_DEVFN(0, 0), 4, TYPE_PCI_BUS); + ppc440_pcix_map_irq, &s->irq, &s->busmem, + get_system_io(), PCI_DEVFN(0, 0), 1, TYPE_PCI_BUS); s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0), "ppc4xx-host-bridge"); diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index e312fdba70..09ccda548f 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -9,10 +9,11 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" -#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qemu/log.h" #include "cpu.h" #include "hw/hw.h" #include "exec/address-spaces.h" @@ -20,7 +21,7 @@ #include "hw/ppc/ppc.h" #include "hw/pci/pci.h" #include "sysemu/block-backend.h" -#include "hw/ppc/ppc440.h" +#include "ppc440.h" /*****************************************************************************/ /* L2 Cache as SRAM */ @@ -215,13 +216,13 @@ void ppc4xx_l2sram_init(CPUPPCState *env) l2sram = g_malloc0(sizeof(*l2sram)); /* XXX: Size is 4*64kB for 460ex, cf. U-Boot, ppc4xx-isram.h */ memory_region_init_ram(&l2sram->bank[0], NULL, "ppc4xx.l2sram_bank0", - 64 * K_BYTE, &error_abort); + 64 * KiB, &error_abort); memory_region_init_ram(&l2sram->bank[1], NULL, "ppc4xx.l2sram_bank1", - 64 * K_BYTE, &error_abort); + 64 * KiB, &error_abort); memory_region_init_ram(&l2sram->bank[2], NULL, "ppc4xx.l2sram_bank2", - 64 * K_BYTE, &error_abort); + 64 * KiB, &error_abort); memory_region_init_ram(&l2sram->bank[3], NULL, "ppc4xx.l2sram_bank3", - 64 * K_BYTE, &error_abort); + 64 * KiB, &error_abort); qemu_register_reset(&l2sram_reset, l2sram); ppc_dcr_register(env, DCR_L2CACHE_CFG, l2sram, &dcr_read_l2sram, &dcr_write_l2sram); @@ -513,28 +514,28 @@ static uint32_t sdram_bcr(hwaddr ram_base, hwaddr ram_size) uint32_t bcr; switch (ram_size) { - case (8 * M_BYTE): + case (8 * MiB): bcr = 0xffc0; break; - case (16 * M_BYTE): + case (16 * MiB): bcr = 0xff80; break; - case (32 * M_BYTE): + case (32 * MiB): bcr = 0xff00; break; - case (64 * M_BYTE): + case (64 * MiB): bcr = 0xfe00; break; - case (128 * M_BYTE): + case (128 * MiB): bcr = 0xfc00; break; - case (256 * M_BYTE): + case (256 * MiB): bcr = 0xf800; break; - case (512 * M_BYTE): + case (512 * MiB): bcr = 0xf000; break; - case (1 * G_BYTE): + case (1 * GiB): bcr = 0xe000; break; default: @@ -561,7 +562,7 @@ static target_ulong sdram_size(uint32_t bcr) if (sh == 0) { size = -1; } else { - size = 8 * M_BYTE * sh; + size = 8 * MiB * sh; } return size; @@ -802,6 +803,227 @@ void ppc4xx_ahb_init(CPUPPCState *env) qemu_register_reset(ppc4xx_ahb_reset, ahb); } +/*****************************************************************************/ +/* DMA controller */ + +#define DMA0_CR_CE (1 << 31) +#define DMA0_CR_PW (1 << 26 | 1 << 25) +#define DMA0_CR_DAI (1 << 24) +#define DMA0_CR_SAI (1 << 23) +#define DMA0_CR_DEC (1 << 2) + +enum { + DMA0_CR = 0x00, + DMA0_CT, + DMA0_SAH, + DMA0_SAL, + DMA0_DAH, + DMA0_DAL, + DMA0_SGH, + DMA0_SGL, + + DMA0_SR = 0x20, + DMA0_SGC = 0x23, + DMA0_SLP = 0x25, + DMA0_POL = 0x26, +}; + +typedef struct { + uint32_t cr; + uint32_t ct; + uint64_t sa; + uint64_t da; + uint64_t sg; +} PPC4xxDmaChnl; + +typedef struct { + int base; + PPC4xxDmaChnl ch[4]; + uint32_t sr; +} PPC4xxDmaState; + +static uint32_t dcr_read_dma(void *opaque, int dcrn) +{ + PPC4xxDmaState *dma = opaque; + uint32_t val = 0; + int addr = dcrn - dma->base; + int chnl = addr / 8; + + switch (addr) { + case 0x00 ... 0x1f: + switch (addr % 8) { + case DMA0_CR: + val = dma->ch[chnl].cr; + break; + case DMA0_CT: + val = dma->ch[chnl].ct; + break; + case DMA0_SAH: + val = dma->ch[chnl].sa >> 32; + break; + case DMA0_SAL: + val = dma->ch[chnl].sa; + break; + case DMA0_DAH: + val = dma->ch[chnl].da >> 32; + break; + case DMA0_DAL: + val = dma->ch[chnl].da; + break; + case DMA0_SGH: + val = dma->ch[chnl].sg >> 32; + break; + case DMA0_SGL: + val = dma->ch[chnl].sg; + break; + } + break; + case DMA0_SR: + val = dma->sr; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register %x (%d, %x)\n", + __func__, dcrn, chnl, addr); + } + + return val; +} + +static void dcr_write_dma(void *opaque, int dcrn, uint32_t val) +{ + PPC4xxDmaState *dma = opaque; + int addr = dcrn - dma->base; + int chnl = addr / 8; + + switch (addr) { + case 0x00 ... 0x1f: + switch (addr % 8) { + case DMA0_CR: + dma->ch[chnl].cr = val; + if (val & DMA0_CR_CE) { + int count = dma->ch[chnl].ct & 0xffff; + + if (count) { + int width, i, sidx, didx; + uint8_t *rptr, *wptr; + hwaddr rlen, wlen; + + sidx = didx = 0; + width = 1 << ((val & DMA0_CR_PW) >> 25); + rptr = cpu_physical_memory_map(dma->ch[chnl].sa, &rlen, 0); + wptr = cpu_physical_memory_map(dma->ch[chnl].da, &wlen, 1); + if (rptr && wptr) { + if (!(val & DMA0_CR_DEC) && + val & DMA0_CR_SAI && val & DMA0_CR_DAI) { + /* optimise common case */ + memmove(wptr, rptr, count * width); + sidx = didx = count * width; + } else { + /* do it the slow way */ + for (sidx = didx = i = 0; i < count; i++) { + uint64_t v = ldn_le_p(rptr + sidx, width); + stn_le_p(wptr + didx, width, v); + if (val & DMA0_CR_SAI) { + sidx += width; + } + if (val & DMA0_CR_DAI) { + didx += width; + } + } + } + } + if (wptr) { + cpu_physical_memory_unmap(wptr, wlen, 1, didx); + } + if (rptr) { + cpu_physical_memory_unmap(rptr, rlen, 0, sidx); + } + } + } + break; + case DMA0_CT: + dma->ch[chnl].ct = val; + break; + case DMA0_SAH: + dma->ch[chnl].sa &= 0xffffffffULL; + dma->ch[chnl].sa |= (uint64_t)val << 32; + break; + case DMA0_SAL: + dma->ch[chnl].sa &= 0xffffffff00000000ULL; + dma->ch[chnl].sa |= val; + break; + case DMA0_DAH: + dma->ch[chnl].da &= 0xffffffffULL; + dma->ch[chnl].da |= (uint64_t)val << 32; + break; + case DMA0_DAL: + dma->ch[chnl].da &= 0xffffffff00000000ULL; + dma->ch[chnl].da |= val; + break; + case DMA0_SGH: + dma->ch[chnl].sg &= 0xffffffffULL; + dma->ch[chnl].sg |= (uint64_t)val << 32; + break; + case DMA0_SGL: + dma->ch[chnl].sg &= 0xffffffff00000000ULL; + dma->ch[chnl].sg |= val; + break; + } + break; + case DMA0_SR: + dma->sr &= ~val; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register %x (%d, %x)\n", + __func__, dcrn, chnl, addr); + } +} + +static void ppc4xx_dma_reset(void *opaque) +{ + PPC4xxDmaState *dma = opaque; + int dma_base = dma->base; + + memset(dma, 0, sizeof(*dma)); + dma->base = dma_base; +} + +void ppc4xx_dma_init(CPUPPCState *env, int dcr_base) +{ + PPC4xxDmaState *dma; + int i; + + dma = g_malloc0(sizeof(*dma)); + dma->base = dcr_base; + qemu_register_reset(&ppc4xx_dma_reset, dma); + for (i = 0; i < 4; i++) { + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_CR, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_CT, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SAH, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SAL, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_DAH, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_DAL, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SGH, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + i * 8 + DMA0_SGL, + dma, &dcr_read_dma, &dcr_write_dma); + } + ppc_dcr_register(env, dcr_base + DMA0_SR, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + DMA0_SGC, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + DMA0_SLP, + dma, &dcr_read_dma, &dcr_write_dma); + ppc_dcr_register(env, dcr_base + DMA0_POL, + dma, &dcr_read_dma, &dcr_write_dma); +} + /*****************************************************************************/ /* PCI Express controller */ /* FIXME: This is not complete and does not work, only implemented partially diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index 2e963894fe..8c6f3c9577 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" @@ -29,6 +30,7 @@ #include "hw/boards.h" #include "qemu/log.h" #include "exec/address-spaces.h" +#include "qemu/error-report.h" #define DEBUG_UIC @@ -353,25 +355,25 @@ static uint32_t sdram_bcr (hwaddr ram_base, uint32_t bcr; switch (ram_size) { - case (4 * 1024 * 1024): + case 4 * MiB: bcr = 0x00000000; break; - case (8 * 1024 * 1024): + case 8 * MiB: bcr = 0x00020000; break; - case (16 * 1024 * 1024): + case 16 * MiB: bcr = 0x00040000; break; - case (32 * 1024 * 1024): + case 32 * MiB: bcr = 0x00060000; break; - case (64 * 1024 * 1024): + case 64 * MiB: bcr = 0x00080000; break; - case (128 * 1024 * 1024): + case 128 * MiB: bcr = 0x000A0000; break; - case (256 * 1024 * 1024): + case 256 * MiB: bcr = 0x000C0000; break; default: @@ -399,7 +401,7 @@ static target_ulong sdram_size (uint32_t bcr) if (sh == 7) size = -1; else - size = (4 * 1024 * 1024) << sh; + size = (4 * MiB) << sh; return size; } @@ -702,8 +704,8 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, ram_size -= size_left; if (size_left) { - printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n", - (int)(ram_size >> 20)); + error_report("Truncating memory to %" PRId64 " MiB to fit SDRAM" + " controller limits", ram_size / MiB); } memory_region_allocate_system_memory(ram, NULL, "ppc4xx.sdram", ram_size); diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index 69ca2d0e42..c45fc858de 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -28,6 +28,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/hw_accel.h" @@ -89,7 +90,7 @@ static void spin_kick(CPUState *cs, run_on_cpu_data data) PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; SpinInfo *curspin = data.host_ptr; - hwaddr map_size = 64 * 1024 * 1024; + hwaddr map_size = 64 * MiB; hwaddr map_start; cpu_synchronize_state(cs); diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index a1e7219db6..3401570d98 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -50,7 +50,7 @@ #include "exec/address-spaces.h" #include "trace.h" #include "elf.h" -#include "qemu/cutils.h" +#include "qemu/units.h" #include "kvm_ppc.h" /* SMP is not enabled, for now */ @@ -60,7 +60,7 @@ #define CFG_ADDR 0xf0000510 -#define BIOS_SIZE (1024 * 1024) +#define BIOS_SIZE (1 * MiB) #define BIOS_FILENAME "ppc_rom.bin" #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 @@ -682,6 +682,7 @@ static void prep_machine_init(MachineClass *mc) mc->max_cpus = MAX_CPUS; mc->default_boot_order = "cad"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("602"); + mc->default_display = "std"; } static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) @@ -770,7 +771,6 @@ static void ibm_40p_init(MachineState *machine) /* add some more devices */ if (defaults_enabled()) { - isa_create_simple(isa_bus, TYPE_I8042); m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59")); dev = DEVICE(isa_create(isa_bus, "cs4231a")); @@ -885,11 +885,11 @@ static void ibm_40p_machine_init(MachineClass *mc) mc->desc = "IBM RS/6000 7020 (40p)", mc->init = ibm_40p_init; mc->max_cpus = 1; - mc->pci_allow_0_address = true; - mc->default_ram_size = 128 * M_BYTE; + mc->default_ram_size = 128 * MiB; mc->block_default_type = IF_SCSI; mc->default_boot_order = "c"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("604"); + mc->default_display = "std"; } DEFINE_MACHINE("40p", ibm_40p_machine_init) diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index b6135650bd..45cb95e08a 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/isa/isa.h" #include "exec/address-spaces.h" #include "hw/boards.h" @@ -109,7 +110,7 @@ static void rs6000mc_port0820_write(void *opaque, uint32_t addr, uint32_t val) size = end_address - start_address; memory_region_set_enabled(&s->simm[socket - 1], size != 0); memory_region_set_address(&s->simm[socket - 1], - start_address * 8 * 1024 * 1024); + start_address * 8 * MiB); } } } @@ -140,7 +141,7 @@ static void rs6000mc_realize(DeviceState *dev, Error **errp) { RS6000MCState *s = RS6000MC_DEVICE(dev); int socket = 0; - unsigned int ram_size = s->ram_size / (1024 * 1024); + unsigned int ram_size = s->ram_size / MiB; while (socket < 6) { if (ram_size >= 64) { @@ -163,8 +164,8 @@ static void rs6000mc_realize(DeviceState *dev, Error **errp) char name[] = "simm.?"; name[5] = socket + '0'; memory_region_allocate_system_memory(&s->simm[socket], OBJECT(dev), - name, s->simm_size[socket] - * 1024 * 1024); + name, + s->simm_size[socket] * MiB); memory_region_add_subregion_overlap(get_system_memory(), 0, &s->simm[socket], socket); } @@ -172,8 +173,8 @@ static void rs6000mc_realize(DeviceState *dev, Error **errp) if (ram_size) { /* unable to push all requested RAM in SIMMs */ error_setg(errp, "RAM size incompatible with this board. " - "Try again with something else, like %d MB", - s->ram_size / 1024 / 1024 - ram_size); + "Try again with something else, like %" PRId64 " MB", + s->ram_size / MiB - ram_size); return; } diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index dfff262f96..9c77183006 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -12,12 +12,11 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" -#include "qemu/cutils.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/blockdev.h" #include "hw/boards.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" @@ -27,8 +26,8 @@ #include "elf.h" #include "exec/address-spaces.h" #include "exec/memory.h" -#include "hw/ppc/ppc440.h" -#include "hw/ppc/ppc405.h" +#include "ppc440.h" +#include "ppc405.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" @@ -37,6 +36,9 @@ #include "hw/i2c/ppc4xx_i2c.h" #include "hw/i2c/smbus.h" #include "hw/usb/hcd-ehci.h" +#include "hw/ppc/fdt.h" + +#include #define BINARY_DEVICE_TREE_FILE "canyonlands.dtb" #define UBOOT_FILENAME "u-boot-sam460-20100605.bin" @@ -47,7 +49,7 @@ /* from Sam460 U-Boot include/configs/Sam460ex.h */ #define FLASH_BASE 0xfff00000 #define FLASH_BASE_H 0x4 -#define FLASH_SIZE (1 << 20) +#define FLASH_SIZE (1 * MiB) #define UBOOT_LOAD_BASE 0xfff80000 #define UBOOT_SIZE 0x00080000 #define UBOOT_ENTRY 0xfffffffc @@ -68,11 +70,15 @@ */ #define CPU_FREQ 1150000000 +#define PLB_FREQ 230000000 +#define OPB_FREQ 115000000 +#define EBC_FREQ 115000000 +#define UART_FREQ 11059200 #define SDRAM_NR_BANKS 4 /* FIXME: See u-boot.git 8ac41e, also fix in ppc440_uc.c */ static const unsigned int ppc460ex_sdram_bank_sizes[] = { - 1024 << 20, 512 << 20, 256 << 20, 128 << 20, 64 << 20, 32 << 20, 0 + 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 0 }; struct boot_info { @@ -127,7 +133,7 @@ static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size) int i; /* work in terms of MB */ - ram_size >>= 20; + ram_size /= MiB; while ((ram_size >= 4) && (nbanks <= 2)) { int sz_log2 = MIN(31 - clz32(ram_size), 14); @@ -226,7 +232,7 @@ static int sam460ex_load_uboot(void) fl_sectors = (bios_size + 65535) >> 16; if (!pflash_cfi01_register(base, NULL, "sam460ex.flash", bios_size, - blk, (64 * 1024), fl_sectors, + blk, 64 * KiB, fl_sectors, 1, 0x89, 0x18, 0x0000, 0x0, 1)) { error_report("qemu: Error registering flash memory."); /* XXX: return an error instead? */ @@ -249,52 +255,41 @@ static int sam460ex_load_device_tree(hwaddr addr, hwaddr initrd_size, const char *kernel_cmdline) { - int ret = -1; uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) }; char *filename; int fdt_size; void *fdt; uint32_t tb_freq = CPU_FREQ; uint32_t clock_freq = CPU_FREQ; + int offset; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { - goto out; + error_report("Couldn't find dtb file `%s'", BINARY_DEVICE_TREE_FILE); + exit(1); } fdt = load_device_tree(filename, &fdt_size); - g_free(filename); - if (fdt == NULL) { - goto out; + if (!fdt) { + error_report("Couldn't load dtb file `%s'", filename); + g_free(filename); + exit(1); } + g_free(filename); /* Manipulate device tree in memory. */ - ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, - sizeof(mem_reg_property)); - if (ret < 0) { - error_report("couldn't set /memory/reg"); - } + qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); /* default FDT doesn't have a /chosen node... */ qemu_fdt_add_subnode(fdt, "/chosen"); - ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - initrd_base); - if (ret < 0) { - error_report("couldn't set /chosen/linux,initrd-start"); - } + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", initrd_base); - ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - (initrd_base + initrd_size)); - if (ret < 0) { - error_report("couldn't set /chosen/linux,initrd-end"); - } + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); - ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", - kernel_cmdline); - if (ret < 0) { - error_report("couldn't set /chosen/bootargs"); - } + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); /* Copy data from the host device tree into the guest. Since the guest can * directly access the timebase without host involvement, we must expose @@ -309,13 +304,31 @@ static int sam460ex_load_device_tree(hwaddr addr, qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", tb_freq); + /* Remove cpm node if it exists (it is not emulated) */ + offset = fdt_path_offset(fdt, "/cpm"); + if (offset >= 0) { + _FDT(fdt_nop_node(fdt, offset)); + } + + /* set serial port clocks */ + offset = fdt_node_offset_by_compatible(fdt, -1, "ns16550"); + while (offset >= 0) { + _FDT(fdt_setprop_cell(fdt, offset, "clock-frequency", UART_FREQ)); + offset = fdt_node_offset_by_compatible(fdt, offset, "ns16550"); + } + + /* some more clocks */ + qemu_fdt_setprop_cell(fdt, "/plb", "clock-frequency", + PLB_FREQ); + qemu_fdt_setprop_cell(fdt, "/plb/opb", "clock-frequency", + OPB_FREQ); + qemu_fdt_setprop_cell(fdt, "/plb/opb/ebc", "clock-frequency", + EBC_FREQ); + rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); g_free(fdt); - ret = fdt_size; -out: - - return ret; + return fdt_size; } /* Create reset TLB entries for BookE, mapping only the flash memory. */ @@ -360,14 +373,14 @@ static void main_cpu_reset(void *opaque) /* either we have a kernel to boot or we jump to U-Boot */ if (bi->entry != UBOOT_ENTRY) { - env->gpr[1] = (16 << 20) - 8; + env->gpr[1] = (16 * MiB) - 8; env->gpr[3] = FDT_ADDR; env->nip = bi->entry; /* Create a mapping for the kernel. */ mmubooke_create_initial_mapping(env, 0, 0); env->gpr[6] = tswap32(EPAPR_MAGIC); - env->gpr[7] = (16 << 20) - 8; /*bi->ima_size;*/ + env->gpr[7] = (16 * MiB) - 8; /* bi->ima_size; */ } else { env->nip = UBOOT_ENTRY; @@ -458,6 +471,7 @@ static void sam460ex_init(MachineState *machine) object_property_set_bool(OBJECT(dev), true, "realized", NULL); smbus_eeprom_init(i2c[0]->bus, 8, smbus_eeprom_buf, smbus_eeprom_size); g_free(smbus_eeprom_buf); + i2c_create_slave(i2c[0]->bus, "m41t80", 0x68); dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600800, uic[0][3]); i2c[1] = PPC4xx_I2C(dev); @@ -477,10 +491,13 @@ static void sam460ex_init(MachineState *machine) /* MAL */ ppc4xx_mal_init(env, 4, 16, &uic[2][3]); + /* DMA */ + ppc4xx_dma_init(env, 0x200); + /* 256K of L2 cache as memory */ ppc4xx_l2sram_init(env); /* FIXME: remove this after fixing l2sram mapping in ppc440_uc.c? */ - memory_region_init_ram(l2cache_ram, NULL, "ppc440.l2cache_ram", 256 << 10, + memory_region_init_ram(l2cache_ram, NULL, "ppc440.l2cache_ram", 256 * KiB, &error_abort); memory_region_add_subregion(address_space_mem, 0x400000000LL, l2cache_ram); @@ -498,10 +515,8 @@ static void sam460ex_init(MachineState *machine) /* PCI bus */ ppc460ex_pcie_init(env); - /* FIXME: is this correct? */ - dev = sysbus_create_varargs("ppc440-pcix-host", 0xc0ec00000, - uic[1][0], uic[1][20], uic[1][21], uic[1][22], - NULL); + /* All PCI irqs are connected to the same UIC pin (cf. UBoot source) */ + dev = sysbus_create_simple("ppc440-pcix-host", 0xc0ec00000, uic[1][0]); pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); if (!pci_bus) { error_report("couldn't create PCI controller!"); @@ -522,14 +537,14 @@ static void sam460ex_init(MachineState *machine) /* SoC has 4 UARTs * but board has only one wired and two are present in fdt */ - if (serial_hds[0] != NULL) { + if (serial_hd(0) != NULL) { serial_mm_init(address_space_mem, 0x4ef600300, 0, uic[1][1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + PPC_SERIAL_MM_BAUDBASE, serial_hd(0), DEVICE_BIG_ENDIAN); } - if (serial_hds[1] != NULL) { + if (serial_hd(1) != NULL) { serial_mm_init(address_space_mem, 0x4ef600400, 0, uic[0][1], - PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + PPC_SERIAL_MM_BAUDBASE, serial_hd(1), DEVICE_BIG_ENDIAN); } @@ -581,10 +596,6 @@ static void sam460ex_init(MachineState *machine) dt_size = sam460ex_load_device_tree(FDT_ADDR, machine->ram_size, RAMDISK_ADDR, initrd_size, machine->kernel_cmdline); - if (dt_size < 0) { - error_report("couldn't load device tree"); - exit(1); - } boot_info->dt_base = FDT_ADDR; boot_info->dt_size = dt_size; @@ -598,7 +609,7 @@ static void sam460ex_machine_init(MachineClass *mc) mc->desc = "aCube Sam460ex"; mc->init = sam460ex_init; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb"); - mc->default_ram_size = 512 * M_BYTE; + mc->default_ram_size = 512 * MiB; } DEFINE_MACHINE("sam460ex", sam460ex_machine_init) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a81570e7c8..421b2dd09b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -35,7 +35,6 @@ #include "elf.h" #include "net/net.h" #include "sysemu/device_tree.h" -#include "sysemu/block-backend.h" #include "sysemu/cpus.h" #include "sysemu/hw_accel.h" #include "kvm_ppc.h" @@ -64,6 +63,7 @@ #include "hw/virtio/vhost-scsi-common.h" #include "exec/address-spaces.h" +#include "exec/ram_addr.h" #include "hw/usb.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -74,6 +74,7 @@ #include "hw/compat.h" #include "qemu/cutils.h" #include "hw/ppc/spapr_cpu_core.h" +#include "hw/mem/memory-device.h" #include @@ -136,7 +137,7 @@ static ICSState *spapr_ics_create(sPAPRMachineState *spapr, goto error; } - return ICS_SIMPLE(obj); + return ICS_BASE(obj); error: error_propagate(errp, local_err); @@ -186,27 +187,33 @@ static int xics_max_server_number(sPAPRMachineState *spapr) static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + Error *local_err = NULL; if (kvm_enabled()) { if (machine_kernel_irqchip_allowed(machine) && - !xics_kvm_init(spapr, errp)) { + !xics_kvm_init(spapr, &local_err)) { spapr->icp_type = TYPE_KVM_ICP; - spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, errp); + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, + &local_err); } if (machine_kernel_irqchip_required(machine) && !spapr->ics) { - error_prepend(errp, "kernel_irqchip requested but unavailable: "); - return; + error_prepend(&local_err, + "kernel_irqchip requested but unavailable: "); + goto error; } + error_free(local_err); + local_err = NULL; } if (!spapr->ics) { xics_spapr_init(spapr); spapr->icp_type = TYPE_ICP; - spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, errp); - if (!spapr->ics) { - return; - } + spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, + &local_err); } + +error: + error_propagate(errp, local_err); } static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, @@ -263,7 +270,6 @@ static void spapr_populate_pa_features(sPAPRMachineState *spapr, void *fdt, int offset, bool legacy_guest) { - CPUPPCState *env = &cpu->env; uint8_t pa_features_206[] = { 6, 0, 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; uint8_t pa_features_207[] = { 24, 0, @@ -315,7 +321,7 @@ static void spapr_populate_pa_features(sPAPRMachineState *spapr, return; } - if (env->ci_large_pages) { + if (ppc_hash64_has(cpu, PPC_HASH64_CI_LARGEPAGE)) { /* * Note: we keep CI large pages off by default because a 64K capable * guest provisioned with large pages might otherwise try to map a qemu @@ -548,8 +554,8 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); - _FDT((fdt_setprop_cell(fdt, offset, "slb-size", env->slb_nr))); - _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr))); + _FDT((fdt_setprop_cell(fdt, offset, "slb-size", cpu->hash64_opts->slb_size))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size))); _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); @@ -557,7 +563,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0))); } - if (env->mmu_model & POWERPC_MMU_1TSEG) { + if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) { _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", segs, sizeof(segs)))); } @@ -581,8 +587,8 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1))); } - page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop, - sizeof(page_sizes_prop)); + page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop, + sizeof(page_sizes_prop)); if (page_sizes_prop_size) { _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", page_sizes_prop, page_sizes_prop_size))); @@ -659,7 +665,7 @@ static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) if (value && value->type == MEMORY_DEVICE_INFO_KIND_DIMM) { PCDIMMDeviceInfo *pcdimm_info = value->u.dimm.data; - if (pcdimm_info->addr >= addr && + if (addr >= pcdimm_info->addr && addr < (pcdimm_info->addr + pcdimm_info->size)) { return pcdimm_info->node; } @@ -669,70 +675,146 @@ static uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) return -1; } -/* - * Adds ibm,dynamic-reconfiguration-memory node. - * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation - * of this device tree node. - */ -static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) +struct sPAPRDrconfCellV2 { + uint32_t seq_lmbs; + uint64_t base_addr; + uint32_t drc_index; + uint32_t aa_index; + uint32_t flags; +} QEMU_PACKED; + +typedef struct DrconfCellQueue { + struct sPAPRDrconfCellV2 cell; + QSIMPLEQ_ENTRY(DrconfCellQueue) entry; +} DrconfCellQueue; + +static DrconfCellQueue * +spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr, + uint32_t drc_index, uint32_t aa_index, + uint32_t flags) +{ + DrconfCellQueue *elem; + + elem = g_malloc0(sizeof(*elem)); + elem->cell.seq_lmbs = cpu_to_be32(seq_lmbs); + elem->cell.base_addr = cpu_to_be64(base_addr); + elem->cell.drc_index = cpu_to_be32(drc_index); + elem->cell.aa_index = cpu_to_be32(aa_index); + elem->cell.flags = cpu_to_be32(flags); + + return elem; +} + +/* ibm,dynamic-memory-v2 */ +static int spapr_populate_drmem_v2(sPAPRMachineState *spapr, void *fdt, + int offset, MemoryDeviceInfoList *dimms) { MachineState *machine = MACHINE(spapr); - int ret, i, offset; + uint8_t *int_buf, *cur_index, buf_len; + int ret; uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; - uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)}; - uint32_t hotplug_lmb_start = spapr->hotplug_memory.base / lmb_size; - uint32_t nr_lmbs = (spapr->hotplug_memory.base + - memory_region_size(&spapr->hotplug_memory.mr)) / + uint64_t addr, cur_addr, size; + uint32_t nr_boot_lmbs = (machine->device_memory->base / lmb_size); + uint64_t mem_end = machine->device_memory->base + + memory_region_size(&machine->device_memory->mr); + uint32_t node, nr_entries = 0; + sPAPRDRConnector *drc; + DrconfCellQueue *elem, *next; + MemoryDeviceInfoList *info; + QSIMPLEQ_HEAD(, DrconfCellQueue) drconf_queue + = QSIMPLEQ_HEAD_INITIALIZER(drconf_queue); + + /* Entry to cover RAM and the gap area */ + elem = spapr_get_drconf_cell(nr_boot_lmbs, 0, 0, -1, + SPAPR_LMB_FLAGS_RESERVED | + SPAPR_LMB_FLAGS_DRC_INVALID); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + + cur_addr = machine->device_memory->base; + for (info = dimms; info; info = info->next) { + PCDIMMDeviceInfo *di = info->value->u.dimm.data; + + addr = di->addr; + size = di->size; + node = di->node; + + /* Entry for hot-pluggable area */ + if (cur_addr < addr) { + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size); + g_assert(drc); + elem = spapr_get_drconf_cell((addr - cur_addr) / lmb_size, + cur_addr, spapr_drc_index(drc), -1, 0); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + } + + /* Entry for DIMM */ + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, addr / lmb_size); + g_assert(drc); + elem = spapr_get_drconf_cell(size / lmb_size, addr, + spapr_drc_index(drc), node, + SPAPR_LMB_FLAGS_ASSIGNED); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + cur_addr = addr + size; + } + + /* Entry for remaining hotpluggable area */ + if (cur_addr < mem_end) { + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, cur_addr / lmb_size); + g_assert(drc); + elem = spapr_get_drconf_cell((mem_end - cur_addr) / lmb_size, + cur_addr, spapr_drc_index(drc), -1, 0); + QSIMPLEQ_INSERT_TAIL(&drconf_queue, elem, entry); + nr_entries++; + } + + buf_len = nr_entries * sizeof(struct sPAPRDrconfCellV2) + sizeof(uint32_t); + int_buf = cur_index = g_malloc0(buf_len); + *(uint32_t *)int_buf = cpu_to_be32(nr_entries); + cur_index += sizeof(nr_entries); + + QSIMPLEQ_FOREACH_SAFE(elem, &drconf_queue, entry, next) { + memcpy(cur_index, &elem->cell, sizeof(elem->cell)); + cur_index += sizeof(elem->cell); + QSIMPLEQ_REMOVE(&drconf_queue, elem, DrconfCellQueue, entry); + g_free(elem); + } + + ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory-v2", int_buf, buf_len); + g_free(int_buf); + if (ret < 0) { + return -1; + } + return 0; +} + +/* ibm,dynamic-memory */ +static int spapr_populate_drmem_v1(sPAPRMachineState *spapr, void *fdt, + int offset, MemoryDeviceInfoList *dimms) +{ + MachineState *machine = MACHINE(spapr); + int i, ret; + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint32_t device_lmb_start = machine->device_memory->base / lmb_size; + uint32_t nr_lmbs = (machine->device_memory->base + + memory_region_size(&machine->device_memory->mr)) / lmb_size; uint32_t *int_buf, *cur_index, buf_len; - int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; - MemoryDeviceInfoList *dimms = NULL; - - /* - * Don't create the node if there is no hotpluggable memory - */ - if (machine->ram_size == machine->maxram_size) { - return 0; - } /* * Allocate enough buffer size to fit in ibm,dynamic-memory - * or ibm,associativity-lookup-arrays */ - buf_len = MAX(nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1, nr_nodes * 4 + 2) - * sizeof(uint32_t); + buf_len = (nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1) * sizeof(uint32_t); cur_index = int_buf = g_malloc0(buf_len); - - offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory"); - - ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size, - sizeof(prop_lmb_size)); - if (ret < 0) { - goto out; - } - - ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff); - if (ret < 0) { - goto out; - } - - ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0); - if (ret < 0) { - goto out; - } - - if (hotplug_lmb_start) { - dimms = qmp_pc_dimm_device_list(); - } - - /* ibm,dynamic-memory */ int_buf[0] = cpu_to_be32(nr_lmbs); cur_index++; for (i = 0; i < nr_lmbs; i++) { uint64_t addr = i * lmb_size; uint32_t *dynamic_memory = cur_index; - if (i >= hotplug_lmb_start) { + if (i >= device_lmb_start) { sPAPRDRConnector *drc; drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, i); @@ -751,7 +833,7 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) } else { /* * LMB information for RMA, boot time RAM and gap b/n RAM and - * hotplug memory region -- all these are marked as reserved + * device memory region -- all these are marked as reserved * and as having no valid DRC. */ dynamic_memory[0] = cpu_to_be32(addr >> 32); @@ -765,13 +847,71 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE; } - qapi_free_MemoryDeviceInfoList(dimms); ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len); + g_free(int_buf); if (ret < 0) { - goto out; + return -1; + } + return 0; +} + +/* + * Adds ibm,dynamic-reconfiguration-memory node. + * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation + * of this device tree node. + */ +static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) +{ + MachineState *machine = MACHINE(spapr); + int ret, i, offset; + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)}; + uint32_t *int_buf, *cur_index, buf_len; + int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; + MemoryDeviceInfoList *dimms = NULL; + + /* + * Don't create the node if there is no device memory + */ + if (machine->ram_size == machine->maxram_size) { + return 0; + } + + offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory"); + + ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size, + sizeof(prop_lmb_size)); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff); + if (ret < 0) { + return ret; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0); + if (ret < 0) { + return ret; + } + + /* ibm,dynamic-memory or ibm,dynamic-memory-v2 */ + dimms = qmp_memory_device_list(); + if (spapr_ovec_test(spapr->ov5_cas, OV5_DRMEM_V2)) { + ret = spapr_populate_drmem_v2(spapr, fdt, offset, dimms); + } else { + ret = spapr_populate_drmem_v1(spapr, fdt, offset, dimms); + } + qapi_free_MemoryDeviceInfoList(dimms); + + if (ret < 0) { + return ret; } /* ibm,associativity-lookup-arrays */ + buf_len = (nr_nodes * 4 + 2) * sizeof(uint32_t); + cur_index = int_buf = g_malloc0(buf_len); + cur_index = int_buf; int_buf[0] = cpu_to_be32(nr_nodes); int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */ @@ -788,8 +928,8 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) } ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf, (cur_index - int_buf) * sizeof(uint32_t)); -out: g_free(int_buf); + return ret; } @@ -902,14 +1042,21 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) GString *hypertas = g_string_sized_new(256); GString *qemu_hypertas = g_string_sized_new(256); uint32_t refpoints[] = { cpu_to_be32(0x4), cpu_to_be32(0x4) }; - uint64_t max_hotplug_addr = spapr->hotplug_memory.base + - memory_region_size(&spapr->hotplug_memory.mr); + uint64_t max_device_addr = MACHINE(spapr)->device_memory->base + + memory_region_size(&MACHINE(spapr)->device_memory->mr); uint32_t lrdr_capacity[] = { - cpu_to_be32(max_hotplug_addr >> 32), - cpu_to_be32(max_hotplug_addr & 0xffffffff), + cpu_to_be32(max_device_addr >> 32), + cpu_to_be32(max_device_addr & 0xffffffff), 0, cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE), cpu_to_be32(max_cpus / smp_threads), }; + uint32_t maxdomains[] = { + cpu_to_be32(4), + cpu_to_be32(0), + cpu_to_be32(0), + cpu_to_be32(0), + cpu_to_be32(nb_numa_nodes ? nb_numa_nodes - 1 : 0), + }; _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); @@ -946,6 +1093,9 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) _FDT(fdt_setprop(fdt, rtas, "ibm,associativity-reference-points", refpoints, sizeof(refpoints))); + _FDT(fdt_setprop(fdt, rtas, "ibm,max-associativity-domains", + maxdomains, sizeof(maxdomains))); + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)); _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate", @@ -1440,21 +1590,6 @@ void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr) } } -static void find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) -{ - bool matched = false; - - if (object_dynamic_cast(OBJECT(sbdev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { - matched = true; - } - - if (!matched) { - error_report("Device %s is not supported by this machine yet.", - qdev_fw_name(DEVICE(sbdev))); - exit(1); - } -} - static int spapr_reset_drcs(Object *child, void *opaque) { sPAPRDRConnector *drc = @@ -1478,15 +1613,12 @@ static void spapr_machine_reset(void) void *fdt; int rc; - /* Check for unknown sysbus devices */ - foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); - - spapr_caps_reset(spapr); + spapr_caps_apply(spapr); first_ppc_cpu = POWERPC_CPU(first_cpu); if (kvm_enabled() && kvmppc_has_cap_mmu_radix() && - ppc_check_compat(first_ppc_cpu, CPU_POWERPC_LOGICAL_3_00, 0, - spapr->max_compat_pvr)) { + ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0, + spapr->max_compat_pvr)) { /* If using KVM with radix mode available, VCPUs can be started * without a HPT because KVM will start them in radix mode. * Set the GR bit in PATB so that we know there is no HPT. */ @@ -1545,10 +1677,8 @@ static void spapr_machine_reset(void) g_free(fdt); /* Set up the entry state */ - first_ppc_cpu->env.gpr[3] = fdt_addr; + spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, fdt_addr); first_ppc_cpu->env.gpr[5] = 0; - first_cpu->halted = 0; - first_ppc_cpu->env.nip = SPAPR_ENTRY_POINT; spapr->cas_reboot = false; } @@ -1728,10 +1858,12 @@ static bool spapr_ov5_cas_needed(void *opaque) * * Thus, for any cases where the set of available CAS-negotiatable * options extends beyond OV5_FORM1_AFFINITY and OV5_DRCONF_MEMORY, we - * include the CAS-negotiated options in the migration stream. + * include the CAS-negotiated options in the migration stream, unless + * if they affect boot time behaviour only. */ spapr_ovec_set(ov5_mask, OV5_FORM1_AFFINITY); spapr_ovec_set(ov5_mask, OV5_DRCONF_MEMORY); + spapr_ovec_set(ov5_mask, OV5_DRMEM_V2); /* spapr_ovec_diff returns true if bits were removed. we avoid using * the mask itself since in the future it's possible "legacy" bits may be @@ -2173,7 +2305,7 @@ static void spapr_create_lmb_dr_connectors(sPAPRMachineState *spapr) for (i = 0; i < nr_lmbs; i++) { uint64_t addr; - addr = i * lmb_size + spapr->hotplug_memory.base; + addr = i * lmb_size + machine->device_memory->base; spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_LMB, addr / lmb_size); } @@ -2190,17 +2322,17 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) if (machine->ram_size % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Memory size 0x" RAM_ADDR_FMT - " is not aligned to %llu MiB", + " is not aligned to %" PRIu64 " MiB", machine->ram_size, - SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + SPAPR_MEMORY_BLOCK_SIZE / MiB); return; } if (machine->maxram_size % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Maximum memory size 0x" RAM_ADDR_FMT - " is not aligned to %llu MiB", + " is not aligned to %" PRIu64 " MiB", machine->ram_size, - SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + SPAPR_MEMORY_BLOCK_SIZE / MiB); return; } @@ -2208,9 +2340,9 @@ static void spapr_validate_node_memory(MachineState *machine, Error **errp) if (numa_info[i].node_mem % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Node %d memory size 0x%" PRIx64 - " is not aligned to %llu MiB", + " is not aligned to %" PRIu64 " MiB", i, numa_info[i].node_mem, - SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + SPAPR_MEMORY_BLOCK_SIZE / MiB); return; } } @@ -2385,9 +2517,6 @@ static void spapr_machine_init(MachineState *machine) int i; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); - MemoryRegion *rma_region; - void *rma = NULL; - hwaddr rma_alloc_size; hwaddr node0_size = spapr_node0_size(machine); long load_limit, fw_size; char *filename; @@ -2398,7 +2527,9 @@ static void spapr_machine_init(MachineState *machine) QLIST_INIT(&spapr->phbs); QTAILQ_INIT(&spapr->pending_dimm_unplugs); - /* Check HPT resizing availability */ + /* Determine capabilities to run with */ + spapr_caps_init(spapr); + kvmppc_check_papr_resize_hpt(&resize_hpt_err); if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DEFAULT) { /* @@ -2426,40 +2557,28 @@ static void spapr_machine_init(MachineState *machine) exit(1); } - /* Allocate RMA if necessary */ - rma_alloc_size = kvmppc_alloc_rma(&rma); + spapr->rma_size = node0_size; - if (rma_alloc_size == -1) { - error_report("Unable to create RMA"); - exit(1); + /* With KVM, we don't actually know whether KVM supports an + * unbounded RMA (PR KVM) or is limited by the hash table size + * (HV KVM using VRMA), so we always assume the latter + * + * In that case, we also limit the initial allocations for RTAS + * etc... to 256M since we have no way to know what the VRMA size + * is going to be as it depends on the size of the hash table + * which isn't determined yet. + */ + if (kvm_enabled()) { + spapr->vrma_adjust = 1; + spapr->rma_size = MIN(spapr->rma_size, 0x10000000); } - if (rma_alloc_size && (rma_alloc_size < node0_size)) { - spapr->rma_size = rma_alloc_size; - } else { - spapr->rma_size = node0_size; - - /* With KVM, we don't actually know whether KVM supports an - * unbounded RMA (PR KVM) or is limited by the hash table size - * (HV KVM using VRMA), so we always assume the latter - * - * In that case, we also limit the initial allocations for RTAS - * etc... to 256M since we have no way to know what the VRMA size - * is going to be as it depends on the size of the hash table - * isn't determined yet. - */ - if (kvm_enabled()) { - spapr->vrma_adjust = 1; - spapr->rma_size = MIN(spapr->rma_size, 0x10000000); - } - - /* Actually we don't support unbounded RMA anymore since we - * added proper emulation of HV mode. The max we can get is - * 16G which also happens to be what we configure for PAPR - * mode so make sure we don't do anything bigger than that - */ - spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); - } + /* Actually we don't support unbounded RMA anymore since we added + * proper emulation of HV mode. The max we can get is 16G which + * also happens to be what we configure for PAPR mode so make sure + * we don't do anything bigger than that + */ + spapr->rma_size = MIN(spapr->rma_size, 0x400000000ull); if (spapr->rma_size > node0_size) { error_report("Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")", @@ -2484,11 +2603,6 @@ static void spapr_machine_init(MachineState *machine) } spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); - if (!kvm_enabled() || kvmppc_has_cap_mmu_radix()) { - /* KVM and TCG always allow GTSE with radix... */ - spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE); - } - /* ... but not with hash (currently). */ /* advertise support for dedicated HP event source to guests */ if (spapr->use_hotplug_event_source) { @@ -2500,9 +2614,20 @@ static void spapr_machine_init(MachineState *machine) spapr_ovec_set(spapr->ov5, OV5_HPT_RESIZE); } + /* advertise support for ibm,dyamic-memory-v2 */ + spapr_ovec_set(spapr->ov5, OV5_DRMEM_V2); + /* init CPUs */ spapr_init_cpus(spapr); + if ((!kvm_enabled() || kvmppc_has_cap_mmu_radix()) && + ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0, + spapr->max_compat_pvr)) { + /* KVM and TCG always allow GTSE with radix... */ + spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE); + } + /* ... but not with hash (currently). */ + if (kvm_enabled()) { /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */ kvmppc_enable_logical_ci_hcalls(); @@ -2517,17 +2642,12 @@ static void spapr_machine_init(MachineState *machine) machine->ram_size); memory_region_add_subregion(sysmem, 0, ram); - if (rma_alloc_size && rma) { - rma_region = g_new(MemoryRegion, 1); - memory_region_init_ram_ptr(rma_region, NULL, "ppc_spapr.rma", - rma_alloc_size, rma); - vmstate_register_ram_global(rma_region); - memory_region_add_subregion(sysmem, 0, rma_region); - } + /* always allocate the device memory information */ + machine->device_memory = g_malloc0(sizeof(*machine->device_memory)); /* initialize hotplug memory address space */ if (machine->ram_size < machine->maxram_size) { - ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size; + ram_addr_t device_mem_size = machine->maxram_size - machine->ram_size; /* * Limit the number of hotpluggable memory slots to half the number * slots that KVM supports, leaving the other half for PCI and other @@ -2546,12 +2666,12 @@ static void spapr_machine_init(MachineState *machine) exit(1); } - spapr->hotplug_memory.base = ROUND_UP(machine->ram_size, - SPAPR_HOTPLUG_MEM_ALIGN); - memory_region_init(&spapr->hotplug_memory.mr, OBJECT(spapr), - "hotplug-memory", hotplug_mem_size); - memory_region_add_subregion(sysmem, spapr->hotplug_memory.base, - &spapr->hotplug_memory.mr); + machine->device_memory->base = ROUND_UP(machine->ram_size, + SPAPR_DEVICE_MEM_ALIGN); + memory_region_init(&machine->device_memory->mr, OBJECT(spapr), + "device-memory", device_mem_size); + memory_region_add_subregion(sysmem, machine->device_memory->base, + &machine->device_memory->mr); } if (smc->dr_lmb_enabled) { @@ -2589,9 +2709,9 @@ static void spapr_machine_init(MachineState *machine) /* Set up VIO bus */ spapr->vio_bus = spapr_vio_bus_init(); - for (i = 0; i < MAX_SERIAL_PORTS; i++) { - if (serial_hds[i]) { - spapr_vty_create(spapr->vio_bus, serial_hds[i]); + for (i = 0; i < serial_max_hds(); i++) { + if (serial_hd(i)) { + spapr_vty_create(spapr->vio_bus, serial_hd(i)); } } @@ -2643,7 +2763,7 @@ static void spapr_machine_init(MachineState *machine) } } - if (spapr->rma_size < (MIN_RMA_SLOF << 20)) { + if (spapr->rma_size < (MIN_RMA_SLOF * MiB)) { error_report( "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)", MIN_RMA_SLOF); @@ -2927,7 +3047,6 @@ static void spapr_instance_init(Object *obj) " place of standard EPOW events when possible" " (required for memory hot-unplug support)", NULL); - ppc_compat_add_property(obj, "max-cpu-compat", &spapr->max_compat_pvr, "Maximum permitted CPU compatibility mode", &error_fatal); @@ -3024,23 +3143,20 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, } static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, - uint32_t node, Error **errp) + Error **errp) { Error *local_err = NULL; sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr; + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); uint64_t align, size, addr; + uint32_t node; - mr = ddc->get_memory_region(dimm, &local_err); - if (local_err) { - goto out; - } align = memory_region_get_alignment(mr); size = memory_region_size(mr); - pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, &local_err); + pc_dimm_plug(dev, MACHINE(ms), align, &local_err); if (local_err) { goto out; } @@ -3051,6 +3167,8 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out_unplug; } + node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, + &error_abort); spapr_add_lmbs(dev, addr, size, node, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), &local_err); @@ -3061,7 +3179,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; out_unplug: - pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr); + pc_dimm_unplug(dev, MACHINE(ms)); out: error_propagate(errp, local_err); } @@ -3069,11 +3187,19 @@ out: static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + const sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(hotplug_dev); + sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr; uint64_t size; - char *mem_dev; + Object *memdev; + hwaddr pagesize; + + if (!smc->dr_lmb_enabled) { + error_setg(errp, "Memory hotplug not supported for this machine"); + return; + } mr = ddc->get_memory_region(dimm, errp); if (!mr) { @@ -3083,19 +3209,14 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, if (size % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Hotplugged memory size must be a multiple of " - "%lld MB", SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + "%" PRIu64 " MB", SPAPR_MEMORY_BLOCK_SIZE / MiB); return; } - mem_dev = object_property_get_str(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, NULL); - if (mem_dev && !kvmppc_is_mem_backend_page_size_ok(mem_dev)) { - error_setg(errp, "Memory backend has bad page size. " - "Use 'memory-backend-file' with correct mem-path."); - goto out; - } - -out: - g_free(mem_dev); + memdev = object_property_get_link(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, + &error_abort); + pagesize = host_memory_backend_pagesize(MEMORY_BACKEND(memdev)); + spapr_check_pagesize(spapr, pagesize, errp); } struct sPAPRDIMMState { @@ -3178,10 +3299,8 @@ static sPAPRDIMMState *spapr_recover_pending_dimm_state(sPAPRMachineState *ms, /* Callback to be called during DRC release. */ void spapr_lmb_release(DeviceState *dev) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_hotplug_handler(dev)); - PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); + HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_ctrl); sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); /* This information will get lost if a migration occurs @@ -3199,9 +3318,17 @@ void spapr_lmb_release(DeviceState *dev) /* * Now that all the LMBs have been removed by the guest, call the - * pc-dimm unplug handler to cleanup up the pc-dimm device. + * unplug handler chain. This can never fail. */ - pc_dimm_memory_unplug(dev, &spapr->hotplug_memory, mr); + hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); +} + +static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); + sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); + + pc_dimm_unplug(dev, MACHINE(hotplug_dev)); object_unparent(OBJECT(dev)); spapr_pending_dimm_unplugs_remove(spapr, ds); } @@ -3213,16 +3340,12 @@ static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, Error *local_err = NULL; PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr; + MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); uint32_t nr_lmbs; uint64_t size, addr_start, addr; int i; sPAPRDRConnector *drc; - mr = ddc->get_memory_region(dimm, &local_err); - if (local_err) { - goto out; - } size = memory_region_size(mr); nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; @@ -3289,7 +3412,15 @@ static void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, /* Callback to be called during DRC release. */ void spapr_core_release(DeviceState *dev) { - MachineState *ms = MACHINE(qdev_get_hotplug_handler(dev)); + HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + + /* Call the unplug handler chain. This can never fail. */ + hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); +} + +static void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +{ + MachineState *ms = MACHINE(hotplug_dev); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(ms); CPUCore *cc = CPU_CORE(dev); CPUArchId *core_slot = spapr_find_cpu_slot(ms, cc->core_id, NULL); @@ -3459,53 +3590,23 @@ out: static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - MachineState *ms = MACHINE(hotplug_dev); - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(ms); - if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - int node; - - if (!smc->dr_lmb_enabled) { - error_setg(errp, "Memory hotplug not supported for this machine"); - return; - } - node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, errp); - if (*errp) { - return; - } - if (node < 0 || node >= MAX_NODES) { - error_setg(errp, "Invaild node %d", node); - return; - } - - /* - * Currently PowerPC kernel doesn't allow hot-adding memory to - * memory-less node, but instead will silently add the memory - * to the first node that has some memory. This causes two - * unexpected behaviours for the user. - * - * - Memory gets hotplugged to a different node than what the user - * specified. - * - Since pc-dimm subsystem in QEMU still thinks that memory belongs - * to memory-less node, a reboot will set things accordingly - * and the previously hotplugged memory now ends in the right node. - * This appears as if some memory moved from one node to another. - * - * So until kernel starts supporting memory hotplug to memory-less - * nodes, just prevent such attempts upfront in QEMU. - */ - if (nb_numa_nodes && !numa_info[node].node_mem) { - error_setg(errp, "Can't hotplug memory to memory-less node %d", - node); - return; - } - - spapr_memory_plug(hotplug_dev, dev, node, errp); + spapr_memory_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_plug(hotplug_dev, dev, errp); } } +static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + spapr_memory_unplug(hotplug_dev, dev); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + spapr_core_unplug(hotplug_dev, dev); + } +} + static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -3705,57 +3806,12 @@ static int ics_find_free_block(ICSState *ics, int num, int alignnum) return -1; } -/* - * Allocate the IRQ number and set the IRQ type, LSI or MSI - */ -static void spapr_irq_set_lsi(sPAPRMachineState *spapr, int irq, bool lsi) -{ - ics_set_irq_type(spapr->ics, irq - spapr->ics->offset, lsi); -} - -int spapr_irq_alloc(sPAPRMachineState *spapr, int irq_hint, bool lsi, - Error **errp) +int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp) { ICSState *ics = spapr->ics; - int irq; + int first = -1; - if (!ics) { - return -1; - } - if (irq_hint) { - if (!ICS_IRQ_FREE(ics, irq_hint - ics->offset)) { - error_setg(errp, "can't allocate IRQ %d: already in use", irq_hint); - return -1; - } - irq = irq_hint; - } else { - irq = ics_find_free_block(ics, 1, 1); - if (irq < 0) { - error_setg(errp, "can't allocate IRQ: no IRQ left"); - return -1; - } - irq += ics->offset; - } - - spapr_irq_set_lsi(spapr, irq, lsi); - trace_spapr_irq_alloc(irq); - - return irq; -} - -/* - * Allocate block of consecutive IRQs, and return the number of the first IRQ in - * the block. If align==true, aligns the first IRQ number to num. - */ -int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi, - bool align, Error **errp) -{ - ICSState *ics = spapr->ics; - int i, first = -1; - - if (!ics) { - return -1; - } + assert(ics); /* * MSIMesage::data is used for storing VIRQ so @@ -3771,19 +3827,33 @@ int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi, } else { first = ics_find_free_block(ics, num, 1); } + if (first < 0) { error_setg(errp, "can't find a free %d-IRQ block", num); return -1; } - first += ics->offset; - for (i = first; i < first + num; ++i) { - spapr_irq_set_lsi(spapr, i, lsi); + return first + ics->offset; +} + +int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp) +{ + ICSState *ics = spapr->ics; + + assert(ics); + + if (!ics_valid_irq(ics, irq)) { + error_setg(errp, "IRQ %d is invalid", irq); + return -1; } - trace_spapr_irq_alloc_block(first, num, lsi, align); + if (!ICS_IRQ_FREE(ics, irq - ics->offset)) { + error_setg(errp, "IRQ %d is not free", irq); + return -1; + } - return first; + ics_set_irq_type(ics, irq - ics->offset, lsi); + return 0; } void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num) @@ -3891,10 +3961,12 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1024; mc->no_parallel = 1; mc->default_boot_order = ""; - mc->default_ram_size = 512 * M_BYTE; + mc->default_ram_size = 512 * MiB; + mc->default_display = "std"; mc->kvm_type = spapr_kvm_type; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_SPAPR_PCI_HOST_BRIDGE); mc->pci_allow_0_address = true; + assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = spapr_get_hotplug_handler; hc->pre_plug = spapr_machine_device_pre_plug; hc->plug = spapr_machine_device_plug; @@ -3902,6 +3974,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) mc->get_default_cpu_node_id = spapr_get_default_cpu_node_id; mc->possible_cpu_arch_ids = spapr_possible_cpu_arch_ids; hc->unplug_request = spapr_machine_device_unplug_request; + hc->unplug = spapr_machine_device_unplug; smc->dr_lmb_enabled = true; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0"); @@ -3933,6 +4006,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; + smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 16; /* 64kiB */ spapr_caps_add_properties(smc, &error_abort); } @@ -3985,18 +4059,56 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* - * pseries-2.12 + * pseries-3.0 */ -static void spapr_machine_2_12_instance_options(MachineState *machine) +static void spapr_machine_3_0_instance_options(MachineState *machine) { } -static void spapr_machine_2_12_class_options(MachineClass *mc) +static void spapr_machine_3_0_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(2_12, "2.12", true); +DEFINE_SPAPR_MACHINE(3_0, "3.0", true); + +/* + * pseries-2.12 + */ +#define SPAPR_COMPAT_2_12 \ + HW_COMPAT_2_12 \ + { \ + .driver = TYPE_POWERPC_CPU, \ + .property = "pre-3.0-migration", \ + .value = "on", \ + }, \ + { \ + .driver = TYPE_SPAPR_CPU_CORE, \ + .property = "pre-3.0-migration", \ + .value = "on", \ + }, + +static void spapr_machine_2_12_instance_options(MachineState *machine) +{ + spapr_machine_3_0_instance_options(machine); +} + +static void spapr_machine_2_12_class_options(MachineClass *mc) +{ + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + + spapr_machine_3_0_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_12); + + /* We depend on kvm_enabled() to choose a default value for the + * hpt-max-page-size capability. Of course we can't do it here + * because this is too early and the HW accelerator isn't initialzed + * yet. Postpone this to machine init (see default_caps_with_cpu()). + */ + smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 0; +} + +DEFINE_SPAPR_MACHINE(2_12, "2.12", false); static void spapr_machine_2_12_sxxm_instance_options(MachineState *machine) { @@ -4153,13 +4265,13 @@ static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index, hwaddr phb0_base, phb_base; int i; - /* Do we have hotpluggable memory? */ + /* Do we have device memory? */ if (MACHINE(spapr)->maxram_size > ram_top) { /* Can't just use maxram_size, because there may be an - * alignment gap between normal and hotpluggable memory - * regions */ - ram_top = spapr->hotplug_memory.base + - memory_region_size(&spapr->hotplug_memory.mr); + * alignment gap between normal and device memory regions + */ + ram_top = MACHINE(spapr)->device_memory->base + + memory_region_size(&MACHINE(spapr)->device_memory->mr); } phb0_base = QEMU_ALIGN_UP(ram_top, phb0_alignment); diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 531e145114..aa605cea91 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -26,7 +26,9 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "sysemu/hw_accel.h" +#include "exec/ram_addr.h" #include "target/ppc/cpu.h" +#include "target/ppc/mmu-hash64.h" #include "cpu-models.h" #include "kvm_ppc.h" @@ -59,6 +61,8 @@ typedef struct sPAPRCapabilityInfo { sPAPRCapPossible *possible; /* Make sure the virtual hardware can support this capability */ void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp); + void (*cpu_apply)(sPAPRMachineState *spapr, PowerPCCPU *cpu, + uint8_t val, Error **errp); } sPAPRCapabilityInfo; static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name, @@ -142,6 +146,42 @@ out: g_free(val); } +static void spapr_cap_get_pagesize(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + sPAPRCapabilityInfo *cap = opaque; + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + uint8_t val = spapr_get_cap(spapr, cap->index); + uint64_t pagesize = (1ULL << val); + + visit_type_size(v, name, &pagesize, errp); +} + +static void spapr_cap_set_pagesize(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + sPAPRCapabilityInfo *cap = opaque; + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + uint64_t pagesize; + uint8_t val; + Error *local_err = NULL; + + visit_type_size(v, name, &pagesize, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (!is_power_of_2(pagesize)) { + error_setg(errp, "cap-%s must be a power of 2", cap->name); + return; + } + + val = ctz64(pagesize); + spapr->cmd_line_caps[cap->index] = true; + spapr->eff.caps[cap->index] = val; +} + static void cap_htm_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { if (!val) { @@ -265,6 +305,69 @@ static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr, #define VALUE_DESC_TRISTATE " (broken, workaround, fixed)" +void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize, + Error **errp) +{ + hwaddr maxpagesize = (1ULL << spapr->eff.caps[SPAPR_CAP_HPT_MAXPAGESIZE]); + + if (!kvmppc_hpt_needs_host_contiguous_pages()) { + return; + } + + if (maxpagesize > pagesize) { + error_setg(errp, + "Can't support %"HWADDR_PRIu" kiB guest pages with %" + HWADDR_PRIu" kiB host pages with this KVM implementation", + maxpagesize >> 10, pagesize >> 10); + } +} + +static void cap_hpt_maxpagesize_apply(sPAPRMachineState *spapr, + uint8_t val, Error **errp) +{ + if (val < 12) { + error_setg(errp, "Require at least 4kiB hpt-max-page-size"); + return; + } else if (val < 16) { + warn_report("Many guests require at least 64kiB hpt-max-page-size"); + } + + spapr_check_pagesize(spapr, qemu_getrampagesize(), errp); +} + +static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift, + uint32_t pshift) +{ + unsigned maxshift = *((unsigned *)opaque); + + assert(pshift >= seg_pshift); + + /* Don't allow the guest to use pages bigger than the configured + * maximum size */ + if (pshift > maxshift) { + return false; + } + + /* For whatever reason, KVM doesn't allow multiple pagesizes + * within a segment, *except* for the case of 16M pages in a 4k or + * 64k segment. Always exclude other cases, so that TCG and KVM + * guests see a consistent environment */ + if ((pshift != seg_pshift) && (pshift != 24)) { + return false; + } + + return true; +} + +static void cap_hpt_maxpagesize_cpu_apply(sPAPRMachineState *spapr, + PowerPCCPU *cpu, + uint8_t val, Error **errp) +{ + unsigned maxshift = val; + + ppc_hash64_filter_pagesizes(cpu, spapr_pagesize_cb, &maxshift); +} + sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { .name = "htm", @@ -324,39 +427,57 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .possible = &cap_ibs_possible, .apply = cap_safe_indirect_branch_apply, }, + [SPAPR_CAP_HPT_MAXPAGESIZE] = { + .name = "hpt-max-page-size", + .description = "Maximum page size for Hash Page Table guests", + .index = SPAPR_CAP_HPT_MAXPAGESIZE, + .get = spapr_cap_get_pagesize, + .set = spapr_cap_set_pagesize, + .type = "int", + .apply = cap_hpt_maxpagesize_apply, + .cpu_apply = cap_hpt_maxpagesize_cpu_apply, + }, }; static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, - CPUState *cs) + const char *cputype) { sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - PowerPCCPU *cpu = POWERPC_CPU(cs); sPAPRCapabilities caps; caps = smc->default_caps; - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, - 0, spapr->max_compat_pvr)) { + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_07, + 0, spapr->max_compat_pvr)) { + caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; } - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07, - 0, spapr->max_compat_pvr)) { - caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; - } - - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06_PLUS, - 0, spapr->max_compat_pvr)) { + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_06_PLUS, + 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; } - if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06, - 0, spapr->max_compat_pvr)) { + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_06, + 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_OFF; caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_OFF; caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; } + /* This is for pseries-2.12 and older */ + if (smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] == 0) { + uint8_t mps; + + if (kvmppc_hpt_needs_host_contiguous_pages()) { + mps = ctz64(qemu_getrampagesize()); + } else { + mps = 34; /* allow everything up to 16GiB, i.e. everything */ + } + + caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = mps; + } + return caps; } @@ -388,7 +509,7 @@ int spapr_caps_post_migration(sPAPRMachineState *spapr) sPAPRCapabilities dstcaps = spapr->eff; sPAPRCapabilities srccaps; - srccaps = default_caps_with_cpu(spapr, first_cpu); + srccaps = default_caps_with_cpu(spapr, MACHINE(spapr)->cpu_type); for (i = 0; i < SPAPR_CAP_NUM; i++) { /* If not default value then assume came in with the migration */ if (spapr->mig.caps[i] != spapr->def.caps[i]) { @@ -444,13 +565,13 @@ SPAPR_CAP_MIG_STATE(cfpc, SPAPR_CAP_CFPC); SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC); SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS); -void spapr_caps_reset(sPAPRMachineState *spapr) +void spapr_caps_init(sPAPRMachineState *spapr) { sPAPRCapabilities default_caps; int i; - /* First compute the actual set of caps we're running with.. */ - default_caps = default_caps_with_cpu(spapr, first_cpu); + /* Compute the actual set of caps we should run with */ + default_caps = default_caps_with_cpu(spapr, MACHINE(spapr)->cpu_type); for (i = 0; i < SPAPR_CAP_NUM; i++) { /* Store the defaults */ @@ -460,8 +581,11 @@ void spapr_caps_reset(sPAPRMachineState *spapr) spapr->eff.caps[i] = default_caps.caps[i]; } } +} - /* .. then apply those caps to the virtual hardware */ +void spapr_caps_apply(sPAPRMachineState *spapr) +{ + int i; for (i = 0; i < SPAPR_CAP_NUM; i++) { sPAPRCapabilityInfo *info = &capability_table[i]; @@ -474,6 +598,23 @@ void spapr_caps_reset(sPAPRMachineState *spapr) } } +void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu) +{ + int i; + + for (i = 0; i < SPAPR_CAP_NUM; i++) { + sPAPRCapabilityInfo *info = &capability_table[i]; + + /* + * If the apply function can't set the desired level and thinks it's + * fatal, it should cause that. + */ + if (info->cpu_apply) { + info->cpu_apply(spapr, cpu, spapr->eff.caps[i], &error_fatal); + } + } +} + void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp) { Error *local_err = NULL; diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 94afeb399e..993759db47 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -28,9 +28,16 @@ static void spapr_cpu_reset(void *opaque) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + target_ulong lpcr; cpu_reset(cs); + /* Set compatibility mode to match the boot CPU, which was either set + * by the machine reset code or by CAS. This should never fail. + */ + ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort); + /* All CPUs start halted. CPU0 is unhalted from the machine level * reset code and the rest are explicitly started up by the guest * using an RTAS call */ @@ -38,39 +45,53 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_HIOR] = 0; - /* Disable Power-saving mode Exit Cause exceptions for the CPU. - * This can cause issues when rebooting the guest if a secondary - * is awaken */ - if (cs != first_cpu) { - env->spr[SPR_LPCR] &= ~pcc->lpcr_pm; - } + lpcr = env->spr[SPR_LPCR]; - /* Set compatibility mode to match the boot CPU, which was either set - * by the machine reset code or by CAS. This should never fail. + /* Set emulated LPCR to not send interrupts to hypervisor. Note that + * under KVM, the actual HW LPCR will be set differently by KVM itself, + * the settings below ensure proper operations with TCG in absence of + * a real hypervisor. + * + * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for + * real mode accesses, which thankfully defaults to 0 and isn't + * accessible in guest mode. + * + * Disable Power-saving mode Exit Cause exceptions for the CPU, so + * we don't get spurious wakups before an RTAS start-cpu call. */ - if (cs != first_cpu) { - ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort); - } + lpcr &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | pcc->lpcr_pm); + lpcr |= LPCR_LPES0 | LPCR_LPES1; + + /* Set RMLS to the max (ie, 16G) */ + lpcr &= ~LPCR_RMLS; + lpcr |= 1ull << LPCR_RMLS_SHIFT; + + ppc_store_lpcr(cpu, lpcr); + + /* Set a full AMOR so guest can use the AMR as it sees fit */ + env->spr[SPR_AMOR] = 0xffffffffffffffffull; + + spapr_cpu->vpa_addr = 0; + spapr_cpu->slb_shadow_addr = 0; + spapr_cpu->slb_shadow_size = 0; + spapr_cpu->dtl_addr = 0; + spapr_cpu->dtl_size = 0; + + spapr_caps_cpu_apply(SPAPR_MACHINE(qdev_get_machine()), cpu); + + kvm_check_mmu(cpu, &error_fatal); } -static void spapr_cpu_destroy(PowerPCCPU *cpu) -{ - qemu_unregister_reset(spapr_cpu_reset, cpu); -} - -static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, - Error **errp) +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3) { + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; - /* Set time-base frequency to 512 MHz */ - cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); - - /* Enable PAPR mode in TCG or KVM */ - cpu_ppc_set_papr(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); - - qemu_register_reset(spapr_cpu_reset, cpu); - spapr_cpu_reset(cpu); + env->nip = nip; + env->gpr[3] = r3; + CPU(cpu)->halted = 0; + /* Enable Power-saving mode Exit Cause exceptions */ + ppc_store_lpcr(cpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); } /* @@ -92,55 +113,191 @@ const char *spapr_get_cpu_core_type(const char *cpu_type) return object_class_get_name(oc); } -static void spapr_cpu_core_unrealizefn(DeviceState *dev, Error **errp) +static void spapr_unrealize_vcpu(PowerPCCPU *cpu) +{ + qemu_unregister_reset(spapr_cpu_reset, cpu); + object_unparent(cpu->intc); + cpu_remove_sync(CPU(cpu)); + object_unparent(OBJECT(cpu)); +} + +static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp) { sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); int i; for (i = 0; i < cc->nr_threads; i++) { - Object *obj = OBJECT(sc->threads[i]); - DeviceState *dev = DEVICE(obj); - CPUState *cs = CPU(dev); - PowerPCCPU *cpu = POWERPC_CPU(cs); - - spapr_cpu_destroy(cpu); - object_unparent(cpu->intc); - cpu_remove_sync(cs); - object_unparent(obj); + spapr_unrealize_vcpu(sc->threads[i]); } g_free(sc->threads); } -static void spapr_cpu_core_realize_child(Object *child, - sPAPRMachineState *spapr, Error **errp) +static bool slb_shadow_needed(void *opaque) { + sPAPRCPUState *spapr_cpu = opaque; + + return spapr_cpu->slb_shadow_addr != 0; +} + +static const VMStateDescription vmstate_spapr_cpu_slb_shadow = { + .name = "spapr_cpu/vpa/slb_shadow", + .version_id = 1, + .minimum_version_id = 1, + .needed = slb_shadow_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(slb_shadow_addr, sPAPRCPUState), + VMSTATE_UINT64(slb_shadow_size, sPAPRCPUState), + VMSTATE_END_OF_LIST() + } +}; + +static bool dtl_needed(void *opaque) +{ + sPAPRCPUState *spapr_cpu = opaque; + + return spapr_cpu->dtl_addr != 0; +} + +static const VMStateDescription vmstate_spapr_cpu_dtl = { + .name = "spapr_cpu/vpa/dtl", + .version_id = 1, + .minimum_version_id = 1, + .needed = dtl_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(dtl_addr, sPAPRCPUState), + VMSTATE_UINT64(dtl_size, sPAPRCPUState), + VMSTATE_END_OF_LIST() + } +}; + +static bool vpa_needed(void *opaque) +{ + sPAPRCPUState *spapr_cpu = opaque; + + return spapr_cpu->vpa_addr != 0; +} + +static const VMStateDescription vmstate_spapr_cpu_vpa = { + .name = "spapr_cpu/vpa", + .version_id = 1, + .minimum_version_id = 1, + .needed = vpa_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(vpa_addr, sPAPRCPUState), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_spapr_cpu_slb_shadow, + &vmstate_spapr_cpu_dtl, + NULL + } +}; + +static const VMStateDescription vmstate_spapr_cpu_state = { + .name = "spapr_cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_spapr_cpu_vpa, + NULL + } +}; + +static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr, + Error **errp) +{ + CPUPPCState *env = &cpu->env; Error *local_err = NULL; - CPUState *cs = CPU(child); - PowerPCCPU *cpu = POWERPC_CPU(cs); - object_property_set_bool(child, true, "realized", &local_err); + object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); if (local_err) { goto error; } - spapr_cpu_init(spapr, cpu, &local_err); - if (local_err) { - goto error; - } + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, SPAPR_TIMEBASE_FREQ); - cpu->intc = icp_create(child, spapr->icp_type, XICS_FABRIC(spapr), + cpu_ppc_set_vhyp(cpu, PPC_VIRTUAL_HYPERVISOR(spapr)); + kvmppc_set_papr(cpu); + + qemu_register_reset(spapr_cpu_reset, cpu); + spapr_cpu_reset(cpu); + + cpu->intc = icp_create(OBJECT(cpu), spapr->icp_type, XICS_FABRIC(spapr), &local_err); if (local_err) { - goto error; + goto error_unregister; } return; +error_unregister: + qemu_unregister_reset(spapr_cpu_reset, cpu); + cpu_remove_sync(CPU(cpu)); error: error_propagate(errp, local_err); } +static PowerPCCPU *spapr_create_vcpu(sPAPRCPUCore *sc, int i, Error **errp) +{ + sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(sc); + CPUCore *cc = CPU_CORE(sc); + Object *obj; + char *id; + CPUState *cs; + PowerPCCPU *cpu; + Error *local_err = NULL; + + obj = object_new(scc->cpu_type); + + cs = CPU(obj); + cpu = POWERPC_CPU(obj); + cs->cpu_index = cc->core_id + i; + spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); + if (local_err) { + goto err; + } + + cpu->node_id = sc->node_id; + + id = g_strdup_printf("thread[%d]", i); + object_property_add_child(OBJECT(sc), id, obj, &local_err); + g_free(id); + if (local_err) { + goto err; + } + + cpu->machine_data = g_new0(sPAPRCPUState, 1); + if (!sc->pre_3_0_migration) { + vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state, + cpu->machine_data); + } + + object_unref(obj); + return cpu; + +err: + object_unref(obj); + error_propagate(errp, local_err); + return NULL; +} + +static void spapr_delete_vcpu(PowerPCCPU *cpu, sPAPRCPUCore *sc) +{ + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + if (!sc->pre_3_0_migration) { + vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data); + } + cpu->machine_data = NULL; + g_free(spapr_cpu); + object_unparent(OBJECT(cpu)); +} + static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) { /* We don't use SPAPR_MACHINE() in order to exit gracefully if the user @@ -150,10 +307,8 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) (sPAPRMachineState *) object_dynamic_cast(qdev_get_machine(), TYPE_SPAPR_MACHINE); sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); - sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(OBJECT(dev)); CPUCore *cc = CPU_CORE(OBJECT(dev)); Error *local_err = NULL; - Object *obj; int i, j; if (!spapr) { @@ -163,46 +318,27 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) sc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { - char id[32]; - CPUState *cs; - PowerPCCPU *cpu; - - obj = object_new(scc->cpu_type); - - cs = CPU(obj); - cpu = sc->threads[i] = POWERPC_CPU(obj); - cs->cpu_index = cc->core_id + i; - spapr_set_vcpu_id(cpu, cs->cpu_index, &local_err); + sc->threads[i] = spapr_create_vcpu(sc, i, &local_err); if (local_err) { goto err; } - - - /* Set NUMA node for the threads belonged to core */ - cpu->node_id = sc->node_id; - - snprintf(id, sizeof(id), "thread[%d]", i); - object_property_add_child(OBJECT(sc), id, obj, &local_err); - if (local_err) { - goto err; - } - object_unref(obj); } for (j = 0; j < cc->nr_threads; j++) { - obj = OBJECT(sc->threads[j]); - - spapr_cpu_core_realize_child(obj, spapr, &local_err); + spapr_realize_vcpu(sc->threads[j], spapr, &local_err); if (local_err) { - goto err; + goto err_unrealize; } } return; +err_unrealize: + while (--j >= 0) { + spapr_unrealize_vcpu(sc->threads[j]); + } err: while (--i >= 0) { - obj = OBJECT(sc->threads[i]); - object_unparent(obj); + spapr_delete_vcpu(sc->threads[i], sc); } g_free(sc->threads); error_propagate(errp, local_err); @@ -210,6 +346,8 @@ err: static Property spapr_cpu_core_properties[] = { DEFINE_PROP_INT32("node-id", sPAPRCPUCore, node_id, CPU_UNSET_NUMA_NODE_ID), + DEFINE_PROP_BOOL("pre-3.0-migration", sPAPRCPUCore, pre_3_0_migration, + false), DEFINE_PROP_END_OF_LIST() }; @@ -219,7 +357,7 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc); dc->realize = spapr_cpu_core_realize; - dc->unrealize = spapr_cpu_core_unrealizefn; + dc->unrealize = spapr_cpu_core_unrealize; dc->props = spapr_cpu_core_properties; scc->cpu_type = data; } diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index aa251133de..2edb7d1e9c 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -305,7 +305,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, if (!drc->fdt) { visit_type_null(v, NULL, &null, errp); - QDECREF(null); + qobject_unref(null); return; } @@ -366,7 +366,8 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, break; } default: - error_setg(&error_abort, "device FDT in unexpected state: %d", tag); + error_report("device FDT in unexpected state: %d", tag); + abort(); } fdt_offset = fdt_offset_next; } while (fdt_depth != 0); diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 86836f0626..e4f5946a21 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -707,13 +707,18 @@ void spapr_clear_pending_events(sPAPRMachineState *spapr) void spapr_events_init(sPAPRMachineState *spapr) { + int epow_irq; + + epow_irq = spapr_irq_findone(spapr, &error_fatal); + + spapr_irq_claim(spapr, epow_irq, false, &error_fatal); + QTAILQ_INIT(&spapr->pending_events); spapr->event_sources = spapr_event_sources_new(); spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_EPOW, - spapr_irq_alloc(spapr, 0, false, - &error_fatal)); + epow_irq); /* NOTE: if machine supports modern/dedicated hotplug event source, * we add it to the device-tree unconditionally. This means we may @@ -724,9 +729,14 @@ void spapr_events_init(sPAPRMachineState *spapr) * checking that it's enabled. */ if (spapr->use_hotplug_event_source) { + int hp_irq; + + hp_irq = spapr_irq_findone(spapr, &error_fatal); + + spapr_irq_claim(spapr, hp_irq, false, &error_fatal); + spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_HOT_PLUG, - spapr_irq_alloc(spapr, 0, false, - &error_fatal)); + hp_irq); } spapr->epow_notifier.notify = spapr_powerdown_req; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 16bccdd5c0..ae913d070f 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -8,39 +8,44 @@ #include "exec/exec-all.h" #include "helper_regs.h" #include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_cpu_core.h" #include "mmu-hash64.h" #include "cpu-models.h" #include "trace.h" #include "kvm_ppc.h" #include "hw/ppc/spapr_ovec.h" #include "mmu-book3s-v3.h" +#include "hw/mem/memory-device.h" -struct SPRSyncState { - int spr; +struct LPCRSyncState { target_ulong value; target_ulong mask; }; -static void do_spr_sync(CPUState *cs, run_on_cpu_data arg) +static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg) { - struct SPRSyncState *s = arg.host_ptr; + struct LPCRSyncState *s = arg.host_ptr; PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + target_ulong lpcr; cpu_synchronize_state(cs); - env->spr[s->spr] &= ~s->mask; - env->spr[s->spr] |= s->value; + lpcr = env->spr[SPR_LPCR]; + lpcr &= ~s->mask; + lpcr |= s->value; + ppc_store_lpcr(cpu, lpcr); } -static void set_spr(CPUState *cs, int spr, target_ulong value, - target_ulong mask) +static void set_all_lpcrs(target_ulong value, target_ulong mask) { - struct SPRSyncState s = { - .spr = spr, + CPUState *cs; + struct LPCRSyncState s = { .value = value, .mask = mask }; - run_on_cpu(cs, do_spr_sync, RUN_ON_CPU_HOST_PTR(&s)); + CPU_FOREACH(cs) { + run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s)); + } } static bool has_spr(PowerPCCPU *cpu, int spr) @@ -63,13 +68,13 @@ static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) static bool is_ram_address(sPAPRMachineState *spapr, hwaddr addr) { MachineState *machine = MACHINE(spapr); - MemoryHotplugState *hpms = &spapr->hotplug_memory; + DeviceMemoryState *dms = machine->device_memory; if (addr < machine->ram_size) { return true; } - if ((addr >= hpms->base) - && ((addr - hpms->base) < memory_region_size(&hpms->mr))) { + if ((addr >= dms->base) + && ((addr - dms->base) < memory_region_size(&dms->mr))) { return true; } @@ -904,9 +909,11 @@ unmap_out: #define VPA_SHARED_PROC_OFFSET 0x9 #define VPA_SHARED_PROC_VAL 0x2 -static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) +static target_ulong register_vpa(PowerPCCPU *cpu, target_ulong vpa) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); uint16_t size; uint8_t tmp; @@ -931,32 +938,34 @@ static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) return H_PARAMETER; } - env->vpa_addr = vpa; + spapr_cpu->vpa_addr = vpa; - tmp = ldub_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET); + tmp = ldub_phys(cs->as, spapr_cpu->vpa_addr + VPA_SHARED_PROC_OFFSET); tmp |= VPA_SHARED_PROC_VAL; - stb_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); + stb_phys(cs->as, spapr_cpu->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); return H_SUCCESS; } -static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa) +static target_ulong deregister_vpa(PowerPCCPU *cpu, target_ulong vpa) { - if (env->slb_shadow_addr) { + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + if (spapr_cpu->slb_shadow_addr) { return H_RESOURCE; } - if (env->dtl_addr) { + if (spapr_cpu->dtl_addr) { return H_RESOURCE; } - env->vpa_addr = 0; + spapr_cpu->vpa_addr = 0; return H_SUCCESS; } -static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) +static target_ulong register_slb_shadow(PowerPCCPU *cpu, target_ulong addr) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); uint32_t size; if (addr == 0) { @@ -964,7 +973,7 @@ static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_be_phys(cs->as, addr + 0x4); + size = ldl_be_phys(CPU(cpu)->as, addr + 0x4); if (size < 0x8) { return H_PARAMETER; } @@ -973,26 +982,28 @@ static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) return H_PARAMETER; } - if (!env->vpa_addr) { + if (!spapr_cpu->vpa_addr) { return H_RESOURCE; } - env->slb_shadow_addr = addr; - env->slb_shadow_size = size; + spapr_cpu->slb_shadow_addr = addr; + spapr_cpu->slb_shadow_size = size; return H_SUCCESS; } -static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr) +static target_ulong deregister_slb_shadow(PowerPCCPU *cpu, target_ulong addr) { - env->slb_shadow_addr = 0; - env->slb_shadow_size = 0; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + spapr_cpu->slb_shadow_addr = 0; + spapr_cpu->slb_shadow_size = 0; return H_SUCCESS; } -static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) +static target_ulong register_dtl(PowerPCCPU *cpu, target_ulong addr) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); uint32_t size; if (addr == 0) { @@ -1000,26 +1011,28 @@ static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) return H_HARDWARE; } - size = ldl_be_phys(cs->as, addr + 0x4); + size = ldl_be_phys(CPU(cpu)->as, addr + 0x4); if (size < 48) { return H_PARAMETER; } - if (!env->vpa_addr) { + if (!spapr_cpu->vpa_addr) { return H_RESOURCE; } - env->dtl_addr = addr; - env->dtl_size = size; + spapr_cpu->dtl_addr = addr; + spapr_cpu->dtl_size = size; return H_SUCCESS; } -static target_ulong deregister_dtl(CPUPPCState *env, target_ulong addr) +static target_ulong deregister_dtl(PowerPCCPU *cpu, target_ulong addr) { - env->dtl_addr = 0; - env->dtl_size = 0; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + + spapr_cpu->dtl_addr = 0; + spapr_cpu->dtl_size = 0; return H_SUCCESS; } @@ -1031,38 +1044,36 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong procno = args[1]; target_ulong vpa = args[2]; target_ulong ret = H_PARAMETER; - CPUPPCState *tenv; PowerPCCPU *tcpu; tcpu = spapr_find_cpu(procno); if (!tcpu) { return H_PARAMETER; } - tenv = &tcpu->env; switch (flags) { case FLAGS_REGISTER_VPA: - ret = register_vpa(tenv, vpa); + ret = register_vpa(tcpu, vpa); break; case FLAGS_DEREGISTER_VPA: - ret = deregister_vpa(tenv, vpa); + ret = deregister_vpa(tcpu, vpa); break; case FLAGS_REGISTER_SLBSHADOW: - ret = register_slb_shadow(tenv, vpa); + ret = register_slb_shadow(tcpu, vpa); break; case FLAGS_DEREGISTER_SLBSHADOW: - ret = deregister_slb_shadow(tenv, vpa); + ret = deregister_slb_shadow(tcpu, vpa); break; case FLAGS_REGISTER_DTL: - ret = register_dtl(tenv, vpa); + ret = register_dtl(tcpu, vpa); break; case FLAGS_DEREGISTER_DTL: - ret = deregister_dtl(tenv, vpa); + ret = deregister_dtl(tcpu, vpa); break; } @@ -1235,8 +1246,6 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, target_ulong value1, target_ulong value2) { - CPUState *cs; - if (value1) { return H_P3; } @@ -1246,16 +1255,12 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, switch (mflags) { case H_SET_MODE_ENDIAN_BIG: - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, 0, LPCR_ILE); - } + set_all_lpcrs(0, LPCR_ILE); spapr_pci_switch_vga(true); return H_SUCCESS; case H_SET_MODE_ENDIAN_LITTLE: - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, LPCR_ILE, LPCR_ILE); - } + set_all_lpcrs(LPCR_ILE, LPCR_ILE); spapr_pci_switch_vga(false); return H_SUCCESS; } @@ -1268,7 +1273,6 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, target_ulong value1, target_ulong value2) { - CPUState *cs; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); if (!(pcc->insns_flags2 & PPC2_ISA207S)) { @@ -1285,9 +1289,7 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, return H_UNSUPPORTED_FLAG; } - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, mflags << LPCR_AIL_SHIFT, LPCR_AIL); - } + set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL); return H_SUCCESS; } @@ -1364,7 +1366,6 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, target_ulong opcode, target_ulong *args) { - CPUState *cs; target_ulong flags = args[0]; target_ulong proc_tbl = args[1]; target_ulong page_size = args[2]; @@ -1422,12 +1423,9 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, spapr->patb_entry = cproc; /* Save new process table */ /* Update the UPRT and GTSE bits in the LPCR for all cpus */ - CPU_FOREACH(cs) { - set_spr(cs, SPR_LPCR, - ((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) | - ((flags & FLAG_GTSE) ? LPCR_GTSE : 0), - LPCR_UPRT | LPCR_GTSE); - } + set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) | + ((flags & FLAG_GTSE) ? LPCR_GTSE : 0), + LPCR_UPRT | LPCR_GTSE); if (kvm_enabled()) { return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX, @@ -1556,6 +1554,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, error_report_err(local_err); return H_HARDWARE; } + error_free(local_err); local_err = NULL; } } diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index aaa6010d5c..1b0880ac9e 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -112,7 +112,8 @@ static void spapr_tce_free_table(uint64_t *table, int fd, uint32_t nb_table) /* Called from RCU critical section */ static IOMMUTLBEntry spapr_tce_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, + int iommu_idx) { sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); uint64_t tce; @@ -428,7 +429,7 @@ static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, entry.translated_addr = tce & page_mask; entry.addr_mask = ~page_mask; entry.perm = spapr_tce_iommu_access_flags(tce); - memory_region_notify_iommu(&tcet->iommu, entry); + memory_region_notify_iommu(&tcet->iommu, 0, entry); return H_SUCCESS; } diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 41df4c35ba..318bf33de4 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -113,7 +113,7 @@ void spapr_ovec_cleanup(sPAPROptionVector *ov) void spapr_ovec_set(sPAPROptionVector *ov, long bitnr) { g_assert(ov); - g_assert_cmpint(bitnr, <, OV_MAXBITS); + g_assert(bitnr < OV_MAXBITS); set_bit(bitnr, ov->bitmap); } @@ -121,7 +121,7 @@ void spapr_ovec_set(sPAPROptionVector *ov, long bitnr) void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr) { g_assert(ov); - g_assert_cmpint(bitnr, <, OV_MAXBITS); + g_assert(bitnr < OV_MAXBITS); clear_bit(bitnr, ov->bitmap); } @@ -129,7 +129,7 @@ void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr) bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr) { g_assert(ov); - g_assert_cmpint(bitnr, <, OV_MAXBITS); + g_assert(bitnr < OV_MAXBITS); return test_bit(bitnr, ov->bitmap) ? true : false; } @@ -186,7 +186,7 @@ sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) int i; g_assert(table_addr); - g_assert_cmpint(vector, >=, 1); /* vector numbering starts at 1 */ + g_assert(vector >= 1); /* vector numbering starts at 1 */ addr = vector_addr(table_addr, vector); if (!addr) { @@ -195,7 +195,7 @@ sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) } vector_len = ldub_phys(&address_space_memory, addr++) + 1; - g_assert_cmpint(vector_len, <=, OV_MAXBYTES); + g_assert(vector_len <= OV_MAXBYTES); ov = spapr_ovec_new(); for (i = 0; i < vector_len; i++) { @@ -225,7 +225,7 @@ int spapr_ovec_populate_dt(void *fdt, int fdt_offset, * encoding/sizing expected in ibm,client-architecture-support */ vec_len = (lastbit == OV_MAXBITS) ? 1 : lastbit / BITS_PER_BYTE + 1; - g_assert_cmpint(vec_len, <=, OV_MAXBYTES); + g_assert(vec_len <= OV_MAXBYTES); /* guest expects vector len encoded as vec_len - 1, since the length byte * is assumed and not included, and the first byte of the vector * is assumed as well diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 39a14980d3..497b896c7d 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -279,6 +279,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, spapr_pci_msi *msi; int *config_addr_key; Error *err = NULL; + int i; /* Fins sPAPRPHBState */ phb = spapr_pci_find_phb(spapr, buid); @@ -371,8 +372,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, } /* Allocate MSIs */ - irq = spapr_irq_alloc_block(spapr, req_num, false, - ret_intr_type == RTAS_TYPE_MSI, &err); + irq = spapr_irq_find(spapr, req_num, ret_intr_type == RTAS_TYPE_MSI, &err); if (err) { error_reportf_err(err, "Can't allocate MSIs for device %x: ", config_addr); @@ -380,6 +380,16 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } + for (i = 0; i < req_num; i++) { + spapr_irq_claim(spapr, irq + i, false, &err); + if (err) { + error_reportf_err(err, "Can't allocate MSIs for device %x: ", + config_addr); + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); + return; + } + } + /* Release previous MSIs */ if (msi) { spapr_irq_free(spapr, msi->first_irq, msi->num); @@ -1698,7 +1708,14 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) uint32_t irq; Error *local_err = NULL; - irq = spapr_irq_alloc_block(spapr, 1, true, false, &local_err); + irq = spapr_irq_findone(spapr, &local_err); + if (local_err) { + error_propagate(errp, local_err); + error_prepend(errp, "can't allocate LSIs: "); + return; + } + + spapr_irq_claim(spapr, irq, true, &local_err); if (local_err) { error_propagate(errp, local_err); error_prepend(errp, "can't allocate LSIs: "); @@ -1717,13 +1734,6 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) } /* DMA setup */ - if (((sphb->page_size_mask & qemu_getrampagesize()) == 0) - && kvm_enabled()) { - warn_report("System page size 0x%lx is not enabled in page_size_mask " - "(0x%"PRIx64"). Performance may be slow", - qemu_getrampagesize(), sphb->page_size_mask); - } - for (i = 0; i < windows_supported; ++i) { tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]); if (!tcet) { diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 0ec5fa4cfe..4ac96bc94b 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -32,11 +32,12 @@ #include "hw/qdev.h" #include "sysemu/device_tree.h" #include "sysemu/cpus.h" -#include "sysemu/kvm.h" +#include "sysemu/hw_accel.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "hw/ppc/spapr_rtas.h" +#include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/ppc.h" #include "hw/boards.h" @@ -45,6 +46,8 @@ #include "qemu/cutils.h" #include "trace.h" #include "hw/ppc/fdt.h" +#include "target/ppc/mmu-hash64.h" +#include "target/ppc/mmu-book3s-v3.h" static void rtas_display_character(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, @@ -119,34 +122,16 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); } -/* - * Set the timebase offset of the CPU to that of first CPU. - * This helps hotplugged CPU to have the correct timebase offset. - */ -static void spapr_cpu_update_tb_offset(PowerPCCPU *cpu) -{ - PowerPCCPU *fcpu = POWERPC_CPU(first_cpu); - - cpu->env.tb_env->tb_offset = fcpu->env.tb_env->tb_offset; -} - -static void spapr_cpu_set_endianness(PowerPCCPU *cpu) -{ - PowerPCCPU *fcpu = POWERPC_CPU(first_cpu); - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(fcpu); - - if (!pcc->interrupts_big_endian(fcpu)) { - cpu->env.spr[SPR_LPCR] |= LPCR_ILE; - } -} - -static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr, +static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { target_ulong id, start, r3; - PowerPCCPU *cpu; + PowerPCCPU *newcpu; + CPUPPCState *env; + PowerPCCPUClass *pcc; + target_ulong lpcr; if (nargs != 3 || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -157,41 +142,55 @@ static void rtas_start_cpu(PowerPCCPU *cpu_, sPAPRMachineState *spapr, start = rtas_ld(args, 1); r3 = rtas_ld(args, 2); - cpu = spapr_find_cpu(id); - if (cpu != NULL) { - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - - if (!cs->halted) { - rtas_st(rets, 0, RTAS_OUT_HW_ERROR); - return; - } - - /* This will make sure qemu state is up to date with kvm, and - * mark it dirty so our changes get flushed back before the - * new cpu enters */ - kvm_cpu_synchronize_state(cs); - - env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); - - /* Enable Power-saving mode Exit Cause exceptions for the new CPU */ - env->spr[SPR_LPCR] |= pcc->lpcr_pm; - - env->nip = start; - env->gpr[3] = r3; - cs->halted = 0; - spapr_cpu_set_endianness(cpu); - spapr_cpu_update_tb_offset(cpu); - - qemu_cpu_kick(cs); - - rtas_st(rets, 0, RTAS_OUT_SUCCESS); + newcpu = spapr_find_cpu(id); + if (!newcpu) { + /* Didn't find a matching cpu */ + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); return; } - /* Didn't find a matching cpu */ - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + env = &newcpu->env; + pcc = POWERPC_CPU_GET_CLASS(newcpu); + + if (!CPU(newcpu)->halted) { + rtas_st(rets, 0, RTAS_OUT_HW_ERROR); + return; + } + + cpu_synchronize_state(CPU(newcpu)); + + env->msr = (1ULL << MSR_SF) | (1ULL << MSR_ME); + + /* Enable Power-saving mode Exit Cause exceptions for the new CPU */ + lpcr = env->spr[SPR_LPCR]; + if (!pcc->interrupts_big_endian(callcpu)) { + lpcr |= LPCR_ILE; + } + if (env->mmu_model == POWERPC_MMU_3_00) { + /* + * New cpus are expected to start in the same radix/hash mode + * as the existing CPUs + */ + if (ppc64_radix_guest(callcpu)) { + lpcr |= LPCR_UPRT | LPCR_GTSE; + } else { + lpcr &= ~(LPCR_UPRT | LPCR_GTSE); + } + } + ppc_store_lpcr(newcpu, lpcr); + + /* + * Set the timebase offset of the new CPU to that of the invoking + * CPU. This helps hotplugged CPU to have the correct timebase + * offset. + */ + newcpu->env.tb_env->tb_offset = callcpu->env.tb_env->tb_offset; + + spapr_cpu_set_entry_state(newcpu, start, r3); + + qemu_cpu_kick(CPU(newcpu)); + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); } static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, @@ -203,13 +202,12 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - cs->halted = 1; - qemu_cpu_kick(cs); - /* Disable Power-saving mode Exit Cause exceptions for the CPU. * This could deliver an interrupt on a dying CPU and crash the * guest */ - env->spr[SPR_LPCR] &= ~pcc->lpcr_pm; + ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); + cs->halted = 1; + qemu_cpu_kick(cs); } static inline int sysparm_st(target_ulong addr, target_ulong len, @@ -239,11 +237,11 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, switch (parameter) { case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: { char *param_val = g_strdup_printf("MaxEntCap=%d," - "DesMem=%llu," + "DesMem=%" PRIu64 "," "DesProcs=%d," "MaxPlatProcs=%d", max_cpus, - current_machine->ram_size / M_BYTE, + current_machine->ram_size / MiB, smp_cpus, max_cpus); ret = sysparm_st(buffer, length, param_val, strlen(param_val) + 1); diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c index 177dcffc9b..329feb148f 100644 --- a/hw/ppc/spapr_rtas_ddw.c +++ b/hw/ppc/spapr_rtas_ddw.c @@ -122,9 +122,8 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, if (machine->ram_size == machine->maxram_size) { max_window_size = machine->ram_size; } else { - MemoryHotplugState *hpms = &spapr->hotplug_memory; - - max_window_size = hpms->base + memory_region_size(&hpms->mr); + max_window_size = machine->device_memory->base + + memory_region_size(&machine->device_memory->mr); } avail = SPAPR_PCI_DMA_MAX_WINDOWS - spapr_phb_get_active_win_num(sphb); diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 472dd6f33a..be9af71437 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qapi/visitor.h" #include "hw/hw.h" #include "qemu/log.h" #include "sysemu/sysemu.h" @@ -32,6 +33,7 @@ #include "sysemu/kvm.h" #include "sysemu/device_tree.h" #include "kvm_ppc.h" +#include "sysemu/qtest.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" @@ -41,8 +43,35 @@ #include +static void spapr_vio_get_irq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop); + + visit_type_uint32(v, name, ptr, errp); +} + +static void spapr_vio_set_irq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Property *prop = opaque; + uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop); + + if (!qtest_enabled()) { + warn_report(TYPE_VIO_SPAPR_DEVICE " '%s' property is deprecated", name); + } + visit_type_uint32(v, name, ptr, errp); +} + +static const PropertyInfo spapr_vio_irq_propinfo = { + .name = "irq", + .get = spapr_vio_get_irq, + .set = spapr_vio_set_irq, +}; + static Property spapr_vio_props[] = { - DEFINE_PROP_UINT32("irq", VIOsPAPRDevice, irq, 0), \ + DEFINE_PROP("irq", VIOsPAPRDevice, irq, spapr_vio_irq_propinfo, uint32_t), DEFINE_PROP_END_OF_LIST(), }; @@ -455,7 +484,15 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) dev->qdev.id = id; } - dev->irq = spapr_irq_alloc(spapr, dev->irq, false, &local_err); + if (!dev->irq) { + dev->irq = spapr_irq_findone(spapr, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + spapr_irq_claim(spapr, dev->irq, false, &local_err); if (local_err) { error_propagate(errp, local_err); return; diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index 66ec7eda6e..dc5e65aee9 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -92,10 +92,6 @@ rs6000mc_size_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" rs6000mc_size_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x" rs6000mc_parity_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" -# hw/ppc/mac_newworld.c -mac99_uninorth_write(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 -mac99_uninorth_read(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"PRIx64 - # hw/ppc/ppc4xx_pci.c ppc4xx_pci_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" ppc4xx_pci_set_irq(int irq_num) "PCI irq %d" diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 77a1778e07..7891464cd9 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" @@ -44,10 +45,8 @@ #include "hw/ppc/ppc4xx.h" #include "ppc405.h" -#include "sysemu/block-backend.h" - #define EPAPR_MAGIC (0x45504150) -#define FLASH_SIZE (16 * 1024 * 1024) +#define FLASH_SIZE (16 * MiB) #define INTC_BASEADDR 0x81800000 #define UART16550_BASEADDR 0x83e01003 @@ -129,7 +128,7 @@ static void main_cpu_reset(void *opaque) * r8: 0 * r9: 0 */ - env->gpr[1] = (16<<20) - 8; + env->gpr[1] = (16 * MiB) - 8; /* Provide a device-tree. */ env->gpr[3] = bi->fdt; env->nip = bi->bootstrap_pc; @@ -237,7 +236,7 @@ static void virtex_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); pflash_cfi01_register(PFLASH_BASEADDR, NULL, "virtex.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), FLASH_SIZE >> 16, + 64 * KiB, FLASH_SIZE >> 16, 1, 0x89, 0x18, 0x0000, 0x0, 1); cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; @@ -251,7 +250,7 @@ static void virtex_init(MachineState *machine) } serial_mm_init(address_space_mem, UART16550_BASEADDR, 2, irq[UART16550_IRQ], - 115200, serial_hds[0], DEVICE_LITTLE_ENDIAN); + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); /* 2 timers at irq 2 @ 62 Mhz. */ dev = qdev_create(NULL, "xlnx.xps-timer"); diff --git a/hw/rdma/rdma_backend.c b/hw/rdma/rdma_backend.c index 5c7b3d8949..e9ced6f9ef 100644 --- a/hw/rdma/rdma_backend.c +++ b/hw/rdma/rdma_backend.c @@ -774,7 +774,7 @@ int rdma_backend_init(RdmaBackendDev *backend_dev, goto out_destroy_comm_channel; } - if (backend_dev->backend_gid_idx > port_attr.gid_tbl_len) { + if (backend_dev->backend_gid_idx >= port_attr.gid_tbl_len) { error_setg(errp, "Invalid backend_gid_idx, should be less than %d", port_attr.gid_tbl_len); goto out_destroy_comm_channel; diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c index 51a47d7292..415da15efe 100644 --- a/hw/rdma/rdma_rm.c +++ b/hw/rdma/rdma_rm.c @@ -21,8 +21,6 @@ #include "rdma_backend.h" #include "rdma_rm.h" -#define MAX_RM_TBL_NAME 16 - /* Page directory and page tables */ #define PG_DIR_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } #define PG_TBL_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } diff --git a/hw/rdma/rdma_rm_defs.h b/hw/rdma/rdma_rm_defs.h index fc646da61f..226011176d 100644 --- a/hw/rdma/rdma_rm_defs.h +++ b/hw/rdma/rdma_rm_defs.h @@ -20,9 +20,9 @@ #define MAX_PORTS 1 #define MAX_PORT_GIDS 1 +#define MAX_GIDS MAX_PORT_GIDS #define MAX_PORT_PKEYS 1 -#define MAX_PKEYS 1 -#define MAX_GIDS 2048 +#define MAX_PKEYS MAX_PORT_PKEYS #define MAX_UCS 512 #define MAX_MR_SIZE (1UL << 27) #define MAX_QP 1024 @@ -34,9 +34,9 @@ #define MAX_QP_INIT_RD_ATOM 16 #define MAX_AH 64 -#define MAX_RMRESTBL_NAME_SZ 16 +#define MAX_RM_TBL_NAME 16 typedef struct RdmaRmResTbl { - char name[MAX_RMRESTBL_NAME_SZ]; + char name[MAX_RM_TBL_NAME]; QemuMutex lock; unsigned long *bitmap; size_t tbl_sz; @@ -87,7 +87,6 @@ typedef struct RdmaRmQP { typedef struct RdmaRmPort { union ibv_gid gid_tbl[MAX_PORT_GIDS]; enum ibv_port_state state; - int *pkey_tbl; /* TODO: Not yet supported */ } RdmaRmPort; typedef struct RdmaDeviceResources { diff --git a/hw/rdma/vmw/pvrdma.h b/hw/rdma/vmw/pvrdma.h index 8c173cb824..81e0e0e99c 100644 --- a/hw/rdma/vmw/pvrdma.h +++ b/hw/rdma/vmw/pvrdma.h @@ -16,6 +16,7 @@ #ifndef PVRDMA_PVRDMA_H #define PVRDMA_PVRDMA_H +#include "qemu/units.h" #include "hw/pci/pci.h" #include "hw/pci/msix.h" @@ -30,8 +31,8 @@ #define RDMA_MSIX_BAR_IDX 0 #define RDMA_REG_BAR_IDX 1 #define RDMA_UAR_BAR_IDX 2 -#define RDMA_BAR0_MSIX_SIZE (16 * 1024) -#define RDMA_BAR1_REGS_SIZE 256 +#define RDMA_BAR0_MSIX_SIZE (16 * KiB) +#define RDMA_BAR1_REGS_SIZE 64 #define RDMA_BAR2_UAR_SIZE (0x1000 * MAX_UCS) /* each uc gets page */ /* MSIX */ @@ -86,7 +87,7 @@ static inline int get_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t *val) { int idx = addr >> 2; - if (idx > RDMA_BAR1_REGS_SIZE) { + if (idx >= RDMA_BAR1_REGS_SIZE) { return -EINVAL; } @@ -99,7 +100,7 @@ static inline int set_reg_val(PVRDMADev *dev, hwaddr addr, uint32_t val) { int idx = addr >> 2; - if (idx > RDMA_BAR1_REGS_SIZE) { + if (idx >= RDMA_BAR1_REGS_SIZE) { return -EINVAL; } diff --git a/hw/rdma/vmw/pvrdma_cmd.c b/hw/rdma/vmw/pvrdma_cmd.c index 99019d8741..14255d609f 100644 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ b/hw/rdma/vmw/pvrdma_cmd.c @@ -232,7 +232,7 @@ static int create_mr(PVRDMADev *dev, union pvrdma_cmd_req *req, cmd->start, cmd->length, host_virt, cmd->access_flags, &resp->mr_handle, &resp->lkey, &resp->rkey); - if (!resp->hdr.err) { + if (host_virt && !resp->hdr.err) { munmap(host_virt, cmd->length); } @@ -576,7 +576,7 @@ static int create_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, pr_dbg("index=%d\n", cmd->index); - if (cmd->index > MAX_PORT_GIDS) { + if (cmd->index >= MAX_PORT_GIDS) { return -EINVAL; } @@ -603,7 +603,11 @@ static int destroy_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_destroy_bind *cmd = &req->destroy_bind; - pr_dbg("clear index %d\n", cmd->index); + pr_dbg("index=%d\n", cmd->index); + + if (cmd->index >= MAX_PORT_GIDS) { + return -EINVAL; + } memset(dev->rdma_dev_res.ports[0].gid_tbl[cmd->index].raw, 0, sizeof(dev->rdma_dev_res.ports[0].gid_tbl[cmd->index].raw)); diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index c552248c90..3ed7409763 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -275,15 +275,6 @@ static void init_dsr_dev_caps(PVRDMADev *dev) pr_dbg("Initialized\n"); } -static void free_ports(PVRDMADev *dev) -{ - int i; - - for (i = 0; i < MAX_PORTS; i++) { - g_free(dev->rdma_dev_res.ports[i].gid_tbl); - } -} - static void init_ports(PVRDMADev *dev, Error **errp) { int i; @@ -292,10 +283,6 @@ static void init_ports(PVRDMADev *dev, Error **errp) for (i = 0; i < MAX_PORTS; i++) { dev->rdma_dev_res.ports[i].state = IBV_PORT_DOWN; - - dev->rdma_dev_res.ports[i].pkey_tbl = - g_malloc0(sizeof(*dev->rdma_dev_res.ports[i].pkey_tbl) * - MAX_PORT_PKEYS); } } @@ -462,14 +449,14 @@ static void init_bars(PCIDevice *pdev) /* BAR 1 - Registers */ memset(&dev->regs_data, 0, sizeof(dev->regs_data)); memory_region_init_io(&dev->regs, OBJECT(dev), ®s_ops, dev, - "pvrdma-regs", RDMA_BAR1_REGS_SIZE); + "pvrdma-regs", sizeof(dev->regs_data)); pci_register_bar(pdev, RDMA_REG_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, &dev->regs); /* BAR 2 - UAR */ memset(&dev->uar_data, 0, sizeof(dev->uar_data)); memory_region_init_io(&dev->uar, OBJECT(dev), &uar_ops, dev, "rdma-uar", - RDMA_BAR2_UAR_SIZE); + sizeof(dev->uar_data)); pci_register_bar(pdev, RDMA_UAR_BAR_IDX, PCI_BASE_ADDRESS_SPACE_MEMORY, &dev->uar); } @@ -622,8 +609,6 @@ static void pvrdma_exit(PCIDevice *pdev) pvrdma_qp_ops_fini(); - free_ports(dev); - rdma_rm_fini(&dev->rdma_dev_res); rdma_backend_fini(&dev->backend_dev); diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c index 750ade6c31..99bb51111e 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ b/hw/rdma/vmw/pvrdma_qp_ops.c @@ -216,6 +216,7 @@ void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle) cq = rdma_rm_get_cq(dev_res, cq_handle); if (!cq) { pr_dbg("Invalid CQ# %d\n", cq_handle); + return; } rdma_backend_poll_cq(dev_res, &cq->backend_cq); diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index 14e3c186fe..e34a26a0ef 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -45,11 +45,10 @@ static void riscv_harts_realize(DeviceState *dev, Error **errp) s->harts = g_new0(RISCVCPU, s->num_harts); for (n = 0; n < s->num_harts; n++) { - - object_initialize(&s->harts[n], sizeof(RISCVCPU), s->cpu_type); + object_initialize_child(OBJECT(s), "harts[*]", &s->harts[n], + sizeof(RISCVCPU), s->cpu_type, + &error_abort, NULL); s->harts[n].env.mhartid = n; - object_property_add_child(OBJECT(s), "harts[*]", OBJECT(&s->harts[n]), - &error_abort); qemu_register_reset(riscv_harts_cpu_reset, &s->harts[n]); object_property_set_bool(OBJECT(&s->harts[n]), true, "realized", &err); @@ -68,16 +67,10 @@ static void riscv_harts_class_init(ObjectClass *klass, void *data) dc->realize = riscv_harts_realize; } -static void riscv_harts_init(Object *obj) -{ - /* RISCVHartArrayState *s = SIFIVE_COREPLEX(obj); */ -} - static const TypeInfo riscv_harts_info = { .name = TYPE_RISCV_HART_ARRAY, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(RISCVHartArrayState), - .instance_init = riscv_harts_init, .class_init = riscv_harts_class_init, }; diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c index 3e17f30251..4f7b11dc37 100644 --- a/hw/riscv/riscv_htif.c +++ b/hw/riscv/riscv_htif.c @@ -29,7 +29,6 @@ #include "chardev/char-fe.h" #include "hw/riscv/riscv_htif.h" #include "qemu/timer.h" -#include "exec/address-spaces.h" #include "qemu/error-report.h" #define RISCV_DEBUG_HTIF 0 @@ -41,17 +40,20 @@ } while (0) static uint64_t fromhost_addr, tohost_addr; +static int address_symbol_set; void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value, - uint64_t st_size) + uint64_t st_size) { if (strcmp("fromhost", st_name) == 0) { + address_symbol_set |= 1; fromhost_addr = st_value; if (st_size != 8) { error_report("HTIF fromhost must be 8 bytes"); exit(1); } } else if (strcmp("tohost", st_name) == 0) { + address_symbol_set |= 2; tohost_addr = st_value; if (st_size != 8) { error_report("HTIF tohost must be 8 bytes"); @@ -248,10 +250,11 @@ HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem, 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 (base) { + if (address_symbol_set == 3) { memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s, - TYPE_HTIF_UART, size); - memory_region_add_subregion(address_space, base, &s->mmio); + TYPE_HTIF_UART, size); + memory_region_add_subregion_overlap(address_space, base, + &s->mmio, 1); } return s; diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c index 4893453b70..7cc606e065 100644 --- a/hw/riscv/sifive_clint.c +++ b/hw/riscv/sifive_clint.c @@ -26,13 +26,10 @@ #include "hw/riscv/sifive_clint.h" #include "qemu/timer.h" -/* See: riscv-pk/machine/sbi_entry.S and arch/riscv/kernel/time.c */ -#define TIMER_FREQ (10 * 1000 * 1000) - static uint64_t cpu_riscv_read_rtc(void) { - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), TIMER_FREQ, - NANOSECONDS_PER_SECOND); + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + SIFIVE_CLINT_TIMEBASE_FREQ, NANOSECONDS_PER_SECOND); } /* @@ -59,7 +56,7 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) diff = cpu->env.timecmp - rtc_r; /* back to ns (note args switched in muldiv64) */ next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - muldiv64(diff, NANOSECONDS_PER_SECOND, TIMER_FREQ); + muldiv64(diff, NANOSECONDS_PER_SECOND, SIFIVE_CLINT_TIMEBASE_FREQ); timer_mod(cpu->env.timer, next); } diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 19eca36ff4..4577d72037 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -74,26 +74,13 @@ static const struct MemmapEntry { [SIFIVE_E_DTIM] = { 0x80000000, 0x4000 } }; -static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) -{ - int i; - for (i = 0; i < (len >> 2); i++) { - stl_phys(&address_space_memory, pa + (i << 2), rom[i]); - } -} - -static uint64_t identity_translate(void *opaque, uint64_t addr) -{ - return addr; -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf(kernel_filename, identity_translate, NULL, + if (load_elf(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, - 0, ELF_MACHINE, 1, 0) < 0) { + 0, EM_RISCV, 1, 0) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); exit(1); } @@ -115,17 +102,12 @@ static void riscv_sifive_e_init(MachineState *machine) SiFiveEState *s = g_new0(SiFiveEState, 1); MemoryRegion *sys_mem = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *mask_rom = g_new(MemoryRegion, 1); - MemoryRegion *xip_mem = g_new(MemoryRegion, 1); + int i; - /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); - object_property_set_str(OBJECT(&s->soc), SIFIVE_E_CPU, "cpu-type", - &error_abort); - object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", - &error_abort); + /* Initialize SoC */ + object_initialize_child(OBJECT(machine), "soc", &s->soc, + sizeof(s->soc), TYPE_RISCV_E_SOC, + &error_abort, NULL); object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort); @@ -135,8 +117,51 @@ static void riscv_sifive_e_init(MachineState *machine) memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_DTIM].base, main_mem); + /* Mask ROM reset vector */ + uint32_t reset_vec[2] = { + 0x204002b7, /* 0x1000: lui t0,0x20400 */ + 0x00028067, /* 0x1004: jr t0 */ + }; + + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[SIFIVE_E_MROM].base, &address_space_memory); + + if (machine->kernel_filename) { + load_kernel(machine->kernel_filename); + } +} + +static void riscv_sifive_e_soc_init(Object *obj) +{ + SiFiveESoCState *s = RISCV_E_SOC(obj); + + object_initialize_child(obj, "cpus", &s->cpus, + sizeof(s->cpus), TYPE_RISCV_HART_ARRAY, + &error_abort, NULL); + object_property_set_str(OBJECT(&s->cpus), SIFIVE_E_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", + &error_abort); +} + +static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) +{ + const struct MemmapEntry *memmap = sifive_e_memmap; + + SiFiveESoCState *s = RISCV_E_SOC(dev); + MemoryRegion *sys_mem = get_system_memory(); + MemoryRegion *xip_mem = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + + object_property_set_bool(OBJECT(&s->cpus), true, "realized", + &error_abort); + /* Mask ROM */ - memory_region_init_ram(mask_rom, NULL, "riscv.sifive.e.mrom", + memory_region_init_rom(mask_rom, NULL, "riscv.sifive.e.mrom", memmap[SIFIVE_E_MROM].size, &error_fatal); memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_MROM].base, mask_rom); @@ -162,13 +187,14 @@ static void riscv_sifive_e_init(MachineState *machine) sifive_mmio_emulate(sys_mem, "riscv.sifive.e.gpio0", memmap[SIFIVE_E_GPIO0].base, memmap[SIFIVE_E_GPIO0].size); sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART0].base, - serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART0_IRQ]); + serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART0_IRQ)); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi0", memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0", memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size); /* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base, - serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_E_UART1_IRQ]); */ + serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), + SIFIVE_E_UART1_IRQ)); */ sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1", memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1", @@ -183,40 +209,8 @@ static void riscv_sifive_e_init(MachineState *machine) memmap[SIFIVE_E_XIP].size, &error_fatal); memory_region_set_readonly(xip_mem, true); memory_region_add_subregion(sys_mem, memmap[SIFIVE_E_XIP].base, xip_mem); - - /* Mask ROM reset vector */ - uint32_t reset_vec[2] = { - 0x204002b7, /* 0x1000: lui t0,0x20400 */ - 0x00028067, /* 0x1004: jr t0 */ - }; - - /* copy in the reset vector */ - copy_le32_to_phys(memmap[SIFIVE_E_MROM].base, reset_vec, sizeof(reset_vec)); - memory_region_set_readonly(mask_rom, true); - - if (machine->kernel_filename) { - load_kernel(machine->kernel_filename); - } } -static int riscv_sifive_e_sysbus_device_init(SysBusDevice *sysbusdev) -{ - return 0; -} - -static void riscv_sifive_e_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = riscv_sifive_e_sysbus_device_init; -} - -static const TypeInfo riscv_sifive_e_device = { - .name = TYPE_SIFIVE_E, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFiveEState), - .class_init = riscv_sifive_e_class_init, -}; - static void riscv_sifive_e_machine_init(MachineClass *mc) { mc->desc = "RISC-V Board compatible with SiFive E SDK"; @@ -226,9 +220,26 @@ static void riscv_sifive_e_machine_init(MachineClass *mc) DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init) -static void riscv_sifive_e_register_types(void) +static void riscv_sifive_e_soc_class_init(ObjectClass *oc, void *data) { - type_register_static(&riscv_sifive_e_device); + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = riscv_sifive_e_soc_realize; + /* Reason: Uses serial_hds in realize function, thus can't be used twice */ + dc->user_creatable = false; } -type_init(riscv_sifive_e_register_types); +static const TypeInfo riscv_sifive_e_soc_type_info = { + .name = TYPE_RISCV_E_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SiFiveESoCState), + .instance_init = riscv_sifive_e_soc_init, + .class_init = riscv_sifive_e_soc_class_init, +}; + +static void riscv_sifive_e_soc_register_types(void) +{ + type_register_static(&riscv_sifive_e_soc_type_info); +} + +type_init(riscv_sifive_e_soc_register_types) diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index 874de2ebaf..a91aeb97ab 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -435,7 +435,6 @@ static void sifive_plic_irq_request(void *opaque, int irq, int level) static void sifive_plic_realize(DeviceState *dev, Error **errp) { SiFivePLICState *plic = SIFIVE_PLIC(dev); - int i; memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, TYPE_SIFIVE_PLIC, plic->aperture_size); @@ -448,10 +447,7 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) plic->claimed = g_new0(uint32_t, plic->bitfield_words); plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); - plic->irqs = g_new0(qemu_irq, plic->num_sources + 1); - for (i = 0; i <= plic->num_sources; i++) { - plic->irqs[i] = qemu_allocate_irq(sifive_plic_irq_request, plic, i); - } + qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); } static void sifive_plic_class_init(ObjectClass *klass, void *data) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 1c2deefa6c..59ae1ce24a 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -47,39 +47,31 @@ #include "exec/address-spaces.h" #include "elf.h" +#include + static const struct MemmapEntry { hwaddr base; hwaddr size; } sifive_u_memmap[] = { [SIFIVE_U_DEBUG] = { 0x0, 0x100 }, - [SIFIVE_U_MROM] = { 0x1000, 0x2000 }, + [SIFIVE_U_MROM] = { 0x1000, 0x11000 }, [SIFIVE_U_CLINT] = { 0x2000000, 0x10000 }, [SIFIVE_U_PLIC] = { 0xc000000, 0x4000000 }, [SIFIVE_U_UART0] = { 0x10013000, 0x1000 }, [SIFIVE_U_UART1] = { 0x10023000, 0x1000 }, [SIFIVE_U_DRAM] = { 0x80000000, 0x0 }, + [SIFIVE_U_GEM] = { 0x100900FC, 0x2000 }, }; -static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) -{ - int i; - for (i = 0; i < (len >> 2); i++) { - stl_phys(&address_space_memory, pa + (i << 2), rom[i]); - } -} - -static uint64_t identity_translate(void *opaque, uint64_t addr) -{ - return addr; -} +#define GEM_REVISION 0x10070109 static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf(kernel_filename, identity_translate, NULL, + if (load_elf(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, - 0, ELF_MACHINE, 1, 0) < 0) { + 0, EM_RISCV, 1, 0) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); exit(1); } @@ -108,7 +100,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_add_subnode(fdt, "/soc"); qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); - qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc"); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); @@ -122,16 +114,18 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(nodename); qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", + SIFIVE_CLINT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); - for (cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) { + for (cpu = s->soc.cpus.num_harts - 1; cpu >= 0; cpu--) { nodename = g_strdup_printf("/cpus/cpu@%d", cpu); char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); - char *isa = riscv_isa_string(&s->soc.harts[cpu]); + char *isa = riscv_isa_string(&s->soc.cpus.harts[cpu]); qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + SIFIVE_U_CLOCK_FREQ); qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); @@ -149,8 +143,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(nodename); } - cells = g_new0(uint32_t, s->soc.num_harts * 4); - for (cpu = 0; cpu < s->soc.num_harts; cpu++) { + cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4); + for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) { nodename = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); @@ -168,12 +162,12 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, 0x0, memmap[SIFIVE_U_CLINT].base, 0x0, memmap[SIFIVE_U_CLINT].size); qemu_fdt_setprop(fdt, nodename, "interrupts-extended", - cells, s->soc.num_harts * sizeof(uint32_t) * 4); + cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4); g_free(cells); g_free(nodename); - cells = g_new0(uint32_t, s->soc.num_harts * 4); - for (cpu = 0; cpu < s->soc.num_harts; cpu++) { + cells = g_new0(uint32_t, s->soc.cpus.num_harts * 4); + for (cpu = 0; cpu < s->soc.cpus.num_harts; cpu++) { nodename = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename); @@ -190,20 +184,41 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0"); qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); qemu_fdt_setprop(fdt, nodename, "interrupts-extended", - cells, s->soc.num_harts * sizeof(uint32_t) * 4); + cells, s->soc.cpus.num_harts * sizeof(uint32_t) * 4); qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0, memmap[SIFIVE_U_PLIC].base, 0x0, memmap[SIFIVE_U_PLIC].size); qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); qemu_fdt_setprop_cell(fdt, nodename, "riscv,max-priority", 7); - qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 4); + qemu_fdt_setprop_cell(fdt, nodename, "riscv,ndev", 0x35); qemu_fdt_setprop_cells(fdt, nodename, "phandle", 2); qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", 2); plic_phandle = qemu_fdt_get_phandle(fdt, nodename); g_free(cells); g_free(nodename); - nodename = g_strdup_printf("/uart@%lx", + nodename = g_strdup_printf("/soc/ethernet@%lx", + (long)memmap[SIFIVE_U_GEM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "cdns,macb"); + qemu_fdt_setprop_cells(fdt, nodename, "reg", + 0x0, memmap[SIFIVE_U_GEM].base, + 0x0, memmap[SIFIVE_U_GEM].size); + qemu_fdt_setprop_string(fdt, nodename, "reg-names", "control"); + qemu_fdt_setprop_string(fdt, nodename, "phy-mode", "gmii"); + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_GEM_IRQ); + qemu_fdt_setprop_cells(fdt, nodename, "#address-cells", 1); + qemu_fdt_setprop_cells(fdt, nodename, "#size-cells", 0); + g_free(nodename); + + nodename = g_strdup_printf("/soc/ethernet@%lx/ethernet-phy@0", + (long)memmap[SIFIVE_U_GEM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0); + g_free(nodename); + + nodename = g_strdup_printf("/soc/uart@%lx", (long)memmap[SIFIVE_U_UART0].base); qemu_fdt_add_subnode(fdt, nodename); qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0"); @@ -224,36 +239,26 @@ static void riscv_sifive_u_init(MachineState *machine) const struct MemmapEntry *memmap = sifive_u_memmap; SiFiveUState *s = g_new0(SiFiveUState, 1); - MemoryRegion *sys_memory = get_system_memory(); + MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + int i; - /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); - object_property_set_str(OBJECT(&s->soc), SIFIVE_U_CPU, "cpu-type", - &error_abort); - object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", - &error_abort); + /* Initialize SoC */ + object_initialize_child(OBJECT(machine), "soc", &s->soc, + sizeof(s->soc), TYPE_RISCV_U_SOC, + &error_abort, NULL); object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_abort); /* register RAM */ memory_region_init_ram(main_mem, NULL, "riscv.sifive.u.ram", machine->ram_size, &error_fatal); - memory_region_add_subregion(sys_memory, memmap[SIFIVE_U_DRAM].base, - main_mem); + memory_region_add_subregion(system_memory, memmap[SIFIVE_U_DRAM].base, + main_mem); /* create device tree */ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); - /* boot rom */ - memory_region_init_ram(boot_rom, NULL, "riscv.sifive.u.mrom", - memmap[SIFIVE_U_MROM].base, &error_fatal); - memory_region_set_readonly(boot_rom, true); - memory_region_add_subregion(sys_memory, 0x0, boot_rom); - if (machine->kernel_filename) { load_kernel(machine->kernel_filename); } @@ -275,13 +280,59 @@ static void riscv_sifive_u_init(MachineState *machine) /* dtb: */ }; - /* copy in the reset vector */ - copy_le32_to_phys(memmap[SIFIVE_U_MROM].base, reset_vec, sizeof(reset_vec)); + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[SIFIVE_U_MROM].base, &address_space_memory); /* copy in the device tree */ - qemu_fdt_dumpdtb(s->fdt, s->fdt_size); - cpu_physical_memory_write(memmap[SIFIVE_U_MROM].base + - sizeof(reset_vec), s->fdt, s->fdt_size); + if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) > + memmap[SIFIVE_U_MROM].size - sizeof(reset_vec)) { + error_report("not enough space to store device-tree"); + exit(1); + } + qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt)); + rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt), + memmap[SIFIVE_U_MROM].base + sizeof(reset_vec), + &address_space_memory); +} + +static void riscv_sifive_u_soc_init(Object *obj) +{ + SiFiveUSoCState *s = RISCV_U_SOC(obj); + + object_initialize_child(obj, "cpus", &s->cpus, sizeof(s->cpus), + TYPE_RISCV_HART_ARRAY, &error_abort, NULL); + object_property_set_str(OBJECT(&s->cpus), SIFIVE_U_CPU, "cpu-type", + &error_abort); + object_property_set_int(OBJECT(&s->cpus), smp_cpus, "num-harts", + &error_abort); + + sysbus_init_child_obj(obj, "gem", &s->gem, sizeof(s->gem), + TYPE_CADENCE_GEM); +} + +static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) +{ + SiFiveUSoCState *s = RISCV_U_SOC(dev); + const struct MemmapEntry *memmap = sifive_u_memmap; + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + qemu_irq plic_gpios[SIFIVE_U_PLIC_NUM_SOURCES]; + int i; + Error *err = NULL; + NICInfo *nd = &nd_table[0]; + + object_property_set_bool(OBJECT(&s->cpus), true, "realized", + &error_abort); + + /* boot rom */ + memory_region_init_rom(mask_rom, NULL, "riscv.sifive.u.mrom", + memmap[SIFIVE_U_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[SIFIVE_U_MROM].base, + mask_rom); /* MMIO */ s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base, @@ -295,40 +346,35 @@ static void riscv_sifive_u_init(MachineState *machine) SIFIVE_U_PLIC_CONTEXT_BASE, SIFIVE_U_PLIC_CONTEXT_STRIDE, memmap[SIFIVE_U_PLIC].size); - sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART0].base, - serial_hds[0], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART0_IRQ]); - /* sifive_uart_create(sys_memory, memmap[SIFIVE_U_UART1].base, - serial_hds[1], SIFIVE_PLIC(s->plic)->irqs[SIFIVE_U_UART1_IRQ]); */ + sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, + serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ)); + /* sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, + serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), + SIFIVE_U_UART1_IRQ)); */ sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + + for (i = 0; i < SIFIVE_U_PLIC_NUM_SOURCES; i++) { + plic_gpios[i] = qdev_get_gpio_in(DEVICE(s->plic), i); + } + + if (nd->used) { + qemu_check_nic_model(nd, TYPE_CADENCE_GEM); + qdev_set_nic_properties(DEVICE(&s->gem), nd); + } + object_property_set_int(OBJECT(&s->gem), GEM_REVISION, "revision", + &error_abort); + object_property_set_bool(OBJECT(&s->gem), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gem), 0, memmap[SIFIVE_U_GEM].base); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gem), 0, + plic_gpios[SIFIVE_U_GEM_IRQ]); } -static int riscv_sifive_u_sysbus_device_init(SysBusDevice *sysbusdev) -{ - return 0; -} - -static void riscv_sifive_u_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = riscv_sifive_u_sysbus_device_init; -} - -static const TypeInfo riscv_sifive_u_device = { - .name = TYPE_SIFIVE_U, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SiFiveUState), - .class_init = riscv_sifive_u_class_init, -}; - -static void riscv_sifive_u_register_types(void) -{ - type_register_static(&riscv_sifive_u_device); -} - -type_init(riscv_sifive_u_register_types); - static void riscv_sifive_u_machine_init(MachineClass *mc) { mc->desc = "RISC-V Board compatible with SiFive U SDK"; @@ -337,3 +383,27 @@ static void riscv_sifive_u_machine_init(MachineClass *mc) } DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init) + +static void riscv_sifive_u_soc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = riscv_sifive_u_soc_realize; + /* Reason: Uses serial_hds in realize function, thus can't be used twice */ + dc->user_creatable = false; +} + +static const TypeInfo riscv_sifive_u_soc_type_info = { + .name = TYPE_RISCV_U_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SiFiveUSoCState), + .instance_init = riscv_sifive_u_soc_init, + .class_init = riscv_sifive_u_soc_class_init, +}; + +static void riscv_sifive_u_soc_register_types(void) +{ + type_register_static(&riscv_sifive_u_soc_type_info); +} + +type_init(riscv_sifive_u_soc_register_types) diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 2d1f114d40..c8c056c50b 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -42,34 +42,23 @@ #include "exec/address-spaces.h" #include "elf.h" +#include + static const struct MemmapEntry { hwaddr base; hwaddr size; } spike_memmap[] = { - [SPIKE_MROM] = { 0x1000, 0x2000 }, + [SPIKE_MROM] = { 0x1000, 0x11000 }, [SPIKE_CLINT] = { 0x2000000, 0x10000 }, [SPIKE_DRAM] = { 0x80000000, 0x0 }, }; -static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) -{ - int i; - for (i = 0; i < (len >> 2); i++) { - stl_phys(&address_space_memory, pa + (i << 2), rom[i]); - } -} - -static uint64_t identity_translate(void *opaque, uint64_t addr) -{ - return addr; -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf_ram_sym(kernel_filename, identity_translate, NULL, - &kernel_entry, NULL, &kernel_high, 0, ELF_MACHINE, 1, 0, + if (load_elf_ram_sym(kernel_filename, NULL, NULL, + &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0, NULL, true, htif_symbol_callback) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); exit(1); @@ -115,7 +104,8 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, g_free(nodename); qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", + SIFIVE_CLINT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); @@ -124,7 +114,8 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); char *isa = riscv_isa_string(&s->soc.harts[cpu]); qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + SPIKE_CLOCK_FREQ); qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); @@ -176,12 +167,12 @@ static void spike_v1_10_0_board_init(MachineState *machine) SpikeState *s = g_new0(SpikeState, 1); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + int i; /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); + object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), + TYPE_RISCV_HART_ARRAY, &error_abort, NULL); object_property_set_str(OBJECT(&s->soc), SPIKE_V1_10_0_CPU, "cpu-type", &error_abort); object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", @@ -199,9 +190,10 @@ static void spike_v1_10_0_board_init(MachineState *machine) create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); /* boot rom */ - memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom", - s->fdt_size + 0x2000, &error_fatal); - memory_region_add_subregion(system_memory, 0x0, boot_rom); + memory_region_init_rom(mask_rom, NULL, "riscv.spike.mrom", + memmap[SPIKE_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base, + mask_rom); if (machine->kernel_filename) { load_kernel(machine->kernel_filename); @@ -224,16 +216,26 @@ static void spike_v1_10_0_board_init(MachineState *machine) /* dtb: */ }; - /* copy in the reset vector */ - copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec)); + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[SPIKE_MROM].base, &address_space_memory); /* copy in the device tree */ - qemu_fdt_dumpdtb(s->fdt, s->fdt_size); - cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec), - s->fdt, s->fdt_size); + if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) > + memmap[SPIKE_MROM].size - sizeof(reset_vec)) { + error_report("not enough space to store device-tree"); + exit(1); + } + qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt)); + rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt), + memmap[SPIKE_MROM].base + sizeof(reset_vec), + &address_space_memory); /* initialize HTIF using symbols found in load_kernel */ - htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]); + htif_mm_init(system_memory, mask_rom, &s->soc.harts[0].env, serial_hd(0)); /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, @@ -247,12 +249,12 @@ static void spike_v1_09_1_board_init(MachineState *machine) SpikeState *s = g_new0(SpikeState, 1); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); + int i; /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); + object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), + TYPE_RISCV_HART_ARRAY, &error_abort, NULL); object_property_set_str(OBJECT(&s->soc), SPIKE_V1_09_1_CPU, "cpu-type", &error_abort); object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", @@ -267,9 +269,10 @@ static void spike_v1_09_1_board_init(MachineState *machine) main_mem); /* boot rom */ - memory_region_init_ram(boot_rom, NULL, "riscv.spike.bootrom", - 0x40000, &error_fatal); - memory_region_add_subregion(system_memory, 0x0, boot_rom); + memory_region_init_rom(mask_rom, NULL, "riscv.spike.mrom", + memmap[SPIKE_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base, + mask_rom); if (machine->kernel_filename) { load_kernel(machine->kernel_filename); @@ -322,33 +325,26 @@ static void spike_v1_09_1_board_init(MachineState *machine) g_free(isa); size_t config_string_len = strlen(config_string); - /* copy in the reset vector */ - copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec)); + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[SPIKE_MROM].base, &address_space_memory); /* copy in the config string */ - cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec), - config_string, config_string_len); + rom_add_blob_fixed_as("mrom.reset", config_string, config_string_len, + memmap[SPIKE_MROM].base + sizeof(reset_vec), + &address_space_memory); /* initialize HTIF using symbols found in load_kernel */ - htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[0]); + htif_mm_init(system_memory, mask_rom, &s->soc.harts[0].env, serial_hd(0)); /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); } -static const TypeInfo spike_v_1_09_1_device = { - .name = TYPE_RISCV_SPIKE_V1_09_1_BOARD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SpikeState), -}; - -static const TypeInfo spike_v_1_10_0_device = { - .name = TYPE_RISCV_SPIKE_V1_10_0_BOARD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SpikeState), -}; - static void spike_v1_09_1_machine_init(MachineClass *mc) { mc->desc = "RISC-V Spike Board (Privileged ISA v1.9.1)"; @@ -366,11 +362,3 @@ static void spike_v1_10_0_machine_init(MachineClass *mc) DEFINE_MACHINE("spike_v1.9.1", spike_v1_09_1_machine_init) DEFINE_MACHINE("spike_v1.10", spike_v1_10_0_machine_init) - -static void riscv_spike_board_register_types(void) -{ - type_register_static(&spike_v_1_09_1_device); - type_register_static(&spike_v_1_10_0_device); -} - -type_init(riscv_spike_board_register_types); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index e2c214e86a..248bbdffd3 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "qapi/error.h" @@ -40,13 +41,15 @@ #include "exec/address-spaces.h" #include "elf.h" +#include + static const struct MemmapEntry { hwaddr base; hwaddr size; } virt_memmap[] = { [VIRT_DEBUG] = { 0x0, 0x100 }, - [VIRT_MROM] = { 0x1000, 0x2000 }, - [VIRT_TEST] = { 0x4000, 0x1000 }, + [VIRT_MROM] = { 0x1000, 0x11000 }, + [VIRT_TEST] = { 0x100000, 0x1000 }, [VIRT_CLINT] = { 0x2000000, 0x10000 }, [VIRT_PLIC] = { 0xc000000, 0x4000000 }, [VIRT_UART0] = { 0x10000000, 0x100 }, @@ -54,26 +57,13 @@ static const struct MemmapEntry { [VIRT_DRAM] = { 0x80000000, 0x0 }, }; -static void copy_le32_to_phys(hwaddr pa, uint32_t *rom, size_t len) -{ - int i; - for (i = 0; i < (len >> 2); i++) { - stl_phys(&address_space_memory, pa + (i << 2), rom[i]); - } -} - -static uint64_t identity_translate(void *opaque, uint64_t addr) -{ - return addr; -} - static uint64_t load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf(kernel_filename, identity_translate, NULL, + if (load_elf(kernel_filename, NULL, NULL, &kernel_entry, NULL, &kernel_high, - 0, ELF_MACHINE, 1, 0) < 0) { + 0, EM_RISCV, 1, 0) < 0) { error_report("qemu: could not load kernel '%s'", kernel_filename); exit(1); } @@ -95,7 +85,7 @@ static hwaddr load_initrd(const char *filename, uint64_t mem_size, * halfway into RAM, and for boards with 256MB of RAM or more we put * the initrd at 128MB. */ - *start = kernel_entry + MIN(mem_size / 2, 128 * 1024 * 1024); + *start = kernel_entry + MIN(mem_size / 2, 128 * MiB); size = load_ramdisk(filename, *start, mem_size - *start); if (size == -1) { @@ -145,7 +135,8 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, g_free(nodename); qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000); + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", + SIFIVE_CLINT_TIMEBASE_FREQ); qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0); qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1); @@ -155,7 +146,8 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu); char *isa = riscv_isa_string(&s->soc.harts[cpu]); qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + VIRT_CLOCK_FREQ); qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48"); qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa); qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv"); @@ -275,16 +267,15 @@ static void riscv_virt_board_init(MachineState *machine) RISCVVirtState *s = g_new0(RISCVVirtState, 1); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *main_mem = g_new(MemoryRegion, 1); - MemoryRegion *boot_rom = g_new(MemoryRegion, 1); + MemoryRegion *mask_rom = g_new(MemoryRegion, 1); char *plic_hart_config; size_t plic_hart_config_len; int i; void *fdt; /* Initialize SOC */ - object_initialize(&s->soc, sizeof(s->soc), TYPE_RISCV_HART_ARRAY); - object_property_add_child(OBJECT(machine), "soc", OBJECT(&s->soc), - &error_abort); + object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), + TYPE_RISCV_HART_ARRAY, &error_abort, NULL); object_property_set_str(OBJECT(&s->soc), VIRT_CPU, "cpu-type", &error_abort); object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts", @@ -302,9 +293,10 @@ static void riscv_virt_board_init(MachineState *machine) fdt = create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline); /* boot rom */ - memory_region_init_ram(boot_rom, NULL, "riscv_virt_board.bootrom", - s->fdt_size + 0x2000, &error_fatal); - memory_region_add_subregion(system_memory, 0x0, boot_rom); + memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom", + memmap[VIRT_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, + mask_rom); if (machine->kernel_filename) { uint64_t kernel_entry = load_kernel(machine->kernel_filename); @@ -338,13 +330,23 @@ static void riscv_virt_board_init(MachineState *machine) /* dtb: */ }; - /* copy in the reset vector */ - copy_le32_to_phys(ROM_BASE, reset_vec, sizeof(reset_vec)); + /* copy in the reset vector in little_endian byte order */ + for (i = 0; i < sizeof(reset_vec) >> 2; i++) { + reset_vec[i] = cpu_to_le32(reset_vec[i]); + } + rom_add_blob_fixed_as("mrom.reset", reset_vec, sizeof(reset_vec), + memmap[VIRT_MROM].base, &address_space_memory); /* copy in the device tree */ - qemu_fdt_dumpdtb(s->fdt, s->fdt_size); - cpu_physical_memory_write(ROM_BASE + sizeof(reset_vec), - s->fdt, s->fdt_size); + if (fdt_pack(s->fdt) || fdt_totalsize(s->fdt) > + memmap[VIRT_MROM].size - sizeof(reset_vec)) { + error_report("not enough space to store device-tree"); + exit(1); + } + qemu_fdt_dumpdtb(s->fdt, fdt_totalsize(s->fdt)); + rom_add_blob_fixed_as("mrom.fdt", s->fdt, fdt_totalsize(s->fdt), + memmap[VIRT_MROM].base + sizeof(reset_vec), + &address_space_memory); /* create PLIC hart topology configuration string */ plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus; @@ -377,44 +379,19 @@ static void riscv_virt_board_init(MachineState *machine) for (i = 0; i < VIRTIO_COUNT; i++) { sysbus_create_simple("virtio-mmio", memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, - SIFIVE_PLIC(s->plic)->irqs[VIRTIO_IRQ + i]); + qdev_get_gpio_in(DEVICE(s->plic), VIRTIO_IRQ + i)); } serial_mm_init(system_memory, memmap[VIRT_UART0].base, - 0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193, - serial_hds[0], DEVICE_LITTLE_ENDIAN); + 0, qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ), 399193, + serial_hd(0), DEVICE_LITTLE_ENDIAN); } -static int riscv_virt_board_sysbus_device_init(SysBusDevice *sysbusdev) -{ - return 0; -} - -static void riscv_virt_board_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = riscv_virt_board_sysbus_device_init; -} - -static const TypeInfo riscv_virt_board_device = { - .name = TYPE_RISCV_VIRT_BOARD, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(RISCVVirtState), - .class_init = riscv_virt_board_class_init, -}; - static void riscv_virt_board_machine_init(MachineClass *mc) { - mc->desc = "RISC-V VirtIO Board (Privileged spec v1.10)"; + mc->desc = "RISC-V VirtIO Board (Privileged ISA v1.10)"; mc->init = riscv_virt_board_init; mc->max_cpus = 8; /* hardcoded limit in BBL */ } DEFINE_MACHINE("virt", riscv_virt_board_machine_init) - -static void riscv_virt_board_register_types(void) -{ - type_register_static(&riscv_virt_board_device); -} - -type_init(riscv_virt_board_register_types); diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index dc704b57d6..93282f7c59 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -14,6 +14,9 @@ obj-$(CONFIG_PCI) += s390-pci-bus.o s390-pci-inst.o obj-$(call lnot,$(CONFIG_PCI)) += s390-pci-stub.o obj-y += s390-skeys.o obj-y += s390-stattrib.o +obj-y += tod.o +obj-$(CONFIG_KVM) += tod-kvm.o +obj-$(CONFIG_TCG) += tod-qemu.o obj-$(CONFIG_KVM) += s390-skeys-kvm.o obj-$(CONFIG_KVM) += s390-stattrib-kvm.o obj-y += s390-ccw.o diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index f9bfa154d6..7cd73df4aa 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -40,6 +40,13 @@ static Property ccw_device_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void ccw_device_reset(DeviceState *d) +{ + CcwDevice *ccw_dev = CCW_DEVICE(d); + + css_reset_sch(ccw_dev->sch); +} + static void ccw_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -48,6 +55,7 @@ static void ccw_device_class_init(ObjectClass *klass, void *data) k->realize = ccw_device_realize; k->refill_ids = ccw_device_refill_ids; dc->props = ccw_device_properties; + dc->reset = ccw_device_reset; } const VMStateDescription vmstate_ccw_dev = { diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 301bf1772f..5424ea4562 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -616,6 +616,14 @@ void css_inject_io_interrupt(SubchDev *sch) void css_conditional_io_interrupt(SubchDev *sch) { + /* + * If the subchannel is not enabled, it is not made status pending + * (see PoP p. 16-17, "Status Control"). + */ + if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA)) { + return; + } + /* * If the subchannel is not currently status pending, make it pending * with alert status. @@ -1191,18 +1199,6 @@ static IOInstEnding sch_handle_start_func_passthrough(SubchDev *sch) assert(orb != NULL); p->intparm = orb->intparm; } - - /* - * Only support prefetch enable mode. - * Only support 64bit addressing idal. - */ - if (!(orb->ctrl0 & ORB_CTRL0_MASK_PFCH) || - !(orb->ctrl0 & ORB_CTRL0_MASK_C64)) { - warn_report("vfio-ccw requires PFCH and C64 flags set"); - sch_gen_unit_exception(sch); - css_inject_io_interrupt(sch); - return IOINST_CC_EXPECTED; - } return s390_ccw_cmd_request(sch); } diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 9c24bc6f7c..ee5b83448b 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -26,11 +26,23 @@ typedef struct SCLPEventsBus { BusState qbus; } SCLPEventsBus; +/* we need to save 32 bit chunks for compatibility */ +#ifdef HOST_WORDS_BIGENDIAN +#define RECV_MASK_LOWER 1 +#define RECV_MASK_UPPER 0 +#else /* little endian host */ +#define RECV_MASK_LOWER 0 +#define RECV_MASK_UPPER 1 +#endif + struct SCLPEventFacility { SysBusDevice parent_obj; SCLPEventsBus sbus; /* guest's receive mask */ - sccb_mask_t receive_mask; + union { + uint32_t receive_mask_pieces[2]; + sccb_mask_t receive_mask; + }; /* * when false, we keep the same broken, backwards compatible behaviour as * before, allowing only masks of size exactly 4; when true, we implement @@ -262,7 +274,7 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb) case SCLP_SELECTIVE_READ: copy_mask((uint8_t *)&sclp_active_selection_mask, (uint8_t *)&red->mask, sizeof(sclp_active_selection_mask), ef->mask_length); - sclp_active_selection_mask = be32_to_cpu(sclp_active_selection_mask); + sclp_active_selection_mask = be64_to_cpu(sclp_active_selection_mask); if (!sclp_cp_receive_mask || (sclp_active_selection_mask & ~sclp_cp_receive_mask)) { sccb->h.response_code = @@ -294,21 +306,22 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb) } /* - * Note: We currently only support masks up to 4 byte length; - * the remainder is filled up with zeroes. Linux uses - * a 4 byte mask length. + * Note: We currently only support masks up to 8 byte length; + * the remainder is filled up with zeroes. Older Linux + * kernels use a 4 byte mask length, newer ones can use both + * 8 or 4 depending on what is available on the host. */ /* keep track of the guest's capability masks */ copy_mask((uint8_t *)&tmp_mask, WEM_CP_RECEIVE_MASK(we_mask, mask_length), sizeof(tmp_mask), mask_length); - ef->receive_mask = be32_to_cpu(tmp_mask); + ef->receive_mask = be64_to_cpu(tmp_mask); /* return the SCLP's capability masks to the guest */ - tmp_mask = cpu_to_be32(get_host_receive_mask(ef)); + tmp_mask = cpu_to_be64(get_host_receive_mask(ef)); copy_mask(WEM_RECEIVE_MASK(we_mask, mask_length), (uint8_t *)&tmp_mask, mask_length, sizeof(tmp_mask)); - tmp_mask = cpu_to_be32(get_host_send_mask(ef)); + tmp_mask = cpu_to_be64(get_host_send_mask(ef)); copy_mask(WEM_SEND_MASK(we_mask, mask_length), (uint8_t *)&tmp_mask, mask_length, sizeof(tmp_mask)); @@ -369,6 +382,13 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code) } } +static bool vmstate_event_facility_mask64_needed(void *opaque) +{ + SCLPEventFacility *ef = opaque; + + return (ef->receive_mask & 0xFFFFFFFF) != 0; +} + static bool vmstate_event_facility_mask_length_needed(void *opaque) { SCLPEventFacility *ef = opaque; @@ -376,6 +396,17 @@ static bool vmstate_event_facility_mask_length_needed(void *opaque) return ef->allow_all_mask_sizes; } +static const VMStateDescription vmstate_event_facility_mask64 = { + .name = "vmstate-event-facility/mask64", + .version_id = 0, + .minimum_version_id = 0, + .needed = vmstate_event_facility_mask64_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(receive_mask_pieces[RECV_MASK_LOWER], SCLPEventFacility), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_event_facility_mask_length = { .name = "vmstate-event-facility/mask_length", .version_id = 0, @@ -392,10 +423,11 @@ static const VMStateDescription vmstate_event_facility = { .version_id = 0, .minimum_version_id = 0, .fields = (VMStateField[]) { - VMSTATE_UINT32(receive_mask, SCLPEventFacility), + VMSTATE_UINT32(receive_mask_pieces[RECV_MASK_UPPER], SCLPEventFacility), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * []) { + &vmstate_event_facility_mask64, &vmstate_event_facility_mask_length, NULL } @@ -511,3 +543,17 @@ static void register_types(void) } type_init(register_types) + +BusState *sclp_get_event_facility_bus(void) +{ + Object *busobj; + SCLPEventsBus *sbus; + + busobj = object_resolve_path_type("", TYPE_SCLP_EVENTS_BUS, NULL); + sbus = OBJECT_CHECK(SCLPEventsBus, busobj, TYPE_SCLP_EVENTS_BUS); + if (!sbus) { + return NULL; + } + + return &sbus->qbus; +} diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index fb554ab156..21f64ad26a 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -26,12 +26,13 @@ #include "qemu/config-file.h" #include "qemu/cutils.h" #include "qemu/option.h" +#include "exec/exec-all.h" #define KERN_IMAGE_START 0x010000UL +#define LINUX_MAGIC_ADDR 0x010008UL #define KERN_PARM_AREA 0x010480UL #define INITRD_START 0x800000UL #define INITRD_PARM_START 0x010408UL -#define INITRD_PARM_SIZE 0x010410UL #define PARMFILE_START 0x001000UL #define ZIPL_IMAGE_START 0x009000UL #define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) @@ -104,7 +105,9 @@ static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr) static void s390_ipl_realize(DeviceState *dev, Error **errp) { S390IPLState *ipl = S390_IPL(dev); - uint64_t pentry = KERN_IMAGE_START; + uint32_t *ipl_psw; + uint64_t pentry; + char *magic; int kernel_size; Error *err = NULL; @@ -156,10 +159,24 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) NULL, 1, EM_S390, 0, 0); if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); - } - if (kernel_size < 0) { - error_setg(&err, "could not load kernel '%s'", ipl->kernel); - goto error; + if (kernel_size < 0) { + error_setg(&err, "could not load kernel '%s'", ipl->kernel); + goto error; + } + /* if this is Linux use KERN_IMAGE_START */ + magic = rom_ptr(LINUX_MAGIC_ADDR, 6); + if (magic && !memcmp(magic, "S390EP", 6)) { + pentry = KERN_IMAGE_START; + } else { + /* if not Linux load the address of the (short) IPL PSW */ + ipl_psw = rom_ptr(4, 4); + if (ipl_psw) { + pentry = be32_to_cpu(*ipl_psw) & 0x7fffffffUL; + } else { + error_setg(&err, "Could not get IPL PSW"); + goto error; + } + } } /* * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the @@ -168,9 +185,12 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) * loader) and it won't work. For this case we force it to 0x10000, too. */ if (pentry == KERN_IMAGE_START || pentry == 0x800) { + char *parm_area = rom_ptr(KERN_PARM_AREA, strlen(ipl->cmdline) + 1); ipl->start_addr = KERN_IMAGE_START; /* Overwrite parameters in the kernel image, which are "rom" */ - strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline); + if (parm_area) { + strcpy(parm_area, ipl->cmdline); + } } else { ipl->start_addr = pentry; } @@ -178,6 +198,7 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) if (ipl->initrd) { ram_addr_t initrd_offset; int initrd_size; + uint64_t *romptr; initrd_offset = INITRD_START; while (kernel_size + 0x100000 > initrd_offset) { @@ -194,8 +215,11 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) * we have to overwrite values in the kernel image, * which are "rom" */ - stq_p(rom_ptr(INITRD_PARM_START), initrd_offset); - stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size); + romptr = rom_ptr(INITRD_PARM_START, 16); + if (romptr) { + stq_p(romptr, initrd_offset); + stq_p(romptr + 1, initrd_size); + } } } /* @@ -373,6 +397,10 @@ int s390_ipl_set_loadparm(uint8_t *loadparm) loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]]; } + if (i < 8) { + memset(loadparm + i, 0x40, 8 - i); /* fill with EBCDIC spaces */ + } + g_free(lp); return 0; } @@ -484,12 +512,20 @@ IplParameterBlock *s390_ipl_get_iplb(void) return &ipl->iplb; } -void s390_reipl_request(void) +void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type) { S390IPLState *ipl = get_ipl_device(); - ipl->reipl_requested = true; - if (ipl->iplb_valid && + if (reset_type == S390_RESET_EXTERNAL || reset_type == S390_RESET_REIPL) { + /* use CPU 0 for full resets */ + ipl->reset_cpu_index = 0; + } else { + ipl->reset_cpu_index = cs->cpu_index; + } + ipl->reset_type = reset_type; + + if (reset_type == S390_RESET_REIPL && + ipl->iplb_valid && !ipl->netboot && ipl->iplb.pbt == S390_IPL_TYPE_CCW && is_virtio_scsi_device(&ipl->iplb)) { @@ -505,7 +541,38 @@ void s390_reipl_request(void) ipl->iplb_valid = s390_gen_initial_iplb(ipl); } } - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + if (reset_type == S390_RESET_MODIFIED_CLEAR || + reset_type == S390_RESET_LOAD_NORMAL) { + /* ignore -no-reboot, send no event */ + qemu_system_reset_request(SHUTDOWN_CAUSE_SUBSYSTEM_RESET); + } else { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + /* as this is triggered by a CPU, make sure to exit the loop */ + if (tcg_enabled()) { + cpu_loop_exit(cs); + } +} + +void s390_ipl_get_reset_request(CPUState **cs, enum s390_reset *reset_type) +{ + S390IPLState *ipl = get_ipl_device(); + + *cs = qemu_get_cpu(ipl->reset_cpu_index); + if (!*cs) { + /* use any CPU */ + *cs = first_cpu; + } + *reset_type = ipl->reset_type; +} + +void s390_ipl_clear_reset_request(void) +{ + S390IPLState *ipl = get_ipl_device(); + + ipl->reset_type = S390_RESET_EXTERNAL; + /* use CPU 0 for full resets */ + ipl->reset_cpu_index = 0; } static void s390_ipl_prepare_qipl(S390CPU *cpu) @@ -552,11 +619,10 @@ static void s390_ipl_reset(DeviceState *dev) { S390IPLState *ipl = S390_IPL(dev); - if (!ipl->reipl_requested) { + if (ipl->reset_type != S390_RESET_REIPL) { ipl->iplb_valid = false; memset(&ipl->iplb, 0, sizeof(IplParameterBlock)); } - ipl->reipl_requested = false; } static void s390_ipl_class_init(ObjectClass *klass, void *data) diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 0570d0ad75..4e87b89418 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -87,7 +87,17 @@ int s390_ipl_set_loadparm(uint8_t *loadparm); void s390_ipl_update_diag308(IplParameterBlock *iplb); void s390_ipl_prepare_cpu(S390CPU *cpu); IplParameterBlock *s390_ipl_get_iplb(void); -void s390_reipl_request(void); + +enum s390_reset { + /* default is a reset not triggered by a CPU e.g. issued by QMP */ + S390_RESET_EXTERNAL = 0, + S390_RESET_REIPL, + S390_RESET_MODIFIED_CLEAR, + S390_RESET_LOAD_NORMAL, +}; +void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type); +void s390_ipl_get_reset_request(CPUState **cs, enum s390_reset *reset_type); +void s390_ipl_clear_reset_request(void); #define QIPL_ADDRESS 0xcc @@ -129,9 +139,11 @@ struct S390IPLState { bool enforce_bios; IplParameterBlock iplb; bool iplb_valid; - bool reipl_requested; bool netboot; QemuIplParameters qipl; + /* reset related properties don't have to be migrated or reset */ + enum s390_reset reset_type; + int reset_cpu_index; /*< public >*/ char *kernel; diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 10da87458e..e3e0ebb7f6 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -484,7 +484,7 @@ uint16_t s390_guest_io_table_walk(uint64_t g_iota, hwaddr addr, } static IOMMUTLBEntry s390_translate_iommu(IOMMUMemoryRegion *mr, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { S390PCIIOMMU *iommu = container_of(mr, S390PCIIOMMU, iommu_mr); S390IOTLBEntry *entry; diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 3fcc330fe3..7b61367ee3 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -155,8 +155,6 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) S390pciState *s = s390_get_phb(); int i; - cpu_synchronize_state(CPU(cpu)); - if (env->psw.mask & PSW_MASK_PSTATE) { s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); return 0; @@ -389,8 +387,6 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) uint32_t fh; uint8_t pcias; - cpu_synchronize_state(CPU(cpu)); - if (env->psw.mask & PSW_MASK_PSTATE) { s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); return 0; @@ -487,8 +483,6 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) uint32_t fh; uint8_t pcias; - cpu_synchronize_state(CPU(cpu)); - if (env->psw.mask & PSW_MASK_PSTATE) { s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); return 0; @@ -595,7 +589,7 @@ static void s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry) } notify.perm = IOMMU_NONE; - memory_region_notify_iommu(&iommu->iommu_mr, notify); + memory_region_notify_iommu(&iommu->iommu_mr, 0, notify); notify.perm = entry->perm; } @@ -607,7 +601,7 @@ static void s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry) g_hash_table_replace(iommu->iotlb, &cache->iova, cache); } - memory_region_notify_iommu(&iommu->iommu_mr, notify); + memory_region_notify_iommu(&iommu->iommu_mr, 0, notify); } int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) @@ -620,8 +614,6 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) S390IOTLBEntry entry; hwaddr start, end; - cpu_synchronize_state(CPU(cpu)); - if (env->psw.mask & PSW_MASK_PSTATE) { s390_program_interrupt(env, PGM_PRIVILEGED, 4, ra); return 0; @@ -770,7 +762,8 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, mr = s390_get_subregion(mr, offset, len); offset -= mr->addr; - if (!memory_region_access_valid(mr, offset, len, true)) { + if (!memory_region_access_valid(mr, offset, len, true, + MEMTXATTRS_UNSPECIFIED)) { s390_program_interrupt(env, PGM_OPERAND, 6, ra); return 0; } diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 76241c240e..15f7ab0e53 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -10,6 +10,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/boards.h" #include "hw/s390x/storage-keys.h" #include "qapi/error.h" @@ -19,7 +20,7 @@ #include "sysemu/kvm.h" #include "migration/register.h" -#define S390_SKEYS_BUFFER_SIZE 131072 /* Room for 128k storage keys */ +#define S390_SKEYS_BUFFER_SIZE (128 * KiB) /* Room for 128k storage keys */ #define S390_SKEYS_SAVE_FLAG_EOS 0x01 #define S390_SKEYS_SAVE_FLAG_SKEYS 0x02 #define S390_SKEYS_SAVE_FLAG_ERROR 0x04 diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index 480551c3db..c7e1f35524 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -105,7 +105,8 @@ static void kvm_s390_stattrib_synchronize(S390StAttribState *sa) KVMS390StAttribState *sas = KVM_S390_STATTRIB(sa); MachineState *machine = MACHINE(qdev_get_machine()); unsigned long max = machine->maxram_size / TARGET_PAGE_SIZE; - unsigned long cx, len = 1 << 19; + /* We do not need to reach the maximum buffer size allowed */ + unsigned long cx, len = KVM_S390_SKEYS_MAX / 2; int r; struct kvm_s390_cmma_log clog = { .flags = 0, diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 70b95550a8..766f2015a4 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -10,6 +10,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/boards.h" #include "cpu.h" #include "migration/qemu-file.h" @@ -20,7 +21,8 @@ #include "qapi/error.h" #include "qapi/qmp/qdict.h" -#define CMMA_BLOCK_SIZE (1 << 10) +/* 512KiB cover 2GB of guest memory */ +#define CMMA_BLOCK_SIZE (512 * KiB) #define STATTR_FLAG_EOS 0x01ULL #define STATTR_FLAG_MORE 0x02ULL @@ -202,7 +204,7 @@ static int cmma_save(QEMUFile *f, void *opaque, int final) S390StAttribClass *sac = S390_STATTRIB_GET_CLASS(sas); uint8_t *buf; int r, cx, reallen = 0, ret = 0; - uint32_t buflen = 1 << 19; /* 512kB cover 2GB of guest memory */ + uint32_t buflen = CMMA_BLOCK_SIZE; uint64_t start_gfn = sas->migration_cur_gfn; buf = g_try_malloc(buflen); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 435f7c99e7..7983185d04 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -35,6 +35,7 @@ #include "migration/register.h" #include "cpu_models.h" #include "hw/nmi.h" +#include "hw/s390x/tod.h" S390CPU *s390_cpu_addr2state(uint16_t cpu_addr) { @@ -93,7 +94,7 @@ static const char *const reset_dev_types[] = { "diag288", }; -void subsystem_reset(void) +static void subsystem_reset(void) { DeviceState *dev; int i; @@ -187,58 +188,6 @@ static void s390_memory_init(ram_addr_t mem_size) s390_stattrib_init(); } -#define S390_TOD_CLOCK_VALUE_MISSING 0x00 -#define S390_TOD_CLOCK_VALUE_PRESENT 0x01 - -static void gtod_save(QEMUFile *f, void *opaque) -{ - uint64_t tod_low; - uint8_t tod_high; - int r; - - r = s390_get_clock(&tod_high, &tod_low); - if (r) { - warn_report("Unable to get guest clock for migration: %s", - strerror(-r)); - error_printf("Guest clock will not be migrated " - "which could cause the guest to hang."); - qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); - return; - } - - qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); - qemu_put_byte(f, tod_high); - qemu_put_be64(f, tod_low); -} - -static int gtod_load(QEMUFile *f, void *opaque, int version_id) -{ - uint64_t tod_low; - uint8_t tod_high; - int r; - - if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { - warn_report("Guest clock was not migrated. This could " - "cause the guest to hang."); - return 0; - } - - tod_high = qemu_get_byte(f); - tod_low = qemu_get_be64(f); - - r = s390_set_clock(&tod_high, &tod_low); - if (r) { - error_report("Unable to set KVM guest TOD clock: %s", strerror(-r)); - } - - return r; -} - -static SaveVMHandlers savevm_gtod = { - .save_state = gtod_save, - .load_state = gtod_load, -}; - static void s390_init_ipl_dev(const char *kernel_filename, const char *kernel_cmdline, const char *initrd_filename, const char *firmware, @@ -288,6 +237,15 @@ static void s390_create_virtio_net(BusState *bus, const char *name) } } +static void s390_create_sclpconsole(const char *type, Chardev *chardev) +{ + DeviceState *dev; + + dev = qdev_create(sclp_get_event_facility_bus(), type); + qdev_prop_set_chr(dev, "chardev", chardev); + qdev_init_nofail(dev); +} + static void ccw_init(MachineState *machine) { int ret; @@ -346,8 +304,16 @@ static void ccw_init(MachineState *machine) /* Create VirtIO network adapters */ s390_create_virtio_net(BUS(css_bus), "virtio-net-ccw"); - /* Register savevm handler for guest TOD clock */ - register_savevm_live(NULL, "todclock", 0, 1, &savevm_gtod, NULL); + /* init consoles */ + if (serial_hd(0)) { + s390_create_sclpconsole("sclpconsole", serial_hd(0)); + } + if (serial_hd(1)) { + s390_create_sclpconsole("sclplmconsole", serial_hd(1)); + } + + /* init the TOD clock */ + s390_init_tod(); } static void s390_cpu_plug(HotplugHandler *hotplug_dev, @@ -364,17 +330,54 @@ static void s390_cpu_plug(HotplugHandler *hotplug_dev, } } +static inline void s390_do_cpu_ipl(CPUState *cs, run_on_cpu_data arg) +{ + S390CPU *cpu = S390_CPU(cs); + + s390_ipl_prepare_cpu(cpu); + s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu); +} + static void s390_machine_reset(void) { - S390CPU *ipl_cpu = S390_CPU(qemu_get_cpu(0)); + enum s390_reset reset_type; + CPUState *cs, *t; + /* get the reset parameters, reset them once done */ + s390_ipl_get_reset_request(&cs, &reset_type); + + /* all CPUs are paused and synchronized at this point */ s390_cmma_reset(); - qemu_devices_reset(); - s390_crypto_reset(); - /* all cpus are stopped - configure and start the ipl cpu only */ - s390_ipl_prepare_cpu(ipl_cpu); - s390_cpu_set_state(S390_CPU_STATE_OPERATING, ipl_cpu); + switch (reset_type) { + case S390_RESET_EXTERNAL: + case S390_RESET_REIPL: + qemu_devices_reset(); + s390_crypto_reset(); + + /* configure and start the ipl CPU only */ + run_on_cpu(cs, s390_do_cpu_ipl, RUN_ON_CPU_NULL); + break; + case S390_RESET_MODIFIED_CLEAR: + CPU_FOREACH(t) { + run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); + } + subsystem_reset(); + s390_crypto_reset(); + run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL); + break; + case S390_RESET_LOAD_NORMAL: + CPU_FOREACH(t) { + run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL); + } + subsystem_reset(); + run_on_cpu(cs, s390_do_cpu_initial_reset, RUN_ON_CPU_NULL); + run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL); + break; + default: + g_assert_not_reached(); + } + s390_ipl_clear_reset_request(); } static void s390_machine_device_plug(HotplugHandler *hotplug_dev, @@ -470,12 +473,11 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->no_floppy = 1; - mc->no_serial = 1; mc->no_parallel = 1; mc->no_sdcard = 1; - mc->use_sclp = 1; mc->max_cpus = S390_MAX_CPUS; mc->has_hotpluggable_cpus = true; + assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = s390_get_hotplug_handler; mc->cpu_index_to_instance_props = s390_cpu_index_to_props; mc->possible_cpu_arch_ids = s390_possible_cpu_arch_ids; @@ -671,6 +673,9 @@ bool css_migration_enabled(void) } \ type_init(ccw_machine_register_##suffix) +#define CCW_COMPAT_2_12 \ + HW_COMPAT_2_12 + #define CCW_COMPAT_2_11 \ HW_COMPAT_2_11 \ {\ @@ -756,14 +761,28 @@ bool css_migration_enabled(void) .value = "0",\ }, +static void ccw_machine_3_0_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_3_0_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(3_0, "3.0", true); + static void ccw_machine_2_12_instance_options(MachineState *machine) { + ccw_machine_3_0_instance_options(machine); + s390_cpudef_featoff_greater(11, 1, S390_FEAT_PPA15); + s390_cpudef_featoff_greater(11, 1, S390_FEAT_BPB); } static void ccw_machine_2_12_class_options(MachineClass *mc) { + ccw_machine_3_0_class_options(mc); + SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_12); } -DEFINE_CCW_MACHINE(2_12, "2.12", true); +DEFINE_CCW_MACHINE(2_12, "2.12", false); static void ccw_machine_2_11_instance_options(MachineState *machine) { diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 047d577313..4510a800cb 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -13,6 +13,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" #include "sysemu/sysemu.h" @@ -289,7 +290,7 @@ static void sclp_realize(DeviceState *dev, Error **errp) ret = s390_set_memory_limit(machine->maxram_size, &hw_limit); if (ret == -E2BIG) { error_setg(&err, "host supports a maximum of %" PRIu64 " GB", - hw_limit >> 30); + hw_limit / GiB); } else if (ret) { error_setg(&err, "setting the guest size failed"); } @@ -319,6 +320,7 @@ static void sclp_memory_init(SCLPDevice *sclp) initial_mem = initial_mem >> increment_size << increment_size; machine->ram_size = initial_mem; + machine->maxram_size = initial_mem; /* let's propagate the changed ram size into the global variable. */ ram_size = initial_mem; } diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c new file mode 100644 index 0000000000..df564ab89c --- /dev/null +++ b/hw/s390x/tod-kvm.c @@ -0,0 +1,64 @@ +/* + * TOD (Time Of Day) clock - KVM implementation + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/s390x/tod.h" +#include "kvm_s390x.h" + +static void kvm_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) +{ + int r; + + r = kvm_s390_get_clock_ext(&tod->high, &tod->low); + if (r == -ENXIO) { + r = kvm_s390_get_clock(&tod->high, &tod->low); + } + if (r) { + error_setg(errp, "Unable to get KVM guest TOD clock: %s", + strerror(-r)); + } +} + +static void kvm_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) +{ + int r; + + r = kvm_s390_set_clock_ext(tod->high, tod->low); + if (r == -ENXIO) { + r = kvm_s390_set_clock(tod->high, tod->low); + } + if (r) { + error_setg(errp, "Unable to set KVM guest TOD clock: %s", + strerror(-r)); + } +} + +static void kvm_s390_tod_class_init(ObjectClass *oc, void *data) +{ + S390TODClass *tdc = S390_TOD_CLASS(oc); + + tdc->get = kvm_s390_tod_get; + tdc->set = kvm_s390_tod_set; +} + +static TypeInfo kvm_s390_tod_info = { + .name = TYPE_KVM_S390_TOD, + .parent = TYPE_S390_TOD, + .instance_size = sizeof(S390TODState), + .class_init = kvm_s390_tod_class_init, + .class_size = sizeof(S390TODClass), +}; + +static void register_types(void) +{ + type_register_static(&kvm_s390_tod_info); +} +type_init(register_types); diff --git a/hw/s390x/tod-qemu.c b/hw/s390x/tod-qemu.c new file mode 100644 index 0000000000..59c015c69d --- /dev/null +++ b/hw/s390x/tod-qemu.c @@ -0,0 +1,87 @@ +/* + * TOD (Time Of Day) clock - QEMU implementation + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/s390x/tod.h" +#include "qemu/timer.h" +#include "qemu/cutils.h" +#include "cpu.h" +#include "tcg_s390x.h" + +static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod, + Error **errp) +{ + *tod = td->base; + + tod->low += time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + if (tod->low < td->base.low) { + tod->high++; + } +} + +static void qemu_s390_tod_set(S390TODState *td, const S390TOD *tod, + Error **errp) +{ + CPUState *cpu; + + td->base = *tod; + + td->base.low -= time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + if (td->base.low > tod->low) { + td->base.high--; + } + + /* + * The TOD has been changed and we have to recalculate the CKC values + * for all CPUs. We do this asynchronously, as "SET CLOCK should be + * issued only while all other activity on all CPUs .. has been + * suspended". + */ + CPU_FOREACH(cpu) { + async_run_on_cpu(cpu, tcg_s390_tod_updated, RUN_ON_CPU_NULL); + } +} + +static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) +{ + S390TODClass *tdc = S390_TOD_CLASS(oc); + + tdc->get = qemu_s390_tod_get; + tdc->set = qemu_s390_tod_set; +} + +static void qemu_s390_tod_init(Object *obj) +{ + S390TODState *td = S390_TOD(obj); + struct tm tm; + + qemu_get_timedate(&tm, 0); + td->base.high = 0; + td->base.low = TOD_UNIX_EPOCH + (time2tod(mktimegm(&tm)) * 1000000000ULL); + if (td->base.low < TOD_UNIX_EPOCH) { + td->base.high += 1; + } +} + +static TypeInfo qemu_s390_tod_info = { + .name = TYPE_QEMU_S390_TOD, + .parent = TYPE_S390_TOD, + .instance_size = sizeof(S390TODState), + .instance_init = qemu_s390_tod_init, + .class_init = qemu_s390_tod_class_init, + .class_size = sizeof(S390TODClass), +}; + +static void register_types(void) +{ + type_register_static(&qemu_s390_tod_info); +} +type_init(register_types); diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c new file mode 100644 index 0000000000..1c63f411e6 --- /dev/null +++ b/hw/s390x/tod.c @@ -0,0 +1,130 @@ +/* + * TOD (Time Of Day) clock + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/s390x/tod.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "sysemu/kvm.h" +#include "migration/register.h" + +void s390_init_tod(void) +{ + Object *obj; + + if (kvm_enabled()) { + obj = object_new(TYPE_KVM_S390_TOD); + } else { + obj = object_new(TYPE_QEMU_S390_TOD); + } + object_property_add_child(qdev_get_machine(), TYPE_S390_TOD, obj, NULL); + object_unref(obj); + + qdev_init_nofail(DEVICE(obj)); +} + +S390TODState *s390_get_todstate(void) +{ + static S390TODState *ts; + + if (!ts) { + ts = S390_TOD(object_resolve_path_type("", TYPE_S390_TOD, NULL)); + } + + return ts; +} + +#define S390_TOD_CLOCK_VALUE_MISSING 0x00 +#define S390_TOD_CLOCK_VALUE_PRESENT 0x01 + +static void s390_tod_save(QEMUFile *f, void *opaque) +{ + S390TODState *td = opaque; + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + Error *err = NULL; + S390TOD tod; + + tdc->get(td, &tod, &err); + if (err) { + warn_report_err(err); + error_printf("Guest clock will not be migrated " + "which could cause the guest to hang."); + qemu_put_byte(f, S390_TOD_CLOCK_VALUE_MISSING); + return; + } + + qemu_put_byte(f, S390_TOD_CLOCK_VALUE_PRESENT); + qemu_put_byte(f, tod.high); + qemu_put_be64(f, tod.low); +} + +static int s390_tod_load(QEMUFile *f, void *opaque, int version_id) +{ + S390TODState *td = opaque; + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + Error *err = NULL; + S390TOD tod; + + if (qemu_get_byte(f) == S390_TOD_CLOCK_VALUE_MISSING) { + warn_report("Guest clock was not migrated. This could " + "cause the guest to hang."); + return 0; + } + + tod.high = qemu_get_byte(f); + tod.low = qemu_get_be64(f); + + tdc->set(td, &tod, &err); + if (err) { + error_report_err(err); + return -1; + } + return 0; +} + +static SaveVMHandlers savevm_tod = { + .save_state = s390_tod_save, + .load_state = s390_tod_load, +}; + +static void s390_tod_realize(DeviceState *dev, Error **errp) +{ + S390TODState *td = S390_TOD(dev); + + /* Legacy migration interface */ + register_savevm_live(NULL, "todclock", 0, 1, &savevm_tod, td); +} + +static void s390_tod_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->desc = "TOD (Time Of Day) Clock"; + dc->realize = s390_tod_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + /* We only have one TOD clock in the system attached to the machine */ + dc->user_creatable = false; +} + +static TypeInfo s390_tod_info = { + .name = TYPE_S390_TOD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(S390TODState), + .class_init = s390_tod_class_init, + .class_size = sizeof(S390TODClass), + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&s390_tod_info); +} +type_init(register_types); diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index e51fbefd23..7ddb378d52 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -13,8 +13,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "net/net.h" @@ -1003,10 +1001,15 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) SubchDev *sch = ccw_dev->sch; uint64_t indicators; - /* queue indicators + secondary indicators */ - if (vector >= VIRTIO_QUEUE_MAX + 64) { + if (vector == VIRTIO_NO_VECTOR) { return; } + /* + * vector < VIRTIO_QUEUE_MAX: notification for a virtqueue + * vector == VIRTIO_QUEUE_MAX: configuration change notification + * bits beyond that are unused and should never be notified for + */ + assert(vector <= VIRTIO_QUEUE_MAX); if (vector < VIRTIO_QUEUE_MAX) { if (!dev->indicators) { @@ -1029,6 +1032,7 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) css_adapter_interrupt(CSS_IO_ADAPTER_VIRTIO, dev->thinint_isc); } } else { + assert(vector < NR_CLASSIC_INDICATOR_BITS); indicators = address_space_ldq(&address_space_memory, dev->indicators->addr, MEMTXATTRS_UNSPECIFIED, @@ -1042,12 +1046,11 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) if (!dev->indicators2) { return; } - vector = 0; indicators = address_space_ldq(&address_space_memory, dev->indicators2->addr, MEMTXATTRS_UNSPECIFIED, NULL); - indicators |= 1ULL << vector; + indicators |= 1ULL; address_space_stq(&address_space_memory, dev->indicators2->addr, indicators, MEMTXATTRS_UNSPECIFIED, NULL); css_conditional_io_interrupt(sch); @@ -1058,10 +1061,12 @@ static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); - CcwDevice *ccw_dev = CCW_DEVICE(d); + VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); virtio_ccw_reset_virtio(dev, vdev); - css_reset_sch(ccw_dev->sch); + if (vdc->parent_reset) { + vdc->parent_reset(d); + } } static void virtio_ccw_vmstate_change(DeviceState *d, bool running) @@ -1345,7 +1350,6 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_net_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_net_properties; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); } @@ -1373,7 +1377,6 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_blk_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_blk_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -1401,7 +1404,6 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_serial_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_serial_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -1429,7 +1431,6 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_balloon_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_balloon_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } @@ -1457,7 +1458,6 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_scsi_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_scsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -1484,7 +1484,6 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) k->realize = vhost_ccw_scsi_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = vhost_ccw_scsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -1521,7 +1520,6 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_rng_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_rng_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } @@ -1559,7 +1557,6 @@ static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_crypto_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_crypto_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); } @@ -1597,7 +1594,6 @@ static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_gpu_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_gpu_properties; dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); @@ -1626,7 +1622,6 @@ static void virtio_ccw_input_class_init(ObjectClass *klass, void *data) k->realize = virtio_ccw_input_realize; k->unrealize = virtio_ccw_unrealize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_input_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } @@ -1725,11 +1720,13 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); + VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_CLASS(klass); k->unplug = virtio_ccw_busdev_unplug; dc->realize = virtio_ccw_busdev_realize; dc->unrealize = virtio_ccw_busdev_unrealize; dc->bus_type = TYPE_VIRTUAL_CSS_BUS; + device_class_set_parent_reset(dc, virtio_ccw_reset, &vdc->parent_reset); } static const TypeInfo virtio_ccw_device_info = { @@ -1806,7 +1803,6 @@ static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) k->unrealize = virtio_ccw_unrealize; k->realize = virtio_ccw_9p_realize; - dc->reset = virtio_ccw_reset; dc->props = virtio_ccw_9p_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } @@ -1840,11 +1836,9 @@ static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) { VHostVSockCCWState *dev = VHOST_VSOCK_CCW(ccw_dev); DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - error_propagate(errp, err); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); } static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) @@ -1856,7 +1850,6 @@ static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) k->unrealize = virtio_ccw_unrealize; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->props = vhost_vsock_ccw_properties; - dc->reset = virtio_ccw_reset; } static void vhost_vsock_ccw_instance_init(Object *obj) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 2fc513001e..3453aa1f98 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -77,6 +77,7 @@ typedef struct VirtIOCCWDeviceClass { CCWDeviceClass parent_class; void (*realize)(VirtioCcwDevice *dev, Error **errp); void (*unrealize)(VirtioCcwDevice *dev, Error **errp); + void (*parent_reset)(DeviceState *dev); } VirtIOCCWDeviceClass; /* Performance improves when virtqueue kick processing is decoupled from the diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs index b188f7242b..718b4c2a68 100644 --- a/hw/scsi/Makefile.objs +++ b/hw/scsi/Makefile.objs @@ -8,7 +8,7 @@ common-obj-$(CONFIG_ESP) += esp.o common-obj-$(CONFIG_ESP_PCI) += esp-pci.o obj-$(CONFIG_PSERIES) += spapr_vscsi.o -ifeq ($(CONFIG_VIRTIO),y) +ifeq ($(CONFIG_VIRTIO_SCSI),y) obj-y += virtio-scsi.o virtio-scsi-dataplane.o obj-$(CONFIG_VHOST_SCSI) += vhost-scsi-common.o vhost-scsi.o obj-$(CONFIG_VHOST_USER_SCSI) += vhost-scsi-common.o vhost-user-scsi.o diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 64ec285826..630d923623 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -564,7 +564,8 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) } static bool esp_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return (size == 1) || (is_write && size == 4); } @@ -618,36 +619,6 @@ static const MemoryRegionOps sysbus_esp_mem_ops = { .valid.accepts = esp_mem_accepts, }; -ESPState *esp_init(hwaddr espaddr, int it_shift, - ESPDMAMemoryReadWriteFunc dma_memory_read, - ESPDMAMemoryReadWriteFunc dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset, - qemu_irq *dma_enable) -{ - DeviceState *dev; - SysBusDevice *s; - SysBusESPState *sysbus; - ESPState *esp; - - dev = qdev_create(NULL, TYPE_ESP); - sysbus = ESP_STATE(dev); - esp = &sysbus->esp; - esp->dma_memory_read = dma_memory_read; - esp->dma_memory_write = dma_memory_write; - esp->dma_opaque = dma_opaque; - sysbus->it_shift = it_shift; - /* XXX for now until rc4030 has been changed to use DMA enable signal */ - esp->dma_enabled = 1; - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, irq); - sysbus_mmio_map(s, 0, espaddr); - *reset = qdev_get_gpio_in(dev, 0); - *dma_enable = qdev_get_gpio_in(dev, 1); - - return esp; -} - static const struct SCSIBusInfo esp_scsi_info = { .tcq = false, .max_target = ESP_MAX_DEVS, diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 3f061f3f68..4176e871e1 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -26,7 +26,6 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "sysemu/dma.h" -#include "sysemu/block-backend.h" #include "hw/pci/msi.h" #include "qemu/iov.h" #include "hw/scsi/scsi.h" diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 9646743a7d..5905f6bf29 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -226,6 +226,8 @@ static void scsi_qdev_unrealize(DeviceState *qdev, Error **errp) SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, int unit, bool removable, int bootindex, bool share_rw, + BlockdevOnError rerror, + BlockdevOnError werror, const char *serial, Error **errp) { const char *driver; @@ -262,6 +264,10 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, object_unparent(OBJECT(dev)); return NULL; } + + qdev_prop_set_enum(dev, "rerror", rerror); + qdev_prop_set_enum(dev, "werror", werror); + object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); @@ -285,7 +291,10 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus) } qemu_opts_loc_restore(dinfo->opts); scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo), - unit, false, -1, false, NULL, &error_fatal); + unit, false, -1, false, + BLOCKDEV_ON_ERROR_AUTO, + BLOCKDEV_ON_ERROR_AUTO, + NULL, &error_fatal); } loc_pop(&loc); } diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index ded23d36ca..5bb390773b 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -29,6 +29,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #endif #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/scsi/scsi.h" @@ -44,13 +45,13 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include #endif -#define SCSI_WRITE_SAME_MAX 524288 -#define SCSI_DMA_BUF_SIZE 131072 +#define SCSI_WRITE_SAME_MAX (512 * KiB) +#define SCSI_DMA_BUF_SIZE (128 * KiB) #define SCSI_MAX_INQUIRY_LEN 256 #define SCSI_MAX_MODE_LEN 256 -#define DEFAULT_DISCARD_GRANULARITY 4096 -#define DEFAULT_MAX_UNMAP_SIZE (1 << 30) /* 1 GB */ +#define DEFAULT_DISCARD_GRANULARITY (4 * KiB) +#define DEFAULT_MAX_UNMAP_SIZE (1 * GiB) #define DEFAULT_MAX_IO_SIZE INT_MAX /* 2 GB - 1 block */ #define TYPE_SCSI_DISK_BASE "scsi-disk-base" @@ -585,219 +586,229 @@ static uint8_t *scsi_get_buf(SCSIRequest *req) return (uint8_t *)r->iov.iov_base; } +int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); + uint8_t page_code = req->cmd.buf[2]; + int start, buflen = 0; + + outbuf[buflen++] = s->qdev.type & 0x1f; + outbuf[buflen++] = page_code; + outbuf[buflen++] = 0x00; + outbuf[buflen++] = 0x00; + start = buflen; + + switch (page_code) { + case 0x00: /* Supported page codes, mandatory */ + { + DPRINTF("Inquiry EVPD[Supported pages] " + "buffer size %zd\n", req->cmd.xfer); + outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ + if (s->serial) { + outbuf[buflen++] = 0x80; /* unit serial number */ + } + outbuf[buflen++] = 0x83; /* device identification */ + if (s->qdev.type == TYPE_DISK) { + outbuf[buflen++] = 0xb0; /* block limits */ + outbuf[buflen++] = 0xb1; /* block device characteristics */ + outbuf[buflen++] = 0xb2; /* thin provisioning */ + } + break; + } + case 0x80: /* Device serial number, optional */ + { + int l; + + if (!s->serial) { + DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); + return -1; + } + + l = strlen(s->serial); + if (l > 36) { + l = 36; + } + + DPRINTF("Inquiry EVPD[Serial number] " + "buffer size %zd\n", req->cmd.xfer); + memcpy(outbuf + buflen, s->serial, l); + buflen += l; + break; + } + + case 0x83: /* Device identification page, mandatory */ + { + const char *str = s->serial ?: blk_name(s->qdev.conf.blk); + int max_len = s->serial ? 20 : 255 - 8; + int id_len = strlen(str); + + if (id_len > max_len) { + id_len = max_len; + } + DPRINTF("Inquiry EVPD[Device identification] " + "buffer size %zd\n", req->cmd.xfer); + + outbuf[buflen++] = 0x2; /* ASCII */ + outbuf[buflen++] = 0; /* not officially assigned */ + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = id_len; /* length of data following */ + memcpy(outbuf + buflen, str, id_len); + buflen += id_len; + + if (s->qdev.wwn) { + outbuf[buflen++] = 0x1; /* Binary */ + outbuf[buflen++] = 0x3; /* NAA */ + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = 8; + stq_be_p(&outbuf[buflen], s->qdev.wwn); + buflen += 8; + } + + if (s->qdev.port_wwn) { + outbuf[buflen++] = 0x61; /* SAS / Binary */ + outbuf[buflen++] = 0x93; /* PIV / Target port / NAA */ + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = 8; + stq_be_p(&outbuf[buflen], s->qdev.port_wwn); + buflen += 8; + } + + if (s->port_index) { + outbuf[buflen++] = 0x61; /* SAS / Binary */ + + /* PIV/Target port/relative target port */ + outbuf[buflen++] = 0x94; + + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = 4; + stw_be_p(&outbuf[buflen + 2], s->port_index); + buflen += 4; + } + break; + } + case 0xb0: /* block limits */ + { + unsigned int unmap_sectors = + s->qdev.conf.discard_granularity / s->qdev.blocksize; + unsigned int min_io_size = + s->qdev.conf.min_io_size / s->qdev.blocksize; + unsigned int opt_io_size = + s->qdev.conf.opt_io_size / s->qdev.blocksize; + unsigned int max_unmap_sectors = + s->max_unmap_size / s->qdev.blocksize; + unsigned int max_io_sectors = + s->max_io_size / s->qdev.blocksize; + + if (s->qdev.type == TYPE_ROM) { + DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", + page_code); + return -1; + } + if (s->qdev.type == TYPE_DISK) { + int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); + int max_io_sectors_blk = + max_transfer_blk / s->qdev.blocksize; + + max_io_sectors = + MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); + + /* min_io_size and opt_io_size can't be greater than + * max_io_sectors */ + if (min_io_size) { + min_io_size = MIN(min_io_size, max_io_sectors); + } + if (opt_io_size) { + opt_io_size = MIN(opt_io_size, max_io_sectors); + } + } + /* required VPD size with unmap support */ + buflen = 0x40; + memset(outbuf + 4, 0, buflen - 4); + + outbuf[4] = 0x1; /* wsnz */ + + /* optimal transfer length granularity */ + outbuf[6] = (min_io_size >> 8) & 0xff; + outbuf[7] = min_io_size & 0xff; + + /* maximum transfer length */ + outbuf[8] = (max_io_sectors >> 24) & 0xff; + outbuf[9] = (max_io_sectors >> 16) & 0xff; + outbuf[10] = (max_io_sectors >> 8) & 0xff; + outbuf[11] = max_io_sectors & 0xff; + + /* optimal transfer length */ + outbuf[12] = (opt_io_size >> 24) & 0xff; + outbuf[13] = (opt_io_size >> 16) & 0xff; + outbuf[14] = (opt_io_size >> 8) & 0xff; + outbuf[15] = opt_io_size & 0xff; + + /* max unmap LBA count, default is 1GB */ + outbuf[20] = (max_unmap_sectors >> 24) & 0xff; + outbuf[21] = (max_unmap_sectors >> 16) & 0xff; + outbuf[22] = (max_unmap_sectors >> 8) & 0xff; + outbuf[23] = max_unmap_sectors & 0xff; + + /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */ + outbuf[24] = 0; + outbuf[25] = 0; + outbuf[26] = 0; + outbuf[27] = 255; + + /* optimal unmap granularity */ + outbuf[28] = (unmap_sectors >> 24) & 0xff; + outbuf[29] = (unmap_sectors >> 16) & 0xff; + outbuf[30] = (unmap_sectors >> 8) & 0xff; + outbuf[31] = unmap_sectors & 0xff; + + /* max write same size */ + outbuf[36] = 0; + outbuf[37] = 0; + outbuf[38] = 0; + outbuf[39] = 0; + + outbuf[40] = (max_io_sectors >> 24) & 0xff; + outbuf[41] = (max_io_sectors >> 16) & 0xff; + outbuf[42] = (max_io_sectors >> 8) & 0xff; + outbuf[43] = max_io_sectors & 0xff; + break; + } + case 0xb1: /* block device characteristics */ + { + buflen = 0x40; + outbuf[4] = (s->rotation_rate >> 8) & 0xff; + outbuf[5] = s->rotation_rate & 0xff; + outbuf[6] = 0; /* PRODUCT TYPE */ + outbuf[7] = 0; /* WABEREQ | WACEREQ | NOMINAL FORM FACTOR */ + outbuf[8] = 0; /* VBULS */ + break; + } + case 0xb2: /* thin provisioning */ + { + buflen = 8; + outbuf[4] = 0; + outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ + outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; + outbuf[7] = 0; + break; + } + default: + return -1; + } + /* done with EVPD */ + assert(buflen - start <= 255); + outbuf[start - 1] = buflen - start; + return buflen; +} + static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); int buflen = 0; - int start; if (req->cmd.buf[1] & 0x1) { /* Vital product data */ - uint8_t page_code = req->cmd.buf[2]; - - outbuf[buflen++] = s->qdev.type & 0x1f; - outbuf[buflen++] = page_code ; // this page - outbuf[buflen++] = 0x00; - outbuf[buflen++] = 0x00; - start = buflen; - - switch (page_code) { - case 0x00: /* Supported page codes, mandatory */ - { - DPRINTF("Inquiry EVPD[Supported pages] " - "buffer size %zd\n", req->cmd.xfer); - outbuf[buflen++] = 0x00; // list of supported pages (this page) - if (s->serial) { - outbuf[buflen++] = 0x80; // unit serial number - } - outbuf[buflen++] = 0x83; // device identification - if (s->qdev.type == TYPE_DISK) { - outbuf[buflen++] = 0xb0; // block limits - outbuf[buflen++] = 0xb1; /* block device characteristics */ - outbuf[buflen++] = 0xb2; // thin provisioning - } - break; - } - case 0x80: /* Device serial number, optional */ - { - int l; - - if (!s->serial) { - DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); - return -1; - } - - l = strlen(s->serial); - if (l > 36) { - l = 36; - } - - DPRINTF("Inquiry EVPD[Serial number] " - "buffer size %zd\n", req->cmd.xfer); - memcpy(outbuf+buflen, s->serial, l); - buflen += l; - break; - } - - case 0x83: /* Device identification page, mandatory */ - { - const char *str = s->serial ?: blk_name(s->qdev.conf.blk); - int max_len = s->serial ? 20 : 255 - 8; - int id_len = strlen(str); - - if (id_len > max_len) { - id_len = max_len; - } - DPRINTF("Inquiry EVPD[Device identification] " - "buffer size %zd\n", req->cmd.xfer); - - outbuf[buflen++] = 0x2; // ASCII - outbuf[buflen++] = 0; // not officially assigned - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = id_len; // length of data following - memcpy(outbuf+buflen, str, id_len); - buflen += id_len; - - if (s->qdev.wwn) { - outbuf[buflen++] = 0x1; // Binary - outbuf[buflen++] = 0x3; // NAA - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 8; - stq_be_p(&outbuf[buflen], s->qdev.wwn); - buflen += 8; - } - - if (s->qdev.port_wwn) { - outbuf[buflen++] = 0x61; // SAS / Binary - outbuf[buflen++] = 0x93; // PIV / Target port / NAA - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 8; - stq_be_p(&outbuf[buflen], s->qdev.port_wwn); - buflen += 8; - } - - if (s->port_index) { - outbuf[buflen++] = 0x61; // SAS / Binary - outbuf[buflen++] = 0x94; // PIV / Target port / relative target port - outbuf[buflen++] = 0; // reserved - outbuf[buflen++] = 4; - stw_be_p(&outbuf[buflen + 2], s->port_index); - buflen += 4; - } - break; - } - case 0xb0: /* block limits */ - { - unsigned int unmap_sectors = - s->qdev.conf.discard_granularity / s->qdev.blocksize; - unsigned int min_io_size = - s->qdev.conf.min_io_size / s->qdev.blocksize; - unsigned int opt_io_size = - s->qdev.conf.opt_io_size / s->qdev.blocksize; - unsigned int max_unmap_sectors = - s->max_unmap_size / s->qdev.blocksize; - unsigned int max_io_sectors = - s->max_io_size / s->qdev.blocksize; - - if (s->qdev.type == TYPE_ROM) { - DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", - page_code); - return -1; - } - if (s->qdev.type == TYPE_DISK) { - int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); - int max_io_sectors_blk = - max_transfer_blk / s->qdev.blocksize; - - max_io_sectors = - MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); - - /* min_io_size and opt_io_size can't be greater than - * max_io_sectors */ - if (min_io_size) { - min_io_size = MIN(min_io_size, max_io_sectors); - } - if (opt_io_size) { - opt_io_size = MIN(opt_io_size, max_io_sectors); - } - } - /* required VPD size with unmap support */ - buflen = 0x40; - memset(outbuf + 4, 0, buflen - 4); - - outbuf[4] = 0x1; /* wsnz */ - - /* optimal transfer length granularity */ - outbuf[6] = (min_io_size >> 8) & 0xff; - outbuf[7] = min_io_size & 0xff; - - /* maximum transfer length */ - outbuf[8] = (max_io_sectors >> 24) & 0xff; - outbuf[9] = (max_io_sectors >> 16) & 0xff; - outbuf[10] = (max_io_sectors >> 8) & 0xff; - outbuf[11] = max_io_sectors & 0xff; - - /* optimal transfer length */ - outbuf[12] = (opt_io_size >> 24) & 0xff; - outbuf[13] = (opt_io_size >> 16) & 0xff; - outbuf[14] = (opt_io_size >> 8) & 0xff; - outbuf[15] = opt_io_size & 0xff; - - /* max unmap LBA count, default is 1GB */ - outbuf[20] = (max_unmap_sectors >> 24) & 0xff; - outbuf[21] = (max_unmap_sectors >> 16) & 0xff; - outbuf[22] = (max_unmap_sectors >> 8) & 0xff; - outbuf[23] = max_unmap_sectors & 0xff; - - /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header. */ - outbuf[24] = 0; - outbuf[25] = 0; - outbuf[26] = 0; - outbuf[27] = 255; - - /* optimal unmap granularity */ - outbuf[28] = (unmap_sectors >> 24) & 0xff; - outbuf[29] = (unmap_sectors >> 16) & 0xff; - outbuf[30] = (unmap_sectors >> 8) & 0xff; - outbuf[31] = unmap_sectors & 0xff; - - /* max write same size */ - outbuf[36] = 0; - outbuf[37] = 0; - outbuf[38] = 0; - outbuf[39] = 0; - - outbuf[40] = (max_io_sectors >> 24) & 0xff; - outbuf[41] = (max_io_sectors >> 16) & 0xff; - outbuf[42] = (max_io_sectors >> 8) & 0xff; - outbuf[43] = max_io_sectors & 0xff; - break; - } - case 0xb1: /* block device characteristics */ - { - buflen = 8; - outbuf[4] = (s->rotation_rate >> 8) & 0xff; - outbuf[5] = s->rotation_rate & 0xff; - outbuf[6] = 0; - outbuf[7] = 0; - break; - } - case 0xb2: /* thin provisioning */ - { - buflen = 8; - outbuf[4] = 0; - outbuf[5] = 0xe0; /* unmap & write_same 10/16 all supported */ - outbuf[6] = s->qdev.conf.discard_granularity ? 2 : 1; - outbuf[7] = 0; - break; - } - default: - return -1; - } - /* done with EVPD */ - assert(buflen - start <= 255); - outbuf[start - 1] = buflen - start; - return buflen; + return scsi_disk_emulate_vpd_page(req, outbuf); } /* Standard INQUIRY data */ @@ -2570,8 +2581,6 @@ static int get_device_type(SCSIDiskState *s) { uint8_t cmd[16]; uint8_t buf[36]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; int ret; memset(cmd, 0, sizeof(cmd)); @@ -2579,19 +2588,9 @@ static int get_device_type(SCSIDiskState *s) cmd[0] = INQUIRY; cmd[4] = sizeof(buf); - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = blk_ioctl(s->qdev.conf.blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { + ret = scsi_SG_IO_FROM_DEV(s->qdev.conf.blk, cmd, sizeof(cmd), + buf, sizeof(buf)); + if (ret < 0) { return -1; } s->qdev.type = buf[0]; @@ -2649,7 +2648,7 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) s->features |= (1 << SCSI_DISK_F_NO_REMOVABLE_DEVOPS); scsi_realize(&s->qdev, errp); - scsi_generic_read_device_identification(&s->qdev); + scsi_generic_read_device_inquiry(&s->qdev); } typedef struct SCSIBlockReq { @@ -3040,6 +3039,10 @@ static Property scsi_block_properties[] = { DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.conf.blk), DEFINE_PROP_BOOL("share-rw", SCSIDiskState, qdev.conf.share_rw, false), DEFINE_PROP_UINT16("rotation_rate", SCSIDiskState, rotation_rate, 0), + DEFINE_PROP_UINT64("max_unmap_size", SCSIDiskState, max_unmap_size, + DEFAULT_MAX_UNMAP_SIZE), + DEFINE_PROP_UINT64("max_io_size", SCSIDiskState, max_io_size, + DEFAULT_MAX_IO_SIZE), DEFINE_PROP_INT32("scsi_version", SCSIDiskState, qdev.default_scsi_version, -1), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 381f04e339..d60c4d0fcf 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -17,7 +17,6 @@ #include "qemu/error-report.h" #include "hw/scsi/scsi.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #ifdef __linux__ @@ -143,10 +142,84 @@ static int execute_command(BlockBackend *blk, return 0; } +static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) +{ + uint8_t page, page_len; + + /* + * EVPD set to zero returns the standard INQUIRY data. + * + * Check if scsi_version is unset (-1) to avoid re-defining it + * each time an INQUIRY with standard data is received. + * scsi_version is initialized with -1 in scsi_generic_reset + * and scsi_disk_reset, making sure that we'll set the + * scsi_version after a reset. If the version field of the + * INQUIRY response somehow changes after a guest reboot, + * we'll be able to keep track of it. + * + * On SCSI-2 and older, first 3 bits of byte 2 is the + * ANSI-approved version, while on later versions the + * whole byte 2 contains the version. Check if we're dealing + * with a newer version and, in that case, assign the + * whole byte. + */ + if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) { + s->scsi_version = r->buf[2] & 0x07; + if (s->scsi_version > 2) { + s->scsi_version = r->buf[2]; + } + } + + if (s->type == TYPE_DISK && (r->req.cmd.buf[1] & 0x01)) { + page = r->req.cmd.buf[2]; + if (page == 0xb0) { + uint32_t max_transfer = + blk_get_max_transfer(s->conf.blk) / s->blocksize; + + assert(max_transfer); + stl_be_p(&r->buf[8], max_transfer); + /* Also take care of the opt xfer len. */ + stl_be_p(&r->buf[12], + MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); + } else if (page == 0x00 && s->needs_vpd_bl_emulation) { + /* + * Now we're capable of supplying the VPD Block Limits + * response if the hardware can't. Add it in the INQUIRY + * Supported VPD pages response in case we are using the + * emulation for this device. + * + * This way, the guest kernel will be aware of the support + * and will use it to proper setup the SCSI device. + */ + page_len = r->buf[3]; + r->buf[page_len + 4] = 0xb0; + r->buf[3] = ++page_len; + } + } +} + +static int scsi_emulate_block_limits(SCSIGenericReq *r) +{ + r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf); + r->io_header.sb_len_wr = 0; + + /* + * We have valid contents in the reply buffer but the + * io_header can report a sense error coming from + * the hardware in scsi_command_complete_noio. Clean + * up the io_header to avoid reporting it. + */ + r->io_header.driver_status = 0; + r->io_header.status = 0; + + return r->buflen; +} + static void scsi_read_complete(void * opaque, int ret) { SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIDevice *s = r->req.dev; + SCSISense sense; int len; assert(r->req.aiocb != NULL); @@ -163,6 +236,27 @@ static void scsi_read_complete(void * opaque, int ret) DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); r->len = -1; + + /* + * Check if this is a VPD Block Limits request that + * resulted in sense error but would need emulation. + * In this case, emulate a valid VPD response. + */ + if (s->needs_vpd_bl_emulation) { + int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY && + r->req.cmd.buf[1] & 0x01 && + r->req.cmd.buf[2] == 0xb0; + + if (is_vpd_bl && sg_io_sense_from_errno(-ret, &r->io_header, &sense)) { + len = scsi_emulate_block_limits(r); + /* + * No need to let scsi_read_complete go on and handle an + * INQUIRY VPD BL request we created manually. + */ + goto req_complete; + } + } + if (len == 0) { scsi_command_complete_noio(r, 0); goto done; @@ -195,40 +289,10 @@ static void scsi_read_complete(void * opaque, int ret) } } if (r->req.cmd.buf[0] == INQUIRY) { - /* - * EVPD set to zero returns the standard INQUIRY data. - * - * Check if scsi_version is unset (-1) to avoid re-defining it - * each time an INQUIRY with standard data is received. - * scsi_version is initialized with -1 in scsi_generic_reset - * and scsi_disk_reset, making sure that we'll set the - * scsi_version after a reset. If the version field of the - * INQUIRY response somehow changes after a guest reboot, - * we'll be able to keep track of it. - * - * On SCSI-2 and older, first 3 bits of byte 2 is the - * ANSI-approved version, while on later versions the - * whole byte 2 contains the version. Check if we're dealing - * with a newer version and, in that case, assign the - * whole byte. - */ - if (s->scsi_version == -1 && !(r->req.cmd.buf[1] & 0x01)) { - s->scsi_version = r->buf[2] & 0x07; - if (s->scsi_version > 2) { - s->scsi_version = r->buf[2]; - } - } - if (s->type == TYPE_DISK && r->req.cmd.buf[2] == 0xb0) { - uint32_t max_transfer = - blk_get_max_transfer(s->conf.blk) / s->blocksize; - - assert(max_transfer); - stl_be_p(&r->buf[8], max_transfer); - /* Also take care of the opt xfer len. */ - stl_be_p(&r->buf[12], - MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); - } + scsi_handle_inquiry_reply(r, s); } + +req_complete: scsi_req_data(&r->req, len); scsi_req_unref(&r->req); @@ -405,12 +469,77 @@ static int read_naa_id(const uint8_t *p, uint64_t *p_wwn) return -EINVAL; } -void scsi_generic_read_device_identification(SCSIDevice *s) +int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, + uint8_t *buf, uint8_t buf_size) +{ + sg_io_hdr_t io_header; + uint8_t sensebuf[8]; + int ret; + + memset(&io_header, 0, sizeof(io_header)); + io_header.interface_id = 'S'; + io_header.dxfer_direction = SG_DXFER_FROM_DEV; + io_header.dxfer_len = buf_size; + io_header.dxferp = buf; + io_header.cmdp = cmd; + io_header.cmd_len = cmd_size; + io_header.mx_sb_len = sizeof(sensebuf); + io_header.sbp = sensebuf; + io_header.timeout = 6000; /* XXX */ + + ret = blk_ioctl(blk, SG_IO, &io_header); + if (ret < 0 || io_header.driver_status || io_header.host_status) { + return -1; + } + return 0; +} + +/* + * Executes an INQUIRY request with EVPD set to retrieve the + * available VPD pages of the device. If the device does + * not support the Block Limits page (page 0xb0), set + * the needs_vpd_bl_emulation flag for future use. + */ +static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s) +{ + uint8_t cmd[6]; + uint8_t buf[250]; + uint8_t page_len; + int ret, i; + + memset(cmd, 0, sizeof(cmd)); + memset(buf, 0, sizeof(buf)); + cmd[0] = INQUIRY; + cmd[1] = 1; + cmd[2] = 0x00; + cmd[4] = sizeof(buf); + + ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd), + buf, sizeof(buf)); + if (ret < 0) { + /* + * Do not assume anything if we can't retrieve the + * INQUIRY response to assert the VPD Block Limits + * support. + */ + s->needs_vpd_bl_emulation = false; + return; + } + + page_len = buf[3]; + for (i = 4; i < page_len + 4; i++) { + if (buf[i] == 0xb0) { + s->needs_vpd_bl_emulation = false; + return; + } + } + s->needs_vpd_bl_emulation = true; +} + +static void scsi_generic_read_device_identification(SCSIDevice *s) { uint8_t cmd[6]; uint8_t buf[250]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; int ret; int i, len; @@ -421,19 +550,9 @@ void scsi_generic_read_device_identification(SCSIDevice *s) cmd[2] = 0x83; cmd[4] = sizeof(buf); - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = blk_ioctl(s->conf.blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { + ret = scsi_SG_IO_FROM_DEV(s->conf.blk, cmd, sizeof(cmd), + buf, sizeof(buf)); + if (ret < 0) { return; } @@ -462,12 +581,20 @@ void scsi_generic_read_device_identification(SCSIDevice *s) } } +void scsi_generic_read_device_inquiry(SCSIDevice *s) +{ + scsi_generic_read_device_identification(s); + if (s->type == TYPE_DISK) { + scsi_generic_set_vpd_bl_emulation(s); + } else { + s->needs_vpd_bl_emulation = false; + } +} + static int get_stream_blocksize(BlockBackend *blk) { uint8_t cmd[6]; uint8_t buf[12]; - uint8_t sensebuf[8]; - sg_io_hdr_t io_header; int ret; memset(cmd, 0, sizeof(cmd)); @@ -475,21 +602,11 @@ static int get_stream_blocksize(BlockBackend *blk) cmd[0] = MODE_SENSE; cmd[4] = sizeof(buf); - memset(&io_header, 0, sizeof(io_header)); - io_header.interface_id = 'S'; - io_header.dxfer_direction = SG_DXFER_FROM_DEV; - io_header.dxfer_len = sizeof(buf); - io_header.dxferp = buf; - io_header.cmdp = cmd; - io_header.cmd_len = sizeof(cmd); - io_header.mx_sb_len = sizeof(sensebuf); - io_header.sbp = sensebuf; - io_header.timeout = 6000; /* XXX */ - - ret = blk_ioctl(blk, SG_IO, &io_header); - if (ret < 0 || io_header.driver_status || io_header.host_status) { + ret = scsi_SG_IO_FROM_DEV(blk, cmd, sizeof(cmd), buf, sizeof(buf)); + if (ret < 0) { return -1; } + return (buf[9] << 16) | (buf[10] << 8) | buf[11]; } @@ -575,7 +692,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) /* Only used by scsi-block, but initialize it nevertheless to be clean. */ s->default_scsi_version = -1; - scsi_generic_read_device_identification(s); + scsi_generic_read_device_inquiry(s); } const SCSIReqOps scsi_generic_req_ops = { diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index 77e9897244..e2a5828af1 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c @@ -17,7 +17,6 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "migration/migration.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-scsi-common.h" #include "hw/virtio/virtio-scsi.h" diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 9389ed48e0..9355cfdf07 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -69,6 +69,7 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); VHostUserSCSI *s = VHOST_USER_SCSI(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + VhostUserState *user; Error *err = NULL; int ret; @@ -85,19 +86,30 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) return; } + user = vhost_user_init(); + if (!user) { + error_setg(errp, "vhost-user-scsi: failed to init vhost_user"); + return; + } + user->chr = &vs->conf.chardev; + vsc->dev.nvqs = 2 + vs->conf.num_queues; vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs); vsc->dev.vq_index = 0; vsc->dev.backend_features = 0; - ret = vhost_dev_init(&vsc->dev, (void *)&vs->conf.chardev, + ret = vhost_dev_init(&vsc->dev, user, VHOST_BACKEND_TYPE_USER, 0); if (ret < 0) { error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s", strerror(-ret)); + vhost_user_cleanup(user); + g_free(user); return; } + s->vhost_user = user; + /* Channel and lun both are 0 for bootable vhost-user-scsi disk */ vsc->channel = 0; vsc->lun = 0; @@ -117,6 +129,12 @@ static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp) g_free(vsc->dev.vqs); virtio_scsi_common_unrealize(dev, errp); + + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } } static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev, diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index 912e5005d8..b995bab3a2 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -142,8 +142,8 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev) /* Set up guest notifier (irq) */ rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); if (rc != 0) { - fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " - "ensure -enable-kvm is set\n", rc); + error_report("virtio-scsi: Failed to set guest notifiers (%d), " + "ensure -accel kvm is set.", rc); goto fail_guest_notifiers; } diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 3aa99717e2..5a3057d1f8 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -797,8 +797,16 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, virtio_scsi_acquire(s); blk_set_aio_context(sd->conf.blk, s->ctx); virtio_scsi_release(s); - } +} + +/* Announce the new device after it has been plugged */ +static void virtio_scsi_post_hotplug(HotplugHandler *hotplug_dev, + DeviceState *dev) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + SCSIDevice *sd = SCSI_DEVICE(dev); if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_acquire(s); @@ -968,6 +976,7 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) vdc->start_ioeventfd = virtio_scsi_dataplane_start; vdc->stop_ioeventfd = virtio_scsi_dataplane_stop; hc->plug = virtio_scsi_hotplug; + hc->post_plug = virtio_scsi_post_hotplug; hc->unplug = virtio_scsi_hotunplug; } diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index ebf3b926c2..1b760b2a7c 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -118,8 +118,6 @@ static void bcm2835_sdhost_send_command(BCM2835SDHostState *s) goto error; } if (!(s->cmd & SDCMD_NO_RESPONSE)) { -#define RWORD(n) (((uint32_t)rsp[n] << 24) | (rsp[n + 1] << 16) \ - | (rsp[n + 2] << 8) | rsp[n + 3]) if (rlen == 0 || (rlen == 4 && (s->cmd & SDCMD_LONG_RESPONSE))) { goto error; } @@ -127,15 +125,14 @@ static void bcm2835_sdhost_send_command(BCM2835SDHostState *s) goto error; } if (rlen == 4) { - s->rsp[0] = RWORD(0); + s->rsp[0] = ldl_be_p(&rsp[0]); s->rsp[1] = s->rsp[2] = s->rsp[3] = 0; } else { - s->rsp[0] = RWORD(12); - s->rsp[1] = RWORD(8); - s->rsp[2] = RWORD(4); - s->rsp[3] = RWORD(0); + s->rsp[0] = ldl_be_p(&rsp[12]); + s->rsp[1] = ldl_be_p(&rsp[8]); + s->rsp[2] = ldl_be_p(&rsp[4]); + s->rsp[3] = ldl_be_p(&rsp[0]); } -#undef RWORD } /* We never really delay commands, so if this was a 'busywait' command * then we've completed it now and can raise the interrupt. @@ -182,9 +179,11 @@ static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s) uint32_t value = 0; int n; int is_read; + int is_write; is_read = (s->cmd & SDCMD_READ_CMD) != 0; - if (s->datacnt != 0 && (!is_read || sdbus_data_ready(&s->sdbus))) { + is_write = (s->cmd & SDCMD_WRITE_CMD) != 0; + if (s->datacnt != 0 && (is_write || sdbus_data_ready(&s->sdbus))) { if (is_read) { n = 0; while (s->datacnt && s->fifo_len < BCM2835_SDHOST_FIFO_LEN) { @@ -204,8 +203,11 @@ static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s) if (n != 0) { bcm2835_sdhost_fifo_push(s, value); s->status |= SDHSTS_DATA_FLAG; + if (s->config & SDHCFG_DATA_IRPT_EN) { + s->status |= SDHSTS_SDIO_IRPT; + } } - } else { /* write */ + } else if (is_write) { /* write */ n = 0; while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { if (n == 0) { @@ -226,11 +228,18 @@ static void bcm2835_sdhost_fifo_run(BCM2835SDHostState *s) s->edm &= ~SDEDM_FSM_MASK; s->edm |= SDEDM_FSM_DATAMODE; trace_bcm2835_sdhost_edm_change("datacnt 0", s->edm); - - if ((s->cmd & SDCMD_WRITE_CMD) && + } + if (is_write) { + /* set block interrupt at end of each block transfer */ + if (s->hbct && s->datacnt % s->hbct == 0 && (s->config & SDHCFG_BLOCK_IRPT_EN)) { s->status |= SDHSTS_BLOCK_IRPT; } + /* set data interrupt after each transfer */ + s->status |= SDHSTS_DATA_FLAG; + if (s->config & SDHCFG_DATA_IRPT_EN) { + s->status |= SDHSTS_SDIO_IRPT; + } } } diff --git a/hw/sd/core.c b/hw/sd/core.c index 820345f704..107e6d71dd 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -91,7 +91,7 @@ int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) { SDState *card = get_card(sdbus); - trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg, req->crc); + trace_sdbus_command(sdbus_name(sdbus), req->cmd, req->arg); if (card) { SDCardClass *sc = SD_CARD_GET_CLASS(card); diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c index 5570c1e9a0..df42aa1c54 100644 --- a/hw/sd/milkymist-memcard.c +++ b/hw/sd/milkymist-memcard.c @@ -27,7 +27,7 @@ #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "trace.h" -#include "include/qapi/error.h" +#include "qapi/error.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/sd/sd.h" @@ -100,8 +100,7 @@ static void memcard_sd_command(MilkymistMemcardState *s) SDRequest req; req.cmd = s->command[0] & 0x3f; - req.arg = (s->command[1] << 24) | (s->command[2] << 16) - | (s->command[3] << 8) | s->command[4]; + req.arg = ldl_be_p(s->command + 1); req.crc = s->command[5]; s->response[0] = req.cmd; @@ -140,7 +139,7 @@ static uint64_t memcard_read(void *opaque, hwaddr addr, r = s->response[s->response_read_ptr++]; if (s->response_read_ptr > s->response_len) { qemu_log_mask(LOG_GUEST_ERROR, "milkymist_memcard: " - "read more cmd bytes than available. Clipping."); + "read more cmd bytes than available: clipping\n"); s->response_read_ptr = 0; } } diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 5b47cadf11..d0c98ca021 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -1,6 +1,8 @@ /* * OMAP on-chip MMC/SD host emulation. * + * Datasheet: TI Multimedia Card (MMC/SD/SDIO) Interface (SPRU765A) + * * Copyright (C) 2006-2007 Andrzej Zaborowski * * This program is free software; you can redistribute it and/or @@ -17,6 +19,7 @@ * with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/arm/omap.h" #include "hw/sd/sd.h" @@ -162,8 +165,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, CID_CSD_OVERWRITE; if (host->sdio & (1 << 13)) mask |= AKE_SEQ_ERROR; - rspstatus = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | (response[3] << 0); + rspstatus = ldl_be_p(response); break; case sd_r2: @@ -181,8 +183,7 @@ static void omap_mmc_command(struct omap_mmc_s *host, int cmd, int dir, } rsplen = 4; - rspstatus = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | (response[3] << 0); + rspstatus = ldl_be_p(response); if (rspstatus & 0x80000000) host->status &= 0xe000; else @@ -279,6 +280,12 @@ static void omap_mmc_update(void *opaque) omap_mmc_interrupts_update(s); } +static void omap_mmc_pseudo_reset(struct omap_mmc_s *host) +{ + host->status = 0; + host->fifo_len = 0; +} + void omap_mmc_reset(struct omap_mmc_s *host) { host->last_cmd = 0; @@ -287,11 +294,9 @@ void omap_mmc_reset(struct omap_mmc_s *host) host->dw = 0; host->mode = 0; host->enable = 0; - host->status = 0; host->mask = 0; host->cto = 0; host->dto = 0; - host->fifo_len = 0; host->blen = 0; host->blen_counter = 0; host->nblk = 0; @@ -306,6 +311,8 @@ void omap_mmc_reset(struct omap_mmc_s *host) qemu_set_irq(host->coverswitch, host->cdet_state); host->clkdiv = 0; + omap_mmc_pseudo_reset(host); + /* Since we're still using the legacy SD API the card is not plugged * into any bus, and we must reset it manually. When omap_mmc is * QOMified this must move into the QOM reset function. @@ -449,14 +456,18 @@ static void omap_mmc_write(void *opaque, hwaddr offset, s->enable = (value >> 11) & 1; s->be = (value >> 10) & 1; s->clkdiv = (value >> 0) & (s->rev >= 2 ? 0x3ff : 0xff); - if (s->mode != 0) - printf("SD mode %i unimplemented!\n", s->mode); - if (s->be != 0) - printf("SD FIFO byte sex unimplemented!\n"); + if (s->mode != 0) { + qemu_log_mask(LOG_UNIMP, + "omap_mmc_wr: mode #%i unimplemented\n", s->mode); + } + if (s->be != 0) { + qemu_log_mask(LOG_UNIMP, + "omap_mmc_wr: Big Endian not implemented\n"); + } if (s->dw != 0 && s->lines < 4) printf("4-bit SD bus enabled\n"); if (!s->enable) - omap_mmc_reset(s); + omap_mmc_pseudo_reset(s); break; case 0x10: /* MMC_STAT */ diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 3ba1f7dd23..3ad7e925c5 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/sysbus.h" #include "hw/sd/sd.h" @@ -183,23 +182,20 @@ static void pl181_send_command(PL181State *s) if (rlen < 0) goto error; if (s->cmd & PL181_CMD_RESPONSE) { -#define RWORD(n) (((uint32_t)response[n] << 24) | (response[n + 1] << 16) \ - | (response[n + 2] << 8) | response[n + 3]) if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) goto error; if (rlen != 4 && rlen != 16) goto error; - s->response[0] = RWORD(0); + s->response[0] = ldl_be_p(&response[0]); if (rlen == 4) { s->response[1] = s->response[2] = s->response[3] = 0; } else { - s->response[1] = RWORD(4); - s->response[2] = RWORD(8); - s->response[3] = RWORD(12) & ~1; + s->response[1] = ldl_be_p(&response[4]); + s->response[2] = ldl_be_p(&response[8]); + s->response[3] = ldl_be_p(&response[12]) & ~1; } DPRINTF("Response received\n"); s->status |= PL181_STATUS_CMDRESPEND; -#undef RWORD } else { DPRINTF("Command sent\n"); s->status |= PL181_STATUS_CMDSENT; diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 235e0518d6..d4356e9b73 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1,9 +1,10 @@ /* * SD Memory Card emulation as defined in the "SD Memory Card Physical - * layer specification, Version 1.10." + * layer specification, Version 2.00." * * Copyright (c) 2006 Andrzej Zaborowski * Copyright (c) 2007 CodeSourcery + * Copyright (c) 2018 Philippe Mathieu-Daudé * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,6 +31,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/qdev.h" #include "hw/hw.h" #include "hw/registerfields.h" @@ -37,7 +39,6 @@ #include "hw/sd/sd.h" #include "qapi/error.h" #include "qemu/bitmap.h" -#include "qemu/cutils.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" #include "qemu/timer.h" @@ -91,6 +92,7 @@ struct SDState { uint8_t sd_status[64]; /* Configurable properties */ + uint8_t spec_version; BlockBackend *blk; bool spi; @@ -303,18 +305,25 @@ static void sd_ocr_powerup(void *opaque) /* card power-up OK */ sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_POWER_UP, 1); - if (sd->size > 1 * G_BYTE) { + if (sd->size > 1 * GiB) { sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_CAPACITY, 1); } } static void sd_set_scr(SDState *sd) { - sd->scr[0] = (0 << 4) /* SCR version 1.0 */ - | 0; /* Spec Versions 1.0 and 1.01 */ + sd->scr[0] = 0 << 4; /* SCR structure version 1.0 */ + if (sd->spec_version == SD_PHY_SPECv1_10_VERS) { + sd->scr[0] |= 1; /* Spec Version 1.10 */ + } else { + sd->scr[0] |= 2; /* Spec Version 2.00 or Version 3.0X */ + } sd->scr[1] = (2 << 4) /* SDSC Card (Security Version 1.01) */ | 0b0101; /* 1-bit or 4-bit width bus modes */ sd->scr[2] = 0x00; /* Extended Security is not supported. */ + if (sd->spec_version >= SD_PHY_SPECv3_01_VERS) { + sd->scr[2] |= 1 << 7; /* Spec Version 3.0X */ + } sd->scr[3] = 0x00; /* reserved for manufacturer usage */ sd->scr[4] = 0x00; @@ -368,7 +377,7 @@ static void sd_set_csd(SDState *sd, uint64_t size) uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1; uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1; - if (size <= 1 * G_BYTE) { /* Standard Capacity SD */ + if (size <= 1 * GiB) { /* Standard Capacity SD */ sd->csd[0] = 0x00; /* CSD structure */ sd->csd[1] = 0x26; /* Data read access-time-1 */ sd->csd[2] = 0x00; /* Data read access-time-2 */ @@ -394,7 +403,7 @@ static void sd_set_csd(SDState *sd, uint64_t size) ((HWBLOCK_SHIFT << 6) & 0xc0); sd->csd[14] = 0x00; /* File format group */ } else { /* SDHC */ - size /= 512 * 1024; + size /= 512 * KiB; size -= 1; sd->csd[0] = 0x40; sd->csd[1] = 0x0e; @@ -787,7 +796,7 @@ static void sd_function_switch(SDState *sd, uint32_t arg) sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4); } memset(&sd->data[17], 0, 47); - stw_be_p(sd->data + 65, sd_crc16(sd->data, 64)); + stw_be_p(sd->data + 64, sd_crc16(sd->data, 64)); } static inline bool sd_wp_addr(SDState *sd, uint64_t addr) @@ -960,8 +969,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) return sd_illegal; case 6: /* CMD6: SWITCH_FUNCTION */ - if (sd->spi) - goto bad_cmd; switch (sd->mode) { case sd_data_transfer_mode: sd_function_switch(sd, req.arg); @@ -1014,7 +1021,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 8: /* CMD8: SEND_IF_COND */ - /* Physical Layer Specification Version 2.00 command */ + if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { + break; + } if (sd->state != sd_idle_state) { break; } @@ -1170,6 +1179,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 19: /* CMD19: SEND_TUNING_BLOCK (SD) */ + if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { + break; + } if (sd->state == sd_transfer_state) { sd->state = sd_sendingdata_state; sd->data_offset = 0; @@ -1178,6 +1190,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 23: /* CMD23: SET_BLOCK_COUNT */ + if (sd->spec_version < SD_PHY_SPECv3_01_VERS) { + break; + } switch (sd->state) { case sd_transfer_state: sd->multi_blk_cnt = req.arg; @@ -1190,9 +1205,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Block write commands (Class 4) */ case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } switch (sd->state) { case sd_transfer_state: /* Writing in SPI mode not implemented. */ @@ -1217,9 +1229,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } switch (sd->state) { case sd_transfer_state: /* Writing in SPI mode not implemented. */ @@ -1259,9 +1268,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) break; case 27: /* CMD27: PROGRAM_CSD */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -1371,9 +1377,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Lock card commands (Class 7) */ case 42: /* CMD42: LOCK_UNLOCK */ - if (sd->spi) { - goto unimplemented_spi_cmd; - } switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -2072,6 +2075,15 @@ static void sd_realize(DeviceState *dev, Error **errp) sd->proto_name = sd->spi ? "SPI" : "SD"; + switch (sd->spec_version) { + case SD_PHY_SPECv1_10_VERS + ... SD_PHY_SPECv3_01_VERS: + break; + default: + error_setg(errp, "Invalid SD card Spec version: %u", sd->spec_version); + return; + } + if (sd->blk && blk_is_read_only(sd->blk)) { error_setg(errp, "Cannot use read-only drive as SD card"); return; @@ -2088,6 +2100,8 @@ static void sd_realize(DeviceState *dev, Error **errp) } static Property sd_properties[] = { + DEFINE_PROP_UINT8("spec_version", SDState, + spec_version, SD_PHY_SPECv2_00_VERS), DEFINE_PROP_DRIVE("drive", SDState, blk), /* We do not model the chip select pin, so allow the board to select * whether card should be in SSI or MMC/SD mode. It is also up to the diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 63c44a4ee8..8f58c31265 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -23,18 +23,16 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "hw/hw.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "sysemu/dma.h" #include "qemu/timer.h" #include "qemu/bitops.h" #include "hw/sd/sdhci.h" #include "sdhci-internal.h" #include "qemu/log.h" -#include "qemu/cutils.h" #include "trace.h" #define TYPE_SDHCI_BUS "sdhci-bus" @@ -344,17 +342,13 @@ static void sdhci_send_command(SDHCIState *s) if (s->cmdreg & SDHC_CMD_RESPONSE) { if (rlen == 4) { - s->rspreg[0] = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | response[3]; + s->rspreg[0] = ldl_be_p(response); s->rspreg[1] = s->rspreg[2] = s->rspreg[3] = 0; trace_sdhci_response4(s->rspreg[0]); } else if (rlen == 16) { - s->rspreg[0] = (response[11] << 24) | (response[12] << 16) | - (response[13] << 8) | response[14]; - s->rspreg[1] = (response[7] << 24) | (response[8] << 16) | - (response[9] << 8) | response[10]; - s->rspreg[2] = (response[3] << 24) | (response[4] << 16) | - (response[5] << 8) | response[6]; + s->rspreg[0] = ldl_be_p(&response[11]); + s->rspreg[1] = ldl_be_p(&response[7]); + s->rspreg[2] = ldl_be_p(&response[3]); s->rspreg[3] = (response[0] << 16) | (response[1] << 8) | response[2]; trace_sdhci_response16(s->rspreg[3], s->rspreg[2], @@ -398,8 +392,7 @@ static void sdhci_end_transfer(SDHCIState *s) trace_sdhci_end_transfer(request.cmd, request.arg); sdbus_do_command(&s->sdbus, &request, response); /* Auto CMD12 response goes to the upper Response register */ - s->rspreg[3] = (response[0] << 24) | (response[1] << 16) | - (response[2] << 8) | response[3]; + s->rspreg[3] = ldl_be_p(response); } s->prnsts &= ~(SDHC_DOING_READ | SDHC_DOING_WRITE | @@ -416,7 +409,7 @@ static void sdhci_end_transfer(SDHCIState *s) /* * Programmed i/o data transfer */ -#define BLOCK_SIZE_MASK (4 * K_BYTE - 1) +#define BLOCK_SIZE_MASK (4 * KiB - 1) /* Fill host controller's read buffer with BLKSIZE bytes of data from card */ static void sdhci_read_block_from_card(SDHCIState *s) @@ -744,7 +737,7 @@ static void get_adma_description(SDHCIState *s, ADMADescr *dscr) if ((dscr->attr & SDHC_ADMA_ATTR_ACT_MASK) == SDHC_ADMA_ATTR_SET_LEN) { dscr->length = (uint16_t)extract32(adma1, 12, 16); } else { - dscr->length = 4096; + dscr->length = 4 * KiB; } break; case SDHC_CTRL_ADMA2_64: @@ -792,7 +785,7 @@ static void sdhci_do_adma(SDHCIState *s) return; } - length = dscr.length ? dscr.length : 65536; + length = dscr.length ? dscr.length : 64 * KiB; switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) { case SDHC_ADMA_ATTR_ACT_TRAN: /* data transfer */ diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index ae04b6641b..95a143bfba 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/ssi/ssi.h" #include "hw/sd/sd.h" @@ -97,8 +96,7 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val) uint8_t longresp[16]; /* FIXME: Check CRC. */ request.cmd = s->cmd; - request.arg = (s->cmdarg[0] << 24) | (s->cmdarg[1] << 16) - | (s->cmdarg[2] << 8) | s->cmdarg[3]; + request.arg = ldl_be_p(s->cmdarg); DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); if (s->arglen <= 0) { @@ -123,8 +121,7 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val) /* CMD13 returns a 2-byte statuse work. Other commands only return the first byte. */ s->arglen = (s->cmd == 13) ? 2 : 1; - cardstatus = (longresp[0] << 24) | (longresp[1] << 16) - | (longresp[2] << 8) | longresp[3]; + cardstatus = ldl_be_p(longresp); status = 0; if (((cardstatus >> 9) & 0xf) < 4) status |= SSI_SDR_IDLE; diff --git a/hw/sd/trace-events b/hw/sd/trace-events index bfd1d62efc..fb0615cd3c 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -7,7 +7,7 @@ bcm2835_sdhost_edm_change(const char *why, uint32_t edm) "(%s) EDM now 0x%x" bcm2835_sdhost_update_irq(uint32_t irq) "IRQ bits 0x%x\n" # hw/sd/core.c -sdbus_command(const char *bus_name, uint8_t cmd, uint32_t arg, uint8_t crc) "@%s CMD%02d arg 0x%08x crc 0x%02x" +sdbus_command(const char *bus_name, uint8_t cmd, uint32_t arg) "@%s CMD%02d arg 0x%08x" sdbus_read(const char *bus_name, uint8_t value) "@%s value 0x%02x" sdbus_write(const char *bus_name, uint8_t value) "@%s value 0x%02x" sdbus_set_voltage(const char *bus_name, uint16_t millivolts) "@%s %u (mV)" @@ -37,7 +37,7 @@ sdcard_powerup(void) "" sdcard_inquiry_cmd41(void) "" sdcard_set_enable(bool current_state, bool new_state) "%u -> %u" sdcard_reset(void) "" -sdcard_set_blocklen(uint16_t length) "0x%04x" +sdcard_set_blocklen(uint16_t length) "0x%03x" sdcard_inserted(bool readonly) "read_only: %u" sdcard_ejected(void) "" sdcard_erase(void) "" diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 458ed83297..6a5fc46a47 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -24,6 +24,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -40,7 +41,6 @@ #include "hw/loader.h" #include "hw/usb.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "exec/address-spaces.h" #define FLASH_BASE 0x00000000 @@ -271,7 +271,7 @@ static void r2d_init(MachineState *machine) busdev = SYS_BUS_DEVICE(dev); qdev_prop_set_uint32(dev, "vram-size", SM501_VRAM_SIZE); qdev_prop_set_uint32(dev, "base", 0x10000000); - qdev_prop_set_ptr(dev, "chr-state", serial_hds[2]); + qdev_prop_set_ptr(dev, "chr-state", serial_hd(2)); qdev_init_nofail(dev); sysbus_mmio_map(busdev, 0, 0x10000000); sysbus_mmio_map(busdev, 1, 0x13e00000); @@ -292,7 +292,7 @@ static void r2d_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); pflash_cfi02_register(0x0, NULL, "r2d.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (16 * 1024), FLASH_SIZE >> 16, + 16 * KiB, FLASH_SIZE >> 16, 1, 4, 0x0000, 0x0000, 0x0000, 0x0000, 0x555, 0x2aa, 0); diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 166e4bd947..2fb6e618d9 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -31,7 +31,6 @@ #include "hw/sh4/sh_intc.h" #include "cpu.h" #include "exec/exec-all.h" -#include "exec/address-spaces.h" #define NB_DEVICES 4 @@ -451,15 +450,43 @@ static void sh7750_mem_writel(void *opaque, hwaddr addr, } } +static uint64_t sh7750_mem_readfn(void *opaque, hwaddr addr, unsigned size) +{ + switch (size) { + case 1: + return sh7750_mem_readb(opaque, addr); + case 2: + return sh7750_mem_readw(opaque, addr); + case 4: + return sh7750_mem_readl(opaque, addr); + default: + g_assert_not_reached(); + } +} + +static void sh7750_mem_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + switch (size) { + case 1: + sh7750_mem_writeb(opaque, addr, value); + break; + case 2: + sh7750_mem_writew(opaque, addr, value); + break; + case 4: + sh7750_mem_writel(opaque, addr, value); + break; + default: + g_assert_not_reached(); + } +} + static const MemoryRegionOps sh7750_mem_ops = { - .old_mmio = { - .read = {sh7750_mem_readb, - sh7750_mem_readw, - sh7750_mem_readl }, - .write = {sh7750_mem_writeb, - sh7750_mem_writew, - sh7750_mem_writel }, - }, + .read = sh7750_mem_readfn, + .write = sh7750_mem_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -773,7 +800,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) cpu->env.intc_handle = &s->intc; sh_serial_init(sysmem, 0x1fe00000, - 0, s->periph_freq, serial_hds[0], + 0, s->periph_freq, serial_hd(0), s->intc.irqs[SCI1_ERI], s->intc.irqs[SCI1_RXI], s->intc.irqs[SCI1_TXI], @@ -781,7 +808,7 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) NULL); sh_serial_init(sysmem, 0x1fe80000, SH_SERIAL_FEAT_SCIF, - s->periph_freq, serial_hds[1], + s->periph_freq, serial_hd(1), s->intc.irqs[SCIF_ERI], s->intc.irqs[SCIF_RXI], s->intc.irqs[SCIF_TXI], diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index 27a07e96f4..a27e54b2fa 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -16,6 +16,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -625,10 +626,6 @@ static void smbios_build_type_11_table(void) SMBIOS_BUILD_TABLE_POST; } -#define ONE_KB ((ram_addr_t)1 << 10) -#define ONE_MB ((ram_addr_t)1 << 20) -#define ONE_GB ((ram_addr_t)1 << 30) - #define MAX_T16_STD_SZ 0x80000000 /* 2T in Kilobytes */ static void smbios_build_type_16_table(unsigned dimm_cnt) @@ -640,7 +637,7 @@ static void smbios_build_type_16_table(unsigned dimm_cnt) t->location = 0x01; /* Other */ t->use = 0x03; /* System memory */ t->error_correction = 0x06; /* Multi-bit ECC (for Microsoft, per SeaBIOS) */ - size_kb = QEMU_ALIGN_UP(ram_size, ONE_KB) / ONE_KB; + size_kb = QEMU_ALIGN_UP(ram_size, KiB) / KiB; if (size_kb < MAX_T16_STD_SZ) { t->maximum_capacity = cpu_to_le32(size_kb); t->extended_maximum_capacity = cpu_to_le64(0); @@ -668,7 +665,7 @@ static void smbios_build_type_17_table(unsigned instance, uint64_t size) t->memory_error_information_handle = cpu_to_le16(0xFFFE); /* Not provided */ t->total_width = cpu_to_le16(0xFFFF); /* Unknown */ t->data_width = cpu_to_le16(0xFFFF); /* Unknown */ - size_mb = QEMU_ALIGN_UP(size, ONE_MB) / ONE_MB; + size_mb = QEMU_ALIGN_UP(size, MiB) / MiB; if (size_mb < MAX_T17_STD_SZ) { t->size = cpu_to_le16(size_mb); t->extended_size = cpu_to_le32(0); @@ -707,8 +704,8 @@ static void smbios_build_type_19_table(unsigned instance, end = start + size - 1; assert(end > start); - start_kb = start / ONE_KB; - end_kb = end / ONE_KB; + start_kb = start / KiB; + end_kb = end / KiB; if (start_kb < UINT32_MAX && end_kb < UINT32_MAX) { t->starting_address = cpu_to_le32(start_kb); t->ending_address = cpu_to_le32(end_kb); @@ -869,7 +866,7 @@ void smbios_get_tables(const struct smbios_phys_mem_area *mem_array, smbios_build_type_11_table(); -#define MAX_DIMM_SZ (16ll * ONE_GB) +#define MAX_DIMM_SZ (16 * GiB) #define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \ : ((ram_size - 1) % MAX_DIMM_SZ) + 1) diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index bba3aa3dee..fa98ab8177 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu-common.h" @@ -139,9 +140,10 @@ static void leon3_generic_hw_init(MachineState *machine) env->qemu_irq_ack = leon3_irq_manager; /* Allocate RAM */ - if ((uint64_t)ram_size > (1UL << 30)) { - error_report("Too much memory for this machine: %d, maximum 1G", - (unsigned int)(ram_size / (1024 * 1024))); + if (ram_size > 1 * GiB) { + error_report("Too much memory for this machine: %" PRId64 "MB," + " maximum 1G", + ram_size / MiB); exit(1); } @@ -149,7 +151,7 @@ static void leon3_generic_hw_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0x40000000, ram); /* Allocate BIOS */ - prom_size = 8 * 1024 * 1024; /* 8Mb */ + prom_size = 8 * MiB; memory_region_init_ram(prom, NULL, "Leon3.bios", prom_size, &error_fatal); memory_region_set_readonly(prom, true); memory_region_add_subregion(address_space_mem, 0x00000000, prom); @@ -206,8 +208,8 @@ static void leon3_generic_hw_init(MachineState *machine) grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6); /* Allocate uart */ - if (serial_hds[0]) { - grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]); + if (serial_hd(0)) { + grlib_apbuart_create(0x80000100, serial_hd(0), cpu_irqs[3]); } } diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 6471aca25d..d981de1841 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -45,7 +46,6 @@ #include "hw/loader.h" #include "elf.h" #include "trace.h" -#include "qemu/cutils.h" /* * Sun4m architecture was used in the following machines: @@ -66,7 +66,7 @@ #define KERNEL_LOAD_ADDR 0x00004000 #define CMDLINE_ADDR 0x007ff000 #define INITRD_LOAD_ADDR 0x00800000 -#define PROM_SIZE_MAX (1024 * 1024) +#define PROM_SIZE_MAX (1 * MiB) #define PROM_VADDR 0xffd00000 #define PROM_FILENAME "openbios-sparc32" #define CFG_ADDR 0xd00000510ULL @@ -272,8 +272,8 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, } if (initrd_size > 0) { for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { - ptr = rom_ptr(KERNEL_LOAD_ADDR + i); - if (ldl_p(ptr) == 0x48647253) { // HdrS + ptr = rom_ptr(KERNEL_LOAD_ADDR + i, 24); + if (ptr && ldl_p(ptr) == 0x48647253) { /* HdrS */ stl_p(ptr + 16, INITRD_LOAD_ADDR); stl_p(ptr + 20, initrd_size); break; @@ -572,23 +572,36 @@ typedef struct IDRegState { MemoryRegion mem; } IDRegState; -static void idreg_init1(Object *obj) +static void idreg_realize(DeviceState *ds, Error **errp) { - IDRegState *s = MACIO_ID_REGISTER(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); + IDRegState *s = MACIO_ID_REGISTER(ds); + SysBusDevice *dev = SYS_BUS_DEVICE(ds); + Error *local_err = NULL; + + memory_region_init_ram_nomigrate(&s->mem, OBJECT(ds), "sun4m.idreg", + sizeof(idreg_data), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - memory_region_init_ram_nomigrate(&s->mem, obj, - "sun4m.idreg", sizeof(idreg_data), &error_fatal); vmstate_register_ram_global(&s->mem); memory_region_set_readonly(&s->mem, true); sysbus_init_mmio(dev, &s->mem); } +static void idreg_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = idreg_realize; +} + static const TypeInfo idreg_info = { .name = TYPE_MACIO_ID_REGISTER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(IDRegState), - .instance_init = idreg_init1, + .class_init = idreg_class_init, }; #define TYPE_TCX_AFX "tcx_afx" @@ -613,21 +626,35 @@ static void afx_init(hwaddr addr) sysbus_mmio_map(s, 0, addr); } -static void afx_init1(Object *obj) +static void afx_realize(DeviceState *ds, Error **errp) { - AFXState *s = TCX_AFX(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); + AFXState *s = TCX_AFX(ds); + SysBusDevice *dev = SYS_BUS_DEVICE(ds); + Error *local_err = NULL; + + memory_region_init_ram_nomigrate(&s->mem, OBJECT(ds), "sun4m.afx", 4, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - memory_region_init_ram_nomigrate(&s->mem, obj, "sun4m.afx", 4, &error_fatal); vmstate_register_ram_global(&s->mem); sysbus_init_mmio(dev, &s->mem); } +static void afx_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = afx_realize; +} + static const TypeInfo afx_info = { .name = TYPE_TCX_AFX, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(AFXState), - .instance_init = afx_init1, + .class_init = afx_class_init, }; #define TYPE_OPENPROM "openprom" @@ -680,13 +707,19 @@ static void prom_init(hwaddr addr, const char *bios_name) } } -static void prom_init1(Object *obj) +static void prom_realize(DeviceState *ds, Error **errp) { - PROMState *s = OPENPROM(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); + PROMState *s = OPENPROM(ds); + SysBusDevice *dev = SYS_BUS_DEVICE(ds); + Error *local_err = NULL; + + memory_region_init_ram_nomigrate(&s->prom, OBJECT(ds), "sun4m.prom", + PROM_SIZE_MAX, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - memory_region_init_ram_nomigrate(&s->prom, obj, "sun4m.prom", PROM_SIZE_MAX, - &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); @@ -701,6 +734,7 @@ static void prom_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->props = prom_properties; + dc->realize = prom_realize; } static const TypeInfo prom_info = { @@ -708,7 +742,6 @@ static const TypeInfo prom_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), .class_init = prom_class_init, - .instance_init = prom_init1, }; #define TYPE_SUN4M_MEMORY "memory" @@ -741,9 +774,9 @@ static void ram_init(hwaddr addr, ram_addr_t RAM_size, /* allocate RAM */ if ((uint64_t)RAM_size > max_mem) { - error_report("Too much memory for this machine: %d, maximum %d", - (unsigned int)(RAM_size / (1024 * 1024)), - (unsigned int)(max_mem / (1024 * 1024))); + error_report("Too much memory for this machine: %" PRId64 "," + " maximum %" PRId64, + RAM_size / MiB, max_mem / MiB); exit(1); } dev = qdev_create(NULL, "memory"); @@ -943,8 +976,8 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, qdev_prop_set_uint32(dev, "disabled", 0); qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); qdev_prop_set_uint32(dev, "it_shift", 1); - qdev_prop_set_chr(dev, "chrB", serial_hds[1]); - qdev_prop_set_chr(dev, "chrA", serial_hds[0]); + qdev_prop_set_chr(dev, "chrB", serial_hd(1)); + qdev_prop_set_chr(dev, "chrA", serial_hd(0)); qdev_prop_set_uint32(dev, "chnBtype", escc_serial); qdev_prop_set_uint32(dev, "chnAtype", escc_serial); qdev_init_nofail(dev); diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index b677601fc6..7ca1e3fce4 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -282,7 +282,8 @@ static void iommu_bad_addr(IOMMUState *s, hwaddr addr, /* Called from RCU critical section */ static IOMMUTLBEntry sun4m_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flags) + IOMMUAccessFlags flags, + int iommu_idx) { IOMMUState *is = container_of(iommu, IOMMUState, iommu); hwaddr page, pa; diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 1874477ef6..4fa8cb2904 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "cpu.h" #include "hw/hw.h" @@ -84,7 +85,7 @@ typedef struct NiagaraBoardState { #define NIAGARA_PROM_BASE 0xfff0000000ULL #define NIAGARA_Q_OFFSET 0x10000ULL #define NIAGARA_OBP_OFFSET 0x80000ULL -#define PROM_SIZE_MAX (4 * 1024 * 1024) +#define PROM_SIZE_MAX (4 * MiB) static void add_rom_or_fail(const char *file, const hwaddr addr) { @@ -156,9 +157,9 @@ static void niagara_init(MachineState *machine) exit(1); } } - if (serial_hds[0]) { + if (serial_hd(0)) { serial_mm_init(sysmem, NIAGARA_UART_BASE, 0, NULL, 115200, - serial_hds[0], DEVICE_BIG_ENDIAN); + serial_hd(0), DEVICE_BIG_ENDIAN); } empty_slot_init(NIAGARA_IOBBASE, NIAGARA_IOBSIZE); sun4v_rtc_init(NIAGARA_RTC_BASE); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 2044a52ded..74b748497e 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu-common.h" @@ -52,11 +53,10 @@ #include "hw/loader.h" #include "elf.h" #include "trace.h" -#include "qemu/cutils.h" #define KERNEL_LOAD_ADDR 0x00404000 #define CMDLINE_ADDR 0x003ff000 -#define PROM_SIZE_MAX (4 * 1024 * 1024) +#define PROM_SIZE_MAX (4 * MiB) #define PROM_VADDR 0x000ffd00000ULL #define PBM_SPECIAL_BASE 0x1fe00000000ULL #define PBM_MEM_BASE 0x1ff00000000ULL @@ -186,8 +186,8 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, } if (*initrd_size > 0) { for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { - ptr = rom_ptr(*kernel_addr + i); - if (ldl_p(ptr + 8) == 0x48647253) { /* HdrS */ + ptr = rom_ptr(*kernel_addr + i, 32); + if (ptr && ldl_p(ptr + 8) == 0x48647253) { /* HdrS */ stl_p(ptr + 24, *initrd_addr + *kernel_addr); stl_p(ptr + 28, *initrd_size); break; @@ -295,10 +295,10 @@ static void ebus_realize(PCIDevice *pci_dev, Error **errp) i = 0; if (s->console_serial_base) { serial_mm_init(pci_address_space(pci_dev), s->console_serial_base, - 0, NULL, 115200, serial_hds[i], DEVICE_BIG_ENDIAN); + 0, NULL, 115200, serial_hd(i), DEVICE_BIG_ENDIAN); i++; } - serial_hds_isa_init(s->isa_bus, i, MAX_SERIAL_PORTS); + serial_hds_isa_init(s->isa_bus, i, MAX_ISA_SERIAL_PORTS); /* Parallel ports */ parallel_hds_isa_init(s->isa_bus, MAX_PARALLEL_PORTS); @@ -425,13 +425,19 @@ static void prom_init(hwaddr addr, const char *bios_name) } } -static void prom_init1(Object *obj) +static void prom_realize(DeviceState *ds, Error **errp) { - PROMState *s = OPENPROM(obj); - SysBusDevice *dev = SYS_BUS_DEVICE(obj); + PROMState *s = OPENPROM(ds); + SysBusDevice *dev = SYS_BUS_DEVICE(ds); + Error *local_err = NULL; + + memory_region_init_ram_nomigrate(&s->prom, OBJECT(ds), "sun4u.prom", + PROM_SIZE_MAX, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } - memory_region_init_ram_nomigrate(&s->prom, obj, "sun4u.prom", PROM_SIZE_MAX, - &error_fatal); vmstate_register_ram_global(&s->prom); memory_region_set_readonly(&s->prom, true); sysbus_init_mmio(dev, &s->prom); @@ -446,6 +452,7 @@ static void prom_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->props = prom_properties; + dc->realize = prom_realize; } static const TypeInfo prom_info = { @@ -453,7 +460,6 @@ static const TypeInfo prom_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PROMState), .class_init = prom_class_init, - .instance_init = prom_init1, }; diff --git a/hw/sparc64/sun4u_iommu.c b/hw/sparc64/sun4u_iommu.c index eb3aaa87e6..1ef7645ba5 100644 --- a/hw/sparc64/sun4u_iommu.c +++ b/hw/sparc64/sun4u_iommu.c @@ -73,7 +73,7 @@ /* Called from RCU critical section */ static IOMMUTLBEntry sun4u_translate_iommu(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag) + IOMMUAccessFlags flag, int iommu_idx) { IOMMUState *is = container_of(iommu, IOMMUState, iommu); hwaddr baseaddr, offset; diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 5059396bc6..b29bfd3124 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -66,6 +66,8 @@ /* CEx Control Register */ #define R_CTRL0 (0x10 / 4) +#define CTRL_IO_DUAL_DATA (1 << 29) +#define CTRL_IO_DUAL_ADDR_DATA (1 << 28) /* Includes dummies */ #define CTRL_CMD_SHIFT 16 #define CTRL_CMD_MASK 0xff #define CTRL_DUMMY_HIGH_SHIFT 14 @@ -492,14 +494,20 @@ static int aspeed_smc_flash_dummies(const AspeedSMCFlash *fl) uint32_t r_ctrl0 = s->regs[s->r_ctrl0 + fl->id]; uint32_t dummy_high = (r_ctrl0 >> CTRL_DUMMY_HIGH_SHIFT) & 0x1; uint32_t dummy_low = (r_ctrl0 >> CTRL_DUMMY_LOW_SHIFT) & 0x3; + uint32_t dummies = ((dummy_high << 2) | dummy_low) * 8; - return ((dummy_high << 2) | dummy_low) * 8; + if (r_ctrl0 & CTRL_IO_DUAL_ADDR_DATA) { + dummies /= 2; + } + + return dummies; } -static void aspeed_smc_flash_send_addr(AspeedSMCFlash *fl, uint32_t addr) +static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr) { const AspeedSMCState *s = fl->controller; uint8_t cmd = aspeed_smc_flash_cmd(fl); + int i; /* Flash access can not exceed CS segment */ addr = aspeed_smc_check_segment_addr(fl, addr); @@ -512,6 +520,18 @@ static void aspeed_smc_flash_send_addr(AspeedSMCFlash *fl, uint32_t addr) ssi_transfer(s->spi, (addr >> 16) & 0xff); ssi_transfer(s->spi, (addr >> 8) & 0xff); ssi_transfer(s->spi, (addr & 0xff)); + + /* + * Use fake transfers to model dummy bytes. The value should + * be configured to some non-zero value in fast read mode and + * zero in read mode. But, as the HW allows inconsistent + * settings, let's check for fast read mode. + */ + if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) { + for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) { + ssi_transfer(fl->controller->spi, 0xFF); + } + } } static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) @@ -530,19 +550,7 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) case CTRL_READMODE: case CTRL_FREADMODE: aspeed_smc_flash_select(fl); - aspeed_smc_flash_send_addr(fl, addr); - - /* - * Use fake transfers to model dummy bytes. The value should - * be configured to some non-zero value in fast read mode and - * zero in read mode. But, as the HW allows inconsistent - * settings, let's check for fast read mode. - */ - if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) { - for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) { - ssi_transfer(fl->controller->spi, 0xFF); - } - } + aspeed_smc_flash_setup(fl, addr); for (i = 0; i < size; i++) { ret |= ssi_transfer(s->spi, 0x0) << (8 * i); @@ -579,7 +587,7 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, break; case CTRL_WRITEMODE: aspeed_smc_flash_select(fl); - aspeed_smc_flash_send_addr(fl, addr); + aspeed_smc_flash_setup(fl, addr); for (i = 0; i < size; i++) { ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); @@ -632,23 +640,17 @@ static void aspeed_smc_reset(DeviceState *d) aspeed_smc_segment_to_reg(&s->ctrl->segments[i]); } - /* HW strapping for AST2500 FMC controllers */ + /* HW strapping flash type for FMC controllers */ if (s->ctrl->segments == aspeed_segments_ast2500_fmc) { /* flash type is fixed to SPI for CE0 and CE1 */ s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1); - - /* 4BYTE mode is autodetected for CE0. Let's force it to 1 for - * now */ - s->regs[s->r_ce_ctrl] |= (1 << (CTRL_EXTENDED0)); } /* HW strapping for AST2400 FMC controllers (SCU70). Let's use the * configuration of the palmetto-bmc machine */ if (s->ctrl->segments == aspeed_segments_fmc) { s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); - - s->regs[s->r_ce_ctrl] |= (1 << (CTRL_EXTENDED0)); } } diff --git a/hw/ssi/omap_spi.c b/hw/ssi/omap_spi.c index 34163e5646..f278a55160 100644 --- a/hw/ssi/omap_spi.c +++ b/hw/ssi/omap_spi.c @@ -20,6 +20,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/arm/omap.h" @@ -294,11 +295,15 @@ static void omap_mcspi_write(void *opaque, hwaddr addr, case 0x2c: /* MCSPI_CHCONF */ if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ omap_mcspi_dmarequest_update(s->ch + ch); - if (((value >> 12) & 3) == 3) /* TRM */ - fprintf(stderr, "%s: invalid TRM value (3)\n", __func__); - if (((value >> 7) & 0x1f) < 3) /* WL */ - fprintf(stderr, "%s: invalid WL value (%" PRIx64 ")\n", - __func__, (value >> 7) & 0x1f); + if (((value >> 12) & 3) == 3) { /* TRM */ + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid TRM value (3)\n", + __func__); + } + if (((value >> 7) & 0x1f) < 3) { /* WL */ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid WL value (%" PRIx64 ")\n", + __func__, (value >> 7) & 0x1f); + } s->ch[ch].config = value & 0x7fffff; break; diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 426f971311..c052bfc4b3 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -616,7 +616,8 @@ static void xilinx_spips_flush_txfifo(XilinxSPIPS *s) if (fifo8_is_empty(&s->tx_fifo)) { xilinx_spips_update_ixr(s); return; - } else if (s->snoop_state == SNOOP_STRIPING) { + } else if (s->snoop_state == SNOOP_STRIPING || + s->snoop_state == SNOOP_NONE) { for (i = 0; i < num_effective_busses(s); ++i) { tx_rx[i] = fifo8_pop(&s->tx_fifo); } @@ -850,12 +851,17 @@ static void xlnx_zynqmp_qspips_notify(void *opaque) { size_t ret; uint32_t num; - const void *rxd = pop_buf(recv_fifo, 4, &num); + const void *rxd; + int len; + + len = recv_fifo->num >= rq->dma_burst_size ? rq->dma_burst_size : + recv_fifo->num; + rxd = pop_buf(recv_fifo, len, &num); memcpy(rq->dma_buf, rxd, num); - ret = stream_push(rq->dma, rq->dma_buf, 4); - assert(ret == 4); + ret = stream_push(rq->dma, rq->dma_buf, num); + assert(ret == num); xlnx_zynqmp_qspips_check_flush(rq); } } @@ -1332,6 +1338,12 @@ static void xlnx_zynqmp_qspips_realize(DeviceState *dev, Error **errp) XlnxZynqMPQSPIPS *s = XLNX_ZYNQMP_QSPIPS(dev); XilinxSPIPSClass *xsc = XILINX_SPIPS_GET_CLASS(s); + if (s->dma_burst_size > QSPI_DMA_MAX_BURST_SIZE) { + error_setg(errp, + "qspi dma burst size %u exceeds maximum limit %d", + s->dma_burst_size, QSPI_DMA_MAX_BURST_SIZE); + return; + } xilinx_qspips_realize(dev, errp); fifo8_create(&s->rx_fifo_g, xsc->rx_fifo_size); fifo8_create(&s->tx_fifo_g, xsc->tx_fifo_size); @@ -1345,7 +1357,7 @@ static void xlnx_zynqmp_qspips_init(Object *obj) object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SLAVE, (Object **)&rq->dma, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, NULL); } @@ -1410,6 +1422,11 @@ static const VMStateDescription vmstate_xlnx_zynqmp_qspips = { } }; +static Property xilinx_zynqmp_qspips_properties[] = { + DEFINE_PROP_UINT32("dma-burst-size", XlnxZynqMPQSPIPS, dma_burst_size, 64), + DEFINE_PROP_END_OF_LIST(), +}; + static Property xilinx_qspips_properties[] = { /* We had to turn this off for 2.10 as it is not compatible with migration. * It can be enabled but will prevent the device to be migrated. @@ -1462,6 +1479,7 @@ static void xlnx_zynqmp_qspips_class_init(ObjectClass *klass, void * data) dc->realize = xlnx_zynqmp_qspips_realize; dc->reset = xlnx_zynqmp_qspips_reset; dc->vmsd = &vmstate_xlnx_zynqmp_qspips; + dc->props = xilinx_zynqmp_qspips_properties; xsc->reg_ops = &xlnx_zynqmp_qspips_ops; xsc->rx_fifo_size = RXFF_A_Q; xsc->tx_fifo_size = TXFF_A_Q; diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index 8b27a4b7ef..e16b2b913c 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -6,6 +6,7 @@ common-obj-$(CONFIG_CADENCE) += cadence_ttc.o common-obj-$(CONFIG_DS1338) += ds1338.o common-obj-$(CONFIG_HPET) += hpet.o common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o +common-obj-$(CONFIG_M41T80) += m41t80.o common-obj-$(CONFIG_M48T59) += m48t59.o ifeq ($(CONFIG_ISA_BUS),y) common-obj-$(CONFIG_M48T59) += m48t59-isa.o diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 50acbf530a..5e3f51b66b 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -10,8 +10,10 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/timer/aspeed_timer.h" +#include "hw/misc/aspeed_scu.h" #include "qemu-common.h" #include "qemu/bitops.h" #include "qemu/timer.h" @@ -26,7 +28,6 @@ #define TIMER_CLOCK_USE_EXT true #define TIMER_CLOCK_EXT_HZ 1000000 #define TIMER_CLOCK_USE_APB false -#define TIMER_CLOCK_APB_HZ 24000000 #define TIMER_REG_STATUS 0 #define TIMER_REG_RELOAD 1 @@ -80,11 +81,11 @@ static inline bool timer_external_clock(AspeedTimer *t) return timer_ctrl_status(t, op_external_clock); } -static uint32_t clock_rates[] = { TIMER_CLOCK_APB_HZ, TIMER_CLOCK_EXT_HZ }; - static inline uint32_t calculate_rate(struct AspeedTimer *t) { - return clock_rates[timer_external_clock(t)]; + AspeedTimerCtrlState *s = timer_to_ctrl(t); + + return timer_external_clock(t) ? TIMER_CLOCK_EXT_HZ : s->scu->apb_freq; } static inline uint32_t calculate_ticks(struct AspeedTimer *t, uint64_t now_ns) @@ -449,6 +450,16 @@ static void aspeed_timer_realize(DeviceState *dev, Error **errp) int i; SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AspeedTimerCtrlState *s = ASPEED_TIMER(dev); + Object *obj; + Error *err = NULL; + + obj = object_property_get_link(OBJECT(dev), "scu", &err); + if (!obj) { + error_propagate(errp, err); + error_prepend(errp, "required link 'scu' not found: "); + return; + } + s->scu = ASPEED_SCU(obj); for (i = 0; i < ASPEED_TIMER_NR_TIMERS; i++) { aspeed_init_one_timer(s, i); @@ -504,7 +515,7 @@ static const VMStateDescription vmstate_aspeed_timer_state = { VMSTATE_UINT32(ctrl, AspeedTimerCtrlState), VMSTATE_UINT32(ctrl2, AspeedTimerCtrlState), VMSTATE_STRUCT_ARRAY(timers, AspeedTimerCtrlState, - ASPEED_TIMER_NR_TIMERS, 2, vmstate_aspeed_timer, + ASPEED_TIMER_NR_TIMERS, 1, vmstate_aspeed_timer, AspeedTimer), VMSTATE_END_OF_LIST() } diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c index 9878746609..801d1dba74 100644 --- a/hw/timer/cmsdk-apb-timer.c +++ b/hw/timer/cmsdk-apb-timer.c @@ -119,17 +119,33 @@ static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value, } s->ctrl = value & 0xf; if (s->ctrl & R_CTRL_EN_MASK) { - ptimer_run(s->timer, 0); + ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0); } else { ptimer_stop(s->timer); } break; case A_RELOAD: /* Writing to reload also sets the current timer value */ + if (!value) { + ptimer_stop(s->timer); + } ptimer_set_limit(s->timer, value, 1); + if (value && (s->ctrl & R_CTRL_EN_MASK)) { + /* + * Make sure timer is running (it might have stopped if this + * was an expired one-shot timer) + */ + ptimer_run(s->timer, 0); + } break; case A_VALUE: + if (!value && !ptimer_get_limit(s->timer)) { + ptimer_stop(s->timer); + } ptimer_set_count(s->timer, value); + if (value && (s->ctrl & R_CTRL_EN_MASK)) { + ptimer_run(s->timer, ptimer_get_limit(s->timer) == 0); + } break; case A_INTSTATUS: /* Just one bit, which is W1C. */ @@ -201,7 +217,7 @@ static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp) bh = qemu_bh_new(cmsdk_apb_timer_tick, s); s->timer = ptimer_init(bh, PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | - PTIMER_POLICY_NO_IMMEDIATE_TRIGGER | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | PTIMER_POLICY_NO_IMMEDIATE_RELOAD | PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c index e1fcf73c3e..4d73077207 100644 --- a/hw/timer/digic-timer.c +++ b/hw/timer/digic-timer.c @@ -73,7 +73,7 @@ static uint64_t digic_timer_read(void *opaque, hwaddr offset, unsigned size) default: qemu_log_mask(LOG_UNIMP, "digic-timer: read access to unknown register 0x" - TARGET_FMT_plx, offset); + TARGET_FMT_plx "\n", offset); } return ret; @@ -109,7 +109,7 @@ static void digic_timer_write(void *opaque, hwaddr offset, default: qemu_log_mask(LOG_UNIMP, "digic-timer: read access to unknown register 0x" - TARGET_FMT_plx, offset); + TARGET_FMT_plx "\n", offset); } } diff --git a/hw/timer/m41t80.c b/hw/timer/m41t80.c new file mode 100644 index 0000000000..734d7d95fc --- /dev/null +++ b/hw/timer/m41t80.c @@ -0,0 +1,117 @@ +/* + * M41T80 serial rtc emulation + * + * Copyright (c) 2018 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/timer.h" +#include "qemu/bcd.h" +#include "hw/i2c/i2c.h" + +#define TYPE_M41T80 "m41t80" +#define M41T80(obj) OBJECT_CHECK(M41t80State, (obj), TYPE_M41T80) + +typedef struct M41t80State { + I2CSlave parent_obj; + int8_t addr; +} M41t80State; + +static void m41t80_realize(DeviceState *dev, Error **errp) +{ + M41t80State *s = M41T80(dev); + + s->addr = -1; +} + +static int m41t80_send(I2CSlave *i2c, uint8_t data) +{ + M41t80State *s = M41T80(i2c); + + if (s->addr < 0) { + s->addr = data; + } else { + s->addr++; + } + return 0; +} + +static int m41t80_recv(I2CSlave *i2c) +{ + M41t80State *s = M41T80(i2c); + struct tm now; + qemu_timeval tv; + + if (s->addr < 0) { + s->addr = 0; + } + if (s->addr >= 1 && s->addr <= 7) { + qemu_get_timedate(&now, -1); + } + switch (s->addr++) { + case 0: + qemu_gettimeofday(&tv); + return to_bcd(tv.tv_usec / 10000); + case 1: + return to_bcd(now.tm_sec); + case 2: + return to_bcd(now.tm_min); + case 3: + return to_bcd(now.tm_hour); + case 4: + return to_bcd(now.tm_wday); + case 5: + return to_bcd(now.tm_mday); + case 6: + return to_bcd(now.tm_mon + 1); + case 7: + return to_bcd(now.tm_year % 100); + case 8 ... 19: + qemu_log_mask(LOG_UNIMP, "%s: unimplemented register: %d\n", + __func__, s->addr - 1); + return 0; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid register: %d\n", + __func__, s->addr - 1); + return 0; + } +} + +static int m41t80_event(I2CSlave *i2c, enum i2c_event event) +{ + M41t80State *s = M41T80(i2c); + + if (event == I2C_START_SEND) { + s->addr = -1; + } + return 0; +} + +static void m41t80_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); + + dc->realize = m41t80_realize; + sc->send = m41t80_send; + sc->recv = m41t80_recv; + sc->event = m41t80_event; +} + +static const TypeInfo m41t80_info = { + .name = TYPE_M41T80, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(M41t80State), + .class_init = m41t80_class_init, +}; + +static void m41t80_register_types(void) +{ + type_register_static(&m41t80_info); +} + +type_init(m41t80_register_types) diff --git a/hw/timer/m48t59-internal.h b/hw/timer/m48t59-internal.h index 32ae957805..d0f0caf3c7 100644 --- a/hw/timer/m48t59-internal.h +++ b/hw/timer/m48t59-internal.h @@ -25,13 +25,10 @@ #ifndef HW_M48T59_INTERNAL_H #define HW_M48T59_INTERNAL_H 1 -//#define DEBUG_NVRAM +#define M48T59_DEBUG 0 -#if defined(DEBUG_NVRAM) -#define NVRAM_PRINTF(fmt, ...) do { printf(fmt , ## __VA_ARGS__); } while (0) -#else -#define NVRAM_PRINTF(fmt, ...) do { } while (0) -#endif +#define NVRAM_PRINTF(fmt, ...) do { \ + if (M48T59_DEBUG) { printf(fmt , ## __VA_ARGS__); } } while (0) /* * The M48T02, M48T08 and M48T59 chips are very similar. The newer '59 has diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index 742c576443..f2991762ab 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -456,7 +456,7 @@ static void NVRAM_writeb(void *opaque, hwaddr addr, uint64_t val, { M48t59State *NVRAM = opaque; - NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); + NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" => 0x%"PRIx64"\n", __func__, addr, val); switch (addr) { case 0: NVRAM->addr &= ~0x00FF; @@ -488,7 +488,7 @@ static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size) retval = -1; break; } - NVRAM_PRINTF("%s: 0x%08x <= 0x%08x\n", __func__, addr, retval); + NVRAM_PRINTF("%s: 0x%"HWADDR_PRIx" <= 0x%08x\n", __func__, addr, retval); return retval; } diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 9afb2d048c..5f8736cf10 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -13,7 +13,6 @@ #include "hw/sh4/sh.h" #include "qemu/timer.h" #include "qemu/main-loop.h" -#include "exec/address-spaces.h" #include "hw/ptimer.h" //#define DEBUG_TIMER diff --git a/hw/timer/twl92230.c b/hw/timer/twl92230.c index ef116c636c..3b43b46199 100644 --- a/hw/timer/twl92230.c +++ b/hw/timer/twl92230.c @@ -853,10 +853,9 @@ static const VMStateDescription vmstate_menelaus = { } }; -static int twl92230_init(I2CSlave *i2c) +static void twl92230_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(i2c); - MenelausState *s = TWL92230(i2c); + MenelausState *s = TWL92230(dev); s->rtc.hz_tm = timer_new_ms(rtc_clock, menelaus_rtc_hz, s); /* Three output pins plus one interrupt pin. */ @@ -865,9 +864,7 @@ static int twl92230_init(I2CSlave *i2c) /* Three input pins plus one power-button pin. */ qdev_init_gpio_in(dev, menelaus_gpio_set, 4); - menelaus_reset(i2c); - - return 0; + menelaus_reset(I2C_SLAVE(dev)); } static void twl92230_class_init(ObjectClass *klass, void *data) @@ -875,7 +872,7 @@ static void twl92230_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - sc->init = twl92230_init; + dc->realize = twl92230_realize; sc->event = menelaus_event; sc->recv = menelaus_rx; sc->send = menelaus_tx; diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c index 6418ef0831..10bc20dbec 100644 --- a/hw/tpm/tpm_emulator.c +++ b/hw/tpm/tpm_emulator.c @@ -4,7 +4,7 @@ * Copyright (c) 2017 Intel Corporation * Author: Amarnath Valluri * - * Copyright (c) 2010 - 2013 IBM Corporation + * Copyright (c) 2010 - 2013, 2018 IBM Corporation * Authors: * Stefan Berger * @@ -49,6 +49,19 @@ #define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) /* data structures */ + +/* blobs from the TPM; part of VM state when migrating */ +typedef struct TPMBlobBuffers { + uint32_t permanent_flags; + TPMSizedBuffer permanent; + + uint32_t volatil_flags; + TPMSizedBuffer volatil; + + uint32_t savestate_flags; + TPMSizedBuffer savestate; +} TPMBlobBuffers; + typedef struct TPMEmulator { TPMBackend parent; @@ -64,6 +77,8 @@ typedef struct TPMEmulator { unsigned int established_flag:1; unsigned int established_flag_cached:1; + + TPMBlobBuffers state_blobs; } TPMEmulator; @@ -293,7 +308,8 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb, return 0; } -static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) +static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, + bool is_resume) { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); ptm_init init = { @@ -301,12 +317,17 @@ static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) }; ptm_res res; + trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize); + if (buffersize != 0 && tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { goto err_exit; } - trace_tpm_emulator_startup_tpm(); + if (is_resume) { + init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE); + } + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), sizeof(init)) < 0) { error_report("tpm-emulator: could not send INIT: %s", @@ -325,6 +346,11 @@ err_exit: return -1; } +static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) +{ + return tpm_emulator_startup_tpm_resume(tb, buffersize, false); +} + static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) { TPMEmulator *tpm_emu = TPM_EMULATOR(tb); @@ -423,16 +449,21 @@ static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) { Error *err = NULL; + ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | + PTM_CAP_STOP; - error_setg(&tpm_emu->migration_blocker, - "Migration disabled: TPM emulator not yet migratable"); - migrate_add_blocker(tpm_emu->migration_blocker, &err); - if (err) { - error_report_err(err); - error_free(tpm_emu->migration_blocker); - tpm_emu->migration_blocker = NULL; + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { + error_setg(&tpm_emu->migration_blocker, + "Migration disabled: TPM emulator does not support " + "migration"); + migrate_add_blocker(tpm_emu->migration_blocker, &err); + if (err) { + error_report_err(err); + error_free(tpm_emu->migration_blocker); + tpm_emu->migration_blocker = NULL; - return -1; + return -1; + } } return 0; @@ -570,6 +601,267 @@ static const QemuOptDesc tpm_emulator_cmdline_opts[] = { { /* end of list */ }, }; +/* + * Transfer a TPM state blob from the TPM into a provided buffer. + * + * @tpm_emu: TPMEmulator + * @type: the type of blob to transfer + * @tsb: the TPMSizeBuffer to fill with the blob + * @flags: the flags to return to the caller + */ +static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu, + uint8_t type, + TPMSizedBuffer *tsb, + uint32_t *flags) +{ + ptm_getstate pgs; + ptm_res res; + ssize_t n; + uint32_t totlength, length; + + tpm_sized_buffer_reset(tsb); + + pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED); + pgs.u.req.type = cpu_to_be32(type); + pgs.u.req.offset = 0; + + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB, + &pgs, sizeof(pgs.u.req), + offsetof(ptm_getstate, u.resp.data)) < 0) { + error_report("tpm-emulator: could not get state blob type %d : %s", + type, strerror(errno)); + return -1; + } + + res = be32_to_cpu(pgs.u.resp.tpm_result); + if (res != 0 && (res & 0x800) == 0) { + error_report("tpm-emulator: Getting the stateblob (type %d) failed " + "with a TPM error 0x%x", type, res); + return -1; + } + + totlength = be32_to_cpu(pgs.u.resp.totlength); + length = be32_to_cpu(pgs.u.resp.length); + if (totlength != length) { + error_report("tpm-emulator: Expecting to read %u bytes " + "but would get %u", totlength, length); + return -1; + } + + *flags = be32_to_cpu(pgs.u.resp.state_flags); + + if (totlength > 0) { + tsb->buffer = g_try_malloc(totlength); + if (!tsb->buffer) { + error_report("tpm-emulator: Out of memory allocating %u bytes", + totlength); + return -1; + } + + n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength); + if (n != totlength) { + error_report("tpm-emulator: Could not read stateblob (type %d); " + "expected %u bytes, got %zd", + type, totlength, n); + return -1; + } + } + tsb->size = totlength; + + trace_tpm_emulator_get_state_blob(type, tsb->size, *flags); + + return 0; +} + +static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu) +{ + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; + + if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, + &state_blobs->permanent, + &state_blobs->permanent_flags) < 0 || + tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, + &state_blobs->volatil, + &state_blobs->volatil_flags) < 0 || + tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, + &state_blobs->savestate, + &state_blobs->savestate_flags) < 0) { + goto err_exit; + } + + return 0; + + err_exit: + tpm_sized_buffer_reset(&state_blobs->volatil); + tpm_sized_buffer_reset(&state_blobs->permanent); + tpm_sized_buffer_reset(&state_blobs->savestate); + + return -1; +} + +/* + * Transfer a TPM state blob to the TPM emulator. + * + * @tpm_emu: TPMEmulator + * @type: the type of TPM state blob to transfer + * @tsb: TPMSizedBuffer containing the TPM state blob + * @flags: Flags describing the (encryption) state of the TPM state blob + */ +static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, + uint32_t type, + TPMSizedBuffer *tsb, + uint32_t flags) +{ + ssize_t n; + ptm_setstate pss; + ptm_res tpm_result; + + if (tsb->size == 0) { + return 0; + } + + pss = (ptm_setstate) { + .u.req.state_flags = cpu_to_be32(flags), + .u.req.type = cpu_to_be32(type), + .u.req.length = cpu_to_be32(tsb->size), + }; + + /* write the header only */ + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, + offsetof(ptm_setstate, u.req.data), 0) < 0) { + error_report("tpm-emulator: could not set state blob type %d : %s", + type, strerror(errno)); + return -1; + } + + /* now the body */ + n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); + if (n != tsb->size) { + error_report("tpm-emulator: Writing the stateblob (type %d) " + "failed; could not write %u bytes, but only %zd", + type, tsb->size, n); + return -1; + } + + /* now get the result */ + n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, + (uint8_t *)&pss, sizeof(pss.u.resp)); + if (n != sizeof(pss.u.resp)) { + error_report("tpm-emulator: Reading response from writing stateblob " + "(type %d) failed; expected %zu bytes, got %zd", type, + sizeof(pss.u.resp), n); + return -1; + } + + tpm_result = be32_to_cpu(pss.u.resp.tpm_result); + if (tpm_result != 0) { + error_report("tpm-emulator: Setting the stateblob (type %d) failed " + "with a TPM error 0x%x", type, tpm_result); + return -1; + } + + trace_tpm_emulator_set_state_blob(type, tsb->size, flags); + + return 0; +} + +/* + * Set all the TPM state blobs. + * + * Returns a negative errno code in case of error. + */ +static int tpm_emulator_set_state_blobs(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; + + trace_tpm_emulator_set_state_blobs(); + + if (tpm_emulator_stop_tpm(tb) < 0) { + trace_tpm_emulator_set_state_blobs_error("Could not stop TPM"); + return -EIO; + } + + if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, + &state_blobs->permanent, + state_blobs->permanent_flags) < 0 || + tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, + &state_blobs->volatil, + state_blobs->volatil_flags) < 0 || + tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, + &state_blobs->savestate, + state_blobs->savestate_flags) < 0) { + return -EIO; + } + + trace_tpm_emulator_set_state_blobs_done(); + + return 0; +} + +static int tpm_emulator_pre_save(void *opaque) +{ + TPMBackend *tb = opaque; + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + + trace_tpm_emulator_pre_save(); + + tpm_backend_finish_sync(tb); + + /* get the state blobs from the TPM */ + return tpm_emulator_get_state_blobs(tpm_emu); +} + +/* + * Load the TPM state blobs into the TPM. + * + * Returns negative errno codes in case of error. + */ +static int tpm_emulator_post_load(void *opaque, int version_id) +{ + TPMBackend *tb = opaque; + int ret; + + ret = tpm_emulator_set_state_blobs(tb); + if (ret < 0) { + return ret; + } + + if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) { + return -EIO; + } + + return 0; +} + +static const VMStateDescription vmstate_tpm_emulator = { + .name = "tpm-emulator", + .version_id = 0, + .pre_save = tpm_emulator_pre_save, + .post_load = tpm_emulator_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer, + TPMEmulator, 0, 0, + state_blobs.permanent.size), + + VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer, + TPMEmulator, 0, 0, + state_blobs.volatil.size), + + VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer, + TPMEmulator, 0, 0, + state_blobs.savestate.size), + + VMSTATE_END_OF_LIST() + } +}; + static void tpm_emulator_inst_init(Object *obj) { TPMEmulator *tpm_emu = TPM_EMULATOR(obj); @@ -579,6 +871,8 @@ static void tpm_emulator_inst_init(Object *obj) tpm_emu->options = g_new0(TPMEmulatorOptions, 1); tpm_emu->cur_locty_number = ~0; qemu_mutex_init(&tpm_emu->mutex); + + vmstate_register(NULL, -1, &vmstate_tpm_emulator, obj); } /* @@ -600,6 +894,7 @@ static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) static void tpm_emulator_inst_finalize(Object *obj) { TPMEmulator *tpm_emu = TPM_EMULATOR(obj); + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; tpm_emulator_shutdown(tpm_emu); @@ -614,7 +909,13 @@ static void tpm_emulator_inst_finalize(Object *obj) error_free(tpm_emu->migration_blocker); } + tpm_sized_buffer_reset(&state_blobs->volatil); + tpm_sized_buffer_reset(&state_blobs->permanent); + tpm_sized_buffer_reset(&state_blobs->savestate); + qemu_mutex_destroy(&tpm_emu->mutex); + + vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); } static void tpm_emulator_class_init(ObjectClass *klass, void *data) diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index 2ac7e74307..12f5c9a759 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -894,9 +894,57 @@ static void tpm_tis_reset(DeviceState *dev) tpm_backend_startup_tpm(s->be_driver, s->be_buffer_size); } +/* persistent state handling */ + +static int tpm_tis_pre_save(void *opaque) +{ + TPMState *s = opaque; + uint8_t locty = s->active_locty; + + trace_tpm_tis_pre_save(locty, s->rw_offset); + + if (DEBUG_TIS) { + tpm_tis_dump_state(opaque, 0); + } + + /* + * Synchronize with backend completion. + */ + tpm_backend_finish_sync(s->be_driver); + + return 0; +} + +static const VMStateDescription vmstate_locty = { + .name = "tpm-tis/locty", + .version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(state, TPMLocality), + VMSTATE_UINT32(inte, TPMLocality), + VMSTATE_UINT32(ints, TPMLocality), + VMSTATE_UINT8(access, TPMLocality), + VMSTATE_UINT32(sts, TPMLocality), + VMSTATE_UINT32(iface_id, TPMLocality), + VMSTATE_END_OF_LIST(), + } +}; + static const VMStateDescription vmstate_tpm_tis = { - .name = "tpm", - .unmigratable = 1, + .name = "tpm-tis", + .version_id = 0, + .pre_save = tpm_tis_pre_save, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(buffer, TPMState), + VMSTATE_UINT16(rw_offset, TPMState), + VMSTATE_UINT8(active_locty, TPMState), + VMSTATE_UINT8(aborting_locty, TPMState), + VMSTATE_UINT8(next_locty, TPMState), + + VMSTATE_STRUCT_ARRAY(loc, TPMState, TPM_TIS_NUM_LOCALITIES, 0, + vmstate_locty, TPMLocality), + + VMSTATE_END_OF_LIST() + } }; static Property tpm_tis_properties[] = { diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events index 9a65384088..25bee0cecf 100644 --- a/hw/tpm/trace-events +++ b/hw/tpm/trace-events @@ -20,13 +20,19 @@ tpm_emulator_set_locality(uint8_t locty) "setting locality to %d" tpm_emulator_handle_request(void) "processing TPM command" tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64 tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u" -tpm_emulator_startup_tpm(void) "startup" +tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu" tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d" tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD" tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2" tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2" tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified" tpm_emulator_handle_device_opts_startup_error(void) "Startup error" +tpm_emulator_get_state_blob(uint8_t type, uint32_t size, uint32_t flags) "got state blob type %d, %u bytes, flags 0x%08x" +tpm_emulator_set_state_blob(uint8_t type, uint32_t size, uint32_t flags) "set state blob type %d, %u bytes, flags 0x%08x" +tpm_emulator_set_state_blobs(void) "setting state blobs" +tpm_emulator_set_state_blobs_error(const char *msg) "error while setting state blobs: %s" +tpm_emulator_set_state_blobs_done(void) "Done setting state blobs" +tpm_emulator_pre_save(void) "" tpm_emulator_inst_init(void) "" # hw/tpm/tpm_tis.c @@ -44,3 +50,4 @@ tpm_tis_mmio_write_locty_seized(uint8_t locty, uint8_t active) "Locality %d seiz tpm_tis_mmio_write_init_abort(void) "Initiating abort" tpm_tis_mmio_write_lowering_irq(void) "Lowering IRQ" tpm_tis_mmio_write_data2send(uint32_t value, unsigned size) "Data to send to TPM: 0x%08x (size=%d)" +tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u" diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index 8e61dfc3e6..a58096f05e 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "cpu.h" @@ -72,17 +73,17 @@ static void tricore_testboard_init(MachineState *machine, int board_id) cpu = TRICORE_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; memory_region_init_ram(ext_cram, NULL, "powerlink_ext_c.ram", - 2 * 1024 * 1024, &error_fatal); + 2 * MiB, &error_fatal); memory_region_init_ram(ext_dram, NULL, "powerlink_ext_d.ram", - 4 * 1024 * 1024, &error_fatal); - memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48 * 1024, + 4 * MiB, &error_fatal); + memory_region_init_ram(int_cram, NULL, "powerlink_int_c.ram", 48 * KiB, &error_fatal); - memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48 * 1024, + memory_region_init_ram(int_dram, NULL, "powerlink_int_d.ram", 48 * KiB, &error_fatal); memory_region_init_ram(pcp_data, NULL, "powerlink_pcp_data.ram", - 16 * 1024, &error_fatal); + 16 * KiB, &error_fatal); memory_region_init_ram(pcp_text, NULL, "powerlink_pcp_text.ram", - 32 * 1024, &error_fatal); + 32 * KiB, &error_fatal); memory_region_add_subregion(sysmem, 0x80000000, ext_cram); memory_region_add_subregion(sysmem, 0xa1000000, ext_dram); diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index ea42e4681d..5c8b3c9907 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -27,10 +27,7 @@ */ #include "qemu/osdep.h" -#include -#include -#include -#include +#include #include "qemu/thread.h" #include "qemu/main-loop.h" diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index b7dd3602dc..0a6c657228 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -9,7 +9,8 @@ */ #include "qemu/osdep.h" -#include +#include "qemu/units.h" +#include #include "chardev/char-fe.h" #include "qemu/error-report.h" #include "qemu/sockets.h" @@ -40,7 +41,7 @@ static const uint8_t DEFAULT_ATR[] = { 0x13, 0x08 }; -#define VSCARD_IN_SIZE 65536 +#define VSCARD_IN_SIZE (64 * KiB) /* maximum size of ATR - from 7816-3 */ #define MAX_ATR_SIZE 40 @@ -275,9 +276,9 @@ static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size) VSCMsgHeader *hdr; if (card->vscard_in_pos + size > VSCARD_IN_SIZE) { - error_report( - "no room for data: pos %d + size %d > %d. dropping connection.", - card->vscard_in_pos, size, VSCARD_IN_SIZE); + error_report("no room for data: pos %u + size %d > %" PRId64 "." + " dropping connection.", + card->vscard_in_pos, size, VSCARD_IN_SIZE); ccid_card_vscard_drop_connection(card); return; } @@ -345,7 +346,7 @@ static void passthru_realize(CCIDCardState *base, Error **errp) card->vscard_in_pos = 0; card->vscard_in_hdr = 0; if (qemu_chr_fe_backend_connected(&card->cs)) { - error_setg(errp, "ccid-card-passthru: initing chardev"); + DPRINTF(card, D_INFO, "ccid-card-passthru: initing chardev"); qemu_chr_fe_set_handlers(&card->cs, ccid_card_vscard_can_read, ccid_card_vscard_read, diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c index 48cac87f6a..01a7ed0848 100644 --- a/hw/usb/combined-packet.c +++ b/hw/usb/combined-packet.c @@ -20,6 +20,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-common.h" #include "hw/usb.h" #include "qemu/iov.h" @@ -171,7 +172,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep) if ((p->iov.size % ep->max_packet_size) != 0 || !p->short_not_ok || next == NULL || /* Work around for Linux usbfs bulk splitting + migration */ - (totalsize == 16348 && p->int_req)) { + (totalsize == (16 * KiB - 36) && p->int_req)) { usb_device_handle_data(ep->dev, first); assert(first->status == USB_RET_ASYNC); if (first->combined) { diff --git a/hw/usb/desc-msos.c b/hw/usb/desc-msos.c index 3652919815..3a5ad7c8d0 100644 --- a/hw/usb/desc-msos.c +++ b/hw/usb/desc-msos.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" /* * Microsoft OS Descriptors diff --git a/hw/usb/desc.c b/hw/usb/desc.c index 85c15addc5..8b6eaea407 100644 --- a/hw/usb/desc.c +++ b/hw/usb/desc.c @@ -1,7 +1,7 @@ #include "qemu/osdep.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "trace.h" /* ------------------------------------------------------------------ */ diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index 343345235c..ee43e4914d 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -32,7 +32,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "hw/hw.h" #include "audio/audio.h" diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c index 0bbceaea0b..eac7365b0a 100644 --- a/hw/usb/dev-bluetooth.c +++ b/hw/usb/dev-bluetooth.c @@ -22,7 +22,7 @@ #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "sysemu/bt.h" #include "hw/bt.h" diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index c40019df96..62d18290dc 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -26,7 +26,7 @@ #include "hw/hw.h" #include "ui/console.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "qapi/error.h" #include "qemu/timer.h" #include "hw/input/hid.h" diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 11bcfae48e..b651406705 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -26,7 +26,7 @@ #include "qemu-common.h" #include "trace.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "qemu/error-report.h" #define NUM_PORTS 8 diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 6ecf70a79b..1ded7ac9a3 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -24,7 +24,7 @@ #include "qemu/iov.h" #include "trace.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" /* ----------------------------------------------------------------------- */ @@ -1017,12 +1017,16 @@ static MTPData *usb_mtp_get_object(MTPState *s, MTPControl *c, static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c, MTPObject *o) { - MTPData *d = usb_mtp_data_alloc(c); + MTPData *d; off_t offset; + if (c->argc <= 2) { + return NULL; + } trace_usb_mtp_op_get_partial_object(s->dev.addr, o->handle, o->path, c->argv[1], c->argv[2]); + d = usb_mtp_data_alloc(c); d->fd = open(o->path, O_RDONLY); if (d->fd == -1) { usb_mtp_data_free(d); @@ -1446,8 +1450,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) if (o == NULL) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, c->trans, 0, 0, 0, 0); - } - if (o->format != FMT_ASSOCIATION) { + } else if (o->format != FMT_ASSOCIATION) { usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, c->trans, 0, 0, 0, 0); } @@ -1660,6 +1663,7 @@ static void usb_mtp_write_metadata(MTPState *s) uint32_t next_handle = s->next_handle; assert(!s->write_pending); + assert(p != NULL); utf16_to_str(dataset->length, dataset->filename, filename); @@ -1696,6 +1700,11 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, uint64_t dlen; uint32_t data_len = p->iov.size; + if (!d) { + usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 0, + 0, 0, 0, 0); + return; + } if (d->first) { /* Total length of incoming data */ d->length = cpu_to_le32(container->length) - sizeof(mtp_container); @@ -1838,7 +1847,7 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) p->status = USB_RET_STALL; return; } - if (s->data_out && !s->data_out->first) { + if ((s->data_out != NULL) && !s->data_out->first) { container_type = TYPE_DATA; } else { usb_packet_copy(p, &container, sizeof(container)); @@ -1948,16 +1957,17 @@ static void usb_mtp_realize(USBDevice *dev, Error **errp) return; } s->desc = strrchr(s->root, '/'); - /* Mark store as RW */ - if (!s->readonly) { - s->flags |= (1 << MTP_FLAG_WRITABLE); - } if (s->desc && s->desc[0]) { s->desc = g_strdup(s->desc + 1); } else { s->desc = g_strdup("none"); } } + /* Mark store as RW */ + if (!s->readonly) { + s->flags |= (1 << MTP_FLAG_WRITABLE); + } + } static const VMStateDescription vmstate_usb_mtp = { diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index aea7edcf31..385e090336 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -27,7 +27,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "net/net.h" #include "qemu/error-report.h" #include "qemu/queue.h" diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 2829dda391..98d1ca3c91 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -14,7 +14,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "chardev/char-serial.h" #include "chardev/char-fe.h" diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index e6468057a0..8f716fc165 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -35,11 +35,12 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "ccid.h" @@ -63,7 +64,7 @@ do { \ * or handle the migration complexity - VMState doesn't handle this case. * sizes are expected never to be exceeded, unless guest misbehaves. */ -#define BULK_OUT_DATA_SIZE 65536 +#define BULK_OUT_DATA_SIZE (64 * KiB) #define PENDING_ANSWERS_NUM 128 #define BULK_IN_BUF_SIZE 384 @@ -329,8 +330,8 @@ static const uint8_t qemu_ccid_descriptor[] = { */ 0x07, /* u8 bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */ - 0x00, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ - 0x01, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ + 0x01, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/ + 0x00, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */ /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */ 0xa0, 0x0f, 0x00, 0x00, /* u32 dwMaximumClock; */ @@ -786,7 +787,7 @@ static void ccid_write_data_block(USBCCIDState *s, uint8_t slot, uint8_t seq, DPRINTF(s, D_VERBOSE, "error %d\n", p->b.bError); } if (len) { - g_assert_nonnull(data); + assert(data); memcpy(p->abData, data, len); } ccid_reset_error_status(s); @@ -1064,7 +1065,8 @@ err: return; } -static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) +static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p, + unsigned int max_packet_size) { int len = 0; @@ -1072,10 +1074,13 @@ static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p) if (s->current_bulk_in != NULL) { len = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, p->iov.size); - usb_packet_copy(p, s->current_bulk_in->data + - s->current_bulk_in->pos, len); + if (len) { + usb_packet_copy(p, s->current_bulk_in->data + + s->current_bulk_in->pos, len); + } s->current_bulk_in->pos += len; - if (s->current_bulk_in->pos == s->current_bulk_in->len) { + if (s->current_bulk_in->pos == s->current_bulk_in->len + && len != max_packet_size) { ccid_bulk_in_release(s); } } else { @@ -1107,7 +1112,7 @@ static void ccid_handle_data(USBDevice *dev, USBPacket *p) case USB_TOKEN_IN: switch (p->ep->nr) { case CCID_BULK_IN_EP: - ccid_bulk_in_copy_to_guest(s, p); + ccid_bulk_in_copy_to_guest(s, p, dev->ep_ctl.max_packet_size); break; case CCID_INT_IN_EP: if (s->notify_slot_change) { diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index b56c75a73a..45a9487cdb 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -14,13 +14,12 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "hw/scsi/scsi.h" #include "ui/console.h" #include "monitor/monitor.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" #include "qapi/visitor.h" #include "qemu/cutils.h" @@ -589,13 +588,6 @@ static const struct SCSIBusInfo usb_msd_scsi_info_bot = { .load_request = usb_msd_load_request, }; -static void usb_msd_unrealize_storage(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - - object_unref(OBJECT(&s->bus)); -} - static void usb_msd_storage_realize(USBDevice *dev, Error **errp) { MSDState *s = USB_STORAGE_DEV(dev); @@ -634,6 +626,7 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) &usb_msd_scsi_info_storage, NULL); scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable, s->conf.bootindex, s->conf.share_rw, + s->conf.rerror, s->conf.werror, dev->serial, errp); blk_unref(blk); @@ -644,13 +637,6 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) s->scsi_dev = scsi_dev; } -static void usb_msd_bot_unrealize(USBDevice *dev, Error **errp) -{ - MSDState *s = USB_STORAGE_DEV(dev); - - object_unref(OBJECT(&s->bus)); -} - static void usb_msd_bot_realize(USBDevice *dev, Error **errp) { MSDState *s = USB_STORAGE_DEV(dev); @@ -687,6 +673,7 @@ static const VMStateDescription vmstate_usb_msd = { static Property msd_properties[] = { DEFINE_BLOCK_PROPERTIES(MSDState, conf), + DEFINE_BLOCK_ERROR_PROPERTIES(MSDState, conf), DEFINE_PROP_BIT("removable", MSDState, removable, 0, false), DEFINE_PROP_END_OF_LIST(), }; @@ -714,7 +701,6 @@ static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) USBDeviceClass *uc = USB_DEVICE_CLASS(klass); uc->realize = usb_msd_storage_realize; - uc->unrealize = usb_msd_unrealize_storage; dc->props = msd_properties; } @@ -777,7 +763,6 @@ static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data) USBDeviceClass *uc = USB_DEVICE_CLASS(klass); uc->realize = usb_msd_bot_realize; - uc->unrealize = usb_msd_bot_unrealize; uc->attached_settable = true; } diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index c218b53f09..be566cad02 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -17,7 +17,7 @@ #include "qemu/error-report.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" #include "hw/scsi/scsi.h" #include "scsi/constants.h" @@ -896,8 +896,6 @@ static void usb_uas_unrealize(USBDevice *dev, Error **errp) UASDevice *uas = USB_UAS(dev); qemu_bh_delete(uas->status_bh); - - object_unref(OBJECT(&uas->bus)); } static void usb_uas_realize(USBDevice *dev, Error **errp) diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c index bf70013059..ac0bc83b52 100644 --- a/hw/usb/dev-wacom.c +++ b/hw/usb/dev-wacom.c @@ -29,7 +29,7 @@ #include "hw/hw.h" #include "ui/console.h" #include "hw/usb.h" -#include "hw/usb/desc.h" +#include "desc.h" /* Interface requests */ #define WACOM_GET_REPORT 0x2101 diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 0134232627..e5acfc5ba5 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -1672,7 +1672,8 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) ehci_set_state(ehci, async, EST_HORIZONTALQH); } else if ((q->qh.token & QTD_TOKEN_ACTIVE) && - (NLPTR_TBIT(q->qh.current_qtd) == 0)) { + (NLPTR_TBIT(q->qh.current_qtd) == 0) && + (q->qh.current_qtd != 0)) { q->qtdaddr = q->qh.current_qtd; ehci_set_state(ehci, async, EST_FETCHQTD); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 721beb5486..8f1a01a405 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1954,7 +1954,12 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) for (i = 0; i < length; i++) { TRBType type; type = xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL); - assert(type); + if (!type) { + xhci_die(xhci); + xhci_ep_free_xfer(xfer); + epctx->kick_active--; + return; + } } xfer->streamid = streamid; diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 1b0be071cc..f31e9cbbb8 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -102,6 +102,7 @@ struct USBHostDevice { /* callbacks & friends */ QEMUBH *bh_nodev; QEMUBH *bh_postld; + bool bh_postld_pending; Notifier exit; /* request queues */ @@ -247,7 +248,11 @@ static int usb_host_init(void) if (rc != 0) { return -1; } +#if LIBUSB_API_VERSION >= 0x01000106 + libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, loglevel); +#else libusb_set_debug(ctx, loglevel); +#endif #ifdef CONFIG_WIN32 /* FIXME: add support for Windows. */ #else @@ -866,6 +871,10 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev) int rc; Error *local_err = NULL; + if (s->bh_postld_pending) { + return -1; + } + trace_usb_host_open_started(bus_num, addr); if (s->dh != NULL) { @@ -1524,6 +1533,7 @@ static void usb_host_post_load_bh(void *opaque) if (udev->attached) { usb_device_detach(udev); } + dev->bh_postld_pending = false; usb_host_auto_check(NULL); } @@ -1535,6 +1545,7 @@ static int usb_host_post_load(void *opaque, int version_id) dev->bh_postld = qemu_bh_new(usb_host_post_load_bh, dev); } qemu_bh_schedule(dev->bh_postld); + dev->bh_postld_pending = true; return 0; } diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 65a9196c1a..99094a721e 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-common.h" #include "qemu/timer.h" @@ -795,7 +796,7 @@ static void usbredir_handle_bulk_data(USBRedirDevice *dev, USBPacket *p, usbredirparser_peer_has_cap(dev->parser, usb_redir_cap_32bits_bulk_length)); - if (ep & USB_DIR_IN) { + if (ep & USB_DIR_IN || size == 0) { usbredirparser_send_bulk_packet(dev->parser, p->id, &bulk_packet, NULL, 0); } else { @@ -1298,7 +1299,7 @@ static int usbredir_chardev_can_read(void *opaque) } /* usbredir_parser_do_read will consume *all* data we give it */ - return 1024 * 1024; + return 1 * MiB; } static void usbredir_chardev_read(void *opaque, const uint8_t *buf, int size) diff --git a/hw/usb/tusb6010.c b/hw/usb/tusb6010.c index 2662c060ed..a2128024c1 100644 --- a/hw/usb/tusb6010.c +++ b/hw/usb/tusb6010.c @@ -641,11 +641,43 @@ static void tusb_async_writew(void *opaque, hwaddr addr, } } +static uint64_t tusb_async_readfn(void *opaque, hwaddr addr, unsigned size) +{ + switch (size) { + case 1: + return tusb_async_readb(opaque, addr); + case 2: + return tusb_async_readh(opaque, addr); + case 4: + return tusb_async_readw(opaque, addr); + default: + g_assert_not_reached(); + } +} + +static void tusb_async_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + switch (size) { + case 1: + tusb_async_writeb(opaque, addr, value); + break; + case 2: + tusb_async_writeh(opaque, addr, value); + break; + case 4: + tusb_async_writew(opaque, addr, value); + break; + default: + g_assert_not_reached(); + } +} + static const MemoryRegionOps tusb_async_ops = { - .old_mmio = { - .read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, }, - .write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, }, - }, + .read = tusb_async_readfn, + .write = tusb_async_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 3beeb0d170..5b2e21ed18 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -173,8 +173,9 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_buffer_segs; i++) { ref[i] = usbback_req->req.seg[i].gref; } - usbback_req->buffer = xengnttab_map_domain_grant_refs(xendev->gnttabdev, - usbback_req->nr_buffer_segs, xendev->dom, ref, prot); + usbback_req->buffer = + xen_be_map_grant_refs(xendev, ref, usbback_req->nr_buffer_segs, + prot); if (!usbback_req->buffer) { return -ENOMEM; @@ -206,8 +207,9 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) for (i = 0; i < usbback_req->nr_extra_segs; i++) { ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref; } - usbback_req->isoc_buffer = xengnttab_map_domain_grant_refs( - xendev->gnttabdev, usbback_req->nr_extra_segs, xendev->dom, ref, prot); + usbback_req->isoc_buffer = + xen_be_map_grant_refs(xendev, ref, usbback_req->nr_extra_segs, + prot); if (!usbback_req->isoc_buffer) { return -ENOMEM; @@ -291,14 +293,14 @@ static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, } if (usbback_req->buffer) { - xengnttab_unmap(xendev->gnttabdev, usbback_req->buffer, - usbback_req->nr_buffer_segs); + xen_be_unmap_grant_refs(xendev, usbback_req->buffer, + usbback_req->nr_buffer_segs); usbback_req->buffer = NULL; } if (usbback_req->isoc_buffer) { - xengnttab_unmap(xendev->gnttabdev, usbback_req->isoc_buffer, - usbback_req->nr_extra_segs); + xen_be_unmap_grant_refs(xendev, usbback_req->isoc_buffer, + usbback_req->nr_extra_segs); usbback_req->isoc_buffer = NULL; } @@ -763,7 +765,7 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, if (!usbif->ports[port - 1].dev) { goto err; } - QDECREF(qdict); + qobject_unref(qdict); speed = usbif->ports[port - 1].dev->speed; switch (speed) { case USB_SPEED_LOW: @@ -796,7 +798,7 @@ static void usbback_portid_add(struct usbback_info *usbif, unsigned port, return; err: - QDECREF(qdict); + qobject_unref(qdict); xen_pv_printf(&usbif->xendev, 0, "device %s could not be opened\n", busid); } @@ -834,11 +836,11 @@ static void usbback_disconnect(struct XenDevice *xendev) xen_pv_unbind_evtchn(xendev); if (usbif->urb_sring) { - xengnttab_unmap(xendev->gnttabdev, usbif->urb_sring, 1); + xen_be_unmap_grant_ref(xendev, usbif->urb_sring); usbif->urb_sring = NULL; } if (usbif->conn_sring) { - xengnttab_unmap(xendev->gnttabdev, usbif->conn_sring, 1); + xen_be_unmap_grant_ref(xendev, usbif->conn_sring); usbif->conn_sring = NULL; } @@ -877,12 +879,10 @@ static int usbback_connect(struct XenDevice *xendev) return -1; } - usbif->urb_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, - urb_ring_ref, - PROT_READ | PROT_WRITE); - usbif->conn_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom, - conn_ring_ref, - PROT_READ | PROT_WRITE); + usbif->urb_sring = xen_be_map_grant_ref(xendev, urb_ring_ref, + PROT_READ | PROT_WRITE); + usbif->conn_sring = xen_be_map_grant_ref(xendev, conn_ring_ref, + PROT_READ | PROT_WRITE); if (!usbif->urb_sring || !usbif->conn_sring) { xen_pv_printf(xendev, 0, "error mapping rings\n"); usbback_disconnect(xendev); @@ -1024,10 +1024,7 @@ static void usbback_alloc(struct XenDevice *xendev) /* max_grants: for each request and for the rings (request and connect). */ max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2; - if (xengnttab_set_max_grants(xendev->gnttabdev, max_grants) < 0) { - xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", - strerror(errno)); - } + xen_be_set_max_grant_refs(xendev, max_grants); } static int usbback_free(struct XenDevice *xendev) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index fe34b50769..351b305e1a 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -22,6 +22,7 @@ #include "hw/vfio/vfio-common.h" #include "hw/s390x/s390-ccw.h" #include "hw/s390x/ccw-device.h" +#include "exec/address-spaces.h" #include "qemu/error-report.h" #define TYPE_VFIO_CCW "vfio-ccw" @@ -32,8 +33,30 @@ typedef struct VFIOCCWDevice { uint64_t io_region_offset; struct ccw_io_region *io_region; EventNotifier io_notifier; + bool force_orb_pfch; + bool warned_orb_pfch; } VFIOCCWDevice; +static inline void warn_once(bool *warned, const char *fmt, ...) +{ + va_list ap; + + if (!warned || *warned) { + return; + } + *warned = true; + va_start(ap, fmt); + warn_vreport(fmt, ap); + va_end(ap); +} + +static inline void warn_once_pfch(VFIOCCWDevice *vcdev, SubchDev *sch, + const char *msg) +{ + warn_once(&vcdev->warned_orb_pfch, "vfio-ccw (devno %x.%x.%04x): %s", + sch->cssid, sch->ssid, sch->devno, msg); +} + static void vfio_ccw_compute_needs_reset(VFIODevice *vdev) { vdev->needs_reset = false; @@ -54,6 +77,18 @@ static IOInstEnding vfio_ccw_handle_request(SubchDev *sch) struct ccw_io_region *region = vcdev->io_region; int ret; + if (!(sch->orb.ctrl0 & ORB_CTRL0_MASK_PFCH)) { + if (!(vcdev->force_orb_pfch)) { + warn_once_pfch(vcdev, sch, "requires PFCH flag set"); + sch_gen_unit_exception(sch); + css_inject_io_interrupt(sch); + return IOINST_CC_EXPECTED; + } else { + sch->orb.ctrl0 |= ORB_CTRL0_MASK_PFCH; + warn_once_pfch(vcdev, sch, "PFCH flag forced"); + } + } + QEMU_BUILD_BUG_ON(sizeof(region->orb_area) != sizeof(ORB)); QEMU_BUILD_BUG_ON(sizeof(region->scsw_area) != sizeof(SCSW)); QEMU_BUILD_BUG_ON(sizeof(region->irb_area) != sizeof(IRB)); @@ -292,12 +327,43 @@ static void vfio_ccw_put_region(VFIOCCWDevice *vcdev) g_free(vcdev->io_region); } -static void vfio_put_device(VFIOCCWDevice *vcdev) +static void vfio_ccw_put_device(VFIOCCWDevice *vcdev) { g_free(vcdev->vdev.name); vfio_put_base_device(&vcdev->vdev); } +static void vfio_ccw_get_device(VFIOGroup *group, VFIOCCWDevice *vcdev, + Error **errp) +{ + char *name = g_strdup_printf("%x.%x.%04x", vcdev->cdev.hostid.cssid, + vcdev->cdev.hostid.ssid, + vcdev->cdev.hostid.devid); + VFIODevice *vbasedev; + + QLIST_FOREACH(vbasedev, &group->device_list, next) { + if (strcmp(vbasedev->name, name) == 0) { + error_setg(errp, "vfio: subchannel %s has already been attached", + name); + goto out_err; + } + } + + if (vfio_get_device(group, vcdev->cdev.mdevid, &vcdev->vdev, errp)) { + goto out_err; + } + + vcdev->vdev.ops = &vfio_ccw_ops; + vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW; + vcdev->vdev.name = name; + vcdev->vdev.dev = &vcdev->cdev.parent_obj.parent_obj; + + return; + +out_err: + g_free(name); +} + static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp) { char *tmp, group_path[PATH_MAX]; @@ -327,7 +393,6 @@ static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp) static void vfio_ccw_realize(DeviceState *dev, Error **errp) { - VFIODevice *vbasedev; VFIOGroup *group; CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev); S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev); @@ -348,22 +413,8 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) goto out_group_err; } - vcdev->vdev.ops = &vfio_ccw_ops; - vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW; - vcdev->vdev.name = g_strdup_printf("%x.%x.%04x", cdev->hostid.cssid, - cdev->hostid.ssid, cdev->hostid.devid); - vcdev->vdev.dev = dev; - QLIST_FOREACH(vbasedev, &group->device_list, next) { - if (strcmp(vbasedev->name, vcdev->vdev.name) == 0) { - error_setg(&err, "vfio: subchannel %s has already been attached", - vcdev->vdev.name); - g_free(vcdev->vdev.name); - goto out_device_err; - } - } - - if (vfio_get_device(group, cdev->mdevid, &vcdev->vdev, &err)) { - g_free(vcdev->vdev.name); + vfio_ccw_get_device(group, vcdev, &err); + if (err) { goto out_device_err; } @@ -382,7 +433,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) out_notifier_err: vfio_ccw_put_region(vcdev); out_region_err: - vfio_put_device(vcdev); + vfio_ccw_put_device(vcdev); out_device_err: vfio_put_group(group); out_group_err: @@ -403,7 +454,7 @@ static void vfio_ccw_unrealize(DeviceState *dev, Error **errp) vfio_ccw_unregister_io_notifier(vcdev); vfio_ccw_put_region(vcdev); - vfio_put_device(vcdev); + vfio_ccw_put_device(vcdev); vfio_put_group(group); if (cdc->unrealize) { @@ -413,6 +464,7 @@ static void vfio_ccw_unrealize(DeviceState *dev, Error **errp) static Property vfio_ccw_properties[] = { DEFINE_PROP_STRING("sysfsdev", VFIOCCWDevice, vdev.sysfsdev), + DEFINE_PROP_BOOL("force-orb-pfch", VFIOCCWDevice, force_orb_pfch, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 07ffa0ba10..fb396cf00a 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -324,7 +324,8 @@ static bool vfio_get_vaddr(IOMMUTLBEntry *iotlb, void **vaddr, */ mr = address_space_translate(&address_space_memory, iotlb->translated_addr, - &xlat, &len, writable); + &xlat, &len, writable, + MEMTXATTRS_UNSPECIFIED); if (!memory_region_is_ram(mr)) { error_report("iommu map to non memory area %"HWADDR_PRIx"", xlat); @@ -506,6 +507,7 @@ static void vfio_listener_region_add(MemoryListener *listener, if (memory_region_is_iommu(section->mr)) { VFIOGuestIOMMU *giommu; IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); + int iommu_idx; trace_vfio_listener_region_add_iommu(iova, end); /* @@ -522,10 +524,13 @@ static void vfio_listener_region_add(MemoryListener *listener, llend = int128_add(int128_make64(section->offset_within_region), section->size); llend = int128_sub(llend, int128_one()); + iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, + MEMTXATTRS_UNSPECIFIED); iommu_notifier_init(&giommu->n, vfio_iommu_map_notify, IOMMU_NOTIFIER_ALL, section->offset_within_region, - int128_get64(llend)); + int128_get64(llend), + iommu_idx); QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next); memory_region_register_iommu_notifier(section->mr, &giommu->n); diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 7d727ce910..59c0e5d1d7 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -198,6 +198,17 @@ static void vfio_display_dmabuf_exit(VFIODisplay *dpy) } /* ---------------------------------------------------------------------- */ +void vfio_display_reset(VFIOPCIDevice *vdev) +{ + if (!vdev || !vdev->dpy || !vdev->dpy->con || + !vdev->dpy->dmabuf.primary) { + return; + } + + dpy_gl_scanout_disable(vdev->dpy->con); + vfio_display_dmabuf_exit(vdev->dpy); + dpy_gfx_update_full(vdev->dpy->con); +} static void vfio_display_region_update(void *opaque) { diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index e5779a7ad3..481fd08df7 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -11,10 +11,13 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/error-report.h" +#include "qemu/main-loop.h" #include "qemu/range.h" #include "qapi/error.h" #include "qapi/visitor.h" +#include #include "hw/nvram/fw_cfg.h" #include "pci.h" #include "trace.h" @@ -202,6 +205,7 @@ typedef struct VFIOConfigMirrorQuirk { uint32_t offset; uint8_t bar; MemoryRegion *mem; + uint8_t data[]; } VFIOConfigMirrorQuirk; static uint64_t vfio_generic_quirk_mirror_read(void *opaque, @@ -275,6 +279,136 @@ static const MemoryRegionOps vfio_ati_3c3_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static VFIOQuirk *vfio_quirk_alloc(int nr_mem) +{ + VFIOQuirk *quirk = g_new0(VFIOQuirk, 1); + QLIST_INIT(&quirk->ioeventfds); + quirk->mem = g_new0(MemoryRegion, nr_mem); + quirk->nr_mem = nr_mem; + + return quirk; +} + +static void vfio_ioeventfd_exit(VFIOPCIDevice *vdev, VFIOIOEventFD *ioeventfd) +{ + QLIST_REMOVE(ioeventfd, next); + memory_region_del_eventfd(ioeventfd->mr, ioeventfd->addr, ioeventfd->size, + true, ioeventfd->data, &ioeventfd->e); + + if (ioeventfd->vfio) { + struct vfio_device_ioeventfd vfio_ioeventfd; + + vfio_ioeventfd.argsz = sizeof(vfio_ioeventfd); + vfio_ioeventfd.flags = ioeventfd->size; + vfio_ioeventfd.data = ioeventfd->data; + vfio_ioeventfd.offset = ioeventfd->region->fd_offset + + ioeventfd->region_addr; + vfio_ioeventfd.fd = -1; + + if (ioctl(vdev->vbasedev.fd, VFIO_DEVICE_IOEVENTFD, &vfio_ioeventfd)) { + error_report("Failed to remove vfio ioeventfd for %s+0x%" + HWADDR_PRIx"[%d]:0x%"PRIx64" (%m)", + memory_region_name(ioeventfd->mr), ioeventfd->addr, + ioeventfd->size, ioeventfd->data); + } + } else { + qemu_set_fd_handler(event_notifier_get_fd(&ioeventfd->e), + NULL, NULL, NULL); + } + + event_notifier_cleanup(&ioeventfd->e); + trace_vfio_ioeventfd_exit(memory_region_name(ioeventfd->mr), + (uint64_t)ioeventfd->addr, ioeventfd->size, + ioeventfd->data); + g_free(ioeventfd); +} + +static void vfio_drop_dynamic_eventfds(VFIOPCIDevice *vdev, VFIOQuirk *quirk) +{ + VFIOIOEventFD *ioeventfd, *tmp; + + QLIST_FOREACH_SAFE(ioeventfd, &quirk->ioeventfds, next, tmp) { + if (ioeventfd->dynamic) { + vfio_ioeventfd_exit(vdev, ioeventfd); + } + } +} + +static void vfio_ioeventfd_handler(void *opaque) +{ + VFIOIOEventFD *ioeventfd = opaque; + + if (event_notifier_test_and_clear(&ioeventfd->e)) { + vfio_region_write(ioeventfd->region, ioeventfd->region_addr, + ioeventfd->data, ioeventfd->size); + trace_vfio_ioeventfd_handler(memory_region_name(ioeventfd->mr), + (uint64_t)ioeventfd->addr, ioeventfd->size, + ioeventfd->data); + } +} + +static VFIOIOEventFD *vfio_ioeventfd_init(VFIOPCIDevice *vdev, + MemoryRegion *mr, hwaddr addr, + unsigned size, uint64_t data, + VFIORegion *region, + hwaddr region_addr, bool dynamic) +{ + VFIOIOEventFD *ioeventfd; + + if (vdev->no_kvm_ioeventfd) { + return NULL; + } + + ioeventfd = g_malloc0(sizeof(*ioeventfd)); + + if (event_notifier_init(&ioeventfd->e, 0)) { + g_free(ioeventfd); + return NULL; + } + + /* + * MemoryRegion and relative offset, plus additional ioeventfd setup + * parameters for configuring and later tearing down KVM ioeventfd. + */ + ioeventfd->mr = mr; + ioeventfd->addr = addr; + ioeventfd->size = size; + ioeventfd->data = data; + ioeventfd->dynamic = dynamic; + /* + * VFIORegion and relative offset for implementing the userspace + * handler. data & size fields shared for both uses. + */ + ioeventfd->region = region; + ioeventfd->region_addr = region_addr; + + if (!vdev->no_vfio_ioeventfd) { + struct vfio_device_ioeventfd vfio_ioeventfd; + + vfio_ioeventfd.argsz = sizeof(vfio_ioeventfd); + vfio_ioeventfd.flags = ioeventfd->size; + vfio_ioeventfd.data = ioeventfd->data; + vfio_ioeventfd.offset = ioeventfd->region->fd_offset + + ioeventfd->region_addr; + vfio_ioeventfd.fd = event_notifier_get_fd(&ioeventfd->e); + + ioeventfd->vfio = !ioctl(vdev->vbasedev.fd, + VFIO_DEVICE_IOEVENTFD, &vfio_ioeventfd); + } + + if (!ioeventfd->vfio) { + qemu_set_fd_handler(event_notifier_get_fd(&ioeventfd->e), + vfio_ioeventfd_handler, NULL, ioeventfd); + } + + memory_region_add_eventfd(ioeventfd->mr, ioeventfd->addr, ioeventfd->size, + true, ioeventfd->data, &ioeventfd->e); + trace_vfio_ioeventfd_init(memory_region_name(mr), (uint64_t)addr, + size, data, ioeventfd->vfio); + + return ioeventfd; +} + static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev) { VFIOQuirk *quirk; @@ -288,9 +422,7 @@ static void vfio_vga_probe_ati_3c3_quirk(VFIOPCIDevice *vdev) return; } - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 1); - quirk->nr_mem = 1; + quirk = vfio_quirk_alloc(1); memory_region_init_io(quirk->mem, OBJECT(vdev), &vfio_ati_3c3_quirk, vdev, "vfio-ati-3c3-quirk", 1); @@ -323,9 +455,7 @@ static void vfio_probe_ati_bar4_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 2); - quirk->nr_mem = 2; + quirk = vfio_quirk_alloc(2); window = quirk->data = g_malloc0(sizeof(*window) + sizeof(VFIOConfigWindowMatch)); window->vdev = vdev; @@ -371,10 +501,9 @@ static void vfio_probe_ati_bar2_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); + quirk = vfio_quirk_alloc(1); mirror = quirk->data = g_malloc0(sizeof(*mirror)); - mirror->mem = quirk->mem = g_new0(MemoryRegion, 1); - quirk->nr_mem = 1; + mirror->mem = quirk->mem; mirror->vdev = vdev; mirror->offset = 0x4000; mirror->bar = nr; @@ -548,10 +677,8 @@ static void vfio_vga_probe_nvidia_3d0_quirk(VFIOPCIDevice *vdev) return; } - quirk = g_malloc0(sizeof(*quirk)); + quirk = vfio_quirk_alloc(2); quirk->data = data = g_malloc0(sizeof(*data)); - quirk->mem = g_new0(MemoryRegion, 2); - quirk->nr_mem = 2; data->vdev = vdev; memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_nvidia_3d4_quirk, @@ -667,9 +794,7 @@ static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 4); - quirk->nr_mem = 4; + quirk = vfio_quirk_alloc(4); bar5 = quirk->data = g_malloc0(sizeof(*bar5) + (sizeof(VFIOConfigWindowMatch) * 2)); window = &bar5->window; @@ -719,6 +844,18 @@ static void vfio_probe_nvidia_bar5_quirk(VFIOPCIDevice *vdev, int nr) trace_vfio_quirk_nvidia_bar5_probe(vdev->vbasedev.name); } +typedef struct LastDataSet { + VFIOQuirk *quirk; + hwaddr addr; + uint64_t data; + unsigned size; + int hits; + int added; +} LastDataSet; + +#define MAX_DYN_IOEVENTFD 10 +#define HITS_FOR_IOEVENTFD 10 + /* * Finally, BAR0 itself. We want to redirect any accesses to either * 0x1800 or 0x88000 through the PCI config space access functions. @@ -729,6 +866,7 @@ static void vfio_nvidia_quirk_mirror_write(void *opaque, hwaddr addr, VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; PCIDevice *pdev = &vdev->pdev; + LastDataSet *last = (LastDataSet *)&mirror->data; vfio_generic_quirk_mirror_write(opaque, addr, data, size); @@ -743,6 +881,49 @@ static void vfio_nvidia_quirk_mirror_write(void *opaque, hwaddr addr, addr + mirror->offset, data, size); trace_vfio_quirk_nvidia_bar0_msi_ack(vdev->vbasedev.name); } + + /* + * Automatically add an ioeventfd to handle any repeated write with the + * same data and size above the standard PCI config space header. This is + * primarily expected to accelerate the MSI-ACK behavior, such as noted + * above. Current hardware/drivers should trigger an ioeventfd at config + * offset 0x704 (region offset 0x88704), with data 0x0, size 4. + * + * The criteria of 10 successive hits is arbitrary but reliably adds the + * MSI-ACK region. Note that as some writes are bypassed via the ioeventfd, + * the remaining ones have a greater chance of being seen successively. + * To avoid the pathological case of burning up all of QEMU's open file + * handles, arbitrarily limit this algorithm from adding no more than 10 + * ioeventfds, print an error if we would have added an 11th, and then + * stop counting. + */ + if (!vdev->no_kvm_ioeventfd && + addr >= PCI_STD_HEADER_SIZEOF && last->added <= MAX_DYN_IOEVENTFD) { + if (addr != last->addr || data != last->data || size != last->size) { + last->addr = addr; + last->data = data; + last->size = size; + last->hits = 1; + } else if (++last->hits >= HITS_FOR_IOEVENTFD) { + if (last->added < MAX_DYN_IOEVENTFD) { + VFIOIOEventFD *ioeventfd; + ioeventfd = vfio_ioeventfd_init(vdev, mirror->mem, addr, size, + data, &vdev->bars[mirror->bar].region, + mirror->offset + addr, true); + if (ioeventfd) { + VFIOQuirk *quirk = last->quirk; + + QLIST_INSERT_HEAD(&quirk->ioeventfds, ioeventfd, next); + last->added++; + } + } else { + last->added++; + warn_report("NVIDIA ioeventfd queue full for %s, unable to " + "accelerate 0x%"HWADDR_PRIx", data 0x%"PRIx64", " + "size %u", vdev->vbasedev.name, addr, data, size); + } + } + } } static const MemoryRegionOps vfio_nvidia_mirror_quirk = { @@ -751,10 +932,21 @@ static const MemoryRegionOps vfio_nvidia_mirror_quirk = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static void vfio_nvidia_bar0_quirk_reset(VFIOPCIDevice *vdev, VFIOQuirk *quirk) +{ + VFIOConfigMirrorQuirk *mirror = quirk->data; + LastDataSet *last = (LastDataSet *)&mirror->data; + + last->addr = last->data = last->size = last->hits = last->added = 0; + + vfio_drop_dynamic_eventfds(vdev, quirk); +} + static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr) { VFIOQuirk *quirk; VFIOConfigMirrorQuirk *mirror; + LastDataSet *last; if (vdev->no_geforce_quirks || !vfio_pci_is(vdev, PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID) || @@ -762,13 +954,15 @@ static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); - mirror = quirk->data = g_malloc0(sizeof(*mirror)); - mirror->mem = quirk->mem = g_new0(MemoryRegion, 1); - quirk->nr_mem = 1; + quirk = vfio_quirk_alloc(1); + quirk->reset = vfio_nvidia_bar0_quirk_reset; + mirror = quirk->data = g_malloc0(sizeof(*mirror) + sizeof(LastDataSet)); + mirror->mem = quirk->mem; mirror->vdev = vdev; mirror->offset = 0x88000; mirror->bar = nr; + last = (LastDataSet *)&mirror->data; + last->quirk = quirk; memory_region_init_io(mirror->mem, OBJECT(vdev), &vfio_nvidia_mirror_quirk, mirror, @@ -781,13 +975,15 @@ static void vfio_probe_nvidia_bar0_quirk(VFIOPCIDevice *vdev, int nr) /* The 0x1800 offset mirror only seems to get used by legacy VGA */ if (vdev->vga) { - quirk = g_malloc0(sizeof(*quirk)); - mirror = quirk->data = g_malloc0(sizeof(*mirror)); - mirror->mem = quirk->mem = g_new0(MemoryRegion, 1); - quirk->nr_mem = 1; + quirk = vfio_quirk_alloc(1); + quirk->reset = vfio_nvidia_bar0_quirk_reset; + mirror = quirk->data = g_malloc0(sizeof(*mirror) + sizeof(LastDataSet)); + mirror->mem = quirk->mem; mirror->vdev = vdev; mirror->offset = 0x1800; mirror->bar = nr; + last = (LastDataSet *)&mirror->data; + last->quirk = quirk; memory_region_init_io(mirror->mem, OBJECT(vdev), &vfio_nvidia_mirror_quirk, mirror, @@ -945,9 +1141,7 @@ static void vfio_probe_rtl8168_bar2_quirk(VFIOPCIDevice *vdev, int nr) return; } - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 2); - quirk->nr_mem = 2; + quirk = vfio_quirk_alloc(2); quirk->data = rtl = g_malloc0(sizeof(*rtl)); rtl->vdev = vdev; @@ -1255,9 +1449,9 @@ static int vfio_igd_gtt_max(VFIOPCIDevice *vdev) ggms = 1 << ggms; } - ggms *= 1024 * 1024; + ggms *= MiB; - return (ggms / (4 * 1024)) * (gen < 8 ? 4 : 8); + return (ggms / (4 * KiB)) * (gen < 8 ? 4 : 8); } /* @@ -1507,14 +1701,12 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) } /* Setup our quirk to munge GTT addresses to the VM allocated buffer */ - quirk = g_malloc0(sizeof(*quirk)); - quirk->mem = g_new0(MemoryRegion, 2); - quirk->nr_mem = 2; + quirk = vfio_quirk_alloc(2); igd = quirk->data = g_malloc0(sizeof(*igd)); igd->vdev = vdev; igd->index = ~0; igd->bdsm = vfio_pci_read_config(&vdev->pdev, IGD_BDSM, 4); - igd->bdsm &= ~((1 << 20) - 1); /* 1MB aligned */ + igd->bdsm &= ~((1 * MiB) - 1); /* 1MB aligned */ memory_region_init_io(&quirk->mem[0], OBJECT(vdev), &vfio_igd_index_quirk, igd, "vfio-igd-index-quirk", 4); @@ -1561,7 +1753,7 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * config offset 0x5C. */ bdsm_size = g_malloc(sizeof(*bdsm_size)); - *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * 1024 * 1024); + *bdsm_size = cpu_to_le64((ggms_mb + gms_mb) * MiB); fw_cfg_add_file(fw_cfg_find(), "etc/igd-bdsm-size", bdsm_size, sizeof(*bdsm_size)); @@ -1674,6 +1866,10 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr) int i; QLIST_FOREACH(quirk, &bar->quirks, next) { + while (!QLIST_EMPTY(&quirk->ioeventfds)) { + vfio_ioeventfd_exit(vdev, QLIST_FIRST(&quirk->ioeventfds)); + } + for (i = 0; i < quirk->nr_mem; i++) { memory_region_del_subregion(bar->region.mem, &quirk->mem[i]); } @@ -1700,6 +1896,21 @@ void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr) /* * Reset quirks */ +void vfio_quirk_reset(VFIOPCIDevice *vdev) +{ + int i; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + VFIOQuirk *quirk; + VFIOBAR *bar = &vdev->bars[i]; + + QLIST_FOREACH(quirk, &bar->quirks, next) { + if (quirk->reset) { + quirk->reset(vdev, quirk); + } + } + } +} /* * AMD Radeon PCI config reset, based on Linux: diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b9bc6cd310..6cbb8fa054 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -28,6 +28,7 @@ #include "qemu/error-report.h" #include "qemu/option.h" #include "qemu/range.h" +#include "qemu/units.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" #include "pci.h" @@ -989,7 +990,6 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_SPACE_MEMORY, &vdev->pdev.rom); - vdev->pdev.has_rom = true; vdev->rom_read_failed = false; } @@ -1417,7 +1417,7 @@ static void vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) } /* 2GB max size for 32-bit BARs, cannot double if already > 1G */ - if (vdev->bars[target_bar].size > (1 * 1024 * 1024 * 1024) && + if (vdev->bars[target_bar].size > 1 * GiB && !vdev->bars[target_bar].mem64) { error_setg(errp, "Invalid MSI-X relocation BAR %d, " "no space to extend 32-bit BAR", target_bar); @@ -2207,6 +2207,8 @@ static void vfio_pci_post_reset(VFIOPCIDevice *vdev) vdev->vbasedev.name, nr); } } + + vfio_quirk_reset(vdev); } static bool vfio_pci_host_match(PCIHostDeviceAddress *addr, const char *name) @@ -3103,6 +3105,10 @@ static void vfio_pci_reset(DeviceState *dev) vfio_pci_pre_reset(vdev); + if (vdev->display != ON_OFF_AUTO_OFF) { + vfio_display_reset(vdev); + } + if (vdev->resetfn && !vdev->resetfn(vdev)) { goto post_reset; } @@ -3154,7 +3160,7 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIOPCIDevice, host), DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev), DEFINE_PROP_ON_OFF_AUTO("display", VFIOPCIDevice, - display, ON_OFF_AUTO_AUTO), + display, ON_OFF_AUTO_OFF), DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice, intx.mmap_timeout, 1100), DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features, @@ -3169,6 +3175,10 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_BOOL("x-no-kvm-msix", VFIOPCIDevice, no_kvm_msix, false), DEFINE_PROP_BOOL("x-no-geforce-quirks", VFIOPCIDevice, no_geforce_quirks, false), + DEFINE_PROP_BOOL("x-no-kvm-ioeventfd", VFIOPCIDevice, no_kvm_ioeventfd, + false), + DEFINE_PROP_BOOL("x-no-vfio-ioeventfd", VFIOPCIDevice, no_vfio_ioeventfd, + false), DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, vendor_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, device_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 629c875701..52b065421a 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -24,11 +24,26 @@ struct VFIOPCIDevice; +typedef struct VFIOIOEventFD { + QLIST_ENTRY(VFIOIOEventFD) next; + MemoryRegion *mr; + hwaddr addr; + unsigned size; + uint64_t data; + EventNotifier e; + VFIORegion *region; + hwaddr region_addr; + bool dynamic; /* Added runtime, removed on device reset */ + bool vfio; +} VFIOIOEventFD; + typedef struct VFIOQuirk { QLIST_ENTRY(VFIOQuirk) next; void *data; + QLIST_HEAD(, VFIOIOEventFD) ioeventfds; int nr_mem; MemoryRegion *mem; + void (*reset)(struct VFIOPCIDevice *vdev, struct VFIOQuirk *quirk); } VFIOQuirk; typedef struct VFIOBAR { @@ -148,6 +163,8 @@ typedef struct VFIOPCIDevice { bool no_kvm_msi; bool no_kvm_msix; bool no_geforce_quirks; + bool no_kvm_ioeventfd; + bool no_vfio_ioeventfd; VFIODisplay *dpy; } VFIOPCIDevice; @@ -167,6 +184,7 @@ void vfio_bar_quirk_exit(VFIOPCIDevice *vdev, int nr); void vfio_bar_quirk_finalize(VFIOPCIDevice *vdev, int nr); void vfio_setup_resetfn_quirk(VFIOPCIDevice *vdev); int vfio_add_virt_caps(VFIOPCIDevice *vdev, Error **errp); +void vfio_quirk_reset(VFIOPCIDevice *vdev); extern const PropertyInfo qdev_prop_nv_gpudirect_clique; @@ -176,6 +194,7 @@ int vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, struct vfio_region_info *info, Error **errp); +void vfio_display_reset(VFIOPCIDevice *vdev); int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp); void vfio_display_finalize(VFIOPCIDevice *vdev); diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 5c921c27ba..57c4a0ee2b 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -24,6 +24,7 @@ #include "qemu/range.h" #include "sysemu/sysemu.h" #include "exec/memory.h" +#include "exec/address-spaces.h" #include "qemu/queue.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 20109cb758..d2a74952e3 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -77,6 +77,9 @@ vfio_quirk_ati_bonaire_reset_no_smc(const char *name) "%s" vfio_quirk_ati_bonaire_reset_timeout(const char *name) "%s" vfio_quirk_ati_bonaire_reset_done(const char *name) "%s" vfio_quirk_ati_bonaire_reset(const char *name) "%s" +vfio_ioeventfd_exit(const char *name, uint64_t addr, unsigned size, uint64_t data) "%s+0x%"PRIx64"[%d]:0x%"PRIx64 +vfio_ioeventfd_handler(const char *name, uint64_t addr, unsigned size, uint64_t data) "%s+0x%"PRIx64"[%d] -> 0x%"PRIx64 +vfio_ioeventfd_init(const char *name, uint64_t addr, unsigned size, uint64_t data, bool vfio) "%s+0x%"PRIx64"[%d]:0x%"PRIx64" vfio:%d" vfio_pci_igd_bar4_write(const char *name, uint32_t index, uint32_t data, uint32_t base) "%s [0x%03x] 0x%08x -> 0x%08x" vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB" vfio_pci_igd_opregion_enabled(const char *name) "%s" diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index 765d363c1f..1b2799cfd8 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -1,15 +1,17 @@ ifeq ($(CONFIG_VIRTIO),y) -common-obj-y += virtio-rng.o -common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-y += virtio-bus.o -common-obj-y += virtio-mmio.o +obj-y += virtio.o + +common-obj-$(CONFIG_VIRTIO_RNG) += virtio-rng.o +common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o +common-obj-$(CONFIG_VIRTIO_MMIO) += virtio-mmio.o +obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon.o +obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-crypto.o +obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-pci.o -obj-y += virtio.o virtio-balloon.o obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o -obj-y += virtio-crypto.o -obj-$(CONFIG_VIRTIO_PCI) += virtio-crypto-pci.o endif -common-obj-$(call lnot,$(CONFIG_LINUX)) += vhost-stub.o +common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO),$(CONFIG_LINUX))) += vhost-stub.o common-obj-$(CONFIG_ALL) += vhost-stub.o diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 1422ff03ab..07bcbe9e85 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -6,6 +6,7 @@ vhost_region_add_section(const char *name, uint64_t gpa, uint64_t size, uint64_t vhost_region_add_section_merge(const char *name, uint64_t new_size, uint64_t gpa, uint64_t owr) "%s: size: 0x%"PRIx64 " gpa: 0x%"PRIx64 " owr: 0x%"PRIx64 vhost_region_add_section_aligned(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64 vhost_section(const char *name, int r) "%s:%d" +vhost_iotlb_miss(void *dev, int step) "%p step %d" # hw/virtio/vhost-user.c vhost_user_postcopy_end_entry(void) "" diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c index 2d76cdebdc..049089b5e2 100644 --- a/hw/virtio/vhost-stub.c +++ b/hw/virtio/vhost-stub.c @@ -1,7 +1,17 @@ #include "qemu/osdep.h" #include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" bool vhost_has_free_slot(void) { return true; } + +VhostUserState *vhost_user_init(void) +{ + return NULL; +} + +void vhost_user_cleanup(VhostUserState *user) +{ +} diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 38da8692bb..b041343632 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -11,7 +11,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" #include "hw/virtio/vhost-backend.h" +#include "hw/virtio/virtio.h" #include "hw/virtio/virtio-net.h" #include "chardev/char-fe.h" #include "sysemu/kvm.h" @@ -30,6 +32,7 @@ #define VHOST_MEMORY_MAX_NREGIONS 8 #define VHOST_USER_F_PROTOCOL_FEATURES 30 +#define VHOST_USER_SLAVE_MAX_FDS 8 /* * Maximum size of virtio device config space @@ -47,6 +50,8 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, VHOST_USER_PROTOCOL_F_CONFIG = 9, + VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, + VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, VHOST_USER_PROTOCOL_F_MAX }; @@ -91,6 +96,7 @@ typedef enum VhostUserSlaveRequest { VHOST_USER_SLAVE_NONE = 0, VHOST_USER_SLAVE_IOTLB_MSG = 1, VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2, + VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3, VHOST_USER_SLAVE_MAX } VhostUserSlaveRequest; @@ -135,6 +141,12 @@ static VhostUserConfig c __attribute__ ((unused)); + sizeof(c.size) \ + sizeof(c.flags)) +typedef struct VhostUserVringArea { + uint64_t u64; + uint64_t size; + uint64_t offset; +} VhostUserVringArea; + typedef struct { VhostUserRequest request; @@ -156,6 +168,7 @@ typedef union { struct vhost_iotlb_msg iotlb; VhostUserConfig config; VhostUserCryptoSession session; + VhostUserVringArea area; } VhostUserPayload; typedef struct VhostUserMsg { @@ -173,7 +186,8 @@ static VhostUserMsg m __attribute__ ((unused)); struct vhost_user { struct vhost_dev *dev; - CharBackend *chr; + /* Shared between vhost devs of the same virtio device */ + VhostUserState *user; int slave_fd; NotifierWithReturn postcopy_notifier; struct PostCopyFD postcopy_fd; @@ -199,7 +213,7 @@ static bool ioeventfd_enabled(void) static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) { struct vhost_user *u = dev->opaque; - CharBackend *chr = u->chr; + CharBackend *chr = u->user->chr; uint8_t *p = (uint8_t *) msg; int r, size = VHOST_USER_HDR_SIZE; @@ -285,7 +299,7 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, int *fds, int fd_num) { struct vhost_user *u = dev->opaque; - CharBackend *chr = u->chr; + CharBackend *chr = u->user->chr; int ret, size = VHOST_USER_HDR_SIZE + msg->hdr.size; /* @@ -636,9 +650,37 @@ static int vhost_user_set_vring_num(struct vhost_dev *dev, return vhost_set_vring(dev, VHOST_USER_SET_VRING_NUM, ring); } +static void vhost_user_host_notifier_restore(struct vhost_dev *dev, + int queue_idx) +{ + struct vhost_user *u = dev->opaque; + VhostUserHostNotifier *n = &u->user->notifier[queue_idx]; + VirtIODevice *vdev = dev->vdev; + + if (n->addr && !n->set) { + virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true); + n->set = true; + } +} + +static void vhost_user_host_notifier_remove(struct vhost_dev *dev, + int queue_idx) +{ + struct vhost_user *u = dev->opaque; + VhostUserHostNotifier *n = &u->user->notifier[queue_idx]; + VirtIODevice *vdev = dev->vdev; + + if (n->addr && n->set) { + virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false); + n->set = false; + } +} + static int vhost_user_set_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { + vhost_user_host_notifier_restore(dev, ring->index); + return vhost_set_vring(dev, VHOST_USER_SET_VRING_BASE, ring); } @@ -672,6 +714,8 @@ static int vhost_user_get_vring_base(struct vhost_dev *dev, .hdr.size = sizeof(msg.payload.state), }; + vhost_user_host_notifier_remove(dev, ring->index); + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { return -1; } @@ -845,6 +889,66 @@ static int vhost_user_slave_handle_config_change(struct vhost_dev *dev) return ret; } +static int vhost_user_slave_handle_vring_host_notifier(struct vhost_dev *dev, + VhostUserVringArea *area, + int fd) +{ + int queue_idx = area->u64 & VHOST_USER_VRING_IDX_MASK; + size_t page_size = qemu_real_host_page_size; + struct vhost_user *u = dev->opaque; + VhostUserState *user = u->user; + VirtIODevice *vdev = dev->vdev; + VhostUserHostNotifier *n; + void *addr; + char *name; + + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_HOST_NOTIFIER) || + vdev == NULL || queue_idx >= virtio_get_num_queues(vdev)) { + return -1; + } + + n = &user->notifier[queue_idx]; + + if (n->addr) { + virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, false); + object_unparent(OBJECT(&n->mr)); + munmap(n->addr, page_size); + n->addr = NULL; + } + + if (area->u64 & VHOST_USER_VRING_NOFD_MASK) { + return 0; + } + + /* Sanity check. */ + if (area->size != page_size) { + return -1; + } + + addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, area->offset); + if (addr == MAP_FAILED) { + return -1; + } + + name = g_strdup_printf("vhost-user/host-notifier@%p mmaps[%d]", + user, queue_idx); + memory_region_init_ram_device_ptr(&n->mr, OBJECT(vdev), name, + page_size, addr); + g_free(name); + + if (virtio_queue_set_host_notifier_mr(vdev, queue_idx, &n->mr, true)) { + munmap(addr, page_size); + return -1; + } + + n->addr = addr; + n->set = true; + + return 0; +} + static void slave_read(void *opaque) { struct vhost_dev *dev = opaque; @@ -852,14 +956,46 @@ static void slave_read(void *opaque) VhostUserHeader hdr = { 0, }; VhostUserPayload payload = { 0, }; int size, ret = 0; + struct iovec iov; + struct msghdr msgh; + int fd[VHOST_USER_SLAVE_MAX_FDS]; + char control[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; + int i, fdsize = 0; + + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + msgh.msg_control = control; + msgh.msg_controllen = sizeof(control); + + memset(fd, -1, sizeof(fd)); /* Read header */ - size = read(u->slave_fd, &hdr, VHOST_USER_HDR_SIZE); + iov.iov_base = &hdr; + iov.iov_len = VHOST_USER_HDR_SIZE; + + size = recvmsg(u->slave_fd, &msgh, 0); if (size != VHOST_USER_HDR_SIZE) { error_report("Failed to read from slave."); goto err; } + if (msgh.msg_flags & MSG_CTRUNC) { + error_report("Truncated message."); + goto err; + } + + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fdsize = cmsg->cmsg_len - CMSG_LEN(0); + memcpy(fd, CMSG_DATA(cmsg), fdsize); + break; + } + } + if (hdr.size > VHOST_USER_PAYLOAD_SIZE) { error_report("Failed to read msg header." " Size %d exceeds the maximum %zu.", hdr.size, @@ -881,11 +1017,22 @@ static void slave_read(void *opaque) case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG : ret = vhost_user_slave_handle_config_change(dev); break; + case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG: + ret = vhost_user_slave_handle_vring_host_notifier(dev, &payload.area, + fd[0]); + break; default: error_report("Received unexpected msg type."); ret = -EINVAL; } + /* Close the remaining file descriptors. */ + for (i = 0; i < fdsize; i++) { + if (fd[i] != -1) { + close(fd[i]); + } + } + /* * REPLY_ACK feature handling. Other reply types has to be managed * directly in their request handlers. @@ -918,6 +1065,11 @@ err: qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); close(u->slave_fd); u->slave_fd = -1; + for (i = 0; i < fdsize; i++) { + if (fd[i] != -1) { + close(fd[i]); + } + } return; } @@ -1044,7 +1196,7 @@ static int vhost_user_postcopy_waker(struct PostCopyFD *pcfd, RAMBlock *rb, static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) { struct vhost_user *u = dev->opaque; - CharBackend *chr = u->chr; + CharBackend *chr = u->user->chr; int ufd; VhostUserMsg msg = { .hdr.request = VHOST_USER_POSTCOPY_ADVISE, @@ -1076,7 +1228,7 @@ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) error_setg(errp, "%s: Failed to get ufd", __func__); return -1; } - fcntl(ufd, F_SETFL, O_NONBLOCK); + qemu_set_nonblock(ufd); /* register ufd with userfault thread */ u->postcopy_fd.fd = ufd; @@ -1182,7 +1334,7 @@ static int vhost_user_postcopy_notifier(NotifierWithReturn *notifier, return 0; } -static int vhost_user_init(struct vhost_dev *dev, void *opaque) +static int vhost_user_backend_init(struct vhost_dev *dev, void *opaque) { uint64_t features, protocol_features; struct vhost_user *u; @@ -1191,7 +1343,7 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); u = g_new0(struct vhost_user, 1); - u->chr = opaque; + u->user = opaque; u->slave_fd = -1; u->dev = dev; dev->opaque = u; @@ -1267,7 +1419,7 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) return 0; } -static int vhost_user_cleanup(struct vhost_dev *dev) +static int vhost_user_backend_cleanup(struct vhost_dev *dev) { struct vhost_user *u; @@ -1316,7 +1468,7 @@ static bool vhost_user_requires_shm_log(struct vhost_dev *dev) static int vhost_user_migration_done(struct vhost_dev *dev, char* mac_addr) { - VhostUserMsg msg = { 0 }; + VhostUserMsg msg = { }; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); @@ -1581,10 +1733,40 @@ vhost_user_crypto_close_session(struct vhost_dev *dev, uint64_t session_id) return 0; } +static bool vhost_user_mem_section_filter(struct vhost_dev *dev, + MemoryRegionSection *section) +{ + bool result; + + result = memory_region_get_fd(section->mr) >= 0; + + return result; +} + +VhostUserState *vhost_user_init(void) +{ + VhostUserState *user = g_new0(struct VhostUserState, 1); + + return user; +} + +void vhost_user_cleanup(VhostUserState *user) +{ + int i; + + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + if (user->notifier[i].addr) { + object_unparent(OBJECT(&user->notifier[i].mr)); + munmap(user->notifier[i].addr, qemu_real_host_page_size); + user->notifier[i].addr = NULL; + } + } +} + const VhostOps user_ops = { .backend_type = VHOST_BACKEND_TYPE_USER, - .vhost_backend_init = vhost_user_init, - .vhost_backend_cleanup = vhost_user_cleanup, + .vhost_backend_init = vhost_user_backend_init, + .vhost_backend_cleanup = vhost_user_backend_cleanup, .vhost_backend_memslots_limit = vhost_user_memslots_limit, .vhost_set_log_base = vhost_user_set_log_base, .vhost_set_mem_table = vhost_user_set_mem_table, @@ -1611,4 +1793,5 @@ const VhostOps user_ops = { .vhost_set_config = vhost_user_set_config, .vhost_crypto_create_session = vhost_user_crypto_create_session, .vhost_crypto_close_session = vhost_user_crypto_close_session, + .vhost_backend_mem_section_filter = vhost_user_mem_section_filter, }; diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 9d5850a7d7..d4cb5894a8 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -386,7 +386,7 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, return r; } -static bool vhost_section(MemoryRegionSection *section) +static bool vhost_section(struct vhost_dev *dev, MemoryRegionSection *section) { bool result; bool log_dirty = memory_region_get_dirty_log_mask(section->mr) & @@ -399,6 +399,11 @@ static bool vhost_section(MemoryRegionSection *section) */ result &= !log_dirty; + if (result && dev->vhost_ops->vhost_backend_mem_section_filter) { + result &= + dev->vhost_ops->vhost_backend_mem_section_filter(dev, section); + } + trace_vhost_section(section->mr->name, result); return result; } @@ -632,7 +637,7 @@ static void vhost_region_addnop(MemoryListener *listener, struct vhost_dev *dev = container_of(listener, struct vhost_dev, memory_listener); - if (!vhost_section(section)) { + if (!vhost_section(dev, section)) { return; } vhost_region_add_section(dev, section); @@ -657,19 +662,26 @@ static void vhost_iommu_region_add(MemoryListener *listener, iommu_listener); struct vhost_iommu *iommu; Int128 end; + int iommu_idx; + IOMMUMemoryRegion *iommu_mr; if (!memory_region_is_iommu(section->mr)) { return; } + iommu_mr = IOMMU_MEMORY_REGION(section->mr); + iommu = g_malloc0(sizeof(*iommu)); end = int128_add(int128_make64(section->offset_within_region), section->size); end = int128_sub(end, int128_one()); + iommu_idx = memory_region_iommu_attrs_to_index(iommu_mr, + MEMTXATTRS_UNSPECIFIED); iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify, IOMMU_NOTIFIER_UNMAP, section->offset_within_region, - int128_get64(end)); + int128_get64(end), + iommu_idx); iommu->mr = section->mr; iommu->iommu_offset = section->offset_within_address_space - section->offset_within_region; @@ -894,12 +906,16 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) rcu_read_lock(); + trace_vhost_iotlb_miss(dev, 1); + iotlb = address_space_get_iotlb_entry(dev->vdev->dma_as, - iova, write); + iova, write, + MEMTXATTRS_UNSPECIFIED); if (iotlb.target_as != NULL) { ret = vhost_memory_region_lookup(dev, iotlb.translated_addr, &uaddr, &len); if (ret) { + trace_vhost_iotlb_miss(dev, 3); error_report("Fail to lookup the translated address " "%"PRIx64, iotlb.translated_addr); goto out; @@ -911,10 +927,14 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write) ret = vhost_backend_update_device_iotlb(dev, iova, uaddr, len, iotlb.perm); if (ret) { + trace_vhost_iotlb_miss(dev, 4); error_report("Fail to update device iotlb"); goto out; } } + + trace_vhost_iotlb_miss(dev, 2); + out: rcu_read_unlock(); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index f456cea2e7..1f7a87f094 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -52,6 +52,8 @@ static const char *balloon_stat_names[] = { [VIRTIO_BALLOON_S_MEMTOT] = "stat-total-memory", [VIRTIO_BALLOON_S_AVAIL] = "stat-available-memory", [VIRTIO_BALLOON_S_CACHES] = "stat-disk-caches", + [VIRTIO_BALLOON_S_HTLB_PGALLOC] = "stat-htlb-pgalloc", + [VIRTIO_BALLOON_S_HTLB_PGFAIL] = "stat-htlb-pgfail", [VIRTIO_BALLOON_S_NR] = NULL }; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 1e8ab7bbc5..3a01fe90f0 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -32,7 +32,6 @@ #include "hw/pci/msix.h" #include "hw/loader.h" #include "sysemu/kvm.h" -#include "sysemu/block-backend.h" #include "virtio-pci.h" #include "qemu/range.h" #include "hw/virtio/virtio-bus.h" @@ -1037,6 +1036,27 @@ assign_error: return r; } +static int virtio_pci_set_host_notifier_mr(DeviceState *d, int n, + MemoryRegion *mr, bool assign) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + int offset; + + if (n >= VIRTIO_QUEUE_MAX || !virtio_pci_modern(proxy) || + virtio_pci_queue_mem_mult(proxy) != memory_region_size(mr)) { + return -1; + } + + if (assign) { + offset = virtio_pci_queue_mem_mult(proxy) * n; + memory_region_add_subregion_overlap(&proxy->notify.mr, offset, mr, 1); + } else { + memory_region_del_subregion(&proxy->notify.mr, mr); + } + + return 0; +} + static void virtio_pci_vmstate_change(DeviceState *d, bool running) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); @@ -2652,6 +2672,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->has_extra_state = virtio_pci_has_extra_state; k->query_guest_notifiers = virtio_pci_query_guest_notifiers; k->set_guest_notifiers = virtio_pci_set_guest_notifiers; + k->set_host_notifier_mr = virtio_pci_set_host_notifier_mr; k->vmstate_change = virtio_pci_vmstate_change; k->pre_plugged = virtio_pci_pre_plugged; k->device_plugged = virtio_pci_device_plugged; diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 289bbcac03..855f1b41d1 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -156,6 +156,19 @@ static void check_rate_limit(void *opaque) vrng->activate_timer = true; } +static void virtio_rng_set_status(VirtIODevice *vdev, uint8_t status) +{ + VirtIORNG *vrng = VIRTIO_RNG(vdev); + + if (!vdev->vm_running) { + return; + } + vdev->status = status; + + /* Something changed, try to process buffers */ + virtio_rng_process(vrng); +} + static void virtio_rng_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); @@ -261,6 +274,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) vdc->realize = virtio_rng_device_realize; vdc->unrealize = virtio_rng_device_unrealize; vdc->get_features = get_features; + vdc->set_status = virtio_rng_set_status; } static const TypeInfo virtio_rng_info = { diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 006d3d1148..d4e4d98b59 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -123,11 +123,22 @@ static void virtio_free_region_cache(VRingMemoryRegionCaches *caches) g_free(caches); } +static void virtio_virtqueue_reset_region_cache(struct VirtQueue *vq) +{ + VRingMemoryRegionCaches *caches; + + caches = atomic_read(&vq->vring.caches); + atomic_rcu_set(&vq->vring.caches, NULL); + if (caches) { + call_rcu(caches, virtio_free_region_cache, rcu); + } +} + static void virtio_init_region_cache(VirtIODevice *vdev, int n) { VirtQueue *vq = &vdev->vq[n]; VRingMemoryRegionCaches *old = vq->vring.caches; - VRingMemoryRegionCaches *new; + VRingMemoryRegionCaches *new = NULL; hwaddr addr, size; int event_size; int64_t len; @@ -136,7 +147,7 @@ static void virtio_init_region_cache(VirtIODevice *vdev, int n) addr = vq->vring.desc; if (!addr) { - return; + goto out_no_cache; } new = g_new0(VRingMemoryRegionCaches, 1); size = virtio_queue_get_desc_size(vdev, n); @@ -170,11 +181,14 @@ static void virtio_init_region_cache(VirtIODevice *vdev, int n) return; err_avail: - address_space_cache_destroy(&new->used); + address_space_cache_destroy(&new->avail); err_used: - address_space_cache_destroy(&new->desc); + address_space_cache_destroy(&new->used); err_desc: + address_space_cache_destroy(&new->desc); +out_no_cache: g_free(new); + virtio_virtqueue_reset_region_cache(vq); } /* virt queue functions */ @@ -1168,17 +1182,6 @@ static enum virtio_device_endian virtio_current_cpu_endian(void) } } -static void virtio_virtqueue_reset_region_cache(struct VirtQueue *vq) -{ - VRingMemoryRegionCaches *caches; - - caches = atomic_read(&vq->vring.caches); - atomic_rcu_set(&vq->vring.caches, NULL); - if (caches) { - call_rcu(caches, virtio_free_region_cache, rcu); - } -} - void virtio_reset(void *opaque) { VirtIODevice *vdev = opaque; @@ -2454,6 +2457,19 @@ EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) return &vq->host_notifier; } +int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, + MemoryRegion *mr, bool assign) +{ + BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + + if (k->set_host_notifier_mr) { + return k->set_host_notifier_mr(qbus->parent, n, mr, assign); + } + + return -1; +} + void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name) { g_free(vdev->bus_name); diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index e596b0804d..7b59469888 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -361,19 +361,43 @@ static void i6300esb_mem_writel(void *vp, hwaddr addr, uint32_t val) } } +static uint64_t i6300esb_mem_readfn(void *opaque, hwaddr addr, unsigned size) +{ + switch (size) { + case 1: + return i6300esb_mem_readb(opaque, addr); + case 2: + return i6300esb_mem_readw(opaque, addr); + case 4: + return i6300esb_mem_readl(opaque, addr); + default: + g_assert_not_reached(); + } +} + +static void i6300esb_mem_writefn(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + switch (size) { + case 1: + i6300esb_mem_writeb(opaque, addr, value); + break; + case 2: + i6300esb_mem_writew(opaque, addr, value); + break; + case 4: + i6300esb_mem_writel(opaque, addr, value); + break; + default: + g_assert_not_reached(); + } +} + static const MemoryRegionOps i6300esb_ops = { - .old_mmio = { - .read = { - i6300esb_mem_readb, - i6300esb_mem_readw, - i6300esb_mem_readl, - }, - .write = { - i6300esb_mem_writeb, - i6300esb_mem_writew, - i6300esb_mem_writel, - }, - }, + .read = i6300esb_mem_readfn, + .write = i6300esb_mem_writefn, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_LITTLE_ENDIAN, }; diff --git a/hw/xen/xen-common.c b/hw/xen/xen-common.c index 83099dd1b1..6ec14c73ca 100644 --- a/hw/xen/xen-common.c +++ b/hw/xen/xen-common.c @@ -101,7 +101,12 @@ static void xenstore_record_dm_state(struct xs_handle *xs, const char *state) } snprintf(path, sizeof (path), "device-model/%u/state", xen_domid); - if (!xs_write(xs, XBT_NULL, path, state, strlen(state))) { + /* + * This call may fail when running restricted so don't make it fatal in + * that case. Toolstacks should instead use QMP to listen for state changes. + */ + if (!xs_write(xs, XBT_NULL, path, state, strlen(state)) && + !xen_domid_restrict) { error_report("error recording dm state"); exit(1); } @@ -117,6 +122,19 @@ static void xen_change_state_handler(void *opaque, int running, } } +static void xen_setup_post(MachineState *ms, AccelState *accel) +{ + int rc; + + if (xen_domid_restrict) { + rc = xen_restrict(xen_domid); + if (rc < 0) { + perror("xen: failed to restrict"); + exit(1); + } + } +} + static int xen_init(MachineState *ms) { xen_xc = xc_interface_open(0, 0, 0); @@ -165,6 +183,7 @@ static void xen_accel_class_init(ObjectClass *oc, void *data) AccelClass *ac = ACCEL_CLASS(oc); ac->name = "Xen"; ac->init_machine = xen_init; + ac->setup_post = xen_setup_post; ac->allowed = &xen_allowed; ac->global_props = xen_compat_props; } diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index 7445b506ac..9a8e8771ec 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -44,9 +44,9 @@ BusState *xen_sysbus; /* public */ struct xs_handle *xenstore = NULL; const char *xen_protocol; -bool xen_feature_grant_copy; /* private */ +static bool xen_feature_grant_copy; static int debug; int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) @@ -106,6 +106,156 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) return 0; } +void xen_be_set_max_grant_refs(struct XenDevice *xendev, + unsigned int nr_refs) +{ + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (xengnttab_set_max_grants(xendev->gnttabdev, nr_refs)) { + xen_pv_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n", + strerror(errno)); + } +} + +void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, + unsigned int nr_refs, int prot) +{ + void *ptr; + + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + ptr = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_refs, + xen_domid, refs, prot); + if (!ptr) { + xen_pv_printf(xendev, 0, + "xengnttab_map_domain_grant_refs failed: %s\n", + strerror(errno)); + } + + return ptr; +} + +void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, + unsigned int nr_refs) +{ + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (xengnttab_unmap(xendev->gnttabdev, ptr, nr_refs)) { + xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", + strerror(errno)); + } +} + +static int compat_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, + XenGrantCopySegment segs[], + unsigned int nr_segs) +{ + uint32_t *refs = g_new(uint32_t, nr_segs); + int prot = to_domain ? PROT_WRITE : PROT_READ; + void *pages; + unsigned int i; + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + + refs[i] = to_domain ? + seg->dest.foreign.ref : seg->source.foreign.ref; + } + + pages = xengnttab_map_domain_grant_refs(xendev->gnttabdev, nr_segs, + xen_domid, refs, prot); + if (!pages) { + xen_pv_printf(xendev, 0, + "xengnttab_map_domain_grant_refs failed: %s\n", + strerror(errno)); + g_free(refs); + return -1; + } + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + void *page = pages + (i * XC_PAGE_SIZE); + + if (to_domain) { + memcpy(page + seg->dest.foreign.offset, seg->source.virt, + seg->len); + } else { + memcpy(seg->dest.virt, page + seg->source.foreign.offset, + seg->len); + } + } + + if (xengnttab_unmap(xendev->gnttabdev, pages, nr_segs)) { + xen_pv_printf(xendev, 0, "xengnttab_unmap failed: %s\n", + strerror(errno)); + } + + g_free(refs); + return 0; +} + +int xen_be_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, + XenGrantCopySegment segs[], + unsigned int nr_segs) +{ + xengnttab_grant_copy_segment_t *xengnttab_segs; + unsigned int i; + int rc; + + assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); + + if (!xen_feature_grant_copy) { + return compat_copy_grant_refs(xendev, to_domain, segs, nr_segs); + } + + xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); + + for (i = 0; i < nr_segs; i++) { + XenGrantCopySegment *seg = &segs[i]; + xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; + + if (to_domain) { + xengnttab_seg->flags = GNTCOPY_dest_gref; + xengnttab_seg->dest.foreign.domid = xen_domid; + xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; + xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; + xengnttab_seg->source.virt = seg->source.virt; + } else { + xengnttab_seg->flags = GNTCOPY_source_gref; + xengnttab_seg->source.foreign.domid = xen_domid; + xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; + xengnttab_seg->source.foreign.offset = + seg->source.foreign.offset; + xengnttab_seg->dest.virt = seg->dest.virt; + } + + xengnttab_seg->len = seg->len; + } + + rc = xengnttab_grant_copy(xendev->gnttabdev, nr_segs, xengnttab_segs); + + if (rc) { + xen_pv_printf(xendev, 0, "xengnttab_copy failed: %s\n", + strerror(errno)); + } + + for (i = 0; i < nr_segs; i++) { + xengnttab_grant_copy_segment_t *xengnttab_seg = + &xengnttab_segs[i]; + + if (xengnttab_seg->status != GNTST_okay) { + xen_pv_printf(xendev, 0, "segment[%u] status: %d\n", i, + xengnttab_seg->status); + rc = -1; + } + } + + g_free(xengnttab_segs); + return rc; +} + /* * get xen backend device, allocate a new one if it doesn't exist. */ @@ -149,18 +299,6 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, } qemu_set_cloexec(xenevtchn_fd(xendev->evtchndev)); - if (ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { - xendev->gnttabdev = xengnttab_open(NULL, 0); - if (xendev->gnttabdev == NULL) { - xen_pv_printf(NULL, 0, "can't open gnttab device\n"); - xenevtchn_close(xendev->evtchndev); - qdev_unplug(DEVICE(xendev), NULL); - return NULL; - } - } else { - xendev->gnttabdev = NULL; - } - xen_pv_insert_xendev(xendev); if (xendev->ops->alloc) { @@ -322,6 +460,16 @@ static int xen_be_try_initialise(struct XenDevice *xendev) } } + if (xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV) { + xendev->gnttabdev = xengnttab_open(NULL, 0); + if (xendev->gnttabdev == NULL) { + xen_pv_printf(NULL, 0, "can't open gnttab device\n"); + return -1; + } + } else { + xendev->gnttabdev = NULL; + } + if (xendev->ops->initialise) { rc = xendev->ops->initialise(xendev); } @@ -369,6 +517,10 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) xendev->ops->disconnect) { xendev->ops->disconnect(xendev); } + if (xendev->gnttabdev) { + xengnttab_close(xendev->gnttabdev); + xendev->gnttabdev = NULL; + } if (xendev->be_state != state) { xen_be_set_state(xendev, state); } diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c index fac9d3fcdc..aebc19bd71 100644 --- a/hw/xen/xen_devconfig.c +++ b/hw/xen/xen_devconfig.c @@ -1,7 +1,6 @@ #include "qemu/osdep.h" #include "hw/xen/xen_backend.h" #include "qemu/option.h" -#include "sysemu/block-backend.h" #include "sysemu/blockdev.h" /* ------------------------------------------------------------- */ diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 9b7a960de1..e5a6eff44f 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -907,7 +907,7 @@ out: } } - memory_listener_register(&s->memory_listener, &s->dev.bus_master_as); + memory_listener_register(&s->memory_listener, &address_space_memory); memory_listener_register(&s->io_listener, &address_space_io); s->listener_set = true; XEN_PT_LOG(d, diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index aa39a9aa5f..dbee3308fd 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -319,7 +319,7 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) } extern void *pci_assign_dev_load_option_rom(PCIDevice *dev, - struct Object *owner, int *size, + int *size, unsigned int domain, unsigned int bus, unsigned int slot, unsigned int function); diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index a3ce33e78b..aee31c62bb 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -504,6 +504,8 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, bar_ro_mask = XEN_PT_BAR_IO_RO_MASK | (r_size - 1); break; case XEN_PT_BAR_FLAG_UPPER: + assert(index > 0); + r_size = d->io_regions[index - 1].size >> 32; bar_emu_mask = XEN_PT_BAR_ALLF; bar_ro_mask = r_size ? r_size - 1 : 0; break; diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index 0f4c8d77e2..135c8df1e7 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -132,7 +132,7 @@ int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) static void *get_vgabios(XenPCIPassthroughState *s, int *size, XenHostPCIDevice *dev) { - return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size, + return pci_assign_dev_load_option_rom(&s->dev, size, dev->domain, dev->bus, dev->dev, dev->func); } diff --git a/hw/xen/xen_pt_load_rom.c b/hw/xen/xen_pt_load_rom.c index 71063c4d79..e6a86ca818 100644 --- a/hw/xen/xen_pt_load_rom.c +++ b/hw/xen/xen_pt_load_rom.c @@ -19,7 +19,7 @@ * load the corresponding ROM data to RAM. If an error occurs while loading an * option ROM, we just ignore that option ROM and continue with the next one. */ -void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, +void *pci_assign_dev_load_option_rom(PCIDevice *dev, int *size, unsigned int domain, unsigned int bus, unsigned int slot, unsigned int function) @@ -29,6 +29,7 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, uint8_t val; struct stat st; void *ptr = NULL; + Object *owner = OBJECT(dev); /* If loading ROM from file, pci handles it */ if (dev->romfile || !dev->rom_bar) { @@ -59,8 +60,7 @@ void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner, fseek(fp, 0, SEEK_SET); snprintf(name, sizeof(name), "%s.rom", object_get_typename(owner)); - memory_region_init_ram_nomigrate(&dev->rom, owner, name, st.st_size, &error_abort); - vmstate_register_ram(&dev->rom, &dev->qdev); + memory_region_init_ram(&dev->rom, owner, name, st.st_size, &error_abort); ptr = memory_region_get_ram_ptr(&dev->rom); memset(ptr, 0xff, st.st_size); diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index 6d1e3bdeb4..cc514f9157 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -498,7 +498,8 @@ static uint64_t pci_msix_read(void *opaque, hwaddr addr, } static bool pci_msix_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return !(addr & (size - 1)); } diff --git a/hw/xenpv/xen_domainbuild.c b/hw/xenpv/xen_domainbuild.c index 027f76fad1..188acaca16 100644 --- a/hw/xenpv/xen_domainbuild.c +++ b/hw/xenpv/xen_domainbuild.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/xen/xen_backend.h" #include "xen_domainbuild.h" #include "qemu/timer.h" @@ -75,9 +76,9 @@ int xenstore_domain_init1(const char *kernel, const char *ramdisk, xenstore_write_str(dom, "vm", vm); /* memory */ - xenstore_write_int(dom, "memory/target", ram_size >> 10); // kB - xenstore_write_int(vm, "memory", ram_size >> 20); // MB - xenstore_write_int(vm, "maxmem", ram_size >> 20); // MB + xenstore_write_int(dom, "memory/target", ram_size / KiB); + xenstore_write_int(vm, "memory", ram_size / MiB); + xenstore_write_int(vm, "maxmem", ram_size / MiB); /* cpus */ for (i = 0; i < smp_cpus; i++) { @@ -113,7 +114,7 @@ int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, /* console */ xenstore_write_str(dom, "console/type", "ioemu"); - xenstore_write_int(dom, "console/limit", 128 * 1024); + xenstore_write_int(dom, "console/limit", 128 * KiB); xenstore_write_int(dom, "console/ring-ref", console_mfn); xenstore_write_int(dom, "console/port", console_port); xen_config_dev_console(0); @@ -260,7 +261,7 @@ int xen_domain_build_pv(const char *kernel, const char *ramdisk, } #endif - rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size >> 10); + rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size / KiB); if (rc < 0) { fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n"); goto err; @@ -269,7 +270,7 @@ int xen_domain_build_pv(const char *kernel, const char *ramdisk, xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); - rc = xc_linux_build(xen_xc, xen_domid, ram_size >> 20, + rc = xc_linux_build(xen_xc, xen_domid, ram_size / MiB, kernel, ramdisk, cmdline, 0, flags, xenstore_port, &xenstore_mfn, diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 5c0ba231d1..b6ccb3cd4a 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -90,8 +90,8 @@ static void xtensa_sim_init(MachineState *machine) get_system_memory()); } - if (serial_hds[0]) { - xtensa_sim_open_console(serial_hds[0]); + if (serial_hd(0)) { + xtensa_sim_open_console(serial_hd(0)); } if (kernel_filename) { uint64_t elf_entry; diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 70686a2eb1..b3161de320 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "cpu.h" #include "sysemu/sysemu.h" @@ -38,7 +39,6 @@ #include "net/net.h" #include "hw/sysbus.h" #include "hw/block/flash.h" -#include "sysemu/block-backend.h" #include "chardev/char.h" #include "sysemu/device_tree.h" #include "qemu/error-report.h" @@ -153,7 +153,7 @@ static void xtfpga_net_init(MemoryRegion *address_space, sysbus_mmio_get_region(s, 1)); ram = g_malloc(sizeof(*ram)); - memory_region_init_ram_nomigrate(ram, OBJECT(s), "open_eth.ram", 16384, + memory_region_init_ram_nomigrate(ram, OBJECT(s), "open_eth.ram", 16 * KiB, &error_fatal); vmstate_register_ram_global(ram); memory_region_add_subregion(address_space, buffers, ram); @@ -230,7 +230,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) const char *kernel_cmdline = qemu_opt_get(machine_opts, "append"); const char *dtb_filename = qemu_opt_get(machine_opts, "dtb"); const char *initrd_filename = qemu_opt_get(machine_opts, "initrd"); - const unsigned system_io_size = 224 * 1024 * 1024; + const unsigned system_io_size = 224 * MiB; int n; for (n = 0; n < smp_cpus; n++) { @@ -278,12 +278,8 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) xtensa_get_extint(env, 1), nd_table); } - if (!serial_hds[0]) { - serial_hds[0] = qemu_chr_new("serial0", "null"); - } - serial_mm_init(system_io, 0x0d050020, 2, xtensa_get_extint(env, 0), - 115200, serial_hds[0], DEVICE_NATIVE_ENDIAN); + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { @@ -347,7 +343,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) cpu_physical_memory_write(cur_lowmem, fdt, fdt_size); cur_tagptr = put_tag(cur_tagptr, BP_TAG_FDT, sizeof(dtb_addr), &dtb_addr); - cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4096); + cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + fdt_size, 4 * KiB); } #else if (dtb_filename) { @@ -375,7 +371,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) initrd_location.end = tswap32(cur_lowmem + initrd_size); cur_tagptr = put_tag(cur_tagptr, BP_TAG_INITRD, sizeof(initrd_location), &initrd_location); - cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + initrd_size, 4096); + cur_lowmem = QEMU_ALIGN_UP(cur_lowmem + initrd_size, 4 * KiB); } cur_tagptr = put_tag(cur_tagptr, BP_TAG_LAST, 0, NULL); env->regs[2] = tagptr; diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index 8c90a2e66e..c85a62f798 100644 --- a/include/block/aio-wait.h +++ b/include/block/aio-wait.h @@ -57,7 +57,8 @@ typedef struct { /** * AIO_WAIT_WHILE: * @wait: the aio wait object - * @ctx: the aio context + * @ctx: the aio context, or NULL if multiple aio contexts (for which the + * caller does not hold a lock) are involved in the polling condition. * @cond: wait while this conditional expression is true * * Wait while a condition is true. Use this to implement synchronous @@ -73,29 +74,27 @@ typedef struct { */ #define AIO_WAIT_WHILE(wait, ctx, cond) ({ \ bool waited_ = false; \ - bool busy_ = true; \ AioWait *wait_ = (wait); \ AioContext *ctx_ = (ctx); \ - if (in_aio_context_home_thread(ctx_)) { \ - while ((cond) || busy_) { \ - busy_ = aio_poll(ctx_, (cond)); \ - waited_ |= !!(cond) | busy_; \ + if (ctx_ && in_aio_context_home_thread(ctx_)) { \ + while ((cond)) { \ + aio_poll(ctx_, true); \ + waited_ = true; \ } \ } else { \ assert(qemu_get_current_aio_context() == \ qemu_get_aio_context()); \ /* Increment wait_->num_waiters before evaluating cond. */ \ atomic_inc(&wait_->num_waiters); \ - while (busy_) { \ - if ((cond)) { \ - waited_ = busy_ = true; \ + while ((cond)) { \ + if (ctx_) { \ aio_context_release(ctx_); \ - aio_poll(qemu_get_aio_context(), true); \ - aio_context_acquire(ctx_); \ - } else { \ - busy_ = aio_poll(ctx_, false); \ - waited_ |= busy_; \ } \ + aio_poll(qemu_get_aio_context(), true); \ + if (ctx_) { \ + aio_context_acquire(ctx_); \ + } \ + waited_ = true; \ } \ atomic_dec(&wait_->num_waiters); \ } \ diff --git a/include/block/aio.h b/include/block/aio.h index a1d6b9e249..f08630c6e5 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -381,6 +381,9 @@ GSource *aio_get_g_source(AioContext *ctx); /* Return the ThreadPool bound to this AioContext */ struct ThreadPool *aio_get_thread_pool(AioContext *ctx); +/* Setup the LinuxAioState bound to this AioContext */ +struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp); + /* Return the LinuxAioState bound to this AioContext */ struct LinuxAioState *aio_get_linux_aio(AioContext *ctx); @@ -554,6 +557,14 @@ static inline bool in_aio_context_home_thread(AioContext *ctx) */ void aio_context_setup(AioContext *ctx); +/** + * aio_context_destroy: + * @ctx: the aio context + * + * Destroy the aio context. + */ +void aio_context_destroy(AioContext *ctx); + /** * aio_context_set_poll_params: * @ctx: the aio context diff --git a/include/block/block.h b/include/block/block.h index cdec3639a3..4e0871aaf9 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -43,19 +43,48 @@ typedef struct BlockFragInfo { typedef enum { BDRV_REQ_COPY_ON_READ = 0x1, BDRV_REQ_ZERO_WRITE = 0x2, - /* The BDRV_REQ_MAY_UNMAP flag is used to indicate that the block driver - * is allowed to optimize a write zeroes request by unmapping (discarding) - * blocks if it is guaranteed that the result will read back as - * zeroes. The flag is only passed to the driver if the block device is - * opened with BDRV_O_UNMAP. + + /* + * The BDRV_REQ_MAY_UNMAP flag is used in write_zeroes requests to indicate + * that the block driver should unmap (discard) blocks if it is guaranteed + * that the result will read back as zeroes. The flag is only passed to the + * driver if the block device is opened with BDRV_O_UNMAP. */ BDRV_REQ_MAY_UNMAP = 0x4, + + /* + * The BDRV_REQ_NO_SERIALISING flag is only valid for reads and means that + * we don't want wait_serialising_requests() during the read operation. + * + * This flag is used for backup copy-on-write operations, when we need to + * read old data before write (write notifier triggered). It is okay since + * we already waited for other serializing requests in the initiating write + * (see bdrv_aligned_pwritev), and it is necessary if the initiating write + * is already serializing (without the flag, the read would deadlock + * waiting for the serialising write to complete). + */ BDRV_REQ_NO_SERIALISING = 0x8, BDRV_REQ_FUA = 0x10, BDRV_REQ_WRITE_COMPRESSED = 0x20, + /* Signifies that this write request will not change the visible disk + * content. */ + BDRV_REQ_WRITE_UNCHANGED = 0x40, + + /* + * BDRV_REQ_SERIALISING forces request serialisation for writes. + * It is used to ensure that writes to the backing file of a backup process + * target cannot race with a read of the backup target that defers to the + * backing file. + * + * Note, that BDRV_REQ_SERIALISING is _not_ opposite in meaning to + * BDRV_REQ_NO_SERIALISING. A more descriptive name for the latter might be + * _DO_NOT_WAIT_FOR_SERIALISING, except that is too long. + */ + BDRV_REQ_SERIALISING = 0x80, + /* Mask of valid flags */ - BDRV_REQ_MASK = 0x3f, + BDRV_REQ_MASK = 0xff, } BdrvRequestFlags; typedef struct BlockSizes { @@ -205,6 +234,9 @@ enum { * This permission (which is weaker than BLK_PERM_WRITE) is both enough and * required for writes to the block node when the caller promises that * the visible disk content doesn't change. + * + * As the BLK_PERM_WRITE permission is strictly stronger, either is + * sufficient to perform an unchanging write. */ BLK_PERM_WRITE_UNCHANGED = 0x04, @@ -218,6 +250,13 @@ enum { BLK_PERM_GRAPH_MOD = 0x10, BLK_PERM_ALL = 0x1f, + + DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ + | BLK_PERM_WRITE + | BLK_PERM_WRITE_UNCHANGED + | BLK_PERM_RESIZE, + + DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH, }; char *bdrv_perm_names(uint64_t perm); @@ -278,10 +317,6 @@ int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes); int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov); int bdrv_pwrite_sync(BdrvChild *child, int64_t offset, const void *buf, int count); -int coroutine_fn bdrv_co_readv(BdrvChild *child, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); -int coroutine_fn bdrv_co_writev(BdrvChild *child, int64_t sector_num, - int nb_sectors, QEMUIOVector *qiov); /* * Efficiently zero a region of the disk image. Note that this is a regular * I/O request like read or write and should have a reasonable size. This @@ -293,8 +328,12 @@ int coroutine_fn bdrv_co_pwrite_zeroes(BdrvChild *child, int64_t offset, BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, const char *backing_file); void bdrv_refresh_filename(BlockDriverState *bs); + +int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, + PreallocMode prealloc, Error **errp); int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, Error **errp); + int64_t bdrv_nb_sectors(BlockDriverState *bs); int64_t bdrv_getlength(BlockDriverState *bs); int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); @@ -336,7 +375,8 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix); typedef void BlockDriverAmendStatusCB(BlockDriverState *bs, int64_t offset, int64_t total_work_size, void *opaque); int bdrv_amend_options(BlockDriverState *bs_new, QemuOpts *opts, - BlockDriverAmendStatusCB *status_cb, void *cb_opaque); + BlockDriverAmendStatusCB *status_cb, void *cb_opaque, + Error **errp); /* external snapshots */ bool bdrv_recurse_is_first_non_filter(BlockDriverState *bs, @@ -379,8 +419,8 @@ AioWait *bdrv_get_aio_wait(BlockDriverState *bs); bdrv_get_aio_context(bs_), \ cond); }) -int bdrv_pdiscard(BlockDriverState *bs, int64_t offset, int bytes); -int bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes); +int bdrv_pdiscard(BdrvChild *child, int64_t offset, int bytes); +int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes); int bdrv_has_zero_init_1(BlockDriverState *bs); int bdrv_has_zero_init(BlockDriverState *bs); bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs); @@ -400,6 +440,7 @@ bool bdrv_is_read_only(BlockDriverState *bs); int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, bool ignore_allow_rdw, Error **errp); int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); +bool bdrv_is_writable(BlockDriverState *bs); bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs); void bdrv_lock_medium(BlockDriverState *bs, bool locked); @@ -412,6 +453,7 @@ BlockDriverState *bdrv_lookup_bs(const char *device, Error **errp); bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base); BlockDriverState *bdrv_next_node(BlockDriverState *bs); +BlockDriverState *bdrv_next_all_states(BlockDriverState *bs); typedef struct BdrvNextIterator { enum { @@ -548,7 +590,16 @@ void bdrv_io_unplug(BlockDriverState *bs); * Begin a quiesced section of all users of @bs. This is part of * bdrv_drained_begin. */ -void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); +void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents); + +/** + * bdrv_parent_drained_begin_single: + * + * Begin a quiesced section for the parent of @c. If @poll is true, wait for + * any pending activity to cease. + */ +void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll); /** * bdrv_parent_drained_end: @@ -556,7 +607,23 @@ void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore); * End a quiesced section of all users of @bs. This is part of * bdrv_drained_end. */ -void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); +void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, + bool ignore_bds_parents); + +/** + * bdrv_drain_poll: + * + * Poll for pending requests in @bs, its parents (except for @ignore_parent), + * and if @recursive is true its children as well (used for subtree drain). + * + * If @ignore_bds_parents is true, parents that are BlockDriverStates must + * ignore the drain request because they will be drained separately (used for + * drain_all). + * + * This is part of bdrv_drained_begin. + */ +bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, + BdrvChild *ignore_parent, bool ignore_bds_parents); /** * bdrv_drained_begin: @@ -570,6 +637,15 @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore); */ void bdrv_drained_begin(BlockDriverState *bs); +/** + * bdrv_do_drained_begin_quiesce: + * + * Quiesces a BDS like bdrv_drained_begin(), but does not wait for already + * running requests to complete. + */ +void bdrv_do_drained_begin_quiesce(BlockDriverState *bs, + BdrvChild *parent, bool ignore_bds_parents); + /** * Like bdrv_drained_begin, but recursively begins a quiesced section for * exclusive access to all child nodes as well. @@ -604,4 +680,38 @@ bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name, */ void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size); void bdrv_unregister_buf(BlockDriverState *bs, void *host); + +/** + * + * bdrv_co_copy_range: + * + * Do offloaded copy between two children. If the operation is not implemented + * by the driver, or if the backend storage doesn't support it, a negative + * error code will be returned. + * + * Note: block layer doesn't emulate or fallback to a bounce buffer approach + * because usually the caller shouldn't attempt offloaded copy any more (e.g. + * calling copy_file_range(2)) after the first error, thus it should fall back + * to a read+write path in the caller level. + * + * @src: Source child to copy data from + * @src_offset: offset in @src image to read data + * @dst: Destination child to copy data to + * @dst_offset: offset in @dst image to write data + * @bytes: number of bytes to copy + * @flags: request flags. Supported flags: + * BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero + * write on @dst as if bdrv_co_pwrite_zeroes is + * called. Used to simplify caller code, or + * during BlockDriver.bdrv_co_copy_range_from() + * recursion. + * BDRV_REQ_NO_SERIALISING - do not serialize with other overlapping + * requests currently in flight. + * + * Returns: 0 if succeeded; negative error code if failed. + **/ +int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); #endif diff --git a/include/block/block_int.h b/include/block/block_int.h index c4dd1d4bb8..903b9c1034 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -63,17 +63,18 @@ enum BdrvTrackedRequestType { BDRV_TRACKED_READ, BDRV_TRACKED_WRITE, BDRV_TRACKED_DISCARD, + BDRV_TRACKED_TRUNCATE, }; typedef struct BdrvTrackedRequest { BlockDriverState *bs; int64_t offset; - unsigned int bytes; + uint64_t bytes; enum BdrvTrackedRequestType type; bool serialising; int64_t overlap_offset; - unsigned int overlap_bytes; + uint64_t overlap_bytes; QLIST_ENTRY(BdrvTrackedRequest) list; Coroutine *co; /* owner, used for deadlock detection */ @@ -141,11 +142,11 @@ struct BlockDriver { void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options); /* aio */ - BlockAIOCB *(*bdrv_aio_readv)(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, BlockCompletionFunc *cb, void *opaque); - BlockAIOCB *(*bdrv_aio_writev)(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockAIOCB *(*bdrv_aio_pwritev)(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags, BlockCompletionFunc *cb, void *opaque); BlockAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque); @@ -174,8 +175,6 @@ struct BlockDriver { int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags); int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs, - int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); - int coroutine_fn (*bdrv_co_writev_flags)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags); /** * @offset: position in bytes to write at @@ -206,6 +205,39 @@ struct BlockDriver { int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs, int64_t offset, int bytes); + /* Map [offset, offset + nbytes) range onto a child of @bs to copy from, + * and invoke bdrv_co_copy_range_from(child, ...), or invoke + * bdrv_co_copy_range_to() if @bs is the leaf child to copy data from. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. + */ + int coroutine_fn (*bdrv_co_copy_range_from)(BlockDriverState *bs, + BdrvChild *src, + uint64_t offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); + + /* Map [offset, offset + nbytes) range onto a child of bs to copy data to, + * and invoke bdrv_co_copy_range_to(child, src, ...), or perform the copy + * operation if @bs is the leaf and @src has the same BlockDriver. Return + * -ENOTSUP if @bs is the leaf but @src has a different BlockDriver. + * + * See the comment of bdrv_co_copy_range for the parameter and return value + * semantics. + */ + int coroutine_fn (*bdrv_co_copy_range_to)(BlockDriverState *bs, + BdrvChild *src, + uint64_t src_offset, + BdrvChild *dst, + uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); + /* * Building block for bdrv_block_status[_above] and * bdrv_is_allocated[_above]. The driver should answer only @@ -260,8 +292,8 @@ struct BlockDriver { * bdrv_parse_filename. */ const char *protocol_name; - int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset, - PreallocMode prealloc, Error **errp); + int coroutine_fn (*bdrv_co_truncate)(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp); int64_t (*bdrv_getlength)(BlockDriverState *bs); bool has_variable_length; @@ -324,7 +356,8 @@ struct BlockDriver { int (*bdrv_amend_options)(BlockDriverState *bs, QemuOpts *opts, BlockDriverAmendStatusCB *status_cb, - void *cb_opaque); + void *cb_opaque, + Error **errp); void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event); @@ -547,6 +580,12 @@ struct BdrvChildRole { * points to. */ bool stay_at_node; + /* If true, the parent is a BlockDriverState and bdrv_next_all_states() + * will return it. This information is used for drain_all, where every node + * will be drained separately, so the drain only needs to be propagated to + * non-BDS parents. */ + bool parent_is_bds; + void (*inherit_options)(int *child_flags, QDict *child_options, int parent_flags, QDict *parent_options); @@ -569,12 +608,22 @@ struct BdrvChildRole { * requests after returning from .drained_begin() until .drained_end() is * called. * + * These functions must not change the graph (and therefore also must not + * call aio_poll(), which could change the graph indirectly). + * * Note that this can be nested. If drained_begin() was called twice, new * I/O is allowed only after drained_end() was called twice, too. */ void (*drained_begin)(BdrvChild *child); void (*drained_end)(BdrvChild *child); + /* + * Returns whether the parent has pending requests for the child. This + * callback is polled after .drained_begin() has been called until all + * activity on the child has stopped. + */ + bool (*drained_poll)(BdrvChild *child); + /* Notifies the parent that the child has been activated/inactivated (e.g. * when migration is completing) and it can start/stop requesting * permissions and doing I/O on it. */ @@ -658,10 +707,24 @@ struct BlockDriverState { /* I/O Limits */ BlockLimits bl; - /* Flags honored during pwrite (so far: BDRV_REQ_FUA) */ + /* Flags honored during pwrite (so far: BDRV_REQ_FUA, + * BDRV_REQ_WRITE_UNCHANGED). + * If a driver does not support BDRV_REQ_WRITE_UNCHANGED, those + * writes will be issued as normal writes without the flag set. + * This is important to note for drivers that do not explicitly + * request a WRITE permission for their children and instead take + * the same permissions as their parent did (this is commonly what + * block filters do). Such drivers have to be aware that the + * parent may have taken a WRITE_UNCHANGED permission only and is + * issuing such requests. Drivers either must make sure that + * these requests do not result in plain WRITE accesses (usually + * by supporting BDRV_REQ_WRITE_UNCHANGED, and then forwarding + * every incoming write request as-is, including potentially that + * flag), or they have to explicitly take the WRITE permission for + * their children. */ unsigned int supported_write_flags; /* Flags honored during pwrite_zeroes (so far: BDRV_REQ_FUA, - * BDRV_REQ_MAY_UNMAP) */ + * BDRV_REQ_MAY_UNMAP, BDRV_REQ_WRITE_UNCHANGED) */ unsigned int supported_zero_flags; /* the following member gives a name to every node on the bs graph. */ @@ -797,6 +860,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, int64_t offset, unsigned int bytes, QEMUIOVector *qiov, BdrvRequestFlags flags); +extern unsigned int bdrv_drain_all_count; void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent); void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent); @@ -973,6 +1037,7 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, * @filter_node_name: The node name that should be assigned to the filter * driver that the mirror job inserts into the graph above @bs. NULL means that * a node name should be autogenerated. + * @copy_mode: When to trigger writes to the target. * @errp: Error object. * * Start a mirroring operation on @bs. Clusters that are allocated @@ -986,7 +1051,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, - bool unmap, const char *filter_node_name, Error **errp); + bool unmap, const char *filter_node_name, + MirrorCopyMode copy_mode, Error **errp); /* * backup_job_create: @@ -1017,7 +1083,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, - BlockJobTxn *txn, Error **errp); + JobTxn *txn, Error **errp); void hmp_drive_add_node(Monitor *mon, const char *optstr); @@ -1090,4 +1156,17 @@ void bdrv_dec_in_flight(BlockDriverState *bs); void blockdev_close_all_bdrv_states(void); +int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); +int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset, + BdrvChild *dst, uint64_t dst_offset, + uint64_t bytes, + BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); + +int refresh_total_sectors(BlockDriverState *bs, int64_t hint); + #endif /* BLOCK_INT_H */ diff --git a/include/block/blockjob.h b/include/block/blockjob.h index fc645dac68..32c00b7dc0 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -26,10 +26,13 @@ #ifndef BLOCKJOB_H #define BLOCKJOB_H +#include "qemu/job.h" #include "block/block.h" +#include "qemu/ratelimit.h" + +#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */ typedef struct BlockJobDriver BlockJobDriver; -typedef struct BlockJobTxn BlockJobTxn; /** * BlockJob: @@ -37,138 +40,40 @@ typedef struct BlockJobTxn BlockJobTxn; * Long-running operation on a BlockDriverState. */ typedef struct BlockJob { - /** The job type, including the job vtable. */ - const BlockJobDriver *driver; + /** Data belonging to the generic Job infrastructure */ + Job job; /** The block device on which the job is operating. */ BlockBackend *blk; - /** - * The ID of the block job. May be NULL for internal jobs. - */ - char *id; - - /** - * The coroutine that executes the job. If not NULL, it is - * reentered when busy is false and the job is cancelled. - */ - Coroutine *co; - - /** - * Set to true if the job should cancel itself. The flag must - * always be tested just before toggling the busy flag from false - * to true. After a job has been cancelled, it should only yield - * if #aio_poll will ("sooner or later") reenter the coroutine. - */ - bool cancelled; - - /** - * Set to true if the job should abort immediately without waiting - * for data to be in sync. - */ - bool force; - - /** - * Counter for pause request. If non-zero, the block job is either paused, - * or if busy == true will pause itself as soon as possible. - */ - int pause_count; - - /** - * Set to true if the job is paused by user. Can be unpaused with the - * block-job-resume QMP command. - */ - bool user_paused; - - /** - * Set to false by the job while the coroutine has yielded and may be - * re-entered by block_job_enter(). There may still be I/O or event loop - * activity pending. Accessed under block_job_mutex (in blockjob.c). - */ - bool busy; - - /** - * Set to true by the job while it is in a quiescent state, where - * no I/O or event loop activity is pending. - */ - bool paused; - - /** - * Set to true when the job is ready to be completed. - */ - bool ready; - - /** - * Set to true when the job has deferred work to the main loop. - */ - bool deferred_to_main_loop; - - /** Element of the list of block jobs */ - QLIST_ENTRY(BlockJob) job_list; - /** Status that is published by the query-block-jobs QMP API */ BlockDeviceIoStatus iostatus; - /** Offset that is published by the query-block-jobs QMP API */ - int64_t offset; - - /** Length that is published by the query-block-jobs QMP API */ - int64_t len; - /** Speed that was set with @block_job_set_speed. */ int64_t speed; - /** The completion function that will be called when the job completes. */ - BlockCompletionFunc *cb; + /** Rate limiting data structure for implementing @speed. */ + RateLimit limit; /** Block other operations when block job is running */ Error *blocker; + /** Called when a cancelled job is finalised. */ + Notifier finalize_cancelled_notifier; + + /** Called when a successfully completed job is finalised. */ + Notifier finalize_completed_notifier; + + /** Called when the job transitions to PENDING */ + Notifier pending_notifier; + + /** Called when the job transitions to READY */ + Notifier ready_notifier; + /** BlockDriverStates that are involved in this block job */ GSList *nodes; - - /** The opaque value that is passed to the completion function. */ - void *opaque; - - /** Reference count of the block job */ - int refcnt; - - /** True when job has reported completion by calling block_job_completed. */ - bool completed; - - /** ret code passed to block_job_completed. */ - int ret; - - /** - * Timer that is used by @block_job_sleep_ns. Accessed under - * block_job_mutex (in blockjob.c). - */ - QEMUTimer sleep_timer; - - /** Current state; See @BlockJobStatus for details. */ - BlockJobStatus status; - - /** True if this job should automatically finalize itself */ - bool auto_finalize; - - /** True if this job should automatically dismiss itself */ - bool auto_dismiss; - - BlockJobTxn *txn; - QLIST_ENTRY(BlockJob) txn_list; } BlockJob; -typedef enum BlockJobCreateFlags { - /* Default behavior */ - BLOCK_JOB_DEFAULT = 0x00, - /* BlockJob is not QMP-created and should not send QMP events */ - BLOCK_JOB_INTERNAL = 0x01, - /* BlockJob requires manual finalize step */ - BLOCK_JOB_MANUAL_FINALIZE = 0x02, - /* BlockJob requires manual dismiss step */ - BLOCK_JOB_MANUAL_DISMISS = 0x04, -} BlockJobCreateFlags; - /** * block_job_next: * @job: A block job, or %NULL. @@ -224,59 +129,6 @@ void block_job_remove_all_bdrv(BlockJob *job); */ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp); -/** - * block_job_start: - * @job: A job that has not yet been started. - * - * Begins execution of a block job. - * Takes ownership of one reference to the job object. - */ -void block_job_start(BlockJob *job); - -/** - * block_job_cancel: - * @job: The job to be canceled. - * @force: Quit a job without waiting for data to be in sync. - * - * Asynchronously cancel the specified job. - */ -void block_job_cancel(BlockJob *job, bool force); - -/** - * block_job_complete: - * @job: The job to be completed. - * @errp: Error object. - * - * Asynchronously complete the specified job. - */ -void block_job_complete(BlockJob *job, Error **errp); - - -/** - * block_job_finalize: - * @job: The job to fully commit and finish. - * @errp: Error object. - * - * For jobs that have finished their work and are pending - * awaiting explicit acknowledgement to commit their work, - * This will commit that work. - * - * FIXME: Make the below statement universally true: - * For jobs that support the manual workflow mode, all graph - * changes that occur as a result will occur after this command - * and before a successful reply. - */ -void block_job_finalize(BlockJob *job, Error **errp); - -/** - * block_job_dismiss: - * @job: The job to be dismissed. - * @errp: Error object. - * - * Remove a concluded job from the query list. - */ -void block_job_dismiss(BlockJob **job, Error **errp); - /** * block_job_query: * @job: The job to get information about. @@ -285,78 +137,6 @@ void block_job_dismiss(BlockJob **job, Error **errp); */ BlockJobInfo *block_job_query(BlockJob *job, Error **errp); -/** - * block_job_user_pause: - * @job: The job to be paused. - * - * Asynchronously pause the specified job. - * Do not allow a resume until a matching call to block_job_user_resume. - */ -void block_job_user_pause(BlockJob *job, Error **errp); - -/** - * block_job_paused: - * @job: The job to query. - * - * Returns true if the job is user-paused. - */ -bool block_job_user_paused(BlockJob *job); - -/** - * block_job_user_resume: - * @job: The job to be resumed. - * - * Resume the specified job. - * Must be paired with a preceding block_job_user_pause. - */ -void block_job_user_resume(BlockJob *job, Error **errp); - -/** - * block_job_user_cancel: - * @job: The job to be cancelled. - * @force: Quit a job without waiting for data to be in sync. - * - * Cancels the specified job, but may refuse to do so if the - * operation isn't currently meaningful. - */ -void block_job_user_cancel(BlockJob *job, bool force, Error **errp); - -/** - * block_job_cancel_sync: - * @job: The job to be canceled. - * - * Synchronously cancel the job. The completion callback is called - * before the function returns. The job may actually complete - * instead of canceling itself; the circumstances under which this - * happens depend on the kind of job that is active. - * - * Returns the return value from the job if the job actually completed - * during the call, or -ECANCELED if it was canceled. - */ -int block_job_cancel_sync(BlockJob *job); - -/** - * block_job_cancel_sync_all: - * - * Synchronously cancels all jobs using block_job_cancel_sync(). - */ -void block_job_cancel_sync_all(void); - -/** - * block_job_complete_sync: - * @job: The job to be completed. - * @errp: Error object which may be set by block_job_complete(); this is not - * necessarily set on every error, the job return value has to be - * checked as well. - * - * Synchronously complete the job. The completion callback is called before the - * function returns, unless it is NULL (which is permissible when using this - * function). - * - * Returns the return value from the job. - */ -int block_job_complete_sync(BlockJob *job, Error **errp); - /** * block_job_iostatus_reset: * @job: The job whose I/O status should be reset. @@ -366,59 +146,6 @@ int block_job_complete_sync(BlockJob *job, Error **errp); */ void block_job_iostatus_reset(BlockJob *job); -/** - * block_job_txn_new: - * - * Allocate and return a new block job transaction. Jobs can be added to the - * transaction using block_job_txn_add_job(). - * - * The transaction is automatically freed when the last job completes or is - * cancelled. - * - * All jobs in the transaction either complete successfully or fail/cancel as a - * group. Jobs wait for each other before completing. Cancelling one job - * cancels all jobs in the transaction. - */ -BlockJobTxn *block_job_txn_new(void); - -/** - * block_job_ref: - * - * Add a reference to BlockJob refcnt, it will be decreased with - * block_job_unref, and then be freed if it comes to be the last - * reference. - */ -void block_job_ref(BlockJob *job); - -/** - * block_job_unref: - * - * Release a reference that was previously acquired with block_job_ref - * or block_job_create. If it's the last reference to the object, it will be - * freed. - */ -void block_job_unref(BlockJob *job); - -/** - * block_job_txn_unref: - * - * Release a reference that was previously acquired with block_job_txn_add_job - * or block_job_txn_new. If it's the last reference to the object, it will be - * freed. - */ -void block_job_txn_unref(BlockJobTxn *txn); - -/** - * block_job_txn_add_job: - * @txn: The transaction (may be NULL) - * @job: Job to add to the transaction - * - * Add @job to the transaction. The @job must not already be in a transaction. - * The caller must call either block_job_txn_unref() or block_job_completed() - * to release the reference that is automatically grabbed here. - */ -void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job); - /** * block_job_is_internal: * @job: The job to determine if it is user-visible or not. @@ -427,4 +154,11 @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job); */ bool block_job_is_internal(BlockJob *job); +/** + * block_job_driver: + * + * Returns the driver associated with a block job. + */ +const BlockJobDriver *block_job_driver(BlockJob *job); + #endif diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h index 642adce68b..e4a318dd15 100644 --- a/include/block/blockjob_int.h +++ b/include/block/blockjob_int.h @@ -35,75 +35,16 @@ * A class type for block job driver. */ struct BlockJobDriver { - /** Derived BlockJob struct size */ - size_t instance_size; + /** Generic JobDriver callbacks and settings */ + JobDriver job_driver; - /** String describing the operation, part of query-block-jobs QMP API */ - BlockJobType job_type; - - /** Optional callback for job types that support setting a speed limit */ - void (*set_speed)(BlockJob *job, int64_t speed, Error **errp); - - /** Mandatory: Entrypoint for the Coroutine. */ - CoroutineEntry *start; - - /** - * Optional callback for job types whose completion must be triggered - * manually. + /* + * Returns whether the job has pending requests for the child or will + * submit new requests before the next pause point. This callback is polled + * in the context of draining a job node after requesting that the job be + * paused, until all activity on the child has stopped. */ - void (*complete)(BlockJob *job, Error **errp); - - /** - * If the callback is not NULL, prepare will be invoked when all the jobs - * belonging to the same transaction complete; or upon this job's completion - * if it is not in a transaction. - * - * This callback will not be invoked if the job has already failed. - * If it fails, abort and then clean will be called. - */ - int (*prepare)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when all the jobs - * belonging to the same transaction complete; or upon this job's - * completion if it is not in a transaction. Skipped if NULL. - * - * All jobs will complete with a call to either .commit() or .abort() but - * never both. - */ - void (*commit)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when any job in the - * same transaction fails; or upon this job's failure (due to error or - * cancellation) if it is not in a transaction. Skipped if NULL. - * - * All jobs will complete with a call to either .commit() or .abort() but - * never both. - */ - void (*abort)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked after a call to either - * .commit() or .abort(). Regardless of which callback is invoked after - * completion, .clean() will always be called, even if the job does not - * belong to a transaction group. - */ - void (*clean)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when the job transitions - * into the paused state. Paused jobs must not perform any asynchronous - * I/O or event loop activity. This callback is used to quiesce jobs. - */ - void coroutine_fn (*pause)(BlockJob *job); - - /** - * If the callback is not NULL, it will be invoked when the job transitions - * out of the paused state. Any asynchronous I/O or event loop activity - * should be restarted from this callback. - */ - void coroutine_fn (*resume)(BlockJob *job); + bool (*drained_poll)(BlockJob *job); /* * If the callback is not NULL, it will be invoked before the job is @@ -116,6 +57,10 @@ struct BlockJobDriver { * If the callback is not NULL, it will be invoked when the job has to be * synchronously cancelled or completed; it should drain BlockDriverStates * as required to ensure progress. + * + * Block jobs must use the default implementation for job_driver.drain, + * which will in turn call this callback after doing generic block job + * stuff. */ void (*drain)(BlockJob *job); }; @@ -129,8 +74,7 @@ struct BlockJobDriver { * @bs: The block * @perm, @shared_perm: Permissions to request for @bs * @speed: The maximum speed, in bytes per second, or 0 for unlimited. - * @flags: Creation flags for the Block Job. - * See @BlockJobCreateFlags + * @flags: Creation flags for the Block Job. See @JobCreateFlags. * @cb: Completion function for the job. * @opaque: Opaque pointer value passed to @cb. * @errp: Error object. @@ -145,93 +89,39 @@ struct BlockJobDriver { * called from a wrapper that is specific to the job type. */ void *block_job_create(const char *job_id, const BlockJobDriver *driver, - BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm, + JobTxn *txn, BlockDriverState *bs, uint64_t perm, uint64_t shared_perm, int64_t speed, int flags, BlockCompletionFunc *cb, void *opaque, Error **errp); /** - * block_job_sleep_ns: - * @job: The job that calls the function. - * @ns: How many nanoseconds to stop for. - * - * Put the job to sleep (assuming that it wasn't canceled) for @ns - * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately - * interrupt the wait. + * block_job_free: + * Callback to be used for JobDriver.free in all block jobs. Frees block job + * specific resources in @job. */ -void block_job_sleep_ns(BlockJob *job, int64_t ns); +void block_job_free(Job *job); /** - * block_job_yield: - * @job: The job that calls the function. - * - * Yield the block job coroutine. + * block_job_user_resume: + * Callback to be used for JobDriver.user_resume in all block jobs. Resets the + * iostatus when the user resumes @job. */ -void block_job_yield(BlockJob *job); +void block_job_user_resume(Job *job); /** - * block_job_pause_all: - * - * Asynchronously pause all jobs. + * block_job_drain: + * Callback to be used for JobDriver.drain in all block jobs. Drains the main + * block node associated with the block jobs and calls BlockJobDriver.drain for + * job-specific actions. */ -void block_job_pause_all(void); +void block_job_drain(Job *job); /** - * block_job_resume_all: + * block_job_ratelimit_get_delay: * - * Resume all block jobs. Must be paired with a preceding block_job_pause_all. + * Calculate and return delay for the next request in ns. See the documentation + * of ratelimit_calculate_delay() for details. */ -void block_job_resume_all(void); - -/** - * block_job_early_fail: - * @bs: The block device. - * - * The block job could not be started, free it. - */ -void block_job_early_fail(BlockJob *job); - -/** - * block_job_completed: - * @job: The job being completed. - * @ret: The status code. - * - * Call the completion function that was registered at creation time, and - * free @job. - */ -void block_job_completed(BlockJob *job, int ret); - -/** - * block_job_is_cancelled: - * @job: The job being queried. - * - * Returns whether the job is scheduled for cancellation. - */ -bool block_job_is_cancelled(BlockJob *job); - -/** - * block_job_pause_point: - * @job: The job that is ready to pause. - * - * Pause now if block_job_pause() has been called. Block jobs that perform - * lots of I/O must call this between requests so that the job can be paused. - */ -void coroutine_fn block_job_pause_point(BlockJob *job); - -/** - * block_job_enter: - * @job: The job to enter. - * - * Continue the specified job by entering the coroutine. - */ -void block_job_enter(BlockJob *job); - -/** - * block_job_event_ready: - * @job: The job which is now ready to be completed. - * - * Send a BLOCK_JOB_READY event for the specified job. - */ -void block_job_event_ready(BlockJob *job); +int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n); /** * block_job_error_action: @@ -246,23 +136,4 @@ void block_job_event_ready(BlockJob *job); BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, int is_read, int error); -typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque); - -/** - * block_job_defer_to_main_loop: - * @job: The job - * @fn: The function to run in the main loop - * @opaque: The opaque value that is passed to @fn - * - * This function must be called by the main job coroutine just before it - * returns. @fn is executed in the main loop with the BlockDriverState - * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and - * anything that uses bdrv_drain_all() in the main loop. - * - * The @job AioContext is held while @fn executes. - */ -void block_job_defer_to_main_loop(BlockJob *job, - BlockJobDeferToMainLoopFn *fn, - void *opaque); - #endif diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 1ff8949b1b..259bd27c40 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -24,7 +24,6 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap); BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name); -void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap); void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs); @@ -33,6 +32,7 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, Error **errp); void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap); void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap); +void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap); BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap); @@ -70,7 +70,8 @@ void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent); void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked); - +void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, + Error **errp); /* Functions that require manual locking. */ void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); @@ -82,6 +83,8 @@ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes); int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); +bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, + uint64_t *offset, int *bytes); void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset); int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); diff --git a/include/block/nbd.h b/include/block/nbd.h index fcdcd54502..4638c839f5 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -135,6 +135,7 @@ typedef struct NBDExtent { #define NBD_FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */ #define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send WRITE_ZEROES */ #define NBD_FLAG_SEND_DF (1 << 7) /* Send DF (Do not Fragment) */ +#define NBD_FLAG_SEND_CACHE (1 << 8) /* Send CACHE (prefetch) */ /* New-style handshake (global) flags, sent from server to client, and control what will happen during handshake phase. */ @@ -195,7 +196,7 @@ enum { NBD_CMD_DISC = 2, NBD_CMD_FLUSH = 3, NBD_CMD_TRIM = 4, - /* 5 reserved for failed experiment NBD_CMD_CACHE */ + NBD_CMD_CACHE = 5, NBD_CMD_WRITE_ZEROES = 6, NBD_CMD_BLOCK_STATUS = 7, }; @@ -229,11 +230,13 @@ enum { #define NBD_REPLY_TYPE_ERROR NBD_REPLY_ERR(1) #define NBD_REPLY_TYPE_ERROR_OFFSET NBD_REPLY_ERR(2) -/* Flags for extents (NBDExtent.flags) of NBD_REPLY_TYPE_BLOCK_STATUS, - * for base:allocation meta context */ +/* Extent flags for base:allocation in NBD_REPLY_TYPE_BLOCK_STATUS */ #define NBD_STATE_HOLE (1 << 0) #define NBD_STATE_ZERO (1 << 1) +/* Extent flags for qemu:dirty-bitmap in NBD_REPLY_TYPE_BLOCK_STATUS */ +#define NBD_STATE_DIRTY (1 << 0) + static inline bool nbd_reply_type_is_error(int type) { return type & (1 << 15); @@ -256,6 +259,7 @@ static inline bool nbd_reply_type_is_error(int type) struct NBDExportInfo { /* Set by client before nbd_receive_negotiate() */ bool request_sizes; + char *x_dirty_bitmap; /* In-out fields, set by client before nbd_receive_negotiate() and * updated by server results during nbd_receive_negotiate() */ @@ -315,6 +319,8 @@ void nbd_client_put(NBDClient *client); void nbd_server_start(SocketAddress *addr, const char *tls_creds, Error **errp); +void nbd_export_bitmap(NBDExport *exp, const char *bitmap, + const char *bitmap_export_name, Error **errp); /* nbd_read * Reads @size bytes from @ioc. Returns 0 on success. diff --git a/include/block/qdict.h b/include/block/qdict.h new file mode 100644 index 0000000000..d8cb502d7d --- /dev/null +++ b/include/block/qdict.h @@ -0,0 +1,34 @@ +/* + * Special QDict functions used by the block layer + * + * Copyright (c) 2013-2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#ifndef BLOCK_QDICT_H +#define BLOCK_QDICT_H + +#include "qapi/qmp/qdict.h" + +void qdict_copy_default(QDict *dst, QDict *src, const char *key); +void qdict_set_default_str(QDict *dst, const char *key, const char *val); + +void qdict_join(QDict *dest, QDict *src, bool overwrite); + +void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); +void qdict_array_split(QDict *src, QList **dst); +int qdict_array_entries(QDict *src, const char *subqdict); +QObject *qdict_crumple(const QDict *src, Error **errp); +void qdict_flatten(QDict *qdict); + +typedef struct QDictRenames { + const char *from; + const char *to; +} QDictRenames; +bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); + +Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, + Error **errp); +#endif diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index a4cdbbf1b7..6799614e56 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -25,9 +25,17 @@ #define QEMU_AIO_FLUSH 0x0008 #define QEMU_AIO_DISCARD 0x0010 #define QEMU_AIO_WRITE_ZEROES 0x0020 +#define QEMU_AIO_COPY_RANGE 0x0040 +#define QEMU_AIO_TRUNCATE 0x0080 #define QEMU_AIO_TYPE_MASK \ - (QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \ - QEMU_AIO_DISCARD|QEMU_AIO_WRITE_ZEROES) + (QEMU_AIO_READ | \ + QEMU_AIO_WRITE | \ + QEMU_AIO_IOCTL | \ + QEMU_AIO_FLUSH | \ + QEMU_AIO_DISCARD | \ + QEMU_AIO_WRITE_ZEROES | \ + QEMU_AIO_COPY_RANGE | \ + QEMU_AIO_TRUNCATE) /* AIO flags */ #define QEMU_AIO_MISALIGNED 0x1000 @@ -37,7 +45,7 @@ /* linux-aio.c - Linux native implementation */ #ifdef CONFIG_LINUX_AIO typedef struct LinuxAioState LinuxAioState; -LinuxAioState *laio_init(void); +LinuxAioState *laio_init(Error **errp); void laio_cleanup(LinuxAioState *s); int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd, uint64_t offset, QEMUIOVector *qiov, int type); @@ -57,7 +65,7 @@ void win32_aio_cleanup(QEMUWin32AIOState *aio); int win32_aio_attach(QEMUWin32AIOState *aio, HANDLE hfile); BlockAIOCB *win32_aio_submit(BlockDriverState *bs, QEMUWin32AIOState *aio, HANDLE hfile, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, BlockCompletionFunc *cb, void *opaque, int type); void win32_aio_detach_aio_context(QEMUWin32AIOState *aio, AioContext *old_context); diff --git a/include/chardev/char.h b/include/chardev/char.h index 04de45795e..6f0576e214 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -22,7 +22,16 @@ typedef enum { CHR_EVENT_OPENED, /* new connection established */ CHR_EVENT_MUX_IN, /* mux-focus was set to this terminal */ CHR_EVENT_MUX_OUT, /* mux-focus will move on */ - CHR_EVENT_CLOSED /* connection closed */ + CHR_EVENT_CLOSED /* connection closed. NOTE: currently this event + * is only bound to the read port of the chardev. + * Normally the read port and write port of a + * chardev should be the same, but it can be + * different, e.g., for fd chardevs, when the two + * fds are different. So when we received the + * CLOSED event it's still possible that the out + * port is still open. TODO: we should only send + * the CLOSED event when both ports are closed. + */ } QEMUChrEvent; #define CHR_READ_BUF_LEN 4096 diff --git a/include/crypto/init.h b/include/crypto/init.h index 04c1edf770..f79c02266b 100644 --- a/include/crypto/init.h +++ b/include/crypto/init.h @@ -21,6 +21,8 @@ #ifndef QCRYPTO_INIT_H #define QCRYPTO_INIT_H +#include "qapi/error.h" + int qcrypto_init(Error **errp); #endif /* QCRYPTO_INIT_H */ diff --git a/include/crypto/tlscredspsk.h b/include/crypto/tlscredspsk.h new file mode 100644 index 0000000000..306d36c67d --- /dev/null +++ b/include/crypto/tlscredspsk.h @@ -0,0 +1,106 @@ +/* + * QEMU crypto TLS Pre-Shared Key (PSK) support + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#ifndef QCRYPTO_TLSCREDSPSK_H +#define QCRYPTO_TLSCREDSPSK_H + +#include "crypto/tlscreds.h" + +#define TYPE_QCRYPTO_TLS_CREDS_PSK "tls-creds-psk" +#define QCRYPTO_TLS_CREDS_PSK(obj) \ + OBJECT_CHECK(QCryptoTLSCredsPSK, (obj), TYPE_QCRYPTO_TLS_CREDS_PSK) + +typedef struct QCryptoTLSCredsPSK QCryptoTLSCredsPSK; +typedef struct QCryptoTLSCredsPSKClass QCryptoTLSCredsPSKClass; + +#define QCRYPTO_TLS_CREDS_PSKFILE "keys.psk" + +/** + * QCryptoTLSCredsPSK: + * + * The QCryptoTLSCredsPSK object provides a representation + * of the Pre-Shared Key credential used to perform a TLS handshake. + * + * This is a user creatable object, which can be instantiated + * via object_new_propv(): + * + * + * Creating TLS-PSK credential objects in code + * + * Object *obj; + * Error *err = NULL; + * obj = object_new_propv(TYPE_QCRYPTO_TLS_CREDS_PSK, + * "tlscreds0", + * &err, + * "dir", "/path/to/dir", + * "endpoint", "client", + * NULL); + * + * + * + * Or via QMP: + * + * + * Creating TLS-PSK credential objects via QMP + * + * { + * "execute": "object-add", "arguments": { + * "id": "tlscreds0", + * "qom-type": "tls-creds-psk", + * "props": { + * "dir": "/path/to/dir", + * "endpoint": "client" + * } + * } + * } + * + * + * + * Or via the CLI: + * + * + * Creating TLS-PSK credential objects via CLI + * + * qemu-system-x86_64 --object tls-creds-psk,id=tlscreds0,\ + * endpoint=client,dir=/path/to/dir[,username=qemu] + * + * + * + * The PSK file can be created and managed using psktool. + */ + +struct QCryptoTLSCredsPSK { + QCryptoTLSCreds parent_obj; + char *username; +#ifdef CONFIG_GNUTLS + union { + gnutls_psk_server_credentials_t server; + gnutls_psk_client_credentials_t client; + } data; +#endif +}; + + +struct QCryptoTLSCredsPSKClass { + QCryptoTLSCredsClass parent_class; +}; + + +#endif /* QCRYPTO_TLSCREDSPSK_H */ diff --git a/include/elf.h b/include/elf.h index c0dc9bb5fd..934dbbd6b3 100644 --- a/include/elf.h +++ b/include/elf.h @@ -1483,6 +1483,7 @@ typedef struct elf64_shdr { #define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ #define ELFOSABI_MODESTO 11 /* Novell Modesto. */ #define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM_FDPIC 65 /* ARM FDPIC */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index f4fa94e966..117d2fbbca 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -133,6 +133,8 @@ static inline void tswap64s(uint64_t *s) #define stq_p(p, v) stq_be_p(p, v) #define stfl_p(p, v) stfl_be_p(p, v) #define stfq_p(p, v) stfq_be_p(p, v) +#define ldn_p(p, sz) ldn_be_p(p, sz) +#define stn_p(p, sz, v) stn_be_p(p, sz, v) #else #define lduw_p(p) lduw_le_p(p) #define ldsw_p(p) ldsw_le_p(p) @@ -145,6 +147,8 @@ static inline void tswap64s(uint64_t *s) #define stq_p(p, v) stq_le_p(p, v) #define stfl_p(p, v) stfl_le_p(p, v) #define stfq_p(p, v) stfq_le_p(p, v) +#define ldn_p(p, sz) ldn_le_p(p, sz) +#define stn_p(p, sz, v) stn_le_p(p, sz, v) #endif /* MMU memory access macros */ @@ -168,51 +172,40 @@ extern unsigned long reserved_va; #else #include "exec/hwaddr.h" -uint32_t lduw_phys(AddressSpace *as, hwaddr addr); -uint32_t ldl_phys(AddressSpace *as, hwaddr addr); -uint64_t ldq_phys(AddressSpace *as, hwaddr addr); -void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val); -void stw_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stl_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stq_phys(AddressSpace *as, hwaddr addr, uint64_t val); -uint32_t address_space_lduw(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_notdirty(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq(AddressSpace *as, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); +#define SUFFIX +#define ARG1 as +#define ARG1_DECL AddressSpace *as +#define TARGET_ENDIANNESS +#include "exec/memory_ldst.inc.h" -uint32_t lduw_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t ldl_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint64_t ldq_phys_cached(MemoryRegionCache *cache, hwaddr addr); -void stl_phys_notdirty_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stw_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stl_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stq_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val); +#define SUFFIX _cached_slow +#define ARG1 cache +#define ARG1_DECL MemoryRegionCache *cache +#define TARGET_ENDIANNESS +#include "exec/memory_ldst.inc.h" -uint32_t address_space_lduw_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_notdirty_cached(MemoryRegionCache *cache, hwaddr addr, - uint32_t val, MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); +static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val) +{ + address_space_stl_notdirty(as, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +#define SUFFIX +#define ARG1 as +#define ARG1_DECL AddressSpace *as +#define TARGET_ENDIANNESS +#include "exec/memory_ldst_phys.inc.h" + +/* Inline fast path for direct RAM access. */ +#define ENDIANNESS +#include "exec/memory_ldst_cached.inc.h" + +#define SUFFIX _cached +#define ARG1 cache +#define ARG1_DECL MemoryRegionCache *cache +#define TARGET_ENDIANNESS +#include "exec/memory_ldst_phys.inc.h" #endif /* page related stuff */ @@ -337,11 +330,37 @@ CPUArchState *cpu_copy(CPUArchState *env); #define TLB_NOTDIRTY (1 << (TARGET_PAGE_BITS - 2)) /* Set if TLB entry is an IO callback. */ #define TLB_MMIO (1 << (TARGET_PAGE_BITS - 3)) +/* Set if TLB entry must have MMU lookup repeated for every access */ +#define TLB_RECHECK (1 << (TARGET_PAGE_BITS - 4)) /* Use this mask to check interception with an alignment mask * in a TCG backend. */ -#define TLB_FLAGS_MASK (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO) +#define TLB_FLAGS_MASK (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO \ + | TLB_RECHECK) + +/** + * tlb_hit_page: return true if page aligned @addr is a hit against the + * TLB entry @tlb_addr + * + * @addr: virtual address to test (must be page aligned) + * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) + */ +static inline bool tlb_hit_page(target_ulong tlb_addr, target_ulong addr) +{ + return addr == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); +} + +/** + * tlb_hit: return true if @addr is a hit against the TLB entry @tlb_addr + * + * @addr: virtual address to test (need not be page aligned) + * @tlb_addr: TLB entry address (a CPUTLBEntry addr_read/write/code value) + */ +static inline bool tlb_hit(target_ulong tlb_addr, target_ulong addr) +{ + return tlb_hit_page(tlb_addr, addr & TARGET_PAGE_MASK); +} void dump_exec_info(FILE *f, fprintf_function cpu_fprintf); void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf); diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 24d335f95d..18b40d6145 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -23,7 +23,7 @@ typedef struct CPUListState { FILE *file; } CPUListState; -/* The CPU list lock nests outside tb_lock/tb_unlock. */ +/* The CPU list lock nests outside page_(un)lock or mmap_(un)lock */ void qemu_init_cpu_list(void); void cpu_list_lock(void); void cpu_list_unlock(void); @@ -75,6 +75,9 @@ const char *qemu_ram_get_idstr(RAMBlock *rb); bool qemu_ram_is_shared(RAMBlock *rb); bool qemu_ram_is_uf_zeroable(RAMBlock *rb); void qemu_ram_set_uf_zeroable(RAMBlock *rb); +bool qemu_ram_is_migratable(RAMBlock *rb); +void qemu_ram_set_migratable(RAMBlock *rb); +void qemu_ram_unset_migratable(RAMBlock *rb); size_t qemu_ram_pagesize(RAMBlock *block); size_t qemu_ram_pagesize_largest(void); @@ -119,6 +122,7 @@ typedef int (RAMBlockIterFunc)(const char *block_name, void *host_addr, ram_addr_t offset, ram_addr_t length, void *opaque); int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); +int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque); int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); #endif diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index e43ff8346b..a171ffc1a4 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -127,6 +127,15 @@ QEMU_BUILD_BUG_ON(sizeof(CPUTLBEntry) != (1 << CPU_TLB_ENTRY_BITS)); * structs into one.) */ typedef struct CPUIOTLBEntry { + /* + * @addr contains: + * - in the lower TARGET_PAGE_BITS, a physical section number + * - with the lower TARGET_PAGE_BITS masked off, an offset which + * must be added to the virtual address to obtain: + * + the ram_addr_t of the target RAM (if the physical section + * number is PHYS_SECTION_NOTDIRTY or PHYS_SECTION_ROM) + * + the offset within the target MemoryRegion (otherwise) + */ hwaddr addr; MemTxAttrs attrs; } CPUIOTLBEntry; diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 5de8c8a5af..0f2cb717b1 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -422,8 +422,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, target_ulong addr, g_assert_not_reached(); } - if ((addr & TARGET_PAGE_MASK) - != (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { + if (!tlb_hit(tlb_addr, addr)) { /* TLB entry is for a different page */ return NULL; } diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h index c168f31bba..e30e58ed4a 100644 --- a/include/exec/cpu_ldst_useronly_template.h +++ b/include/exec/cpu_ldst_useronly_template.h @@ -33,20 +33,24 @@ #define SUFFIX q #define USUFFIX q #define DATA_TYPE uint64_t +#define SHIFT 3 #elif DATA_SIZE == 4 #define SUFFIX l #define USUFFIX l #define DATA_TYPE uint32_t +#define SHIFT 2 #elif DATA_SIZE == 2 #define SUFFIX w #define USUFFIX uw #define DATA_TYPE uint16_t #define DATA_STYPE int16_t +#define SHIFT 1 #elif DATA_SIZE == 1 #define SUFFIX b #define USUFFIX ub #define DATA_TYPE uint8_t #define DATA_STYPE int8_t +#define SHIFT 0 #else #error unsupported data size #endif @@ -63,7 +67,7 @@ glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) #if !defined(CODE_ACCESS) trace_guest_mem_before_exec( ENV_GET_CPU(env), ptr, - trace_mem_build_info(DATA_SIZE, false, MO_TE, false)); + trace_mem_build_info(SHIFT, false, MO_TE, false)); #endif return glue(glue(ld, USUFFIX), _p)(g2h(ptr)); } @@ -87,7 +91,7 @@ glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) #if !defined(CODE_ACCESS) trace_guest_mem_before_exec( ENV_GET_CPU(env), ptr, - trace_mem_build_info(DATA_SIZE, true, MO_TE, false)); + trace_mem_build_info(SHIFT, true, MO_TE, false)); #endif return glue(glue(lds, SUFFIX), _p)(g2h(ptr)); } @@ -113,7 +117,7 @@ glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, #if !defined(CODE_ACCESS) trace_guest_mem_before_exec( ENV_GET_CPU(env), ptr, - trace_mem_build_info(DATA_SIZE, false, MO_TE, true)); + trace_mem_build_info(SHIFT, false, MO_TE, true)); #endif glue(glue(st, SUFFIX), _p)(g2h(ptr), v); } @@ -136,3 +140,4 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, #undef SUFFIX #undef USUFFIX #undef DATA_SIZE +#undef SHIFT diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index bd68328ed9..da73e3bfed 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -255,7 +255,6 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, void tlb_set_page(CPUState *cpu, target_ulong vaddr, hwaddr paddr, int prot, int mmu_idx, target_ulong size); -void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr); void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, uintptr_t retaddr); #else @@ -299,13 +298,11 @@ static inline void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *cpu, static inline void tlb_flush_by_mmuidx_all_cpus(CPUState *cpu, uint16_t idxmap) { } + static inline void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *cpu, uint16_t idxmap) { } -static inline void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr) -{ -} #endif #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ @@ -344,7 +341,7 @@ struct TranslationBlock { #define CF_LAST_IO 0x00008000 /* Last insn may be an IO access. */ #define CF_NOCACHE 0x00010000 /* To be freed after execution */ #define CF_USE_ICOUNT 0x00020000 -#define CF_INVALID 0x00040000 /* TB is stale. Setters need tb_lock */ +#define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */ #define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */ /* cflags' mask for hashing/comparison */ #define CF_HASH_MASK \ @@ -358,10 +355,14 @@ struct TranslationBlock { /* original tb when cflags has CF_NOCACHE */ struct TranslationBlock *orig_tb; /* first and second physical page containing code. The lower bit - of the pointer tells the index in page_next[] */ - struct TranslationBlock *page_next[2]; + of the pointer tells the index in page_next[]. + The list is protected by the TB's page('s) lock(s) */ + uintptr_t page_next[2]; tb_page_addr_t page_addr[2]; + /* jmp_lock placed here to fill a 4-byte hole. Its documentation is below */ + QemuSpin jmp_lock; + /* The following data are used to directly call another TB from * the code of this one. This can be done either by emitting direct or * indirect native jump instructions. These jumps are reset so that the TB @@ -373,20 +374,26 @@ struct TranslationBlock { #define TB_JMP_RESET_OFFSET_INVALID 0xffff /* indicates no jump generated */ uintptr_t jmp_target_arg[2]; /* target address or offset */ - /* Each TB has an associated circular list of TBs jumping to this one. - * jmp_list_first points to the first TB jumping to this one. - * jmp_list_next is used to point to the next TB in a list. - * Since each TB can have two jumps, it can participate in two lists. - * jmp_list_first and jmp_list_next are 4-byte aligned pointers to a - * TranslationBlock structure, but the two least significant bits of - * them are used to encode which data field of the pointed TB should - * be used to traverse the list further from that TB: - * 0 => jmp_list_next[0], 1 => jmp_list_next[1], 2 => jmp_list_first. - * In other words, 0/1 tells which jump is used in the pointed TB, - * and 2 means that this is a pointer back to the target TB of this list. + /* + * Each TB has a NULL-terminated list (jmp_list_head) of incoming jumps. + * Each TB can have two outgoing jumps, and therefore can participate + * in two lists. The list entries are kept in jmp_list_next[2]. The least + * significant bit (LSB) of the pointers in these lists is used to encode + * which of the two list entries is to be used in the pointed TB. + * + * List traversals are protected by jmp_lock. The destination TB of each + * outgoing jump is kept in jmp_dest[] so that the appropriate jmp_lock + * can be acquired from any origin TB. + * + * jmp_dest[] are tagged pointers as well. The LSB is set when the TB is + * being invalidated, so that no further outgoing jumps from it can be set. + * + * jmp_lock also protects the CF_INVALID cflag; a jump must not be chained + * to a destination TB that has CF_INVALID set. */ + uintptr_t jmp_list_head; uintptr_t jmp_list_next[2]; - uintptr_t jmp_list_first; + uintptr_t jmp_dest[2]; }; extern bool parallel_cpus; @@ -404,7 +411,13 @@ static inline uint32_t curr_cflags(void) | (use_icount ? CF_USE_ICOUNT : 0); } -void tb_remove(TranslationBlock *tb); +/* TranslationBlock invalidate API */ +#if defined(CONFIG_USER_ONLY) +void tb_invalidate_phys_addr(target_ulong addr); +void tb_invalidate_phys_range(target_ulong start, target_ulong end); +#else +void tb_invalidate_phys_addr(AddressSpace *as, hwaddr addr, MemTxAttrs attrs); +#endif void tb_flush(CPUState *cpu); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, @@ -430,14 +443,27 @@ extern uintptr_t tci_tb_ptr; smaller than 4 bytes, so we don't worry about special-casing this. */ #define GETPC_ADJ 2 -void tb_lock(void); -void tb_unlock(void); -void tb_lock_reset(void); +#if !defined(CONFIG_USER_ONLY) && defined(CONFIG_DEBUG_TCG) +void assert_no_pages_locked(void); +#else +static inline void assert_no_pages_locked(void) +{ +} +#endif #if !defined(CONFIG_USER_ONLY) -struct MemoryRegion *iotlb_to_region(CPUState *cpu, - hwaddr index, MemTxAttrs attrs); +/** + * iotlb_to_section: + * @cpu: CPU performing the access + * @index: TCG CPU IOTLB entry + * + * Given a TCG CPU IOTLB entry, return the MemoryRegionSection that + * it refers to. @index will have been initially created and returned + * by memory_region_section_get_iotlb(). + */ +struct MemoryRegionSection *iotlb_to_section(CPUState *cpu, + hwaddr index, MemTxAttrs attrs); void tlb_fill(CPUState *cpu, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); @@ -468,7 +494,8 @@ void tb_flush_jmp_cache(CPUState *cpu, target_ulong addr); MemoryRegionSection * address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, - hwaddr *xlat, hwaddr *plen); + hwaddr *xlat, hwaddr *plen, + MemTxAttrs attrs, int *prot); hwaddr memory_region_section_get_iotlb(CPUState *cpu, MemoryRegionSection *section, target_ulong vaddr, diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 2e8a4b83b9..08363969c1 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -48,6 +48,21 @@ int use_gdb_syscalls(void); void gdb_set_stop_cpu(CPUState *cpu); void gdb_exit(CPUArchState *, int); #ifdef CONFIG_USER_ONLY +/** + * gdb_handlesig: yield control to gdb + * @cpu: CPU + * @sig: if non-zero, the signal number which caused us to stop + * + * This function yields control to gdb, when a user-mode-only target + * needs to stop execution. If @sig is non-zero, then we will send a + * stop packet to tell gdb that we have stopped because of this signal. + * + * This function will block (handling protocol requests from gdb) + * until gdb tells us to continue target execution. When it does + * return, the return value is a signal to deliver to the target, + * or 0 if no signal should be delivered, ie the signal that caused + * us to stop should be ignored. + */ int gdb_handlesig(CPUState *, int); void gdb_signalled(CPUArchState *, int); void gdbserver_fork(CPUState *); diff --git a/include/exec/gen-icount.h b/include/exec/gen-icount.h index 54aaa61d65..24f7991781 100644 --- a/include/exec/gen-icount.h +++ b/include/exec/gen-icount.h @@ -52,7 +52,7 @@ static inline void gen_tb_end(TranslationBlock *tb, int num_insns) } gen_set_label(tcg_ctx->exitreq_label); - tcg_gen_exit_tb((uintptr_t)tb + TB_EXIT_REQUESTED); + tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED); } static inline void gen_io_start(void) diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index 15b6a68de3..276dd5afce 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -39,7 +39,7 @@ #define dh_ctype_int int #define dh_ctype_i64 uint64_t #define dh_ctype_s64 int64_t -#define dh_ctype_f16 float16 +#define dh_ctype_f16 uint32_t #define dh_ctype_f32 float32 #define dh_ctype_f64 float64 #define dh_ctype_ptr void * diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 6a5ee42d36..bb08fa4d2f 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -31,10 +31,14 @@ static inline AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as) return flatview_to_dispatch(address_space_to_flatview(as)); } +FlatView *address_space_get_flatview(AddressSpace *as); +void flatview_unref(FlatView *view); + extern const MemoryRegionOps unassigned_mem_ops; bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, - unsigned size, bool is_write); + unsigned size, bool is_write, + MemTxAttrs attrs); void flatview_add_to_dispatch(FlatView *fv, MemoryRegionSection *section); AddressSpaceDispatch *address_space_dispatch_new(FlatView *fv); @@ -45,6 +49,8 @@ void mtree_print_dispatch(fprintf_function mon, void *f, struct AddressSpaceDispatch *d, MemoryRegion *root); +struct page_collection; + /* Opaque struct for passing info from memory_notdirty_write_prepare() * to memory_notdirty_write_complete(). Callers should treat all fields * as private, with the exception of @active. @@ -56,10 +62,10 @@ void mtree_print_dispatch(fprintf_function mon, void *f, */ typedef struct { CPUState *cpu; + struct page_collection *pages; ram_addr_t ram_addr; vaddr mem_vaddr; unsigned size; - bool locked; bool active; } NotDirtyInfo; @@ -87,7 +93,7 @@ typedef struct { * * This must only be called if we are using TCG; it will assert otherwise. * - * We may take a lock in the prepare call, so callers must ensure that + * We may take locks in the prepare call, so callers must ensure that * they don't exit (via longjump or otherwise) without calling complete. * * This call must only be made inside an RCU critical section. diff --git a/include/exec/memory.h b/include/exec/memory.h index 9e5c741a68..c5ffa98069 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -98,18 +98,21 @@ struct IOMMUNotifier { /* Notify for address space range start <= addr <= end */ hwaddr start; hwaddr end; + int iommu_idx; QLIST_ENTRY(IOMMUNotifier) node; }; typedef struct IOMMUNotifier IOMMUNotifier; static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, - hwaddr start, hwaddr end) + hwaddr start, hwaddr end, + int iommu_idx) { n->notify = fn; n->notifier_flags = flags; n->start = start; n->end = end; + n->iommu_idx = iommu_idx; } /* @@ -166,7 +169,8 @@ struct MemoryRegionOps { * as a machine check exception). */ bool (*accepts)(void *opaque, hwaddr addr, - unsigned size, bool is_write); + unsigned size, bool is_write, + MemTxAttrs attrs); } valid; /* Internal implementation constraints: */ struct { @@ -194,30 +198,139 @@ enum IOMMUMemoryRegionAttr { IOMMU_ATTR_SPAPR_TCE_FD }; +/** + * IOMMUMemoryRegionClass: + * + * All IOMMU implementations need to subclass TYPE_IOMMU_MEMORY_REGION + * and provide an implementation of at least the @translate method here + * to handle requests to the memory region. Other methods are optional. + * + * The IOMMU implementation must use the IOMMU notifier infrastructure + * to report whenever mappings are changed, by calling + * memory_region_notify_iommu() (or, if necessary, by calling + * memory_region_notify_one() for each registered notifier). + * + * Conceptually an IOMMU provides a mapping from input address + * to an output TLB entry. If the IOMMU is aware of memory transaction + * attributes and the output TLB entry depends on the transaction + * attributes, we represent this using IOMMU indexes. Each index + * selects a particular translation table that the IOMMU has: + * @attrs_to_index returns the IOMMU index for a set of transaction attributes + * @translate takes an input address and an IOMMU index + * and the mapping returned can only depend on the input address and the + * IOMMU index. + * + * Most IOMMUs don't care about the transaction attributes and support + * only a single IOMMU index. A more complex IOMMU might have one index + * for secure transactions and one for non-secure transactions. + */ typedef struct IOMMUMemoryRegionClass { /* private */ struct DeviceClass parent_class; /* - * Return a TLB entry that contains a given address. Flag should - * be the access permission of this translation operation. We can - * set flag to IOMMU_NONE to mean that we don't need any - * read/write permission checks, like, when for region replay. + * Return a TLB entry that contains a given address. + * + * The IOMMUAccessFlags indicated via @flag are optional and may + * be specified as IOMMU_NONE to indicate that the caller needs + * the full translation information for both reads and writes. If + * the access flags are specified then the IOMMU implementation + * may use this as an optimization, to stop doing a page table + * walk as soon as it knows that the requested permissions are not + * allowed. If IOMMU_NONE is passed then the IOMMU must do the + * full page table walk and report the permissions in the returned + * IOMMUTLBEntry. (Note that this implies that an IOMMU may not + * return different mappings for reads and writes.) + * + * The returned information remains valid while the caller is + * holding the big QEMU lock or is inside an RCU critical section; + * if the caller wishes to cache the mapping beyond that it must + * register an IOMMU notifier so it can invalidate its cached + * information when the IOMMU mapping changes. + * + * @iommu: the IOMMUMemoryRegion + * @hwaddr: address to be translated within the memory region + * @flag: requested access permissions + * @iommu_idx: IOMMU index for the translation */ IOMMUTLBEntry (*translate)(IOMMUMemoryRegion *iommu, hwaddr addr, - IOMMUAccessFlags flag); - /* Returns minimum supported page size */ + IOMMUAccessFlags flag, int iommu_idx); + /* Returns minimum supported page size in bytes. + * If this method is not provided then the minimum is assumed to + * be TARGET_PAGE_SIZE. + * + * @iommu: the IOMMUMemoryRegion + */ uint64_t (*get_min_page_size)(IOMMUMemoryRegion *iommu); - /* Called when IOMMU Notifier flag changed */ + /* Called when IOMMU Notifier flag changes (ie when the set of + * events which IOMMU users are requesting notification for changes). + * Optional method -- need not be provided if the IOMMU does not + * need to know exactly which events must be notified. + * + * @iommu: the IOMMUMemoryRegion + * @old_flags: events which previously needed to be notified + * @new_flags: events which now need to be notified + */ void (*notify_flag_changed)(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old_flags, IOMMUNotifierFlag new_flags); - /* Set this up to provide customized IOMMU replay function */ + /* Called to handle memory_region_iommu_replay(). + * + * The default implementation of memory_region_iommu_replay() is to + * call the IOMMU translate method for every page in the address space + * with flag == IOMMU_NONE and then call the notifier if translate + * returns a valid mapping. If this method is implemented then it + * overrides the default behaviour, and must provide the full semantics + * of memory_region_iommu_replay(), by calling @notifier for every + * translation present in the IOMMU. + * + * Optional method -- an IOMMU only needs to provide this method + * if the default is inefficient or produces undesirable side effects. + * + * Note: this is not related to record-and-replay functionality. + */ void (*replay)(IOMMUMemoryRegion *iommu, IOMMUNotifier *notifier); - /* Get IOMMU misc attributes */ - int (*get_attr)(IOMMUMemoryRegion *iommu, enum IOMMUMemoryRegionAttr, + /* Get IOMMU misc attributes. This is an optional method that + * can be used to allow users of the IOMMU to get implementation-specific + * information. The IOMMU implements this method to handle calls + * by IOMMU users to memory_region_iommu_get_attr() by filling in + * the arbitrary data pointer for any IOMMUMemoryRegionAttr values that + * the IOMMU supports. If the method is unimplemented then + * memory_region_iommu_get_attr() will always return -EINVAL. + * + * @iommu: the IOMMUMemoryRegion + * @attr: attribute being queried + * @data: memory to fill in with the attribute data + * + * Returns 0 on success, or a negative errno; in particular + * returns -EINVAL for unrecognized or unimplemented attribute types. + */ + int (*get_attr)(IOMMUMemoryRegion *iommu, enum IOMMUMemoryRegionAttr attr, void *data); + + /* Return the IOMMU index to use for a given set of transaction attributes. + * + * Optional method: if an IOMMU only supports a single IOMMU index then + * the default implementation of memory_region_iommu_attrs_to_index() + * will return 0. + * + * The indexes supported by an IOMMU must be contiguous, starting at 0. + * + * @iommu: the IOMMUMemoryRegion + * @attrs: memory transaction attributes + */ + int (*attrs_to_index)(IOMMUMemoryRegion *iommu, MemTxAttrs attrs); + + /* Return the number of IOMMU indexes this IOMMU supports. + * + * Optional method: if this method is not provided, then + * memory_region_iommu_num_indexes() will return 1, indicating that + * only a single IOMMU index is supported. + * + * @iommu: the IOMMUMemoryRegion + */ + int (*num_indexes)(IOMMUMemoryRegion *iommu); } IOMMUMemoryRegionClass; typedef struct CoalescedMemoryRange CoalescedMemoryRange; @@ -675,29 +788,6 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, uint64_t size, Error **errp); -/** - * memory_region_init_reservation: Initialize a memory region that reserves - * I/O space. - * - * A reservation region primariy serves debugging purposes. It claims I/O - * space that is not supposed to be handled by QEMU itself. Any access via - * the memory API will cause an abort(). - * This function is deprecated. Use memory_region_init_io() with NULL - * callbacks instead. - * - * @mr: the #MemoryRegion to be initialized - * @owner: the object that tracks the region's reference count - * @name: used for debugging; not visible to the user or ABI - * @size: size of the region. - */ -static inline void memory_region_init_reservation(MemoryRegion *mr, - Object *owner, - const char *name, - uint64_t size) -{ - memory_region_init_io(mr, owner, NULL, mr, name, size); -} - /** * memory_region_init_iommu: Initialize a memory region of a custom type * that translates addresses @@ -705,6 +795,14 @@ static inline void memory_region_init_reservation(MemoryRegion *mr, * An IOMMU region translates addresses and forwards accesses to a target * memory region. * + * The IOMMU implementation must define a subclass of TYPE_IOMMU_MEMORY_REGION. + * @_iommu_mr should be a pointer to enough memory for an instance of + * that subclass, @instance_size is the size of that subclass, and + * @mrtypename is its name. This function will initialize @_iommu_mr as an + * instance of the subclass, and its methods will then be called to handle + * accesses to the memory region. See the documentation of + * #IOMMUMemoryRegionClass for further details. + * * @_iommu_mr: the #IOMMUMemoryRegion to be initialized * @instance_size: the IOMMUMemoryRegion subclass instance size * @mrtypename: the type name of the #IOMMUMemoryRegion @@ -914,11 +1012,13 @@ uint64_t memory_region_iommu_get_min_page_size(IOMMUMemoryRegion *iommu_mr); * should be notified with an UNMAP followed by a MAP. * * @iommu_mr: the memory region that was changed + * @iommu_idx: the IOMMU index for the translation table which has changed * @entry: the new entry in the IOMMU translation table. The entry * replaces all old entries for the same virtual I/O address range. * Deleted entries have .@perm == 0. */ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, + int iommu_idx, IOMMUTLBEntry entry); /** @@ -953,6 +1053,8 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, * a notifier with the minimum page granularity returned by * mr->iommu_ops->get_page_size(). * + * Note: this is not related to record-and-replay functionality. + * * @iommu_mr: the memory region to observe * @n: the notifier to which to replay iommu mappings */ @@ -962,6 +1064,8 @@ void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n); * memory_region_iommu_replay_all: replay existing IOMMU translations * to all the notifiers registered. * + * Note: this is not related to record-and-replay functionality. + * * @iommu_mr: the memory region to observe */ void memory_region_iommu_replay_all(IOMMUMemoryRegion *iommu_mr); @@ -981,7 +1085,9 @@ void memory_region_unregister_iommu_notifier(MemoryRegion *mr, * memory_region_iommu_get_attr: return an IOMMU attr if get_attr() is * defined on the IOMMU. * - * Returns 0 if succeded, error code otherwise. + * Returns 0 on success, or a negative errno otherwise. In particular, + * -EINVAL indicates that the IOMMU does not support the requested + * attribute. * * @iommu_mr: the memory region * @attr: the requested attribute @@ -991,6 +1097,24 @@ int memory_region_iommu_get_attr(IOMMUMemoryRegion *iommu_mr, enum IOMMUMemoryRegionAttr attr, void *data); +/** + * memory_region_iommu_attrs_to_index: return the IOMMU index to + * use for translations with the given memory transaction attributes. + * + * @iommu_mr: the memory region + * @attrs: the memory transaction attributes + */ +int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr, + MemTxAttrs attrs); + +/** + * memory_region_iommu_num_indexes: return the total number of IOMMU + * indexes that this IOMMU supports. + * + * @iommu_mr: the memory region + */ +int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr); + /** * memory_region_name: get a memory region's name * @@ -1560,7 +1684,7 @@ void memory_global_dirty_log_start(void); void memory_global_dirty_log_stop(void); void mtree_info(fprintf_function mon_printf, void *f, bool flatview, - bool dispatch_tree); + bool dispatch_tree, bool owner); /** * memory_region_request_mmio_ptr: request a pointer to an mmio @@ -1695,57 +1819,91 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, * @result: location to write the success/failure of the transaction; * if NULL, this information is discarded */ -uint32_t address_space_ldub(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_lduw_le(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_lduw_be(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_le(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_be(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_le(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_be(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stb(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_le(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_be(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_le(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_be(AddressSpace *as, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_le(AddressSpace *as, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_be(AddressSpace *as, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); -uint32_t ldub_phys(AddressSpace *as, hwaddr addr); -uint32_t lduw_le_phys(AddressSpace *as, hwaddr addr); -uint32_t lduw_be_phys(AddressSpace *as, hwaddr addr); -uint32_t ldl_le_phys(AddressSpace *as, hwaddr addr); -uint32_t ldl_be_phys(AddressSpace *as, hwaddr addr); -uint64_t ldq_le_phys(AddressSpace *as, hwaddr addr); -uint64_t ldq_be_phys(AddressSpace *as, hwaddr addr); -void stb_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stw_le_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stw_be_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stl_le_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stl_be_phys(AddressSpace *as, hwaddr addr, uint32_t val); -void stq_le_phys(AddressSpace *as, hwaddr addr, uint64_t val); -void stq_be_phys(AddressSpace *as, hwaddr addr, uint64_t val); +#define SUFFIX +#define ARG1 as +#define ARG1_DECL AddressSpace *as +#include "exec/memory_ldst.inc.h" + +#define SUFFIX +#define ARG1 as +#define ARG1_DECL AddressSpace *as +#include "exec/memory_ldst_phys.inc.h" struct MemoryRegionCache { + void *ptr; hwaddr xlat; hwaddr len; - AddressSpace *as; + FlatView *fv; + MemoryRegionSection mrs; + bool is_write; }; -#define MEMORY_REGION_CACHE_INVALID ((MemoryRegionCache) { .as = NULL }) +#define MEMORY_REGION_CACHE_INVALID ((MemoryRegionCache) { .mrs.mr = NULL }) + + +/* address_space_ld*_cached: load from a cached #MemoryRegion + * address_space_st*_cached: store into a cached #MemoryRegion + * + * These functions perform a load or store of the byte, word, + * longword or quad to the specified address. The address is + * a physical address in the AddressSpace, but it must lie within + * a #MemoryRegion that was mapped with address_space_cache_init. + * + * The _le suffixed functions treat the data as little endian; + * _be indicates big endian; no suffix indicates "same endianness + * as guest CPU". + * + * The "guest CPU endianness" accessors are deprecated for use outside + * target-* code; devices should be CPU-agnostic and use either the LE + * or the BE accessors. + * + * @cache: previously initialized #MemoryRegionCache to be accessed + * @addr: address within the address space + * @val: data value, for stores + * @attrs: memory transaction attributes + * @result: location to write the success/failure of the transaction; + * if NULL, this information is discarded + */ + +#define SUFFIX _cached_slow +#define ARG1 cache +#define ARG1_DECL MemoryRegionCache *cache +#include "exec/memory_ldst.inc.h" + +/* Inline fast path for direct RAM access. */ +static inline uint8_t address_space_ldub_cached(MemoryRegionCache *cache, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len); + if (likely(cache->ptr)) { + return ldub_p(cache->ptr + addr); + } else { + return address_space_ldub_cached_slow(cache, addr, attrs, result); + } +} + +static inline void address_space_stb_cached(MemoryRegionCache *cache, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len); + if (likely(cache->ptr)) { + stb_p(cache->ptr + addr, val); + } else { + address_space_stb_cached_slow(cache, addr, val, attrs, result); + } +} + +#define ENDIANNESS _le +#include "exec/memory_ldst_cached.inc.h" + +#define ENDIANNESS _be +#include "exec/memory_ldst_cached.inc.h" + +#define SUFFIX _cached +#define ARG1 cache +#define ARG1_DECL MemoryRegionCache *cache +#include "exec/memory_ldst_phys.inc.h" /* address_space_cache_init: prepare for repeated access to a physical * memory region @@ -1791,77 +1949,11 @@ void address_space_cache_invalidate(MemoryRegionCache *cache, */ void address_space_cache_destroy(MemoryRegionCache *cache); -/* address_space_ld*_cached: load from a cached #MemoryRegion - * address_space_st*_cached: store into a cached #MemoryRegion - * - * These functions perform a load or store of the byte, word, - * longword or quad to the specified address. The address is - * a physical address in the AddressSpace, but it must lie within - * a #MemoryRegion that was mapped with address_space_cache_init. - * - * The _le suffixed functions treat the data as little endian; - * _be indicates big endian; no suffix indicates "same endianness - * as guest CPU". - * - * The "guest CPU endianness" accessors are deprecated for use outside - * target-* code; devices should be CPU-agnostic and use either the LE - * or the BE accessors. - * - * @cache: previously initialized #MemoryRegionCache to be accessed - * @addr: address within the address space - * @val: data value, for stores - * @attrs: memory transaction attributes - * @result: location to write the success/failure of the transaction; - * if NULL, this information is discarded - */ -uint32_t address_space_ldub_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_lduw_le_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_lduw_be_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_le_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint32_t address_space_ldl_be_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_le_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -uint64_t address_space_ldq_be_cached(MemoryRegionCache *cache, hwaddr addr, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stb_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_le_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stw_be_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_le_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stl_be_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_le_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); -void address_space_stq_be_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val, - MemTxAttrs attrs, MemTxResult *result); - -uint32_t ldub_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t lduw_le_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t lduw_be_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t ldl_le_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint32_t ldl_be_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint64_t ldq_le_phys_cached(MemoryRegionCache *cache, hwaddr addr); -uint64_t ldq_be_phys_cached(MemoryRegionCache *cache, hwaddr addr); -void stb_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stw_le_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stw_be_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stl_le_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stl_be_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint32_t val); -void stq_le_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val); -void stq_be_phys_cached(MemoryRegionCache *cache, hwaddr addr, uint64_t val); /* address_space_get_iotlb_entry: translate an address into an IOTLB * entry. Should be called from an RCU critical section. */ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, - bool is_write); + bool is_write, MemTxAttrs attrs); /* address_space_translate: translate an address range into an address space * into a MemoryRegion and an address range into that section. Should be @@ -1874,17 +1966,20 @@ IOMMUTLBEntry address_space_get_iotlb_entry(AddressSpace *as, hwaddr addr, * #MemoryRegion. * @len: pointer to length * @is_write: indicates the transfer direction + * @attrs: memory attributes */ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, - hwaddr *len, bool is_write); + hwaddr *len, bool is_write, + MemTxAttrs attrs); static inline MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, hwaddr *xlat, - hwaddr *len, bool is_write) + hwaddr *len, bool is_write, + MemTxAttrs attrs) { return flatview_translate(address_space_to_flatview(as), - addr, xlat, len, is_write); + addr, xlat, len, is_write, attrs); } /* address_space_access_valid: check for validity of accessing an address @@ -1901,8 +1996,10 @@ static inline MemoryRegion *address_space_translate(AddressSpace *as, * @addr: address within that address space * @len: length of the area to be checked * @is_write: indicates the transfer direction + * @attrs: memory attributes */ -bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_write); +bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, + bool is_write, MemTxAttrs attrs); /* address_space_map: map a physical memory region into a host virtual address * @@ -1916,9 +2013,10 @@ bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, bool is_ * @addr: address within that address space * @plen: pointer to length of buffer; updated on return * @is_write: indicates the transfer direction + * @attrs: memory attributes */ void *address_space_map(AddressSpace *as, hwaddr addr, - hwaddr *plen, bool is_write); + hwaddr *plen, bool is_write, MemTxAttrs attrs); /* address_space_unmap: Unmaps a memory region previously mapped by address_space_map() * @@ -1944,6 +2042,13 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, MemoryRegion *mr); void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr); +/* Internal functions, part of the implementation of address_space_read_cached + * and address_space_write_cached. */ +void address_space_read_cached_slow(MemoryRegionCache *cache, + hwaddr addr, void *buf, int len); +void address_space_write_cached_slow(MemoryRegionCache *cache, + hwaddr addr, const void *buf, int len); + static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) { if (is_write) { @@ -1983,7 +2088,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, rcu_read_lock(); fv = address_space_to_flatview(as); l = len; - mr = flatview_translate(fv, addr, &addr1, &l, false); + mr = flatview_translate(fv, addr, &addr1, &l, false, attrs); if (len == l && memory_access_is_direct(mr, false)) { ptr = qemu_map_ram_ptr(mr->ram_block, addr1); memcpy(buf, ptr, len); @@ -2012,7 +2117,11 @@ address_space_read_cached(MemoryRegionCache *cache, hwaddr addr, void *buf, int len) { assert(addr < cache->len && len <= cache->len - addr); - address_space_read(cache->as, cache->xlat + addr, MEMTXATTRS_UNSPECIFIED, buf, len); + if (likely(cache->ptr)) { + memcpy(buf, cache->ptr + addr, len); + } else { + address_space_read_cached_slow(cache, addr, buf, len); + } } /** @@ -2028,7 +2137,11 @@ address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, void *buf, int len) { assert(addr < cache->len && len <= cache->len - addr); - address_space_write(cache->as, cache->xlat + addr, MEMTXATTRS_UNSPECIFIED, buf, len); + if (likely(cache->ptr)) { + memcpy(cache->ptr + addr, buf, len); + } else { + address_space_write_cached_slow(cache, addr, buf, len); + } } void memory_region_destroy(MemoryRegion *mr); diff --git a/include/exec/memory_ldst.inc.h b/include/exec/memory_ldst.inc.h new file mode 100644 index 0000000000..272c20f02e --- /dev/null +++ b/include/exec/memory_ldst.inc.h @@ -0,0 +1,71 @@ +/* + * Physical memory access templates + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2015 Linaro, Inc. + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifdef TARGET_ENDIANNESS +extern uint32_t glue(address_space_lduw, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_ldl, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint64_t glue(address_space_ldq, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stw, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stl, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stq, SUFFIX)(ARG1_DECL, + hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); +#else +extern uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_lduw_le, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_lduw_be, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_ldl_le, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint32_t glue(address_space_ldl_be, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint64_t glue(address_space_ldq_le, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern uint64_t glue(address_space_ldq_be, SUFFIX)(ARG1_DECL, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stb, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stw_le, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stw_be, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stl_le, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stl_be, SUFFIX)(ARG1_DECL, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stq_le, SUFFIX)(ARG1_DECL, + hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); +extern void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, + hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); +#endif + +#undef ARG1_DECL +#undef ARG1 +#undef SUFFIX +#undef TARGET_ENDIANNESS diff --git a/include/exec/memory_ldst_cached.inc.h b/include/exec/memory_ldst_cached.inc.h new file mode 100644 index 0000000000..fd4bbb40e7 --- /dev/null +++ b/include/exec/memory_ldst_cached.inc.h @@ -0,0 +1,108 @@ +/* + * Memory access templates for MemoryRegionCache + * + * Copyright (c) 2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#define ADDRESS_SPACE_LD_CACHED(size) \ + glue(glue(address_space_ld, size), glue(ENDIANNESS, _cached)) +#define ADDRESS_SPACE_LD_CACHED_SLOW(size) \ + glue(glue(address_space_ld, size), glue(ENDIANNESS, _cached_slow)) +#define LD_P(size) \ + glue(glue(ld, size), glue(ENDIANNESS, _p)) + +static inline uint32_t ADDRESS_SPACE_LD_CACHED(l)(MemoryRegionCache *cache, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 4 <= cache->len - addr); + if (likely(cache->ptr)) { + return LD_P(l)(cache->ptr + addr); + } else { + return ADDRESS_SPACE_LD_CACHED_SLOW(l)(cache, addr, attrs, result); + } +} + +static inline uint64_t ADDRESS_SPACE_LD_CACHED(q)(MemoryRegionCache *cache, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 8 <= cache->len - addr); + if (likely(cache->ptr)) { + return LD_P(q)(cache->ptr + addr); + } else { + return ADDRESS_SPACE_LD_CACHED_SLOW(q)(cache, addr, attrs, result); + } +} + +static inline uint32_t ADDRESS_SPACE_LD_CACHED(uw)(MemoryRegionCache *cache, + hwaddr addr, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 2 <= cache->len - addr); + if (likely(cache->ptr)) { + return LD_P(uw)(cache->ptr + addr); + } else { + return ADDRESS_SPACE_LD_CACHED_SLOW(uw)(cache, addr, attrs, result); + } +} + +#undef ADDRESS_SPACE_LD_CACHED +#undef ADDRESS_SPACE_LD_CACHED_SLOW +#undef LD_P + +#define ADDRESS_SPACE_ST_CACHED(size) \ + glue(glue(address_space_st, size), glue(ENDIANNESS, _cached)) +#define ADDRESS_SPACE_ST_CACHED_SLOW(size) \ + glue(glue(address_space_st, size), glue(ENDIANNESS, _cached_slow)) +#define ST_P(size) \ + glue(glue(st, size), glue(ENDIANNESS, _p)) + +static inline void ADDRESS_SPACE_ST_CACHED(l)(MemoryRegionCache *cache, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 4 <= cache->len - addr); + if (likely(cache->ptr)) { + ST_P(l)(cache->ptr + addr, val); + } else { + ADDRESS_SPACE_ST_CACHED_SLOW(l)(cache, addr, val, attrs, result); + } +} + +static inline void ADDRESS_SPACE_ST_CACHED(w)(MemoryRegionCache *cache, + hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 2 <= cache->len - addr); + if (likely(cache->ptr)) { + ST_P(w)(cache->ptr + addr, val); + } else { + ADDRESS_SPACE_ST_CACHED_SLOW(w)(cache, addr, val, attrs, result); + } +} + +static inline void ADDRESS_SPACE_ST_CACHED(q)(MemoryRegionCache *cache, + hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result) +{ + assert(addr < cache->len && 8 <= cache->len - addr); + if (likely(cache->ptr)) { + ST_P(q)(cache->ptr + addr, val); + } else { + ADDRESS_SPACE_ST_CACHED_SLOW(q)(cache, addr, val, attrs, result); + } +} + +#undef ADDRESS_SPACE_ST_CACHED +#undef ADDRESS_SPACE_ST_CACHED_SLOW +#undef ST_P + +#undef ENDIANNESS diff --git a/include/exec/memory_ldst_phys.inc.h b/include/exec/memory_ldst_phys.inc.h new file mode 100644 index 0000000000..91f72973cb --- /dev/null +++ b/include/exec/memory_ldst_phys.inc.h @@ -0,0 +1,147 @@ +/* + * Physical memory access templates + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2015 Linaro, Inc. + * Copyright (c) 2016 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifdef TARGET_ENDIANNESS +static inline uint32_t glue(ldl_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldl, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint64_t glue(ldq_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldq, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(lduw_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_lduw, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stl_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stl, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stw_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stw, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stq_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) +{ + glue(address_space_stq, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} +#else +static inline uint32_t glue(ldl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldl_le, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(ldl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldl_be, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint64_t glue(ldq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldq_le, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint64_t glue(ldq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldq_be, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(ldub_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_ldub, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(lduw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_lduw_le, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline uint32_t glue(lduw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) +{ + return glue(address_space_lduw_be, SUFFIX)(ARG1, addr, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stl_le, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stl_be, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stb_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stb, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stw_le, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) +{ + glue(address_space_stw_be, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) +{ + glue(address_space_stq_le, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + +static inline void glue(stq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) +{ + glue(address_space_stq_be, SUFFIX)(ARG1, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} +#endif + +#undef ARG1_DECL +#undef ARG1 +#undef SUFFIX +#undef TARGET_ENDIANNESS diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 45cab31f18..65b6522237 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -71,7 +71,6 @@ static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr, } long qemu_getrampagesize(void); -unsigned long last_ram_page(void); RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, bool share, const char *mem_path, Error **errp); @@ -94,6 +93,8 @@ int qemu_ram_resize(RAMBlock *block, ram_addr_t newsize, Error **errp); #define DIRTY_CLIENTS_ALL ((1 << DIRTY_MEMORY_NUM) - 1) #define DIRTY_CLIENTS_NOCODE (DIRTY_CLIENTS_ALL & ~(1 << DIRTY_MEMORY_CODE)) +void tb_invalidate_phys_range(ram_addr_t start, ram_addr_t end); + static inline bool cpu_physical_memory_get_dirty(ram_addr_t start, ram_addr_t length, unsigned client) diff --git a/include/exec/ramlist.h b/include/exec/ramlist.h index b85bd709a4..23bcd3d343 100644 --- a/include/exec/ramlist.h +++ b/include/exec/ramlist.h @@ -57,8 +57,10 @@ typedef struct RAMList { extern RAMList ram_list; /* Should be holding either ram_list.mutex, or the RCU lock. */ -#define RAMBLOCK_FOREACH(block) \ +#define INTERNAL_RAMBLOCK_FOREACH(block) \ QLIST_FOREACH_RCU(block, &ram_list.blocks, next) +/* Never use the INTERNAL_ version except for defining other macros */ +#define RAMBLOCK_FOREACH(block) INTERNAL_RAMBLOCK_FOREACH(block) void qemu_mutex_lock_ramlist(void); void qemu_mutex_unlock_ramlist(void); diff --git a/include/exec/tb-context.h b/include/exec/tb-context.h index 1d41202485..feb585e0a7 100644 --- a/include/exec/tb-context.h +++ b/include/exec/tb-context.h @@ -31,14 +31,10 @@ typedef struct TBContext TBContext; struct TBContext { - GTree *tb_tree; struct qht htable; - /* any access to the tbs or the page table must use this lock */ - QemuMutex tb_lock; /* statistics */ unsigned tb_flush_count; - int tb_phys_invalidate_count; }; extern TBContext tb_ctx; diff --git a/include/exec/translator.h b/include/exec/translator.h index e2dc2a04ae..71e7b2c347 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -58,6 +58,7 @@ typedef enum DisasJumpType { * disassembly). * @is_jmp: What instruction to disassemble next. * @num_insns: Number of translated instructions (including current). + * @max_insns: Maximum number of instructions to be translated in this TB. * @singlestep_enabled: "Hardware" single stepping enabled. * * Architecture-agnostic disassembly context. @@ -67,7 +68,8 @@ typedef struct DisasContextBase { target_ulong pc_first; target_ulong pc_next; DisasJumpType is_jmp; - unsigned int num_insns; + int num_insns; + int max_insns; bool singlestep_enabled; } DisasContextBase; @@ -76,7 +78,6 @@ typedef struct DisasContextBase { * @init_disas_context: * Initialize the target-specific portions of DisasContext struct. * The generic DisasContextBase has already been initialized. - * Return max_insns, modified as necessary by db->tb->flags. * * @tb_start: * Emit any code required before the start of the main loop, @@ -106,8 +107,7 @@ typedef struct DisasContextBase { * Print instruction disassembly to log. */ typedef struct TranslatorOps { - int (*init_disas_context)(DisasContextBase *db, CPUState *cpu, - int max_insns); + void (*init_disas_context)(DisasContextBase *db, CPUState *cpu); void (*tb_start)(DisasContextBase *db, CPUState *cpu); void (*insn_start)(DisasContextBase *db, CPUState *cpu); bool (*breakpoint_check)(DisasContextBase *db, CPUState *cpu, diff --git a/include/exec/user/abitypes.h b/include/exec/user/abitypes.h index ba188608c2..743b8bb9ea 100644 --- a/include/exec/user/abitypes.h +++ b/include/exec/user/abitypes.h @@ -15,7 +15,7 @@ #define ABI_LLONG_ALIGNMENT 2 #endif -#if defined(TARGET_I386) && !defined(TARGET_X86_64) +#if (defined(TARGET_I386) && !defined(TARGET_X86_64)) || defined(TARGET_SH4) #define ABI_LLONG_ALIGNMENT 4 #endif diff --git a/include/exec/user/thunk.h b/include/exec/user/thunk.h index f19ef4b230..8d3af5a3be 100644 --- a/include/exec/user/thunk.h +++ b/include/exec/user/thunk.h @@ -135,7 +135,7 @@ static inline int thunk_type_size(const argtype *type_ptr, int is_host) se = struct_entries + type_ptr[1]; return se->size[is_host]; default: - return -1; + g_assert_not_reached(); } } @@ -149,20 +149,32 @@ static inline int thunk_type_align(const argtype *type_ptr, int is_host) case TYPE_CHAR: return 1; case TYPE_SHORT: - return 2; + if (is_host) { + return __alignof__(short); + } else { + return ABI_SHORT_ALIGNMENT; + } case TYPE_INT: - return 4; + if (is_host) { + return __alignof__(int); + } else { + return ABI_INT_ALIGNMENT; + } case TYPE_LONGLONG: case TYPE_ULONGLONG: - return 8; + if (is_host) { + return __alignof__(long long); + } else { + return ABI_LLONG_ALIGNMENT; + } case TYPE_LONG: case TYPE_ULONG: case TYPE_PTRVOID: case TYPE_PTR: if (is_host) { - return sizeof(void *); + return __alignof__(long); } else { - return TARGET_ABI_BITS / 8; + return ABI_LONG_ALIGNMENT; } break; case TYPE_OLDDEVT: @@ -173,7 +185,7 @@ static inline int thunk_type_align(const argtype *type_ptr, int is_host) se = struct_entries + type_ptr[1]; return se->align[is_host]; default: - return -1; + g_assert_not_reached(); } } diff --git a/include/fpu/softfloat-types.h b/include/fpu/softfloat-types.h index 4e378cb612..2aae6a89b1 100644 --- a/include/fpu/softfloat-types.h +++ b/include/fpu/softfloat-types.h @@ -173,6 +173,7 @@ typedef struct float_status { /* should denormalised inputs go to zero and set the input_denormal flag? */ flag flush_inputs_to_zero; flag default_nan_mode; + /* not always used -- see snan_bit_is_one() in softfloat-specialize.h */ flag snan_bit_is_one; } float_status; diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 36626a501b..69f4dbc4db 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -211,10 +211,10 @@ float128 uint64_to_float128(uint64_t, float_status *status); /*---------------------------------------------------------------------------- | Software half-precision conversion routines. *----------------------------------------------------------------------------*/ -float16 float32_to_float16(float32, flag, float_status *status); -float32 float16_to_float32(float16, flag, float_status *status); -float16 float64_to_float16(float64 a, flag ieee, float_status *status); -float64 float16_to_float64(float16 a, flag ieee, float_status *status); +float16 float32_to_float16(float32, bool ieee, float_status *status); +float32 float16_to_float32(float16, bool ieee, float_status *status); +float16 float64_to_float16(float64 a, bool ieee, float_status *status); +float64 float16_to_float64(float16 a, bool ieee, float_status *status); int16_t float16_to_int16(float16, float_status *status); uint16_t float16_to_uint16(float16 a, float_status *status); int16_t float16_to_int16_round_to_zero(float16, float_status *status); @@ -257,7 +257,7 @@ int float16_compare_quiet(float16, float16, float_status *status); int float16_is_quiet_nan(float16, float_status *status); int float16_is_signaling_nan(float16, float_status *status); -float16 float16_maybe_silence_nan(float16, float_status *status); +float16 float16_silence_nan(float16, float_status *status); static inline int float16_is_any_nan(float16 a) { @@ -368,7 +368,7 @@ float32 float32_minnummag(float32, float32, float_status *status); float32 float32_maxnummag(float32, float32, float_status *status); int float32_is_quiet_nan(float32, float_status *status); int float32_is_signaling_nan(float32, float_status *status); -float32 float32_maybe_silence_nan(float32, float_status *status); +float32 float32_silence_nan(float32, float_status *status); float32 float32_scalbn(float32, int, float_status *status); static inline float32 float32_abs(float32 a) @@ -497,7 +497,7 @@ float64 float64_minnummag(float64, float64, float_status *status); float64 float64_maxnummag(float64, float64, float_status *status); int float64_is_quiet_nan(float64 a, float_status *status); int float64_is_signaling_nan(float64, float_status *status); -float64 float64_maybe_silence_nan(float64, float_status *status); +float64 float64_silence_nan(float64, float_status *status); float64 float64_scalbn(float64, int, float_status *status); static inline float64 float64_abs(float64 a) @@ -600,7 +600,7 @@ int floatx80_compare(floatx80, floatx80, float_status *status); int floatx80_compare_quiet(floatx80, floatx80, float_status *status); int floatx80_is_quiet_nan(floatx80, float_status *status); int floatx80_is_signaling_nan(floatx80, float_status *status); -floatx80 floatx80_maybe_silence_nan(floatx80, float_status *status); +floatx80 floatx80_silence_nan(floatx80, float_status *status); floatx80 floatx80_scalbn(floatx80, int, float_status *status); static inline floatx80 floatx80_abs(floatx80 a) @@ -811,7 +811,7 @@ int float128_compare(float128, float128, float_status *status); int float128_compare_quiet(float128, float128, float_status *status); int float128_is_quiet_nan(float128, float_status *status); int float128_is_signaling_nan(float128, float_status *status); -float128 float128_maybe_silence_nan(float128, float_status *status); +float128 float128_silence_nan(float128, float_status *status); float128 float128_scalbn(float128, int, float_status *status); static inline float128 float128_abs(float128 a) diff --git a/include/glib-compat.h b/include/glib-compat.h index c49cf87196..fdf95a255d 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -16,28 +16,73 @@ #ifndef QEMU_GLIB_COMPAT_H #define QEMU_GLIB_COMPAT_H +/* Ask for warnings for anything that was marked deprecated in + * the defined version, or before. It is a candidate for rewrite. + */ +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_40 + +/* Ask for warnings if code tries to use function that did not + * exist in the defined version. These risk breaking builds + */ +#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_40 + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + #include -/* GLIB version compatibility flags */ -#if !GLIB_CHECK_VERSION(2, 26, 0) -#define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000)) -#endif +/* + * Note that because of the GLIB_VERSION_MAX_ALLOWED constant above, allowing + * use of functions from newer GLib via this compat header needs a little + * trickery to prevent warnings being emitted. + * + * Consider a function from newer glib-X.Y that we want to use + * + * 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. + * + * static inline void g_foo_qemu(const char *wibble) + * { + * #if GLIB_CHECK_VERSION(X, Y, 0) + * g_foo(wibble) + * #else + * g_something_equivalent_in_older_glib(wibble); + * #endif + * } + * + * The #pragma at the top of this file turns off -Wdeprecated-declarations, + * 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) + * + * So now the code elsewhere in QEMU, which *does* have the + * -Wdeprecated-declarations warning active, can call g_foo(...) as normal, + * without generating warnings. + */ -#if !GLIB_CHECK_VERSION(2, 28, 0) -static inline gint64 qemu_g_get_monotonic_time(void) +static inline gboolean g_strv_contains_qemu(const gchar *const *strv, + const gchar *str) { - /* g_get_monotonic_time() is best-effort so we can use the wall clock as a - * fallback. - */ +#if GLIB_CHECK_VERSION(2, 44, 0) + return g_strv_contains(strv, str); +#else + g_return_val_if_fail(strv != NULL, FALSE); + g_return_val_if_fail(str != NULL, FALSE); - GTimeVal time; - g_get_current_time(&time); + for (; *strv != NULL; strv++) { + if (g_str_equal(str, *strv)) { + return TRUE; + } + } - return time.tv_sec * G_TIME_SPAN_SECOND + time.tv_usec; -} -/* work around distro backports of this interface */ -#define g_get_monotonic_time() qemu_g_get_monotonic_time() + return FALSE; #endif +} +#define g_strv_contains(a, b) g_strv_contains_qemu(a, b) + #if defined(_WIN32) && !GLIB_CHECK_VERSION(2, 50, 0) /* @@ -48,228 +93,6 @@ static inline gint64 qemu_g_get_monotonic_time(void) gint g_poll_fixed(GPollFD *fds, guint nfds, gint timeout); #endif -#if !GLIB_CHECK_VERSION(2, 30, 0) -/* Not a 100% compatible implementation, but good enough for most - * cases. Placeholders are only supported at the end of the - * template. */ -static inline gchar *qemu_g_dir_make_tmp(gchar const *tmpl, GError **error) -{ - gchar *path = g_build_filename(g_get_tmp_dir(), tmpl ?: ".XXXXXX", NULL); - - if (mkdtemp(path) != NULL) { - return path; - } - /* Error occurred, clean up. */ - g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), - "mkdtemp() failed"); - g_free(path); - return NULL; -} -#define g_dir_make_tmp(tmpl, error) qemu_g_dir_make_tmp(tmpl, error) -#endif /* glib 2.30 */ - -#if !GLIB_CHECK_VERSION(2, 31, 0) -/* before glib-2.31, GMutex and GCond was dynamic-only (there was a separate - * GStaticMutex, but it didn't work with condition variables). - * - * Our implementation uses GOnce to fake a static implementation that does - * not require separate initialization. - * We need to rename the types to avoid passing our CompatGMutex/CompatGCond - * by mistake to a function that expects GMutex/GCond. However, for ease - * of use we keep the GLib function names. GLib uses macros for the - * implementation, we use inline functions instead and undefine the macros. - */ - -typedef struct CompatGMutex { - GOnce once; -} CompatGMutex; - -typedef struct CompatGCond { - GOnce once; -} CompatGCond; - -static inline gpointer do_g_mutex_new(gpointer unused) -{ - return (gpointer) g_mutex_new(); -} - -static inline void g_mutex_init(CompatGMutex *mutex) -{ - mutex->once = (GOnce) G_ONCE_INIT; -} - -static inline void g_mutex_clear(CompatGMutex *mutex) -{ - g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS); - if (mutex->once.retval) { - g_mutex_free((GMutex *) mutex->once.retval); - } - mutex->once = (GOnce) G_ONCE_INIT; -} - -static inline void (g_mutex_lock)(CompatGMutex *mutex) -{ - g_once(&mutex->once, do_g_mutex_new, NULL); - g_mutex_lock((GMutex *) mutex->once.retval); -} -#undef g_mutex_lock - -static inline gboolean (g_mutex_trylock)(CompatGMutex *mutex) -{ - g_once(&mutex->once, do_g_mutex_new, NULL); - return g_mutex_trylock((GMutex *) mutex->once.retval); -} -#undef g_mutex_trylock - - -static inline void (g_mutex_unlock)(CompatGMutex *mutex) -{ - g_mutex_unlock((GMutex *) mutex->once.retval); -} -#undef g_mutex_unlock - -static inline gpointer do_g_cond_new(gpointer unused) -{ - return (gpointer) g_cond_new(); -} - -static inline void g_cond_init(CompatGCond *cond) -{ - cond->once = (GOnce) G_ONCE_INIT; -} - -static inline void g_cond_clear(CompatGCond *cond) -{ - g_assert(cond->once.status != G_ONCE_STATUS_PROGRESS); - if (cond->once.retval) { - g_cond_free((GCond *) cond->once.retval); - } - cond->once = (GOnce) G_ONCE_INIT; -} - -static inline void (g_cond_wait)(CompatGCond *cond, CompatGMutex *mutex) -{ - g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS); - g_once(&cond->once, do_g_cond_new, NULL); - g_cond_wait((GCond *) cond->once.retval, (GMutex *) mutex->once.retval); -} -#undef g_cond_wait - -static inline void (g_cond_broadcast)(CompatGCond *cond) -{ - g_once(&cond->once, do_g_cond_new, NULL); - g_cond_broadcast((GCond *) cond->once.retval); -} -#undef g_cond_broadcast - -static inline void (g_cond_signal)(CompatGCond *cond) -{ - g_once(&cond->once, do_g_cond_new, NULL); - g_cond_signal((GCond *) cond->once.retval); -} -#undef g_cond_signal - -static inline gboolean (g_cond_timed_wait)(CompatGCond *cond, - CompatGMutex *mutex, - GTimeVal *time) -{ - g_assert(mutex->once.status != G_ONCE_STATUS_PROGRESS); - g_once(&cond->once, do_g_cond_new, NULL); - return g_cond_timed_wait((GCond *) cond->once.retval, - (GMutex *) mutex->once.retval, time); -} -#undef g_cond_timed_wait - -/* This is not a macro, because it didn't exist until 2.32. */ -static inline gboolean g_cond_wait_until(CompatGCond *cond, CompatGMutex *mutex, - gint64 end_time) -{ - GTimeVal time; - - /* Convert from monotonic to CLOCK_REALTIME. */ - end_time -= g_get_monotonic_time(); - g_get_current_time(&time); - end_time += time.tv_sec * G_TIME_SPAN_SECOND + time.tv_usec; - - time.tv_sec = end_time / G_TIME_SPAN_SECOND; - time.tv_usec = end_time % G_TIME_SPAN_SECOND; - return g_cond_timed_wait(cond, mutex, &time); -} - -/* before 2.31 there was no g_thread_new() */ -static inline GThread *g_thread_new(const char *name, - GThreadFunc func, gpointer data) -{ - GThread *thread = g_thread_create(func, data, TRUE, NULL); - if (!thread) { - g_error("creating thread"); - } - return thread; -} -#else -#define CompatGMutex GMutex -#define CompatGCond GCond -#endif /* glib 2.31 */ - -#if !GLIB_CHECK_VERSION(2, 32, 0) -/* Beware, function returns gboolean since 2.39.2, see GLib commit 9101915 */ -static inline void g_hash_table_add(GHashTable *hash_table, gpointer key) -{ - g_hash_table_replace(hash_table, key, key); -} - -static inline gboolean g_hash_table_contains(GHashTable *hash_table, - gpointer key) -{ - return g_hash_table_lookup_extended(hash_table, key, NULL, NULL); -} -#define G_SOURCE_CONTINUE TRUE -#define G_SOURCE_REMOVE FALSE -#endif - -#ifndef g_assert_true -#define g_assert_true(expr) \ - do { \ - if (G_LIKELY(expr)) { \ - } else { \ - g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "'" #expr "' should be TRUE"); \ - } \ - } while (0) -#endif - -#ifndef g_assert_false -#define g_assert_false(expr) \ - do { \ - if (G_LIKELY(!(expr))) { \ - } else { \ - g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "'" #expr "' should be FALSE"); \ - } \ - } while (0) -#endif - -#ifndef g_assert_null -#define g_assert_null(expr) \ - do { \ - if (G_LIKELY((expr) == NULL)) { \ - } else { \ - g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "'" #expr "' should be NULL"); \ - } \ - } while (0) -#endif - -#ifndef g_assert_nonnull -#define g_assert_nonnull(expr) \ - do { \ - if (G_LIKELY((expr) != NULL)) { \ - } else { \ - g_assertion_message(G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "'" #expr "' should not be NULL"); \ - } \ - } while (0) -#endif #ifndef g_assert_cmpmem #define g_assert_cmpmem(m1, l1, m2, l2) \ @@ -288,80 +111,6 @@ static inline gboolean g_hash_table_contains(GHashTable *hash_table, } while (0) #endif -#if !GLIB_CHECK_VERSION(2, 28, 0) -static inline void g_list_free_full(GList *list, GDestroyNotify free_func) -{ - GList *l; - - for (l = list; l; l = l->next) { - free_func(l->data); - } - - g_list_free(list); -} - -static inline void g_slist_free_full(GSList *list, GDestroyNotify free_func) -{ - GSList *l; - - for (l = list; l; l = l->next) { - free_func(l->data); - } - - g_slist_free(list); -} -#endif - -#if !GLIB_CHECK_VERSION(2, 26, 0) -static inline void g_source_set_name(GSource *source, const char *name) -{ - /* This is just a debugging aid, so leaving it a no-op */ -} -static inline void g_source_set_name_by_id(guint tag, const char *name) -{ - /* This is just a debugging aid, so leaving it a no-op */ -} -#endif - -#if !GLIB_CHECK_VERSION(2, 36, 0) -/* Always fail. This will not include error_report output in the test log, - * sending it instead to stderr. - */ -#define g_test_initialized() (0) -#endif -#if !GLIB_CHECK_VERSION(2, 38, 0) -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS -#error schizophrenic detection of glib subprocess testing -#endif -#define g_test_subprocess() (0) -#endif - - -#if !GLIB_CHECK_VERSION(2, 34, 0) -static inline void -g_test_add_data_func_full(const char *path, - gpointer data, - gpointer fn, - gpointer data_free_func) -{ -#if GLIB_CHECK_VERSION(2, 26, 0) - /* back-compat casts, remove this once we can require new-enough glib */ - g_test_add_vtable(path, 0, data, NULL, - (GTestFixtureFunc)fn, (GTestFixtureFunc) data_free_func); -#else - /* back-compat casts, remove this once we can require new-enough glib */ - g_test_add_vtable(path, 0, data, NULL, - (void (*)(void)) fn, (void (*)(void)) data_free_func); -#endif -} -#endif - -/* Small compat shim from glib 2.32 */ -#ifndef G_SOURCE_CONTINUE -#define G_SOURCE_CONTINUE TRUE -#endif -#ifndef G_SOURCE_REMOVE -#define G_SOURCE_REMOVE FALSE -#endif +#pragma GCC diagnostic pop #endif diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index 5955eb4fc0..af8e023968 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -628,6 +628,21 @@ struct AcpiIortItsGroup { } QEMU_PACKED; typedef struct AcpiIortItsGroup AcpiIortItsGroup; +struct AcpiIortSmmu3 { + ACPI_IORT_NODE_HEADER_DEF + uint64_t base_address; + uint32_t flags; + uint32_t reserved2; + uint64_t vatos_address; + uint32_t model; + uint32_t event_gsiv; + uint32_t pri_gsiv; + uint32_t gerr_gsiv; + uint32_t sync_gsiv; + AcpiIortIdMapping id_mapping_array[0]; +} QEMU_PACKED; +typedef struct AcpiIortSmmu3 AcpiIortSmmu3; + struct AcpiIortRC { ACPI_IORT_NODE_HEADER_DEF AcpiIortMemoryAccess memory_properties; diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h index 46ac4dc581..3580ffd50c 100644 --- a/include/hw/acpi/tpm.h +++ b/include/hw/acpi/tpm.h @@ -16,6 +16,7 @@ #ifndef HW_ACPI_TPM_H #define HW_ACPI_TPM_H +#include "qemu/units.h" #include "hw/registerfields.h" #define TPM_TIS_ADDR_BASE 0xFED40000 @@ -176,7 +177,7 @@ REG32(CRB_DATA_BUFFER, 0x80) #define TPM_CRB_ADDR_CTRL (TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ) #define TPM_CRB_R_MAX R_CRB_DATA_BUFFER -#define TPM_LOG_AREA_MINIMUM_SIZE (64 * 1024) +#define TPM_LOG_AREA_MINIMUM_SIZE (64 * KiB) #define TPM_TCPA_ACPI_CLASS_CLIENT 0 #define TPM_TCPA_ACPI_CLASS_SERVER 1 diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index 6b32a99e21..efb8fc8123 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -11,7 +11,6 @@ #include "hw/ide/ahci.h" #include "sysemu/sysemu.h" -#include "exec/address-spaces.h" #define AW_A10_PIC_REG_BASE 0x01c20400 diff --git a/include/hw/arm/arm.h b/include/hw/arm/arm.h index ce769bde6a..ffed39252d 100644 --- a/include/hw/arm/arm.h +++ b/include/hw/arm/arm.h @@ -23,9 +23,6 @@ typedef enum { ARM_ENDIANNESS_BE32, } arm_endianness; -/* armv7m.c */ -DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, - const char *kernel_filename, const char *cpu_type); /** * armv7m_load_kernel: * @cpu: CPU @@ -33,21 +30,11 @@ DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq, * @mem_size: mem_size: maximum image size to load * * Load the guest image for an ARMv7M system. This must be called by - * any ARMv7M board, either directly or via armv7m_init(). (This is - * necessary to ensure that the CPU resets correctly on system reset, - * as well as for kernel loading.) + * any ARMv7M board. (This is necessary to ensure that the CPU resets + * correctly on system reset, as well as for kernel loading.) */ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size); -/* - * struct used as a parameter of the arm_load_kernel machine init - * done notifier - */ -typedef struct { - Notifier notifier; /* actual notifier */ - ARMCPU *cpu; /* handle to the first cpu object */ -} ArmLoadKernelNotifier; - /* arm_boot.c */ struct arm_boot_info { uint64_t ram_size; @@ -56,6 +43,13 @@ struct arm_boot_info { const char *initrd_filename; const char *dtb_filename; hwaddr loader_start; + hwaddr dtb_start; + hwaddr dtb_limit; + /* If set to True, arm_load_kernel() will not load DTB. + * It allows board to load DTB manually later. + * (default: False) + */ + bool skip_dtb_autoload; /* multicore boards that use the default secondary core boot functions * need to put the address of the secondary boot code, the boot reg, * and the GIC address in the next 3 values, respectively. boards that @@ -94,8 +88,6 @@ struct arm_boot_info { * the user it should implement this hook. */ void (*modify_dtb)(const struct arm_boot_info *info, void *fdt); - /* machine init done notifier executing arm_load_dtb */ - ArmLoadKernelNotifier load_kernel_notifier; /* Used internally by arm_boot.c */ int is_linux; hwaddr initrd_start; @@ -143,6 +135,33 @@ struct arm_boot_info { */ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info); +AddressSpace *arm_boot_address_space(ARMCPU *cpu, + const struct arm_boot_info *info); + +/** + * arm_load_dtb() - load a device tree binary image into memory + * @addr: the address to load the image at + * @binfo: struct describing the boot environment + * @addr_limit: upper limit of the available memory area at @addr + * @as: address space to load image to + * + * Load a device tree supplied by the machine or by the user with the + * '-dtb' command line option, and put it at offset @addr in target + * memory. + * + * If @addr_limit contains a meaningful value (i.e., it is strictly greater + * than @addr), the device tree is only loaded if its size does not exceed + * the limit. + * + * Returns: the size of the device tree image on success, + * 0 if the image size exceeds the limit, + * -1 on errors. + * + * Note: Must not be called unless have_dtb(binfo) is true. + */ +int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, + hwaddr addr_limit, AddressSpace *as); + /* Write a secure board setup routine with a dummy handler for SMCs */ void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu, const struct arm_boot_info *info, diff --git a/include/hw/arm/bcm2835_peripherals.h b/include/hw/arm/bcm2835_peripherals.h index 122b286de7..f5b193f670 100644 --- a/include/hw/arm/bcm2835_peripherals.h +++ b/include/hw/arm/bcm2835_peripherals.h @@ -12,7 +12,6 @@ #define BCM2835_PERIPHERALS_H #include "qemu-common.h" -#include "exec/address-spaces.h" #include "hw/sysbus.h" #include "hw/char/bcm2835_aux.h" #include "hw/display/bcm2835_fb.h" diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h index c6129d926b..2cddde55dd 100644 --- a/include/hw/arm/iotkit.h +++ b/include/hw/arm/iotkit.h @@ -42,6 +42,9 @@ * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + * Controlling each of the 16 expansion MPCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mpcexp_status[0..15] */ #ifndef IOTKIT_H @@ -51,6 +54,7 @@ #include "hw/arm/armv7m.h" #include "hw/misc/iotkit-secctl.h" #include "hw/misc/tz-ppc.h" +#include "hw/misc/tz-mpc.h" #include "hw/timer/cmsdk-apb-timer.h" #include "hw/misc/unimp.h" #include "hw/or-irq.h" @@ -74,11 +78,14 @@ typedef struct IoTKit { IoTKitSecCtl secctl; TZPPC apb_ppc0; TZPPC apb_ppc1; + TZMPC mpc; CMSDKAPBTIMER timer0; CMSDKAPBTIMER timer1; qemu_or_irq ppc_irq_orgate; SplitIRQ sec_resp_splitter; SplitIRQ ppc_irq_splitter[NUM_PPCS]; + SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; + qemu_or_irq mpc_irq_orgate; UnimplementedDeviceState dualtimer; UnimplementedDeviceState s32ktimer; @@ -97,6 +104,7 @@ typedef struct IoTKit { qemu_irq nsc_cfg_in; qemu_irq irq_status_in[NUM_EXTERNAL_PPCS]; + qemu_irq mpcexp_status_in[IOTS_NUM_EXP_MPC]; uint32_t nsccfg; diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index b398607b06..e7fbd340f3 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -21,6 +21,7 @@ # define hw_omap_h "omap.h" #include "hw/irq.h" #include "target/arm/cpu-qom.h" +#include "qemu/log.h" # define OMAP_EMIFS_BASE 0x00000000 # define OMAP2_Q0_BASE 0x00000000 @@ -944,8 +945,6 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, unsigned long sdram_size, const char *core); -#define OMAP_FMT_plx "%#08" HWADDR_PRIx - uint32_t omap_badwidth_read8(void *opaque, hwaddr addr); void omap_badwidth_write8(void *opaque, hwaddr addr, uint32_t value); @@ -959,11 +958,12 @@ void omap_badwidth_write32(void *opaque, hwaddr addr, void omap_mpu_wakeup(void *opaque, int irq, int req); # define OMAP_BAD_REG(paddr) \ - fprintf(stderr, "%s: Bad register " OMAP_FMT_plx "\n", \ - __func__, paddr) + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad register %#08"HWADDR_PRIx"\n", \ + __func__, paddr) # define OMAP_RO_REG(paddr) \ - fprintf(stderr, "%s: Read-only register " OMAP_FMT_plx "\n", \ - __func__, paddr) + qemu_log_mask(LOG_GUEST_ERROR, "%s: Read-only register %#08" \ + HWADDR_PRIx "\n", \ + __func__, paddr) /* OMAP-specific Linux bootloader tags for the ATAG_BOARD area (Board-specifc tags are not here) */ @@ -993,24 +993,6 @@ enum { #define OMAP_GPIOSW_INVERTED 0x0001 #define OMAP_GPIOSW_OUTPUT 0x0002 -# define TCMI_VERBOSE 1 - -# ifdef TCMI_VERBOSE -# define OMAP_8B_REG(paddr) \ - fprintf(stderr, "%s: 8-bit register " OMAP_FMT_plx "\n", \ - __func__, paddr) -# define OMAP_16B_REG(paddr) \ - fprintf(stderr, "%s: 16-bit register " OMAP_FMT_plx "\n", \ - __func__, paddr) -# define OMAP_32B_REG(paddr) \ - fprintf(stderr, "%s: 32-bit register " OMAP_FMT_plx "\n", \ - __func__, paddr) -# else -# define OMAP_8B_REG(paddr) -# define OMAP_16B_REG(paddr) -# define OMAP_32B_REG(paddr) -# endif - # define OMAP_MPUI_REG_MASK 0x000007ff #endif /* hw_omap_h */ diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h new file mode 100644 index 0000000000..b07cadd0ef --- /dev/null +++ b/include/hw/arm/smmu-common.h @@ -0,0 +1,170 @@ +/* + * ARM SMMU Support + * + * Copyright (C) 2015-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef HW_ARM_SMMU_COMMON_H +#define HW_ARM_SMMU_COMMON_H + +#include "hw/sysbus.h" +#include "hw/pci/pci.h" + +#define SMMU_PCI_BUS_MAX 256 +#define SMMU_PCI_DEVFN_MAX 256 +#define SMMU_PCI_DEVFN(sid) (sid & 0xFF) + +#define SMMU_MAX_VA_BITS 48 + +/* + * Page table walk error types + */ +typedef enum { + SMMU_PTW_ERR_NONE, + SMMU_PTW_ERR_WALK_EABT, /* Translation walk external abort */ + SMMU_PTW_ERR_TRANSLATION, /* Translation fault */ + SMMU_PTW_ERR_ADDR_SIZE, /* Address Size fault */ + SMMU_PTW_ERR_ACCESS, /* Access fault */ + SMMU_PTW_ERR_PERMISSION, /* Permission fault */ +} SMMUPTWEventType; + +typedef struct SMMUPTWEventInfo { + SMMUPTWEventType type; + dma_addr_t addr; /* fetched address that induced an abort, if any */ +} SMMUPTWEventInfo; + +typedef struct SMMUTransTableInfo { + bool disabled; /* is the translation table disabled? */ + uint64_t ttb; /* TT base address */ + uint8_t tsz; /* input range, ie. 2^(64 -tsz)*/ + uint8_t granule_sz; /* granule page shift */ +} SMMUTransTableInfo; + +/* + * Generic structure populated by derived SMMU devices + * after decoding the configuration information and used as + * input to the page table walk + */ +typedef struct SMMUTransCfg { + int stage; /* translation stage */ + bool aa64; /* arch64 or aarch32 translation table */ + bool disabled; /* smmu is disabled */ + bool bypassed; /* translation is bypassed */ + bool aborted; /* translation is aborted */ + uint64_t ttb; /* TT base address */ + uint8_t oas; /* output address width */ + uint8_t tbi; /* Top Byte Ignore */ + uint16_t asid; + SMMUTransTableInfo tt[2]; + uint32_t iotlb_hits; /* counts IOTLB hits for this asid */ + uint32_t iotlb_misses; /* counts IOTLB misses for this asid */ +} SMMUTransCfg; + +typedef struct SMMUDevice { + void *smmu; + PCIBus *bus; + int devfn; + IOMMUMemoryRegion iommu; + AddressSpace as; + uint32_t cfg_cache_hits; + uint32_t cfg_cache_misses; +} SMMUDevice; + +typedef struct SMMUNotifierNode { + SMMUDevice *sdev; + QLIST_ENTRY(SMMUNotifierNode) next; +} SMMUNotifierNode; + +typedef struct SMMUPciBus { + PCIBus *bus; + SMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically alloc */ +} SMMUPciBus; + +typedef struct SMMUIOTLBKey { + uint64_t iova; + uint16_t asid; +} SMMUIOTLBKey; + +typedef struct SMMUState { + /* */ + SysBusDevice dev; + const char *mrtypename; + MemoryRegion iomem; + + GHashTable *smmu_pcibus_by_busptr; + GHashTable *configs; /* cache for configuration data */ + GHashTable *iotlb; + SMMUPciBus *smmu_pcibus_by_bus_num[SMMU_PCI_BUS_MAX]; + PCIBus *pci_bus; + QLIST_HEAD(, SMMUNotifierNode) notifiers_list; + uint8_t bus_num; + PCIBus *primary_bus; +} SMMUState; + +typedef struct { + /* */ + SysBusDeviceClass parent_class; + + /*< public >*/ + + DeviceRealize parent_realize; + +} SMMUBaseClass; + +#define TYPE_ARM_SMMU "arm-smmu" +#define ARM_SMMU(obj) OBJECT_CHECK(SMMUState, (obj), TYPE_ARM_SMMU) +#define ARM_SMMU_CLASS(klass) \ + OBJECT_CLASS_CHECK(SMMUBaseClass, (klass), TYPE_ARM_SMMU) +#define ARM_SMMU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SMMUBaseClass, (obj), TYPE_ARM_SMMU) + +/* Return the SMMUPciBus handle associated to a PCI bus number */ +SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num); + +/* Return the stream ID of an SMMU device */ +static inline uint16_t smmu_get_sid(SMMUDevice *sdev) +{ + return PCI_BUILD_BDF(pci_bus_num(sdev->bus), sdev->devfn); +} + +/** + * smmu_ptw - Perform the page table walk for a given iova / access flags + * pair, according to @cfg translation config + */ +int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, + IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info); + +/** + * select_tt - compute which translation table shall be used according to + * the input iova and translation config and return the TT specific info + */ +SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova); + +/* Return the iommu mr associated to @sid, or NULL if none */ +IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid); + +#define SMMU_IOTLB_MAX_SIZE 256 + +void smmu_iotlb_inv_all(SMMUState *s); +void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid); +void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova); + +/* Unmap the range of all the notifiers registered to any IOMMU mr */ +void smmu_inv_notifiers_all(SMMUState *s); + +/* Unmap the range of all the notifiers registered to @mr */ +void smmu_inv_notifiers_mr(IOMMUMemoryRegion *mr); + +#endif /* HW_ARM_SMMU_COMMON */ diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h new file mode 100644 index 0000000000..36b2f45253 --- /dev/null +++ b/include/hw/arm/smmuv3.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ + +#ifndef HW_ARM_SMMUV3_H +#define HW_ARM_SMMUV3_H + +#include "hw/arm/smmu-common.h" +#include "hw/registerfields.h" + +#define TYPE_SMMUV3_IOMMU_MEMORY_REGION "smmuv3-iommu-memory-region" + +typedef struct SMMUQueue { + uint64_t base; /* base register */ + uint32_t prod; + uint32_t cons; + uint8_t entry_size; + uint8_t log2size; +} SMMUQueue; + +typedef struct SMMUv3State { + SMMUState smmu_state; + + uint32_t features; + uint8_t sid_size; + uint8_t sid_split; + + uint32_t idr[6]; + uint32_t iidr; + uint32_t cr[3]; + uint32_t cr0ack; + uint32_t statusr; + uint32_t irq_ctrl; + uint32_t gerror; + uint32_t gerrorn; + uint64_t gerror_irq_cfg0; + uint32_t gerror_irq_cfg1; + uint32_t gerror_irq_cfg2; + uint64_t strtab_base; + uint32_t strtab_base_cfg; + uint64_t eventq_irq_cfg0; + uint32_t eventq_irq_cfg1; + uint32_t eventq_irq_cfg2; + + SMMUQueue eventq, cmdq; + + qemu_irq irq[4]; + QemuMutex mutex; +} SMMUv3State; + +typedef enum { + SMMU_IRQ_EVTQ, + SMMU_IRQ_PRIQ, + SMMU_IRQ_CMD_SYNC, + SMMU_IRQ_GERROR, +} SMMUIrq; + +typedef struct { + /*< private >*/ + SMMUBaseClass smmu_base_class; + /*< public >*/ + + DeviceRealize parent_realize; + DeviceReset parent_reset; +} SMMUv3Class; + +#define TYPE_ARM_SMMUV3 "arm-smmuv3" +#define ARM_SMMUV3(obj) OBJECT_CHECK(SMMUv3State, (obj), TYPE_ARM_SMMUV3) +#define ARM_SMMUV3_CLASS(klass) \ + OBJECT_CLASS_CHECK(SMMUv3Class, (klass), TYPE_ARM_SMMUV3) +#define ARM_SMMUV3_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SMMUv3Class, (obj), TYPE_ARM_SMMUV3) + +#endif diff --git a/include/hw/arm/sysbus-fdt.h b/include/hw/arm/sysbus-fdt.h index e15bb81807..340c382cdd 100644 --- a/include/hw/arm/sysbus-fdt.h +++ b/include/hw/arm/sysbus-fdt.h @@ -24,37 +24,14 @@ #ifndef HW_ARM_SYSBUS_FDT_H #define HW_ARM_SYSBUS_FDT_H -#include "hw/arm/arm.h" -#include "qemu-common.h" -#include "hw/sysbus.h" - -/* - * struct that contains dimensioning parameters of the platform bus - */ -typedef struct { - hwaddr platform_bus_base; /* start address of the bus */ - hwaddr platform_bus_size; /* size of the bus */ - int platform_bus_first_irq; /* first hwirq assigned to the bus */ - int platform_bus_num_irqs; /* number of hwirq assigned to the bus */ -} ARMPlatformBusSystemParams; - -/* - * struct that contains all relevant info to build the fdt nodes of - * platform bus and attached dynamic sysbus devices - * in the future might be augmented with additional info - * such as PHY, CLK handles ... - */ -typedef struct { - const ARMPlatformBusSystemParams *system_params; - struct arm_boot_info *binfo; - const char *intc; /* parent interrupt controller name */ -} ARMPlatformBusFDTParams; +#include "exec/hwaddr.h" /** - * arm_register_platform_bus_fdt_creator - register a machine init done - * notifier that creates the device tree nodes of the platform bus and - * associated dynamic sysbus devices + * platform_bus_add_all_fdt_nodes - create all the platform bus nodes + * + * builds the parent platform bus node and all the nodes of dynamic + * sysbus devices attached to it. */ -void arm_register_platform_bus_fdt_creator(ARMPlatformBusFDTParams *fdt_params); - +void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr, + hwaddr bus_size, int irq_start); #endif diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index ba0c1a4faa..9a870ccb6a 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -35,9 +35,12 @@ #include "qemu/notify.h" #include "hw/boards.h" #include "hw/arm/arm.h" +#include "sysemu/kvm.h" +#include "hw/intc/arm_gicv3_common.h" #define NUM_GICV2M_SPIS 64 #define NUM_VIRTIO_TRANSPORTS 32 +#define NUM_SMMU_IRQS 4 #define ARCH_GICV3_MAINT_IRQ 9 @@ -59,6 +62,8 @@ enum { VIRT_GIC_V2M, VIRT_GIC_ITS, VIRT_GIC_REDIST, + VIRT_GIC_REDIST2, + VIRT_SMMU, VIRT_UART, VIRT_MMIO, VIRT_RTC, @@ -67,6 +72,7 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PCIE_ECAM, + VIRT_PCIE_ECAM_HIGH, VIRT_PLATFORM_BUS, VIRT_PCIE_MMIO_HIGH, VIRT_GPIO, @@ -74,6 +80,12 @@ enum { VIRT_SECURE_MEM, }; +typedef enum VirtIOMMUType { + VIRT_IOMMU_NONE, + VIRT_IOMMU_SMMUV3, + VIRT_IOMMU_VIRTIO, +} VirtIOMMUType; + typedef struct MemMapEntry { hwaddr base; hwaddr size; @@ -86,17 +98,21 @@ typedef struct { bool no_pmu; bool claim_edge_triggered_timers; bool smbios_old_sys_ver; + bool no_highmem_ecam; } VirtMachineClass; typedef struct { MachineState parent; Notifier machine_done; + DeviceState *platform_bus_dev; FWCfgState *fw_cfg; bool secure; bool highmem; + bool highmem_ecam; bool its; bool virt; int32_t gic_version; + VirtIOMMUType iommu; struct arm_boot_info bootinfo; const MemMapEntry *memmap; const int *irqmap; @@ -106,9 +122,12 @@ typedef struct { uint32_t clock_phandle; uint32_t gic_phandle; uint32_t msi_phandle; + uint32_t iommu_phandle; int psci_conduit; } VirtMachineState; +#define VIRT_ECAM_ID(high) (high ? VIRT_PCIE_ECAM_HIGH : VIRT_PCIE_ECAM) + #define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt") #define VIRT_MACHINE(obj) \ OBJECT_CHECK(VirtMachineState, (obj), TYPE_VIRT_MACHINE) @@ -119,4 +138,15 @@ typedef struct { void virt_acpi_setup(VirtMachineState *vms); +/* Return the number of used redistributor regions */ +static inline int virt_gicv3_redist_region_count(VirtMachineState *vms) +{ + uint32_t redist0_capacity = + vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + + assert(vms->gic_version == 3); + + return vms->smp_cpus > redist0_capacity ? 2 : 1; +} + #endif /* QEMU_ARM_VIRT_H */ diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 3b613e364d..82b6ec2486 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -27,6 +27,7 @@ #include "hw/sd/sdhci.h" #include "hw/ssi/xilinx_spips.h" #include "hw/dma/xlnx_dpdma.h" +#include "hw/dma/xlnx-zdma.h" #include "hw/display/xlnx_dp.h" #include "hw/intc/xlnx-zynqmp-ipi.h" #include "hw/timer/xlnx-zynqmp-rtc.h" @@ -41,6 +42,8 @@ #define XLNX_ZYNQMP_NUM_UARTS 2 #define XLNX_ZYNQMP_NUM_SDHCI 2 #define XLNX_ZYNQMP_NUM_SPIS 2 +#define XLNX_ZYNQMP_NUM_GDMA_CH 8 +#define XLNX_ZYNQMP_NUM_ADMA_CH 8 #define XLNX_ZYNQMP_NUM_QSPI_BUS 2 #define XLNX_ZYNQMP_NUM_QSPI_BUS_CS 2 @@ -94,6 +97,8 @@ typedef struct XlnxZynqMPState { XlnxDPDMAState dpdma; XlnxZynqMPIPI ipi; XlnxZynqMPRTC rtc; + XlnxZDMA gdma[XLNX_ZYNQMP_NUM_GDMA_CH]; + XlnxZDMA adma[XLNX_ZYNQMP_NUM_ADMA_CH]; char *boot_cpu; ARMCPU *boot_cpu_ptr; diff --git a/include/hw/boards.h b/include/hw/boards.h index a609239112..d139a431a6 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -35,8 +35,7 @@ * * Smaller pieces of memory (display RAM, static RAMs, etc) don't need * to be backed via the -mem-path memory backend and can simply - * be created via memory_region_allocate_aux_memory() or - * memory_region_init_ram(). + * be created via memory_region_init_ram(). */ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, const char *name, @@ -107,6 +106,8 @@ typedef struct { /** * MachineClass: + * @deprecation_reason: If set, the machine is marked as deprecated. The + * string should provide some clear information about what to use instead. * @max_cpus: maximum number of CPUs supported. Default: 1 * @min_cpus: minimum number of CPUs supported. Default: 1 * @default_cpus: number of CPUs instantiated if none are specified. Default: 1 @@ -166,6 +167,7 @@ struct MachineClass { char *name; const char *alias; const char *desc; + const char *deprecation_reason; void (*init)(MachineState *state); void (*reset)(void); @@ -180,7 +182,6 @@ struct MachineClass { unsigned int no_serial:1, no_parallel:1, use_virtcon:1, - use_sclp:1, no_floppy:1, no_cdrom:1, no_sdcard:1, @@ -214,6 +215,17 @@ struct MachineClass { int64_t (*get_default_cpu_node_id)(const MachineState *ms, int idx); }; +/** + * DeviceMemoryState: + * @base: address in guest physical address space where the memory + * address space for memory devices starts + * @mr: address space container for memory devices + */ +typedef struct DeviceMemoryState { + hwaddr base; + MemoryRegion mr; +} DeviceMemoryState; + /** * MachineState: */ @@ -244,6 +256,7 @@ struct MachineState { bool enforce_config_section; bool enable_graphics; char *memory_encryption; + DeviceMemoryState *device_memory; ram_addr_t ram_size; ram_addr_t maxram_size; diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index c4daf11a14..0acfbbc382 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -95,6 +95,9 @@ SerialState *serial_mm_init(MemoryRegion *address_space, Chardev *chr, enum device_endian end); /* serial-isa.c */ + +#define MAX_ISA_SERIAL_PORTS 4 + #define TYPE_ISA_SERIAL "isa-serial" void serial_hds_isa_init(ISABus *bus, int from, int to); diff --git a/include/hw/compat.h b/include/hw/compat.h index 13242b831a..c08f4040bb 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -1,6 +1,33 @@ #ifndef HW_COMPAT_H #define HW_COMPAT_H +#define HW_COMPAT_2_12 \ + {\ + .driver = "migration",\ + .property = "decompress-error-check",\ + .value = "off",\ + },{\ + .driver = "hda-audio",\ + .property = "use-timer",\ + .value = "false",\ + },{\ + .driver = "cirrus-vga",\ + .property = "global-vmstate",\ + .value = "true",\ + },{\ + .driver = "VGA",\ + .property = "global-vmstate",\ + .value = "true",\ + },{\ + .driver = "vmware-svga",\ + .property = "global-vmstate",\ + .value = "true",\ + },{\ + .driver = "qxl-vga",\ + .property = "global-vmstate",\ + .value = "true",\ + }, + #define HW_COMPAT_2_11 \ {\ .driver = "hpet",\ diff --git a/include/hw/devices.h b/include/hw/devices.h index 861ddea8af..0e27feb0c2 100644 --- a/include/hw/devices.h +++ b/include/hw/devices.h @@ -1,13 +1,10 @@ #ifndef QEMU_DEVICES_H #define QEMU_DEVICES_H -#include "hw/irq.h" - -/* ??? Not all users of this file can include cpu-common.h. */ -struct MemoryRegion; - /* Devices that have nowhere better to go. */ +#include "hw/hw.h" + /* smc91c111.c */ void smc91c111_init(NICInfo *, uint32_t, qemu_irq); diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index 9a12d7afa2..ae0a3807f2 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -12,7 +12,6 @@ #define BCM2835_FB_H #include "hw/sysbus.h" -#include "exec/address-spaces.h" #include "ui/console.h" #define TYPE_BCM2835_FB "bcm2835-fb" diff --git a/include/hw/display/bochs-vbe.h b/include/hw/display/bochs-vbe.h new file mode 100644 index 0000000000..bc2f046eee --- /dev/null +++ b/include/hw/display/bochs-vbe.h @@ -0,0 +1,69 @@ +#ifndef HW_DISPLAY_BOCHS_VBE_H +#define HW_DISPLAY_BOCHS_VBE_H + +/* + * bochs vesa bios extension interface + */ + +#define VBE_DISPI_MAX_XRES 16000 +#define VBE_DISPI_MAX_YRES 12000 +#define VBE_DISPI_MAX_BPP 32 + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ + +/* VBE_DISPI_INDEX_ID */ +#define VBE_DISPI_ID0 0xB0C0 +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 +#define VBE_DISPI_ID3 0xB0C3 +#define VBE_DISPI_ID4 0xB0C4 +#define VBE_DISPI_ID5 0xB0C5 + +/* VBE_DISPI_INDEX_ENABLE */ +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +/* only used by isa-vga, pci vga devices use a memory bar */ +#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + + +/* + * qemu extension: mmio bar (region 2) + */ + +#define PCI_VGA_MMIO_SIZE 0x1000 + +/* vga register region */ +#define PCI_VGA_IOPORT_OFFSET 0x400 +#define PCI_VGA_IOPORT_SIZE (0x3e0 - 0x3c0) + +/* bochs vbe register region */ +#define PCI_VGA_BOCHS_OFFSET 0x500 +#define PCI_VGA_BOCHS_SIZE (0x0b * 2) + +/* qemu extension register region */ +#define PCI_VGA_QEXT_OFFSET 0x600 +#define PCI_VGA_QEXT_SIZE (2 * 4) + +/* qemu extension registers */ +#define PCI_VGA_QEXT_REG_SIZE (0 * 4) +#define PCI_VGA_QEXT_REG_BYTEORDER (1 * 4) +#define PCI_VGA_QEXT_LITTLE_ENDIAN 0x1e1e1e1e +#define PCI_VGA_QEXT_BIG_ENDIAN 0xbebebebe + +#endif /* HW_DISPLAY_BOCHS_VBE_H */ diff --git a/include/hw/display/ramfb.h b/include/hw/display/ramfb.h new file mode 100644 index 0000000000..b33a2c467b --- /dev/null +++ b/include/hw/display/ramfb.h @@ -0,0 +1,12 @@ +#ifndef RAMFB_H +#define RAMFB_H + +/* ramfb.c */ +typedef struct RAMFBState RAMFBState; +void ramfb_display_update(QemuConsole *con, RAMFBState *s); +RAMFBState *ramfb_setup(Error **errp); + +/* ramfb-standalone.c */ +#define TYPE_RAMFB_DEVICE "ramfb" + +#endif /* RAMFB_H */ diff --git a/include/hw/display/xlnx_dp.h b/include/hw/display/xlnx_dp.h index ee046a5fac..26b759cd44 100644 --- a/include/hw/display/xlnx_dp.h +++ b/include/hw/display/xlnx_dp.h @@ -29,14 +29,15 @@ #include "hw/display/dpcd.h" #include "hw/i2c/i2c-ddc.h" #include "qemu/fifo8.h" +#include "qemu/units.h" #include "hw/dma/xlnx_dpdma.h" #include "audio/audio.h" #ifndef XLNX_DP_H #define XLNX_DP_H -#define AUD_CHBUF_MAX_DEPTH 32768 -#define MAX_QEMU_BUFFER_SIZE 4096 +#define AUD_CHBUF_MAX_DEPTH (32 * KiB) +#define MAX_QEMU_BUFFER_SIZE (4 * KiB) #define DP_CORE_REG_ARRAY_SIZE (0x3AF >> 2) #define DP_AVBUF_REG_ARRAY_SIZE (0x238 >> 2) diff --git a/include/hw/dma/bcm2835_dma.h b/include/hw/dma/bcm2835_dma.h index 75312e2e17..60138f4d31 100644 --- a/include/hw/dma/bcm2835_dma.h +++ b/include/hw/dma/bcm2835_dma.h @@ -7,7 +7,6 @@ #define BCM2835_DMA_H #include "qemu-common.h" -#include "exec/address-spaces.h" #include "hw/sysbus.h" typedef struct { diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h new file mode 100644 index 0000000000..0b240b4c3c --- /dev/null +++ b/include/hw/dma/xlnx-zdma.h @@ -0,0 +1,84 @@ +/* + * QEMU model of the ZynqMP generic DMA + * + * Copyright (c) 2014 Xilinx Inc. + * Copyright (c) 2018 FEIMTECH AB + * + * Written by Edgar E. Iglesias , + * Francisco Iglesias + * + * 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 XLNX_ZDMA_H +#define XLNX_ZDMA_H + +#include "hw/sysbus.h" +#include "hw/register.h" +#include "sysemu/dma.h" + +#define ZDMA_R_MAX (0x204 / 4) + +typedef enum { + DISABLED = 0, + ENABLED = 1, + PAUSED = 2, +} XlnxZDMAState; + +typedef union { + struct { + uint64_t addr; + uint32_t size; + uint32_t attr; + }; + uint32_t words[4]; +} XlnxZDMADescr; + +typedef struct XlnxZDMA { + SysBusDevice parent_obj; + MemoryRegion iomem; + MemTxAttrs attr; + MemoryRegion *dma_mr; + AddressSpace *dma_as; + qemu_irq irq_zdma_ch_imr; + + struct { + uint32_t bus_width; + } cfg; + + XlnxZDMAState state; + bool error; + + XlnxZDMADescr dsc_src; + XlnxZDMADescr dsc_dst; + + uint32_t regs[ZDMA_R_MAX]; + RegisterInfo regs_info[ZDMA_R_MAX]; + + /* We don't model the common bufs. Must be at least 16 bytes + to model write only mode. */ + uint8_t buf[2048]; +} XlnxZDMA; + +#define TYPE_XLNX_ZDMA "xlnx.zdma" + +#define XLNX_ZDMA(obj) \ + OBJECT_CHECK(XlnxZDMA, (obj), TYPE_XLNX_ZDMA) + +#endif /* XLNX_ZDMA_H */ diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h index 1a0516a479..51541d63e1 100644 --- a/include/hw/hotplug.h +++ b/include/hw/hotplug.h @@ -47,6 +47,8 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler, * @parent: Opaque parent interface. * @pre_plug: pre plug callback called at start of device.realize(true) * @plug: plug callback called at end of device.realize(true). + * @post_plug: post plug callback called after device.realize(true) and device + * reset * @unplug_request: unplug request callback. * Used as a means to initiate device unplug for devices that * require asynchronous unplug handling. @@ -61,6 +63,7 @@ typedef struct HotplugHandlerClass { /* */ hotplug_fn pre_plug; hotplug_fn plug; + void (*post_plug)(HotplugHandler *plug_handler, DeviceState *plugged_dev); hotplug_fn unplug_request; hotplug_fn unplug; } HotplugHandlerClass; @@ -83,6 +86,14 @@ void hotplug_handler_pre_plug(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp); +/** + * hotplug_handler_post_plug: + * + * Call #HotplugHandlerClass.post_plug callback of @plug_handler. + */ +void hotplug_handler_post_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev); + /** * hotplug_handler_unplug_request: * diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index d727379b48..5dc166158b 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -28,9 +28,6 @@ typedef struct I2CSlave I2CSlave; typedef struct I2CSlaveClass { DeviceClass parent_class; - /* Callbacks provided by the device. */ - int (*init)(I2CSlave *dev); - /* Master to slave. Returns non-zero for a NAK, 0 for success. */ int (*send)(I2CSlave *s, uint8_t data); diff --git a/include/hw/i2c/ppc4xx_i2c.h b/include/hw/i2c/ppc4xx_i2c.h index 3c603071bd..0891a9c948 100644 --- a/include/hw/i2c/ppc4xx_i2c.h +++ b/include/hw/i2c/ppc4xx_i2c.h @@ -3,7 +3,7 @@ * * Copyright (c) 2007 Jocelyn Mayer * Copyright (c) 2012 François Revol - * Copyright (c) 2016 BALATON Zoltan + * Copyright (c) 2016-2018 BALATON Zoltan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -31,6 +31,9 @@ #include "hw/sysbus.h" #include "hw/i2c/i2c.h" +/* from hw/i2c/bitbang_i2c.h */ +typedef struct bitbang_i2c_interface bitbang_i2c_interface; + #define TYPE_PPC4xx_I2C "ppc4xx-i2c" #define PPC4xx_I2C(obj) OBJECT_CHECK(PPC4xxI2CState, (obj), TYPE_PPC4xx_I2C) @@ -42,14 +45,15 @@ typedef struct PPC4xxI2CState { I2CBus *bus; qemu_irq irq; MemoryRegion iomem; - uint8_t mdata; + bitbang_i2c_interface *bitbang; + int mdidx; + uint8_t mdata[4]; uint8_t lmadr; uint8_t hmadr; uint8_t cntl; uint8_t mdcntl; uint8_t sts; uint8_t extsts; - uint8_t sdata; uint8_t lsadr; uint8_t hsadr; uint8_t clkdiv; @@ -57,7 +61,6 @@ typedef struct PPC4xxI2CState { uint8_t xfrcnt; uint8_t xtcntlss; uint8_t directcntl; - uint8_t intr; } PPC4xxI2CState; #endif /* PPC4XX_I2C_H */ diff --git a/include/hw/i2c/smbus.h b/include/hw/i2c/smbus.h index 06046d7e0c..e7094113e0 100644 --- a/include/hw/i2c/smbus.h +++ b/include/hw/i2c/smbus.h @@ -38,7 +38,6 @@ typedef struct SMBusDeviceClass { I2CSlaveClass parent_class; - int (*init)(SMBusDevice *dev); void (*quick_cmd)(SMBusDevice *dev, uint8_t read); void (*send_byte)(SMBusDevice *dev, uint8_t val); uint8_t (*receive_byte)(SMBusDevice *dev); @@ -77,6 +76,7 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data); int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, int len); +void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf); void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, const uint8_t *eeprom_spd, int size); diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index 45ec8919b6..fbfedcb1c0 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -27,6 +27,7 @@ #include "hw/i386/ioapic.h" #include "hw/pci/msi.h" #include "hw/sysbus.h" +#include "qemu/iova-tree.h" #define TYPE_INTEL_IOMMU_DEVICE "intel-iommu" #define INTEL_IOMMU_DEVICE(obj) \ @@ -67,7 +68,6 @@ typedef union VTD_IR_TableEntry VTD_IR_TableEntry; typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress; typedef struct VTDIrq VTDIrq; typedef struct VTD_MSIMessage VTD_MSIMessage; -typedef struct IntelIOMMUNotifierNode IntelIOMMUNotifierNode; /* Context-Entry */ struct VTDContextEntry { @@ -93,6 +93,10 @@ struct VTDAddressSpace { MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */ IntelIOMMUState *iommu_state; VTDContextCacheEntry context_cache_entry; + QLIST_ENTRY(VTDAddressSpace) next; + /* Superset of notifier flags that this address space has */ + IOMMUNotifierFlag notifier_flags; + IOVATree *iova_tree; /* Traces mapped IOVA ranges */ }; struct VTDBus { @@ -253,11 +257,6 @@ struct VTD_MSIMessage { /* When IR is enabled, all MSI/MSI-X data bits should be zero */ #define VTD_IR_MSI_DATA (0) -struct IntelIOMMUNotifierNode { - VTDAddressSpace *vtd_as; - QLIST_ENTRY(IntelIOMMUNotifierNode) next; -}; - /* The iommu (DMAR) device state struct */ struct IntelIOMMUState { X86IOMMUState x86_iommu; @@ -295,7 +294,7 @@ struct IntelIOMMUState { GHashTable *vtd_as_by_busptr; /* VTDBus objects indexed by PCIBus* reference */ VTDBus *vtd_as_by_bus_num[VTD_PCI_BUS_MAX]; /* VTDBus objects indexed by bus number */ /* list of registered notifiers */ - QLIST_HEAD(, IntelIOMMUNotifierNode) notifiers_list; + QLIST_HEAD(, VTDAddressSpace) vtd_as_with_notifiers; /* interrupt remapping */ bool intr_enabled; /* Whether guest enabled IR */ @@ -305,6 +304,12 @@ struct IntelIOMMUState { OnOffAuto intr_eim; /* Toggle for EIM cabability */ bool buggy_eim; /* Force buggy EIM unless eim=off */ uint8_t aw_bits; /* Host/IOVA address width (in bits) */ + + /* + * Protects IOMMU states in general. Currently it protects the + * per-IOMMU IOTLB cache, and context entry cache in VTDAddressSpace. + */ + QemuMutex iommu_lock; }; /* Find the VTD Address space associated with the given bus pointer, diff --git a/include/hw/i386/ioapic_internal.h b/include/hw/i386/ioapic_internal.h index a11d86de46..9848f391bb 100644 --- a/include/hw/i386/ioapic_internal.h +++ b/include/hw/i386/ioapic_internal.h @@ -109,10 +109,13 @@ struct IOAPICCommonState { uint64_t ioredtbl[IOAPIC_NUM_PINS]; Notifier machine_done; uint8_t version; + uint64_t irq_count[IOAPIC_NUM_PINS]; + int irq_level[IOAPIC_NUM_PINS]; }; 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 */ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index ffee8413f0..6894f37df1 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -32,7 +32,6 @@ struct PCMachineState { /* */ /* State for other subsystems/APIs: */ - MemoryHotplugState hotplug_memory; Notifier machine_done; /* Pointers to devices and objects: */ @@ -72,11 +71,12 @@ struct PCMachineState { }; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" -#define PC_MACHINE_MEMHP_REGION_SIZE "hotplug-memory-region-size" +#define PC_MACHINE_DEVMEM_REGION_SIZE "device-memory-region-size" #define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" #define PC_MACHINE_VMPORT "vmport" #define PC_MACHINE_SMM "smm" #define PC_MACHINE_NVDIMM "nvdimm" +#define PC_MACHINE_NVDIMM_PERSIST "nvdimm-persistence" #define PC_MACHINE_SMBUS "smbus" #define PC_MACHINE_SATA "sata" #define PC_MACHINE_PIT "pit" @@ -84,10 +84,6 @@ struct PCMachineState { /** * PCMachineClass: * - * Methods: - * - * @get_hotplug_handler: pointer to parent class callback @get_hotplug_handler - * * Compat fields: * * @enforce_aligned_dimm: check that DIMM's address/size is aligned by @@ -107,10 +103,6 @@ struct PCMachineClass { /*< public >*/ - /* Methods: */ - HotplugHandler *(*get_hotplug_handler)(MachineState *machine, - DeviceState *dev); - /* Device configuration: */ bool pci_enabled; bool kvmclock_enabled; @@ -162,9 +154,6 @@ int pic_get_output(DeviceState *d); /* ioapic.c */ -void kvm_ioapic_dump_state(Monitor *mon, const QDict *qdict); -void ioapic_dump_state(Monitor *mon, const QDict *qdict); - /* Global System Interrupts */ #define GSI_NUM_PINS IOAPIC_NUM_PINS @@ -305,9 +294,33 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); int e820_get_num_entries(void); bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); +#define PC_COMPAT_2_12 \ + HW_COMPAT_2_12 \ + {\ + .driver = TYPE_X86_CPU,\ + .property = "legacy-cache",\ + .value = "on",\ + },{\ + .driver = TYPE_X86_CPU,\ + .property = "topoext",\ + .value = "off",\ + },{\ + .driver = "EPYC-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "EPYC-IBPB-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + }, + #define PC_COMPAT_2_11 \ HW_COMPAT_2_11 \ {\ + .driver = TYPE_X86_CPU,\ + .property = "x-migrate-smi-count",\ + .value = "off",\ + },{\ .driver = "Skylake-Server" "-" TYPE_X86_CPU,\ .property = "clflushopt",\ .value = "off",\ diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index dcad2af2a1..9a73bf8b77 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -444,7 +444,7 @@ struct IDEState { struct IDEDMAOps { DMAStartFunc *start_dma; - DMAVoidFunc *start_transfer; + DMAVoidFunc *pio_transfer; DMAInt32Func *prepare_buf; DMAu32Func *commit_buf; DMAIntFunc *rw_buf; @@ -624,6 +624,8 @@ void ide_exec_cmd(IDEBus *bus, uint32_t val); void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func); +bool ide_transfer_start_norecurse(IDEState *s, uint8_t *buf, int size, + EndTransferFunc *end_transfer_func); void ide_transfer_stop(IDEState *s); void ide_set_inactive(IDEState *s, bool more); BlockAIOCB *ide_issue_trim( diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h index 3ae8445e95..f99d478252 100644 --- a/include/hw/input/adb.h +++ b/include/hw/input/adb.h @@ -49,6 +49,7 @@ struct ADBDevice { int devaddr; int handler; + bool disable_direct_reg3_writes; }; #define ADB_DEVICE_CLASS(cls) \ diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h index 94709b8502..213aa16aa3 100644 --- a/include/hw/input/ps2.h +++ b/include/hw/input/ps2.h @@ -37,7 +37,12 @@ void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); void ps2_write_mouse(void *, int val); void ps2_write_keyboard(void *, int val); uint32_t ps2_read_data(PS2State *s); +void ps2_queue_noirq(PS2State *s, int b); +void ps2_raise_irq(PS2State *s); void ps2_queue(PS2State *s, int b); +void ps2_queue_2(PS2State *s, int b1, int b2); +void ps2_queue_3(PS2State *s, int b1, int b2, int b3); +void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4); void ps2_keyboard_set_translation(void *opaque, int mode); void ps2_mouse_fake_event(void *opaque); diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index bccdfe17c6..b798486ecf 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -35,6 +35,8 @@ #define GICV3_MAXIRQ 1020 #define GICV3_MAXSPI (GICV3_MAXIRQ - GIC_INTERNAL) +#define GICV3_REDIST_SIZE 0x20000 + /* Number of SGI target-list bits */ #define GICV3_TARGETLIST_BITS 16 @@ -210,13 +212,16 @@ struct GICv3State { /*< public >*/ MemoryRegion iomem_dist; /* Distributor */ - MemoryRegion iomem_redist; /* Redistributors */ + MemoryRegion *iomem_redist; /* Redistributor Regions */ + uint32_t *redist_region_count; /* redistributor count within each region */ + uint32_t nb_redist_regions; /* number of redist regions */ uint32_t num_cpu; uint32_t num_irq; uint32_t revision; bool security_extn; bool irq_reset_nonsecure; + bool gicd_no_migration_shift_bug; int dev_fd; /* kvm device fd if backed by kvm vgic support */ Error *migration_blocker; @@ -291,6 +296,6 @@ typedef struct ARMGICv3CommonClass { } ARMGICv3CommonClass; void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler, - const MemoryRegionOps *ops); + const MemoryRegionOps *ops, Error **errp); #endif diff --git a/include/hw/intc/heathrow_pic.h b/include/hw/intc/heathrow_pic.h index bc3ffaab87..56c2ef339f 100644 --- a/include/hw/intc/heathrow_pic.h +++ b/include/hw/intc/heathrow_pic.h @@ -41,7 +41,7 @@ typedef struct HeathrowState { MemoryRegion mem; HeathrowPICState pics[2]; - qemu_irq *irqs; + qemu_irq irqs[1]; } HeathrowState; #define HEATHROW_NUM_IRQS 64 diff --git a/include/hw/intc/mips_gic.h b/include/hw/intc/mips_gic.h index b98d50094a..902a12b178 100644 --- a/include/hw/intc/mips_gic.h +++ b/include/hw/intc/mips_gic.h @@ -11,6 +11,7 @@ #ifndef MIPS_GIC_H #define MIPS_GIC_H +#include "qemu/units.h" #include "hw/timer/mips_gictimer.h" #include "cpu.h" /* @@ -19,7 +20,7 @@ /* The MIPS default location */ #define GIC_BASE_ADDR 0x1bdc0000ULL -#define GIC_ADDRSPACE_SZ (128 * 1024) +#define GIC_ADDRSPACE_SZ (128 * KiB) /* Constants */ #define GIC_POL_POS 1 diff --git a/include/hw/isa/superio.h b/include/hw/isa/superio.h index f9ba29aa30..345f006081 100644 --- a/include/hw/isa/superio.h +++ b/include/hw/isa/superio.h @@ -22,13 +22,15 @@ #define ISA_SUPERIO_CLASS(klass) \ OBJECT_CLASS_CHECK(ISASuperIOClass, (klass), TYPE_ISA_SUPERIO) +#define SUPERIO_MAX_SERIAL_PORTS 4 + typedef struct ISASuperIODevice { /*< private >*/ ISADevice parent_obj; /*< public >*/ ISADevice *parallel[MAX_PARALLEL_PORTS]; - ISADevice *serial[MAX_SERIAL_PORTS]; + ISADevice *serial[SUPERIO_MAX_SERIAL_PORTS]; ISADevice *floppy; ISADevice *kbc; ISADevice *ide; diff --git a/include/hw/loader.h b/include/hw/loader.h index 5ed3fd8ae6..e98b84b8f9 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -226,7 +226,7 @@ void rom_set_fw(FWCfgState *f); void rom_set_order_override(int order); void rom_reset_order_override(void); int rom_copy(uint8_t *dest, hwaddr addr, size_t size); -void *rom_ptr(hwaddr addr); +void *rom_ptr(hwaddr addr, size_t size); void hmp_info_roms(Monitor *mon, const QDict *qdict); #define rom_add_file_fixed(_f, _a, _i) \ diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h new file mode 100644 index 0000000000..2853b084b5 --- /dev/null +++ b/include/hw/mem/memory-device.h @@ -0,0 +1,51 @@ +/* + * Memory Device Interface + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * 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 MEMORY_DEVICE_H +#define MEMORY_DEVICE_H + +#include "qom/object.h" +#include "hw/qdev.h" + +#define TYPE_MEMORY_DEVICE "memory-device" + +#define MEMORY_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(MemoryDeviceClass, (klass), TYPE_MEMORY_DEVICE) +#define MEMORY_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MemoryDeviceClass, (obj), TYPE_MEMORY_DEVICE) +#define MEMORY_DEVICE(obj) \ + INTERFACE_CHECK(MemoryDeviceState, (obj), TYPE_MEMORY_DEVICE) + +typedef struct MemoryDeviceState { + Object parent_obj; +} MemoryDeviceState; + +typedef struct MemoryDeviceClass { + InterfaceClass parent_class; + + uint64_t (*get_addr)(const MemoryDeviceState *md); + uint64_t (*get_plugged_size)(const MemoryDeviceState *md); + uint64_t (*get_region_size)(const MemoryDeviceState *md); + void (*fill_device_info)(const MemoryDeviceState *md, + MemoryDeviceInfo *info); +} MemoryDeviceClass; + +MemoryDeviceInfoList *qmp_memory_device_list(void); +uint64_t get_plugged_memory_size(void); +uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, + uint64_t align, uint64_t size, + Error **errp); +void memory_device_plug_region(MachineState *ms, MemoryRegion *mr, + uint64_t addr); +void memory_device_unplug_region(MachineState *ms, MemoryRegion *mr); + +#endif diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index 7fd87c4e1c..c5c9b3c7f8 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -48,7 +48,7 @@ #define NVDIMM_GET_CLASS(obj) OBJECT_GET_CLASS(NVDIMMClass, (obj), \ TYPE_NVDIMM) -#define NVDIMM_LABLE_SIZE_PROP "label-size" +#define NVDIMM_LABEL_SIZE_PROP "label-size" #define NVDIMM_UNARMED_PROP "unarmed" struct NVDIMMDevice { @@ -74,7 +74,7 @@ struct NVDIMMDevice { * it's the PMEM region in NVDIMM device, which is presented to * guest via ACPI NFIT and _FIT method if NVDIMM hotplug is supported. */ - MemoryRegion nvdimm_mr; + MemoryRegion *nvdimm_mr; /* * The 'on' value results in the unarmed flag set in ACPI NFIT, @@ -134,6 +134,12 @@ struct AcpiNVDIMMState { /* the IO region used by OSPM to transfer control to QEMU. */ MemoryRegion io_mr; + + /* + * Platform capabilities, section 5.2.25.9 of ACPI 6.2 Errata A + */ + int32_t persistence; + char *persistence_string; }; typedef struct AcpiNVDIMMState AcpiNVDIMMState; diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 1fc479281c..26ebb7d5e9 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -19,6 +19,7 @@ #include "exec/memory.h" #include "sysemu/hostmem.h" #include "hw/qdev.h" +#include "hw/boards.h" #define TYPE_PC_DIMM "pc-dimm" #define PC_DIMM(obj) \ @@ -61,9 +62,11 @@ typedef struct PCDIMMDevice { * @realize: called after common dimm is realized so that the dimm based * devices get the chance to do specified operations. * @get_memory_region: returns #MemoryRegion associated with @dimm which - * is directly mapped into the physical address space of guest. + * is directly mapped into the physical address space of guest. Will not + * fail after the device was realized. * @get_vmstate_memory_region: returns #MemoryRegion which indicates the - * memory of @dimm should be kept during live migration. + * memory of @dimm should be kept during live migration. Will not fail + * after the device was realized. */ typedef struct PCDIMMDeviceClass { /* private */ @@ -72,32 +75,11 @@ typedef struct PCDIMMDeviceClass { /* public */ void (*realize)(PCDIMMDevice *dimm, Error **errp); MemoryRegion *(*get_memory_region)(PCDIMMDevice *dimm, Error **errp); - MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm); + MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm, + Error **errp); } PCDIMMDeviceClass; -/** - * MemoryHotplugState: - * @base: address in guest physical address space where hotplug memory - * address space begins. - * @mr: hotplug memory address space container - */ -typedef struct MemoryHotplugState { - hwaddr base; - MemoryRegion mr; -} MemoryHotplugState; - -uint64_t pc_dimm_get_free_addr(uint64_t address_space_start, - uint64_t address_space_size, - uint64_t *hint, uint64_t align, uint64_t size, - Error **errp); - -int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); - -MemoryDeviceInfoList *qmp_pc_dimm_device_list(void); -uint64_t pc_existing_dimms_capacity(Error **errp); -uint64_t get_plugged_memory_size(void); -void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr, uint64_t align, Error **errp); -void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms, - MemoryRegion *mr); +void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, + Error **errp); +void pc_dimm_unplug(DeviceState *dev, MachineState *machine); #endif diff --git a/include/hw/mips/bios.h b/include/hw/mips/bios.h index b4b88ac43d..d67ef33e83 100644 --- a/include/hw/mips/bios.h +++ b/include/hw/mips/bios.h @@ -1,6 +1,7 @@ +#include "qemu/units.h" #include "cpu.h" -#define BIOS_SIZE (4 * 1024 * 1024) +#define BIOS_SIZE (4 * MiB) #ifdef TARGET_WORDS_BIGENDIAN #define BIOS_FILENAME "mips_bios.bin" #else diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index d70cc0aeca..38996adc59 100644 --- a/include/hw/misc/aspeed_scu.h +++ b/include/hw/misc/aspeed_scu.h @@ -30,6 +30,10 @@ typedef struct AspeedSCUState { uint32_t hw_strap1; uint32_t hw_strap2; uint32_t hw_prot_key; + + uint32_t clkin; + uint32_t hpll; + uint32_t apb_freq; } AspeedSCUState; #define AST2400_A0_SILICON_REV 0x02000303U @@ -37,6 +41,8 @@ typedef struct AspeedSCUState { #define AST2500_A0_SILICON_REV 0x04000303U #define AST2500_A1_SILICON_REV 0x04010303U +#define ASPEED_IS_AST2500(si_rev) ((((si_rev) >> 24) & 0xff) == 0x04) + extern bool is_supported_silicon_rev(uint32_t silicon_rev); #define ASPEED_SCU_PROT_KEY 0x1688A8A8 @@ -58,7 +64,64 @@ extern bool is_supported_silicon_rev(uint32_t silicon_rev); * 1. 2012/12/29 Ryan Chen Create */ -/* Hardware Strapping Register definition (for Aspeed AST2400 SOC) +/* SCU08 Clock Selection Register + * + * 31 Enable Video Engine clock dynamic slow down + * 30:28 Video Engine clock slow down setting + * 27 2D Engine GCLK clock source selection + * 26 2D Engine GCLK clock throttling enable + * 25:23 APB PCLK divider selection + * 22:20 LPC Host LHCLK divider selection + * 19 LPC Host LHCLK clock generation/output enable control + * 18:16 MAC AHB bus clock divider selection + * 15 SD/SDIO clock running enable + * 14:12 SD/SDIO divider selection + * 11 Reserved + * 10:8 Video port output clock delay control bit + * 7 ARM CPU/AHB clock slow down enable + * 6:4 ARM CPU/AHB clock slow down setting + * 3:2 ECLK clock source selection + * 1 CPU/AHB clock slow down idle timer + * 0 CPU/AHB clock dynamic slow down enable (defined in bit[6:4]) + */ +#define SCU_CLK_GET_PCLK_DIV(x) (((x) >> 23) & 0x7) + +/* SCU24 H-PLL Parameter Register (for Aspeed AST2400 SOC) + * + * 18 H-PLL parameter selection + * 0: Select H-PLL by strapping resistors + * 1: Select H-PLL by the programmed registers (SCU24[17:0]) + * 17 Enable H-PLL bypass mode + * 16 Turn off H-PLL + * 10:5 H-PLL Numerator + * 4 H-PLL Output Divider + * 3:0 H-PLL Denumerator + * + * (Output frequency) = 24MHz * (2-OD) * [(Numerator+2) / (Denumerator+1)] + */ + +#define SCU_AST2400_H_PLL_PROGRAMMED (0x1 << 18) +#define SCU_AST2400_H_PLL_BYPASS_EN (0x1 << 17) +#define SCU_AST2400_H_PLL_OFF (0x1 << 16) + +/* SCU24 H-PLL Parameter Register (for Aspeed AST2500 SOC) + * + * 21 Enable H-PLL reset + * 20 Enable H-PLL bypass mode + * 19 Turn off H-PLL + * 18:13 H-PLL Post Divider + * 12:5 H-PLL Numerator (M) + * 4:0 H-PLL Denumerator (N) + * + * (Output frequency) = CLKIN(24MHz) * [(M+1) / (N+1)] / (P+1) + * + * The default frequency is 792Mhz when CLKIN = 24MHz + */ + +#define SCU_H_PLL_BYPASS_EN (0x1 << 20) +#define SCU_H_PLL_OFF (0x1 << 19) + +/* SCU70 Hardware Strapping Register definition (for Aspeed AST2400 SOC) * * 31:29 Software defined strapping registers * 28:27 DRAM size setting (for VGA driver use) @@ -107,12 +170,13 @@ extern bool is_supported_silicon_rev(uint32_t silicon_rev); #define SCU_AST2400_HW_STRAP_GET_CLK_SOURCE(x) (((((x) >> 23) & 0x1) << 1) \ | (((x) >> 18) & 0x1)) #define SCU_AST2400_HW_STRAP_CLK_SOURCE_MASK ((0x1 << 23) | (0x1 << 18)) -#define AST2400_CLK_25M_IN (0x1 << 23) +#define SCU_HW_STRAP_CLK_25M_IN (0x1 << 23) #define AST2400_CLK_24M_IN 0 #define AST2400_CLK_48M_IN 1 #define AST2400_CLK_25M_IN_24M_USB_CKI 2 #define AST2400_CLK_25M_IN_48M_USB_CKI 3 +#define SCU_HW_STRAP_CLK_48M_IN (0x1 << 18) #define SCU_HW_STRAP_2ND_BOOT_WDT (0x1 << 17) #define SCU_HW_STRAP_SUPER_IO_CONFIG (0x1 << 16) #define SCU_HW_STRAP_VGA_CLASS_CODE (0x1 << 15) @@ -160,8 +224,8 @@ extern bool is_supported_silicon_rev(uint32_t silicon_rev); #define AST2400_DIS_BOOT 3 /* - * Hardware strapping register definition (for Aspeed AST2500 SoC and - * higher) + * SCU70 Hardware strapping register definition (for Aspeed AST2500 + * SoC and higher) * * 31 Enable SPI Flash Strap Auto Fetch Mode * 30 Enable GPIO Strap Mode diff --git a/include/hw/misc/auxbus.h b/include/hw/misc/auxbus.h index 68ade8a90f..c15b444748 100644 --- a/include/hw/misc/auxbus.h +++ b/include/hw/misc/auxbus.h @@ -123,6 +123,18 @@ I2CBus *aux_get_i2c_bus(AUXBus *bus); */ void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio); -DeviceState *aux_create_slave(AUXBus *bus, const char *name, uint32_t addr); +/* aux_create_slave: Create a new device on an AUX bus + * + * @bus The AUX bus for the new device. + * @name The type of the device to be created. + */ +DeviceState *aux_create_slave(AUXBus *bus, const char *name); + +/* aux_map_slave: Map the mmio for an AUX slave on the bus. + * + * @dev The AUX slave. + * @addr The address for the slave's mmio. + */ +void aux_map_slave(AUXSlave *dev, hwaddr addr); #endif /* HW_MISC_AUXBUS_H */ diff --git a/include/hw/misc/bcm2835_mbox.h b/include/hw/misc/bcm2835_mbox.h index f4e9ff9ef6..7e8f3ce86d 100644 --- a/include/hw/misc/bcm2835_mbox.h +++ b/include/hw/misc/bcm2835_mbox.h @@ -8,7 +8,6 @@ #include "bcm2835_mbox_defs.h" #include "hw/sysbus.h" -#include "exec/address-spaces.h" #define TYPE_BCM2835_MBOX "bcm2835-mbox" #define BCM2835_MBOX(obj) \ diff --git a/include/hw/misc/bcm2835_property.h b/include/hw/misc/bcm2835_property.h index edcab603ce..11be0dbeac 100644 --- a/include/hw/misc/bcm2835_property.h +++ b/include/hw/misc/bcm2835_property.h @@ -7,7 +7,6 @@ #define BCM2835_PROPERTY_H #include "hw/sysbus.h" -#include "exec/address-spaces.h" #include "net/net.h" #include "hw/display/bcm2835_fb.h" diff --git a/include/hw/misc/iotkit-secctl.h b/include/hw/misc/iotkit-secctl.h index faad0c9190..082c14c925 100644 --- a/include/hw/misc/iotkit-secctl.h +++ b/include/hw/misc/iotkit-secctl.h @@ -39,6 +39,11 @@ * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + * Controlling the MPC in the IoTKit: + * + named GPIO input mpc_status + * Controlling each of the 16 expansion MPCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mpcexp_status[0..15] */ #ifndef IOTKIT_SECCTL_H @@ -55,6 +60,8 @@ #define IOTS_NUM_APB_PPC 2 #define IOTS_NUM_APB_EXP_PPC 4 #define IOTS_NUM_AHB_EXP_PPC 4 +#define IOTS_NUM_EXP_MPC 16 +#define IOTS_NUM_MPC 1 typedef struct IoTKitSecCtl IoTKitSecCtl; @@ -94,6 +101,7 @@ struct IoTKitSecCtl { uint32_t secrespcfg; uint32_t nsccfg; uint32_t brginten; + uint32_t mpcintstatus; IoTKitSecCtlPPC apb[IOTS_NUM_APB_PPC]; IoTKitSecCtlPPC apbexp[IOTS_NUM_APB_EXP_PPC]; diff --git a/include/hw/misc/macio/cuda.h b/include/hw/misc/macio/cuda.h index 494b709579..7dad469142 100644 --- a/include/hw/misc/macio/cuda.h +++ b/include/hw/misc/macio/cuda.h @@ -54,12 +54,21 @@ #define CUDA_TIMER_TICKLE 0x24 #define CUDA_COMBINED_FORMAT_IIC 0x25 + +/* MOS6522 CUDA */ +typedef struct MOS6522CUDAState { + /*< private >*/ + MOS6522State parent_obj; +} MOS6522CUDAState; + +#define TYPE_MOS6522_CUDA "mos6522-cuda" +#define MOS6522_CUDA(obj) OBJECT_CHECK(MOS6522CUDAState, (obj), \ + TYPE_MOS6522_CUDA) + /* Cuda */ #define TYPE_CUDA "cuda" #define CUDA(obj) OBJECT_CHECK(CUDAState, (obj), TYPE_CUDA) -typedef struct MOS6522CUDAState MOS6522CUDAState; - typedef struct CUDAState { /*< private >*/ SysBusDevice parent_obj; @@ -67,7 +76,7 @@ typedef struct CUDAState { MemoryRegion mem; ADBBusState adb_bus; - MOS6522CUDAState *mos6522_cuda; + MOS6522CUDAState mos6522_cuda; uint32_t tick_offset; uint64_t tb_frequency; @@ -92,16 +101,4 @@ typedef struct CUDAState { QEMUTimer *adb_poll_timer; } CUDAState; -/* MOS6522 CUDA */ -struct MOS6522CUDAState { - /*< private >*/ - MOS6522State parent_obj; - - CUDAState *cuda; -}; - -#define TYPE_MOS6522_CUDA "mos6522-cuda" -#define MOS6522_CUDA(obj) OBJECT_CHECK(MOS6522CUDAState, (obj), \ - TYPE_MOS6522_CUDA) - #endif /* CUDA_H */ diff --git a/include/hw/misc/macio/gpio.h b/include/hw/misc/macio/gpio.h new file mode 100644 index 0000000000..2838ae5fde --- /dev/null +++ b/include/hw/misc/macio/gpio.h @@ -0,0 +1,47 @@ +/* + * PowerMac NewWorld MacIO GPIO emulation + * + * Copyright (c) 2016 Benjamin Herrenschmidt + * Copyright (c) 2018 Mark Cave-Ayland + * + * 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 MACIO_GPIO_H +#define MACIO_GPIO_H + +#define TYPE_MACIO_GPIO "macio-gpio" +#define MACIO_GPIO(obj) OBJECT_CHECK(MacIOGPIOState, (obj), TYPE_MACIO_GPIO) + +typedef struct MacIOGPIOState { + /*< private >*/ + SysBusDevice parent; + /*< public >*/ + + OpenPICState *pic; + + MemoryRegion gpiomem; + qemu_irq gpio_extirqs[10]; + uint8_t gpio_levels[8]; + uint8_t gpio_regs[36]; /* XXX Check count */ +} MacIOGPIOState; + +void macio_set_gpio(MacIOGPIOState *s, uint32_t gpio, bool state); + +#endif diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index 4528282b36..cfaa145500 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -26,8 +26,11 @@ #ifndef MACIO_H #define MACIO_H +#include "hw/char/escc.h" #include "hw/intc/heathrow_pic.h" #include "hw/misc/macio/cuda.h" +#include "hw/misc/macio/gpio.h" +#include "hw/misc/macio/pmu.h" #include "hw/ppc/mac_dbdma.h" #include "hw/ppc/openpic.h" @@ -41,6 +44,7 @@ typedef struct MacIOState { MemoryRegion bar; CUDAState cuda; + PMUState pmu; DBDMAState dbdma; ESCCState escc; uint64_t frequency; @@ -56,7 +60,6 @@ typedef struct OldWorldMacIOState { /*< public >*/ HeathrowState *pic; - qemu_irq irqs[7]; MacIONVRAMState nvram; MACIOIDEState ide[2]; @@ -71,9 +74,11 @@ typedef struct NewWorldMacIOState { MacIOState parent_obj; /*< public >*/ + bool has_pmu; + bool has_adb; OpenPICState *pic; - qemu_irq irqs[7]; MACIOIDEState ide[2]; + MacIOGPIOState gpio; } NewWorldMacIOState; #endif /* MACIO_H */ diff --git a/include/hw/misc/macio/pmu.h b/include/hw/misc/macio/pmu.h new file mode 100644 index 0000000000..d10895ba5f --- /dev/null +++ b/include/hw/misc/macio/pmu.h @@ -0,0 +1,237 @@ +/* + * Definitions for talking to the PMU. The PMU is a microcontroller + * which controls battery charging and system power on PowerBook 3400 + * and 2400 models as well as the RTC and various other things. + * + * Copyright (C) 1998 Paul Mackerras. + * Copyright (C) 2016 Ben Herrenschmidt + */ + +#ifndef PMU_H +#define PMU_H + +/* + * PMU commands + */ + +#define PMU_POWER_CTRL0 0x10 /* control power of some devices */ +#define PMU_POWER_CTRL 0x11 /* control power of some devices */ +#define PMU_ADB_CMD 0x20 /* send ADB packet */ +#define PMU_ADB_POLL_OFF 0x21 /* disable ADB auto-poll */ +#define PMU_WRITE_NVRAM 0x33 /* write non-volatile RAM */ +#define PMU_READ_NVRAM 0x3b /* read non-volatile RAM */ +#define PMU_SET_RTC 0x30 /* set real-time clock */ +#define PMU_READ_RTC 0x38 /* read real-time clock */ +#define PMU_SET_VOLBUTTON 0x40 /* set volume up/down position */ +#define PMU_BACKLIGHT_BRIGHT 0x41 /* set backlight brightness */ +#define PMU_GET_VOLBUTTON 0x48 /* get volume up/down position */ +#define PMU_PCEJECT 0x4c /* eject PC-card from slot */ +#define PMU_BATTERY_STATE 0x6b /* report battery state etc. */ +#define PMU_SMART_BATTERY_STATE 0x6f /* report battery state (new way) */ +#define PMU_SET_INTR_MASK 0x70 /* set PMU interrupt mask */ +#define PMU_INT_ACK 0x78 /* read interrupt bits */ +#define PMU_SHUTDOWN 0x7e /* turn power off */ +#define PMU_CPU_SPEED 0x7d /* control CPU speed on some models */ +#define PMU_SLEEP 0x7f /* put CPU to sleep */ +#define PMU_POWER_EVENTS 0x8f /* Send power-event commands to PMU */ +#define PMU_I2C_CMD 0x9a /* I2C operations */ +#define PMU_RESET 0xd0 /* reset CPU */ +#define PMU_GET_BRIGHTBUTTON 0xd9 /* report brightness up/down pos */ +#define PMU_GET_COVER 0xdc /* report cover open/closed */ +#define PMU_SYSTEM_READY 0xdf /* tell PMU we are awake */ +#define PMU_DOWNLOAD_STATUS 0xe2 /* Called by MacOS during boot... */ +#define PMU_READ_PMU_RAM 0xe8 /* read the PMU RAM... ??? */ +#define PMU_GET_VERSION 0xea /* read the PMU version */ + +/* Bits to use with the PMU_POWER_CTRL0 command */ +#define PMU_POW0_ON 0x80 /* OR this to power ON the device */ +#define PMU_POW0_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ +#define PMU_POW0_HARD_DRIVE 0x04 /* Hard drive power + * (on wallstreet/lombard ?) */ + +/* Bits to use with the PMU_POWER_CTRL command */ +#define PMU_POW_ON 0x80 /* OR this to power ON the device */ +#define PMU_POW_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ +#define PMU_POW_BACKLIGHT 0x01 /* backlight power */ +#define PMU_POW_CHARGER 0x02 /* battery charger power */ +#define PMU_POW_IRLED 0x04 /* IR led power (on wallstreet) */ +#define PMU_POW_MEDIABAY 0x08 /* media bay power + * (wallstreet/lombard ?) */ + +/* Bits in PMU interrupt and interrupt mask bytes */ +#define PMU_INT_PCEJECT 0x04 /* PC-card eject buttons */ +#define PMU_INT_SNDBRT 0x08 /* sound/brightness up/down buttons */ +#define PMU_INT_ADB 0x10 /* ADB autopoll or reply data */ +#define PMU_INT_BATTERY 0x20 /* Battery state change */ +#define PMU_INT_ENVIRONMENT 0x40 /* Environment interrupts */ +#define PMU_INT_TICK 0x80 /* 1-second tick interrupt */ + +/* Other bits in PMU interrupt valid when PMU_INT_ADB is set */ +#define PMU_INT_ADB_AUTO 0x04 /* ADB autopoll, when PMU_INT_ADB */ +#define PMU_INT_WAITING_CHARGER 0x01 /* ??? */ +#define PMU_INT_AUTO_SRQ_POLL 0x02 /* ??? */ + +/* Bits in the environement message (either obtained via PMU_GET_COVER, + * or via PMU_INT_ENVIRONMENT on core99 */ +#define PMU_ENV_LID_CLOSED 0x01 /* The lid is closed */ + +/* I2C related definitions */ +#define PMU_I2C_MODE_SIMPLE 0 +#define PMU_I2C_MODE_STDSUB 1 +#define PMU_I2C_MODE_COMBINED 2 + +#define PMU_I2C_BUS_STATUS 0 +#define PMU_I2C_BUS_SYSCLK 1 +#define PMU_I2C_BUS_POWER 2 + +#define PMU_I2C_STATUS_OK 0 +#define PMU_I2C_STATUS_DATAREAD 1 +#define PMU_I2C_STATUS_BUSY 0xfe + +/* Kind of PMU (model) */ +enum { + PMU_UNKNOWN, + PMU_OHARE_BASED, /* 2400, 3400, 3500 (old G3 powerbook) */ + PMU_HEATHROW_BASED, /* PowerBook G3 series */ + PMU_PADDINGTON_BASED, /* 1999 PowerBook G3 */ + PMU_KEYLARGO_BASED, /* Core99 motherboard (PMU99) */ + PMU_68K_V1, /* 68K PMU, version 1 */ + PMU_68K_V2, /* 68K PMU, version 2 */ +}; + +/* PMU PMU_POWER_EVENTS commands */ +enum { + PMU_PWR_GET_POWERUP_EVENTS = 0x00, + PMU_PWR_SET_POWERUP_EVENTS = 0x01, + PMU_PWR_CLR_POWERUP_EVENTS = 0x02, + PMU_PWR_GET_WAKEUP_EVENTS = 0x03, + PMU_PWR_SET_WAKEUP_EVENTS = 0x04, + PMU_PWR_CLR_WAKEUP_EVENTS = 0x05, +}; + +/* Power events wakeup bits */ +enum { + PMU_PWR_WAKEUP_KEY = 0x01, /* Wake on key press */ + PMU_PWR_WAKEUP_AC_INSERT = 0x02, /* Wake on AC adapter plug */ + PMU_PWR_WAKEUP_AC_CHANGE = 0x04, + PMU_PWR_WAKEUP_LID_OPEN = 0x08, + PMU_PWR_WAKEUP_RING = 0x10, +}; + +/* + * This table indicates for each PMU opcode: + * - the number of data bytes to be sent with the command, or -1 + * if a length byte should be sent, + * - the number of response bytes which the PMU will return, or + * -1 if it will send a length byte. + */ + +static const int8_t pmu_data_len[256][2] = { +/* 0 1 2 3 4 5 6 7 */ + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 1},{ 0, 1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{ 0, 0}, + {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, -1},{ 0, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{ 0, -1}, + { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 4},{ 0, 20},{ 2, -1},{ 2, 1},{ 3, -1},{-1, -1},{-1, -1},{ 4, 0}, + { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 1},{ 0, 1},{-1, -1},{ 1, 0},{ 1, 0},{-1, -1},{-1, -1},{-1, -1}, + { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0}, + { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0, -1},{-1, -1},{-1, -1},{-1, -1}, + { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0, -1},{ 0, -1},{-1, -1},{-1, -1}, + { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, -1},{ 0, -1},{-1, -1},{-1, -1},{-1, -1},{ 5, 1},{ 4, 1},{ 4, 1}, + { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 5},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 0, 1},{ 0, 1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0}, + { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, + { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + { 1, 1},{ 1, 1},{-1, -1},{-1, -1},{ 0, 1},{ 0, -1},{-1, -1},{-1, -1}, + {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0}, + { 3, -1},{-1, -1},{ 0, 1},{-1, -1},{ 0, -1},{-1, -1},{-1, -1},{ 0, 0}, + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, +}; + +/* Command protocol state machine */ +typedef enum { + pmu_state_idle, /* Waiting for command */ + pmu_state_cmd, /* Receiving command */ + pmu_state_rsp, /* Responding to command */ +} PMUCmdState; + +/* MOS6522 PMU */ +typedef struct MOS6522PMUState { + /*< private >*/ + MOS6522State parent_obj; +} MOS6522PMUState; + +#define TYPE_MOS6522_PMU "mos6522-pmu" +#define MOS6522_PMU(obj) OBJECT_CHECK(MOS6522PMUState, (obj), \ + TYPE_MOS6522_PMU) +/** + * PMUState: + * @last_b: last value of B register + */ + +typedef struct PMUState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + + MemoryRegion mem; + uint64_t frequency; + qemu_irq via_irq; + bool via_irq_state; + + /* PMU state */ + MOS6522PMUState mos6522_pmu; + + /* PMU low level protocol state */ + PMUCmdState cmd_state; + uint8_t last_b; + uint8_t cmd; + uint32_t cmdlen; + uint32_t rsplen; + uint8_t cmd_buf_pos; + uint8_t cmd_buf[128]; + uint8_t cmd_rsp_pos; + uint8_t cmd_rsp_sz; + uint8_t cmd_rsp[128]; + + /* PMU events/interrupts */ + uint8_t intbits; + uint8_t intmask; + + /* ADB */ + bool has_adb; + ADBBusState adb_bus; + uint16_t adb_poll_mask; + uint8_t autopoll_rate_ms; + uint8_t autopoll_mask; + QEMUTimer *adb_poll_timer; + uint8_t adb_reply_size; + uint8_t adb_reply[ADB_MAX_OUT_LEN]; + + /* RTC */ + uint32_t tick_offset; + QEMUTimer *one_sec_timer; + int64_t one_sec_target; + + /* GPIO */ + MacIOGPIOState *gpio; +} PMUState; + +#define TYPE_VIA_PMU "via-pmu" +#define VIA_PMU(obj) OBJECT_CHECK(PMUState, (obj), TYPE_VIA_PMU) + +#endif /* PMU_H */ diff --git a/include/hw/misc/mips_itu.h b/include/hw/misc/mips_itu.h index b3a4532036..030eb4ac62 100644 --- a/include/hw/misc/mips_itu.h +++ b/include/hw/misc/mips_itu.h @@ -20,6 +20,8 @@ #ifndef MIPS_ITU_H #define MIPS_ITU_H +#include "hw/sysbus.h" + #define TYPE_MIPS_ITU "mips-itu" #define MIPS_ITU(obj) OBJECT_CHECK(MIPSITUState, (obj), TYPE_MIPS_ITU) diff --git a/include/hw/misc/mos6522.h b/include/hw/misc/mos6522.h index a53c161b00..03d9f0c059 100644 --- a/include/hw/misc/mos6522.h +++ b/include/hw/misc/mos6522.h @@ -130,10 +130,11 @@ typedef struct MOS6522State { typedef struct MOS6522DeviceClass { DeviceClass parent_class; - DeviceRealize parent_realize; + DeviceReset parent_reset; void (*set_sr_int)(MOS6522State *dev); void (*portB_write)(MOS6522State *dev); void (*portA_write)(MOS6522State *dev); + void (*update_irq)(MOS6522State *dev); /* These are used to influence the CUDA MacOS timebase calibration */ uint64_t (*get_timer1_counter_value)(MOS6522State *dev, MOS6522Timer *ti); uint64_t (*get_timer2_counter_value)(MOS6522State *dev, MOS6522Timer *ti); @@ -146,6 +147,8 @@ typedef struct MOS6522DeviceClass { #define MOS6522_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(MOS6522DeviceClass, (obj), TYPE_MOS6522) +extern const VMStateDescription vmstate_mos6522; + uint64_t mos6522_read(void *opaque, hwaddr addr, unsigned size); void mos6522_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); diff --git a/include/hw/misc/pca9552.h b/include/hw/misc/pca9552.h new file mode 100644 index 0000000000..ebb43c63fe --- /dev/null +++ b/include/hw/misc/pca9552.h @@ -0,0 +1,32 @@ +/* + * PCA9552 I2C LED blinker + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * 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 PCA9552_H +#define PCA9552_H + +#include "hw/i2c/i2c.h" + +#define TYPE_PCA9552 "pca9552" +#define PCA9552(obj) OBJECT_CHECK(PCA9552State, (obj), TYPE_PCA9552) + +#define PCA9552_NR_REGS 10 + +typedef struct PCA9552State { + /*< private >*/ + I2CSlave i2c; + /*< public >*/ + + uint8_t len; + uint8_t pointer; + + uint8_t regs[PCA9552_NR_REGS]; + uint8_t max_reg; + uint8_t nr_leds; +} PCA9552State; + +#endif diff --git a/include/hw/misc/pca9552_regs.h b/include/hw/misc/pca9552_regs.h new file mode 100644 index 0000000000..d8051cfbd6 --- /dev/null +++ b/include/hw/misc/pca9552_regs.h @@ -0,0 +1,32 @@ +/* + * PCA9552 I2C LED blinker registers + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * 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 PCA9552_REGS_H +#define PCA9552_REGS_H + +/* + * Bits [0:3] are used to address a specific register. + */ +#define PCA9552_INPUT0 0 /* read only input register 0 */ +#define PCA9552_INPUT1 1 /* read only input register 1 */ +#define PCA9552_PSC0 2 /* read/write frequency prescaler 0 */ +#define PCA9552_PWM0 3 /* read/write PWM register 0 */ +#define PCA9552_PSC1 4 /* read/write frequency prescaler 1 */ +#define PCA9552_PWM1 5 /* read/write PWM register 1 */ +#define PCA9552_LS0 6 /* read/write LED0 to LED3 selector */ +#define PCA9552_LS1 7 /* read/write LED4 to LED7 selector */ +#define PCA9552_LS2 8 /* read/write LED8 to LED11 selector */ +#define PCA9552_LS3 9 /* read/write LED12 to LED15 selector */ + +/* + * Bit [4] is used to activate the Auto-Increment option of the + * register address + */ +#define PCA9552_AUTOINC (1 << 4) + +#endif diff --git a/include/hw/misc/tz-mpc.h b/include/hw/misc/tz-mpc.h new file mode 100644 index 0000000000..6f15945410 --- /dev/null +++ b/include/hw/misc/tz-mpc.h @@ -0,0 +1,80 @@ +/* + * ARM AHB5 TrustZone Memory Protection Controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the TrustZone memory protection controller (MPC). + * It is documented in the ARM CoreLink SIE-200 System IP for Embedded TRM + * (DDI 0571G): + * https://developer.arm.com/products/architecture/m-profile/docs/ddi0571/g + * + * The MPC sits in front of memory and allows secure software to + * configure it to either pass through or reject transactions. + * Rejected transactions may be configured to either be aborted, or to + * behave as RAZ/WI. An interrupt can be signalled for a rejected transaction. + * + * The MPC has a register interface which the guest uses to configure it. + * + * QEMU interface: + * + sysbus MMIO region 0: MemoryRegion for the MPC's config registers + * + sysbus MMIO region 1: MemoryRegion for the upstream end of the MPC + * + Property "downstream": MemoryRegion defining the downstream memory + * + Named GPIO output "irq": set for a transaction-failed interrupt + */ + +#ifndef TZ_MPC_H +#define TZ_MPC_H + +#include "hw/sysbus.h" + +#define TYPE_TZ_MPC "tz-mpc" +#define TZ_MPC(obj) OBJECT_CHECK(TZMPC, (obj), TYPE_TZ_MPC) + +#define TZ_NUM_PORTS 16 + +#define TYPE_TZ_MPC_IOMMU_MEMORY_REGION "tz-mpc-iommu-memory-region" + +typedef struct TZMPC TZMPC; + +struct TZMPC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + + /* State */ + uint32_t ctrl; + uint32_t blk_idx; + uint32_t int_stat; + uint32_t int_en; + uint32_t int_info1; + uint32_t int_info2; + + uint32_t *blk_lut; + + qemu_irq irq; + + /* Properties */ + MemoryRegion *downstream; + + hwaddr blocksize; + uint32_t blk_max; + + /* MemoryRegions exposed to user */ + MemoryRegion regmr; + IOMMUMemoryRegion upstream; + + /* MemoryRegion used internally */ + MemoryRegion blocked_io; + + AddressSpace downstream_as; + AddressSpace blocked_io_as; +}; + +#endif diff --git a/include/hw/net/allwinner_emac.h b/include/hw/net/allwinner_emac.h index 4cc8aab7ec..905a43deb4 100644 --- a/include/hw/net/allwinner_emac.h +++ b/include/hw/net/allwinner_emac.h @@ -23,6 +23,7 @@ #ifndef ALLWINNER_EMAC_H #define ALLWINNER_EMAC_H +#include "qemu/units.h" #include "net/net.h" #include "qemu/fifo8.h" #include "hw/net/mii.h" @@ -125,8 +126,8 @@ #define EMAC_INT_RX (1 << 8) /* Due to lack of specifications, size of fifos is chosen arbitrarily */ -#define TX_FIFO_SIZE (4 * 1024) -#define RX_FIFO_SIZE (32 * 1024) +#define TX_FIFO_SIZE (4 * KiB) +#define RX_FIFO_SIZE (32 * KiB) #define NUM_TX_FIFOS 2 #define RX_HDR_SIZE 8 diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h index d9bc589fbf..94cfe05332 100644 --- a/include/hw/net/ftgmac100.h +++ b/include/hw/net/ftgmac100.h @@ -16,6 +16,11 @@ #include "hw/sysbus.h" #include "net/net.h" +/* + * Max frame size for the receiving buffer + */ +#define FTGMAC100_MAX_FRAME_SIZE 9220 + typedef struct FTGMAC100State { /*< private >*/ SysBusDevice parent_obj; @@ -26,7 +31,7 @@ typedef struct FTGMAC100State { qemu_irq irq; MemoryRegion iomem; - uint8_t *frame; + uint8_t frame[FTGMAC100_MAX_FRAME_SIZE]; uint32_t irq_state; uint32_t isr; diff --git a/include/hw/or-irq.h b/include/hw/or-irq.h index 3f6fc1b58a..5a31e5a188 100644 --- a/include/hw/or-irq.h +++ b/include/hw/or-irq.h @@ -31,7 +31,10 @@ #define TYPE_OR_IRQ "or-irq" -#define MAX_OR_LINES 16 +/* This can safely be increased if necessary without breaking + * migration compatibility (as long as it remains greater than 15). + */ +#define MAX_OR_LINES 32 typedef struct OrIRQState qemu_or_irq; diff --git a/include/hw/pci-host/uninorth.h b/include/hw/pci-host/uninorth.h new file mode 100644 index 0000000000..2a1cf9f284 --- /dev/null +++ b/include/hw/pci-host/uninorth.h @@ -0,0 +1,69 @@ +/* + * QEMU Uninorth PCI host (for all Mac99 and newer machines) + * + * Copyright (c) 2006 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. + */ + +#ifndef UNINORTH_H +#define UNINORTH_H + +#include "hw/hw.h" + +#include "hw/ppc/openpic.h" + +/* UniNorth version */ +#define UNINORTH_VERSION_10A 0x7 + +#define TYPE_UNI_NORTH_PCI_HOST_BRIDGE "uni-north-pci-pcihost" +#define TYPE_UNI_NORTH_AGP_HOST_BRIDGE "uni-north-agp-pcihost" +#define TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE "uni-north-internal-pci-pcihost" +#define TYPE_U3_AGP_HOST_BRIDGE "u3-agp-pcihost" + +#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINHostState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) +#define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINHostState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) +#define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINHostState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) +#define U3_AGP_HOST_BRIDGE(obj) \ + OBJECT_CHECK(UNINHostState, (obj), TYPE_U3_AGP_HOST_BRIDGE) + +typedef struct UNINHostState { + PCIHostState parent_obj; + + OpenPICState *pic; + qemu_irq irqs[4]; + MemoryRegion pci_mmio; + MemoryRegion pci_hole; + MemoryRegion pci_io; +} UNINHostState; + +typedef struct UNINState { + SysBusDevice parent_obj; + + MemoryRegion mem; +} UNINState; + +#define TYPE_UNI_NORTH "uni-north" +#define UNI_NORTH(obj) \ + OBJECT_CHECK(UNINState, (obj), TYPE_UNI_NORTH) + +#endif /* UNINORTH_H */ diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index a9c3ee5aa2..990d6fcbde 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -101,6 +101,7 @@ extern bool pci_available; #define PCI_DEVICE_ID_REDHAT_PCIE_RP 0x000c #define PCI_DEVICE_ID_REDHAT_XHCI 0x000d #define PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE 0x000e +#define PCI_DEVICE_ID_REDHAT_MDPY 0x000f #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 diff --git a/include/hw/platform-bus.h b/include/hw/platform-bus.h index a00775cba6..19e20c57ce 100644 --- a/include/hw/platform-bus.h +++ b/include/hw/platform-bus.h @@ -37,8 +37,6 @@ typedef struct PlatformBusDevice PlatformBusDevice; struct PlatformBusDevice { /*< private >*/ SysBusDevice parent_obj; - Notifier notifier; - bool done_gathering; /*< public >*/ uint32_t mmio_size; @@ -54,4 +52,6 @@ int platform_bus_get_irqn(PlatformBusDevice *platform_bus, SysBusDevice *sbdev, hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev, int n); +void platform_bus_link_device(PlatformBusDevice *pbus, SysBusDevice *sbdev); + #endif /* HW_PLATFORM_BUS_H */ diff --git a/include/hw/ppc/fdt.h b/include/hw/ppc/fdt.h index bd5b0a8c3d..a8cd85069f 100644 --- a/include/hw/ppc/fdt.h +++ b/include/hw/ppc/fdt.h @@ -23,7 +23,7 @@ } \ } while (0) -size_t ppc_create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, +size_t ppc_create_page_sizes_prop(PowerPCCPU *cpu, uint32_t *prop, size_t maxsize); #endif /* PPC_FDT_H */ diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 90759240a7..86d5f54e54 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -57,12 +57,32 @@ typedef struct PnvChip { MemoryRegion xscom_mmio; MemoryRegion xscom; AddressSpace xscom_as; +} PnvChip; + +#define TYPE_PNV8_CHIP "pnv8-chip" +#define PNV8_CHIP(obj) OBJECT_CHECK(Pnv8Chip, (obj), TYPE_PNV8_CHIP) + +typedef struct Pnv8Chip { + /*< private >*/ + PnvChip parent_obj; + + /*< public >*/ MemoryRegion icp_mmio; PnvLpcController lpc; PnvPsi psi; PnvOCC occ; -} PnvChip; +} Pnv8Chip; + +#define TYPE_PNV9_CHIP "pnv9-chip" +#define PNV9_CHIP(obj) OBJECT_CHECK(Pnv9Chip, (obj), TYPE_PNV9_CHIP) + +typedef struct Pnv9Chip { + /*< private >*/ + PnvChip parent_obj; + + /*< public >*/ +} Pnv9Chip; typedef struct PnvChipClass { /*< private >*/ @@ -75,7 +95,11 @@ typedef struct PnvChipClass { hwaddr xscom_base; + DeviceRealize parent_realize; + uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); + Object *(*intc_create)(PnvChip *chip, Object *child, Error **errp); + ISABus *(*isa_create)(PnvChip *chip, Error **errp); } PnvChipClass; #define PNV_CHIP_TYPE_SUFFIX "-" TYPE_PNV_CHIP diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index e337af7a3a..447ae761f7 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -34,7 +34,7 @@ typedef struct PnvCore { CPUCore parent_obj; /*< public >*/ - void *threads; + PowerPCCPU **threads; uint32_t pir; MemoryRegion xscom_regs; diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 023b4f0fec..d657489b07 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -38,6 +38,7 @@ typedef struct PnvLpcController { /* ISA IO and Memory space */ MemoryRegion isa_io; MemoryRegion isa_mem; + MemoryRegion isa_fw; /* Windows from OPB to ISA (aliases) */ MemoryRegion opb_isa_io; @@ -69,7 +70,6 @@ typedef struct PnvLpcController { PnvPsi *psi; } PnvLpcController; -qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, - int nirqs); +ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp); #endif /* _PPC_PNV_LPC_H */ diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index ff0ac306be..298ec354a8 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -100,6 +100,8 @@ enum { #define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07) #define FW_CFG_PPC_NVRAM_ADDR (FW_CFG_ARCH_LOCAL + 0x08) #define FW_CFG_PPC_BUSFREQ (FW_CFG_ARCH_LOCAL + 0x09) +#define FW_CFG_PPC_NVRAM_FLAT (FW_CFG_ARCH_LOCAL + 0x0a) +#define FW_CFG_PPC_VIACONFIG (FW_CFG_ARCH_LOCAL + 0x0b) #define PPC_SERIAL_MM_BAUDBASE 399193 diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h index cb0bb55cec..3a2a04c8ce 100644 --- a/include/hw/ppc/ppc4xx.h +++ b/include/hw/ppc/ppc4xx.h @@ -25,8 +25,6 @@ #ifndef PPC4XX_H #define PPC4XX_H -#include "hw/pci/pci.h" - /* PowerPC 4xx core initialization */ PowerPCCPU *ppc4xx_init(const char *cpu_model, clk_setup_t *cpu_clk, clk_setup_t *tb_clk, diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index d60b7c6d7a..7e5de1a6fd 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -1,6 +1,7 @@ #ifndef HW_SPAPR_H #define HW_SPAPR_H +#include "qemu/units.h" #include "sysemu/dma.h" #include "hw/boards.h" #include "hw/ppc/xics.h" @@ -66,8 +67,10 @@ typedef enum { #define SPAPR_CAP_SBBC 0x04 /* Indirect Branch Serialisation */ #define SPAPR_CAP_IBS 0x05 +/* HPT Maximum Page Size (encoded as a shift) */ +#define SPAPR_CAP_HPT_MAXPAGESIZE 0x06 /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_IBS + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_HPT_MAXPAGESIZE + 1) /* * Capability Values @@ -162,7 +165,6 @@ struct sPAPRMachineState { /*< public >*/ char *kvm_type; - MemoryHotplugState hotplug_memory; const char *icp_type; @@ -749,7 +751,7 @@ int spapr_rng_populate_dt(void *fdt); #define SPAPR_MAX_RAM_SLOTS 32 /* 1GB alignment for hotplug memory region */ -#define SPAPR_HOTPLUG_MEM_ALIGN (1ULL << 30) +#define SPAPR_DEVICE_MEM_ALIGN (1 * GiB) /* * Number of 32 bit words in each LMB list entry in ibm,dynamic-memory @@ -773,10 +775,10 @@ int spapr_get_vcpu_id(PowerPCCPU *cpu); void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp); PowerPCCPU *spapr_find_cpu(int vcpu_id); -int spapr_irq_alloc(sPAPRMachineState *spapr, int irq_hint, bool lsi, - Error **errp); -int spapr_irq_alloc_block(sPAPRMachineState *spapr, int num, bool lsi, - bool align, Error **errp); +int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, + Error **errp); +#define spapr_irq_findone(spapr, errp) spapr_irq_find(spapr, 1, false, errp) +int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp); void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num); qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq); @@ -799,8 +801,13 @@ static inline uint8_t spapr_get_cap(sPAPRMachineState *spapr, int cap) return spapr->eff.caps[cap]; } -void spapr_caps_reset(sPAPRMachineState *spapr); +void spapr_caps_init(sPAPRMachineState *spapr); +void spapr_caps_apply(sPAPRMachineState *spapr); +void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu); void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp); int spapr_caps_post_migration(sPAPRMachineState *spapr); +void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize, + Error **errp); + #endif /* HW_SPAPR_H */ diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index 1129f344aa..9e2821e4b3 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -12,6 +12,7 @@ #include "hw/qdev.h" #include "hw/cpu/core.h" #include "target/ppc/cpu-qom.h" +#include "target/ppc/cpu.h" #define TYPE_SPAPR_CPU_CORE "spapr-cpu-core" #define SPAPR_CPU_CORE(obj) \ @@ -30,6 +31,7 @@ typedef struct sPAPRCPUCore { /*< public >*/ PowerPCCPU **threads; int node_id; + bool pre_3_0_migration; /* older machine don't know about sPAPRCPUState */ } sPAPRCPUCore; typedef struct sPAPRCPUCoreClass { @@ -38,4 +40,17 @@ typedef struct sPAPRCPUCoreClass { } sPAPRCPUCoreClass; const char *spapr_get_cpu_core_type(const char *cpu_type); +void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3); + +typedef struct sPAPRCPUState { + uint64_t vpa_addr; + uint64_t slb_shadow_addr, slb_shadow_size; + uint64_t dtl_addr, dtl_size; +} sPAPRCPUState; + +static inline sPAPRCPUState *spapr_cpu_state(PowerPCCPU *cpu) +{ + return (sPAPRCPUState *)cpu->machine_data; +} + #endif diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index bf25e5d954..0f2d8d715d 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -51,6 +51,7 @@ typedef struct sPAPROptionVector sPAPROptionVector; #define OV5_FORM1_AFFINITY OV_BIT(5, 0) #define OV5_HP_EVT OV_BIT(6, 5) #define OV5_HPT_RESIZE OV_BIT(6, 7) +#define OV5_DRMEM_V2 OV_BIT(22, 0) #define OV5_XIVE_BOTH OV_BIT(23, 0) #define OV5_XIVE_EXPLOIT OV_BIT(23, 1) /* 1=exploitation 0=legacy */ diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 6cebff47a7..6ac8a9392d 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -65,10 +65,11 @@ typedef struct XICSFabric XICSFabric; struct ICPStateClass { DeviceClass parent_class; - void (*realize)(ICPState *icp, Error **errp); + DeviceRealize parent_realize; + DeviceReset parent_reset; + void (*pre_save)(ICPState *icp); int (*post_load)(ICPState *icp, int version_id); - void (*reset)(ICPState *icp); void (*synchronize_state)(ICPState *icp); }; @@ -114,7 +115,9 @@ struct PnvICPState { struct ICSStateClass { DeviceClass parent_class; - void (*realize)(ICSState *s, Error **errp); + DeviceRealize parent_realize; + DeviceReset parent_reset; + void (*pre_save)(ICSState *s); int (*post_load)(ICSState *s, int version_id); void (*reject)(ICSState *s, uint32_t irq); diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h index fc4ef5cc1d..0731d9aef1 100644 --- a/include/hw/ptimer.h +++ b/include/hw/ptimer.h @@ -69,6 +69,15 @@ * not the one less. */ #define PTIMER_POLICY_NO_COUNTER_ROUND_DOWN (1 << 4) +/* + * Starting to run with a zero counter, or setting the counter to "0" via + * ptimer_set_count() or ptimer_set_limit() will not trigger the timer + * (though it will cause a reload). Only a counter decrement to "0" + * will cause a trigger. Not compatible with NO_IMMEDIATE_TRIGGER; + * ptimer_init() will assert() that you don't set both. + */ +#define PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT (1 << 5) + /* ptimer.c */ typedef struct ptimer_state ptimer_state; typedef void (*ptimer_cb)(void *opaque); diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 9453588160..f1fd0f8736 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -29,8 +29,6 @@ typedef enum DeviceCategory { DEVICE_CATEGORY_MAX } DeviceCategory; -typedef int (*qdev_initfn)(DeviceState *dev); -typedef int (*qdev_event)(DeviceState *dev); typedef void (*DeviceRealize)(DeviceState *dev, Error **errp); typedef void (*DeviceUnrealize)(DeviceState *dev, Error **errp); typedef void (*DeviceReset)(DeviceState *dev); @@ -43,13 +41,9 @@ struct VMStateDescription; * DeviceClass: * @props: Properties accessing state fields. * @realize: Callback function invoked when the #DeviceState:realized - * property is changed to %true. The default invokes @init if not %NULL. + * property is changed to %true. * @unrealize: Callback function invoked when the #DeviceState:realized * property is changed to %false. - * @init: Callback function invoked when the #DeviceState::realized property - * is changed to %true. Deprecated, new types inheriting directly from - * TYPE_DEVICE should use @realize instead, new leaf types should consult - * their respective parent type. * @hotpluggable: indicates if #DeviceClass is hotpluggable, available * as readonly "hotpluggable" property of #DeviceState instance * @@ -73,19 +67,15 @@ struct VMStateDescription; * object_initialize() in their own #TypeInfo.instance_init and forward the * realization events appropriately. * - * The @init callback is considered private to a particular bus implementation - * (immediate abstract child types of TYPE_DEVICE). Derived leaf types set an - * "init" callback on their parent class instead. - * * Any type may override the @realize and/or @unrealize callbacks but needs * to call the parent type's implementation if keeping their functionality * is desired. Refer to QOM documentation for further discussion and examples. * * * - * If a type derived directly from TYPE_DEVICE implements @realize, it does - * not need to implement @init and therefore does not need to store and call - * #DeviceClass' default @realize callback. + * Since TYPE_DEVICE doesn't implement @realize and @unrealize, types + * derived directly from it need not call their parent's @realize and + * @unrealize. * For other types consult the documentation and implementation of the * respective parent types. * @@ -124,8 +114,6 @@ typedef struct DeviceClass { const struct VMStateDescription *vmsd; /* Private to qdev / bus. */ - qdev_initfn init; /* TODO remove, once users are converted to realize */ - qdev_event exit; /* TODO remove, once users are converted to unrealize */ const char *bus_type; } DeviceClass; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index b2ad8e9faa..4f60cc88f3 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -29,7 +29,6 @@ extern const PropertyInfo qdev_prop_bios_chs_trans; extern const PropertyInfo qdev_prop_fdc_drive_type; extern const PropertyInfo qdev_prop_drive; extern const PropertyInfo qdev_prop_netdev; -extern const PropertyInfo qdev_prop_vlan; extern const PropertyInfo qdev_prop_pci_devfn; extern const PropertyInfo qdev_prop_blocksize; extern const PropertyInfo qdev_prop_pci_host_devaddr; @@ -195,8 +194,6 @@ extern const PropertyInfo qdev_prop_off_auto_pcibar; DEFINE_PROP(_n, _s, _f, qdev_prop_string, char*) #define DEFINE_PROP_NETDEV(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NICPeers) -#define DEFINE_PROP_VLAN(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, NICPeers) #define DEFINE_PROP_DRIVE(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockBackend *) #define DEFINE_PROP_MACADDR(_n, _s, _f) \ diff --git a/include/hw/riscv/sifive_clint.h b/include/hw/riscv/sifive_clint.h index aaa2a58c6e..e2865be1d1 100644 --- a/include/hw/riscv/sifive_clint.h +++ b/include/hw/riscv/sifive_clint.h @@ -47,4 +47,8 @@ enum { SIFIVE_TIME_BASE = 0xBFF8 }; +enum { + SIFIVE_CLINT_TIMEBASE_FREQ = 10000000 +}; + #endif diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index 0aebc576c1..7b6d8aed96 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -19,18 +19,25 @@ #ifndef HW_SIFIVE_E_H #define HW_SIFIVE_E_H -#define TYPE_SIFIVE_E "riscv.sifive_e" +#define TYPE_RISCV_E_SOC "riscv.sifive.e.soc" +#define RISCV_E_SOC(obj) \ + OBJECT_CHECK(SiFiveESoCState, (obj), TYPE_RISCV_E_SOC) -#define SIFIVE_E(obj) \ - OBJECT_CHECK(SiFiveEState, (obj), TYPE_SIFIVE_E) +typedef struct SiFiveESoCState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + RISCVHartArrayState cpus; + DeviceState *plic; +} SiFiveESoCState; typedef struct SiFiveEState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ - RISCVHartArrayState soc; - DeviceState *plic; + SiFiveESoCState soc; } SiFiveEState; enum { diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h index 11a5a98df1..2f2af7e686 100644 --- a/include/hw/riscv/sifive_plic.h +++ b/include/hw/riscv/sifive_plic.h @@ -56,7 +56,6 @@ typedef struct SiFivePLICState { uint32_t *claimed; uint32_t *enable; QemuMutex lock; - qemu_irq *irqs; /* config */ char *hart_config; diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index 662e8a1c1a..e8b4d9ffa3 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -19,18 +19,28 @@ #ifndef HW_SIFIVE_U_H #define HW_SIFIVE_U_H -#define TYPE_SIFIVE_U "riscv.sifive_u" +#include "hw/net/cadence_gem.h" -#define SIFIVE_U(obj) \ - OBJECT_CHECK(SiFiveUState, (obj), TYPE_SIFIVE_U) +#define TYPE_RISCV_U_SOC "riscv.sifive.u.soc" +#define RISCV_U_SOC(obj) \ + OBJECT_CHECK(SiFiveUSoCState, (obj), TYPE_RISCV_U_SOC) + +typedef struct SiFiveUSoCState { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + RISCVHartArrayState cpus; + DeviceState *plic; + CadenceGEMState gem; +} SiFiveUSoCState; typedef struct SiFiveUState { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ - RISCVHartArrayState soc; - DeviceState *plic; + SiFiveUSoCState soc; void *fdt; int fdt_size; } SiFiveUState; @@ -42,12 +52,18 @@ enum { SIFIVE_U_PLIC, SIFIVE_U_UART0, SIFIVE_U_UART1, - SIFIVE_U_DRAM + SIFIVE_U_DRAM, + SIFIVE_U_GEM }; enum { SIFIVE_U_UART0_IRQ = 3, - SIFIVE_U_UART1_IRQ = 4 + SIFIVE_U_UART1_IRQ = 4, + SIFIVE_U_GEM_IRQ = 0x35 +}; + +enum { + SIFIVE_U_CLOCK_FREQ = 1000000000 }; #define SIFIVE_U_PLIC_HART_CONFIG "MS" diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h index cb55a14d30..641b70da67 100644 --- a/include/hw/riscv/spike.h +++ b/include/hw/riscv/spike.h @@ -16,14 +16,8 @@ * this program. If not, see . */ -#ifndef HW_SPIKE_H -#define HW_SPIKE_H - -#define TYPE_RISCV_SPIKE_V1_09_1_BOARD "riscv.spike_v1_9_1" -#define TYPE_RISCV_SPIKE_V1_10_0_BOARD "riscv.spike_v1_10" - -#define SPIKE(obj) \ - OBJECT_CHECK(SpikeState, (obj), TYPE_RISCV_SPIKE_BOARD) +#ifndef HW_RISCV_SPIKE_H +#define HW_RISCV_SPIKE_H typedef struct { /*< private >*/ @@ -35,13 +29,16 @@ typedef struct { int fdt_size; } SpikeState; - enum { SPIKE_MROM, SPIKE_CLINT, SPIKE_DRAM }; +enum { + SPIKE_CLOCK_FREQ = 1000000000 +}; + #if defined(TARGET_RISCV32) #define SPIKE_V1_09_1_CPU TYPE_RISCV_CPU_RV32GCSU_V1_09_1 #define SPIKE_V1_10_0_CPU TYPE_RISCV_CPU_RV32GCSU_V1_10_0 diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 7525647e63..91163d6cbf 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -1,5 +1,5 @@ /* - * SiFive VirtIO Board + * QEMU RISC-V VirtIO machine interface * * Copyright (c) 2017 SiFive, Inc. * @@ -16,14 +16,8 @@ * this program. If not, see . */ -#ifndef HW_VIRT_H -#define HW_VIRT_H - -#define TYPE_RISCV_VIRT_BOARD "riscv.virt" -#define VIRT(obj) \ - OBJECT_CHECK(RISCVVirtState, (obj), TYPE_RISCV_VIRT_BOARD) - -enum { ROM_BASE = 0x1000 }; +#ifndef HW_RISCV_VIRT_H +#define HW_RISCV_VIRT_H typedef struct { /*< private >*/ @@ -47,7 +41,6 @@ enum { VIRT_DRAM }; - enum { UART0_IRQ = 10, VIRTIO_IRQ = 1, /* 1 to 8 */ @@ -55,6 +48,10 @@ enum { VIRTIO_NDEV = 10 }; +enum { + VIRT_CLOCK_FREQ = 1000000000 +}; + #define VIRT_PLIC_HART_CONFIG "MS" #define VIRT_PLIC_NUM_SOURCES 127 #define VIRT_PLIC_NUM_PRIORITIES 7 diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index 5698e5e96c..6cf71cec38 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -73,7 +73,7 @@ typedef struct WriteEventMask { #define WEM_RECEIVE_MASK(wem, mask_len) ((wem)->masks + 2 * (mask_len)) #define WEM_SEND_MASK(wem, mask_len) ((wem)->masks + 3 * (mask_len)) -typedef uint32_t sccb_mask_t; +typedef uint64_t sccb_mask_t; typedef struct EventBufferHeader { uint16_t length; @@ -210,4 +210,6 @@ typedef struct SCLPEventFacilityClass { bool (*event_pending)(SCLPEventFacility *ef); } SCLPEventFacilityClass; +BusState *sclp_get_event_facility_bus(void); + #endif diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index ac896e31ea..ab88d49d10 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -53,6 +53,4 @@ bool cpu_model_allowed(void); */ bool css_migration_enabled(void); -void subsystem_reset(void); - #endif diff --git a/include/hw/s390x/tod.h b/include/hw/s390x/tod.h new file mode 100644 index 0000000000..413c0d7c02 --- /dev/null +++ b/include/hw/s390x/tod.h @@ -0,0 +1,65 @@ +/* + * TOD (Time Of Day) clock + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * 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 HW_S390_TOD_H +#define HW_S390_TOD_H + +#include "hw/qdev.h" + +typedef struct S390TOD { + uint8_t high; + uint64_t low; +} S390TOD; + +#define TYPE_S390_TOD "s390-tod" +#define S390_TOD(obj) OBJECT_CHECK(S390TODState, (obj), TYPE_S390_TOD) +#define S390_TOD_CLASS(oc) OBJECT_CLASS_CHECK(S390TODClass, (oc), \ + TYPE_S390_TOD) +#define S390_TOD_GET_CLASS(obj) OBJECT_GET_CLASS(S390TODClass, (obj), \ + TYPE_S390_TOD) +#define TYPE_KVM_S390_TOD TYPE_S390_TOD "-kvm" +#define TYPE_QEMU_S390_TOD TYPE_S390_TOD "-qemu" + +typedef struct S390TODState { + /* private */ + DeviceState parent_obj; + + /* unused by KVM implementation */ + S390TOD base; +} S390TODState; + +typedef struct S390TODClass { + /* private */ + DeviceClass parent_class; + + /* public */ + void (*get)(const S390TODState *td, S390TOD *tod, Error **errp); + void (*set)(S390TODState *td, const S390TOD *tod, Error **errp); +} S390TODClass; + +/* The value of the TOD clock for 1.1.1970. */ +#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL + +/* Converts ns to s390's clock format */ +static inline uint64_t time2tod(uint64_t ns) +{ + return (ns << 9) / 125 + (((ns & 0xff10000000000000ull) / 125) << 9); +} + +/* Converts s390's clock format to ns */ +static inline uint64_t tod2time(uint64_t t) +{ + return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9); +} + +void s390_init_tod(void); +S390TODState *s390_get_todstate(void); + +#endif diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index 93fdaced67..682a0d2de0 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -131,11 +131,6 @@ typedef struct { #define TCHI_FAS100A 0x4 #define TCHI_AM53C974 0x12 -ESPState *esp_init(hwaddr espaddr, int it_shift, - ESPDMAMemoryReadWriteFunc dma_memory_read, - ESPDMAMemoryReadWriteFunc dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset, - qemu_irq *dma_enable); void esp_dma_enable(ESPState *s, int irq, int level); void esp_request_cancelled(SCSIRequest *req); void esp_command_complete(SCSIRequest *req, uint32_t status, size_t resid); diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index e35137ea78..ee3a4118fb 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -87,6 +87,7 @@ struct SCSIDevice uint64_t port_wwn; int scsi_version; int default_scsi_version; + bool needs_vpd_bl_emulation; }; extern const VMStateDescription vmstate_scsi_device; @@ -154,6 +155,8 @@ static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d) SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk, int unit, bool removable, int bootindex, bool share_rw, + BlockdevOnError rerror, + BlockdevOnError werror, const char *serial, Error **errp); void scsi_bus_legacy_handle_cmdline(SCSIBus *bus); void scsi_legacy_handle_cmdline(void); @@ -184,8 +187,11 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense); void scsi_device_set_ua(SCSIDevice *sdev, SCSISense sense); void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); void scsi_device_unit_attention_reported(SCSIDevice *dev); -void scsi_generic_read_device_identification(SCSIDevice *dev); +void scsi_generic_read_device_inquiry(SCSIDevice *dev); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); +int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); +int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, + uint8_t *buf, uint8_t buf_size); SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); /* scsi-generic.c. */ diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 9bdb3c9285..b865aafc33 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -54,6 +54,12 @@ #define APP_CMD (1 << 5) #define AKE_SEQ_ERROR (1 << 3) +enum SDPhySpecificationVersion { + SD_PHY_SPECv1_10_VERS = 1, + SD_PHY_SPECv2_00_VERS = 2, + SD_PHY_SPECv3_01_VERS = 3, +}; + typedef enum { SD_VOLTAGE_0_4V = 400, /* currently not supported */ SD_VOLTAGE_1_8V = 1800, diff --git a/include/hw/sh4/sh_intc.h b/include/hw/sh4/sh_intc.h index 7913bc48a2..fbcee94ed7 100644 --- a/include/hw/sh4/sh_intc.h +++ b/include/hw/sh4/sh_intc.h @@ -3,7 +3,6 @@ #include "qemu-common.h" #include "hw/irq.h" -#include "exec/address-spaces.h" typedef unsigned char intc_enum; diff --git a/include/hw/ssi/xilinx_spips.h b/include/hw/ssi/xilinx_spips.h index d398a4e81c..a0a0ae7584 100644 --- a/include/hw/ssi/xilinx_spips.h +++ b/include/hw/ssi/xilinx_spips.h @@ -37,6 +37,8 @@ typedef struct XilinxSPIPS XilinxSPIPS; /* Bite off 4k chunks at a time */ #define LQSPI_CACHE_SIZE 1024 +#define QSPI_DMA_MAX_BURST_SIZE 2048 + typedef enum { READ = 0x3, READ_4 = 0x13, FAST_READ = 0xb, FAST_READ_4 = 0x0c, @@ -95,7 +97,6 @@ typedef struct { XilinxQSPIPS parent_obj; StreamSlave *dma; - uint8_t dma_buf[4]; int gqspi_irqline; uint32_t regs[XLNX_ZYNQMP_SPIPS_R_MAX]; @@ -113,6 +114,8 @@ typedef struct { uint8_t rx_fifo_g_align; uint8_t tx_fifo_g_align; bool man_start_com_g; + uint32_t dma_burst_size; + uint8_t dma_buf[QSPI_DMA_MAX_BURST_SIZE]; } XlnxZynqMPQSPIPS; typedef struct XilinxSPIPSClass { diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index e88bb6dae0..0b59a3b8d6 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -96,6 +96,23 @@ void sysbus_add_io(SysBusDevice *dev, hwaddr addr, MemoryRegion *mem); MemoryRegion *sysbus_address_space(SysBusDevice *dev); +/** + * sysbus_init_child_obj: + * @parent: The parent object + * @childname: Used as name of the "child<>" property in the parent + * @child: A pointer to the memory to be used for the object. + * @childsize: The maximum size available at @child for the object. + * @childtype: The name of the type of the object to instantiate. + * + * This function will initialize an object and attach it to the main system + * bus. The memory for the object should have already been allocated. The + * object will then be added as child to the given parent. The returned object + * has a reference count of 1 (for the "child<...>" property from the parent), + * so the object will be finalized automatically when the parent gets removed. + */ +void sysbus_init_child_obj(Object *parent, const char *childname, void *child, + size_t childsize, const char *childtype); + /* Call func for every dynamically created sysbus device in the system */ void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque); diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h index bd6c1a7f96..040a088734 100644 --- a/include/hw/timer/aspeed_timer.h +++ b/include/hw/timer/aspeed_timer.h @@ -24,6 +24,8 @@ #include "qemu/timer.h" +typedef struct AspeedSCUState AspeedSCUState; + #define ASPEED_TIMER(obj) \ OBJECT_CHECK(AspeedTimerCtrlState, (obj), TYPE_ASPEED_TIMER); #define TYPE_ASPEED_TIMER "aspeed.timer" @@ -55,6 +57,8 @@ typedef struct AspeedTimerCtrlState { uint32_t ctrl; uint32_t ctrl2; AspeedTimer timers[ASPEED_TIMER_NR_TIMERS]; + + AspeedSCUState *scu; } AspeedTimerCtrlState; #endif /* ASPEED_TIMER_H */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index d9360148e6..a9036929b2 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -22,7 +22,6 @@ #define HW_VFIO_VFIO_COMMON_H #include "qemu-common.h" -#include "exec/address-spaces.h" #include "exec/memory.h" #include "qemu/queue.h" #include "qemu/notify.h" @@ -34,15 +33,6 @@ #define ERR_PREFIX "vfio error: %s: " #define WARN_PREFIX "vfio warning: %s: " -/*#define DEBUG_VFIO*/ -#ifdef DEBUG_VFIO -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, "vfio: " fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - enum { VFIO_DEVICE_TYPE_PCI = 0, VFIO_DEVICE_TYPE_PLATFORM = 1, diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 5dac61f9ea..81283ec50f 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -101,6 +101,9 @@ typedef int (*vhost_crypto_create_session_op)(struct vhost_dev *dev, typedef int (*vhost_crypto_close_session_op)(struct vhost_dev *dev, uint64_t session_id); +typedef bool (*vhost_backend_mem_section_filter_op)(struct vhost_dev *dev, + MemoryRegionSection *section); + typedef struct VhostOps { VhostBackendType backend_type; vhost_backend_init vhost_backend_init; @@ -138,6 +141,7 @@ typedef struct VhostOps { vhost_set_config_op vhost_set_config; vhost_crypto_create_session_op vhost_crypto_create_session; vhost_crypto_close_session_op vhost_crypto_close_session; + vhost_backend_mem_section_filter_op vhost_backend_mem_section_filter; } VhostOps; extern const VhostOps user_ops; diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h index 5804cc904a..d52944aeeb 100644 --- a/include/hw/virtio/vhost-user-blk.h +++ b/include/hw/virtio/vhost-user-blk.h @@ -21,6 +21,7 @@ #include "hw/block/block.h" #include "chardev/char-fe.h" #include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" #define TYPE_VHOST_USER_BLK "vhost-user-blk" #define VHOST_USER_BLK(obj) \ @@ -34,8 +35,8 @@ typedef struct VHostUserBlk { uint16_t num_queues; uint32_t queue_size; uint32_t config_wce; - uint32_t config_ro; struct vhost_dev dev; + VhostUserState *vhost_user; } VHostUserBlk; #endif diff --git a/include/hw/virtio/vhost-user-scsi.h b/include/hw/virtio/vhost-user-scsi.h index 01861f78d0..3ec34ae867 100644 --- a/include/hw/virtio/vhost-user-scsi.h +++ b/include/hw/virtio/vhost-user-scsi.h @@ -21,6 +21,7 @@ #include "hw/qdev.h" #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" #include "hw/virtio/vhost-scsi-common.h" #define TYPE_VHOST_USER_SCSI "vhost-user-scsi" @@ -30,6 +31,7 @@ typedef struct VHostUserSCSI { VHostSCSICommon parent_obj; uint64_t host_features; + VhostUserState *vhost_user; } VHostUserSCSI; #endif /* VHOST_USER_SCSI_H */ diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h new file mode 100644 index 0000000000..fd660393a0 --- /dev/null +++ b/include/hw/virtio/vhost-user.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017-2018 Intel Corporation + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_VIRTIO_VHOST_USER_H +#define HW_VIRTIO_VHOST_USER_H + +#include "chardev/char-fe.h" +#include "hw/virtio/virtio.h" + +typedef struct VhostUserHostNotifier { + MemoryRegion mr; + void *addr; + bool set; +} VhostUserHostNotifier; + +typedef struct VhostUserState { + CharBackend *chr; + VhostUserHostNotifier notifier[VIRTIO_QUEUE_MAX]; +} VhostUserState; + +VhostUserState *vhost_user_init(void); +void vhost_user_cleanup(VhostUserState *user); + +#endif diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h index 2e92074bd1..bdf58f3119 100644 --- a/include/hw/virtio/virtio-access.h +++ b/include/hw/virtio/virtio-access.h @@ -18,7 +18,6 @@ #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-bus.h" -#include "exec/address-spaces.h" #if defined(TARGET_PPC64) || defined(TARGET_ARM) #define LEGACY_VIRTIO_IS_BIENDIAN 1 diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index 1ea13bd6a4..e0df3528c8 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -17,7 +17,6 @@ #include "standard-headers/linux/virtio_balloon.h" #include "hw/virtio/virtio.h" -#include "hw/pci/pci.h" #define TYPE_VIRTIO_BALLOON "virtio-balloon-device" #define VIRTIO_BALLOON(obj) \ diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index ced3d2d2b0..7fec9dc929 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -52,6 +52,8 @@ typedef struct VirtioBusClass { bool (*has_extra_state)(DeviceState *d); bool (*query_guest_notifiers)(DeviceState *d); int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assign); + int (*set_host_notifier_mr)(DeviceState *d, int n, + MemoryRegion *mr, bool assign); void (*vmstate_change)(DeviceState *d, bool running); /* * Expose the features the transport layer supports before diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index 22ac3c2d0e..d0321672f4 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -18,10 +18,10 @@ #include "ui/qemu-pixman.h" #include "ui/console.h" #include "hw/virtio/virtio.h" -#include "hw/pci/pci.h" #include "qemu/log.h" #include "standard-headers/linux/virtio_gpu.h" + #define TYPE_VIRTIO_GPU "virtio-gpu-device" #define VIRTIO_GPU(obj) \ OBJECT_CHECK(VirtIOGPU, (obj), TYPE_VIRTIO_GPU) @@ -125,6 +125,7 @@ typedef struct VirtIOGPU { uint32_t bytes_3d; } stats; + void (*disable_scanout)(struct VirtIOGPU *g, int scanout_id); Error *migration_blocker; } VirtIOGPU; diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 02484dc94c..4d7f3c82ca 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -14,6 +14,7 @@ #ifndef QEMU_VIRTIO_NET_H #define QEMU_VIRTIO_NET_H +#include "qemu/units.h" #include "standard-headers/linux/virtio_net.h" #include "hw/virtio/virtio.h" @@ -44,7 +45,7 @@ typedef struct virtio_net_conf } virtio_net_conf; /* Maximum packet size we can receive from tap device: header + 64k */ -#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10)) +#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 * KiB)) typedef struct VirtIONetQueue { VirtQueue *rx_vq; diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 098bdaaea3..9c1fa07d6d 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -239,6 +239,8 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align); void virtio_queue_notify(VirtIODevice *vdev, int n); uint16_t virtio_queue_vector(VirtIODevice *vdev, int n); void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector); +int virtio_queue_set_host_notifier_mr(VirtIODevice *vdev, int n, + MemoryRegion *mr, bool assign); int virtio_set_status(VirtIODevice *vdev, uint8_t val); void virtio_reset(void *opaque); void virtio_update_irq(VirtIODevice *vdev); diff --git a/include/hw/xen/io/ring.h b/include/hw/xen/io/ring.h index abbca47687..ffa3ebadc8 100644 --- a/include/hw/xen/io/ring.h +++ b/include/hw/xen/io/ring.h @@ -65,7 +65,7 @@ typedef unsigned int RING_IDX; */ #define __CONST_RING_SIZE(_s, _sz) \ (__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \ - sizeof(((struct _s##_sring *)0)->ring[0]))) + sizeof_field(struct _s##_sring, ring[0]))) /* * The same for passing in an actual pointer instead of a name tag. */ diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen_backend.h index 3a27692407..9c17fdd85d 100644 --- a/include/hw/xen/xen_backend.h +++ b/include/hw/xen/xen_backend.h @@ -16,7 +16,6 @@ /* variables */ extern struct xs_handle *xenstore; extern const char *xen_protocol; -extern bool xen_feature_grant_copy; extern DeviceState *xen_sysdev; extern BusState *xen_sysbus; @@ -42,6 +41,39 @@ void xen_be_register_common(void); int xen_be_register(const char *type, struct XenDevOps *ops); int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); int xen_be_bind_evtchn(struct XenDevice *xendev); +void xen_be_set_max_grant_refs(struct XenDevice *xendev, + unsigned int nr_refs); +void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, + unsigned int nr_refs, int prot); +void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, + unsigned int nr_refs); + +typedef struct XenGrantCopySegment { + union { + void *virt; + struct { + uint32_t ref; + off_t offset; + } foreign; + } source, dest; + size_t len; +} XenGrantCopySegment; + +int xen_be_copy_grant_refs(struct XenDevice *xendev, + bool to_domain, XenGrantCopySegment segs[], + unsigned int nr_segs); + +static inline void *xen_be_map_grant_ref(struct XenDevice *xendev, + uint32_t ref, int prot) +{ + return xen_be_map_grant_refs(xendev, &ref, 1, prot); +} + +static inline void xen_be_unmap_grant_ref(struct XenDevice *xendev, + void *ptr) +{ + return xen_be_unmap_grant_refs(xendev, ptr, 1); +} /* actual backend drivers */ extern struct XenDevOps xen_console_ops; /* xen_console.c */ diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 64a978e4e0..93f631e5bf 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -78,6 +78,65 @@ static inline void *xenforeignmemory_map(xc_interface *h, uint32_t dom, extern xenforeignmemory_handle *xen_fmem; +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 + +typedef xc_interface xendevicemodel_handle; + +#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40900 */ + +#undef XC_WANT_COMPAT_DEVICEMODEL_API +#include + +#endif + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 + +static inline int xendevicemodel_relocate_memory( + xendevicemodel_handle *dmod, domid_t domid, uint32_t size, uint64_t src_gfn, + uint64_t dst_gfn) +{ + uint32_t i; + int rc; + + for (i = 0; i < size; i++) { + unsigned long idx = src_gfn + i; + xen_pfn_t gpfn = dst_gfn + i; + + rc = xc_domain_add_to_physmap(xen_xc, domid, XENMAPSPACE_gmfn, idx, + gpfn); + if (rc) { + return rc; + } + } + + return 0; +} + +static inline int xendevicemodel_pin_memory_cacheattr( + xendevicemodel_handle *dmod, domid_t domid, uint64_t start, uint64_t end, + uint32_t type) +{ + return xc_domain_pin_memory_cacheattr(xen_xc, domid, start, end, type); +} + +typedef void xenforeignmemory_resource_handle; + +#define XENMEM_resource_ioreq_server 0 + +#define XENMEM_resource_ioreq_server_frame_bufioreq 0 +#define XENMEM_resource_ioreq_server_frame_ioreq(n) (1 + (n)) + +static inline xenforeignmemory_resource_handle *xenforeignmemory_map_resource( + xenforeignmemory_handle *fmem, domid_t domid, unsigned int type, + unsigned int id, unsigned long frame, unsigned long nr_frames, + void **paddr, int prot, int flags) +{ + errno = EOPNOTSUPP; + return NULL; +} + +#endif /* CONFIG_XEN_CTRL_INTERFACE_VERSION < 41100 */ + #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 41000 #define XEN_COMPAT_PHYSMAP @@ -91,12 +150,27 @@ static inline void *xenforeignmemory_map2(xenforeignmemory_handle *h, return xenforeignmemory_map(h, dom, prot, pages, arr, err); } +static inline int xentoolcore_restrict_all(domid_t domid) +{ + errno = ENOTTY; + return -1; +} + +static inline int xendevicemodel_shutdown(xendevicemodel_handle *dmod, + domid_t domid, unsigned int reason) +{ + errno = ENOTTY; + return -1; +} + +#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 41000 */ + +#include + #endif #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40900 -typedef xc_interface xendevicemodel_handle; - static inline xendevicemodel_handle *xendevicemodel_open( struct xentoollog_logger *logger, unsigned int open_flags) { @@ -218,25 +292,6 @@ static inline int xendevicemodel_set_mem_type( return xc_hvm_set_mem_type(dmod, domid, mem_type, first_pfn, nr); } -static inline int xendevicemodel_restrict( - xendevicemodel_handle *dmod, domid_t domid) -{ - errno = ENOTTY; - return -1; -} - -static inline int xenforeignmemory_restrict( - xenforeignmemory_handle *fmem, domid_t domid) -{ - errno = ENOTTY; - return -1; -} - -#else /* CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40900 */ - -#undef XC_WANT_COMPAT_DEVICEMODEL_API -#include - #endif extern xendevicemodel_handle *xen_dmod; @@ -290,28 +345,8 @@ static inline int xen_modified_memory(domid_t domid, uint64_t first_pfn, static inline int xen_restrict(domid_t domid) { int rc; - - /* Attempt to restrict devicemodel operations */ - rc = xendevicemodel_restrict(xen_dmod, domid); + rc = xentoolcore_restrict_all(domid); trace_xen_domid_restrict(rc ? errno : 0); - - if (rc < 0) { - /* - * If errno is ENOTTY then restriction is not implemented so - * there's no point in trying to restrict other types of - * operation, but it should not be treated as a failure. - */ - if (errno == ENOTTY) { - return 0; - } - - return rc; - } - - /* Restrict foreignmemory operations */ - rc = xenforeignmemory_restrict(xen_fmem, domid); - trace_xen_domid_restrict(rc ? errno : 0); - return rc; } @@ -626,28 +661,6 @@ static inline int xen_set_ioreq_server_state(domid_t dom, #endif -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40600 -static inline int xen_xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid, - unsigned int space, - unsigned long idx, - xen_pfn_t gpfn) -{ - return xc_domain_add_to_physmap(xch, domid, space, idx, gpfn); -} -#else -static inline int xen_xc_domain_add_to_physmap(xc_interface *xch, uint32_t domid, - unsigned int space, - unsigned long idx, - xen_pfn_t gpfn) -{ - /* In Xen 4.6 rc is -1 and errno contains the error value. */ - int rc = xc_domain_add_to_physmap(xch, domid, space, idx, gpfn); - if (rc == -1) - return errno; - return rc; -} -#endif - #ifdef CONFIG_XEN_PV_DOMAIN_BUILD #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40700 static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, @@ -670,8 +683,21 @@ static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40800 +struct xengnttab_grant_copy_segment { + union xengnttab_copy_ptr { + void *virt; + struct { + uint32_t ref; + uint16_t offset; + uint16_t domid; + } foreign; + } source, dest; + uint16_t len; + uint16_t flags; + int16_t status; +}; -typedef void *xengnttab_grant_copy_segment_t; +typedef struct xengnttab_grant_copy_segment xengnttab_grant_copy_segment_t; static inline int xengnttab_grant_copy(xengnttab_handle *xgt, uint32_t count, xengnttab_grant_copy_segment_t *segs) diff --git a/include/migration/register.h b/include/migration/register.h index f6f12f9b1a..d287f4c317 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -64,6 +64,8 @@ typedef struct SaveVMHandlers { LoadStateHandler *load_state; int (*load_setup)(QEMUFile *f, void *opaque); int (*load_cleanup)(void *opaque); + /* Called when postcopy migration wants to resume from failure */ + int (*resume_prepare)(MigrationState *s, void *opaque); } SaveVMHandlers; int register_savevm_live(DeviceState *dev, diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index df463fd33d..42b946ce90 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -27,8 +27,6 @@ #ifndef QEMU_VMSTATE_H #define QEMU_VMSTATE_H -#include "migration/qjson.h" - typedef struct VMStateInfo VMStateInfo; typedef struct VMStateDescription VMStateDescription; typedef struct VMStateField VMStateField; @@ -143,6 +141,11 @@ enum VMStateFlags { * to determine the number of entries in the array. Only valid in * combination with one of VMS_VARRAY*. */ VMS_MULTIPLY_ELEMENTS = 0x4000, + + /* A structure field that is like VMS_STRUCT, but uses + * VMStateField.struct_version_id to tell which version of the + * structure we are referencing to use. */ + VMS_VSTRUCT = 0x8000, }; typedef enum { @@ -167,6 +170,7 @@ struct VMStateField { enum VMStateFlags flags; const VMStateDescription *vmsd; int version_id; + int struct_version_id; bool (*field_exists)(void *opaque, int version_id); }; @@ -248,6 +252,25 @@ extern const VMStateInfo vmstate_info_qtailq; vmstate_offset_array(_state, _field, uint8_t, \ sizeof(typeof_field(_state, _field))) +/* In the macros below, if there is a _version, that means the macro's + * field will be processed only if the version being received is >= + * the _version specified. In general, if you add a new field, you + * would increment the structure's version and put that version + * number into the new field so it would only be processed with the + * new version. + * + * In particular, for VMSTATE_STRUCT() and friends the _version does + * *NOT* pick the version of the sub-structure. It works just as + * specified above. The version of the top-level structure received + * is passed down to all sub-structures. This means that the + * sub-structures must have version that are compatible with all the + * structures that use them. + * + * If you want to specify the version of the sub-structure, use + * VMSTATE_VSTRUCT(), which allows the specific sub-structure version + * to be directly specified. + */ + #define VMSTATE_SINGLE_TEST(_field, _state, _test, _version, _info, _type) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -395,6 +418,17 @@ extern const VMStateInfo vmstate_info_qtailq; .offset = offsetof(_state, _field), \ } +#define VMSTATE_VSTRUCT_TEST(_field, _state, _test, _version, _vmsd, _type, _struct_version) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .struct_version_id = (_struct_version), \ + .field_exists = (_test), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_VSTRUCT, \ + .offset = vmstate_offset_value(_state, _field, _type), \ +} + #define VMSTATE_STRUCT_TEST(_field, _state, _test, _version, _vmsd, _type) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -712,6 +746,13 @@ extern const VMStateInfo vmstate_info_qtailq; #define VMSTATE_SINGLE(_field, _state, _version, _info, _type) \ VMSTATE_SINGLE_TEST(_field, _state, NULL, _version, _info, _type) +#define VMSTATE_VSTRUCT(_field, _state, _vmsd, _type, _struct_version)\ + VMSTATE_VSTRUCT_TEST(_field, _state, NULL, 0, _vmsd, _type, _struct_version) + +#define VMSTATE_VSTRUCT_V(_field, _state, _version, _vmsd, _type, _struct_version) \ + VMSTATE_VSTRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type, \ + _struct_version) + #define VMSTATE_STRUCT(_field, _state, _version, _vmsd, _type) \ VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type) @@ -870,6 +911,9 @@ extern const VMStateInfo vmstate_info_qtailq; #define VMSTATE_BOOL_ARRAY(_f, _s, _n) \ VMSTATE_BOOL_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_BOOL_SUB_ARRAY(_f, _s, _start, _num) \ + VMSTATE_SUB_ARRAY(_f, _s, _start, _num, 0, vmstate_info_bool, bool) + #define VMSTATE_UINT16_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint16, uint16_t) @@ -1000,6 +1044,8 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id); int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, QJSON *vmdesc); +int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque, QJSON *vmdesc, int version_id); bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque); diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index d6ab70cae2..2ef5e04b37 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -6,7 +6,7 @@ #include "qapi/qapi-types-misc.h" #include "qemu/readline.h" -extern Monitor *cur_mon; +extern __thread Monitor *cur_mon; /* flags for monitor_init */ /* 0x01 unused */ diff --git a/include/net/net.h b/include/net/net.h index 1f7341e459..1425960f76 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -37,7 +37,6 @@ typedef struct NICConf { #define DEFINE_NIC_PROPERTIES(_state, _conf) \ DEFINE_PROP_MACADDR("mac", _state, _conf.macaddr), \ - DEFINE_PROP_VLAN("vlan", _state, _conf.peers), \ DEFINE_PROP_NETDEV("netdev", _state, _conf.peers) diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index ffb4652f71..4e2e749faf 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -23,6 +23,7 @@ typedef enum QmpCommandOptions QCO_NO_OPTIONS = 0x0, QCO_NO_SUCCESS_RESP = (1U << 0), QCO_ALLOW_OOB = (1U << 1), + QCO_ALLOW_PRECONFIG = (1U << 2), } QmpCommandOptions; typedef struct QmpCommand @@ -40,15 +41,15 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); void qmp_unregister_command(QmpCommandList *cmds, const char *name); QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name); -QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); bool qmp_command_is_enabled(const QmpCommand *cmd); const char *qmp_command_name(const QmpCommand *cmd); bool qmp_has_success_response(const QmpCommand *cmd); -QObject *qmp_build_error_object(Error *err); -QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp); +QDict *qmp_error_response(Error *err); +QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, + bool allow_oob); bool qmp_is_oob(QDict *dict); typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque); diff --git a/include/qapi/qmp/qbool.h b/include/qapi/qmp/qbool.h index b9a44a1bfe..5f61e38e64 100644 --- a/include/qapi/qmp/qbool.h +++ b/include/qapi/qmp/qbool.h @@ -17,7 +17,7 @@ #include "qapi/qmp/qobject.h" struct QBool { - QObject base; + struct QObjectBase_ base; bool value; }; diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h index 2cc3e906f7..7f3ec10a10 100644 --- a/include/qapi/qmp/qdict.h +++ b/include/qapi/qmp/qdict.h @@ -25,7 +25,7 @@ typedef struct QDictEntry { } QDictEntry; struct QDict { - QObject base; + struct QObjectBase_ base; size_t size; QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX]; }; @@ -67,23 +67,6 @@ int64_t qdict_get_try_int(const QDict *qdict, const char *key, bool qdict_get_try_bool(const QDict *qdict, const char *key, bool def_value); const char *qdict_get_try_str(const QDict *qdict, const char *key); -void qdict_copy_default(QDict *dst, QDict *src, const char *key); -void qdict_set_default_str(QDict *dst, const char *key, const char *val); - QDict *qdict_clone_shallow(const QDict *src); -void qdict_flatten(QDict *qdict); - -void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start); -void qdict_array_split(QDict *src, QList **dst); -int qdict_array_entries(QDict *src, const char *subqdict); -QObject *qdict_crumple(const QDict *src, Error **errp); - -void qdict_join(QDict *dest, QDict *src, bool overwrite); - -typedef struct QDictRenames { - const char *from; - const char *to; -} QDictRenames; -bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp); #endif /* QDICT_H */ diff --git a/include/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h index b274ac3a86..43b2ce2f33 100644 --- a/include/qapi/qmp/qjson.h +++ b/include/qapi/qmp/qjson.h @@ -19,6 +19,8 @@ QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2); QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp) GCC_FMT_ATTR(1, 0); +QDict *qdict_from_jsonf_nofail(const char *string, ...) GCC_FMT_ATTR(1, 2); + QString *qobject_to_json(const QObject *obj); QString *qobject_to_json_pretty(const QObject *obj); diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h index 5c673acb06..8d2c32ca28 100644 --- a/include/qapi/qmp/qlist.h +++ b/include/qapi/qmp/qlist.h @@ -22,7 +22,7 @@ typedef struct QListEntry { } QListEntry; struct QList { - QObject base; + struct QObjectBase_ base; QTAILQ_HEAD(,QListEntry) head; }; diff --git a/include/qapi/qmp/qnull.h b/include/qapi/qmp/qnull.h index c992ee2ae1..c1426882c5 100644 --- a/include/qapi/qmp/qnull.h +++ b/include/qapi/qmp/qnull.h @@ -16,15 +16,14 @@ #include "qapi/qmp/qobject.h" struct QNull { - QObject base; + struct QObjectBase_ base; }; extern QNull qnull_; static inline QNull *qnull(void) { - QINCREF(&qnull_); - return &qnull_; + return qobject_ref(&qnull_); } bool qnull_is_equal(const QObject *x, const QObject *y); diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h index 3e47475b2c..45bf02a036 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qapi/qmp/qnum.h @@ -45,7 +45,7 @@ typedef enum { * convert under the hood. */ struct QNum { - QObject base; + struct QObjectBase_ base; QNumKind kind; union { int64_t i64; diff --git a/include/qapi/qmp/qobject.h b/include/qapi/qmp/qobject.h index e022707578..fcfd549220 100644 --- a/include/qapi/qmp/qobject.h +++ b/include/qapi/qmp/qobject.h @@ -15,17 +15,17 @@ * ------------------------------------ * * - Returning references: A function that returns an object may - * return it as either a weak or a strong reference. If the reference - * is strong, you are responsible for calling QDECREF() on the reference - * when you are done. + * return it as either a weak or a strong reference. If the + * reference is strong, you are responsible for calling + * qobject_unref() on the reference when you are done. * * If the reference is weak, the owner of the reference may free it at * any time in the future. Before storing the reference anywhere, you - * should call QINCREF() to make the reference strong. + * should call qobject_ref() to make the reference strong. * * - Transferring ownership: when you transfer ownership of a reference * by calling a function, you are no longer responsible for calling - * QDECREF() when the reference is no longer needed. In other words, + * qobject_unref() when the reference is no longer needed. In other words, * when the function returns you must behave as if the reference to the * passed object was weak. */ @@ -34,21 +34,21 @@ #include "qapi/qapi-builtin-types.h" -struct QObject { +/* Not for use outside include/qapi/qmp/ */ +struct QObjectBase_ { QType type; size_t refcnt; }; -/* Get the 'base' part of an object */ -#define QOBJECT(obj) (&(obj)->base) +/* this struct must have no other members than base */ +struct QObject { + struct QObjectBase_ base; +}; -/* High-level interface for qobject_incref() */ -#define QINCREF(obj) \ - qobject_incref(QOBJECT(obj)) - -/* High-level interface for qobject_decref() */ -#define QDECREF(obj) \ - qobject_decref(obj ? QOBJECT(obj) : NULL) +#define QOBJECT(obj) ({ \ + typeof(obj) _obj = (obj); \ + _obj ? container_of(&(_obj)->base, QObject, base) : NULL; \ +}) /* Required for qobject_to() */ #define QTYPE_CAST_TO_QNull QTYPE_QNULL @@ -61,25 +61,22 @@ struct QObject { QEMU_BUILD_BUG_MSG(QTYPE__MAX != 7, "The QTYPE_CAST_TO_* list needs to be extended"); -#define qobject_to(type, obj) ({ \ - QObject *_tmp = qobject_check_type(obj, glue(QTYPE_CAST_TO_, type)); \ - _tmp ? container_of(_tmp, type, base) : (type *)NULL; }) +#define qobject_to(type, obj) \ + ((type *)qobject_check_type(obj, glue(QTYPE_CAST_TO_, type))) /* Initialize an object to default values */ static inline void qobject_init(QObject *obj, QType type) { assert(QTYPE_NONE < type && type < QTYPE__MAX); - obj->refcnt = 1; - obj->type = type; + obj->base.refcnt = 1; + obj->base.type = type; } -/** - * qobject_incref(): Increment QObject's reference count - */ -static inline void qobject_incref(QObject *obj) +static inline void qobject_ref_impl(QObject *obj) { - if (obj) - obj->refcnt++; + if (obj) { + obj->base.refcnt++; + } } /** @@ -96,25 +93,39 @@ bool qobject_is_equal(const QObject *x, const QObject *y); */ void qobject_destroy(QObject *obj); -/** - * qobject_decref(): Decrement QObject's reference count, deallocate - * when it reaches zero - */ -static inline void qobject_decref(QObject *obj) +static inline void qobject_unref_impl(QObject *obj) { - assert(!obj || obj->refcnt); - if (obj && --obj->refcnt == 0) { + assert(!obj || obj->base.refcnt); + if (obj && --obj->base.refcnt == 0) { qobject_destroy(obj); } } +/** + * qobject_ref(): Increment QObject's reference count + * + * Returns: the same @obj. The type of @obj will be propagated to the + * return type. + */ +#define qobject_ref(obj) ({ \ + typeof(obj) _o = (obj); \ + qobject_ref_impl(QOBJECT(_o)); \ + _o; \ +}) + +/** + * qobject_unref(): Decrement QObject's reference count, deallocate + * when it reaches zero + */ +#define qobject_unref(obj) qobject_unref_impl(QOBJECT(obj)) + /** * qobject_type(): Return the QObject's type */ static inline QType qobject_type(const QObject *obj) { - assert(QTYPE_NONE < obj->type && obj->type < QTYPE__MAX); - return obj->type; + assert(QTYPE_NONE < obj->base.type && obj->base.type < QTYPE__MAX); + return obj->base.type; } /** diff --git a/include/qapi/qmp/qstring.h b/include/qapi/qmp/qstring.h index 30ae260a7f..3e83e3a95d 100644 --- a/include/qapi/qmp/qstring.h +++ b/include/qapi/qmp/qstring.h @@ -16,7 +16,7 @@ #include "qapi/qmp/qobject.h" struct QString { - QObject base; + struct QObjectBase_ base; char *string; size_t length; size_t capacity; @@ -24,7 +24,7 @@ struct QString { QString *qstring_new(void); QString *qstring_from_str(const char *str); -QString *qstring_from_substr(const char *str, int start, int end); +QString *qstring_from_substr(const char *str, size_t start, size_t end); size_t qstring_get_length(const QString *qstring); const char *qstring_get_str(const QString *qstring); const char *qstring_get_try_str(const QString *qstring); diff --git a/include/qemu-common.h b/include/qemu-common.h index 8a4f63c9de..85f4749aef 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -137,7 +137,7 @@ char *qemu_find_file(int type, const char *name); /* OS specific functions */ void os_setup_early_signal_handling(void); char *os_find_datadir(void); -void os_parse_cmd_args(int index, const char *optarg); +int os_parse_cmd_args(int index, const char *optarg); #include "qemu/module.h" diff --git a/include/qemu-io.h b/include/qemu-io.h index 196fde0f3a..7433239372 100644 --- a/include/qemu-io.h +++ b/include/qemu-io.h @@ -22,7 +22,12 @@ #define CMD_FLAG_GLOBAL ((int)0x80000000) /* don't iterate "args" */ +/* Implement a qemu-io command. + * Operate on @blk using @argc/@argv as the command's arguments, and + * return 0 on success or negative errno on failure. + */ typedef int (*cfunc_t)(BlockBackend *blk, int argc, char **argv); + typedef void (*helpfunc_t)(void); typedef struct cmdinfo { @@ -41,10 +46,10 @@ typedef struct cmdinfo { extern bool qemuio_misalign; -bool qemuio_command(BlockBackend *blk, const char *cmd); +int qemuio_command(BlockBackend *blk, const char *cmd); void qemuio_add_command(const cmdinfo_t *ci); -int qemuio_command_usage(const cmdinfo_t *ci); +void qemuio_command_usage(const cmdinfo_t *ci); void qemuio_complete_command(const char *input, void (*fn)(const char *cmd, void *opaque), void *opaque); diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index d73c9e14d7..9ed39effd3 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -187,7 +187,7 @@ /* Returns the eventual value, failed or not */ #define atomic_cmpxchg__nocheck(ptr, old, new) ({ \ typeof_strip_qual(*ptr) _old = (old); \ - __atomic_compare_exchange_n(ptr, &_old, new, false, \ + (void)__atomic_compare_exchange_n(ptr, &_old, new, false, \ __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); \ _old; \ }) diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 3f28f661b1..a684c1a7a2 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -290,6 +290,15 @@ typedef union { * For accessors that take a guest address rather than a * host address, see the cpu_{ld,st}_* accessors defined in * cpu_ldst.h. + * + * For cases where the size to be used is not fixed at compile time, + * there are + * stn{endian}_p(ptr, sz, val) + * which stores @val to @ptr as an @endian-order number @sz bytes in size + * and + * ldn{endian}_p(ptr, sz) + * which loads @sz bytes from @ptr as an unsigned @endian-order number + * and returns it in a uint64_t. */ static inline int ldub_p(const void *ptr) @@ -495,6 +504,49 @@ static inline unsigned long leul_to_cpu(unsigned long v) #endif } +/* Store v to p as a sz byte value in host order */ +#define DO_STN_LDN_P(END) \ + static inline void stn_## END ## _p(void *ptr, int sz, uint64_t v) \ + { \ + switch (sz) { \ + case 1: \ + stb_p(ptr, v); \ + break; \ + case 2: \ + stw_ ## END ## _p(ptr, v); \ + break; \ + case 4: \ + stl_ ## END ## _p(ptr, v); \ + break; \ + case 8: \ + stq_ ## END ## _p(ptr, v); \ + break; \ + default: \ + g_assert_not_reached(); \ + } \ + } \ + static inline uint64_t ldn_## END ## _p(const void *ptr, int sz) \ + { \ + switch (sz) { \ + case 1: \ + return ldub_p(ptr); \ + case 2: \ + return lduw_ ## END ## _p(ptr); \ + case 4: \ + return (uint32_t)ldl_ ## END ## _p(ptr); \ + case 8: \ + return ldq_ ## END ## _p(ptr); \ + default: \ + g_assert_not_reached(); \ + } \ + } + +DO_STN_LDN_P(he) +DO_STN_LDN_P(le) +DO_STN_LDN_P(be) + +#undef DO_STN_LDN_P + #undef le_bswap #undef be_bswap #undef le_bswaps diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 9f762695d1..5843812710 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -64,6 +64,8 @@ (type *) ((char *) __mptr - offsetof(type, member));}) #endif +#define sizeof_field(type, field) sizeof(((type *)0)->field) + /* Convert from a base type to a parent type, with compile time checking. */ #ifdef __GNUC__ #define DO_UPCAST(type, field, dev) ( __extension__ ( { \ diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index a663340b23..47aaa3b0b9 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -122,6 +122,14 @@ int qemu_strnlen(const char *s, int max_len); * Returns: the pointer originally in @input. */ char *qemu_strsep(char **input, const char *delim); +#ifdef HAVE_STRCHRNUL +static inline const char *qemu_strchrnul(const char *s, int c) +{ + return strchrnul(s, c); +} +#else +const char *qemu_strchrnul(const char *s, int c); +#endif time_t mktimegm(struct tm *tm); int qemu_fdatasync(int fd); int fcntl_setfl(int fd, int flag); @@ -147,13 +155,6 @@ int qemu_strtosz(const char *nptr, char **end, uint64_t *result); int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result); int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result); -#define K_BYTE (1ULL << 10) -#define M_BYTE (1ULL << 20) -#define G_BYTE (1ULL << 30) -#define T_BYTE (1ULL << 40) -#define P_BYTE (1ULL << 50) -#define E_BYTE (1ULL << 60) - /* used to print char* safely */ #define STR_OR_NULL(str) ((str) ? (str) : "null") diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index 6b6490ecad..ddca52c48e 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -324,11 +324,14 @@ void hbitmap_free_meta(HBitmap *hb); /** * hbitmap_iter_next: * @hbi: HBitmapIter to operate on. + * @advance: If true, advance the iterator. Otherwise, the next call + * of this function will return the same result (if that + * position is still dirty). * * Return the next bit that is set in @hbi's associated HBitmap, * or -1 if all remaining bits are zero. */ -int64_t hbitmap_iter_next(HBitmapIter *hbi); +int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance); /** * hbitmap_iter_next_word: diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h new file mode 100644 index 0000000000..b66cf93c4b --- /dev/null +++ b/include/qemu/iova-tree.h @@ -0,0 +1,133 @@ +/* + * An very simplified iova tree implementation based on GTree. + * + * Copyright 2018 Red Hat, Inc. + * + * Authors: + * Peter Xu + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + */ +#ifndef IOVA_TREE_H +#define IOVA_TREE_H + +/* + * Currently the iova tree will only allow to keep ranges + * information, and no extra user data is allowed for each element. A + * benefit is that we can merge adjacent ranges internally within the + * tree. It can save a lot of memory when the ranges are splitted but + * mostly continuous. + * + * Note that current implementation does not provide any thread + * protections. Callers of the iova tree should be responsible + * for the thread safety issue. + */ + +#include "exec/memory.h" +#include "exec/hwaddr.h" + +#define IOVA_OK (0) +#define IOVA_ERR_INVALID (-1) /* Invalid parameters */ +#define IOVA_ERR_OVERLAP (-2) /* IOVA range overlapped */ + +typedef struct IOVATree IOVATree; +typedef struct DMAMap { + hwaddr iova; + hwaddr translated_addr; + hwaddr size; /* Inclusive */ + IOMMUAccessFlags perm; +} QEMU_PACKED DMAMap; +typedef gboolean (*iova_tree_iterator)(DMAMap *map); + +/** + * iova_tree_new: + * + * Create a new iova tree. + * + * Returns: the tree pointer when succeeded, or NULL if error. + */ +IOVATree *iova_tree_new(void); + +/** + * iova_tree_insert: + * + * @tree: the iova tree to insert + * @map: the mapping to insert + * + * Insert an iova range to the tree. If there is overlapped + * ranges, IOVA_ERR_OVERLAP will be returned. + * + * Return: 0 if succeeded, or <0 if error. + */ +int iova_tree_insert(IOVATree *tree, DMAMap *map); + +/** + * iova_tree_remove: + * + * @tree: the iova tree to remove range from + * @map: the map range to remove + * + * Remove mappings from the tree that are covered by the map range + * provided. The range does not need to be exactly what has inserted, + * all the mappings that are included in the provided range will be + * removed from the tree. Here map->translated_addr is meaningless. + * + * Return: 0 if succeeded, or <0 if error. + */ +int iova_tree_remove(IOVATree *tree, DMAMap *map); + +/** + * iova_tree_find: + * + * @tree: the iova tree to search from + * @map: the mapping to search + * + * Search for a mapping in the iova tree that overlaps with the + * mapping range specified. Only the first found mapping will be + * returned. + * + * Return: DMAMap pointer if found, or NULL if not found. Note that + * the returned DMAMap pointer is maintained internally. User should + * only read the content but never modify or free the content. Also, + * user is responsible to make sure the pointer is valid (say, no + * concurrent deletion in progress). + */ +DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map); + +/** + * iova_tree_find_address: + * + * @tree: the iova tree to search from + * @iova: the iova address to find + * + * Similar to iova_tree_find(), but it tries to find mapping with + * range iova=iova & size=0. + * + * Return: same as iova_tree_find(). + */ +DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova); + +/** + * iova_tree_foreach: + * + * @tree: the iova tree to iterate on + * @iterator: the interator for the mappings, return true to stop + * + * Iterate over the iova tree. + * + * Return: 1 if found any overlap, 0 if not, <0 if error. + */ +void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator); + +/** + * iova_tree_destroy: + * + * @tree: the iova tree to destroy + * + * Destroy an existing iova tree. + * + * Return: None. + */ +void iova_tree_destroy(IOVATree *tree); + +#endif diff --git a/include/qemu/job.h b/include/qemu/job.h new file mode 100644 index 0000000000..18c9223e31 --- /dev/null +++ b/include/qemu/job.h @@ -0,0 +1,582 @@ +/* + * Declarations for background jobs + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * + * 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 JOB_H +#define JOB_H + +#include "qapi/qapi-types-block-core.h" +#include "qemu/queue.h" +#include "qemu/coroutine.h" +#include "block/aio.h" + +typedef struct JobDriver JobDriver; +typedef struct JobTxn JobTxn; + + +/** + * Long-running operation. + */ +typedef struct Job { + /** The ID of the job. May be NULL for internal jobs. */ + char *id; + + /** The type of this job. */ + const JobDriver *driver; + + /** Reference count of the block job */ + int refcnt; + + /** Current state; See @JobStatus for details. */ + JobStatus status; + + /** AioContext to run the job coroutine in */ + AioContext *aio_context; + + /** + * The coroutine that executes the job. If not NULL, it is reentered when + * busy is false and the job is cancelled. + */ + Coroutine *co; + + /** + * Timer that is used by @job_sleep_ns. Accessed under job_mutex (in + * job.c). + */ + QEMUTimer sleep_timer; + + /** + * Counter for pause request. If non-zero, the block job is either paused, + * or if busy == true will pause itself as soon as possible. + */ + int pause_count; + + /** + * Set to false by the job while the coroutine has yielded and may be + * re-entered by job_enter(). There may still be I/O or event loop activity + * pending. Accessed under block_job_mutex (in blockjob.c). + */ + bool busy; + + /** + * Set to true by the job while it is in a quiescent state, where + * no I/O or event loop activity is pending. + */ + bool paused; + + /** + * Set to true if the job is paused by user. Can be unpaused with the + * block-job-resume QMP command. + */ + bool user_paused; + + /** + * Set to true if the job should cancel itself. The flag must + * always be tested just before toggling the busy flag from false + * to true. After a job has been cancelled, it should only yield + * if #aio_poll will ("sooner or later") reenter the coroutine. + */ + bool cancelled; + + /** + * Set to true if the job should abort immediately without waiting + * for data to be in sync. + */ + bool force_cancel; + + /** Set to true when the job has deferred work to the main loop. */ + bool deferred_to_main_loop; + + /** True if this job should automatically finalize itself */ + bool auto_finalize; + + /** True if this job should automatically dismiss itself */ + bool auto_dismiss; + + /** + * Current progress. The unit is arbitrary as long as the ratio between + * progress_current and progress_total represents the estimated percentage + * of work already done. + */ + int64_t progress_current; + + /** Estimated progress_current value at the completion of the job */ + int64_t progress_total; + + /** Error string for a failed job (NULL if, and only if, job->ret == 0) */ + char *error; + + /** ret code passed to job_completed. */ + int ret; + + /** The completion function that will be called when the job completes. */ + BlockCompletionFunc *cb; + + /** The opaque value that is passed to the completion function. */ + void *opaque; + + /** Notifiers called when a cancelled job is finalised */ + NotifierList on_finalize_cancelled; + + /** Notifiers called when a successfully completed job is finalised */ + NotifierList on_finalize_completed; + + /** Notifiers called when the job transitions to PENDING */ + NotifierList on_pending; + + /** Notifiers called when the job transitions to READY */ + NotifierList on_ready; + + /** Element of the list of jobs */ + QLIST_ENTRY(Job) job_list; + + /** Transaction this job is part of */ + JobTxn *txn; + + /** Element of the list of jobs in a job transaction */ + QLIST_ENTRY(Job) txn_list; +} Job; + +/** + * Callbacks and other information about a Job driver. + */ +struct JobDriver { + /** Derived Job struct size */ + size_t instance_size; + + /** Enum describing the operation */ + JobType job_type; + + /** Mandatory: Entrypoint for the Coroutine. */ + CoroutineEntry *start; + + /** + * If the callback is not NULL, it will be invoked when the job transitions + * into the paused state. Paused jobs must not perform any asynchronous + * I/O or event loop activity. This callback is used to quiesce jobs. + */ + void coroutine_fn (*pause)(Job *job); + + /** + * If the callback is not NULL, it will be invoked when the job transitions + * out of the paused state. Any asynchronous I/O or event loop activity + * should be restarted from this callback. + */ + void coroutine_fn (*resume)(Job *job); + + /** + * Called when the job is resumed by the user (i.e. user_paused becomes + * false). .user_resume is called before .resume. + */ + void (*user_resume)(Job *job); + + /** + * Optional callback for job types whose completion must be triggered + * manually. + */ + void (*complete)(Job *job, Error **errp); + + /* + * If the callback is not NULL, it will be invoked when the job has to be + * synchronously cancelled or completed; it should drain any activities + * as required to ensure progress. + */ + void (*drain)(Job *job); + + /** + * If the callback is not NULL, prepare will be invoked when all the jobs + * belonging to the same transaction complete; or upon this job's completion + * if it is not in a transaction. + * + * This callback will not be invoked if the job has already failed. + * If it fails, abort and then clean will be called. + */ + int (*prepare)(Job *job); + + /** + * If the callback is not NULL, it will be invoked when all the jobs + * belonging to the same transaction complete; or upon this job's + * completion if it is not in a transaction. Skipped if NULL. + * + * All jobs will complete with a call to either .commit() or .abort() but + * never both. + */ + void (*commit)(Job *job); + + /** + * If the callback is not NULL, it will be invoked when any job in the + * same transaction fails; or upon this job's failure (due to error or + * cancellation) if it is not in a transaction. Skipped if NULL. + * + * All jobs will complete with a call to either .commit() or .abort() but + * never both. + */ + void (*abort)(Job *job); + + /** + * If the callback is not NULL, it will be invoked after a call to either + * .commit() or .abort(). Regardless of which callback is invoked after + * completion, .clean() will always be called, even if the job does not + * belong to a transaction group. + */ + void (*clean)(Job *job); + + + /** Called when the job is freed */ + void (*free)(Job *job); +}; + +typedef enum JobCreateFlags { + /* Default behavior */ + JOB_DEFAULT = 0x00, + /* Job is not QMP-created and should not send QMP events */ + JOB_INTERNAL = 0x01, + /* Job requires manual finalize step */ + JOB_MANUAL_FINALIZE = 0x02, + /* Job requires manual dismiss step */ + JOB_MANUAL_DISMISS = 0x04, +} JobCreateFlags; + +/** + * Allocate and return a new job transaction. Jobs can be added to the + * transaction using job_txn_add_job(). + * + * The transaction is automatically freed when the last job completes or is + * cancelled. + * + * All jobs in the transaction either complete successfully or fail/cancel as a + * group. Jobs wait for each other before completing. Cancelling one job + * cancels all jobs in the transaction. + */ +JobTxn *job_txn_new(void); + +/** + * Release a reference that was previously acquired with job_txn_add_job or + * job_txn_new. If it's the last reference to the object, it will be freed. + */ +void job_txn_unref(JobTxn *txn); + +/** + * @txn: The transaction (may be NULL) + * @job: Job to add to the transaction + * + * Add @job to the transaction. The @job must not already be in a transaction. + * The caller must call either job_txn_unref() or job_completed() to release + * the reference that is automatically grabbed here. + * + * If @txn is NULL, the function does nothing. + */ +void job_txn_add_job(JobTxn *txn, Job *job); + +/** + * Create a new long-running job and return it. + * + * @job_id: The id of the newly-created job, or %NULL for internal jobs + * @driver: The class object for the newly-created job. + * @txn: The transaction this job belongs to, if any. %NULL otherwise. + * @ctx: The AioContext to run the job coroutine in. + * @flags: Creation flags for the job. See @JobCreateFlags. + * @cb: Completion function for the job. + * @opaque: Opaque pointer value passed to @cb. + * @errp: Error object. + */ +void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, + AioContext *ctx, int flags, BlockCompletionFunc *cb, + void *opaque, Error **errp); + +/** + * Add a reference to Job refcnt, it will be decreased with job_unref, and then + * be freed if it comes to be the last reference. + */ +void job_ref(Job *job); + +/** + * Release a reference that was previously acquired with job_ref() or + * job_create(). If it's the last reference to the object, it will be freed. + */ +void job_unref(Job *job); + +/** + * @job: The job that has made progress + * @done: How much progress the job made since the last call + * + * Updates the progress counter of the job. + */ +void job_progress_update(Job *job, uint64_t done); + +/** + * @job: The job whose expected progress end value is set + * @remaining: Missing progress (on top of the current progress counter value) + * until the new expected end value is reached + * + * Sets the expected end value of the progress counter of a job so that a + * completion percentage can be calculated when the progress is updated. + */ +void job_progress_set_remaining(Job *job, uint64_t remaining); + +/** + * @job: The job whose expected progress end value is updated + * @delta: Value which is to be added to the current expected end + * value + * + * Increases the expected end value of the progress counter of a job. + * This is useful for parenthesis operations: If a job has to + * conditionally perform a high-priority operation as part of its + * progress, it calls this function with the expected operation's + * length before, and job_progress_update() afterwards. + * (So the operation acts as a parenthesis in regards to the main job + * operation running in background.) + */ +void job_progress_increase_remaining(Job *job, uint64_t delta); + +/** To be called when a cancelled job is finalised. */ +void job_event_cancelled(Job *job); + +/** To be called when a successfully completed job is finalised. */ +void job_event_completed(Job *job); + +/** + * Conditionally enter the job coroutine if the job is ready to run, not + * already busy and fn() returns true. fn() is called while under the job_lock + * critical section. + */ +void job_enter_cond(Job *job, bool(*fn)(Job *job)); + +/** + * @job: A job that has not yet been started. + * + * Begins execution of a job. + * Takes ownership of one reference to the job object. + */ +void job_start(Job *job); + +/** + * @job: The job to enter. + * + * Continue the specified job by entering the coroutine. + */ +void job_enter(Job *job); + +/** + * @job: The job that is ready to pause. + * + * Pause now if job_pause() has been called. Jobs that perform lots of I/O + * must call this between requests so that the job can be paused. + */ +void coroutine_fn job_pause_point(Job *job); + +/** + * @job: The job that calls the function. + * + * Yield the job coroutine. + */ +void job_yield(Job *job); + +/** + * @job: The job that calls the function. + * @ns: How many nanoseconds to stop for. + * + * Put the job to sleep (assuming that it wasn't canceled) for @ns + * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately + * interrupt the wait. + */ +void coroutine_fn job_sleep_ns(Job *job, int64_t ns); + + +/** Returns the JobType of a given Job. */ +JobType job_type(const Job *job); + +/** Returns the enum string for the JobType of a given Job. */ +const char *job_type_str(const Job *job); + +/** Returns true if the job should not be visible to the management layer. */ +bool job_is_internal(Job *job); + +/** Returns whether the job is scheduled for cancellation. */ +bool job_is_cancelled(Job *job); + +/** Returns whether the job is in a completed state. */ +bool job_is_completed(Job *job); + +/** Returns whether the job is ready to be completed. */ +bool job_is_ready(Job *job); + +/** + * Request @job to pause at the next pause point. Must be paired with + * job_resume(). If the job is supposed to be resumed by user action, call + * job_user_pause() instead. + */ +void job_pause(Job *job); + +/** Resumes a @job paused with job_pause. */ +void job_resume(Job *job); + +/** + * Asynchronously pause the specified @job. + * Do not allow a resume until a matching call to job_user_resume. + */ +void job_user_pause(Job *job, Error **errp); + +/** Returns true if the job is user-paused. */ +bool job_user_paused(Job *job); + +/** + * Resume the specified @job. + * Must be paired with a preceding job_user_pause. + */ +void job_user_resume(Job *job, Error **errp); + +/* + * Drain any activities as required to ensure progress. This can be called in a + * loop to synchronously complete a job. + */ +void job_drain(Job *job); + +/** + * Get the next element from the list of block jobs after @job, or the + * first one if @job is %NULL. + * + * Returns the requested job, or %NULL if there are no more jobs left. + */ +Job *job_next(Job *job); + +/** + * Get the job identified by @id (which must not be %NULL). + * + * Returns the requested job, or %NULL if it doesn't exist. + */ +Job *job_get(const char *id); + +/** + * Check whether the verb @verb can be applied to @job in its current state. + * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM + * returned. + */ +int job_apply_verb(Job *job, JobVerb verb, Error **errp); + +/** The @job could not be started, free it. */ +void job_early_fail(Job *job); + +/** Moves the @job from RUNNING to READY */ +void job_transition_to_ready(Job *job); + +/** + * @job: The job being completed. + * @ret: The status code. + * @error: The error message for a failing job (only with @ret < 0). If @ret is + * negative, but NULL is given for @error, strerror() is used. + * + * Marks @job as completed. If @ret is non-zero, the job transaction it is part + * of is aborted. If @ret is zero, the job moves into the WAITING state. If it + * is the last job to complete in its transaction, all jobs in the transaction + * move from WAITING to PENDING. + */ +void job_completed(Job *job, int ret, Error *error); + +/** Asynchronously complete the specified @job. */ +void job_complete(Job *job, Error **errp); + +/** + * Asynchronously cancel the specified @job. If @force is true, the job should + * be cancelled immediately without waiting for a consistent state. + */ +void job_cancel(Job *job, bool force); + +/** + * Cancels the specified job like job_cancel(), but may refuse to do so if the + * operation isn't meaningful in the current state of the job. + */ +void job_user_cancel(Job *job, bool force, Error **errp); + +/** + * Synchronously cancel the @job. The completion callback is called + * before the function returns. The job may actually complete + * instead of canceling itself; the circumstances under which this + * happens depend on the kind of job that is active. + * + * Returns the return value from the job if the job actually completed + * during the call, or -ECANCELED if it was canceled. + */ +int job_cancel_sync(Job *job); + +/** Synchronously cancels all jobs using job_cancel_sync(). */ +void job_cancel_sync_all(void); + +/** + * @job: The job to be completed. + * @errp: Error object which may be set by job_complete(); this is not + * necessarily set on every error, the job return value has to be + * checked as well. + * + * Synchronously complete the job. The completion callback is called before the + * function returns, unless it is NULL (which is permissible when using this + * function). + * + * Returns the return value from the job. + */ +int job_complete_sync(Job *job, Error **errp); + +/** + * For a @job that has finished its work and is pending awaiting explicit + * acknowledgement to commit its work, this will commit that work. + * + * FIXME: Make the below statement universally true: + * For jobs that support the manual workflow mode, all graph changes that occur + * as a result will occur after this command and before a successful reply. + */ +void job_finalize(Job *job, Error **errp); + +/** + * Remove the concluded @job from the query list and resets the passed pointer + * to %NULL. Returns an error if the job is not actually concluded. + */ +void job_dismiss(Job **job, Error **errp); + +typedef void JobDeferToMainLoopFn(Job *job, void *opaque); + +/** + * @job: The job + * @fn: The function to run in the main loop + * @opaque: The opaque value that is passed to @fn + * + * This function must be called by the main job coroutine just before it + * returns. @fn is executed in the main loop with the job AioContext acquired. + * + * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses + * bdrv_drain_all() in the main loop. + * + * The @job AioContext is held while @fn executes. + */ +void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); + +/** + * Synchronously finishes the given @job. If @finish is given, it is called to + * trigger completion or cancellation of the job. + * + * Returns 0 if the job is successfully completed, -ECANCELED if the job was + * cancelled before completing, and -errno in other error cases. + */ +int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp); + +#endif diff --git a/include/qemu/log.h b/include/qemu/log.h index ff92a8b86a..b097a6cae1 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -44,6 +44,7 @@ static inline bool qemu_log_separate(void) #define CPU_LOG_PAGE (1 << 14) /* LOG_TRACE (1 << 15) is defined in log-for-trace.h */ #define CPU_LOG_TB_OP_IND (1 << 16) +#define CPU_LOG_TB_FPU (1 << 17) /* Lock output for a series of related logs. Since this is not needed * for a single qemu_log / qemu_log_mask / qemu_log_mask_and_addr, we diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 6b4b60bf6d..721aa2416a 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -168,6 +168,20 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); /* async I/O support */ typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); + +/** + * IOCanReadHandler: Return the number of bytes that #IOReadHandler can accept + * + * This function reports how many bytes #IOReadHandler is prepared to accept. + * #IOReadHandler may be invoked with up to this number of bytes. If this + * function returns 0 then #IOReadHandler is not invoked. + * + * This function is typically called from an event loop. If the number of + * bytes changes outside the event loop (e.g. because a vcpu thread drained the + * buffer), then it is necessary to kick the event loop so that this function + * is called again. aio_notify() or qemu_notify_event() can be used to kick + * the event loop. + */ typedef int IOCanReadHandler(void *opaque); /** diff --git a/include/qemu/option.h b/include/qemu/option.h index 306fdb5f7a..3dfb4493cc 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -28,8 +28,7 @@ #include "qemu/queue.h" -const char *get_opt_name(char *buf, int buf_size, const char *p, char delim); -const char *get_opt_value(char *buf, int buf_size, const char *p); +const char *get_opt_value(const char *p, char **value); void parse_option_size(const char *name, const char *value, uint64_t *ret, Error **errp); diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 41658060a7..a91068df0e 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -33,6 +33,21 @@ #else #include "exec/poison.h" #endif +#ifdef __COVERITY__ +/* Coverity does not like the new _Float* types that are used by + * recent glibc, and croaks on every single file that includes + * stdlib.h. These typedefs are enough to please it. + * + * Note that these fix parse errors so they cannot be placed in + * scripts/coverity-model.c. + */ +typedef float _Float32; +typedef double _Float32x; +typedef double _Float64; +typedef __float80 _Float64x; +typedef __float128 _Float128; +#endif + #include "qemu/compiler.h" /* Older versions of C++ don't get definitions of various macros from @@ -107,6 +122,16 @@ extern int daemon(int, int); #include "glib-compat.h" #include "qemu/typedefs.h" +/* + * According to waitpid man page: + * WCOREDUMP + * This macro is not specified in POSIX.1-2001 and is not + * available on some UNIX implementations (e.g., AIX, SunOS). + * Therefore, enclose its use inside #ifdef WCOREDUMP ... #endif. + */ +#ifndef WCOREDUMP +#define WCOREDUMP(status) 0 +#endif /* * We have a lot of unaudited code that may fail in strange ways, or * even be a security risk during migration, if you disable assertions @@ -357,7 +382,8 @@ void qemu_anon_ram_free(void *ptr, size_t size); #endif #if defined(__linux__) && \ - (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)) + (defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) \ + || defined(__powerpc64__)) /* Use 2 MiB alignment so transparent hugepages can be used by KVM. Valgrind does not support alignments larger than 1 MiB, therefore we need special code which handles running on Valgrind. */ diff --git a/include/qemu/qht.h b/include/qemu/qht.h index 531aa95325..1fb9116fa0 100644 --- a/include/qemu/qht.h +++ b/include/qemu/qht.h @@ -11,8 +11,11 @@ #include "qemu/thread.h" #include "qemu/qdist.h" +typedef bool (*qht_cmp_func_t)(const void *a, const void *b); + struct qht { struct qht_map *map; + qht_cmp_func_t cmp; QemuMutex lock; /* serializes setters of ht->map */ unsigned int mode; }; @@ -47,10 +50,12 @@ typedef void (*qht_iter_func_t)(struct qht *ht, void *p, uint32_t h, void *up); /** * qht_init - Initialize a QHT * @ht: QHT to be initialized + * @cmp: default comparison function. Cannot be NULL. * @n_elems: number of entries the hash table should be optimized for. * @mode: bitmask with OR'ed QHT_MODE_* */ -void qht_init(struct qht *ht, size_t n_elems, unsigned int mode); +void qht_init(struct qht *ht, qht_cmp_func_t cmp, size_t n_elems, + unsigned int mode); /** * qht_destroy - destroy a previously initialized QHT @@ -65,6 +70,7 @@ void qht_destroy(struct qht *ht); * @ht: QHT to insert to * @p: pointer to be inserted * @hash: hash corresponding to @p + * @existing: address where the pointer to an existing entry can be copied to * * Attempting to insert a NULL @p is a bug. * Inserting the same pointer @p with different @hash values is a bug. @@ -73,16 +79,18 @@ void qht_destroy(struct qht *ht); * inserted into the hash table. * * Returns true on success. - * Returns false if the @p-@hash pair already exists in the hash table. + * Returns false if there is an existing entry in the table that is equivalent + * (i.e. ht->cmp matches and the hash is the same) to @p-@h. If @existing + * is !NULL, a pointer to this existing entry is copied to it. */ -bool qht_insert(struct qht *ht, void *p, uint32_t hash); +bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing); /** - * qht_lookup - Look up a pointer in a QHT + * qht_lookup_custom - Look up a pointer using a custom comparison function. * @ht: QHT to be looked up - * @func: function to compare existing pointers against @userp * @userp: pointer to pass to @func * @hash: hash of the pointer to be looked up + * @func: function to compare existing pointers against @userp * * Needs to be called under an RCU read-critical section. * @@ -94,8 +102,18 @@ bool qht_insert(struct qht *ht, void *p, uint32_t hash); * Returns the corresponding pointer when a match is found. * Returns NULL otherwise. */ -void *qht_lookup(struct qht *ht, qht_lookup_func_t func, const void *userp, - uint32_t hash); +void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash, + qht_lookup_func_t func); + +/** + * qht_lookup - Look up a pointer in a QHT + * @ht: QHT to be looked up + * @userp: pointer to pass to the comparison function + * @hash: hash of the pointer to be looked up + * + * Calls qht_lookup_custom() using @ht's default comparison function. + */ +void *qht_lookup(struct qht *ht, const void *userp, uint32_t hash); /** * qht_remove - remove a pointer from the hash table diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index f3f47e426f..fd27b34128 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -12,6 +12,10 @@ typedef QemuMutex QemuRecMutex; struct QemuMutex { pthread_mutex_t lock; +#ifdef CONFIG_DEBUG_MUTEX + const char *file; + int line; +#endif bool initialized; }; diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h index 3a05e3b3aa..d668d789b4 100644 --- a/include/qemu/thread-win32.h +++ b/include/qemu/thread-win32.h @@ -5,6 +5,10 @@ struct QemuMutex { SRWLOCK lock; +#ifdef CONFIG_DEBUG_MUTEX + const char *file; + int line; +#endif bool initialized; }; diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index a46b0b347b..3ec0e13a96 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -62,7 +62,6 @@ typedef struct NetClientState NetClientState; typedef struct NetFilterState NetFilterState; typedef struct NICInfo NICInfo; typedef struct NumaNodeMem NumaNodeMem; -typedef struct PcGuestInfo PcGuestInfo; typedef struct PCIBridge PCIBridge; typedef struct PCIBus PCIBus; typedef struct PCIDevice PCIDevice; @@ -98,6 +97,7 @@ typedef struct QEMUTimer QEMUTimer; typedef struct QEMUTimerListGroup QEMUTimerListGroup; typedef struct QBool QBool; typedef struct QDict QDict; +typedef struct QJSON QJSON; typedef struct QList QList; typedef struct QNull QNull; typedef struct QNum QNum; diff --git a/include/qemu/units.h b/include/qemu/units.h new file mode 100644 index 0000000000..692db3fbb2 --- /dev/null +++ b/include/qemu/units.h @@ -0,0 +1,20 @@ +/* + * IEC binary prefixes definitions + * + * Copyright (C) 2015 Nikunj A Dadhania, IBM Corporation + * Copyright (C) 2018 Philippe Mathieu-Daudé + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_UNITS_H +#define QEMU_UNITS_H + +#define KiB (INT64_C(1) << 10) +#define MiB (INT64_C(1) << 20) +#define GiB (INT64_C(1) << 30) +#define TiB (INT64_C(1) << 40) +#define PiB (INT64_C(1) << 50) +#define EiB (INT64_C(1) << 60) + +#endif diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 14e45c4282..bd796579ee 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -132,6 +132,9 @@ struct TranslationBlock; * before the insn which triggers a watchpoint rather than after it. * @gdb_arch_name: Optional callback that returns the architecture name known * to GDB. The caller must free the returned string with g_free. + * @gdb_get_dynamic_xml: Callback to return dynamically generated XML for the + * gdb stub. Returns a pointer to the XML contents for the specified XML file + * or NULL if the CPU doesn't have a dynamically generated content for it. * @cpu_exec_enter: Callback for cpu_exec preparation. * @cpu_exec_exit: Callback for cpu_exec cleanup. * @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec. @@ -198,7 +201,7 @@ typedef struct CPUClass { const struct VMStateDescription *vmsd; const char *gdb_core_xml_file; gchar * (*gdb_arch_name)(CPUState *cpu); - + const char * (*gdb_get_dynamic_xml)(CPUState *cpu, const char *xmlname); void (*cpu_exec_enter)(CPUState *cpu); void (*cpu_exec_exit)(CPUState *cpu); bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); @@ -426,6 +429,9 @@ struct CPUState { uint16_t pending_tlb_flush; int hvf_fd; + + /* track IOMMUs whose translations we've cached in the TCG TLB */ + GArray *iommu_notifiers; }; QTAILQ_HEAD(CPUTailQ, CPUState); @@ -614,11 +620,13 @@ static inline hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr) static inline int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs) { CPUClass *cc = CPU_GET_CLASS(cpu); + int ret = 0; if (cc->asidx_from_attrs) { - return cc->asidx_from_attrs(cpu, attrs); + ret = cc->asidx_from_attrs(cpu, attrs); + assert(ret < cpu->num_ases && ret >= 0); } - return 0; + return ret; } #endif diff --git a/include/qom/object.h b/include/qom/object.h index 96ce81bc5e..f0b0bf39cc 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -748,6 +748,47 @@ int object_set_propv(Object *obj, */ void object_initialize(void *obj, size_t size, const char *typename); +/** + * object_initialize_child: + * @parentobj: The parent object to add a property to + * @propname: The name of the property + * @childobj: A pointer to the memory to be used for the object. + * @size: The maximum size available at @childobj for the object. + * @type: The name of the type of the object to instantiate. + * @errp: If an error occurs, a pointer to an area to store the error + * @...: list of property names and values + * + * This function will initialize an object. The memory for the object should + * have already been allocated. The object will then be added as child property + * to a parent with object_property_add_child() function. The returned object + * has a reference count of 1 (for the "child<...>" property from the parent), + * so the object will be finalized automatically when the parent gets removed. + * + * The variadic parameters are a list of pairs of (propname, propvalue) + * strings. The propname of %NULL indicates the end of the property list. + * If the object implements the user creatable interface, the object will + * be marked complete once all the properties have been processed. + */ +void object_initialize_child(Object *parentobj, const char *propname, + void *childobj, size_t size, const char *type, + Error **errp, ...) QEMU_SENTINEL; + +/** + * object_initialize_childv: + * @parentobj: The parent object to add a property to + * @propname: The name of the property + * @childobj: A pointer to the memory to be used for the object. + * @size: The maximum size available at @childobj for the object. + * @type: The name of the type of the object to instantiate. + * @errp: If an error occurs, a pointer to an area to store the error + * @vargs: list of property names and values + * + * See object_initialize_child() for documentation. + */ +void object_initialize_childv(Object *parentobj, const char *propname, + void *childobj, size_t size, const char *type, + Error **errp, va_list vargs); + /** * object_dynamic_cast: * @obj: The object to cast. @@ -1103,6 +1144,11 @@ char *object_property_get_str(Object *obj, const char *name, * @errp: returns an error if this function fails * * Writes an object's canonical path to a property. + * + * If the link property was created with + * OBJ_PROP_LINK_STRONG bit, the old target object is + * unreferenced, and a reference is added to the new target object. + * */ void object_property_set_link(Object *obj, Object *value, const char *name, Error **errp); @@ -1302,6 +1348,7 @@ Object *object_get_internal_root(void); * * Returns: The final component in the object's canonical path. The canonical * path is the path within the composition tree starting from the root. + * %NULL if the object doesn't have a parent (and thus a canonical path). */ gchar *object_get_canonical_path_component(Object *obj); @@ -1376,7 +1423,7 @@ Object *object_resolve_path_component(Object *parent, const gchar *part); * @obj: the object to add a property to * @name: the name of the property * @child: the child object - * @errp: if an error occurs, a pointer to an area to store the area + * @errp: if an error occurs, a pointer to an area to store the error * * Child properties form the composition tree. All objects need to be a child * of another object. Objects can only be a child of one object. @@ -1393,7 +1440,7 @@ void object_property_add_child(Object *obj, const char *name, typedef enum { /* Unref the link pointer when the property is deleted */ - OBJ_PROP_LINK_UNREF_ON_RELEASE = 0x1, + OBJ_PROP_LINK_STRONG = 0x1, } ObjectPropertyLinkFlags; /** @@ -1414,7 +1461,7 @@ void object_property_allow_set_link(const Object *, const char *, * @child: a pointer to where the link object reference is stored * @check: callback to veto setting or NULL if the property is read-only * @flags: additional options for the link - * @errp: if an error occurs, a pointer to an area to store the area + * @errp: if an error occurs, a pointer to an area to store the error * * Links establish relationships between objects. Links are unidirectional * although two links can be combined to form a bidirectional relationship @@ -1431,8 +1478,9 @@ void object_property_allow_set_link(const Object *, const char *, * link property. The reference count for *@child is * managed by the property from after the function returns till the * property is deleted with object_property_del(). If the - * @flags OBJ_PROP_LINK_UNREF_ON_RELEASE bit is set, - * the reference count is decremented when the property is deleted. + * @flags OBJ_PROP_LINK_STRONG bit is set, + * the reference count is decremented when the property is deleted or + * modified. */ void object_property_add_link(Object *obj, const char *name, const char *type, Object **child, diff --git a/include/scsi/constants.h b/include/scsi/constants.h index a141dd71f8..083a8e887a 100644 --- a/include/scsi/constants.h +++ b/include/scsi/constants.h @@ -311,4 +311,8 @@ #define MMC_PROFILE_HDDVD_RW_DL 0x005A #define MMC_PROFILE_INVALID 0xFFFF +#define XCOPY_DESC_OFFSET 16 +#define IDENT_DESCR_TGT_DESCR_SIZE 32 +#define XCOPY_BLK2BLK_SEG_DESC_SIZE 28 + #endif diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h index 5d2f13a5e4..50a77b08fc 100644 --- a/include/scsi/pr-manager.h +++ b/include/scsi/pr-manager.h @@ -33,23 +33,16 @@ typedef struct PRManagerClass { /* */ int (*run)(PRManager *pr_mgr, int fd, struct sg_io_hdr *hdr); + bool (*is_connected)(PRManager *pr_mgr); } PRManagerClass; +bool pr_manager_is_connected(PRManager *pr_mgr); BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, AioContext *ctx, int fd, struct sg_io_hdr *hdr, BlockCompletionFunc *complete, void *opaque); -#ifdef CONFIG_LINUX PRManager *pr_manager_lookup(const char *id, Error **errp); -#else -static inline PRManager *pr_manager_lookup(const char *id, Error **errp) -{ - /* The classes do not exist at all! */ - error_setg(errp, "No persistent reservation manager with id '%s'", id); - return NULL; -} -#endif #endif diff --git a/include/standard-headers/asm-x86/hyperv.h b/include/standard-headers/asm-x86/hyperv.h deleted file mode 100644 index ce87d0c344..0000000000 --- a/include/standard-headers/asm-x86/hyperv.h +++ /dev/null @@ -1 +0,0 @@ - /* this is a temporary placeholder until kvm_para.h stops including it */ diff --git a/linux-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h similarity index 80% rename from linux-headers/asm-x86/kvm_para.h rename to include/standard-headers/asm-x86/kvm_para.h index 4c58184395..1617c84b0d 100644 --- a/linux-headers/asm-x86/kvm_para.h +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -2,16 +2,17 @@ #ifndef _ASM_X86_KVM_PARA_H #define _ASM_X86_KVM_PARA_H -#include -#include +#include "standard-headers/linux/types.h" /* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It * should be used to determine that a VM is running under KVM. */ #define KVM_CPUID_SIGNATURE 0x40000000 -/* This CPUID returns a feature bitmap in eax. Before enabling a particular - * paravirtualization, the appropriate feature bit should be checked. +/* This CPUID returns two feature bitmaps in eax, edx. Before enabling + * a particular paravirtualization, the appropriate feature bit should + * be checked in eax. The performance hint feature bit should be checked + * in edx. */ #define KVM_CPUID_FEATURES 0x40000001 #define KVM_FEATURE_CLOCKSOURCE 0 @@ -28,6 +29,8 @@ #define KVM_FEATURE_PV_TLB_FLUSH 9 #define KVM_FEATURE_ASYNC_PF_VMEXIT 10 +#define KVM_HINTS_REALTIME 0 + /* The last 8 bits are used to indicate how to interpret the flags field * in pvclock structure. If no bits are set, all flags are ignored. */ @@ -45,12 +48,12 @@ #define MSR_KVM_PV_EOI_EN 0x4b564d04 struct kvm_steal_time { - __u64 steal; - __u32 version; - __u32 flags; - __u8 preempted; - __u8 u8_pad[3]; - __u32 pad[11]; + uint64_t steal; + uint32_t version; + uint32_t flags; + uint8_t preempted; + uint8_t uint8_t_pad[3]; + uint32_t pad[11]; }; #define KVM_VCPU_PREEMPTED (1 << 0) @@ -58,11 +61,11 @@ struct kvm_steal_time { #define KVM_CLOCK_PAIRING_WALLCLOCK 0 struct kvm_clock_pairing { - __s64 sec; - __s64 nsec; - __u64 tsc; - __u32 flags; - __u32 pad[9]; + int64_t sec; + int64_t nsec; + uint64_t tsc; + uint32_t flags; + uint32_t pad[9]; }; #define KVM_STEAL_ALIGNMENT_BITS 5 @@ -82,14 +85,14 @@ struct kvm_clock_pairing { /* Payload for KVM_HC_MMU_OP */ struct kvm_mmu_op_header { - __u32 op; - __u32 pad; + uint32_t op; + uint32_t pad; }; struct kvm_mmu_op_write_pte { struct kvm_mmu_op_header header; - __u64 pte_phys; - __u64 pte_val; + uint64_t pte_phys; + uint64_t pte_val; }; struct kvm_mmu_op_flush_tlb { @@ -98,16 +101,16 @@ struct kvm_mmu_op_flush_tlb { struct kvm_mmu_op_release_pt { struct kvm_mmu_op_header header; - __u64 pt_phys; + uint64_t pt_phys; }; #define KVM_PV_REASON_PAGE_NOT_PRESENT 1 #define KVM_PV_REASON_PAGE_READY 2 struct kvm_vcpu_pv_apf_data { - __u32 reason; - __u8 pad[60]; - __u32 enabled; + uint32_t reason; + uint8_t pad[60]; + uint32_t enabled; }; #define KVM_PV_EOI_BIT 0 diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 94aacb7adf..eb10c075e4 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -217,10 +217,14 @@ struct ethtool_value { uint32_t data; }; +#define PFC_STORM_PREVENTION_AUTO 0xffff +#define PFC_STORM_PREVENTION_DISABLE 0 + enum tunable_id { ETHTOOL_ID_UNSPEC, ETHTOOL_RX_COPYBREAK, ETHTOOL_TX_COPYBREAK, + ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */ /* * Add your fresh new tubale attribute above and remember to update * tunable_strings[] in net/core/ethtool.c @@ -914,12 +918,15 @@ static inline uint64_t ethtool_get_flow_spec_ring_vf(uint64_t ring_cookie) * @flow_type: Type of flow to be affected, e.g. %TCP_V4_FLOW * @data: Command-dependent value * @fs: Flow classification rule + * @rss_context: RSS context to be affected * @rule_cnt: Number of rules to be affected * @rule_locs: Array of used rule locations * * For %ETHTOOL_GRXFH and %ETHTOOL_SRXFH, @data is a bitmask indicating * the fields included in the flow hash, e.g. %RXH_IP_SRC. The following - * structure fields must not be used. + * structure fields must not be used, except that if @flow_type includes + * the %FLOW_RSS flag, then @rss_context determines which RSS context to + * act on. * * For %ETHTOOL_GRXRINGS, @data is set to the number of RX rings/queues * on return. @@ -931,7 +938,9 @@ static inline uint64_t ethtool_get_flow_spec_ring_vf(uint64_t ring_cookie) * set in @data then special location values should not be used. * * For %ETHTOOL_GRXCLSRULE, @fs.@location specifies the location of an - * existing rule on entry and @fs contains the rule on return. + * existing rule on entry and @fs contains the rule on return; if + * @fs.@flow_type includes the %FLOW_RSS flag, then @rss_context is + * filled with the RSS context ID associated with the rule. * * For %ETHTOOL_GRXCLSRLALL, @rule_cnt specifies the array size of the * user buffer for @rule_locs on entry. On return, @data is the size @@ -942,7 +951,11 @@ static inline uint64_t ethtool_get_flow_spec_ring_vf(uint64_t ring_cookie) * For %ETHTOOL_SRXCLSRLINS, @fs specifies the rule to add or update. * @fs.@location either specifies the location to use or is a special * location value with %RX_CLS_LOC_SPECIAL flag set. On return, - * @fs.@location is the actual rule location. + * @fs.@location is the actual rule location. If @fs.@flow_type + * includes the %FLOW_RSS flag, @rss_context is the RSS context ID to + * use for flow spreading traffic which matches this rule. The value + * from the rxfh indirection table will be added to @fs.@ring_cookie + * to choose which ring to deliver to. * * For %ETHTOOL_SRXCLSRLDEL, @fs.@location specifies the location of an * existing rule on entry. @@ -963,7 +976,10 @@ struct ethtool_rxnfc { uint32_t flow_type; uint64_t data; struct ethtool_rx_flow_spec fs; - uint32_t rule_cnt; + union { + uint32_t rule_cnt; + uint32_t rss_context; + }; uint32_t rule_locs[0]; }; @@ -990,7 +1006,11 @@ struct ethtool_rxfh_indir { /** * struct ethtool_rxfh - command to get/set RX flow hash indir or/and hash key. * @cmd: Specific command number - %ETHTOOL_GRSSH or %ETHTOOL_SRSSH - * @rss_context: RSS context identifier. + * @rss_context: RSS context identifier. Context 0 is the default for normal + * traffic; other contexts can be referenced as the destination for RX flow + * classification rules. %ETH_RXFH_CONTEXT_ALLOC is used with command + * %ETHTOOL_SRSSH to allocate a new RSS context; on return this field will + * contain the ID of the newly allocated context. * @indir_size: On entry, the array size of the user buffer for the * indirection table, which may be zero, or (for %ETHTOOL_SRSSH), * %ETH_RXFH_INDIR_NO_CHANGE. On return from %ETHTOOL_GRSSH, @@ -1009,7 +1029,8 @@ struct ethtool_rxfh_indir { * size should be returned. For %ETHTOOL_SRSSH, an @indir_size of * %ETH_RXFH_INDIR_NO_CHANGE means that indir table setting is not requested * and a @indir_size of zero means the indir table should be reset to default - * values. An hfunc of zero means that hash function setting is not requested. + * values (if @rss_context == 0) or that the RSS context should be deleted. + * An hfunc of zero means that hash function setting is not requested. */ struct ethtool_rxfh { uint32_t cmd; @@ -1021,6 +1042,7 @@ struct ethtool_rxfh { uint32_t rsvd32; uint32_t rss_config[0]; }; +#define ETH_RXFH_CONTEXT_ALLOC 0xffffffff #define ETH_RXFH_INDIR_NO_CHANGE 0xffffffff /** @@ -1635,6 +1657,8 @@ static inline int ethtool_validate_duplex(uint8_t duplex) /* Flag to enable additional fields in struct ethtool_rx_flow_spec */ #define FLOW_EXT 0x80000000 #define FLOW_MAC_EXT 0x40000000 +/* Flag to enable RSS spreading of traffic matching rule (nfc only) */ +#define FLOW_RSS 0x20000000 /* L3-L4 network traffic flow hash options */ #define RXH_L2DA (1 << 1) diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h index 939b62775c..6d6128c081 100644 --- a/include/standard-headers/linux/input.h +++ b/include/standard-headers/linux/input.h @@ -28,8 +28,8 @@ struct input_event { #define input_event_sec time.tv_sec #define input_event_usec time.tv_usec #else - __kernel_ulong_t __sec; - __kernel_ulong_t __usec; + unsigned long __sec; + unsigned long __usec; #define input_event_sec __sec #define input_event_usec __usec #endif diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 0c79eac5e9..4da87e2ef8 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -506,6 +506,8 @@ #define PCI_EXP_DEVCTL_READRQ_256B 0x1000 /* 256 Bytes */ #define PCI_EXP_DEVCTL_READRQ_512B 0x2000 /* 512 Bytes */ #define PCI_EXP_DEVCTL_READRQ_1024B 0x3000 /* 1024 Bytes */ +#define PCI_EXP_DEVCTL_READRQ_2048B 0x4000 /* 2048 Bytes */ +#define PCI_EXP_DEVCTL_READRQ_4096B 0x5000 /* 4096 Bytes */ #define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ #define PCI_EXP_DEVSTA 10 /* Device Status */ #define PCI_EXP_DEVSTA_CED 0x0001 /* Correctable Error Detected */ @@ -520,6 +522,7 @@ #define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */ #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ +#define PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */ #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ @@ -547,6 +550,7 @@ #define PCI_EXP_LNKSTA_CLS_2_5GB 0x0001 /* Current Link Speed 2.5GT/s */ #define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */ +#define PCI_EXP_LNKSTA_CLS_16_0GB 0x0004 /* Current Link Speed 16.0GT/s */ #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */ #define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */ #define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */ @@ -648,10 +652,16 @@ #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V2 44 /* v2 endpoints without link end here */ #define PCI_EXP_LNKCAP2 44 /* Link Capabilities 2 */ #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x00000002 /* Supported Speed 2.5GT/s */ -#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5.0GT/s */ -#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8.0GT/s */ +#define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ +#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ +#define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ +#define PCI_EXP_LNKCTL2_TLS 0x000f +#define PCI_EXP_LNKCTL2_TLS_2_5GT 0x0001 /* Supported Speed 2.5GT/s */ +#define PCI_EXP_LNKCTL2_TLS_5_0GT 0x0002 /* Supported Speed 5GT/s */ +#define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed 8GT/s */ +#define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed 16GT/s */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ @@ -978,6 +988,7 @@ #define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */ #define PCI_EXP_DPC_CTL 6 /* DPC control */ +#define PCI_EXP_DPC_CTL_EN_FATAL 0x0001 /* Enable trigger on ERR_FATAL message */ #define PCI_EXP_DPC_CTL_EN_NONFATAL 0x0002 /* Enable trigger on ERR_NONFATAL message */ #define PCI_EXP_DPC_CTL_INT_EN 0x0008 /* DPC Interrupt Enable */ diff --git a/include/standard-headers/linux/virtio_balloon.h b/include/standard-headers/linux/virtio_balloon.h index 7b0a41b8fc..4dbb7dc6c0 100644 --- a/include/standard-headers/linux/virtio_balloon.h +++ b/include/standard-headers/linux/virtio_balloon.h @@ -53,7 +53,24 @@ struct virtio_balloon_config { #define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ #define VIRTIO_BALLOON_S_AVAIL 6 /* Available memory as in /proc */ #define VIRTIO_BALLOON_S_CACHES 7 /* Disk caches */ -#define VIRTIO_BALLOON_S_NR 8 +#define VIRTIO_BALLOON_S_HTLB_PGALLOC 8 /* Hugetlb page allocations */ +#define VIRTIO_BALLOON_S_HTLB_PGFAIL 9 /* Hugetlb page allocation failures */ +#define VIRTIO_BALLOON_S_NR 10 + +#define VIRTIO_BALLOON_S_NAMES_WITH_PREFIX(VIRTIO_BALLOON_S_NAMES_prefix) { \ + VIRTIO_BALLOON_S_NAMES_prefix "swap-in", \ + VIRTIO_BALLOON_S_NAMES_prefix "swap-out", \ + VIRTIO_BALLOON_S_NAMES_prefix "major-faults", \ + VIRTIO_BALLOON_S_NAMES_prefix "minor-faults", \ + VIRTIO_BALLOON_S_NAMES_prefix "free-memory", \ + VIRTIO_BALLOON_S_NAMES_prefix "total-memory", \ + VIRTIO_BALLOON_S_NAMES_prefix "available-memory", \ + VIRTIO_BALLOON_S_NAMES_prefix "disk-caches", \ + VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-allocations", \ + VIRTIO_BALLOON_S_NAMES_prefix "hugetlb-failures" \ +} + +#define VIRTIO_BALLOON_S_NAMES VIRTIO_BALLOON_S_NAMES_WITH_PREFIX("") /* * Memory statistics structure. diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h index e9f255ea3f..260c3681d7 100644 --- a/include/standard-headers/linux/virtio_net.h +++ b/include/standard-headers/linux/virtio_net.h @@ -57,6 +57,9 @@ * Steering */ #define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ +#define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device + * with the same MAC. + */ #define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */ #ifndef VIRTIO_NET_NO_LEGACY diff --git a/include/standard-headers/rdma/vmw_pvrdma-abi.h b/include/standard-headers/rdma/vmw_pvrdma-abi.h index 07a820d337..6c2bc46116 100644 --- a/include/standard-headers/rdma/vmw_pvrdma-abi.h +++ b/include/standard-headers/rdma/vmw_pvrdma-abi.h @@ -143,7 +143,7 @@ struct pvrdma_alloc_pd_resp { }; struct pvrdma_create_cq { - uint64_t buf_addr; + uint64_t __attribute__((aligned(8))) buf_addr; uint32_t buf_size; uint32_t reserved; }; @@ -154,13 +154,13 @@ struct pvrdma_create_cq_resp { }; struct pvrdma_resize_cq { - uint64_t buf_addr; + uint64_t __attribute__((aligned(8))) buf_addr; uint32_t buf_size; uint32_t reserved; }; struct pvrdma_create_srq { - uint64_t buf_addr; + uint64_t __attribute__((aligned(8))) buf_addr; uint32_t buf_size; uint32_t reserved; }; @@ -171,25 +171,25 @@ struct pvrdma_create_srq_resp { }; struct pvrdma_create_qp { - uint64_t rbuf_addr; - uint64_t sbuf_addr; + uint64_t __attribute__((aligned(8))) rbuf_addr; + uint64_t __attribute__((aligned(8))) sbuf_addr; uint32_t rbuf_size; uint32_t sbuf_size; - uint64_t qp_addr; + uint64_t __attribute__((aligned(8))) qp_addr; }; /* PVRDMA masked atomic compare and swap */ struct pvrdma_ex_cmp_swap { - uint64_t swap_val; - uint64_t compare_val; - uint64_t swap_mask; - uint64_t compare_mask; + uint64_t __attribute__((aligned(8))) swap_val; + uint64_t __attribute__((aligned(8))) compare_val; + uint64_t __attribute__((aligned(8))) swap_mask; + uint64_t __attribute__((aligned(8))) compare_mask; }; /* PVRDMA masked atomic fetch and add */ struct pvrdma_ex_fetch_add { - uint64_t add_val; - uint64_t field_boundary; + uint64_t __attribute__((aligned(8))) add_val; + uint64_t __attribute__((aligned(8))) field_boundary; }; /* PVRDMA address vector. */ @@ -207,14 +207,14 @@ struct pvrdma_av { /* PVRDMA scatter/gather entry */ struct pvrdma_sge { - uint64_t addr; + uint64_t __attribute__((aligned(8))) addr; uint32_t length; uint32_t lkey; }; /* PVRDMA receive queue work request */ struct pvrdma_rq_wqe_hdr { - uint64_t wr_id; /* wr id */ + uint64_t __attribute__((aligned(8))) wr_id; /* wr id */ uint32_t num_sge; /* size of s/g array */ uint32_t total_len; /* reserved */ }; @@ -222,7 +222,7 @@ struct pvrdma_rq_wqe_hdr { /* PVRDMA send queue work request */ struct pvrdma_sq_wqe_hdr { - uint64_t wr_id; /* wr id */ + uint64_t __attribute__((aligned(8))) wr_id; /* wr id */ uint32_t num_sge; /* size of s/g array */ uint32_t total_len; /* reserved */ uint32_t opcode; /* operation type */ @@ -234,19 +234,19 @@ struct pvrdma_sq_wqe_hdr { uint32_t reserved; union { struct { - uint64_t remote_addr; + uint64_t __attribute__((aligned(8))) remote_addr; uint32_t rkey; uint8_t reserved[4]; } rdma; struct { - uint64_t remote_addr; - uint64_t compare_add; - uint64_t swap; + uint64_t __attribute__((aligned(8))) remote_addr; + uint64_t __attribute__((aligned(8))) compare_add; + uint64_t __attribute__((aligned(8))) swap; uint32_t rkey; uint32_t reserved; } atomic; struct { - uint64_t remote_addr; + uint64_t __attribute__((aligned(8))) remote_addr; uint32_t log_arg_sz; uint32_t rkey; union { @@ -255,13 +255,14 @@ struct pvrdma_sq_wqe_hdr { } wr_data; } masked_atomics; struct { - uint64_t iova_start; - uint64_t pl_pdir_dma; + uint64_t __attribute__((aligned(8))) iova_start; + uint64_t __attribute__((aligned(8))) pl_pdir_dma; uint32_t page_shift; uint32_t page_list_len; uint32_t length; uint32_t access_flags; uint32_t rkey; + uint32_t reserved; } fast_reg; struct { uint32_t remote_qpn; @@ -274,8 +275,8 @@ struct pvrdma_sq_wqe_hdr { /* Completion queue element. */ struct pvrdma_cqe { - uint64_t wr_id; - uint64_t qp; + uint64_t __attribute__((aligned(8))) wr_id; + uint64_t __attribute__((aligned(8))) qp; uint32_t opcode; uint32_t status; uint32_t byte_len; diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h index 5a632cee1d..637358f430 100644 --- a/include/sysemu/accel.h +++ b/include/sysemu/accel.h @@ -40,6 +40,7 @@ typedef struct AccelClass { const char *name; int (*available)(void); int (*init_machine)(MachineState *ms); + void (*setup_post)(MachineState *ms, AccelState *accel); bool *allowed; /* * Array of global properties that would be applied when specific @@ -68,5 +69,7 @@ extern unsigned long tcg_tb_size; void configure_accelerator(MachineState *ms); /* Register accelerator specific global properties */ void accel_register_compat_props(AccelState *accel); +/* Called just before os_setup_post (ie just before drop OS privs) */ +void accel_setup_post(MachineState *ms); #endif diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 92ab624fac..830d873f24 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -232,4 +232,9 @@ void blk_set_force_allow_inactivate(BlockBackend *blk); void blk_register_buf(BlockBackend *blk, void *host, size_t size); void blk_unregister_buf(BlockBackend *blk, void *host); +int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, + BlockBackend *blk_out, int64_t off_out, + int bytes, BdrvRequestFlags read_flags, + BdrvRequestFlags write_flags); + #endif diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h index e22e5bec9c..c16fd69bc0 100644 --- a/include/sysemu/device_tree.h +++ b/include/sysemu/device_tree.h @@ -43,6 +43,22 @@ void *load_device_tree_from_sysfs(void); char **qemu_fdt_node_path(void *fdt, const char *name, char *compat, Error **errp); +/** + * qemu_fdt_node_unit_path: return the paths of nodes matching a given + * node-name, ie. node-name and node-name@unit-address + * @fdt: pointer to the dt blob + * @name: node name + * @errp: handle to an error object + * + * returns a newly allocated NULL-terminated array of node paths. + * Use g_strfreev() to free it. If one or more nodes were found, the + * array contains the path of each node and the last element equals to + * NULL. If there is no error but no matching node was found, the + * returned array contains a single element equal to NULL. If an error + * was encountered when parsing the blob, the function returns NULL + */ +char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp); + int qemu_fdt_setprop(void *fdt, const char *node_path, const char *property, const void *val, int size); int qemu_fdt_setprop_cell(void *fdt, const char *node_path, diff --git a/include/sysemu/dma.h b/include/sysemu/dma.h index c228c66513..5da3c4e3c5 100644 --- a/include/sysemu/dma.h +++ b/include/sysemu/dma.h @@ -77,7 +77,8 @@ static inline bool dma_memory_valid(AddressSpace *as, DMADirection dir) { return address_space_access_valid(as, addr, len, - dir == DMA_DIRECTION_FROM_DEVICE); + dir == DMA_DIRECTION_FROM_DEVICE, + MEMTXATTRS_UNSPECIFIED); } static inline int dma_memory_rw_relaxed(AddressSpace *as, dma_addr_t addr, @@ -132,7 +133,8 @@ static inline void *dma_memory_map(AddressSpace *as, hwaddr xlen = *len; void *p; - p = address_space_map(as, addr, &xlen, dir == DMA_DIRECTION_FROM_DEVICE); + p = address_space_map(as, addr, &xlen, dir == DMA_DIRECTION_FROM_DEVICE, + MEMTXATTRS_UNSPECIFIED); *len = xlen; return p; } diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index 47bc9846ac..6e6bd2c1cb 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -52,7 +52,6 @@ struct HostMemoryBackend { Object parent; /* protected */ - char *id; uint64_t size; bool merge, dump; bool prealloc, force_prealloc, is_mapped, share; @@ -63,9 +62,10 @@ struct HostMemoryBackend { }; bool host_memory_backend_mr_inited(HostMemoryBackend *backend); -MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend, - Error **errp); +MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend); void host_memory_backend_set_mapped(HostMemoryBackend *backend, bool mapped); bool host_memory_backend_is_mapped(HostMemoryBackend *backend); +size_t host_memory_backend_pagesize(HostMemoryBackend *memdev); + #endif diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 23669c4d5a..0b64b8e067 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -22,7 +22,6 @@ #ifdef NEED_CPU_H # ifdef CONFIG_KVM # include -# include # define CONFIG_KVM_IS_POSSIBLE # endif #else diff --git a/include/sysemu/kvm_int.h b/include/sysemu/kvm_int.h index 888557a1ca..f838412491 100644 --- a/include/sysemu/kvm_int.h +++ b/include/sysemu/kvm_int.h @@ -20,6 +20,7 @@ typedef struct KVMSlot void *ram; int slot; int flags; + int old_flags; } KVMSlot; typedef struct KVMMemoryListener { diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h index d99e5474b4..7a0ae751aa 100644 --- a/include/sysemu/numa.h +++ b/include/sysemu/numa.h @@ -22,7 +22,9 @@ struct NumaNodeMem { }; extern NodeInfo numa_info[MAX_NODES]; +int parse_numa(void *opaque, QemuOpts *opts, Error **errp); void parse_numa_opts(MachineState *ms); +void numa_complete_configuration(MachineState *ms); void query_numa_node_mem(NumaNodeMem node_mem[]); extern QemuOptsList qemu_numa_opts; void numa_legacy_auto_assign_ram(MachineClass *mc, NodeInfo *nodes, diff --git a/include/sysemu/seccomp.h b/include/sysemu/seccomp.h index 9b092aa23f..fe859894f6 100644 --- a/include/sysemu/seccomp.h +++ b/include/sysemu/seccomp.h @@ -21,5 +21,6 @@ #define QEMU_SECCOMP_SET_SPAWN (1 << 3) #define QEMU_SECCOMP_SET_RESOURCECTL (1 << 4) -int seccomp_start(uint32_t seccomp_opts); +int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp); + #endif diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 2b42151c63..76ef6196a7 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -44,6 +44,10 @@ typedef enum ShutdownCause { turns that into a shutdown */ SHUTDOWN_CAUSE_GUEST_PANIC, /* Guest panicked, and command line turns that into a shutdown */ + SHUTDOWN_CAUSE_SUBSYSTEM_RESET,/* Partial guest reset that does not trigger + QMP events and ignores --no-reboot. This + is useful for sanitize hypercalls on s390 + that are used during kexec/kdump/boot */ SHUTDOWN_CAUSE__MAX, } ShutdownCause; @@ -66,6 +70,7 @@ typedef enum WakeupReason { QEMU_WAKEUP_REASON_OTHER, } WakeupReason; +void qemu_exit_preconfig_request(void); void qemu_system_reset_request(ShutdownCause reason); void qemu_system_suspend_request(void); void qemu_register_suspend_notifier(Notifier *notifier); @@ -127,6 +132,7 @@ extern bool boot_strict; extern uint8_t *boot_splash_filedata; extern size_t boot_splash_filedata_size; extern bool enable_mlock; +extern bool enable_cpu_pm; extern uint8_t qemu_extra_params_fw[2]; extern QEMUClockType rtc_clock; extern const char *mem_path; @@ -159,9 +165,12 @@ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); /* serial ports */ -#define MAX_SERIAL_PORTS 4 - -extern Chardev *serial_hds[MAX_SERIAL_PORTS]; +/* Return the Chardev for serial port i, or NULL if none */ +Chardev *serial_hd(int i); +/* return the number of serial ports defined by the user. serial_hd(i) + * will always return NULL for any i which is greater than or equal to this. + */ +int serial_max_hds(void); /* parallel ports */ diff --git a/include/sysemu/xen-mapcache.h b/include/sysemu/xen-mapcache.h index bd4d49e0a4..a03e2f1878 100644 --- a/include/sysemu/xen-mapcache.h +++ b/include/sysemu/xen-mapcache.h @@ -9,9 +9,8 @@ #ifndef XEN_MAPCACHE_H #define XEN_MAPCACHE_H -typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr start_addr, - ram_addr_t size, - void *opaque); +typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, + ram_addr_t size); #ifdef CONFIG_XEN void xen_map_cache_init(phys_offset_to_gaddr_t f, diff --git a/include/ui/console.h b/include/ui/console.h index 1b88e0019b..ef2e26e409 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -291,6 +291,7 @@ bool dpy_ui_info_supported(QemuConsole *con); int dpy_set_ui_info(QemuConsole *con, QemuUIInfo *info); void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h); +void dpy_gfx_update_full(QemuConsole *con); void dpy_gfx_replace_surface(QemuConsole *con, DisplaySurface *surface); void dpy_text_cursor(QemuConsole *con, int x, int y); diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 747233ce58..9db7293bdb 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -7,6 +7,7 @@ extern EGLDisplay *qemu_egl_display; extern EGLConfig qemu_egl_config; +extern DisplayGLMode qemu_egl_mode; typedef struct egl_fb { int width; @@ -34,7 +35,7 @@ extern int qemu_egl_rn_fd; extern struct gbm_device *qemu_egl_rn_gbm_dev; extern EGLContext qemu_egl_rn_ctx; -int egl_rendernode_init(const char *rendernode); +int egl_rendernode_init(const char *rendernode, DisplayGLMode mode); int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc); void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf); @@ -44,8 +45,8 @@ void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf); EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win); -int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy); -int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy); +int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode); +int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode); EGLContext qemu_egl_init_ctx(void); #endif /* EGL_HELPERS_H */ diff --git a/include/ui/gtk.h b/include/ui/gtk.h index 2922fc64b2..a79780afc7 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -127,7 +127,7 @@ void gd_egl_release_dmabuf(DisplayChangeListener *dcl, QemuDmaBuf *dmabuf); void gd_egl_scanout_flush(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h); -void gtk_egl_init(void); +void gtk_egl_init(DisplayGLMode mode); int gd_egl_make_current(DisplayChangeListener *dcl, QEMUGLContext ctx); diff --git a/include/ui/input.h b/include/ui/input.h index 16395ab8f2..34ebc67c5a 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -116,4 +116,7 @@ extern const guint16 qemu_input_map_xorgxquartz_to_qcode[]; extern const guint qemu_input_map_xorgxwin_to_qcode_len; extern const guint16 qemu_input_map_xorgxwin_to_qcode[]; +extern const guint qemu_input_map_osx_to_qcode_len; +extern const guint16 qemu_input_map_osx_to_qcode[]; + #endif /* INPUT_H */ diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h index 51084e6320..f43eecdbd6 100644 --- a/include/ui/sdl2.h +++ b/include/ui/sdl2.h @@ -14,6 +14,7 @@ struct sdl2_console { DisplayChangeListener dcl; DisplaySurface *surface; + DisplayOptions *opts; SDL_Texture *texture; SDL_Window *real_window; SDL_Renderer *real_renderer; diff --git a/io/channel-socket.c b/io/channel-socket.c index 57cfb4d3a6..b50e63a053 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -685,8 +685,10 @@ qio_channel_socket_close(QIOChannel *ioc, Error **errp) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc); + int rc = 0; if (sioc->fd != -1) { + SocketAddress *addr = socket_local_address(sioc->fd, errp); #ifdef WIN32 WSAEventSelect(sioc->fd, NULL, 0); #endif @@ -697,8 +699,22 @@ qio_channel_socket_close(QIOChannel *ioc, return -1; } sioc->fd = -1; + + if (addr && addr->type == SOCKET_ADDRESS_TYPE_UNIX + && addr->u.q_unix.path) { + if (unlink(addr->u.q_unix.path) < 0 && errno != ENOENT) { + error_setg_errno(errp, errno, + "Failed to unlink socket %s", + addr->u.q_unix.path); + rc = -1; + } + } + + if (addr) { + qapi_free_SocketAddress(addr); + } } - return 0; + return rc; } static int diff --git a/io/net-listener.c b/io/net-listener.c index 555e8acaa4..3317aa6e5f 100644 --- a/io/net-listener.c +++ b/io/net-listener.c @@ -25,11 +25,7 @@ QIONetListener *qio_net_listener_new(void) { - QIONetListener *ret; - - ret = QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); - - return ret; + return QIO_NET_LISTENER(object_new(TYPE_QIO_NET_LISTENER)); } void qio_net_listener_set_name(QIONetListener *listener, diff --git a/job-qmp.c b/job-qmp.c new file mode 100644 index 0000000000..410775df61 --- /dev/null +++ b/job-qmp.c @@ -0,0 +1,183 @@ +/* + * QMP interface for background jobs + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/job.h" +#include "qapi/qapi-commands-job.h" +#include "qapi/error.h" +#include "trace-root.h" + +/* Get a job using its ID and acquire its AioContext */ +static Job *find_job(const char *id, AioContext **aio_context, Error **errp) +{ + Job *job; + + *aio_context = NULL; + + job = job_get(id); + if (!job) { + error_setg(errp, "Job not found"); + return NULL; + } + + *aio_context = job->aio_context; + aio_context_acquire(*aio_context); + + return job; +} + +void qmp_job_cancel(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_cancel(job); + job_user_cancel(job, true, errp); + aio_context_release(aio_context); +} + +void qmp_job_pause(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_pause(job); + job_user_pause(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_resume(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_resume(job); + job_user_resume(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_complete(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_complete(job); + job_complete(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_finalize(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_finalize(job); + job_finalize(job, errp); + aio_context_release(aio_context); +} + +void qmp_job_dismiss(const char *id, Error **errp) +{ + AioContext *aio_context; + Job *job = find_job(id, &aio_context, errp); + + if (!job) { + return; + } + + trace_qmp_job_dismiss(job); + job_dismiss(&job, errp); + aio_context_release(aio_context); +} + +static JobInfo *job_query_single(Job *job, Error **errp) +{ + JobInfo *info; + + assert(!job_is_internal(job)); + + info = g_new(JobInfo, 1); + *info = (JobInfo) { + .id = g_strdup(job->id), + .type = job_type(job), + .status = job->status, + .current_progress = job->progress_current, + .total_progress = job->progress_total, + .has_error = !!job->error, + .error = g_strdup(job->error), + }; + + return info; +} + +JobInfoList *qmp_query_jobs(Error **errp) +{ + JobInfoList *head = NULL, **p_next = &head; + Job *job; + + for (job = job_next(NULL); job; job = job_next(job)) { + JobInfoList *elem; + AioContext *aio_context; + + if (job_is_internal(job)) { + continue; + } + elem = g_new0(JobInfoList, 1); + aio_context = job->aio_context; + aio_context_acquire(aio_context); + elem->value = job_query_single(job, errp); + aio_context_release(aio_context); + if (!elem->value) { + g_free(elem); + qapi_free_JobInfoList(head); + return NULL; + } + *p_next = elem; + p_next = &elem->next; + } + + return head; +} diff --git a/job.c b/job.c new file mode 100644 index 0000000000..fa671b431a --- /dev/null +++ b/job.c @@ -0,0 +1,1017 @@ +/* + * Background jobs (long-running operations) + * + * Copyright (c) 2011 IBM Corp. + * Copyright (c) 2012, 2018 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "qemu/job.h" +#include "qemu/id.h" +#include "qemu/main-loop.h" +#include "trace-root.h" +#include "qapi/qapi-events-job.h" + +static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs); + +/* Job State Transition Table */ +bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = { + /* U, C, R, P, Y, S, W, D, X, E, N */ + /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1}, + /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0}, + /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, + /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0}, + /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, + /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0}, + /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, + /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0}, + /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + +bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = { + /* U, C, R, P, Y, S, W, D, X, E, N */ + [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, + [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, + [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, + [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, + [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, +}; + +/* Transactional group of jobs */ +struct JobTxn { + + /* Is this txn being cancelled? */ + bool aborting; + + /* List of jobs */ + QLIST_HEAD(, Job) jobs; + + /* Reference count */ + int refcnt; +}; + +/* Right now, this mutex is only needed to synchronize accesses to job->busy + * and job->sleep_timer, such as concurrent calls to job_do_yield and + * job_enter. */ +static QemuMutex job_mutex; + +static void job_lock(void) +{ + qemu_mutex_lock(&job_mutex); +} + +static void job_unlock(void) +{ + qemu_mutex_unlock(&job_mutex); +} + +static void __attribute__((__constructor__)) job_init(void) +{ + qemu_mutex_init(&job_mutex); +} + +JobTxn *job_txn_new(void) +{ + JobTxn *txn = g_new0(JobTxn, 1); + QLIST_INIT(&txn->jobs); + txn->refcnt = 1; + return txn; +} + +static void job_txn_ref(JobTxn *txn) +{ + txn->refcnt++; +} + +void job_txn_unref(JobTxn *txn) +{ + if (txn && --txn->refcnt == 0) { + g_free(txn); + } +} + +void job_txn_add_job(JobTxn *txn, Job *job) +{ + if (!txn) { + return; + } + + assert(!job->txn); + job->txn = txn; + + QLIST_INSERT_HEAD(&txn->jobs, job, txn_list); + job_txn_ref(txn); +} + +static void job_txn_del_job(Job *job) +{ + if (job->txn) { + QLIST_REMOVE(job, txn_list); + job_txn_unref(job->txn); + job->txn = NULL; + } +} + +static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) +{ + AioContext *ctx; + Job *job, *next; + int rc = 0; + + QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { + if (lock) { + ctx = job->aio_context; + aio_context_acquire(ctx); + } + rc = fn(job); + if (lock) { + aio_context_release(ctx); + } + if (rc) { + break; + } + } + return rc; +} + +bool job_is_internal(Job *job) +{ + return (job->id == NULL); +} + +static void job_state_transition(Job *job, JobStatus s1) +{ + JobStatus s0 = job->status; + assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); + trace_job_state_transition(job, job->ret, + JobSTT[s0][s1] ? "allowed" : "disallowed", + JobStatus_str(s0), JobStatus_str(s1)); + assert(JobSTT[s0][s1]); + job->status = s1; + + if (!job_is_internal(job) && s1 != s0) { + qapi_event_send_job_status_change(job->id, job->status, &error_abort); + } +} + +int job_apply_verb(Job *job, JobVerb verb, Error **errp) +{ + JobStatus s0 = job->status; + assert(verb >= 0 && verb <= JOB_VERB__MAX); + trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), + JobVerbTable[verb][s0] ? "allowed" : "prohibited"); + if (JobVerbTable[verb][s0]) { + return 0; + } + error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'", + job->id, JobStatus_str(s0), JobVerb_str(verb)); + return -EPERM; +} + +JobType job_type(const Job *job) +{ + return job->driver->job_type; +} + +const char *job_type_str(const Job *job) +{ + return JobType_str(job_type(job)); +} + +bool job_is_cancelled(Job *job) +{ + return job->cancelled; +} + +bool job_is_ready(Job *job) +{ + switch (job->status) { + case JOB_STATUS_UNDEFINED: + case JOB_STATUS_CREATED: + case JOB_STATUS_RUNNING: + case JOB_STATUS_PAUSED: + case JOB_STATUS_WAITING: + case JOB_STATUS_PENDING: + case JOB_STATUS_ABORTING: + case JOB_STATUS_CONCLUDED: + case JOB_STATUS_NULL: + return false; + case JOB_STATUS_READY: + case JOB_STATUS_STANDBY: + return true; + default: + g_assert_not_reached(); + } + return false; +} + +bool job_is_completed(Job *job) +{ + switch (job->status) { + case JOB_STATUS_UNDEFINED: + case JOB_STATUS_CREATED: + case JOB_STATUS_RUNNING: + case JOB_STATUS_PAUSED: + case JOB_STATUS_READY: + case JOB_STATUS_STANDBY: + return false; + case JOB_STATUS_WAITING: + case JOB_STATUS_PENDING: + case JOB_STATUS_ABORTING: + case JOB_STATUS_CONCLUDED: + case JOB_STATUS_NULL: + return true; + default: + g_assert_not_reached(); + } + return false; +} + +static bool job_started(Job *job) +{ + return job->co; +} + +static bool job_should_pause(Job *job) +{ + return job->pause_count > 0; +} + +Job *job_next(Job *job) +{ + if (!job) { + return QLIST_FIRST(&jobs); + } + return QLIST_NEXT(job, job_list); +} + +Job *job_get(const char *id) +{ + Job *job; + + QLIST_FOREACH(job, &jobs, job_list) { + if (job->id && !strcmp(id, job->id)) { + return job; + } + } + + return NULL; +} + +static void job_sleep_timer_cb(void *opaque) +{ + Job *job = opaque; + + job_enter(job); +} + +void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn, + AioContext *ctx, int flags, BlockCompletionFunc *cb, + void *opaque, Error **errp) +{ + Job *job; + + if (job_id) { + if (flags & JOB_INTERNAL) { + error_setg(errp, "Cannot specify job ID for internal job"); + return NULL; + } + if (!id_wellformed(job_id)) { + error_setg(errp, "Invalid job ID '%s'", job_id); + return NULL; + } + if (job_get(job_id)) { + error_setg(errp, "Job ID '%s' already in use", job_id); + return NULL; + } + } else if (!(flags & JOB_INTERNAL)) { + error_setg(errp, "An explicit job ID is required"); + return NULL; + } + + job = g_malloc0(driver->instance_size); + job->driver = driver; + job->id = g_strdup(job_id); + job->refcnt = 1; + job->aio_context = ctx; + job->busy = false; + job->paused = true; + job->pause_count = 1; + job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE); + job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS); + job->cb = cb; + job->opaque = opaque; + + notifier_list_init(&job->on_finalize_cancelled); + notifier_list_init(&job->on_finalize_completed); + notifier_list_init(&job->on_pending); + notifier_list_init(&job->on_ready); + + job_state_transition(job, JOB_STATUS_CREATED); + aio_timer_init(qemu_get_aio_context(), &job->sleep_timer, + QEMU_CLOCK_REALTIME, SCALE_NS, + job_sleep_timer_cb, job); + + QLIST_INSERT_HEAD(&jobs, job, job_list); + + /* Single jobs are modeled as single-job transactions for sake of + * consolidating the job management logic */ + if (!txn) { + txn = job_txn_new(); + job_txn_add_job(txn, job); + job_txn_unref(txn); + } else { + job_txn_add_job(txn, job); + } + + return job; +} + +void job_ref(Job *job) +{ + ++job->refcnt; +} + +void job_unref(Job *job) +{ + if (--job->refcnt == 0) { + assert(job->status == JOB_STATUS_NULL); + assert(!timer_pending(&job->sleep_timer)); + assert(!job->txn); + + if (job->driver->free) { + job->driver->free(job); + } + + QLIST_REMOVE(job, job_list); + + g_free(job->error); + g_free(job->id); + g_free(job); + } +} + +void job_progress_update(Job *job, uint64_t done) +{ + job->progress_current += done; +} + +void job_progress_set_remaining(Job *job, uint64_t remaining) +{ + job->progress_total = job->progress_current + remaining; +} + +void job_progress_increase_remaining(Job *job, uint64_t delta) +{ + job->progress_total += delta; +} + +void job_event_cancelled(Job *job) +{ + notifier_list_notify(&job->on_finalize_cancelled, job); +} + +void job_event_completed(Job *job) +{ + notifier_list_notify(&job->on_finalize_completed, job); +} + +static void job_event_pending(Job *job) +{ + notifier_list_notify(&job->on_pending, job); +} + +static void job_event_ready(Job *job) +{ + notifier_list_notify(&job->on_ready, job); +} + +void job_enter_cond(Job *job, bool(*fn)(Job *job)) +{ + if (!job_started(job)) { + return; + } + if (job->deferred_to_main_loop) { + return; + } + + job_lock(); + if (job->busy) { + job_unlock(); + return; + } + + if (fn && !fn(job)) { + job_unlock(); + return; + } + + assert(!job->deferred_to_main_loop); + timer_del(&job->sleep_timer); + job->busy = true; + job_unlock(); + aio_co_wake(job->co); +} + +void job_enter(Job *job) +{ + job_enter_cond(job, NULL); +} + +/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds. + * Reentering the job coroutine with job_enter() before the timer has expired + * is allowed and cancels the timer. + * + * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be + * called explicitly. */ +static void coroutine_fn job_do_yield(Job *job, uint64_t ns) +{ + job_lock(); + if (ns != -1) { + timer_mod(&job->sleep_timer, ns); + } + job->busy = false; + job_unlock(); + qemu_coroutine_yield(); + + /* Set by job_enter_cond() before re-entering the coroutine. */ + assert(job->busy); +} + +void coroutine_fn job_pause_point(Job *job) +{ + assert(job && job_started(job)); + + if (!job_should_pause(job)) { + return; + } + if (job_is_cancelled(job)) { + return; + } + + if (job->driver->pause) { + job->driver->pause(job); + } + + if (job_should_pause(job) && !job_is_cancelled(job)) { + JobStatus status = job->status; + job_state_transition(job, status == JOB_STATUS_READY + ? JOB_STATUS_STANDBY + : JOB_STATUS_PAUSED); + job->paused = true; + job_do_yield(job, -1); + job->paused = false; + job_state_transition(job, status); + } + + if (job->driver->resume) { + job->driver->resume(job); + } +} + +void job_yield(Job *job) +{ + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (job_is_cancelled(job)) { + return; + } + + if (!job_should_pause(job)) { + job_do_yield(job, -1); + } + + job_pause_point(job); +} + +void coroutine_fn job_sleep_ns(Job *job, int64_t ns) +{ + assert(job->busy); + + /* Check cancellation *before* setting busy = false, too! */ + if (job_is_cancelled(job)) { + return; + } + + if (!job_should_pause(job)) { + job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns); + } + + job_pause_point(job); +} + +void job_drain(Job *job) +{ + /* If job is !busy this kicks it into the next pause point. */ + job_enter(job); + + if (job->driver->drain) { + job->driver->drain(job); + } +} + + +/** + * All jobs must allow a pause point before entering their job proper. This + * ensures that jobs can be paused prior to being started, then resumed later. + */ +static void coroutine_fn job_co_entry(void *opaque) +{ + Job *job = opaque; + + assert(job && job->driver && job->driver->start); + job_pause_point(job); + job->driver->start(job); +} + + +void job_start(Job *job) +{ + assert(job && !job_started(job) && job->paused && + job->driver && job->driver->start); + job->co = qemu_coroutine_create(job_co_entry, job); + job->pause_count--; + job->busy = true; + job->paused = false; + job_state_transition(job, JOB_STATUS_RUNNING); + aio_co_enter(job->aio_context, job->co); +} + +/* Assumes the block_job_mutex is held */ +static bool job_timer_not_pending(Job *job) +{ + return !timer_pending(&job->sleep_timer); +} + +void job_pause(Job *job) +{ + job->pause_count++; +} + +void job_resume(Job *job) +{ + assert(job->pause_count > 0); + job->pause_count--; + if (job->pause_count) { + return; + } + + /* kick only if no timer is pending */ + job_enter_cond(job, job_timer_not_pending); +} + +void job_user_pause(Job *job, Error **errp) +{ + if (job_apply_verb(job, JOB_VERB_PAUSE, errp)) { + return; + } + if (job->user_paused) { + error_setg(errp, "Job is already paused"); + return; + } + job->user_paused = true; + job_pause(job); +} + +bool job_user_paused(Job *job) +{ + return job->user_paused; +} + +void job_user_resume(Job *job, Error **errp) +{ + assert(job); + if (!job->user_paused || job->pause_count <= 0) { + error_setg(errp, "Can't resume a job that was not paused"); + return; + } + if (job_apply_verb(job, JOB_VERB_RESUME, errp)) { + return; + } + if (job->driver->user_resume) { + job->driver->user_resume(job); + } + job->user_paused = false; + job_resume(job); +} + +static void job_do_dismiss(Job *job) +{ + assert(job); + job->busy = false; + job->paused = false; + job->deferred_to_main_loop = true; + + job_txn_del_job(job); + + job_state_transition(job, JOB_STATUS_NULL); + job_unref(job); +} + +void job_dismiss(Job **jobptr, Error **errp) +{ + Job *job = *jobptr; + /* similarly to _complete, this is QMP-interface only. */ + assert(job->id); + if (job_apply_verb(job, JOB_VERB_DISMISS, errp)) { + return; + } + + job_do_dismiss(job); + *jobptr = NULL; +} + +void job_early_fail(Job *job) +{ + assert(job->status == JOB_STATUS_CREATED); + job_do_dismiss(job); +} + +static void job_conclude(Job *job) +{ + job_state_transition(job, JOB_STATUS_CONCLUDED); + if (job->auto_dismiss || !job_started(job)) { + job_do_dismiss(job); + } +} + +static void job_update_rc(Job *job) +{ + if (!job->ret && job_is_cancelled(job)) { + job->ret = -ECANCELED; + } + if (job->ret) { + if (!job->error) { + job->error = g_strdup(strerror(-job->ret)); + } + job_state_transition(job, JOB_STATUS_ABORTING); + } +} + +static void job_commit(Job *job) +{ + assert(!job->ret); + if (job->driver->commit) { + job->driver->commit(job); + } +} + +static void job_abort(Job *job) +{ + assert(job->ret); + if (job->driver->abort) { + job->driver->abort(job); + } +} + +static void job_clean(Job *job) +{ + if (job->driver->clean) { + job->driver->clean(job); + } +} + +static int job_finalize_single(Job *job) +{ + assert(job_is_completed(job)); + + /* Ensure abort is called for late-transactional failures */ + job_update_rc(job); + + if (!job->ret) { + job_commit(job); + } else { + job_abort(job); + } + job_clean(job); + + if (job->cb) { + job->cb(job->opaque, job->ret); + } + + /* Emit events only if we actually started */ + if (job_started(job)) { + if (job_is_cancelled(job)) { + job_event_cancelled(job); + } else { + job_event_completed(job); + } + } + + job_txn_del_job(job); + job_conclude(job); + return 0; +} + +static void job_cancel_async(Job *job, bool force) +{ + if (job->user_paused) { + /* Do not call job_enter here, the caller will handle it. */ + job->user_paused = false; + if (job->driver->user_resume) { + job->driver->user_resume(job); + } + assert(job->pause_count > 0); + job->pause_count--; + } + job->cancelled = true; + /* To prevent 'force == false' overriding a previous 'force == true' */ + job->force_cancel |= force; +} + +static void job_completed_txn_abort(Job *job) +{ + AioContext *ctx; + JobTxn *txn = job->txn; + Job *other_job; + + if (txn->aborting) { + /* + * We are cancelled by another job, which will handle everything. + */ + return; + } + txn->aborting = true; + job_txn_ref(txn); + + /* We are the first failed job. Cancel other jobs. */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + ctx = other_job->aio_context; + aio_context_acquire(ctx); + } + + /* Other jobs are effectively cancelled by us, set the status for + * them; this job, however, may or may not be cancelled, depending + * on the caller, so leave it. */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + if (other_job != job) { + job_cancel_async(other_job, false); + } + } + while (!QLIST_EMPTY(&txn->jobs)) { + other_job = QLIST_FIRST(&txn->jobs); + ctx = other_job->aio_context; + if (!job_is_completed(other_job)) { + assert(job_is_cancelled(other_job)); + job_finish_sync(other_job, NULL, NULL); + } + job_finalize_single(other_job); + aio_context_release(ctx); + } + + job_txn_unref(txn); +} + +static int job_prepare(Job *job) +{ + if (job->ret == 0 && job->driver->prepare) { + job->ret = job->driver->prepare(job); + job_update_rc(job); + } + return job->ret; +} + +static int job_needs_finalize(Job *job) +{ + return !job->auto_finalize; +} + +static void job_do_finalize(Job *job) +{ + int rc; + assert(job && job->txn); + + /* prepare the transaction to complete */ + rc = job_txn_apply(job->txn, job_prepare, true); + if (rc) { + job_completed_txn_abort(job); + } else { + job_txn_apply(job->txn, job_finalize_single, true); + } +} + +void job_finalize(Job *job, Error **errp) +{ + assert(job && job->id); + if (job_apply_verb(job, JOB_VERB_FINALIZE, errp)) { + return; + } + job_do_finalize(job); +} + +static int job_transition_to_pending(Job *job) +{ + job_state_transition(job, JOB_STATUS_PENDING); + if (!job->auto_finalize) { + job_event_pending(job); + } + return 0; +} + +void job_transition_to_ready(Job *job) +{ + job_state_transition(job, JOB_STATUS_READY); + job_event_ready(job); +} + +static void job_completed_txn_success(Job *job) +{ + JobTxn *txn = job->txn; + Job *other_job; + + job_state_transition(job, JOB_STATUS_WAITING); + + /* + * Successful completion, see if there are other running jobs in this + * txn. + */ + QLIST_FOREACH(other_job, &txn->jobs, txn_list) { + if (!job_is_completed(other_job)) { + return; + } + assert(other_job->ret == 0); + } + + job_txn_apply(txn, job_transition_to_pending, false); + + /* If no jobs need manual finalization, automatically do so */ + if (job_txn_apply(txn, job_needs_finalize, false) == 0) { + job_do_finalize(job); + } +} + +void job_completed(Job *job, int ret, Error *error) +{ + assert(job && job->txn && !job_is_completed(job)); + + job->ret = ret; + if (error) { + assert(job->ret < 0); + job->error = g_strdup(error_get_pretty(error)); + error_free(error); + } + + job_update_rc(job); + trace_job_completed(job, ret, job->ret); + if (job->ret) { + job_completed_txn_abort(job); + } else { + job_completed_txn_success(job); + } +} + +void job_cancel(Job *job, bool force) +{ + if (job->status == JOB_STATUS_CONCLUDED) { + job_do_dismiss(job); + return; + } + job_cancel_async(job, force); + if (!job_started(job)) { + job_completed(job, -ECANCELED, NULL); + } else if (job->deferred_to_main_loop) { + job_completed_txn_abort(job); + } else { + job_enter(job); + } +} + +void job_user_cancel(Job *job, bool force, Error **errp) +{ + if (job_apply_verb(job, JOB_VERB_CANCEL, errp)) { + return; + } + job_cancel(job, force); +} + +/* A wrapper around job_cancel() taking an Error ** parameter so it may be + * used with job_finish_sync() without the need for (rather nasty) function + * pointer casts there. */ +static void job_cancel_err(Job *job, Error **errp) +{ + job_cancel(job, false); +} + +int job_cancel_sync(Job *job) +{ + return job_finish_sync(job, &job_cancel_err, NULL); +} + +void job_cancel_sync_all(void) +{ + Job *job; + AioContext *aio_context; + + while ((job = job_next(NULL))) { + aio_context = job->aio_context; + aio_context_acquire(aio_context); + job_cancel_sync(job); + aio_context_release(aio_context); + } +} + +int job_complete_sync(Job *job, Error **errp) +{ + return job_finish_sync(job, job_complete, errp); +} + +void job_complete(Job *job, Error **errp) +{ + /* Should not be reachable via external interface for internal jobs */ + assert(job->id); + if (job_apply_verb(job, JOB_VERB_COMPLETE, errp)) { + return; + } + if (job->pause_count || job_is_cancelled(job) || !job->driver->complete) { + error_setg(errp, "The active block job '%s' cannot be completed", + job->id); + return; + } + + job->driver->complete(job, errp); +} + + +typedef struct { + Job *job; + JobDeferToMainLoopFn *fn; + void *opaque; +} JobDeferToMainLoopData; + +static void job_defer_to_main_loop_bh(void *opaque) +{ + JobDeferToMainLoopData *data = opaque; + Job *job = data->job; + AioContext *aio_context = job->aio_context; + + aio_context_acquire(aio_context); + data->fn(data->job, data->opaque); + aio_context_release(aio_context); + + g_free(data); +} + +void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque) +{ + JobDeferToMainLoopData *data = g_malloc(sizeof(*data)); + data->job = job; + data->fn = fn; + data->opaque = opaque; + job->deferred_to_main_loop = true; + + aio_bh_schedule_oneshot(qemu_get_aio_context(), + job_defer_to_main_loop_bh, data); +} + +int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) +{ + Error *local_err = NULL; + int ret; + + job_ref(job); + + if (finish) { + finish(job, &local_err); + } + if (local_err) { + error_propagate(errp, local_err); + job_unref(job); + return -EBUSY; + } + /* job_drain calls job_enter, and it should be enough to induce progress + * until the job completes or moves to the main thread. */ + while (!job->deferred_to_main_loop && !job_is_completed(job)) { + job_drain(job); + } + while (!job_is_completed(job)) { + aio_poll(qemu_get_aio_context(), true); + } + ret = (job_is_cancelled(job) && job->ret == 0) ? -ECANCELED : job->ret; + job_unref(job); + return ret; +} diff --git a/linux-headers/COPYING b/linux-headers/COPYING index ca442d313d..da4cb28feb 100644 --- a/linux-headers/COPYING +++ b/linux-headers/COPYING @@ -1,356 +1,18 @@ +The Linux Kernel is provided under: - NOTE! This copyright does *not* cover user programs that use kernel - services by normal system calls - this is merely considered normal use - of the kernel, and does *not* fall under the heading of "derived work". - Also note that the GPL below is copyrighted by the Free Software - Foundation, but the instance of code that it refers to (the Linux - kernel) is copyrighted by me and others who actually wrote it. + SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note - Also note that the only valid version of the GPL as far as the kernel - is concerned is _this_ particular version of the license (ie v2, not - v2.2 or v3.x or whatever), unless explicitly otherwise stated. +Being under the terms of the GNU General Public License version 2 only, +according with: - Linus Torvalds + LICENSES/preferred/GPL-2.0 ----------------------------------------- +With an explicit syscall exception, as stated at: - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + LICENSES/exceptions/Linux-syscall-note - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +In addition, other licenses may also apply. Please see: - Preamble + Documentation/process/license-rules.rst - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. +for more details. diff --git a/linux-headers/LICENSES/exceptions/Linux-syscall-note b/linux-headers/LICENSES/exceptions/Linux-syscall-note new file mode 100644 index 0000000000..9abdad71fa --- /dev/null +++ b/linux-headers/LICENSES/exceptions/Linux-syscall-note @@ -0,0 +1,25 @@ +SPDX-Exception-Identifier: Linux-syscall-note +SPDX-URL: https://spdx.org/licenses/Linux-syscall-note.html +SPDX-Licenses: GPL-2.0, GPL-2.0+, GPL-1.0+, LGPL-2.0, LGPL-2.0+, LGPL-2.1, LGPL-2.1+, GPL-2.0-only, GPL-2.0-or-later +Usage-Guide: + This exception is used together with one of the above SPDX-Licenses + to mark user space API (uapi) header files so they can be included + into non GPL compliant user space application code. + To use this exception add it with the keyword WITH to one of the + identifiers in the SPDX-Licenses tag: + SPDX-License-Identifier: WITH Linux-syscall-note +License-Text: + + NOTE! This copyright does *not* cover user programs that use kernel + services by normal system calls - this is merely considered normal use + of the kernel, and does *not* fall under the heading of "derived work". + Also note that the GPL below is copyrighted by the Free Software + Foundation, but the instance of code that it refers to (the Linux + kernel) is copyrighted by me and others who actually wrote it. + + Also note that the only valid version of the GPL as far as the kernel + is concerned is _this_ particular version of the license (ie v2, not + v2.2 or v3.x or whatever), unless explicitly otherwise stated. + + Linus Torvalds + diff --git a/linux-headers/LICENSES/preferred/BSD-2-Clause b/linux-headers/LICENSES/preferred/BSD-2-Clause new file mode 100644 index 0000000000..da366e2ce5 --- /dev/null +++ b/linux-headers/LICENSES/preferred/BSD-2-Clause @@ -0,0 +1,32 @@ +Valid-License-Identifier: BSD-2-Clause +SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html +Usage-Guide: + To use the BSD 2-clause "Simplified" License put the following SPDX + tag/value pair into a comment according to the placement guidelines in + the licensing rules documentation: + SPDX-License-Identifier: BSD-2-Clause +License-Text: + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux-headers/LICENSES/preferred/BSD-3-Clause b/linux-headers/LICENSES/preferred/BSD-3-Clause new file mode 100644 index 0000000000..34c7f057c8 --- /dev/null +++ b/linux-headers/LICENSES/preferred/BSD-3-Clause @@ -0,0 +1,36 @@ +Valid-License-Identifier: BSD-3-Clause +SPDX-URL: https://spdx.org/licenses/BSD-3-Clause.html +Usage-Guide: + To use the BSD 3-clause "New" or "Revised" License put the following SPDX + tag/value pair into a comment according to the placement guidelines in + the licensing rules documentation: + SPDX-License-Identifier: BSD-3-Clause +License-Text: + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/linux-headers/LICENSES/preferred/GPL-2.0 b/linux-headers/LICENSES/preferred/GPL-2.0 new file mode 100644 index 0000000000..ff0812fd89 --- /dev/null +++ b/linux-headers/LICENSES/preferred/GPL-2.0 @@ -0,0 +1,359 @@ +Valid-License-Identifier: GPL-2.0 +Valid-License-Identifier: GPL-2.0-only +Valid-License-Identifier: GPL-2.0+ +Valid-License-Identifier: GPL-2.0-or-later +SPDX-URL: https://spdx.org/licenses/GPL-2.0.html +Usage-Guide: + To use this license in source code, put one of the following SPDX + tag/value pairs into a comment according to the placement + guidelines in the licensing rules documentation. + For 'GNU General Public License (GPL) version 2 only' use: + SPDX-License-Identifier: GPL-2.0 + or + SPDX-License-Identifier: GPL-2.0-only + For 'GNU General Public License (GPL) version 2 or any later version' use: + SPDX-License-Identifier: GPL-2.0+ + or + SPDX-License-Identifier: GPL-2.0-or-later +License-Text: + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/linux-headers/asm-arm/bitsperlong.h b/linux-headers/asm-arm/bitsperlong.h new file mode 100644 index 0000000000..6dc0bb0c13 --- /dev/null +++ b/linux-headers/asm-arm/bitsperlong.h @@ -0,0 +1 @@ +#include diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index 4392955081..72aa226e6c 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -91,6 +91,7 @@ struct kvm_regs { #define KVM_VGIC_V3_ADDR_TYPE_DIST 2 #define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 #define KVM_VGIC_ITS_ADDR_TYPE 4 +#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5 #define KVM_VGIC_V3_DIST_SIZE SZ_64K #define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) @@ -135,6 +136,15 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_CRM_SHIFT 7 #define KVM_REG_ARM_32_CRN_MASK 0x0000000000007800 #define KVM_REG_ARM_32_CRN_SHIFT 11 +/* + * For KVM currently all guest registers are nonsecure, but we reserve a bit + * in the encoding to distinguish secure from nonsecure for AArch32 system + * registers that are banked by security. This is 1 for the secure banked + * register, and 0 for the nonsecure banked register or if the register is + * not banked by security. + */ +#define KVM_REG_ARM_SECURE_MASK 0x0000000010000000 +#define KVM_REG_ARM_SECURE_SHIFT 28 #define ARM_CP15_REG_SHIFT_MASK(x,n) \ (((x) << KVM_REG_ARM_ ## n ## _SHIFT) & KVM_REG_ARM_ ## n ## _MASK) @@ -186,6 +196,12 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_VFP_FPINST 0x1009 #define KVM_REG_ARM_VFP_FPINST2 0x100A +/* KVM-as-firmware specific pseudo-registers */ +#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_FW | ((r) & 0xffff)) +#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) + /* Device Control API: ARM VGIC */ #define KVM_DEV_ARM_VGIC_GRP_ADDR 0 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 diff --git a/linux-headers/asm-arm/kvm_para.h b/linux-headers/asm-arm/kvm_para.h deleted file mode 100644 index baacc4996d..0000000000 --- a/linux-headers/asm-arm/kvm_para.h +++ /dev/null @@ -1,2 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#include diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h index 8d5ceaee1a..60c2d931d0 100644 --- a/linux-headers/asm-arm/unistd-common.h +++ b/linux-headers/asm-arm/unistd-common.h @@ -354,5 +354,6 @@ #define __NR_pkey_alloc (__NR_SYSCALL_BASE + 395) #define __NR_pkey_free (__NR_SYSCALL_BASE + 396) #define __NR_statx (__NR_SYSCALL_BASE + 397) +#define __NR_rseq (__NR_SYSCALL_BASE + 398) #endif /* _ASM_ARM_UNISTD_COMMON_H */ diff --git a/linux-headers/asm-arm64/bitsperlong.h b/linux-headers/asm-arm64/bitsperlong.h new file mode 100644 index 0000000000..485d60bee2 --- /dev/null +++ b/linux-headers/asm-arm64/bitsperlong.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (C) 2012 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 . + */ +#ifndef __ASM_BITSPERLONG_H +#define __ASM_BITSPERLONG_H + +#define __BITS_PER_LONG 64 + +#include + +#endif /* __ASM_BITSPERLONG_H */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 4e80651efe..99cb9ad14a 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -91,6 +91,7 @@ struct kvm_regs { #define KVM_VGIC_V3_ADDR_TYPE_DIST 2 #define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 #define KVM_VGIC_ITS_ADDR_TYPE 4 +#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5 #define KVM_VGIC_V3_DIST_SIZE SZ_64K #define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) @@ -206,6 +207,12 @@ struct kvm_arch_memory_slot { #define KVM_REG_ARM_TIMER_CNT ARM64_SYS_REG(3, 3, 14, 3, 2) #define KVM_REG_ARM_TIMER_CVAL ARM64_SYS_REG(3, 3, 14, 0, 2) +/* KVM-as-firmware specific pseudo-registers */ +#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT) +#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | \ + KVM_REG_ARM_FW | ((r) & 0xffff)) +#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) + /* Device Control API: ARM VGIC */ #define KVM_DEV_ARM_VGIC_GRP_ADDR 0 #define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 diff --git a/linux-headers/asm-arm64/kvm_para.h b/linux-headers/asm-arm64/kvm_para.h deleted file mode 100644 index 14fab8f0b9..0000000000 --- a/linux-headers/asm-arm64/kvm_para.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/linux-headers/asm-generic/bitsperlong.h b/linux-headers/asm-generic/bitsperlong.h new file mode 100644 index 0000000000..0aac245b6b --- /dev/null +++ b/linux-headers/asm-generic/bitsperlong.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_GENERIC_BITS_PER_LONG +#define __ASM_GENERIC_BITS_PER_LONG + +/* + * There seems to be no way of detecting this automatically from user + * space, so 64 bit architectures should override this in their + * bitsperlong.h. In particular, an architecture that supports + * both 32 and 64 bit user space must not rely on CONFIG_64BIT + * to decide it, but rather check a compiler provided macro. + */ +#ifndef __BITS_PER_LONG +#define __BITS_PER_LONG 32 +#endif + +#endif /* __ASM_GENERIC_BITS_PER_LONG */ diff --git a/linux-headers/asm-generic/kvm_para.h b/linux-headers/asm-generic/kvm_para.h deleted file mode 100644 index 486f0af73c..0000000000 --- a/linux-headers/asm-generic/kvm_para.h +++ /dev/null @@ -1,4 +0,0 @@ -/* - * There isn't anything here, but the file must not be empty or patch - * will delete it. - */ diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h new file mode 100644 index 0000000000..42990676a5 --- /dev/null +++ b/linux-headers/asm-generic/unistd.h @@ -0,0 +1,783 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#include + +/* + * This file contains the system call numbers, based on the + * layout of the x86-64 architecture, which embeds the + * pointer to the syscall in the table. + * + * As a basic principle, no duplication of functionality + * should be added, e.g. we don't use lseek when llseek + * is present. New architectures should use this file + * and implement the less feature-full calls in user space. + */ + +#ifndef __SYSCALL +#define __SYSCALL(x, y) +#endif + +#if __BITS_PER_LONG == 32 || defined(__SYSCALL_COMPAT) +#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _32) +#else +#define __SC_3264(_nr, _32, _64) __SYSCALL(_nr, _64) +#endif + +#ifdef __SYSCALL_COMPAT +#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _comp) +#define __SC_COMP_3264(_nr, _32, _64, _comp) __SYSCALL(_nr, _comp) +#else +#define __SC_COMP(_nr, _sys, _comp) __SYSCALL(_nr, _sys) +#define __SC_COMP_3264(_nr, _32, _64, _comp) __SC_3264(_nr, _32, _64) +#endif + +#define __NR_io_setup 0 +__SC_COMP(__NR_io_setup, sys_io_setup, compat_sys_io_setup) +#define __NR_io_destroy 1 +__SYSCALL(__NR_io_destroy, sys_io_destroy) +#define __NR_io_submit 2 +__SC_COMP(__NR_io_submit, sys_io_submit, compat_sys_io_submit) +#define __NR_io_cancel 3 +__SYSCALL(__NR_io_cancel, sys_io_cancel) +#define __NR_io_getevents 4 +__SC_COMP(__NR_io_getevents, sys_io_getevents, compat_sys_io_getevents) + +/* fs/xattr.c */ +#define __NR_setxattr 5 +__SYSCALL(__NR_setxattr, sys_setxattr) +#define __NR_lsetxattr 6 +__SYSCALL(__NR_lsetxattr, sys_lsetxattr) +#define __NR_fsetxattr 7 +__SYSCALL(__NR_fsetxattr, sys_fsetxattr) +#define __NR_getxattr 8 +__SYSCALL(__NR_getxattr, sys_getxattr) +#define __NR_lgetxattr 9 +__SYSCALL(__NR_lgetxattr, sys_lgetxattr) +#define __NR_fgetxattr 10 +__SYSCALL(__NR_fgetxattr, sys_fgetxattr) +#define __NR_listxattr 11 +__SYSCALL(__NR_listxattr, sys_listxattr) +#define __NR_llistxattr 12 +__SYSCALL(__NR_llistxattr, sys_llistxattr) +#define __NR_flistxattr 13 +__SYSCALL(__NR_flistxattr, sys_flistxattr) +#define __NR_removexattr 14 +__SYSCALL(__NR_removexattr, sys_removexattr) +#define __NR_lremovexattr 15 +__SYSCALL(__NR_lremovexattr, sys_lremovexattr) +#define __NR_fremovexattr 16 +__SYSCALL(__NR_fremovexattr, sys_fremovexattr) + +/* fs/dcache.c */ +#define __NR_getcwd 17 +__SYSCALL(__NR_getcwd, sys_getcwd) + +/* fs/cookies.c */ +#define __NR_lookup_dcookie 18 +__SC_COMP(__NR_lookup_dcookie, sys_lookup_dcookie, compat_sys_lookup_dcookie) + +/* fs/eventfd.c */ +#define __NR_eventfd2 19 +__SYSCALL(__NR_eventfd2, sys_eventfd2) + +/* fs/eventpoll.c */ +#define __NR_epoll_create1 20 +__SYSCALL(__NR_epoll_create1, sys_epoll_create1) +#define __NR_epoll_ctl 21 +__SYSCALL(__NR_epoll_ctl, sys_epoll_ctl) +#define __NR_epoll_pwait 22 +__SC_COMP(__NR_epoll_pwait, sys_epoll_pwait, compat_sys_epoll_pwait) + +/* fs/fcntl.c */ +#define __NR_dup 23 +__SYSCALL(__NR_dup, sys_dup) +#define __NR_dup3 24 +__SYSCALL(__NR_dup3, sys_dup3) +#define __NR3264_fcntl 25 +__SC_COMP_3264(__NR3264_fcntl, sys_fcntl64, sys_fcntl, compat_sys_fcntl64) + +/* fs/inotify_user.c */ +#define __NR_inotify_init1 26 +__SYSCALL(__NR_inotify_init1, sys_inotify_init1) +#define __NR_inotify_add_watch 27 +__SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch) +#define __NR_inotify_rm_watch 28 +__SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch) + +/* fs/ioctl.c */ +#define __NR_ioctl 29 +__SC_COMP(__NR_ioctl, sys_ioctl, compat_sys_ioctl) + +/* fs/ioprio.c */ +#define __NR_ioprio_set 30 +__SYSCALL(__NR_ioprio_set, sys_ioprio_set) +#define __NR_ioprio_get 31 +__SYSCALL(__NR_ioprio_get, sys_ioprio_get) + +/* fs/locks.c */ +#define __NR_flock 32 +__SYSCALL(__NR_flock, sys_flock) + +/* fs/namei.c */ +#define __NR_mknodat 33 +__SYSCALL(__NR_mknodat, sys_mknodat) +#define __NR_mkdirat 34 +__SYSCALL(__NR_mkdirat, sys_mkdirat) +#define __NR_unlinkat 35 +__SYSCALL(__NR_unlinkat, sys_unlinkat) +#define __NR_symlinkat 36 +__SYSCALL(__NR_symlinkat, sys_symlinkat) +#define __NR_linkat 37 +__SYSCALL(__NR_linkat, sys_linkat) +#ifdef __ARCH_WANT_RENAMEAT +/* renameat is superseded with flags by renameat2 */ +#define __NR_renameat 38 +__SYSCALL(__NR_renameat, sys_renameat) +#endif /* __ARCH_WANT_RENAMEAT */ + +/* fs/namespace.c */ +#define __NR_umount2 39 +__SYSCALL(__NR_umount2, sys_umount) +#define __NR_mount 40 +__SC_COMP(__NR_mount, sys_mount, compat_sys_mount) +#define __NR_pivot_root 41 +__SYSCALL(__NR_pivot_root, sys_pivot_root) + +/* fs/nfsctl.c */ +#define __NR_nfsservctl 42 +__SYSCALL(__NR_nfsservctl, sys_ni_syscall) + +/* fs/open.c */ +#define __NR3264_statfs 43 +__SC_COMP_3264(__NR3264_statfs, sys_statfs64, sys_statfs, \ + compat_sys_statfs64) +#define __NR3264_fstatfs 44 +__SC_COMP_3264(__NR3264_fstatfs, sys_fstatfs64, sys_fstatfs, \ + compat_sys_fstatfs64) +#define __NR3264_truncate 45 +__SC_COMP_3264(__NR3264_truncate, sys_truncate64, sys_truncate, \ + compat_sys_truncate64) +#define __NR3264_ftruncate 46 +__SC_COMP_3264(__NR3264_ftruncate, sys_ftruncate64, sys_ftruncate, \ + compat_sys_ftruncate64) + +#define __NR_fallocate 47 +__SC_COMP(__NR_fallocate, sys_fallocate, compat_sys_fallocate) +#define __NR_faccessat 48 +__SYSCALL(__NR_faccessat, sys_faccessat) +#define __NR_chdir 49 +__SYSCALL(__NR_chdir, sys_chdir) +#define __NR_fchdir 50 +__SYSCALL(__NR_fchdir, sys_fchdir) +#define __NR_chroot 51 +__SYSCALL(__NR_chroot, sys_chroot) +#define __NR_fchmod 52 +__SYSCALL(__NR_fchmod, sys_fchmod) +#define __NR_fchmodat 53 +__SYSCALL(__NR_fchmodat, sys_fchmodat) +#define __NR_fchownat 54 +__SYSCALL(__NR_fchownat, sys_fchownat) +#define __NR_fchown 55 +__SYSCALL(__NR_fchown, sys_fchown) +#define __NR_openat 56 +__SC_COMP(__NR_openat, sys_openat, compat_sys_openat) +#define __NR_close 57 +__SYSCALL(__NR_close, sys_close) +#define __NR_vhangup 58 +__SYSCALL(__NR_vhangup, sys_vhangup) + +/* fs/pipe.c */ +#define __NR_pipe2 59 +__SYSCALL(__NR_pipe2, sys_pipe2) + +/* fs/quota.c */ +#define __NR_quotactl 60 +__SYSCALL(__NR_quotactl, sys_quotactl) + +/* fs/readdir.c */ +#define __NR_getdents64 61 +__SYSCALL(__NR_getdents64, sys_getdents64) + +/* fs/read_write.c */ +#define __NR3264_lseek 62 +__SC_3264(__NR3264_lseek, sys_llseek, sys_lseek) +#define __NR_read 63 +__SYSCALL(__NR_read, sys_read) +#define __NR_write 64 +__SYSCALL(__NR_write, sys_write) +#define __NR_readv 65 +__SC_COMP(__NR_readv, sys_readv, compat_sys_readv) +#define __NR_writev 66 +__SC_COMP(__NR_writev, sys_writev, compat_sys_writev) +#define __NR_pread64 67 +__SC_COMP(__NR_pread64, sys_pread64, compat_sys_pread64) +#define __NR_pwrite64 68 +__SC_COMP(__NR_pwrite64, sys_pwrite64, compat_sys_pwrite64) +#define __NR_preadv 69 +__SC_COMP(__NR_preadv, sys_preadv, compat_sys_preadv) +#define __NR_pwritev 70 +__SC_COMP(__NR_pwritev, sys_pwritev, compat_sys_pwritev) + +/* fs/sendfile.c */ +#define __NR3264_sendfile 71 +__SYSCALL(__NR3264_sendfile, sys_sendfile64) + +/* fs/select.c */ +#define __NR_pselect6 72 +__SC_COMP(__NR_pselect6, sys_pselect6, compat_sys_pselect6) +#define __NR_ppoll 73 +__SC_COMP(__NR_ppoll, sys_ppoll, compat_sys_ppoll) + +/* fs/signalfd.c */ +#define __NR_signalfd4 74 +__SC_COMP(__NR_signalfd4, sys_signalfd4, compat_sys_signalfd4) + +/* fs/splice.c */ +#define __NR_vmsplice 75 +__SC_COMP(__NR_vmsplice, sys_vmsplice, compat_sys_vmsplice) +#define __NR_splice 76 +__SYSCALL(__NR_splice, sys_splice) +#define __NR_tee 77 +__SYSCALL(__NR_tee, sys_tee) + +/* fs/stat.c */ +#define __NR_readlinkat 78 +__SYSCALL(__NR_readlinkat, sys_readlinkat) +#define __NR3264_fstatat 79 +__SC_3264(__NR3264_fstatat, sys_fstatat64, sys_newfstatat) +#define __NR3264_fstat 80 +__SC_3264(__NR3264_fstat, sys_fstat64, sys_newfstat) + +/* fs/sync.c */ +#define __NR_sync 81 +__SYSCALL(__NR_sync, sys_sync) +#define __NR_fsync 82 +__SYSCALL(__NR_fsync, sys_fsync) +#define __NR_fdatasync 83 +__SYSCALL(__NR_fdatasync, sys_fdatasync) +#ifdef __ARCH_WANT_SYNC_FILE_RANGE2 +#define __NR_sync_file_range2 84 +__SC_COMP(__NR_sync_file_range2, sys_sync_file_range2, \ + compat_sys_sync_file_range2) +#else +#define __NR_sync_file_range 84 +__SC_COMP(__NR_sync_file_range, sys_sync_file_range, \ + compat_sys_sync_file_range) +#endif + +/* fs/timerfd.c */ +#define __NR_timerfd_create 85 +__SYSCALL(__NR_timerfd_create, sys_timerfd_create) +#define __NR_timerfd_settime 86 +__SC_COMP(__NR_timerfd_settime, sys_timerfd_settime, \ + compat_sys_timerfd_settime) +#define __NR_timerfd_gettime 87 +__SC_COMP(__NR_timerfd_gettime, sys_timerfd_gettime, \ + compat_sys_timerfd_gettime) + +/* fs/utimes.c */ +#define __NR_utimensat 88 +__SC_COMP(__NR_utimensat, sys_utimensat, compat_sys_utimensat) + +/* kernel/acct.c */ +#define __NR_acct 89 +__SYSCALL(__NR_acct, sys_acct) + +/* kernel/capability.c */ +#define __NR_capget 90 +__SYSCALL(__NR_capget, sys_capget) +#define __NR_capset 91 +__SYSCALL(__NR_capset, sys_capset) + +/* kernel/exec_domain.c */ +#define __NR_personality 92 +__SYSCALL(__NR_personality, sys_personality) + +/* kernel/exit.c */ +#define __NR_exit 93 +__SYSCALL(__NR_exit, sys_exit) +#define __NR_exit_group 94 +__SYSCALL(__NR_exit_group, sys_exit_group) +#define __NR_waitid 95 +__SC_COMP(__NR_waitid, sys_waitid, compat_sys_waitid) + +/* kernel/fork.c */ +#define __NR_set_tid_address 96 +__SYSCALL(__NR_set_tid_address, sys_set_tid_address) +#define __NR_unshare 97 +__SYSCALL(__NR_unshare, sys_unshare) + +/* kernel/futex.c */ +#define __NR_futex 98 +__SC_COMP(__NR_futex, sys_futex, compat_sys_futex) +#define __NR_set_robust_list 99 +__SC_COMP(__NR_set_robust_list, sys_set_robust_list, \ + compat_sys_set_robust_list) +#define __NR_get_robust_list 100 +__SC_COMP(__NR_get_robust_list, sys_get_robust_list, \ + compat_sys_get_robust_list) + +/* kernel/hrtimer.c */ +#define __NR_nanosleep 101 +__SC_COMP(__NR_nanosleep, sys_nanosleep, compat_sys_nanosleep) + +/* kernel/itimer.c */ +#define __NR_getitimer 102 +__SC_COMP(__NR_getitimer, sys_getitimer, compat_sys_getitimer) +#define __NR_setitimer 103 +__SC_COMP(__NR_setitimer, sys_setitimer, compat_sys_setitimer) + +/* kernel/kexec.c */ +#define __NR_kexec_load 104 +__SC_COMP(__NR_kexec_load, sys_kexec_load, compat_sys_kexec_load) + +/* kernel/module.c */ +#define __NR_init_module 105 +__SYSCALL(__NR_init_module, sys_init_module) +#define __NR_delete_module 106 +__SYSCALL(__NR_delete_module, sys_delete_module) + +/* kernel/posix-timers.c */ +#define __NR_timer_create 107 +__SC_COMP(__NR_timer_create, sys_timer_create, compat_sys_timer_create) +#define __NR_timer_gettime 108 +__SC_COMP(__NR_timer_gettime, sys_timer_gettime, compat_sys_timer_gettime) +#define __NR_timer_getoverrun 109 +__SYSCALL(__NR_timer_getoverrun, sys_timer_getoverrun) +#define __NR_timer_settime 110 +__SC_COMP(__NR_timer_settime, sys_timer_settime, compat_sys_timer_settime) +#define __NR_timer_delete 111 +__SYSCALL(__NR_timer_delete, sys_timer_delete) +#define __NR_clock_settime 112 +__SC_COMP(__NR_clock_settime, sys_clock_settime, compat_sys_clock_settime) +#define __NR_clock_gettime 113 +__SC_COMP(__NR_clock_gettime, sys_clock_gettime, compat_sys_clock_gettime) +#define __NR_clock_getres 114 +__SC_COMP(__NR_clock_getres, sys_clock_getres, compat_sys_clock_getres) +#define __NR_clock_nanosleep 115 +__SC_COMP(__NR_clock_nanosleep, sys_clock_nanosleep, \ + compat_sys_clock_nanosleep) + +/* kernel/printk.c */ +#define __NR_syslog 116 +__SYSCALL(__NR_syslog, sys_syslog) + +/* kernel/ptrace.c */ +#define __NR_ptrace 117 +__SYSCALL(__NR_ptrace, sys_ptrace) + +/* kernel/sched/core.c */ +#define __NR_sched_setparam 118 +__SYSCALL(__NR_sched_setparam, sys_sched_setparam) +#define __NR_sched_setscheduler 119 +__SYSCALL(__NR_sched_setscheduler, sys_sched_setscheduler) +#define __NR_sched_getscheduler 120 +__SYSCALL(__NR_sched_getscheduler, sys_sched_getscheduler) +#define __NR_sched_getparam 121 +__SYSCALL(__NR_sched_getparam, sys_sched_getparam) +#define __NR_sched_setaffinity 122 +__SC_COMP(__NR_sched_setaffinity, sys_sched_setaffinity, \ + compat_sys_sched_setaffinity) +#define __NR_sched_getaffinity 123 +__SC_COMP(__NR_sched_getaffinity, sys_sched_getaffinity, \ + compat_sys_sched_getaffinity) +#define __NR_sched_yield 124 +__SYSCALL(__NR_sched_yield, sys_sched_yield) +#define __NR_sched_get_priority_max 125 +__SYSCALL(__NR_sched_get_priority_max, sys_sched_get_priority_max) +#define __NR_sched_get_priority_min 126 +__SYSCALL(__NR_sched_get_priority_min, sys_sched_get_priority_min) +#define __NR_sched_rr_get_interval 127 +__SC_COMP(__NR_sched_rr_get_interval, sys_sched_rr_get_interval, \ + compat_sys_sched_rr_get_interval) + +/* kernel/signal.c */ +#define __NR_restart_syscall 128 +__SYSCALL(__NR_restart_syscall, sys_restart_syscall) +#define __NR_kill 129 +__SYSCALL(__NR_kill, sys_kill) +#define __NR_tkill 130 +__SYSCALL(__NR_tkill, sys_tkill) +#define __NR_tgkill 131 +__SYSCALL(__NR_tgkill, sys_tgkill) +#define __NR_sigaltstack 132 +__SC_COMP(__NR_sigaltstack, sys_sigaltstack, compat_sys_sigaltstack) +#define __NR_rt_sigsuspend 133 +__SC_COMP(__NR_rt_sigsuspend, sys_rt_sigsuspend, compat_sys_rt_sigsuspend) +#define __NR_rt_sigaction 134 +__SC_COMP(__NR_rt_sigaction, sys_rt_sigaction, compat_sys_rt_sigaction) +#define __NR_rt_sigprocmask 135 +__SC_COMP(__NR_rt_sigprocmask, sys_rt_sigprocmask, compat_sys_rt_sigprocmask) +#define __NR_rt_sigpending 136 +__SC_COMP(__NR_rt_sigpending, sys_rt_sigpending, compat_sys_rt_sigpending) +#define __NR_rt_sigtimedwait 137 +__SC_COMP(__NR_rt_sigtimedwait, sys_rt_sigtimedwait, \ + compat_sys_rt_sigtimedwait) +#define __NR_rt_sigqueueinfo 138 +__SC_COMP(__NR_rt_sigqueueinfo, sys_rt_sigqueueinfo, \ + compat_sys_rt_sigqueueinfo) +#define __NR_rt_sigreturn 139 +__SC_COMP(__NR_rt_sigreturn, sys_rt_sigreturn, compat_sys_rt_sigreturn) + +/* kernel/sys.c */ +#define __NR_setpriority 140 +__SYSCALL(__NR_setpriority, sys_setpriority) +#define __NR_getpriority 141 +__SYSCALL(__NR_getpriority, sys_getpriority) +#define __NR_reboot 142 +__SYSCALL(__NR_reboot, sys_reboot) +#define __NR_setregid 143 +__SYSCALL(__NR_setregid, sys_setregid) +#define __NR_setgid 144 +__SYSCALL(__NR_setgid, sys_setgid) +#define __NR_setreuid 145 +__SYSCALL(__NR_setreuid, sys_setreuid) +#define __NR_setuid 146 +__SYSCALL(__NR_setuid, sys_setuid) +#define __NR_setresuid 147 +__SYSCALL(__NR_setresuid, sys_setresuid) +#define __NR_getresuid 148 +__SYSCALL(__NR_getresuid, sys_getresuid) +#define __NR_setresgid 149 +__SYSCALL(__NR_setresgid, sys_setresgid) +#define __NR_getresgid 150 +__SYSCALL(__NR_getresgid, sys_getresgid) +#define __NR_setfsuid 151 +__SYSCALL(__NR_setfsuid, sys_setfsuid) +#define __NR_setfsgid 152 +__SYSCALL(__NR_setfsgid, sys_setfsgid) +#define __NR_times 153 +__SC_COMP(__NR_times, sys_times, compat_sys_times) +#define __NR_setpgid 154 +__SYSCALL(__NR_setpgid, sys_setpgid) +#define __NR_getpgid 155 +__SYSCALL(__NR_getpgid, sys_getpgid) +#define __NR_getsid 156 +__SYSCALL(__NR_getsid, sys_getsid) +#define __NR_setsid 157 +__SYSCALL(__NR_setsid, sys_setsid) +#define __NR_getgroups 158 +__SYSCALL(__NR_getgroups, sys_getgroups) +#define __NR_setgroups 159 +__SYSCALL(__NR_setgroups, sys_setgroups) +#define __NR_uname 160 +__SYSCALL(__NR_uname, sys_newuname) +#define __NR_sethostname 161 +__SYSCALL(__NR_sethostname, sys_sethostname) +#define __NR_setdomainname 162 +__SYSCALL(__NR_setdomainname, sys_setdomainname) +#define __NR_getrlimit 163 +__SC_COMP(__NR_getrlimit, sys_getrlimit, compat_sys_getrlimit) +#define __NR_setrlimit 164 +__SC_COMP(__NR_setrlimit, sys_setrlimit, compat_sys_setrlimit) +#define __NR_getrusage 165 +__SC_COMP(__NR_getrusage, sys_getrusage, compat_sys_getrusage) +#define __NR_umask 166 +__SYSCALL(__NR_umask, sys_umask) +#define __NR_prctl 167 +__SYSCALL(__NR_prctl, sys_prctl) +#define __NR_getcpu 168 +__SYSCALL(__NR_getcpu, sys_getcpu) + +/* kernel/time.c */ +#define __NR_gettimeofday 169 +__SC_COMP(__NR_gettimeofday, sys_gettimeofday, compat_sys_gettimeofday) +#define __NR_settimeofday 170 +__SC_COMP(__NR_settimeofday, sys_settimeofday, compat_sys_settimeofday) +#define __NR_adjtimex 171 +__SC_COMP(__NR_adjtimex, sys_adjtimex, compat_sys_adjtimex) + +/* kernel/timer.c */ +#define __NR_getpid 172 +__SYSCALL(__NR_getpid, sys_getpid) +#define __NR_getppid 173 +__SYSCALL(__NR_getppid, sys_getppid) +#define __NR_getuid 174 +__SYSCALL(__NR_getuid, sys_getuid) +#define __NR_geteuid 175 +__SYSCALL(__NR_geteuid, sys_geteuid) +#define __NR_getgid 176 +__SYSCALL(__NR_getgid, sys_getgid) +#define __NR_getegid 177 +__SYSCALL(__NR_getegid, sys_getegid) +#define __NR_gettid 178 +__SYSCALL(__NR_gettid, sys_gettid) +#define __NR_sysinfo 179 +__SC_COMP(__NR_sysinfo, sys_sysinfo, compat_sys_sysinfo) + +/* ipc/mqueue.c */ +#define __NR_mq_open 180 +__SC_COMP(__NR_mq_open, sys_mq_open, compat_sys_mq_open) +#define __NR_mq_unlink 181 +__SYSCALL(__NR_mq_unlink, sys_mq_unlink) +#define __NR_mq_timedsend 182 +__SC_COMP(__NR_mq_timedsend, sys_mq_timedsend, compat_sys_mq_timedsend) +#define __NR_mq_timedreceive 183 +__SC_COMP(__NR_mq_timedreceive, sys_mq_timedreceive, \ + compat_sys_mq_timedreceive) +#define __NR_mq_notify 184 +__SC_COMP(__NR_mq_notify, sys_mq_notify, compat_sys_mq_notify) +#define __NR_mq_getsetattr 185 +__SC_COMP(__NR_mq_getsetattr, sys_mq_getsetattr, compat_sys_mq_getsetattr) + +/* ipc/msg.c */ +#define __NR_msgget 186 +__SYSCALL(__NR_msgget, sys_msgget) +#define __NR_msgctl 187 +__SC_COMP(__NR_msgctl, sys_msgctl, compat_sys_msgctl) +#define __NR_msgrcv 188 +__SC_COMP(__NR_msgrcv, sys_msgrcv, compat_sys_msgrcv) +#define __NR_msgsnd 189 +__SC_COMP(__NR_msgsnd, sys_msgsnd, compat_sys_msgsnd) + +/* ipc/sem.c */ +#define __NR_semget 190 +__SYSCALL(__NR_semget, sys_semget) +#define __NR_semctl 191 +__SC_COMP(__NR_semctl, sys_semctl, compat_sys_semctl) +#define __NR_semtimedop 192 +__SC_COMP(__NR_semtimedop, sys_semtimedop, compat_sys_semtimedop) +#define __NR_semop 193 +__SYSCALL(__NR_semop, sys_semop) + +/* ipc/shm.c */ +#define __NR_shmget 194 +__SYSCALL(__NR_shmget, sys_shmget) +#define __NR_shmctl 195 +__SC_COMP(__NR_shmctl, sys_shmctl, compat_sys_shmctl) +#define __NR_shmat 196 +__SC_COMP(__NR_shmat, sys_shmat, compat_sys_shmat) +#define __NR_shmdt 197 +__SYSCALL(__NR_shmdt, sys_shmdt) + +/* net/socket.c */ +#define __NR_socket 198 +__SYSCALL(__NR_socket, sys_socket) +#define __NR_socketpair 199 +__SYSCALL(__NR_socketpair, sys_socketpair) +#define __NR_bind 200 +__SYSCALL(__NR_bind, sys_bind) +#define __NR_listen 201 +__SYSCALL(__NR_listen, sys_listen) +#define __NR_accept 202 +__SYSCALL(__NR_accept, sys_accept) +#define __NR_connect 203 +__SYSCALL(__NR_connect, sys_connect) +#define __NR_getsockname 204 +__SYSCALL(__NR_getsockname, sys_getsockname) +#define __NR_getpeername 205 +__SYSCALL(__NR_getpeername, sys_getpeername) +#define __NR_sendto 206 +__SYSCALL(__NR_sendto, sys_sendto) +#define __NR_recvfrom 207 +__SC_COMP(__NR_recvfrom, sys_recvfrom, compat_sys_recvfrom) +#define __NR_setsockopt 208 +__SC_COMP(__NR_setsockopt, sys_setsockopt, compat_sys_setsockopt) +#define __NR_getsockopt 209 +__SC_COMP(__NR_getsockopt, sys_getsockopt, compat_sys_getsockopt) +#define __NR_shutdown 210 +__SYSCALL(__NR_shutdown, sys_shutdown) +#define __NR_sendmsg 211 +__SC_COMP(__NR_sendmsg, sys_sendmsg, compat_sys_sendmsg) +#define __NR_recvmsg 212 +__SC_COMP(__NR_recvmsg, sys_recvmsg, compat_sys_recvmsg) + +/* mm/filemap.c */ +#define __NR_readahead 213 +__SC_COMP(__NR_readahead, sys_readahead, compat_sys_readahead) + +/* mm/nommu.c, also with MMU */ +#define __NR_brk 214 +__SYSCALL(__NR_brk, sys_brk) +#define __NR_munmap 215 +__SYSCALL(__NR_munmap, sys_munmap) +#define __NR_mremap 216 +__SYSCALL(__NR_mremap, sys_mremap) + +/* security/keys/keyctl.c */ +#define __NR_add_key 217 +__SYSCALL(__NR_add_key, sys_add_key) +#define __NR_request_key 218 +__SYSCALL(__NR_request_key, sys_request_key) +#define __NR_keyctl 219 +__SC_COMP(__NR_keyctl, sys_keyctl, compat_sys_keyctl) + +/* arch/example/kernel/sys_example.c */ +#define __NR_clone 220 +__SYSCALL(__NR_clone, sys_clone) +#define __NR_execve 221 +__SC_COMP(__NR_execve, sys_execve, compat_sys_execve) + +#define __NR3264_mmap 222 +__SC_3264(__NR3264_mmap, sys_mmap2, sys_mmap) +/* mm/fadvise.c */ +#define __NR3264_fadvise64 223 +__SC_COMP(__NR3264_fadvise64, sys_fadvise64_64, compat_sys_fadvise64_64) + +/* mm/, CONFIG_MMU only */ +#ifndef __ARCH_NOMMU +#define __NR_swapon 224 +__SYSCALL(__NR_swapon, sys_swapon) +#define __NR_swapoff 225 +__SYSCALL(__NR_swapoff, sys_swapoff) +#define __NR_mprotect 226 +__SYSCALL(__NR_mprotect, sys_mprotect) +#define __NR_msync 227 +__SYSCALL(__NR_msync, sys_msync) +#define __NR_mlock 228 +__SYSCALL(__NR_mlock, sys_mlock) +#define __NR_munlock 229 +__SYSCALL(__NR_munlock, sys_munlock) +#define __NR_mlockall 230 +__SYSCALL(__NR_mlockall, sys_mlockall) +#define __NR_munlockall 231 +__SYSCALL(__NR_munlockall, sys_munlockall) +#define __NR_mincore 232 +__SYSCALL(__NR_mincore, sys_mincore) +#define __NR_madvise 233 +__SYSCALL(__NR_madvise, sys_madvise) +#define __NR_remap_file_pages 234 +__SYSCALL(__NR_remap_file_pages, sys_remap_file_pages) +#define __NR_mbind 235 +__SC_COMP(__NR_mbind, sys_mbind, compat_sys_mbind) +#define __NR_get_mempolicy 236 +__SC_COMP(__NR_get_mempolicy, sys_get_mempolicy, compat_sys_get_mempolicy) +#define __NR_set_mempolicy 237 +__SC_COMP(__NR_set_mempolicy, sys_set_mempolicy, compat_sys_set_mempolicy) +#define __NR_migrate_pages 238 +__SC_COMP(__NR_migrate_pages, sys_migrate_pages, compat_sys_migrate_pages) +#define __NR_move_pages 239 +__SC_COMP(__NR_move_pages, sys_move_pages, compat_sys_move_pages) +#endif + +#define __NR_rt_tgsigqueueinfo 240 +__SC_COMP(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo, \ + compat_sys_rt_tgsigqueueinfo) +#define __NR_perf_event_open 241 +__SYSCALL(__NR_perf_event_open, sys_perf_event_open) +#define __NR_accept4 242 +__SYSCALL(__NR_accept4, sys_accept4) +#define __NR_recvmmsg 243 +__SC_COMP(__NR_recvmmsg, sys_recvmmsg, compat_sys_recvmmsg) + +/* + * Architectures may provide up to 16 syscalls of their own + * starting with this value. + */ +#define __NR_arch_specific_syscall 244 + +#define __NR_wait4 260 +__SC_COMP(__NR_wait4, sys_wait4, compat_sys_wait4) +#define __NR_prlimit64 261 +__SYSCALL(__NR_prlimit64, sys_prlimit64) +#define __NR_fanotify_init 262 +__SYSCALL(__NR_fanotify_init, sys_fanotify_init) +#define __NR_fanotify_mark 263 +__SYSCALL(__NR_fanotify_mark, sys_fanotify_mark) +#define __NR_name_to_handle_at 264 +__SYSCALL(__NR_name_to_handle_at, sys_name_to_handle_at) +#define __NR_open_by_handle_at 265 +__SC_COMP(__NR_open_by_handle_at, sys_open_by_handle_at, \ + compat_sys_open_by_handle_at) +#define __NR_clock_adjtime 266 +__SC_COMP(__NR_clock_adjtime, sys_clock_adjtime, compat_sys_clock_adjtime) +#define __NR_syncfs 267 +__SYSCALL(__NR_syncfs, sys_syncfs) +#define __NR_setns 268 +__SYSCALL(__NR_setns, sys_setns) +#define __NR_sendmmsg 269 +__SC_COMP(__NR_sendmmsg, sys_sendmmsg, compat_sys_sendmmsg) +#define __NR_process_vm_readv 270 +__SC_COMP(__NR_process_vm_readv, sys_process_vm_readv, \ + compat_sys_process_vm_readv) +#define __NR_process_vm_writev 271 +__SC_COMP(__NR_process_vm_writev, sys_process_vm_writev, \ + compat_sys_process_vm_writev) +#define __NR_kcmp 272 +__SYSCALL(__NR_kcmp, sys_kcmp) +#define __NR_finit_module 273 +__SYSCALL(__NR_finit_module, sys_finit_module) +#define __NR_sched_setattr 274 +__SYSCALL(__NR_sched_setattr, sys_sched_setattr) +#define __NR_sched_getattr 275 +__SYSCALL(__NR_sched_getattr, sys_sched_getattr) +#define __NR_renameat2 276 +__SYSCALL(__NR_renameat2, sys_renameat2) +#define __NR_seccomp 277 +__SYSCALL(__NR_seccomp, sys_seccomp) +#define __NR_getrandom 278 +__SYSCALL(__NR_getrandom, sys_getrandom) +#define __NR_memfd_create 279 +__SYSCALL(__NR_memfd_create, sys_memfd_create) +#define __NR_bpf 280 +__SYSCALL(__NR_bpf, sys_bpf) +#define __NR_execveat 281 +__SC_COMP(__NR_execveat, sys_execveat, compat_sys_execveat) +#define __NR_userfaultfd 282 +__SYSCALL(__NR_userfaultfd, sys_userfaultfd) +#define __NR_membarrier 283 +__SYSCALL(__NR_membarrier, sys_membarrier) +#define __NR_mlock2 284 +__SYSCALL(__NR_mlock2, sys_mlock2) +#define __NR_copy_file_range 285 +__SYSCALL(__NR_copy_file_range, sys_copy_file_range) +#define __NR_preadv2 286 +__SC_COMP(__NR_preadv2, sys_preadv2, compat_sys_preadv2) +#define __NR_pwritev2 287 +__SC_COMP(__NR_pwritev2, sys_pwritev2, compat_sys_pwritev2) +#define __NR_pkey_mprotect 288 +__SYSCALL(__NR_pkey_mprotect, sys_pkey_mprotect) +#define __NR_pkey_alloc 289 +__SYSCALL(__NR_pkey_alloc, sys_pkey_alloc) +#define __NR_pkey_free 290 +__SYSCALL(__NR_pkey_free, sys_pkey_free) +#define __NR_statx 291 +__SYSCALL(__NR_statx, sys_statx) +#define __NR_io_pgetevents 292 +__SC_COMP(__NR_io_pgetevents, sys_io_pgetevents, compat_sys_io_pgetevents) + +#undef __NR_syscalls +#define __NR_syscalls 293 + +/* + * 32 bit systems traditionally used different + * syscalls for off_t and loff_t arguments, while + * 64 bit systems only need the off_t version. + * For new 32 bit platforms, there is no need to + * implement the old 32 bit off_t syscalls, so + * they take different names. + * Here we map the numbers so that both versions + * use the same syscall table layout. + */ +#if __BITS_PER_LONG == 64 && !defined(__SYSCALL_COMPAT) +#define __NR_fcntl __NR3264_fcntl +#define __NR_statfs __NR3264_statfs +#define __NR_fstatfs __NR3264_fstatfs +#define __NR_truncate __NR3264_truncate +#define __NR_ftruncate __NR3264_ftruncate +#define __NR_lseek __NR3264_lseek +#define __NR_sendfile __NR3264_sendfile +#define __NR_newfstatat __NR3264_fstatat +#define __NR_fstat __NR3264_fstat +#define __NR_mmap __NR3264_mmap +#define __NR_fadvise64 __NR3264_fadvise64 +#ifdef __NR3264_stat +#define __NR_stat __NR3264_stat +#define __NR_lstat __NR3264_lstat +#endif +#else +#define __NR_fcntl64 __NR3264_fcntl +#define __NR_statfs64 __NR3264_statfs +#define __NR_fstatfs64 __NR3264_fstatfs +#define __NR_truncate64 __NR3264_truncate +#define __NR_ftruncate64 __NR3264_ftruncate +#define __NR_llseek __NR3264_lseek +#define __NR_sendfile64 __NR3264_sendfile +#define __NR_fstatat64 __NR3264_fstatat +#define __NR_fstat64 __NR3264_fstat +#define __NR_mmap2 __NR3264_mmap +#define __NR_fadvise64_64 __NR3264_fadvise64 +#ifdef __NR3264_stat +#define __NR_stat64 __NR3264_stat +#define __NR_lstat64 __NR3264_lstat +#endif +#endif diff --git a/linux-headers/asm-mips/bitsperlong.h b/linux-headers/asm-mips/bitsperlong.h new file mode 100644 index 0000000000..7268380d8d --- /dev/null +++ b/linux-headers/asm-mips/bitsperlong.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_MIPS_BITSPERLONG_H +#define __ASM_MIPS_BITSPERLONG_H + +#define __BITS_PER_LONG _MIPS_SZLONG + +#include + +#endif /* __ASM_MIPS_BITSPERLONG_H */ diff --git a/linux-headers/asm-mips/kvm.h b/linux-headers/asm-mips/kvm.h index 6985eb59b0..edcf717c43 100644 --- a/linux-headers/asm-mips/kvm.h +++ b/linux-headers/asm-mips/kvm.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -19,6 +20,10 @@ * Some parts derived from the x86 version of this file. */ +#define __KVM_HAVE_READONLY_MEM + +#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 + /* * for KVM_GET_REGS and KVM_SET_REGS * @@ -52,9 +57,14 @@ struct kvm_fpu { * Register set = 0: GP registers from kvm_regs (see definitions below). * * Register set = 1: CP0 registers. - * bits[15..8] - Must be zero. - * bits[7..3] - Register 'rd' index. - * bits[2..0] - Register 'sel' index. + * bits[15..8] - COP0 register set. + * + * COP0 register set = 0: Main CP0 registers. + * bits[7..3] - Register 'rd' index. + * bits[2..0] - Register 'sel' index. + * + * COP0 register set = 1: MAARs. + * bits[7..0] - MAAR index. * * Register set = 2: KVM specific registers (see definitions below). * @@ -112,6 +122,15 @@ struct kvm_fpu { #define KVM_REG_MIPS_PC (KVM_REG_MIPS_GP | KVM_REG_SIZE_U64 | 34) +/* + * KVM_REG_MIPS_CP0 - Coprocessor 0 registers. + */ + +#define KVM_REG_MIPS_MAAR (KVM_REG_MIPS_CP0 | (1 << 8)) +#define KVM_REG_MIPS_CP0_MAAR(n) (KVM_REG_MIPS_MAAR | \ + KVM_REG_SIZE_U64 | (n)) + + /* * KVM_REG_MIPS_KVM - KVM specific control registers. */ diff --git a/linux-headers/asm-mips/kvm_para.h b/linux-headers/asm-mips/kvm_para.h deleted file mode 100644 index dbb2464f3b..0000000000 --- a/linux-headers/asm-mips/kvm_para.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef _ASM_MIPS_KVM_PARA_H -#define _ASM_MIPS_KVM_PARA_H - - -#endif /* _ASM_MIPS_KVM_PARA_H */ diff --git a/linux-headers/asm-mips/sgidefs.h b/linux-headers/asm-mips/sgidefs.h new file mode 100644 index 0000000000..26143e3b7c --- /dev/null +++ b/linux-headers/asm-mips/sgidefs.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1996, 1999, 2001 Ralf Baechle + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) 2001 MIPS Technologies, Inc. + */ +#ifndef __ASM_SGIDEFS_H +#define __ASM_SGIDEFS_H + +/* + * Using a Linux compiler for building Linux seems logic but not to + * everybody. + */ +#ifndef __linux__ +#error Use a Linux compiler or give up. +#endif + +/* + * Definitions for the ISA levels + * + * With the introduction of MIPS32 / MIPS64 instruction sets definitions + * MIPS ISAs are no longer subsets of each other. Therefore comparisons + * on these symbols except with == may result in unexpected results and + * are forbidden! + */ +#define _MIPS_ISA_MIPS1 1 +#define _MIPS_ISA_MIPS2 2 +#define _MIPS_ISA_MIPS3 3 +#define _MIPS_ISA_MIPS4 4 +#define _MIPS_ISA_MIPS5 5 +#define _MIPS_ISA_MIPS32 6 +#define _MIPS_ISA_MIPS64 7 + +/* + * Subprogram calling convention + */ +#define _MIPS_SIM_ABI32 1 +#define _MIPS_SIM_NABI32 2 +#define _MIPS_SIM_ABI64 3 + +#endif /* __ASM_SGIDEFS_H */ diff --git a/linux-headers/asm-mips/unistd.h b/linux-headers/asm-mips/unistd.h index 2a2020938e..9bfef7f764 100644 --- a/linux-headers/asm-mips/unistd.h +++ b/linux-headers/asm-mips/unistd.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -377,16 +378,27 @@ #define __NR_memfd_create (__NR_Linux + 354) #define __NR_bpf (__NR_Linux + 355) #define __NR_execveat (__NR_Linux + 356) +#define __NR_userfaultfd (__NR_Linux + 357) +#define __NR_membarrier (__NR_Linux + 358) +#define __NR_mlock2 (__NR_Linux + 359) +#define __NR_copy_file_range (__NR_Linux + 360) +#define __NR_preadv2 (__NR_Linux + 361) +#define __NR_pwritev2 (__NR_Linux + 362) +#define __NR_pkey_mprotect (__NR_Linux + 363) +#define __NR_pkey_alloc (__NR_Linux + 364) +#define __NR_pkey_free (__NR_Linux + 365) +#define __NR_statx (__NR_Linux + 366) + /* * Offset of the last Linux o32 flavoured syscall */ -#define __NR_Linux_syscalls 356 +#define __NR_Linux_syscalls 366 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ #define __NR_O32_Linux 4000 -#define __NR_O32_Linux_syscalls 356 +#define __NR_O32_Linux_syscalls 366 #if _MIPS_SIM == _MIPS_SIM_ABI64 @@ -711,16 +723,26 @@ #define __NR_memfd_create (__NR_Linux + 314) #define __NR_bpf (__NR_Linux + 315) #define __NR_execveat (__NR_Linux + 316) +#define __NR_userfaultfd (__NR_Linux + 317) +#define __NR_membarrier (__NR_Linux + 318) +#define __NR_mlock2 (__NR_Linux + 319) +#define __NR_copy_file_range (__NR_Linux + 320) +#define __NR_preadv2 (__NR_Linux + 321) +#define __NR_pwritev2 (__NR_Linux + 322) +#define __NR_pkey_mprotect (__NR_Linux + 323) +#define __NR_pkey_alloc (__NR_Linux + 324) +#define __NR_pkey_free (__NR_Linux + 325) +#define __NR_statx (__NR_Linux + 326) /* * Offset of the last Linux 64-bit flavoured syscall */ -#define __NR_Linux_syscalls 316 +#define __NR_Linux_syscalls 326 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ #define __NR_64_Linux 5000 -#define __NR_64_Linux_syscalls 316 +#define __NR_64_Linux_syscalls 326 #if _MIPS_SIM == _MIPS_SIM_NABI32 @@ -1049,15 +1071,25 @@ #define __NR_memfd_create (__NR_Linux + 318) #define __NR_bpf (__NR_Linux + 319) #define __NR_execveat (__NR_Linux + 320) +#define __NR_userfaultfd (__NR_Linux + 321) +#define __NR_membarrier (__NR_Linux + 322) +#define __NR_mlock2 (__NR_Linux + 323) +#define __NR_copy_file_range (__NR_Linux + 324) +#define __NR_preadv2 (__NR_Linux + 325) +#define __NR_pwritev2 (__NR_Linux + 326) +#define __NR_pkey_mprotect (__NR_Linux + 327) +#define __NR_pkey_alloc (__NR_Linux + 328) +#define __NR_pkey_free (__NR_Linux + 329) +#define __NR_statx (__NR_Linux + 330) /* * Offset of the last N32 flavoured syscall */ -#define __NR_Linux_syscalls 320 +#define __NR_Linux_syscalls 330 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ #define __NR_N32_Linux 6000 -#define __NR_N32_Linux_syscalls 320 +#define __NR_N32_Linux_syscalls 330 #endif /* _ASM_UNISTD_H */ diff --git a/linux-headers/asm-powerpc/bitsperlong.h b/linux-headers/asm-powerpc/bitsperlong.h new file mode 100644 index 0000000000..46ece3ecff --- /dev/null +++ b/linux-headers/asm-powerpc/bitsperlong.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_POWERPC_BITSPERLONG_H +#define __ASM_POWERPC_BITSPERLONG_H + +#if defined(__powerpc64__) +# define __BITS_PER_LONG 64 +#else +# define __BITS_PER_LONG 32 +#endif + +#include + +#endif /* __ASM_POWERPC_BITSPERLONG_H */ diff --git a/linux-headers/asm-powerpc/epapr_hcalls.h b/linux-headers/asm-powerpc/epapr_hcalls.h deleted file mode 100644 index 6cca559993..0000000000 --- a/linux-headers/asm-powerpc/epapr_hcalls.h +++ /dev/null @@ -1,99 +0,0 @@ -/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */ -/* - * ePAPR hcall interface - * - * Copyright 2008-2011 Freescale Semiconductor, Inc. - * - * Author: Timur Tabi - * - * This file is provided under a dual BSD/GPL license. When using or - * redistributing this file, you may do so under either license. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _ASM_POWERPC_EPAPR_HCALLS_H -#define _ASM_POWERPC_EPAPR_HCALLS_H - -#define EV_BYTE_CHANNEL_SEND 1 -#define EV_BYTE_CHANNEL_RECEIVE 2 -#define EV_BYTE_CHANNEL_POLL 3 -#define EV_INT_SET_CONFIG 4 -#define EV_INT_GET_CONFIG 5 -#define EV_INT_SET_MASK 6 -#define EV_INT_GET_MASK 7 -#define EV_INT_IACK 9 -#define EV_INT_EOI 10 -#define EV_INT_SEND_IPI 11 -#define EV_INT_SET_TASK_PRIORITY 12 -#define EV_INT_GET_TASK_PRIORITY 13 -#define EV_DOORBELL_SEND 14 -#define EV_MSGSND 15 -#define EV_IDLE 16 - -/* vendor ID: epapr */ -#define EV_LOCAL_VENDOR_ID 0 /* for private use */ -#define EV_EPAPR_VENDOR_ID 1 -#define EV_FSL_VENDOR_ID 2 /* Freescale Semiconductor */ -#define EV_IBM_VENDOR_ID 3 /* IBM */ -#define EV_GHS_VENDOR_ID 4 /* Green Hills Software */ -#define EV_ENEA_VENDOR_ID 5 /* Enea */ -#define EV_WR_VENDOR_ID 6 /* Wind River Systems */ -#define EV_AMCC_VENDOR_ID 7 /* Applied Micro Circuits */ -#define EV_KVM_VENDOR_ID 42 /* KVM */ - -/* The max number of bytes that a byte channel can send or receive per call */ -#define EV_BYTE_CHANNEL_MAX_BYTES 16 - - -#define _EV_HCALL_TOKEN(id, num) (((id) << 16) | (num)) -#define EV_HCALL_TOKEN(hcall_num) _EV_HCALL_TOKEN(EV_EPAPR_VENDOR_ID, hcall_num) - -/* epapr return codes */ -#define EV_SUCCESS 0 -#define EV_EPERM 1 /* Operation not permitted */ -#define EV_ENOENT 2 /* Entry Not Found */ -#define EV_EIO 3 /* I/O error occurred */ -#define EV_EAGAIN 4 /* The operation had insufficient - * resources to complete and should be - * retried - */ -#define EV_ENOMEM 5 /* There was insufficient memory to - * complete the operation */ -#define EV_EFAULT 6 /* Bad guest address */ -#define EV_ENODEV 7 /* No such device */ -#define EV_EINVAL 8 /* An argument supplied to the hcall - was out of range or invalid */ -#define EV_INTERNAL 9 /* An internal error occurred */ -#define EV_CONFIG 10 /* A configuration error was detected */ -#define EV_INVALID_STATE 11 /* The object is in an invalid state */ -#define EV_UNIMPLEMENTED 12 /* Unimplemented hypercall */ -#define EV_BUFFER_OVERFLOW 13 /* Caller-supplied buffer too small */ - -#endif /* _ASM_POWERPC_EPAPR_HCALLS_H */ diff --git a/linux-headers/asm-powerpc/kvm_para.h b/linux-headers/asm-powerpc/kvm_para.h deleted file mode 100644 index 9beb49cc10..0000000000 --- a/linux-headers/asm-powerpc/kvm_para.h +++ /dev/null @@ -1,98 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * 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, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -#ifndef __POWERPC_KVM_PARA_H__ -#define __POWERPC_KVM_PARA_H__ - -#include - -/* - * Additions to this struct must only occur at the end, and should be - * accompanied by a KVM_MAGIC_FEAT flag to advertise that they are present - * (albeit not necessarily relevant to the current target hardware platform). - * - * Struct fields are always 32 or 64 bit aligned, depending on them being 32 - * or 64 bit wide respectively. - * - * See Documentation/virtual/kvm/ppc-pv.txt - */ -struct kvm_vcpu_arch_shared { - __u64 scratch1; - __u64 scratch2; - __u64 scratch3; - __u64 critical; /* Guest may not get interrupts if == r1 */ - __u64 sprg0; - __u64 sprg1; - __u64 sprg2; - __u64 sprg3; - __u64 srr0; - __u64 srr1; - __u64 dar; /* dear on BookE */ - __u64 msr; - __u32 dsisr; - __u32 int_pending; /* Tells the guest if we have an interrupt */ - __u32 sr[16]; - __u32 mas0; - __u32 mas1; - __u64 mas7_3; - __u64 mas2; - __u32 mas4; - __u32 mas6; - __u32 esr; - __u32 pir; - - /* - * SPRG4-7 are user-readable, so we can only keep these consistent - * between the shared area and the real registers when there's an - * intervening exit to KVM. This also applies to SPRG3 on some - * chips. - * - * This suffices for access by guest userspace, since in PR-mode - * KVM, an exit must occur when changing the guest's MSR[PR]. - * If the guest kernel writes to SPRG3-7 via the shared area, it - * must also use the shared area for reading while in kernel space. - */ - __u64 sprg4; - __u64 sprg5; - __u64 sprg6; - __u64 sprg7; -}; - -#define KVM_SC_MAGIC_R0 0x4b564d21 /* "KVM!" */ - -#define KVM_HCALL_TOKEN(num) _EV_HCALL_TOKEN(EV_KVM_VENDOR_ID, num) - -#include - -#define KVM_FEATURE_MAGIC_PAGE 1 - -/* Magic page flags from host to guest */ - -#define KVM_MAGIC_FEAT_SR (1 << 0) - -/* MASn, ESR, PIR, and high SPRGs */ -#define KVM_MAGIC_FEAT_MAS0_TO_SPRG7 (1 << 1) - -/* Magic page flags from guest to host */ - -#define MAGIC_PAGE_FLAG_NOT_MAPPED_NX (1 << 0) - - -#endif /* __POWERPC_KVM_PARA_H__ */ diff --git a/linux-headers/asm-powerpc/unistd.h b/linux-headers/asm-powerpc/unistd.h index 0c08edcfcd..3629858142 100644 --- a/linux-headers/asm-powerpc/unistd.h +++ b/linux-headers/asm-powerpc/unistd.h @@ -398,5 +398,6 @@ #define __NR_pkey_alloc 384 #define __NR_pkey_free 385 #define __NR_pkey_mprotect 386 +#define __NR_rseq 387 #endif /* _ASM_POWERPC_UNISTD_H_ */ diff --git a/linux-headers/asm-s390/bitsperlong.h b/linux-headers/asm-s390/bitsperlong.h new file mode 100644 index 0000000000..cceaf47b02 --- /dev/null +++ b/linux-headers/asm-s390/bitsperlong.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_S390_BITSPERLONG_H +#define __ASM_S390_BITSPERLONG_H + +#ifndef __s390x__ +#define __BITS_PER_LONG 32 +#else +#define __BITS_PER_LONG 64 +#endif + +#include + +#endif /* __ASM_S390_BITSPERLONG_H */ + diff --git a/linux-headers/asm-s390/kvm_para.h b/linux-headers/asm-s390/kvm_para.h deleted file mode 100644 index b9ab584adf..0000000000 --- a/linux-headers/asm-s390/kvm_para.h +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * User API definitions for paravirtual devices on s390 - * - * Copyright IBM Corp. 2008 - * - * Author(s): Christian Borntraeger - */ diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index 1ae66a263b..d0f97cd0a4 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -360,5 +360,6 @@ #define __NR_s390_guarded_storage 378 #define __NR_statx 379 #define __NR_s390_sthyi 380 +#define __NR_kexec_file_load 381 #endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index 8aa9d046a9..23ffb97746 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -327,5 +327,6 @@ #define __NR_s390_guarded_storage 378 #define __NR_statx 379 #define __NR_s390_sthyi 380 +#define __NR_kexec_file_load 381 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/bitsperlong.h b/linux-headers/asm-x86/bitsperlong.h new file mode 100644 index 0000000000..5d72c84588 --- /dev/null +++ b/linux-headers/asm-x86/bitsperlong.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef __ASM_X86_BITSPERLONG_H +#define __ASM_X86_BITSPERLONG_H + +#if defined(__x86_64__) && !defined(__ILP32__) +# define __BITS_PER_LONG 64 +#else +# define __BITS_PER_LONG 32 +#endif + +#include + +#endif /* __ASM_X86_BITSPERLONG_H */ + diff --git a/linux-headers/asm-x86/hyperv.h b/linux-headers/asm-x86/hyperv.h deleted file mode 100644 index 01af4d8593..0000000000 --- a/linux-headers/asm-x86/hyperv.h +++ /dev/null @@ -1 +0,0 @@ -#include "standard-headers/asm-x86/hyperv.h" diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index f3a960488e..c535c2fdea 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -354,8 +354,25 @@ struct kvm_xcrs { __u64 padding[16]; }; -/* definition of registers in kvm_run */ +#define KVM_SYNC_X86_REGS (1UL << 0) +#define KVM_SYNC_X86_SREGS (1UL << 1) +#define KVM_SYNC_X86_EVENTS (1UL << 2) + +#define KVM_SYNC_X86_VALID_FIELDS \ + (KVM_SYNC_X86_REGS| \ + KVM_SYNC_X86_SREGS| \ + KVM_SYNC_X86_EVENTS) + +/* kvm_sync_regs struct included by kvm_run struct */ struct kvm_sync_regs { + /* Members of this structure are potentially malicious. + * Care must be taken by code reading, esp. interpreting, + * data fields from them inside KVM to prevent TOCTOU and + * double-fetch types of vulnerabilities. + */ + struct kvm_regs regs; + struct kvm_sregs sregs; + struct kvm_vcpu_events events; }; #define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0) diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h index 8a206df454..c1b30a0cf4 100644 --- a/linux-headers/asm-x86/unistd_32.h +++ b/linux-headers/asm-x86/unistd_32.h @@ -382,5 +382,7 @@ #define __NR_pkey_free 382 #define __NR_statx 383 #define __NR_arch_prctl 384 +#define __NR_io_pgetevents 385 +#define __NR_rseq 386 #endif /* _ASM_X86_UNISTD_32_H */ diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h index 336c2e4aaa..c2e464c115 100644 --- a/linux-headers/asm-x86/unistd_64.h +++ b/linux-headers/asm-x86/unistd_64.h @@ -334,5 +334,7 @@ #define __NR_pkey_alloc 330 #define __NR_pkey_free 331 #define __NR_statx 332 +#define __NR_io_pgetevents 333 +#define __NR_rseq 334 #endif /* _ASM_X86_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h index cb98a52998..37229021f0 100644 --- a/linux-headers/asm-x86/unistd_x32.h +++ b/linux-headers/asm-x86/unistd_x32.h @@ -287,6 +287,8 @@ #define __NR_pkey_alloc (__X32_SYSCALL_BIT + 330) #define __NR_pkey_free (__X32_SYSCALL_BIT + 331) #define __NR_statx (__X32_SYSCALL_BIT + 332) +#define __NR_io_pgetevents (__X32_SYSCALL_BIT + 333) +#define __NR_rseq (__X32_SYSCALL_BIT + 334) #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512) #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513) #define __NR_ioctl (__X32_SYSCALL_BIT + 514) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index a167be89d1..98f389a5a3 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -396,6 +396,10 @@ struct kvm_run { char padding[256]; }; + /* 2048 is the size of the char array used to bound/pad the size + * of the union that holds sync regs. + */ + #define SYNC_REGS_SIZE_BYTES 2048 /* * shared registers between kvm and userspace. * kvm_valid_regs specifies the register classes set by the host @@ -407,7 +411,7 @@ struct kvm_run { __u64 kvm_dirty_regs; union { struct kvm_sync_regs regs; - char padding[2048]; + char padding[SYNC_REGS_SIZE_BYTES]; } s; }; @@ -672,6 +676,13 @@ struct kvm_ioeventfd { __u8 pad[36]; }; +#define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0) +#define KVM_X86_DISABLE_EXITS_HLT (1 << 1) +#define KVM_X86_DISABLE_EXITS_PAUSE (1 << 2) +#define KVM_X86_DISABLE_VALID_EXITS (KVM_X86_DISABLE_EXITS_MWAIT | \ + KVM_X86_DISABLE_EXITS_HLT | \ + KVM_X86_DISABLE_EXITS_PAUSE) + /* for KVM_ENABLE_CAP */ struct kvm_enable_cap { /* in */ @@ -925,7 +936,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_S390_GS 140 #define KVM_CAP_S390_AIS 141 #define KVM_CAP_SPAPR_TCE_VFIO 142 -#define KVM_CAP_X86_GUEST_MWAIT 143 +#define KVM_CAP_X86_DISABLE_EXITS 143 #define KVM_CAP_ARM_USER_IRQ 144 #define KVM_CAP_S390_CMMA_MIGRATION 145 #define KVM_CAP_PPC_FWNMI 146 @@ -936,6 +947,8 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_PPC_GET_CPU_CHAR 151 #define KVM_CAP_S390_BPB 152 #define KVM_CAP_GET_MSR_FEATURES 153 +#define KVM_CAP_HYPERV_EVENTFD 154 +#define KVM_CAP_HYPERV_TLBFLUSH 155 #ifdef KVM_CAP_IRQ_ROUTING @@ -1375,6 +1388,10 @@ struct kvm_enc_region { #define KVM_MEMORY_ENCRYPT_REG_REGION _IOR(KVMIO, 0xbb, struct kvm_enc_region) #define KVM_MEMORY_ENCRYPT_UNREG_REGION _IOR(KVMIO, 0xbc, struct kvm_enc_region) +/* Available with KVM_CAP_HYPERV_EVENTFD */ +#define KVM_HYPERV_EVENTFD _IOW(KVMIO, 0xbd, struct kvm_hyperv_eventfd) + + /* Secure Encrypted Virtualization command */ enum sev_cmd_id { /* Guest initialization commands */ @@ -1515,4 +1532,14 @@ struct kvm_assigned_msix_entry { #define KVM_ARM_DEV_EL1_PTIMER (1 << 1) #define KVM_ARM_DEV_PMU (1 << 2) +struct kvm_hyperv_eventfd { + __u32 conn_id; + __s32 fd; + __u32 flags; + __u32 padding[3]; +}; + +#define KVM_HYPERV_CONN_ID_MASK 0x00ffffff +#define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0) + #endif /* __LINUX_KVM_H */ diff --git a/linux-headers/linux/kvm_para.h b/linux-headers/linux/kvm_para.h deleted file mode 100644 index 8bcd0aa853..0000000000 --- a/linux-headers/linux/kvm_para.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -#ifndef __LINUX_KVM_PARA_H -#define __LINUX_KVM_PARA_H - -/* - * This header file provides a method for making a hypercall to the host - * Architectures should define: - * - kvm_hypercall0, kvm_hypercall1... - * - kvm_arch_para_features - * - kvm_para_available - */ - -/* Return values for hypercalls */ -#define KVM_ENOSYS 1000 -#define KVM_EFAULT EFAULT -#define KVM_E2BIG E2BIG -#define KVM_EPERM EPERM -#define KVM_EOPNOTSUPP 95 - -#define KVM_HC_VAPIC_POLL_IRQ 1 -#define KVM_HC_MMU_OP 2 -#define KVM_HC_FEATURES 3 -#define KVM_HC_PPC_MAP_MAGIC_PAGE 4 -#define KVM_HC_KICK_CPU 5 -#define KVM_HC_MIPS_GET_CLOCK_FREQ 6 -#define KVM_HC_MIPS_EXIT_VM 7 -#define KVM_HC_MIPS_CONSOLE_OUTPUT 8 -#define KVM_HC_CLOCK_PAIRING 9 - -/* - * hypercalls use architecture specific - */ -#include - -#endif /* __LINUX_KVM_PARA_H */ diff --git a/linux-headers/linux/psp-sev.h b/linux-headers/linux/psp-sev.h index 33e247471a..b7b933ffaa 100644 --- a/linux-headers/linux/psp-sev.h +++ b/linux-headers/linux/psp-sev.h @@ -30,6 +30,7 @@ enum { SEV_PDH_GEN, SEV_PDH_CERT_EXPORT, SEV_PEK_CERT_IMPORT, + SEV_GET_ID, SEV_MAX, }; @@ -123,6 +124,17 @@ struct sev_user_data_pdh_cert_export { __u32 cert_chain_len; /* In/Out */ } __attribute__((packed)); +/** + * struct sev_user_data_get_id - GET_ID command parameters + * + * @socket1: Buffer to pass unique ID of first socket + * @socket2: Buffer to pass unique ID of second socket + */ +struct sev_user_data_get_id { + __u8 socket1[64]; /* Out */ + __u8 socket2[64]; /* Out */ +} __attribute__((packed)); + /** * struct sev_issue_cmd - SEV ioctl parameters * diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 3a0a305c8c..3615a269d3 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -575,6 +575,33 @@ struct vfio_device_gfx_plane_info { #define VFIO_DEVICE_GET_GFX_DMABUF _IO(VFIO_TYPE, VFIO_BASE + 15) +/** + * VFIO_DEVICE_IOEVENTFD - _IOW(VFIO_TYPE, VFIO_BASE + 16, + * struct vfio_device_ioeventfd) + * + * Perform a write to the device at the specified device fd offset, with + * the specified data and width when the provided eventfd is triggered. + * vfio bus drivers may not support this for all regions, for all widths, + * or at all. vfio-pci currently only enables support for BAR regions, + * excluding the MSI-X vector table. + * + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_ioeventfd { + __u32 argsz; + __u32 flags; +#define VFIO_DEVICE_IOEVENTFD_8 (1 << 0) /* 1-byte write */ +#define VFIO_DEVICE_IOEVENTFD_16 (1 << 1) /* 2-byte write */ +#define VFIO_DEVICE_IOEVENTFD_32 (1 << 2) /* 4-byte write */ +#define VFIO_DEVICE_IOEVENTFD_64 (1 << 3) /* 8-byte write */ +#define VFIO_DEVICE_IOEVENTFD_SIZE_MASK (0xf) + __u64 offset; /* device fd offset of write */ + __u64 data; /* data to be written */ + __s32 fd; /* -1 for de-assignment */ +}; + +#define VFIO_DEVICE_IOEVENTFD _IO(VFIO_TYPE, VFIO_BASE + 16) + /* -------- API for Type1 VFIO IOMMU -------- */ /** diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index 8c93058100..b5dfb71f25 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -1,6 +1,7 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \ elfload.o linuxload.o uaccess.o uname.o \ - safe-syscall.o + safe-syscall.o $(TARGET_ABI_DIR)/signal.o \ + $(TARGET_ABI_DIR)/cpu_loop.o exit.o obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c new file mode 100644 index 0000000000..c97a646546 --- /dev/null +++ b/linux-user/aarch64/cpu_loop.c @@ -0,0 +1,182 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +#define get_user_code_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_code_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define get_user_data_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_data_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define put_user_data_u32(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap32(__x); \ + } \ + put_user_u32(__x, (gaddr)); \ + }) + +#define put_user_data_u16(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap16(__x); \ + } \ + put_user_u16(__x, (gaddr)); \ + }) + +/* AArch64 main loop */ +void cpu_loop(CPUARMState *env) +{ + CPUState *cs = CPU(arm_env_get_cpu(env)); + int trapnr, sig; + abi_long ret; + target_siginfo_t info; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_SWI: + ret = do_syscall(env, + env->xregs[8], + env->xregs[0], + env->xregs[1], + env->xregs[2], + env->xregs[3], + env->xregs[4], + env->xregs[5], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->xregs[0] = ret; + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_UDEF: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->exception.vaddress; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_DEBUG: + case EXCP_BKPT: + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_SEMIHOST: + env->xregs[0] = do_arm_semihosting(env); + 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: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + abort(); + } + process_pending_signals(env); + /* Exception return on AArch64 always clears the exclusive monitor, + * so any return to running guest code implies this. + */ + env->exclusive_addr = -1; + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + int i; + + if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { + fprintf(stderr, + "The selected ARM CPU does not support 64 bit mode\n"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < 31; i++) { + env->xregs[i] = regs->regs[i]; + } + env->pc = regs->pc; + env->xregs[31] = regs->sp; +#ifdef TARGET_WORDS_BIGENDIAN + env->cp15.sctlr_el[1] |= SCTLR_E0E; + for (i = 1; i < 4; ++i) { + env->cp15.sctlr_el[i] |= SCTLR_EE; + } +#endif + + ts->stack_base = info->start_stack; + ts->heap_base = info->brk; + /* This will be filled in on the first SYS_HEAPINFO call. */ + ts->heap_limit = 0; +} diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c new file mode 100644 index 0000000000..07fedfc33c --- /dev/null +++ b/linux-user/aarch64/signal.c @@ -0,0 +1,576 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + uint64_t fault_address; + /* AArch64 registers */ + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; + /* 4K reserved for FP/SIMD state and future expansion */ + char __reserved[4096] __attribute__((__aligned__(16))); +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + target_sigset_t tuc_sigmask; + /* glibc uses a 1024-bit sigset_t */ + char __unused[1024 / 8 - sizeof(target_sigset_t)]; + /* last for future expansion */ + struct target_sigcontext tuc_mcontext; +}; + +/* + * Header to be used at the beginning of structures extending the user + * context. Such structures must be placed after the rt_sigframe on the stack + * and be 16-byte aligned. The last structure must be a dummy one with the + * magic and size set to 0. + */ +struct target_aarch64_ctx { + uint32_t magic; + uint32_t size; +}; + +#define TARGET_FPSIMD_MAGIC 0x46508001 + +struct target_fpsimd_context { + struct target_aarch64_ctx head; + uint32_t fpsr; + uint32_t fpcr; + uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */ +}; + +#define TARGET_EXTRA_MAGIC 0x45585401 + +struct target_extra_context { + struct target_aarch64_ctx head; + uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */ + uint32_t size; /* size in bytes of the extra space */ + uint32_t reserved[3]; +}; + +#define TARGET_SVE_MAGIC 0x53564501 + +struct target_sve_context { + struct target_aarch64_ctx head; + uint16_t vl; + uint16_t reserved[3]; + /* The actual SVE data immediately follows. It is layed out + * according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of + * the original struct pointer. + */ +}; + +#define TARGET_SVE_VQ_BYTES 16 + +#define TARGET_SVE_SIG_ZREG_SIZE(VQ) ((VQ) * TARGET_SVE_VQ_BYTES) +#define TARGET_SVE_SIG_PREG_SIZE(VQ) ((VQ) * (TARGET_SVE_VQ_BYTES / 8)) + +#define TARGET_SVE_SIG_REGS_OFFSET \ + QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES) +#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \ + (TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N)) +#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \ + (TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N)) +#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \ + (TARGET_SVE_SIG_PREG_OFFSET(VQ, 16)) +#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \ + (TARGET_SVE_SIG_PREG_OFFSET(VQ, 17)) + +struct target_rt_sigframe { + struct target_siginfo info; + struct target_ucontext uc; +}; + +struct target_rt_frame_record { + uint64_t fp; + uint64_t lr; + uint32_t tramp[2]; +}; + +static void target_setup_general_frame(struct target_rt_sigframe *sf, + CPUARMState *env, target_sigset_t *set) +{ + int i; + + __put_user(0, &sf->uc.tuc_flags); + __put_user(0, &sf->uc.tuc_link); + + target_save_altstack(&sf->uc.tuc_stack, env); + + for (i = 0; i < 31; i++) { + __put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); + } + __put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); + __put_user(env->pc, &sf->uc.tuc_mcontext.pc); + __put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate); + + __put_user(env->exception.vaddress, &sf->uc.tuc_mcontext.fault_address); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]); + } +} + +static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd, + CPUARMState *env) +{ + int i; + + __put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic); + __put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size); + __put_user(vfp_get_fpsr(env), &fpsimd->fpsr); + __put_user(vfp_get_fpcr(env), &fpsimd->fpcr); + + for (i = 0; i < 32; i++) { + uint64_t *q = aa64_vfp_qreg(env, i); +#ifdef TARGET_WORDS_BIGENDIAN + __put_user(q[0], &fpsimd->vregs[i * 2 + 1]); + __put_user(q[1], &fpsimd->vregs[i * 2]); +#else + __put_user(q[0], &fpsimd->vregs[i * 2]); + __put_user(q[1], &fpsimd->vregs[i * 2 + 1]); +#endif + } +} + +static void target_setup_extra_record(struct target_extra_context *extra, + uint64_t datap, uint32_t extra_size) +{ + __put_user(TARGET_EXTRA_MAGIC, &extra->head.magic); + __put_user(sizeof(struct target_extra_context), &extra->head.size); + __put_user(datap, &extra->datap); + __put_user(extra_size, &extra->size); +} + +static void target_setup_end_record(struct target_aarch64_ctx *end) +{ + __put_user(0, &end->magic); + __put_user(0, &end->size); +} + +static void target_setup_sve_record(struct target_sve_context *sve, + CPUARMState *env, int vq, int size) +{ + int i, j; + + __put_user(TARGET_SVE_MAGIC, &sve->head.magic); + __put_user(size, &sve->head.size); + __put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl); + + /* Note that SVE regs are stored as a byte stream, with each byte element + * at a subsequent address. This corresponds to a little-endian store + * of our 64-bit hunks. + */ + for (i = 0; i < 32; ++i) { + uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __put_user_e(env->vfp.zregs[i].d[j], z + j, le); + } + } + for (i = 0; i <= 16; ++i) { + uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); + for (j = 0; j < vq; ++j) { + uint64_t r = env->vfp.pregs[i].p[j >> 2]; + __put_user_e(r >> ((j & 3) * 16), p + j, le); + } + } +} + +static void target_restore_general_frame(CPUARMState *env, + struct target_rt_sigframe *sf) +{ + sigset_t set; + uint64_t pstate; + int i; + + target_to_host_sigset(&set, &sf->uc.tuc_sigmask); + set_sigmask(&set); + + for (i = 0; i < 31; i++) { + __get_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); + } + + __get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); + __get_user(env->pc, &sf->uc.tuc_mcontext.pc); + __get_user(pstate, &sf->uc.tuc_mcontext.pstate); + pstate_write(env, pstate); +} + +static void target_restore_fpsimd_record(CPUARMState *env, + struct target_fpsimd_context *fpsimd) +{ + uint32_t fpsr, fpcr; + int i; + + __get_user(fpsr, &fpsimd->fpsr); + vfp_set_fpsr(env, fpsr); + __get_user(fpcr, &fpsimd->fpcr); + vfp_set_fpcr(env, fpcr); + + for (i = 0; i < 32; i++) { + uint64_t *q = aa64_vfp_qreg(env, i); +#ifdef TARGET_WORDS_BIGENDIAN + __get_user(q[0], &fpsimd->vregs[i * 2 + 1]); + __get_user(q[1], &fpsimd->vregs[i * 2]); +#else + __get_user(q[0], &fpsimd->vregs[i * 2]); + __get_user(q[1], &fpsimd->vregs[i * 2 + 1]); +#endif + } +} + +static void target_restore_sve_record(CPUARMState *env, + struct target_sve_context *sve, int vq) +{ + int i, j; + + /* Note that SVE regs are stored as a byte stream, with each byte element + * at a subsequent address. This corresponds to a little-endian load + * of our 64-bit hunks. + */ + for (i = 0; i < 32; ++i) { + uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); + for (j = 0; j < vq * 2; ++j) { + __get_user_e(env->vfp.zregs[i].d[j], z + j, le); + } + } + for (i = 0; i <= 16; ++i) { + uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); + for (j = 0; j < vq; ++j) { + uint16_t r; + __get_user_e(r, p + j, le); + if (j & 3) { + env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16); + } else { + env->vfp.pregs[i].p[j >> 2] = r; + } + } + } +} + +static int target_restore_sigframe(CPUARMState *env, + struct target_rt_sigframe *sf) +{ + struct target_aarch64_ctx *ctx, *extra = NULL; + struct target_fpsimd_context *fpsimd = NULL; + struct target_sve_context *sve = NULL; + uint64_t extra_datap = 0; + bool used_extra = false; + bool err = false; + int vq = 0, sve_size = 0; + + target_restore_general_frame(env, sf); + + ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved; + while (ctx) { + uint32_t magic, size, extra_size; + + __get_user(magic, &ctx->magic); + __get_user(size, &ctx->size); + switch (magic) { + case 0: + if (size != 0) { + err = true; + goto exit; + } + if (used_extra) { + ctx = NULL; + } else { + ctx = extra; + used_extra = true; + } + continue; + + case TARGET_FPSIMD_MAGIC: + if (fpsimd || size != sizeof(struct target_fpsimd_context)) { + err = true; + goto exit; + } + fpsimd = (struct target_fpsimd_context *)ctx; + break; + + case TARGET_SVE_MAGIC: + if (arm_feature(env, ARM_FEATURE_SVE)) { + vq = (env->vfp.zcr_el[1] & 0xf) + 1; + sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); + if (!sve && size == sve_size) { + sve = (struct target_sve_context *)ctx; + break; + } + } + err = true; + goto exit; + + case TARGET_EXTRA_MAGIC: + if (extra || size != sizeof(struct target_extra_context)) { + err = true; + goto exit; + } + __get_user(extra_datap, + &((struct target_extra_context *)ctx)->datap); + __get_user(extra_size, + &((struct target_extra_context *)ctx)->size); + extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0); + break; + + default: + /* Unknown record -- we certainly didn't generate it. + * Did we in fact get out of sync? + */ + err = true; + goto exit; + } + ctx = (void *)ctx + size; + } + + /* Require FPSIMD always. */ + if (fpsimd) { + target_restore_fpsimd_record(env, fpsimd); + } else { + err = true; + } + + /* SVE data, if present, overwrites FPSIMD data. */ + if (sve) { + target_restore_sve_record(env, sve, vq); + } + + exit: + unlock_user(extra, extra_datap, 0); + return err; +} + +static abi_ulong get_sigframe(struct target_sigaction *ka, + CPUARMState *env, int size) +{ + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(env), ka); + + sp = (sp - size) & ~15; + + return sp; +} + +typedef struct { + int total_size; + int extra_base; + int extra_size; + int std_end_ofs; + int extra_ofs; + int extra_end_ofs; +} target_sigframe_layout; + +static int alloc_sigframe_space(int this_size, target_sigframe_layout *l) +{ + /* Make sure there will always be space for the end marker. */ + const int std_size = sizeof(struct target_rt_sigframe) + - sizeof(struct target_aarch64_ctx); + int this_loc = l->total_size; + + if (l->extra_base) { + /* Once we have begun an extra space, all allocations go there. */ + l->extra_size += this_size; + } else if (this_size + this_loc > std_size) { + /* This allocation does not fit in the standard space. */ + /* Allocate the extra record. */ + l->extra_ofs = this_loc; + l->total_size += sizeof(struct target_extra_context); + + /* Allocate the standard end record. */ + l->std_end_ofs = l->total_size; + l->total_size += sizeof(struct target_aarch64_ctx); + + /* Allocate the requested record. */ + l->extra_base = this_loc = l->total_size; + l->extra_size = this_size; + } + l->total_size += this_size; + + return this_loc; +} + +static void target_setup_frame(int usig, struct target_sigaction *ka, + target_siginfo_t *info, target_sigset_t *set, + CPUARMState *env) +{ + target_sigframe_layout layout = { + /* Begin with the size pointing to the reserved space. */ + .total_size = offsetof(struct target_rt_sigframe, + uc.tuc_mcontext.__reserved), + }; + int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0; + struct target_rt_sigframe *frame; + struct target_rt_frame_record *fr; + abi_ulong frame_addr, return_addr; + + /* FPSIMD record is always in the standard space. */ + fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context), + &layout); + + /* SVE state needs saving only if it exists. */ + if (arm_feature(env, ARM_FEATURE_SVE)) { + vq = (env->vfp.zcr_el[1] & 0xf) + 1; + sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); + sve_ofs = alloc_sigframe_space(sve_size, &layout); + } + + if (layout.extra_ofs) { + /* Reserve space for the extra end marker. The standard end marker + * will have been allocated when we allocated the extra record. + */ + layout.extra_end_ofs + = alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout); + } else { + /* Reserve space for the standard end marker. + * Do not use alloc_sigframe_space because we cheat + * std_size therein to reserve space for this. + */ + layout.std_end_ofs = layout.total_size; + layout.total_size += sizeof(struct target_aarch64_ctx); + } + + /* We must always provide at least the standard 4K reserved space, + * even if we don't use all of it (this is part of the ABI) + */ + layout.total_size = MAX(layout.total_size, + sizeof(struct target_rt_sigframe)); + + /* Reserve space for the return code. On a real system this would + * be within the VDSO. So, despite the name this is not a "real" + * record within the frame. + */ + fr_ofs = layout.total_size; + layout.total_size += sizeof(struct target_rt_frame_record); + + frame_addr = get_sigframe(ka, env, layout.total_size); + trace_user_setup_frame(env, frame_addr); + frame = lock_user(VERIFY_WRITE, frame_addr, layout.total_size, 0); + if (!frame) { + goto give_sigsegv; + } + + target_setup_general_frame(frame, env, set); + target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env); + target_setup_end_record((void *)frame + layout.std_end_ofs); + if (layout.extra_ofs) { + target_setup_extra_record((void *)frame + layout.extra_ofs, + frame_addr + layout.extra_base, + layout.extra_size); + target_setup_end_record((void *)frame + layout.extra_end_ofs); + } + if (sve_ofs) { + target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size); + } + + /* Set up the stack frame for unwinding. */ + fr = (void *)frame + fr_ofs; + __put_user(env->xregs[29], &fr->fp); + __put_user(env->xregs[30], &fr->lr); + + if (ka->sa_flags & TARGET_SA_RESTORER) { + return_addr = ka->sa_restorer; + } else { + /* + * mov x8,#__NR_rt_sigreturn; svc #0 + * Since these are instructions they need to be put as little-endian + * regardless of target default or current CPU endianness. + */ + __put_user_e(0xd2801168, &fr->tramp[0], le); + __put_user_e(0xd4000001, &fr->tramp[1], le); + return_addr = frame_addr + fr_ofs + + offsetof(struct target_rt_frame_record, tramp); + } + env->xregs[0] = usig; + env->xregs[31] = frame_addr; + env->xregs[29] = frame_addr + fr_ofs; + env->pc = ka->_sa_handler; + env->xregs[30] = return_addr; + if (info) { + tswap_siginfo(&frame->info, info); + env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->xregs[2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + } + + unlock_user(frame, frame_addr, layout.total_size); + return; + + give_sigsegv: + unlock_user(frame, frame_addr, layout.total_size); + force_sigsegv(usig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, target_sigset_t *set, + CPUARMState *env) +{ + target_setup_frame(sig, ka, info, set, env); +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *env) +{ + target_setup_frame(sig, ka, 0, set, env); +} + +long do_rt_sigreturn(CPUARMState *env) +{ + struct target_rt_sigframe *frame = NULL; + abi_ulong frame_addr = env->xregs[31]; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (frame_addr & 15) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + if (target_restore_sigframe(env, frame)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_sigreturn(CPUARMState *env) +{ + return do_rt_sigreturn(env); +} diff --git a/linux-user/aarch64/sockbits.h b/linux-user/aarch64/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/aarch64/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/aarch64/target_cpu.h b/linux-user/aarch64/target_cpu.h index 777ce29f16..a021c95fa4 100644 --- a/linux-user/aarch64/target_cpu.h +++ b/linux-user/aarch64/target_cpu.h @@ -35,4 +35,8 @@ static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) env->cp15.tpidr_el[0] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) +{ + return state->xregs[31]; +} #endif diff --git a/linux-user/aarch64/target_fcntl.h b/linux-user/aarch64/target_fcntl.h new file mode 100644 index 0000000000..efdf6e5f05 --- /dev/null +++ b/linux-user/aarch64/target_fcntl.h @@ -0,0 +1,16 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef AARCH64_TARGET_FCNTL_H +#define AARCH64_TARGET_FCNTL_H + +#define TARGET_O_DIRECTORY 040000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ +#define TARGET_O_DIRECT 0200000 /* direct disk access hint */ + +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/aarch64/target_signal.h b/linux-user/aarch64/target_signal.h index e66367cac1..ddd73169f0 100644 --- a/linux-user/aarch64/target_signal.h +++ b/linux-user/aarch64/target_signal.h @@ -1,8 +1,6 @@ #ifndef AARCH64_TARGET_SIGNAL_H #define AARCH64_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,9 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) -{ - return state->xregs[31]; -} +#include "../generic/signal.h" +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* AARCH64_TARGET_SIGNAL_H */ diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c new file mode 100644 index 0000000000..c1a98c8cbf --- /dev/null +++ b/linux-user/alpha/cpu_loop.c @@ -0,0 +1,225 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void cpu_loop(CPUAlphaState *env) +{ + CPUState *cs = CPU(alpha_env_get_cpu(env)); + int trapnr; + target_siginfo_t info; + abi_long sysret; + + while (1) { + bool arch_interrupt = true; + + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_RESET: + fprintf(stderr, "Reset requested. Exit\n"); + exit(EXIT_FAILURE); + break; + case EXCP_MCHK: + fprintf(stderr, "Machine check exception. Exit\n"); + exit(EXIT_FAILURE); + break; + case EXCP_SMP_INTERRUPT: + case EXCP_CLK_INTERRUPT: + case EXCP_DEV_INTERRUPT: + fprintf(stderr, "External interrupt. Exit\n"); + exit(EXIT_FAILURE); + break; + case EXCP_MMFAULT: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID + ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR); + info._sifields._sigfault._addr = env->trap_arg0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_UNALIGN: + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_BUS_ADRALN; + info._sifields._sigfault._addr = env->trap_arg0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_OPCDEC: + do_sigill: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ARITH: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_FLTINV; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_FEN: + /* No-op. Linux simply re-enables the FPU. */ + break; + case EXCP_CALL_PAL: + switch (env->error_code) { + case 0x80: + /* BPT */ + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case 0x81: + /* BUGCHK */ + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case 0x83: + /* CALLSYS */ + trapnr = env->ir[IR_V0]; + sysret = do_syscall(env, trapnr, + env->ir[IR_A0], env->ir[IR_A1], + env->ir[IR_A2], env->ir[IR_A3], + env->ir[IR_A4], env->ir[IR_A5], + 0, 0); + if (sysret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + break; + } + if (sysret == -TARGET_QEMU_ESIGRETURN) { + break; + } + /* Syscall writes 0 to V0 to bypass error check, similar + to how this is handled internal to Linux kernel. + (Ab)use trapnr temporarily as boolean indicating error. */ + trapnr = (env->ir[IR_V0] != 0 && sysret < 0); + env->ir[IR_V0] = (trapnr ? -sysret : sysret); + env->ir[IR_A3] = trapnr; + break; + case 0x86: + /* IMB */ + /* ??? We can probably elide the code using page_unprotect + that is checking for self-modifying code. Instead we + could simply call tb_flush here. Until we work out the + changes required to turn off the extra write protection, + this can be a no-op. */ + break; + case 0x9E: + /* RDUNIQUE */ + /* Handled in the translator for usermode. */ + abort(); + case 0x9F: + /* WRUNIQUE */ + /* Handled in the translator for usermode. */ + abort(); + case 0xAA: + /* GENTRAP */ + info.si_signo = TARGET_SIGFPE; + switch (env->ir[IR_A0]) { + case TARGET_GEN_INTOVF: + info.si_code = TARGET_FPE_INTOVF; + break; + case TARGET_GEN_INTDIV: + info.si_code = TARGET_FPE_INTDIV; + break; + case TARGET_GEN_FLTOVF: + info.si_code = TARGET_FPE_FLTOVF; + break; + case TARGET_GEN_FLTUND: + info.si_code = TARGET_FPE_FLTUND; + break; + case TARGET_GEN_FLTINV: + info.si_code = TARGET_FPE_FLTINV; + break; + case TARGET_GEN_FLTINE: + info.si_code = TARGET_FPE_FLTRES; + break; + case TARGET_GEN_ROPRAND: + info.si_code = 0; + break; + default: + info.si_signo = TARGET_SIGTRAP; + info.si_code = 0; + break; + } + info.si_errno = 0; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + default: + goto do_sigill; + } + break; + case EXCP_DEBUG: + info.si_signo = gdb_handlesig(cs, TARGET_SIGTRAP); + if (info.si_signo) { + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else { + arch_interrupt = false; + } + break; + case EXCP_INTERRUPT: + /* Just indicate that signals should be handled asap. */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + arch_interrupt = false; + break; + default: + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + + /* Most of the traps imply a transition through PALcode, which + implies an REI instruction has been executed. Which means + that RX and LOCK_ADDR should be cleared. But there are a + few exceptions for traps internal to QEMU. */ + if (arch_interrupt) { + env->flags &= ~ENV_FLAG_RX_FLAG; + env->lock_addr = -1; + } + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + + for(i = 0; i < 28; i++) { + env->ir[i] = ((abi_ulong *)regs)[i]; + } + env->ir[IR_SP] = regs->usp; + env->pc = regs->pc; +} diff --git a/linux-user/alpha/signal.c b/linux-user/alpha/signal.c new file mode 100644 index 0000000000..c5c27ce084 --- /dev/null +++ b/linux-user/alpha/signal.c @@ -0,0 +1,274 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_long sc_onstack; + abi_long sc_mask; + abi_long sc_pc; + abi_long sc_ps; + abi_long sc_regs[32]; + abi_long sc_ownedfp; + abi_long sc_fpregs[32]; + abi_ulong sc_fpcr; + abi_ulong sc_fp_control; + abi_ulong sc_reserved1; + abi_ulong sc_reserved2; + abi_ulong sc_ssize; + abi_ulong sc_sbase; + abi_ulong sc_traparg_a0; + abi_ulong sc_traparg_a1; + abi_ulong sc_traparg_a2; + abi_ulong sc_fp_trap_pc; + abi_ulong sc_fp_trigger_sum; + abi_ulong sc_fp_trigger_inst; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + abi_ulong tuc_osf_sigmask; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_sigframe { + struct target_sigcontext sc; + unsigned int retcode[3]; +}; + +struct target_rt_sigframe { + target_siginfo_t info; + struct target_ucontext uc; + unsigned int retcode[3]; +}; + +#define INSN_MOV_R30_R16 0x47fe0410 +#define INSN_LDI_R0 0x201f0000 +#define INSN_CALLSYS 0x00000083 + +static void setup_sigcontext(struct target_sigcontext *sc, CPUAlphaState *env, + abi_ulong frame_addr, target_sigset_t *set) +{ + int i; + + __put_user(on_sig_stack(frame_addr), &sc->sc_onstack); + __put_user(set->sig[0], &sc->sc_mask); + __put_user(env->pc, &sc->sc_pc); + __put_user(8, &sc->sc_ps); + + for (i = 0; i < 31; ++i) { + __put_user(env->ir[i], &sc->sc_regs[i]); + } + __put_user(0, &sc->sc_regs[31]); + + for (i = 0; i < 31; ++i) { + __put_user(env->fir[i], &sc->sc_fpregs[i]); + } + __put_user(0, &sc->sc_fpregs[31]); + __put_user(cpu_alpha_load_fpcr(env), &sc->sc_fpcr); + + __put_user(0, &sc->sc_traparg_a0); /* FIXME */ + __put_user(0, &sc->sc_traparg_a1); /* FIXME */ + __put_user(0, &sc->sc_traparg_a2); /* FIXME */ +} + +static void restore_sigcontext(CPUAlphaState *env, + struct target_sigcontext *sc) +{ + uint64_t fpcr; + int i; + + __get_user(env->pc, &sc->sc_pc); + + for (i = 0; i < 31; ++i) { + __get_user(env->ir[i], &sc->sc_regs[i]); + } + for (i = 0; i < 31; ++i) { + __get_user(env->fir[i], &sc->sc_fpregs[i]); + } + + __get_user(fpcr, &sc->sc_fpcr); + cpu_alpha_store_fpcr(env, fpcr); +} + +static inline abi_ulong get_sigframe(struct target_sigaction *sa, + CPUAlphaState *env, + unsigned long framesize) +{ + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(env), sa); + + return (sp - framesize) & -32; +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUAlphaState *env) +{ + abi_ulong frame_addr, r26; + struct target_sigframe *frame; + int err = 0; + + 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)) { + goto give_sigsegv; + } + + setup_sigcontext(&frame->sc, env, frame_addr, set); + + if (ka->sa_restorer) { + r26 = ka->sa_restorer; + } else { + __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); + __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn, + &frame->retcode[1]); + __put_user(INSN_CALLSYS, &frame->retcode[2]); + /* imb() */ + r26 = frame_addr + offsetof(struct target_sigframe, retcode); + } + + unlock_user_struct(frame, frame_addr, 1); + + if (err) { +give_sigsegv: + force_sigsegv(sig); + return; + } + + env->ir[IR_RA] = r26; + env->ir[IR_PV] = env->pc = ka->_sa_handler; + env->ir[IR_A0] = sig; + env->ir[IR_A1] = 0; + env->ir[IR_A2] = frame_addr + offsetof(struct target_sigframe, sc); + env->ir[IR_SP] = frame_addr; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUAlphaState *env) +{ + abi_ulong frame_addr, r26; + struct target_rt_sigframe *frame; + int i, err = 0; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + __put_user(set->sig[0], &frame->uc.tuc_osf_sigmask); + + target_save_altstack(&frame->uc.tuc_stack, env); + + setup_sigcontext(&frame->uc.tuc_mcontext, env, frame_addr, set); + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + if (ka->sa_restorer) { + r26 = ka->sa_restorer; + } else { + __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); + __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn, + &frame->retcode[1]); + __put_user(INSN_CALLSYS, &frame->retcode[2]); + /* imb(); */ + r26 = frame_addr + offsetof(struct target_sigframe, retcode); + } + + if (err) { +give_sigsegv: + force_sigsegv(sig); + return; + } + + env->ir[IR_RA] = r26; + env->ir[IR_PV] = env->pc = ka->_sa_handler; + env->ir[IR_A0] = sig; + env->ir[IR_A1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->ir[IR_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + env->ir[IR_SP] = frame_addr; +} + +long do_sigreturn(CPUAlphaState *env) +{ + struct target_sigcontext *sc; + abi_ulong sc_addr = env->ir[IR_A0]; + target_sigset_t target_set; + sigset_t set; + + if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) { + goto badframe; + } + + target_sigemptyset(&target_set); + __get_user(target_set.sig[0], &sc->sc_mask); + + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + restore_sigcontext(env, sc); + unlock_user_struct(sc, sc_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUAlphaState *env) +{ + abi_ulong frame_addr = env->ir[IR_A0]; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->ir[IR_SP]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/alpha/sockbits.h b/linux-user/alpha/sockbits.h new file mode 100644 index 0000000000..d54dc98c09 --- /dev/null +++ b/linux-user/alpha/sockbits.h @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef ALPHA_SOCKBITS_H +#define ALPHA_SOCKBITS_H + +/* For setsockopt(2) */ +#define TARGET_SOL_SOCKET 0xffff + +#define TARGET_SO_DEBUG 0x0001 +#define TARGET_SO_REUSEADDR 0x0004 +#define TARGET_SO_KEEPALIVE 0x0008 +#define TARGET_SO_DONTROUTE 0x0010 +#define TARGET_SO_BROADCAST 0x0020 +#define TARGET_SO_LINGER 0x0080 +#define TARGET_SO_OOBINLINE 0x0100 +#define TARGET_SO_REUSEPORT 0x0200 + +#define TARGET_SO_TYPE 0x1008 +#define TARGET_SO_ERROR 0x1007 +#define TARGET_SO_SNDBUF 0x1001 +#define TARGET_SO_RCVBUF 0x1002 +#define TARGET_SO_SNDBUFFORCE 0x100a +#define TARGET_SO_RCVBUFFORCE 0x100b +#define TARGET_SO_RCVLOWAT 0x1010 +#define TARGET_SO_SNDLOWAT 0x1011 +#define TARGET_SO_RCVTIMEO 0x1012 +#define TARGET_SO_SNDTIMEO 0x1013 +#define TARGET_SO_ACCEPTCONN 0x1014 +#define TARGET_SO_PROTOCOL 0x1028 +#define TARGET_SO_DOMAIN 0x1029 + +/* linux-specific, might as well be the same as on i386 */ +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_BSDCOMPAT 14 + +#define TARGET_SO_PASSCRED 17 +#define TARGET_SO_PEERCRED 18 +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + +#define TARGET_SO_PEERSEC 30 +#define TARGET_SO_PASSSEC 34 +#define TARGET_SO_TIMESTAMPNS 35 +#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 19 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 20 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 21 + +#define TARGET_SO_MARK 36 + +#define TARGET_SO_TIMESTAMPING 37 +#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING + +#define TARGET_SO_RXQ_OVFL 40 + +#define TARGET_SO_WIFI_STATUS 41 +#define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS +#define TARGET_SO_PEEK_OFF 42 + +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define TARGET_SO_NOFCS 43 + +/* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. + */ +#define TARGET_SOCK_NONBLOCK 0x40000000 + +#endif diff --git a/linux-user/alpha/syscall_nr.h b/linux-user/alpha/syscall_nr.h index 00e14bb6b3..fbb1ed288b 100644 --- a/linux-user/alpha/syscall_nr.h +++ b/linux-user/alpha/syscall_nr.h @@ -343,9 +343,9 @@ #define TARGET_NR_io_cancel 402 #define TARGET_NR_exit_group 405 #define TARGET_NR_lookup_dcookie 406 -#define TARGET_NR_sys_epoll_create 407 -#define TARGET_NR_sys_epoll_ctl 408 -#define TARGET_NR_sys_epoll_wait 409 +#define TARGET_NR_epoll_create 407 +#define TARGET_NR_epoll_ctl 408 +#define TARGET_NR_epoll_wait 409 #define TARGET_NR_remap_file_pages 410 #define TARGET_NR_set_tid_address 411 #define TARGET_NR_restart_syscall 412 diff --git a/linux-user/alpha/target_cpu.h b/linux-user/alpha/target_cpu.h index ad124da7c0..ac4d255ae7 100644 --- a/linux-user/alpha/target_cpu.h +++ b/linux-user/alpha/target_cpu.h @@ -33,4 +33,8 @@ static inline void cpu_set_tls(CPUAlphaState *env, target_ulong newtls) env->unique = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state) +{ + return state->ir[IR_SP]; +} #endif diff --git a/linux-user/alpha/target_fcntl.h b/linux-user/alpha/target_fcntl.h new file mode 100644 index 0000000000..2617e73472 --- /dev/null +++ b/linux-user/alpha/target_fcntl.h @@ -0,0 +1,40 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef ALPHA_TARGET_FCNTL_H +#define ALPHA_TARGET_FCNTL_H + +#define TARGET_O_NONBLOCK 04 +#define TARGET_O_APPEND 010 +#define TARGET_O_CREAT 01000 /* not fcntl */ +#define TARGET_O_TRUNC 02000 /* not fcntl */ +#define TARGET_O_EXCL 04000 /* not fcntl */ +#define TARGET_O_NOCTTY 010000 /* not fcntl */ +#define TARGET_O_DSYNC 040000 +#define TARGET_O_LARGEFILE 0 /* not necessary, always 64-bit */ +#define TARGET_O_DIRECTORY 0100000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0200000 /* don't follow links */ +#define TARGET_O_DIRECT 02000000 /* direct disk access hint */ +#define TARGET_O_NOATIME 04000000 +#define TARGET_O_CLOEXEC 010000000 +#define TARGET___O_SYNC 020000000 +#define TARGET_O_PATH 040000000 + +#define TARGET_F_GETLK 7 +#define TARGET_F_SETLK 8 +#define TARGET_F_SETLKW 9 +#define TARGET_F_SETOWN 5 /* for sockets. */ +#define TARGET_F_GETOWN 6 /* for sockets. */ + +#define TARGET_F_RDLCK 1 +#define TARGET_F_WRLCK 2 +#define TARGET_F_UNLCK 8 +#define TARGET_F_EXLCK 16 +#define TARGET_F_SHLCK 32 + +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/alpha/target_signal.h b/linux-user/alpha/target_signal.h index f1ed00d50e..cd63d59fde 100644 --- a/linux-user/alpha/target_signal.h +++ b/linux-user/alpha/target_signal.h @@ -1,7 +1,42 @@ #ifndef ALPHA_TARGET_SIGNAL_H #define ALPHA_TARGET_SIGNAL_H -#include "cpu.h" +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGSTKFLT 7 /* actually SIGEMT */ +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGBUS 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGSYS 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGURG 16 +#define TARGET_SIGSTOP 17 +#define TARGET_SIGTSTP 18 +#define TARGET_SIGCONT 19 +#define TARGET_SIGCHLD 20 +#define TARGET_SIGTTIN 21 +#define TARGET_SIGTTOU 22 +#define TARGET_SIGIO 23 +#define TARGET_SIGXCPU 24 +#define TARGET_SIGXFSZ 25 +#define TARGET_SIGVTALRM 26 +#define TARGET_SIGPROF 27 +#define TARGET_SIGWINCH 28 +#define TARGET_SIGPWR 29 /* actually SIGINFO */ +#define TARGET_SIGUSR1 30 +#define TARGET_SIGUSR2 31 +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 1 +#define TARGET_SIG_UNBLOCK 2 +#define TARGET_SIG_SETMASK 3 /* this struct defines a stack used during syscall handling */ @@ -19,15 +54,17 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_ONSTACK 0x00000001 +#define TARGET_SA_RESTART 0x00000002 +#define TARGET_SA_NOCLDSTOP 0x00000004 +#define TARGET_SA_NODEFER 0x00000008 +#define TARGET_SA_RESETHAND 0x00000010 +#define TARGET_SA_NOCLDWAIT 0x00000020 /* not supported yet */ +#define TARGET_SA_SIGINFO 0x00000040 + #define TARGET_MINSIGSTKSZ 4096 #define TARGET_SIGSTKSZ 16384 -static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state) -{ - return state->ir[IR_SP]; -} - - /* From . */ #define TARGET_GEN_INTOVF -1 /* integer overflow */ #define TARGET_GEN_INTDIV -2 /* integer division by zero */ @@ -55,4 +92,5 @@ static inline abi_ulong get_sp_from_cpustate(CPUAlphaState *state) #define TARGET_GEN_SUBRNG6 -24 #define TARGET_GEN_SUBRNG7 -25 +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* ALPHA_TARGET_SIGNAL_H */ diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c new file mode 100644 index 0000000000..26928fbbb2 --- /dev/null +++ b/linux-user/arm/cpu_loop.c @@ -0,0 +1,459 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "elf.h" +#include "cpu_loop-common.h" + +#define get_user_code_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_code_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && bswap_code(arm_sctlr_b(env))) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define get_user_data_u32(x, gaddr, env) \ + ({ abi_long __r = get_user_u32((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap32(x); \ + } \ + __r; \ + }) + +#define get_user_data_u16(x, gaddr, env) \ + ({ abi_long __r = get_user_u16((x), (gaddr)); \ + if (!__r && arm_cpu_bswap_data(env)) { \ + (x) = bswap16(x); \ + } \ + __r; \ + }) + +#define put_user_data_u32(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap32(__x); \ + } \ + put_user_u32(__x, (gaddr)); \ + }) + +#define put_user_data_u16(x, gaddr, env) \ + ({ typeof(x) __x = (x); \ + if (arm_cpu_bswap_data(env)) { \ + __x = bswap16(__x); \ + } \ + put_user_u16(__x, (gaddr)); \ + }) + +/* Commpage handling -- there is no commpage for AArch64 */ + +/* + * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt + * Input: + * r0 = pointer to oldval + * r1 = pointer to newval + * r2 = pointer to target value + * + * Output: + * r0 = 0 if *ptr was changed, non-0 if no exchange happened + * C set if *ptr was changed, clear if no exchange happened + * + * Note segv's in kernel helpers are a bit tricky, we can set the + * data address sensibly but the PC address is just the entry point. + */ +static void arm_kernel_cmpxchg64_helper(CPUARMState *env) +{ + uint64_t oldval, newval, val; + uint32_t addr, cpsr; + target_siginfo_t info; + + /* Based on the 32 bit code in do_kernel_trap */ + + /* XXX: This only works between threads, not between processes. + It's probably possible to implement this with native host + operations. However things like ldrex/strex are much harder so + there's not much point trying. */ + start_exclusive(); + cpsr = cpsr_read(env); + addr = env->regs[2]; + + if (get_user_u64(oldval, env->regs[0])) { + env->exception.vaddress = env->regs[0]; + goto segv; + }; + + if (get_user_u64(newval, env->regs[1])) { + env->exception.vaddress = env->regs[1]; + goto segv; + }; + + if (get_user_u64(val, addr)) { + env->exception.vaddress = addr; + goto segv; + } + + if (val == oldval) { + val = newval; + + if (put_user_u64(val, addr)) { + env->exception.vaddress = addr; + goto segv; + }; + + env->regs[0] = 0; + cpsr |= CPSR_C; + } else { + env->regs[0] = -1; + cpsr &= ~CPSR_C; + } + cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); + end_exclusive(); + return; + +segv: + end_exclusive(); + /* We get the PC of the entry address - which is as good as anything, + on a real kernel what you get depends on which mode it uses. */ + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->exception.vaddress; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); +} + +/* Handle a jump to the kernel code page. */ +static int +do_kernel_trap(CPUARMState *env) +{ + uint32_t addr; + uint32_t cpsr; + uint32_t val; + + switch (env->regs[15]) { + case 0xffff0fa0: /* __kernel_memory_barrier */ + /* ??? No-op. Will need to do better for SMP. */ + break; + case 0xffff0fc0: /* __kernel_cmpxchg */ + /* XXX: This only works between threads, not between processes. + It's probably possible to implement this with native host + operations. However things like ldrex/strex are much harder so + there's not much point trying. */ + start_exclusive(); + cpsr = cpsr_read(env); + addr = env->regs[2]; + /* FIXME: This should SEGV if the access fails. */ + if (get_user_u32(val, addr)) + val = ~env->regs[0]; + if (val == env->regs[0]) { + val = env->regs[1]; + /* FIXME: Check for segfaults. */ + put_user_u32(val, addr); + env->regs[0] = 0; + cpsr |= CPSR_C; + } else { + env->regs[0] = -1; + cpsr &= ~CPSR_C; + } + cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); + end_exclusive(); + break; + case 0xffff0fe0: /* __kernel_get_tls */ + env->regs[0] = cpu_get_tls(env); + break; + case 0xffff0f60: /* __kernel_cmpxchg64 */ + arm_kernel_cmpxchg64_helper(env); + break; + + default: + return 1; + } + /* Jump back to the caller. */ + addr = env->regs[14]; + if (addr & 1) { + env->thumb = 1; + addr &= ~1; + } + env->regs[15] = addr; + + return 0; +} + +void cpu_loop(CPUARMState *env) +{ + CPUState *cs = CPU(arm_env_get_cpu(env)); + int trapnr; + unsigned int n, insn; + target_siginfo_t info; + uint32_t addr; + abi_ulong ret; + + for(;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch(trapnr) { + case EXCP_UDEF: + case EXCP_NOCP: + case EXCP_INVSTATE: + { + TaskState *ts = cs->opaque; + uint32_t opcode; + int rc; + + /* we handle the FPU emulation here, as Linux */ + /* we get the opcode */ + /* FIXME - what to do if get_user() fails? */ + get_user_code_u32(opcode, env->regs[15], env); + + rc = EmulateAll(opcode, &ts->fpa, env); + if (rc == 0) { /* illegal instruction */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->regs[15]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else if (rc < 0) { /* FP exception */ + int arm_fpe=0; + + /* translate softfloat flags to FPSR flags */ + if (-rc & float_flag_invalid) + arm_fpe |= BIT_IOC; + if (-rc & float_flag_divbyzero) + arm_fpe |= BIT_DZC; + if (-rc & float_flag_overflow) + arm_fpe |= BIT_OFC; + if (-rc & float_flag_underflow) + arm_fpe |= BIT_UFC; + if (-rc & float_flag_inexact) + arm_fpe |= BIT_IXC; + + FPSR fpsr = ts->fpa.fpsr; + //printf("fpsr 0x%x, arm_fpe 0x%x\n",fpsr,arm_fpe); + + if (fpsr & (arm_fpe << 16)) { /* exception enabled? */ + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + + /* ordered by priority, least first */ + if (arm_fpe & BIT_IXC) info.si_code = TARGET_FPE_FLTRES; + if (arm_fpe & BIT_UFC) info.si_code = TARGET_FPE_FLTUND; + if (arm_fpe & BIT_OFC) info.si_code = TARGET_FPE_FLTOVF; + if (arm_fpe & BIT_DZC) info.si_code = TARGET_FPE_FLTDIV; + if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV; + + info._sifields._sigfault._addr = env->regs[15]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else { + env->regs[15] += 4; + } + + /* accumulate unenabled exceptions */ + if ((!(fpsr & BIT_IXE)) && (arm_fpe & BIT_IXC)) + fpsr |= BIT_IXC; + if ((!(fpsr & BIT_UFE)) && (arm_fpe & BIT_UFC)) + fpsr |= BIT_UFC; + if ((!(fpsr & BIT_OFE)) && (arm_fpe & BIT_OFC)) + fpsr |= BIT_OFC; + if ((!(fpsr & BIT_DZE)) && (arm_fpe & BIT_DZC)) + fpsr |= BIT_DZC; + if ((!(fpsr & BIT_IOE)) && (arm_fpe & BIT_IOC)) + fpsr |= BIT_IOC; + ts->fpa.fpsr=fpsr; + } else { /* everything OK */ + /* increment PC */ + env->regs[15] += 4; + } + } + break; + case EXCP_SWI: + case EXCP_BKPT: + { + env->eabi = 1; + /* system call */ + if (trapnr == EXCP_BKPT) { + if (env->thumb) { + /* FIXME - what to do if get_user() fails? */ + get_user_code_u16(insn, env->regs[15], env); + n = insn & 0xff; + env->regs[15] += 2; + } else { + /* FIXME - what to do if get_user() fails? */ + get_user_code_u32(insn, env->regs[15], env); + n = (insn & 0xf) | ((insn >> 4) & 0xff0); + env->regs[15] += 4; + } + } else { + if (env->thumb) { + /* FIXME - what to do if get_user() fails? */ + get_user_code_u16(insn, env->regs[15] - 2, env); + n = insn & 0xff; + } else { + /* FIXME - what to do if get_user() fails? */ + get_user_code_u32(insn, env->regs[15] - 4, env); + n = insn & 0xffffff; + } + } + + if (n == ARM_NR_cacheflush) { + /* nop */ + } else if (n == ARM_NR_semihosting + || n == ARM_NR_thumb_semihosting) { + env->regs[0] = do_arm_semihosting (env); + } else if (n == 0 || n >= ARM_SYSCALL_BASE || env->thumb) { + /* linux syscall */ + if (env->thumb || n == 0) { + n = env->regs[7]; + } else { + n -= ARM_SYSCALL_BASE; + env->eabi = 0; + } + if ( n > ARM_NR_BASE) { + switch (n) { + case ARM_NR_cacheflush: + /* nop */ + break; + case ARM_NR_set_tls: + cpu_set_tls(env, env->regs[0]); + env->regs[0] = 0; + break; + case ARM_NR_breakpoint: + env->regs[15] -= env->thumb ? 2 : 4; + goto excp_debug; + case ARM_NR_get_tls: + env->regs[0] = cpu_get_tls(env); + break; + default: + gemu_log("qemu: Unsupported ARM syscall: 0x%x\n", + n); + env->regs[0] = -TARGET_ENOSYS; + break; + } + } else { + ret = do_syscall(env, + n, + env->regs[0], + env->regs[1], + env->regs[2], + env->regs[3], + env->regs[4], + env->regs[5], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->regs[15] -= env->thumb ? 2 : 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[0] = ret; + } + } + } else { + goto error; + } + } + break; + case EXCP_SEMIHOST: + env->regs[0] = do_arm_semihosting(env); + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_PREFETCH_ABORT: + case EXCP_DATA_ABORT: + addr = env->exception.vaddress; + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = addr; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_DEBUG: + excp_debug: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_KERNEL_TRAP: + if (do_kernel_trap(env)) + goto error; + 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: + error: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + abort(); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + int i; + + cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, + CPSRWriteByInstr); + for(i = 0; i < 16; i++) { + env->regs[i] = regs->uregs[i]; + } +#ifdef TARGET_WORDS_BIGENDIAN + /* Enable BE8. */ + if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 + && (info->elf_flags & EF_ARM_BE8)) { + env->uncached_cpsr |= CPSR_E; + env->cp15.sctlr_el[1] |= SCTLR_E0E; + } else { + env->cp15.sctlr_el[1] |= SCTLR_B; + } +#endif + + ts->stack_base = info->start_stack; + ts->heap_base = info->brk; + /* This will be filled in on the first SYS_HEAPINFO call. */ + ts->heap_limit = 0; +} diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c new file mode 100644 index 0000000000..b0e753801b --- /dev/null +++ b/linux-user/arm/signal.c @@ -0,0 +1,835 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_ulong trap_no; + abi_ulong error_code; + abi_ulong oldmask; + abi_ulong arm_r0; + abi_ulong arm_r1; + abi_ulong arm_r2; + abi_ulong arm_r3; + abi_ulong arm_r4; + abi_ulong arm_r5; + abi_ulong arm_r6; + abi_ulong arm_r7; + abi_ulong arm_r8; + abi_ulong arm_r9; + abi_ulong arm_r10; + abi_ulong arm_fp; + abi_ulong arm_ip; + abi_ulong arm_sp; + abi_ulong arm_lr; + abi_ulong arm_pc; + abi_ulong arm_cpsr; + abi_ulong fault_address; +}; + +struct target_ucontext_v1 { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_ucontext_v2 { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ + char __unused[128 - sizeof(target_sigset_t)]; + abi_ulong tuc_regspace[128] __attribute__((__aligned__(8))); +}; + +struct target_user_vfp { + uint64_t fpregs[32]; + abi_ulong fpscr; +}; + +struct target_user_vfp_exc { + abi_ulong fpexc; + abi_ulong fpinst; + abi_ulong fpinst2; +}; + +struct target_vfp_sigframe { + abi_ulong magic; + abi_ulong size; + struct target_user_vfp ufp; + struct target_user_vfp_exc ufp_exc; +} __attribute__((__aligned__(8))); + +struct target_iwmmxt_sigframe { + abi_ulong magic; + abi_ulong size; + uint64_t regs[16]; + /* Note that not all the coprocessor control registers are stored here */ + uint32_t wcssf; + uint32_t wcasf; + uint32_t wcgr0; + uint32_t wcgr1; + uint32_t wcgr2; + uint32_t wcgr3; +} __attribute__((__aligned__(8))); + +#define TARGET_VFP_MAGIC 0x56465001 +#define TARGET_IWMMXT_MAGIC 0x12ef842a + +struct sigframe_v1 +{ + struct target_sigcontext sc; + abi_ulong extramask[TARGET_NSIG_WORDS-1]; + abi_ulong retcode[4]; +}; + +struct sigframe_v2 +{ + struct target_ucontext_v2 uc; + abi_ulong retcode[4]; +}; + +struct rt_sigframe_v1 +{ + abi_ulong pinfo; + abi_ulong puc; + struct target_siginfo info; + struct target_ucontext_v1 uc; + abi_ulong retcode[4]; +}; + +struct rt_sigframe_v2 +{ + struct target_siginfo info; + struct target_ucontext_v2 uc; + abi_ulong retcode[4]; +}; + +#define TARGET_CONFIG_CPU_32 1 + +/* + * For ARM syscalls, we encode the syscall number into the instruction. + */ +#define SWI_SYS_SIGRETURN (0xef000000|(TARGET_NR_sigreturn + ARM_SYSCALL_BASE)) +#define SWI_SYS_RT_SIGRETURN (0xef000000|(TARGET_NR_rt_sigreturn + ARM_SYSCALL_BASE)) + +/* + * For Thumb syscalls, we pass the syscall number via r7. We therefore + * need two 16-bit instructions. + */ +#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_sigreturn)) +#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_rt_sigreturn)) + +static const abi_ulong retcodes[4] = { + SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, + SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN +}; + +/* + * Stub needed to make sure the FD register (r9) contains the right + * value. + */ +static const unsigned long sigreturn_fdpic_codes[3] = { + 0xe59fc004, /* ldr r12, [pc, #4] to read function descriptor */ + 0xe59c9004, /* ldr r9, [r12, #4] to setup GOT */ + 0xe59cf000 /* ldr pc, [r12] to jump into restorer */ +}; + +static const unsigned long sigreturn_fdpic_thumb_codes[3] = { + 0xc008f8df, /* ldr r12, [pc, #8] to read function descriptor */ + 0x9004f8dc, /* ldr r9, [r12, #4] to setup GOT */ + 0xf000f8dc /* ldr pc, [r12] to jump into restorer */ +}; + +static inline int valid_user_regs(CPUARMState *regs) +{ + return 1; +} + +static void +setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ + CPUARMState *env, abi_ulong mask) +{ + __put_user(env->regs[0], &sc->arm_r0); + __put_user(env->regs[1], &sc->arm_r1); + __put_user(env->regs[2], &sc->arm_r2); + __put_user(env->regs[3], &sc->arm_r3); + __put_user(env->regs[4], &sc->arm_r4); + __put_user(env->regs[5], &sc->arm_r5); + __put_user(env->regs[6], &sc->arm_r6); + __put_user(env->regs[7], &sc->arm_r7); + __put_user(env->regs[8], &sc->arm_r8); + __put_user(env->regs[9], &sc->arm_r9); + __put_user(env->regs[10], &sc->arm_r10); + __put_user(env->regs[11], &sc->arm_fp); + __put_user(env->regs[12], &sc->arm_ip); + __put_user(env->regs[13], &sc->arm_sp); + __put_user(env->regs[14], &sc->arm_lr); + __put_user(env->regs[15], &sc->arm_pc); +#ifdef TARGET_CONFIG_CPU_32 + __put_user(cpsr_read(env), &sc->arm_cpsr); +#endif + + __put_user(/* current->thread.trap_no */ 0, &sc->trap_no); + __put_user(/* current->thread.error_code */ 0, &sc->error_code); + __put_user(/* current->thread.address */ 0, &sc->fault_address); + __put_user(mask, &sc->oldmask); +} + +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) +{ + unsigned long sp; + + sp = target_sigsp(get_sp_from_cpustate(regs), ka); + /* + * ATPCS B01 mandates 8-byte alignment + */ + return (sp - framesize) & ~7; +} + +static int +setup_return(CPUARMState *env, struct target_sigaction *ka, + abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) +{ + abi_ulong handler = 0; + abi_ulong handler_fdpic_GOT = 0; + abi_ulong retcode; + + int thumb; + int is_fdpic = info_is_fdpic(((TaskState *)thread_cpu->opaque)->info); + + if (is_fdpic) { + /* In FDPIC mode, ka->_sa_handler points to a function + * descriptor (FD). The first word contains the address of the + * handler. The second word contains the value of the PIC + * register (r9). */ + abi_ulong funcdesc_ptr = ka->_sa_handler; + if (get_user_ual(handler, funcdesc_ptr) + || get_user_ual(handler_fdpic_GOT, funcdesc_ptr + 4)) { + return 1; + } + } else { + handler = ka->_sa_handler; + } + + thumb = handler & 1; + + uint32_t cpsr = cpsr_read(env); + + cpsr &= ~CPSR_IT; + if (thumb) { + cpsr |= CPSR_T; + } else { + cpsr &= ~CPSR_T; + } + + if (ka->sa_flags & TARGET_SA_RESTORER) { + if (is_fdpic) { + /* For FDPIC we ensure that the restorer is called with a + * correct r9 value. For that we need to write code on + * the stack that sets r9 and jumps back to restorer + * value. + */ + if (thumb) { + __put_user(sigreturn_fdpic_thumb_codes[0], rc); + __put_user(sigreturn_fdpic_thumb_codes[1], rc + 1); + __put_user(sigreturn_fdpic_thumb_codes[2], rc + 2); + __put_user((abi_ulong)ka->sa_restorer, rc + 3); + } else { + __put_user(sigreturn_fdpic_codes[0], rc); + __put_user(sigreturn_fdpic_codes[1], rc + 1); + __put_user(sigreturn_fdpic_codes[2], rc + 2); + __put_user((abi_ulong)ka->sa_restorer, rc + 3); + } + + retcode = rc_addr + thumb; + } else { + retcode = ka->sa_restorer; + } + } else { + unsigned int idx = thumb; + + if (ka->sa_flags & TARGET_SA_SIGINFO) { + idx += 2; + } + + __put_user(retcodes[idx], rc); + + retcode = rc_addr + thumb; + } + + env->regs[0] = usig; + if (is_fdpic) { + env->regs[9] = handler_fdpic_GOT; + } + env->regs[13] = frame_addr; + env->regs[14] = retcode; + env->regs[15] = handler & (thumb ? ~1 : ~3); + cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr); + + return 0; +} + +static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) +{ + int i; + struct target_vfp_sigframe *vfpframe; + vfpframe = (struct target_vfp_sigframe *)regspace; + __put_user(TARGET_VFP_MAGIC, &vfpframe->magic); + __put_user(sizeof(*vfpframe), &vfpframe->size); + for (i = 0; i < 32; i++) { + __put_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]); + } + __put_user(vfp_get_fpscr(env), &vfpframe->ufp.fpscr); + __put_user(env->vfp.xregs[ARM_VFP_FPEXC], &vfpframe->ufp_exc.fpexc); + __put_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); + __put_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); + return (abi_ulong*)(vfpframe+1); +} + +static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, + CPUARMState *env) +{ + int i; + struct target_iwmmxt_sigframe *iwmmxtframe; + iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; + __put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic); + __put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size); + for (i = 0; i < 16; i++) { + __put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); + } + __put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); + return (abi_ulong*)(iwmmxtframe+1); +} + +static void setup_sigframe_v2(struct target_ucontext_v2 *uc, + target_sigset_t *set, CPUARMState *env) +{ + struct target_sigaltstack stack; + int i; + abi_ulong *regspace; + + /* Clear all the bits of the ucontext we don't use. */ + memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext)); + + memset(&stack, 0, sizeof(stack)); + target_save_altstack(&stack, env); + memcpy(&uc->tuc_stack, &stack, sizeof(stack)); + + setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]); + /* Save coprocessor signal frame. */ + regspace = uc->tuc_regspace; + if (arm_feature(env, ARM_FEATURE_VFP)) { + regspace = setup_sigframe_v2_vfp(regspace, env); + } + if (arm_feature(env, ARM_FEATURE_IWMMXT)) { + regspace = setup_sigframe_v2_iwmmxt(regspace, env); + } + + /* Write terminating magic word */ + __put_user(0, regspace); + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &uc->tuc_sigmask.sig[i]); + } +} + +/* compare linux/arch/arm/kernel/signal.c:setup_frame() */ +static void setup_frame_v1(int usig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *regs) +{ + struct sigframe_v1 *frame; + abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); + int i; + + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto sigsegv; + } + + setup_sigcontext(&frame->sc, regs, set->sig[0]); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + if (setup_return(regs, ka, frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct sigframe_v1, retcode))) { + goto sigsegv; + } + + unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(usig); +} + +static void setup_frame_v2(int usig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *regs) +{ + struct sigframe_v2 *frame; + abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); + + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto sigsegv; + } + + setup_sigframe_v2(&frame->uc, set, regs); + + if (setup_return(regs, ka, frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct sigframe_v2, retcode))) { + goto sigsegv; + } + + unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(usig); +} + +void setup_frame(int usig, struct target_sigaction *ka, + target_sigset_t *set, CPUARMState *regs) +{ + if (get_osversion() >= 0x020612) { + setup_frame_v2(usig, ka, set, regs); + } else { + setup_frame_v1(usig, ka, set, regs); + } +} + +/* compare linux/arch/arm/kernel/signal.c:setup_rt_frame() */ +static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUARMState *env) +{ + struct rt_sigframe_v1 *frame; + abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); + struct target_sigaltstack stack; + int i; + abi_ulong info_addr, uc_addr; + + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto sigsegv; + } + + info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info); + __put_user(info_addr, &frame->pinfo); + uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc); + __put_user(uc_addr, &frame->puc); + tswap_siginfo(&frame->info, info); + + /* Clear all the bits of the ucontext we don't use. */ + memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext)); + + memset(&stack, 0, sizeof(stack)); + target_save_altstack(&stack, env); + memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack)); + + setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]); + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + if (setup_return(env, ka, frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct rt_sigframe_v1, retcode))) { + goto sigsegv; + } + + env->regs[1] = info_addr; + env->regs[2] = uc_addr; + + unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(usig); +} + +static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUARMState *env) +{ + struct rt_sigframe_v2 *frame; + abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); + abi_ulong info_addr, uc_addr; + + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto sigsegv; + } + + info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info); + uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc); + tswap_siginfo(&frame->info, info); + + setup_sigframe_v2(&frame->uc, set, env); + + if (setup_return(env, ka, frame->retcode, frame_addr, usig, + frame_addr + offsetof(struct rt_sigframe_v2, retcode))) { + goto sigsegv; + } + + env->regs[1] = info_addr; + env->regs[2] = uc_addr; + + unlock_user_struct(frame, frame_addr, 1); + return; +sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(usig); +} + +void setup_rt_frame(int usig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUARMState *env) +{ + if (get_osversion() >= 0x020612) { + setup_rt_frame_v2(usig, ka, info, set, env); + } else { + setup_rt_frame_v1(usig, ka, info, set, env); + } +} + +static int +restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) +{ + int err = 0; + uint32_t cpsr; + + __get_user(env->regs[0], &sc->arm_r0); + __get_user(env->regs[1], &sc->arm_r1); + __get_user(env->regs[2], &sc->arm_r2); + __get_user(env->regs[3], &sc->arm_r3); + __get_user(env->regs[4], &sc->arm_r4); + __get_user(env->regs[5], &sc->arm_r5); + __get_user(env->regs[6], &sc->arm_r6); + __get_user(env->regs[7], &sc->arm_r7); + __get_user(env->regs[8], &sc->arm_r8); + __get_user(env->regs[9], &sc->arm_r9); + __get_user(env->regs[10], &sc->arm_r10); + __get_user(env->regs[11], &sc->arm_fp); + __get_user(env->regs[12], &sc->arm_ip); + __get_user(env->regs[13], &sc->arm_sp); + __get_user(env->regs[14], &sc->arm_lr); + __get_user(env->regs[15], &sc->arm_pc); +#ifdef TARGET_CONFIG_CPU_32 + __get_user(cpsr, &sc->arm_cpsr); + cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); +#endif + + err |= !valid_user_regs(env); + + return err; +} + +static long do_sigreturn_v1(CPUARMState *env) +{ + abi_ulong frame_addr; + struct sigframe_v1 *frame = NULL; + target_sigset_t set; + sigset_t host_set; + int i; + + /* + * Since we stacked the signal on a 64-bit boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + frame_addr = env->regs[13]; + trace_user_do_sigreturn(env, frame_addr); + if (frame_addr & 7) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + __get_user(set.sig[0], &frame->sc.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(set.sig[i], &frame->extramask[i - 1]); + } + + target_to_host_sigset_internal(&host_set, &set); + set_sigmask(&host_set); + + if (restore_sigcontext(env, &frame->sc)) { + goto badframe; + } + +#if 0 + /* Send SIGTRAP if we're single-stepping */ + if (ptrace_cancel_bpt(current)) + send_sig(SIGTRAP, current, 1); +#endif + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) +{ + int i; + abi_ulong magic, sz; + uint32_t fpscr, fpexc; + struct target_vfp_sigframe *vfpframe; + vfpframe = (struct target_vfp_sigframe *)regspace; + + __get_user(magic, &vfpframe->magic); + __get_user(sz, &vfpframe->size); + if (magic != TARGET_VFP_MAGIC || sz != sizeof(*vfpframe)) { + return 0; + } + for (i = 0; i < 32; i++) { + __get_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]); + } + __get_user(fpscr, &vfpframe->ufp.fpscr); + vfp_set_fpscr(env, fpscr); + __get_user(fpexc, &vfpframe->ufp_exc.fpexc); + /* Sanitise FPEXC: ensure VFP is enabled, FPINST2 is invalid + * and the exception flag is cleared + */ + fpexc |= (1 << 30); + fpexc &= ~((1 << 31) | (1 << 28)); + env->vfp.xregs[ARM_VFP_FPEXC] = fpexc; + __get_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); + __get_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); + return (abi_ulong*)(vfpframe + 1); +} + +static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env, + abi_ulong *regspace) +{ + int i; + abi_ulong magic, sz; + struct target_iwmmxt_sigframe *iwmmxtframe; + iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; + + __get_user(magic, &iwmmxtframe->magic); + __get_user(sz, &iwmmxtframe->size); + if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) { + return 0; + } + for (i = 0; i < 16; i++) { + __get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); + } + __get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); + return (abi_ulong*)(iwmmxtframe + 1); +} + +static int do_sigframe_return_v2(CPUARMState *env, + target_ulong context_addr, + struct target_ucontext_v2 *uc) +{ + sigset_t host_set; + abi_ulong *regspace; + + target_to_host_sigset(&host_set, &uc->tuc_sigmask); + set_sigmask(&host_set); + + if (restore_sigcontext(env, &uc->tuc_mcontext)) + return 1; + + /* Restore coprocessor signal frame */ + regspace = uc->tuc_regspace; + if (arm_feature(env, ARM_FEATURE_VFP)) { + regspace = restore_sigframe_v2_vfp(env, regspace); + if (!regspace) { + return 1; + } + } + if (arm_feature(env, ARM_FEATURE_IWMMXT)) { + regspace = restore_sigframe_v2_iwmmxt(env, regspace); + if (!regspace) { + return 1; + } + } + + if (do_sigaltstack(context_addr + + offsetof(struct target_ucontext_v2, tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) { + return 1; + } + +#if 0 + /* Send SIGTRAP if we're single-stepping */ + if (ptrace_cancel_bpt(current)) + send_sig(SIGTRAP, current, 1); +#endif + + return 0; +} + +static long do_sigreturn_v2(CPUARMState *env) +{ + abi_ulong frame_addr; + struct sigframe_v2 *frame = NULL; + + /* + * Since we stacked the signal on a 64-bit boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + frame_addr = env->regs[13]; + trace_user_do_sigreturn(env, frame_addr); + if (frame_addr & 7) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + if (do_sigframe_return_v2(env, + frame_addr + + offsetof(struct sigframe_v2, uc), + &frame->uc)) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_sigreturn(CPUARMState *env) +{ + if (get_osversion() >= 0x020612) { + return do_sigreturn_v2(env); + } else { + return do_sigreturn_v1(env); + } +} + +static long do_rt_sigreturn_v1(CPUARMState *env) +{ + abi_ulong frame_addr; + struct rt_sigframe_v1 *frame = NULL; + sigset_t host_set; + + /* + * Since we stacked the signal on a 64-bit boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + frame_addr = env->regs[13]; + trace_user_do_rt_sigreturn(env, frame_addr); + if (frame_addr & 7) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); + set_sigmask(&host_set); + + if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe_v1, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) + goto badframe; + +#if 0 + /* Send SIGTRAP if we're single-stepping */ + if (ptrace_cancel_bpt(current)) + send_sig(SIGTRAP, current, 1); +#endif + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +static long do_rt_sigreturn_v2(CPUARMState *env) +{ + abi_ulong frame_addr; + struct rt_sigframe_v2 *frame = NULL; + + /* + * Since we stacked the signal on a 64-bit boundary, + * then 'sp' should be word aligned here. If it's + * not, then the user is trying to mess with us. + */ + frame_addr = env->regs[13]; + trace_user_do_rt_sigreturn(env, frame_addr); + if (frame_addr & 7) { + goto badframe; + } + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + if (do_sigframe_return_v2(env, + frame_addr + + offsetof(struct rt_sigframe_v2, uc), + &frame->uc)) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUARMState *env) +{ + if (get_osversion() >= 0x020612) { + return do_rt_sigreturn_v2(env); + } else { + return do_rt_sigreturn_v1(env); + } +} diff --git a/linux-user/arm/sockbits.h b/linux-user/arm/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/arm/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/arm/target_cpu.h b/linux-user/arm/target_cpu.h index c3eb4b243d..8a3764919a 100644 --- a/linux-user/arm/target_cpu.h +++ b/linux-user/arm/target_cpu.h @@ -49,4 +49,8 @@ static inline target_ulong cpu_get_tls(CPUARMState *env) } } +static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) +{ + return state->regs[13]; +} #endif diff --git a/linux-user/arm/target_fcntl.h b/linux-user/arm/target_fcntl.h new file mode 100644 index 0000000000..c8ff6b2505 --- /dev/null +++ b/linux-user/arm/target_fcntl.h @@ -0,0 +1,17 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef ARM_TARGET_FCNTL_H +#define ARM_TARGET_FCNTL_H + +#define TARGET_O_DIRECTORY 040000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ +#define TARGET_O_DIRECT 0200000 /* direct disk access hint */ +#define TARGET_O_LARGEFILE 0400000 + +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/arm/target_signal.h b/linux-user/arm/target_signal.h index cbbeb09f4d..ea123c40f3 100644 --- a/linux-user/arm/target_signal.h +++ b/linux-user/arm/target_signal.h @@ -1,8 +1,6 @@ #ifndef ARM_TARGET_SIGNAL_H #define ARM_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUARMState *state) -{ - return state->regs[13]; -} - +#include "../generic/signal.h" +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* ARM_TARGET_SIGNAL_H */ diff --git a/linux-user/arm/target_structs.h b/linux-user/arm/target_structs.h index 0bf034cc25..9a3dbce03d 100644 --- a/linux-user/arm/target_structs.h +++ b/linux-user/arm/target_structs.h @@ -49,4 +49,11 @@ struct target_shmid_ds { abi_ulong __unused5; }; +struct target_oabi_flock64 { + abi_short l_type; + abi_short l_whence; + abi_llong l_start; + abi_llong l_len; + abi_int l_pid; +} QEMU_PACKED; #endif diff --git a/linux-user/arm/target_syscall.h b/linux-user/arm/target_syscall.h index 94e2a42cb2..afc0772e19 100644 --- a/linux-user/arm/target_syscall.h +++ b/linux-user/arm/target_syscall.h @@ -16,6 +16,7 @@ struct target_pt_regs { #define ARM_NR_breakpoint (ARM_NR_BASE + 1) #define ARM_NR_cacheflush (ARM_NR_BASE + 2) #define ARM_NR_set_tls (ARM_NR_BASE + 5) +#define ARM_NR_get_tls (ARM_NR_BASE + 6) #define ARM_NR_semihosting 0x123456 #define ARM_NR_thumb_semihosting 0xAB diff --git a/linux-user/cpu_loop-common.h b/linux-user/cpu_loop-common.h new file mode 100644 index 0000000000..ffe3fe9ad5 --- /dev/null +++ b/linux-user/cpu_loop-common.h @@ -0,0 +1,37 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#ifndef CPU_LOOP_COMMON_H +#define CPU_LOOP_COMMON_H + +#include "exec/log.h" + +#define EXCP_DUMP(env, fmt, ...) \ +do { \ + CPUState *cs = ENV_GET_CPU(env); \ + fprintf(stderr, fmt , ## __VA_ARGS__); \ + cpu_dump_state(cs, stderr, fprintf, 0); \ + if (qemu_log_separate()) { \ + qemu_log(fmt, ## __VA_ARGS__); \ + log_cpu_state(cs, 0); \ + } \ +} while (0) + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs); +#endif diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c new file mode 100644 index 0000000000..37bdcfa8cc --- /dev/null +++ b/linux-user/cris/cpu_loop.c @@ -0,0 +1,115 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void cpu_loop(CPUCRISState *env) +{ + CPUState *cs = CPU(cris_env_get_cpu(env)); + int trapnr, ret; + target_siginfo_t info; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case 0xaa: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->pregs[PR_EDA]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_BREAK: + ret = do_syscall(env, + env->regs[9], + env->regs[10], + env->regs[11], + env->regs[12], + env->regs[13], + env->pregs[7], + env->pregs[11], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[10] = ret; + } + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + + env->regs[0] = regs->r0; + env->regs[1] = regs->r1; + env->regs[2] = regs->r2; + env->regs[3] = regs->r3; + env->regs[4] = regs->r4; + env->regs[5] = regs->r5; + env->regs[6] = regs->r6; + env->regs[7] = regs->r7; + env->regs[8] = regs->r8; + env->regs[9] = regs->r9; + env->regs[10] = regs->r10; + env->regs[11] = regs->r11; + env->regs[12] = regs->r12; + env->regs[13] = regs->r13; + env->regs[14] = info->start_stack; + env->regs[15] = regs->acr; + env->pc = regs->erp; +} diff --git a/linux-user/cris/signal.c b/linux-user/cris/signal.c new file mode 100644 index 0000000000..1e02194377 --- /dev/null +++ b/linux-user/cris/signal.c @@ -0,0 +1,188 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + struct target_pt_regs regs; /* needs to be first */ + uint32_t oldmask; + uint32_t usp; /* usp before stacking this gunk on it */ +}; + +/* Signal frames. */ +struct target_signal_frame { + struct target_sigcontext sc; + uint32_t extramask[TARGET_NSIG_WORDS - 1]; + uint16_t retcode[4]; /* Trampoline code. */ +}; + +struct rt_signal_frame { + siginfo_t *pinfo; + void *puc; + siginfo_t info; + ucontext_t uc; + uint16_t retcode[4]; /* Trampoline code. */ +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) +{ + __put_user(env->regs[0], &sc->regs.r0); + __put_user(env->regs[1], &sc->regs.r1); + __put_user(env->regs[2], &sc->regs.r2); + __put_user(env->regs[3], &sc->regs.r3); + __put_user(env->regs[4], &sc->regs.r4); + __put_user(env->regs[5], &sc->regs.r5); + __put_user(env->regs[6], &sc->regs.r6); + __put_user(env->regs[7], &sc->regs.r7); + __put_user(env->regs[8], &sc->regs.r8); + __put_user(env->regs[9], &sc->regs.r9); + __put_user(env->regs[10], &sc->regs.r10); + __put_user(env->regs[11], &sc->regs.r11); + __put_user(env->regs[12], &sc->regs.r12); + __put_user(env->regs[13], &sc->regs.r13); + __put_user(env->regs[14], &sc->usp); + __put_user(env->regs[15], &sc->regs.acr); + __put_user(env->pregs[PR_MOF], &sc->regs.mof); + __put_user(env->pregs[PR_SRP], &sc->regs.srp); + __put_user(env->pc, &sc->regs.erp); +} + +static void restore_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) +{ + __get_user(env->regs[0], &sc->regs.r0); + __get_user(env->regs[1], &sc->regs.r1); + __get_user(env->regs[2], &sc->regs.r2); + __get_user(env->regs[3], &sc->regs.r3); + __get_user(env->regs[4], &sc->regs.r4); + __get_user(env->regs[5], &sc->regs.r5); + __get_user(env->regs[6], &sc->regs.r6); + __get_user(env->regs[7], &sc->regs.r7); + __get_user(env->regs[8], &sc->regs.r8); + __get_user(env->regs[9], &sc->regs.r9); + __get_user(env->regs[10], &sc->regs.r10); + __get_user(env->regs[11], &sc->regs.r11); + __get_user(env->regs[12], &sc->regs.r12); + __get_user(env->regs[13], &sc->regs.r13); + __get_user(env->regs[14], &sc->usp); + __get_user(env->regs[15], &sc->regs.acr); + __get_user(env->pregs[PR_MOF], &sc->regs.mof); + __get_user(env->pregs[PR_SRP], &sc->regs.srp); + __get_user(env->pc, &sc->regs.erp); +} + +static abi_ulong get_sigframe(CPUCRISState *env, int framesize) +{ + abi_ulong sp; + /* Align the stack downwards to 4. */ + sp = (env->regs[R_SP] & ~3); + return sp - framesize; +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUCRISState *env) +{ + struct target_signal_frame *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(env, sizeof *frame); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto badframe; + + /* + * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't + * use this trampoline anymore but it sets it up for GDB. + * In QEMU, using the trampoline simplifies things a bit so we use it. + * + * This is movu.w __NR_sigreturn, r9; break 13; + */ + __put_user(0x9c5f, frame->retcode+0); + __put_user(TARGET_NR_sigreturn, + frame->retcode + 1); + __put_user(0xe93d, frame->retcode + 2); + + /* Save the mask. */ + __put_user(set->sig[0], &frame->sc.oldmask); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + setup_sigcontext(&frame->sc, env); + + /* Move the stack and setup the arguments for the handler. */ + env->regs[R_SP] = frame_addr; + env->regs[10] = sig; + env->pc = (unsigned long) ka->_sa_handler; + /* Link SRP so the guest returns through the trampoline. */ + env->pregs[PR_SRP] = frame_addr + offsetof(typeof(*frame), retcode); + + unlock_user_struct(frame, frame_addr, 1); + return; +badframe: + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUCRISState *env) +{ + qemu_log_mask(LOG_UNIMP, "setup_rt_frame: not implemented\n"); +} + +long do_sigreturn(CPUCRISState *env) +{ + struct target_signal_frame *frame; + abi_ulong frame_addr; + target_sigset_t target_set; + sigset_t set; + int i; + + frame_addr = env->regs[R_SP]; + trace_user_do_sigreturn(env, frame_addr); + /* Make sure the guest isn't playing games. */ + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) { + goto badframe; + } + + /* Restore blocked signals */ + __get_user(target_set.sig[0], &frame->sc.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + restore_sigcontext(&frame->sc, env); + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUCRISState *env) +{ + trace_user_do_rt_sigreturn(env, 0); + qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} diff --git a/linux-user/cris/sockbits.h b/linux-user/cris/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/cris/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/cris/target_cpu.h b/linux-user/cris/target_cpu.h index c43aac62f9..2309343979 100644 --- a/linux-user/cris/target_cpu.h +++ b/linux-user/cris/target_cpu.h @@ -33,4 +33,8 @@ static inline void cpu_set_tls(CPUCRISState *env, target_ulong newtls) env->pregs[PR_PID] = (env->pregs[PR_PID] & 0xff) | newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state) +{ + return state->regs[14]; +} #endif diff --git a/linux-user/cris/target_fcntl.h b/linux-user/cris/target_fcntl.h new file mode 100644 index 0000000000..df0aceea34 --- /dev/null +++ b/linux-user/cris/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef CRIS_TARGET_FCNTL_H +#define CRIS_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/cris/target_signal.h b/linux-user/cris/target_signal.h index 664621bbcd..1cb5548f85 100644 --- a/linux-user/cris/target_signal.h +++ b/linux-user/cris/target_signal.h @@ -1,8 +1,6 @@ #ifndef CRIS_TARGET_SIGNAL_H #define CRIS_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUCRISState *state) -{ - return state->regs[14]; -} - +#include "../generic/signal.h" +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* CRIS_TARGET_SIGNAL_H */ diff --git a/linux-user/elfload.c b/linux-user/elfload.c index c77ed1bb01..df07055361 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -78,6 +78,11 @@ enum { */ #define personality(pers) (pers & PER_MASK) +int info_is_fdpic(struct image_info *info) +{ + return info->personality == PER_LINUX_FDPIC; +} + /* this flag is uneffective under linux too, should be deleted */ #ifndef MAP_DENYWRITE #define MAP_DENYWRITE 0 @@ -287,6 +292,25 @@ static inline void init_thread(struct target_pt_regs *regs, /* For uClinux PIC binaries. */ /* XXX: Linux does this only on ARM with no MMU (do we care ?) */ regs->uregs[10] = infop->start_data; + + /* Support ARM FDPIC. */ + if (info_is_fdpic(infop)) { + /* As described in the ABI document, r7 points to the loadmap info + * prepared by the kernel. If an interpreter is needed, r8 points + * to the interpreter loadmap and r9 points to the interpreter + * PT_DYNAMIC info. If no interpreter is needed, r8 is zero, and + * r9 points to the main program PT_DYNAMIC info. + */ + regs->uregs[7] = infop->loadmap_addr; + if (infop->interpreter_loadmap_addr) { + /* Executable is dynamically loaded. */ + regs->uregs[8] = infop->interpreter_loadmap_addr; + regs->uregs[9] = infop->interpreter_pt_dynamic_addr; + } else { + regs->uregs[8] = 0; + regs->uregs[9] = infop->pt_dynamic_addr; + } + } } #define ELF_NREG 18 @@ -557,8 +581,11 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE(ARM_FEATURE_V8_SHA512, ARM_HWCAP_A64_SHA512); GET_FEATURE(ARM_FEATURE_V8_FP16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); + GET_FEATURE(ARM_FEATURE_V8_ATOMICS, ARM_HWCAP_A64_ATOMICS); GET_FEATURE(ARM_FEATURE_V8_RDM, ARM_HWCAP_A64_ASIMDRDM); + GET_FEATURE(ARM_FEATURE_V8_DOTPROD, ARM_HWCAP_A64_ASIMDDP); GET_FEATURE(ARM_FEATURE_V8_FCMA, ARM_HWCAP_A64_FCMA); + GET_FEATURE(ARM_FEATURE_SVE, ARM_HWCAP_A64_SVE); #undef GET_FEATURE return hwcaps; @@ -1681,7 +1708,19 @@ static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot) } } -#ifdef CONFIG_USE_FDPIC +#ifdef TARGET_ARM +static int elf_is_fdpic(struct elfhdr *exec) +{ + return exec->e_ident[EI_OSABI] == ELFOSABI_ARM_FDPIC; +} +#else +/* Default implementation, always false. */ +static int elf_is_fdpic(struct elfhdr *exec) +{ + return 0; +} +#endif + static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong sp) { uint16_t n; @@ -1706,7 +1745,6 @@ static abi_ulong loader_build_fdpic_loadmap(struct image_info *info, abi_ulong s return sp; } -#endif static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, struct elfhdr *exec, @@ -1725,7 +1763,6 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, sp = p; -#ifdef CONFIG_USE_FDPIC /* Needs to be before we load the env/argc/... */ if (elf_is_fdpic(exec)) { /* Need 4 byte alignment for these structs */ @@ -1735,9 +1772,13 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, if (interp_info) { interp_info->other_info = info; sp = loader_build_fdpic_loadmap(interp_info, sp); + info->interpreter_loadmap_addr = interp_info->loadmap_addr; + info->interpreter_pt_dynamic_addr = interp_info->pt_dynamic_addr; + } else { + info->interpreter_loadmap_addr = 0; + info->interpreter_pt_dynamic_addr = 0; } } -#endif u_platform = 0; k_platform = ELF_PLATFORM; @@ -1834,7 +1875,13 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff)); NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr))); NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum)); - NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(MAX(TARGET_PAGE_SIZE, getpagesize()))); + if ((info->alignment & ~qemu_host_page_mask) != 0) { + /* Target doesn't support host page size alignment */ + NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE)); + } else { + NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(MAX(TARGET_PAGE_SIZE, + qemu_host_page_size))); + } NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_info ? interp_info->load_addr : 0)); NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0); NEW_AUX_ENT(AT_ENTRY, info->entry); @@ -2153,16 +2200,15 @@ static void load_elf_image(const char *image_name, int image_fd, } bswap_phdr(phdr, ehdr->e_phnum); -#ifdef CONFIG_USE_FDPIC info->nsegs = 0; info->pt_dynamic_addr = 0; -#endif mmap_lock(); /* Find the maximum size of the image and allocate an appropriate amount of memory to handle that. */ loaddr = -1, hiaddr = 0; + info->alignment = 0; for (i = 0; i < ehdr->e_phnum; ++i) { if (phdr[i].p_type == PT_LOAD) { abi_ulong a = phdr[i].p_vaddr - phdr[i].p_offset; @@ -2173,9 +2219,8 @@ static void load_elf_image(const char *image_name, int image_fd, if (a > hiaddr) { hiaddr = a; } -#ifdef CONFIG_USE_FDPIC ++info->nsegs; -#endif + info->alignment |= phdr[i].p_align; } } @@ -2200,8 +2245,7 @@ static void load_elf_image(const char *image_name, int image_fd, } load_bias = load_addr - loaddr; -#ifdef CONFIG_USE_FDPIC - { + if (elf_is_fdpic(ehdr)) { struct elf32_fdpic_loadseg *loadsegs = info->loadsegs = g_malloc(sizeof(*loadsegs) * info->nsegs); @@ -2219,7 +2263,6 @@ static void load_elf_image(const char *image_name, int image_fd, } } } -#endif info->load_bias = load_bias; info->load_addr = load_addr; diff --git a/linux-user/exit.c b/linux-user/exit.c new file mode 100644 index 0000000000..14e94e28fa --- /dev/null +++ b/linux-user/exit.c @@ -0,0 +1,35 @@ +/* + * exit support for qemu + * + * Copyright (c) 2018 Alex Bennée + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" + +#ifdef CONFIG_GCOV +extern void __gcov_dump(void); +#endif + +void preexit_cleanup(CPUArchState *env, int code) +{ +#ifdef TARGET_GPROF + _mcleanup(); +#endif +#ifdef CONFIG_GCOV + __gcov_dump(); +#endif + gdb_exit(env, code); +} diff --git a/linux-user/flatload.c b/linux-user/flatload.c index a35a560904..10c529910f 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -224,8 +224,9 @@ static int decompress_exec( ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); if (ret <= 0) break; - if (ret >= (unsigned long) -4096) + if (is_error(ret)) { break; + } len -= ret; strm.next_in = buf; @@ -283,8 +284,7 @@ calc_reloc(abi_ulong r, struct lib_info *p, int curid, int internalp) "in same module (%d != %d)\n", (unsigned) r, curid, id); goto failed; - } else if ( ! p[id].loaded && - load_flat_shared_library(id, p) > (unsigned long) -4096) { + } else if (!p[id].loaded && is_error(load_flat_shared_library(id, p))) { fprintf(stderr, "BINFMT_FLAT: failed to load library %d\n", id); goto failed; } @@ -523,9 +523,10 @@ static int load_flat_file(struct linux_binprm * bprm, fpos = 0; result = bprm->file->f_op->read(bprm->file, (char *) textpos, text_len, &fpos); - if (result < (unsigned long) -4096) + if (!is_error(result)) { result = decompress_exec(bprm, text_len, (char *) datapos, data_len + (relocs * sizeof(unsigned long)), 0); + } } else #endif @@ -693,8 +694,9 @@ static int load_flat_shared_library(int id, struct lib_info *libs) res = prepare_binprm(&bprm); - if (res <= (unsigned long)-4096) + if (!is_error(res)) { res = load_flat_file(&bprm, libs, id, NULL); + } if (bprm.file) { allow_write_access(bprm.file); fput(bprm.file); @@ -737,8 +739,9 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) res = load_flat_file(bprm, libinfo, 0, &stack_len); - if (res > (unsigned long)-4096) + if (is_error(res)) { return res; + } /* Update data segment pointers for all libraries */ for (i=0; i values follow. */ +#define TARGET_O_ACCMODE 0003 +#define TARGET_O_RDONLY 00 +#define TARGET_O_WRONLY 01 +#define TARGET_O_RDWR 02 +#ifndef TARGET_O_CREAT +#define TARGET_O_CREAT 0100 /* not fcntl */ +#endif +#ifndef TARGET_O_EXCL +#define TARGET_O_EXCL 0200 /* not fcntl */ +#endif +#ifndef TARGET_O_NOCTTY +#define TARGET_O_NOCTTY 0400 /* not fcntl */ +#endif +#ifndef TARGET_O_TRUNC +#define TARGET_O_TRUNC 01000 /* not fcntl */ +#endif +#ifndef TARGET_O_APPEND +#define TARGET_O_APPEND 02000 +#endif +#ifndef TARGET_O_NONBLOCK +#define TARGET_O_NONBLOCK 04000 +#endif +#ifndef TARGET_O_DSYNC +#define TARGET_O_DSYNC 010000 +#endif +#ifndef TARGET_FASYNC +#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */ +#endif +#ifndef TARGET_O_DIRECT +#define TARGET_O_DIRECT 040000 /* direct disk access hint */ +#endif +#ifndef TARGET_O_LARGEFILE +#define TARGET_O_LARGEFILE 0100000 +#endif +#ifndef TARGET_O_DIRECTORY +#define TARGET_O_DIRECTORY 0200000 /* must be a directory */ +#endif +#ifndef TARGET_O_NOFOLLOW +#define TARGET_O_NOFOLLOW 0400000 /* don't follow links */ +#endif +#ifndef TARGET_O_NOATIME +#define TARGET_O_NOATIME 01000000 +#endif +#ifndef TARGET_O_CLOEXEC +#define TARGET_O_CLOEXEC 02000000 +#endif +#ifndef TARGET___O_SYNC +#define TARGET___O_SYNC 04000000 +#endif +#ifndef TARGET_O_PATH +#define TARGET_O_PATH 010000000 +#endif +#ifndef TARGET___O_TMPFILE +#define TARGET___O_TMPFILE 020000000 +#endif +#ifndef TARGET_O_TMPFILE +#define TARGET_O_TMPFILE (TARGET___O_TMPFILE | TARGET_O_DIRECTORY) +#endif +#ifndef TARGET_O_NDELAY +#define TARGET_O_NDELAY TARGET_O_NONBLOCK +#endif +#ifndef TARGET_O_SYNC +#define TARGET_O_SYNC (TARGET___O_SYNC | TARGET_O_DSYNC) +#endif + +#define TARGET_F_DUPFD 0 /* dup */ +#define TARGET_F_GETFD 1 /* get close_on_exec */ +#define TARGET_F_SETFD 2 /* set/clear close_on_exec */ +#define TARGET_F_GETFL 3 /* get file->f_flags */ +#define TARGET_F_SETFL 4 /* set file->f_flags */ +#ifndef TARGET_F_GETLK +#define TARGET_F_GETLK 5 +#define TARGET_F_SETLK 6 +#define TARGET_F_SETLKW 7 +#endif +#ifndef TARGET_F_SETOWN +#define TARGET_F_SETOWN 8 /* for sockets. */ +#define TARGET_F_GETOWN 9 /* for sockets. */ +#endif +#ifndef TARGET_F_SETSIG +#define TARGET_F_SETSIG 10 /* for sockets. */ +#define TARGET_F_GETSIG 11 /* for sockets. */ +#endif + +#ifndef TARGET_F_GETLK64 +#define TARGET_F_GETLK64 12 /* using 'struct flock64' */ +#define TARGET_F_SETLK64 13 +#define TARGET_F_SETLKW64 14 +#endif + +#ifndef TARGET_F_SETOWN_EX +#define TARGET_F_SETOWN_EX 15 +#define TARGET_F_GETOWN_EX 16 +#endif + +struct target_f_owner_ex { + int type; /* Owner type of ID. */ + int pid; /* ID of owner. */ +}; + +#ifndef TARGET_F_RDLCK +#define TARGET_F_RDLCK 0 +#define TARGET_F_WRLCK 1 +#define TARGET_F_UNLCK 2 +#endif + +#ifndef TARGET_F_EXLCK +#define TARGET_F_EXLCK 4 +#define TARGET_F_SHLCK 8 +#endif + +#ifndef TARGET_ARCH_FLOCK_PAD +#define TARGET_ARCH_FLOCK_PAD +#endif + +struct target_flock { + short l_type; + short l_whence; + abi_long l_start; + abi_long l_len; +#if defined(TARGET_MIPS) + abi_long l_sysid; +#endif + int l_pid; + TARGET_ARCH_FLOCK_PAD +}; + +#ifndef TARGET_ARCH_FLOCK64_PAD +#define TARGET_ARCH_FLOCK64_PAD +#endif + +struct target_flock64 { + abi_short l_type; + abi_short l_whence; + abi_llong l_start; + abi_llong l_len; + abi_int l_pid; + TARGET_ARCH_FLOCK64_PAD +}; +#endif diff --git a/linux-user/generic/signal.h b/linux-user/generic/signal.h new file mode 100644 index 0000000000..e1083f8fba --- /dev/null +++ b/linux-user/generic/signal.h @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef GENERIC_SIGNAL_H +#define GENERIC_SIGNAL_H + +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */ +#define TARGET_SA_SIGINFO 0x00000004 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESETHAND 0x80000000 +#define TARGET_SA_RESTORER 0x04000000 + +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGIOT 6 +#define TARGET_SIGBUS 7 +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGUSR1 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGUSR2 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGSTKFLT 16 +#define TARGET_SIGCHLD 17 +#define TARGET_SIGCONT 18 +#define TARGET_SIGSTOP 19 +#define TARGET_SIGTSTP 20 +#define TARGET_SIGTTIN 21 +#define TARGET_SIGTTOU 22 +#define TARGET_SIGURG 23 +#define TARGET_SIGXCPU 24 +#define TARGET_SIGXFSZ 25 +#define TARGET_SIGVTALRM 26 +#define TARGET_SIGPROF 27 +#define TARGET_SIGWINCH 28 +#define TARGET_SIGIO 29 +#define TARGET_SIGPWR 30 +#define TARGET_SIGSYS 31 +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 0 /* for blocking signals */ +#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */ +#define TARGET_SIG_SETMASK 2 /* for setting the signal mask */ +#endif diff --git a/linux-user/generic/sockbits.h b/linux-user/generic/sockbits.h new file mode 100644 index 0000000000..e44733c601 --- /dev/null +++ b/linux-user/generic/sockbits.h @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef GENERIC_SOCKBITS_H +#define GENERIC_SOCKBITS_H + +#define TARGET_SO_PASSSEC 34 + +/* For setsockopt(2) */ +#define TARGET_SOL_SOCKET 1 + +#define TARGET_SO_DEBUG 1 +#define TARGET_SO_REUSEADDR 2 +#define TARGET_SO_TYPE 3 +#define TARGET_SO_ERROR 4 +#define TARGET_SO_DONTROUTE 5 +#define TARGET_SO_BROADCAST 6 +#define TARGET_SO_SNDBUF 7 +#define TARGET_SO_RCVBUF 8 +#define TARGET_SO_SNDBUFFORCE 32 +#define TARGET_SO_RCVBUFFORCE 33 +#define TARGET_SO_KEEPALIVE 9 +#define TARGET_SO_OOBINLINE 10 +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_LINGER 13 +#define TARGET_SO_BSDCOMPAT 14 +#define TARGET_SO_REUSEPORT 15 +#define TARGET_SO_PASSCRED 16 +#define TARGET_SO_PEERCRED 17 +#define TARGET_SO_RCVLOWAT 18 +#define TARGET_SO_SNDLOWAT 19 +#define TARGET_SO_RCVTIMEO 20 +#define TARGET_SO_SNDTIMEO 21 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 22 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + +#define TARGET_SO_ACCEPTCONN 30 + +#define TARGET_SO_PEERSEC 31 +#endif diff --git a/linux-user/host/ppc64/safe-syscall.inc.S b/linux-user/host/ppc64/safe-syscall.inc.S index d30050a67c..8ed73a5b86 100644 --- a/linux-user/host/ppc64/safe-syscall.inc.S +++ b/linux-user/host/ppc64/safe-syscall.inc.S @@ -49,7 +49,9 @@ safe_syscall_base: * and returns the result in r3 * Shuffle everything around appropriately. */ - mr 11, 3 /* signal_pending */ + std 14, 16(1) /* Preserve r14 in SP+16 */ + .cfi_offset 14, 16 + mr 14, 3 /* signal_pending */ mr 0, 4 /* syscall number */ mr 3, 5 /* syscall arguments */ mr 4, 6 @@ -67,12 +69,13 @@ safe_syscall_base: */ safe_syscall_start: /* if signal_pending is non-zero, don't do the call */ - lwz 12, 0(11) + lwz 12, 0(14) cmpwi 0, 12, 0 bne- 0f sc safe_syscall_end: /* code path when we did execute the syscall */ + ld 14, 16(1) /* restore r14 to its original value */ bnslr+ /* syscall failed; return negative errno */ @@ -81,6 +84,7 @@ safe_syscall_end: /* code path when we didn't execute the syscall */ 0: addi 3, 0, -TARGET_ERESTARTSYS + ld 14, 16(1) /* restore r14 to its orginal value */ blr .cfi_endproc diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c new file mode 100644 index 0000000000..0301c766c6 --- /dev/null +++ b/linux-user/hppa/cpu_loop.c @@ -0,0 +1,211 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +static abi_ulong hppa_lws(CPUHPPAState *env) +{ + uint32_t which = env->gr[20]; + abi_ulong addr = env->gr[26]; + abi_ulong old = env->gr[25]; + abi_ulong new = env->gr[24]; + abi_ulong size, ret; + + switch (which) { + default: + return -TARGET_ENOSYS; + + case 0: /* elf32 atomic 32bit cmpxchg */ + if ((addr & 3) || !access_ok(VERIFY_WRITE, addr, 4)) { + return -TARGET_EFAULT; + } + old = tswap32(old); + new = tswap32(new); + ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); + ret = tswap32(ret); + break; + + case 2: /* elf32 atomic "new" cmpxchg */ + size = env->gr[23]; + if (size >= 4) { + return -TARGET_ENOSYS; + } + if (((addr | old | new) & ((1 << size) - 1)) + || !access_ok(VERIFY_WRITE, addr, 1 << size) + || !access_ok(VERIFY_READ, old, 1 << size) + || !access_ok(VERIFY_READ, new, 1 << size)) { + return -TARGET_EFAULT; + } + /* Note that below we use host-endian loads so that the cmpxchg + can be host-endian as well. */ + switch (size) { + case 0: + old = *(uint8_t *)g2h(old); + new = *(uint8_t *)g2h(new); + ret = atomic_cmpxchg((uint8_t *)g2h(addr), old, new); + ret = ret != old; + break; + case 1: + old = *(uint16_t *)g2h(old); + new = *(uint16_t *)g2h(new); + ret = atomic_cmpxchg((uint16_t *)g2h(addr), old, new); + ret = ret != old; + break; + case 2: + old = *(uint32_t *)g2h(old); + new = *(uint32_t *)g2h(new); + ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); + ret = ret != old; + break; + case 3: + { + uint64_t o64, n64, r64; + o64 = *(uint64_t *)g2h(old); + n64 = *(uint64_t *)g2h(new); +#ifdef CONFIG_ATOMIC64 + r64 = atomic_cmpxchg__nocheck((uint64_t *)g2h(addr), o64, n64); + ret = r64 != o64; +#else + start_exclusive(); + r64 = *(uint64_t *)g2h(addr); + ret = 1; + if (r64 == o64) { + *(uint64_t *)g2h(addr) = n64; + ret = 0; + } + end_exclusive(); +#endif + } + break; + } + break; + } + + env->gr[28] = ret; + return 0; +} + +void cpu_loop(CPUHPPAState *env) +{ + CPUState *cs = CPU(hppa_env_get_cpu(env)); + target_siginfo_t info; + abi_ulong ret; + int trapnr; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_SYSCALL: + ret = do_syscall(env, env->gr[20], + env->gr[26], env->gr[25], + env->gr[24], env->gr[23], + env->gr[22], env->gr[21], 0, 0); + switch (ret) { + default: + env->gr[28] = ret; + /* We arrived here by faking the gateway page. Return. */ + env->iaoq_f = env->gr[31]; + env->iaoq_b = env->gr[31] + 4; + break; + case -TARGET_ERESTARTSYS: + case -TARGET_QEMU_ESIGRETURN: + break; + } + break; + case EXCP_SYSCALL_LWS: + env->gr[21] = hppa_lws(env); + /* We arrived here by faking the gateway page. Return. */ + env->iaoq_f = env->gr[31]; + env->iaoq_b = env->gr[31] + 4; + break; + case EXCP_ITLB_MISS: + case EXCP_DTLB_MISS: + case EXCP_NA_ITLB_MISS: + case EXCP_NA_DTLB_MISS: + case EXCP_IMP: + case EXCP_DMP: + case EXCP_DMB: + case EXCP_PAGE_REF: + case EXCP_DMAR: + case EXCP_DMPI: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_ACCERR; + info._sifields._sigfault._addr = env->cr[CR_IOR]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_UNALIGN: + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->cr[CR_IOR]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ILL: + case EXCP_PRIV_OPR: + case EXCP_PRIV_REG: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->iaoq_f; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_OVERFLOW: + case EXCP_COND: + case EXCP_ASSIST: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->iaoq_f; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_DEBUG: + trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); + if (trapnr) { + info.si_signo = trapnr; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, trapnr, QEMU_SI_FAULT, &info); + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + default: + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + for (i = 1; i < 32; i++) { + env->gr[i] = regs->gr[i]; + } + env->iaoq_f = regs->iaoq[0]; + env->iaoq_b = regs->iaoq[1]; +} diff --git a/linux-user/hppa/signal.c b/linux-user/hppa/signal.c new file mode 100644 index 0000000000..b6927ee673 --- /dev/null +++ b/linux-user/hppa/signal.c @@ -0,0 +1,203 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_ulong sc_flags; + abi_ulong sc_gr[32]; + uint64_t sc_fr[32]; + abi_ulong sc_iasq[2]; + abi_ulong sc_iaoq[2]; + abi_ulong sc_sar; +}; + +struct target_ucontext { + abi_uint tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + abi_uint pad[1]; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe { + abi_uint tramp[9]; + target_siginfo_t info; + struct target_ucontext uc; + /* hidden location of upper halves of pa2.0 64-bit gregs */ +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env) +{ + int flags = 0; + int i; + + /* ??? if on_sig_stack, flags |= 1 (PARISC_SC_FLAG_ONSTACK). */ + + if (env->iaoq_f < TARGET_PAGE_SIZE) { + /* In the gateway page, executing a syscall. */ + flags |= 2; /* PARISC_SC_FLAG_IN_SYSCALL */ + __put_user(env->gr[31], &sc->sc_iaoq[0]); + __put_user(env->gr[31] + 4, &sc->sc_iaoq[1]); + } else { + __put_user(env->iaoq_f, &sc->sc_iaoq[0]); + __put_user(env->iaoq_b, &sc->sc_iaoq[1]); + } + __put_user(0, &sc->sc_iasq[0]); + __put_user(0, &sc->sc_iasq[1]); + __put_user(flags, &sc->sc_flags); + + __put_user(cpu_hppa_get_psw(env), &sc->sc_gr[0]); + for (i = 1; i < 32; ++i) { + __put_user(env->gr[i], &sc->sc_gr[i]); + } + + __put_user((uint64_t)env->fr0_shadow << 32, &sc->sc_fr[0]); + for (i = 1; i < 32; ++i) { + __put_user(env->fr[i], &sc->sc_fr[i]); + } + + __put_user(env->cr[CR_SAR], &sc->sc_sar); +} + +static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) +{ + target_ulong psw; + int i; + + __get_user(psw, &sc->sc_gr[0]); + cpu_hppa_put_psw(env, psw); + + for (i = 1; i < 32; ++i) { + __get_user(env->gr[i], &sc->sc_gr[i]); + } + for (i = 0; i < 32; ++i) { + __get_user(env->fr[i], &sc->sc_fr[i]); + } + cpu_hppa_loaded_fr0(env); + + __get_user(env->iaoq_f, &sc->sc_iaoq[0]); + __get_user(env->iaoq_b, &sc->sc_iaoq[1]); + __get_user(env->cr[CR_SAR], &sc->sc_sar); +} + +/* No, this doesn't look right, but it's copied straight from the kernel. */ +#define PARISC_RT_SIGFRAME_SIZE32 \ + ((sizeof(struct target_rt_sigframe) + 48 + 64) & -64) + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env) +{ + abi_ulong frame_addr, sp, haddr; + struct target_rt_sigframe *frame; + int i; + + sp = get_sp_from_cpustate(env); + if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { + sp = (target_sigaltstack_used.ss_sp + 0x7f) & ~0x3f; + } + frame_addr = QEMU_ALIGN_UP(sp, 64); + sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32; + + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + frame->uc.tuc_flags = 0; + frame->uc.tuc_link = 0; + + target_save_altstack(&frame->uc.tuc_stack, env); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + setup_sigcontext(&frame->uc.tuc_mcontext, env); + + __put_user(0x34190000, frame->tramp + 0); /* ldi 0,%r25 */ + __put_user(0x3414015a, frame->tramp + 1); /* ldi __NR_rt_sigreturn,%r20 */ + __put_user(0xe4008200, frame->tramp + 2); /* be,l 0x100(%sr2,%r0) */ + __put_user(0x08000240, frame->tramp + 3); /* nop */ + + unlock_user_struct(frame, frame_addr, 1); + + env->gr[2] = h2g(frame->tramp); + env->gr[30] = sp; + env->gr[26] = sig; + env->gr[25] = h2g(&frame->info); + env->gr[24] = h2g(&frame->uc); + + haddr = ka->_sa_handler; + if (haddr & 2) { + /* Function descriptor. */ + target_ulong *fdesc, dest; + + haddr &= -4; + if (!lock_user_struct(VERIFY_READ, fdesc, haddr, 1)) { + goto give_sigsegv; + } + __get_user(dest, fdesc); + __get_user(env->gr[19], fdesc + 1); + unlock_user_struct(fdesc, haddr, 1); + haddr = dest; + } + env->iaoq_f = haddr; + env->iaoq_b = haddr + 4; + return; + + give_sigsegv: + force_sigsegv(sig); +} + +long do_rt_sigreturn(CPUArchState *env) +{ + abi_ulong frame_addr = env->gr[30] - PARISC_RT_SIGFRAME_SIZE32; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + unlock_user_struct(frame, frame_addr, 0); + + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->gr[30]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/hppa/sockbits.h b/linux-user/hppa/sockbits.h index 5044619e16..2641aea859 100644 --- a/linux-user/hppa/sockbits.h +++ b/linux-user/hppa/sockbits.h @@ -64,34 +64,7 @@ #define TARGET_SO_CNX_ADVICE 0x402E -/** sock_type - Socket types - default values - * - * - * @SOCK_STREAM - stream (connection) socket - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. +/* TARGET_O_NONBLOCK clashes with the bits used for socket types. Therefore we + * have to define SOCK_NONBLOCK to a different value here. */ -enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 010000000, - TARGET_SOCK_NONBLOCK = 0x40000000, -}; - -#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) -#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - -#define ARCH_HAS_SOCKET_TYPES 1 +#define TARGET_SOCK_NONBLOCK 0x40000000 diff --git a/linux-user/hppa/syscall_nr.h b/linux-user/hppa/syscall_nr.h index 55bdf71d50..9c1d0a195d 100644 --- a/linux-user/hppa/syscall_nr.h +++ b/linux-user/hppa/syscall_nr.h @@ -279,7 +279,7 @@ #define TARGET_NR_ppoll 274 #define TARGET_NR_openat 275 #define TARGET_NR_mkdirat 276 -#define TARGET_NR_mknotat 277 +#define TARGET_NR_mknodat 277 #define TARGET_NR_fchownat 278 #define TARGET_NR_futimesat 279 #define TARGET_NR_fstatat64 280 diff --git a/linux-user/hppa/target_cpu.h b/linux-user/hppa/target_cpu.h index 7b78bbea80..1c539bdbd6 100644 --- a/linux-user/hppa/target_cpu.h +++ b/linux-user/hppa/target_cpu.h @@ -16,8 +16,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#ifndef ALPHA_TARGET_CPU_H -#define ALPHA_TARGET_CPU_H +#ifndef HPPA_TARGET_CPU_H +#define HPPA_TARGET_CPU_H static inline void cpu_clone_regs(CPUHPPAState *env, target_ulong newsp) { @@ -36,4 +36,8 @@ static inline void cpu_set_tls(CPUHPPAState *env, target_ulong newtls) env->cr[27] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state) +{ + return state->gr[30]; +} #endif diff --git a/linux-user/hppa/target_fcntl.h b/linux-user/hppa/target_fcntl.h new file mode 100644 index 0000000000..bd966a59b8 --- /dev/null +++ b/linux-user/hppa/target_fcntl.h @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef HPPA_TARGET_FCNTL_H +#define HPPA_TARGET_FCNTL_H + +#define TARGET_O_NONBLOCK 000200004 /* HPUX has separate NDELAY & NONBLOCK */ +#define TARGET_O_APPEND 000000010 +#define TARGET_O_CREAT 000000400 /* not fcntl */ +#define TARGET_O_EXCL 000002000 /* not fcntl */ +#define TARGET_O_NOCTTY 000400000 /* not fcntl */ +#define TARGET_O_DSYNC 001000000 +#define TARGET_O_LARGEFILE 000004000 +#define TARGET_O_DIRECTORY 000010000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 000000200 /* don't follow links */ +#define TARGET_O_NOATIME 004000000 +#define TARGET_O_CLOEXEC 010000000 +#define TARGET___O_SYNC 000100000 +#define TARGET_O_PATH 020000000 + +#define TARGET_F_RDLCK 1 +#define TARGET_F_WRLCK 2 +#define TARGET_F_UNLCK 3 + +#define TARGET_F_GETLK64 8 /* using 'struct flock64' */ +#define TARGET_F_SETLK64 9 +#define TARGET_F_SETLKW64 10 + +#define TARGET_F_GETLK 5 +#define TARGET_F_SETLK 6 +#define TARGET_F_SETLKW 7 +#define TARGET_F_GETOWN 11 /* for sockets. */ +#define TARGET_F_SETOWN 12 /* for sockets. */ +#define TARGET_F_SETSIG 13 /* for sockets. */ +#define TARGET_F_GETSIG 14 /* for sockets. */ + +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h index e115890b48..ba159ff8d0 100644 --- a/linux-user/hppa/target_signal.h +++ b/linux-user/hppa/target_signal.h @@ -1,7 +1,43 @@ #ifndef HPPA_TARGET_SIGNAL_H #define HPPA_TARGET_SIGNAL_H -#include "cpu.h" +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGIOT 6 +#define TARGET_SIGSTKFLT 7 +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGBUS 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGXCPU 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGUSR1 16 +#define TARGET_SIGUSR2 17 +#define TARGET_SIGCHLD 18 +#define TARGET_SIGPWR 19 +#define TARGET_SIGVTALRM 20 +#define TARGET_SIGPROF 21 +#define TARGET_SIGIO 22 +#define TARGET_SIGPOLL TARGET_SIGIO +#define TARGET_SIGWINCH 23 +#define TARGET_SIGSTOP 24 +#define TARGET_SIGTSTP 25 +#define TARGET_SIGCONT 26 +#define TARGET_SIGTTIN 27 +#define TARGET_SIGTTOU 28 +#define TARGET_SIGURG 29 +#define TARGET_SIGXFSZ 30 +#define TARGET_SIGSYS 31 + +#define TARGET_SIG_BLOCK 0 +#define TARGET_SIG_UNBLOCK 1 +#define TARGET_SIG_SETMASK 2 /* this struct defines a stack used during syscall handling */ @@ -18,12 +54,15 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_ONSTACK 0x00000001 +#define TARGET_SA_RESETHAND 0x00000004 +#define TARGET_SA_NOCLDSTOP 0x00000008 +#define TARGET_SA_SIGINFO 0x00000010 +#define TARGET_SA_NODEFER 0x00000020 +#define TARGET_SA_RESTART 0x00000040 +#define TARGET_SA_NOCLDWAIT 0x00000080 + #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state) -{ - return state->gr[30]; -} - #endif /* HPPA_TARGET_SIGNAL_H */ diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c new file mode 100644 index 0000000000..2374abfd0b --- /dev/null +++ b/linux-user/i386/cpu_loop.c @@ -0,0 +1,369 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +/***********************************************************/ +/* CPUX86 core interface */ + +uint64_t cpu_get_tsc(CPUX86State *env) +{ + return cpu_get_host_ticks(); +} + +static void write_dt(void *ptr, unsigned long addr, unsigned long limit, + int flags) +{ + unsigned int e1, e2; + uint32_t *p; + e1 = (addr << 16) | (limit & 0xffff); + e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); + e2 |= flags; + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); +} + +static uint64_t *idt_table; +#ifdef TARGET_X86_64 +static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, + uint64_t addr, unsigned int sel) +{ + uint32_t *p, e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); + p[2] = tswap32(addr >> 32); + p[3] = 0; +} +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate64(idt_table + n * 2, 0, dpl, 0, 0); +} +#else +static void set_gate(void *ptr, unsigned int type, unsigned int dpl, + uint32_t addr, unsigned int sel) +{ + uint32_t *p, e1, e2; + e1 = (addr & 0xffff) | (sel << 16); + e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); + p = ptr; + p[0] = tswap32(e1); + p[1] = tswap32(e2); +} + +/* only dpl matters as we do only user space emulation */ +static void set_idt(int n, unsigned int dpl) +{ + set_gate(idt_table + n, 0, dpl, 0, 0); +} +#endif + +void cpu_loop(CPUX86State *env) +{ + CPUState *cs = CPU(x86_env_get_cpu(env)); + int trapnr; + abi_ulong pc; + abi_ulong ret; + target_siginfo_t info; + + for(;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch(trapnr) { + case 0x80: + /* linux syscall from int $0x80 */ + ret = do_syscall(env, + env->regs[R_EAX], + env->regs[R_EBX], + env->regs[R_ECX], + env->regs[R_EDX], + env->regs[R_ESI], + env->regs[R_EDI], + env->regs[R_EBP], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->eip -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[R_EAX] = ret; + } + break; +#ifndef TARGET_ABI32 + case EXCP_SYSCALL: + /* linux syscall from syscall instruction */ + ret = do_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[10], + env->regs[8], + env->regs[9], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->eip -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[R_EAX] = ret; + } + break; +#endif + case EXCP0B_NOSEG: + case EXCP0C_STACK: + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP0D_GPF: + /* XXX: potential problem if ABI32 */ +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_fault(env); + } else +#endif + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP0E_PAGE: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + if (!(env->error_code & 1)) + info.si_code = TARGET_SEGV_MAPERR; + else + info.si_code = TARGET_SEGV_ACCERR; + info._sifields._sigfault._addr = env->cr[2]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP00_DIVZ: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + /* division by zero */ + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTDIV; + info._sifields._sigfault._addr = env->eip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP01_DB: + case EXCP03_INT3: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + if (trapnr == EXCP01_DB) { + info.si_code = TARGET_TRAP_BRKPT; + info._sifields._sigfault._addr = env->eip; + } else { + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + } + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP04_INTO: + case EXCP05_BOUND: +#ifndef TARGET_X86_64 + if (env->eflags & VM_MASK) { + handle_vm86_trap(env, trapnr); + } else +#endif + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SI_KERNEL; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP06_ILLOP: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->eip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + pc = env->segs[R_CS].base + env->eip; + EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", + (long)pc, trapnr); + abort(); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; + env->hflags |= HF_PE_MASK | HF_CPL_MASK; + if (env->features[FEAT_1_EDX] & CPUID_SSE) { + env->cr[4] |= CR4_OSFXSR_MASK; + env->hflags |= HF_OSFXSR_MASK; + } +#ifndef TARGET_ABI32 + /* enable 64 bit mode if possible */ + if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) { + fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); + exit(EXIT_FAILURE); + } + env->cr[4] |= CR4_PAE_MASK; + env->efer |= MSR_EFER_LMA | MSR_EFER_LME; + env->hflags |= HF_LMA_MASK; +#endif + + /* flags setup : we activate the IRQs by default as in user mode */ + env->eflags |= IF_MASK; + + /* linux register setup */ +#ifndef TARGET_ABI32 + env->regs[R_EAX] = regs->rax; + env->regs[R_EBX] = regs->rbx; + env->regs[R_ECX] = regs->rcx; + env->regs[R_EDX] = regs->rdx; + env->regs[R_ESI] = regs->rsi; + env->regs[R_EDI] = regs->rdi; + env->regs[R_EBP] = regs->rbp; + env->regs[R_ESP] = regs->rsp; + env->eip = regs->rip; +#else + env->regs[R_EAX] = regs->eax; + env->regs[R_EBX] = regs->ebx; + env->regs[R_ECX] = regs->ecx; + env->regs[R_EDX] = regs->edx; + env->regs[R_ESI] = regs->esi; + env->regs[R_EDI] = regs->edi; + env->regs[R_EBP] = regs->ebp; + env->regs[R_ESP] = regs->esp; + env->eip = regs->eip; +#endif + + /* linux interrupt setup */ +#ifndef TARGET_ABI32 + env->idt.limit = 511; +#else + env->idt.limit = 255; +#endif + env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1), + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + idt_table = g2h(env->idt.base); + set_idt(0, 0); + set_idt(1, 0); + set_idt(2, 0); + set_idt(3, 3); + set_idt(4, 3); + set_idt(5, 0); + set_idt(6, 0); + set_idt(7, 0); + set_idt(8, 0); + set_idt(9, 0); + set_idt(10, 0); + set_idt(11, 0); + set_idt(12, 0); + set_idt(13, 0); + set_idt(14, 0); + set_idt(15, 0); + set_idt(16, 0); + set_idt(17, 0); + set_idt(18, 0); + set_idt(19, 0); + set_idt(0x80, 3); + + /* linux segment setup */ + { + uint64_t *gdt_table; + env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES, + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; + gdt_table = g2h(env->gdt.base); +#ifdef TARGET_ABI32 + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#else + /* 64 bit code segment */ + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + DESC_L_MASK | + (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); +#endif + write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | + (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); + } + cpu_x86_load_seg(env, R_CS, __USER_CS); + cpu_x86_load_seg(env, R_SS, __USER_DS); +#ifdef TARGET_ABI32 + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_FS, __USER_DS); + cpu_x86_load_seg(env, R_GS, __USER_DS); + /* This hack makes Wine work... */ + env->segs[R_FS].selector = 0; +#else + cpu_x86_load_seg(env, R_DS, 0); + cpu_x86_load_seg(env, R_ES, 0); + cpu_x86_load_seg(env, R_FS, 0); + cpu_x86_load_seg(env, R_GS, 0); +#endif +} diff --git a/linux-user/i386/signal.c b/linux-user/i386/signal.c new file mode 100644 index 0000000000..fecb4c99c3 --- /dev/null +++ b/linux-user/i386/signal.c @@ -0,0 +1,595 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +/* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */ + +struct target_fpreg { + uint16_t significand[4]; + uint16_t exponent; +}; + +struct target_fpxreg { + uint16_t significand[4]; + uint16_t exponent; + uint16_t padding[3]; +}; + +struct target_xmmreg { + uint32_t element[4]; +}; + +struct target_fpstate_32 { + /* Regular FPU environment */ + uint32_t cw; + uint32_t sw; + uint32_t tag; + uint32_t ipoff; + uint32_t cssel; + uint32_t dataoff; + uint32_t datasel; + struct target_fpreg st[8]; + uint16_t status; + uint16_t magic; /* 0xffff = regular FPU data only */ + + /* FXSR FPU environment */ + uint32_t _fxsr_env[6]; /* FXSR FPU env is ignored */ + uint32_t mxcsr; + uint32_t reserved; + struct target_fpxreg fxsr_st[8]; /* FXSR FPU reg data is ignored */ + struct target_xmmreg xmm[8]; + uint32_t padding[56]; +}; + +struct target_fpstate_64 { + /* FXSAVE format */ + uint16_t cw; + uint16_t sw; + uint16_t twd; + uint16_t fop; + uint64_t rip; + uint64_t rdp; + uint32_t mxcsr; + uint32_t mxcsr_mask; + uint32_t st_space[32]; + uint32_t xmm_space[64]; + uint32_t reserved[24]; +}; + +#ifndef TARGET_X86_64 +# define target_fpstate target_fpstate_32 +#else +# define target_fpstate target_fpstate_64 +#endif + +struct target_sigcontext_32 { + uint16_t gs, __gsh; + uint16_t fs, __fsh; + uint16_t es, __esh; + uint16_t ds, __dsh; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t trapno; + uint32_t err; + uint32_t eip; + uint16_t cs, __csh; + uint32_t eflags; + uint32_t esp_at_signal; + uint16_t ss, __ssh; + uint32_t fpstate; /* pointer */ + uint32_t oldmask; + uint32_t cr2; +}; + +struct target_sigcontext_64 { + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rax; + uint64_t rcx; + uint64_t rsp; + uint64_t rip; + + uint64_t eflags; + + uint16_t cs; + uint16_t gs; + uint16_t fs; + uint16_t ss; + + uint64_t err; + uint64_t trapno; + uint64_t oldmask; + uint64_t cr2; + + uint64_t fpstate; /* pointer */ + uint64_t padding[8]; +}; + +#ifndef TARGET_X86_64 +# define target_sigcontext target_sigcontext_32 +#else +# define target_sigcontext target_sigcontext_64 +#endif + +/* see Linux/include/uapi/asm-generic/ucontext.h */ +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +#ifndef TARGET_X86_64 +struct sigframe { + abi_ulong pretcode; + int sig; + struct target_sigcontext sc; + struct target_fpstate fpstate; + abi_ulong extramask[TARGET_NSIG_WORDS-1]; + char retcode[8]; +}; + +struct rt_sigframe { + abi_ulong pretcode; + int sig; + abi_ulong pinfo; + abi_ulong puc; + struct target_siginfo info; + struct target_ucontext uc; + struct target_fpstate fpstate; + char retcode[8]; +}; + +#else + +struct rt_sigframe { + abi_ulong pretcode; + struct target_ucontext uc; + struct target_siginfo info; + struct target_fpstate fpstate; +}; + +#endif + +/* + * Set up a signal frame. + */ + +/* XXX: save x87 state */ +static void setup_sigcontext(struct target_sigcontext *sc, + struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask, + abi_ulong fpstate_addr) +{ + CPUState *cs = CPU(x86_env_get_cpu(env)); +#ifndef TARGET_X86_64 + uint16_t magic; + + /* already locked in setup_frame() */ + __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs); + __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs); + __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es); + __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds); + __put_user(env->regs[R_EDI], &sc->edi); + __put_user(env->regs[R_ESI], &sc->esi); + __put_user(env->regs[R_EBP], &sc->ebp); + __put_user(env->regs[R_ESP], &sc->esp); + __put_user(env->regs[R_EBX], &sc->ebx); + __put_user(env->regs[R_EDX], &sc->edx); + __put_user(env->regs[R_ECX], &sc->ecx); + __put_user(env->regs[R_EAX], &sc->eax); + __put_user(cs->exception_index, &sc->trapno); + __put_user(env->error_code, &sc->err); + __put_user(env->eip, &sc->eip); + __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs); + __put_user(env->eflags, &sc->eflags); + __put_user(env->regs[R_ESP], &sc->esp_at_signal); + __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss); + + cpu_x86_fsave(env, fpstate_addr, 1); + fpstate->status = fpstate->sw; + magic = 0xffff; + __put_user(magic, &fpstate->magic); + __put_user(fpstate_addr, &sc->fpstate); + + /* non-iBCS2 extensions.. */ + __put_user(mask, &sc->oldmask); + __put_user(env->cr[2], &sc->cr2); +#else + __put_user(env->regs[R_EDI], &sc->rdi); + __put_user(env->regs[R_ESI], &sc->rsi); + __put_user(env->regs[R_EBP], &sc->rbp); + __put_user(env->regs[R_ESP], &sc->rsp); + __put_user(env->regs[R_EBX], &sc->rbx); + __put_user(env->regs[R_EDX], &sc->rdx); + __put_user(env->regs[R_ECX], &sc->rcx); + __put_user(env->regs[R_EAX], &sc->rax); + + __put_user(env->regs[8], &sc->r8); + __put_user(env->regs[9], &sc->r9); + __put_user(env->regs[10], &sc->r10); + __put_user(env->regs[11], &sc->r11); + __put_user(env->regs[12], &sc->r12); + __put_user(env->regs[13], &sc->r13); + __put_user(env->regs[14], &sc->r14); + __put_user(env->regs[15], &sc->r15); + + __put_user(cs->exception_index, &sc->trapno); + __put_user(env->error_code, &sc->err); + __put_user(env->eip, &sc->rip); + + __put_user(env->eflags, &sc->eflags); + __put_user(env->segs[R_CS].selector, &sc->cs); + __put_user((uint16_t)0, &sc->gs); + __put_user((uint16_t)0, &sc->fs); + __put_user(env->segs[R_SS].selector, &sc->ss); + + __put_user(mask, &sc->oldmask); + __put_user(env->cr[2], &sc->cr2); + + /* fpstate_addr must be 16 byte aligned for fxsave */ + assert(!(fpstate_addr & 0xf)); + + cpu_x86_fxsave(env, fpstate_addr); + __put_user(fpstate_addr, &sc->fpstate); +#endif +} + +/* + * Determine which stack to use.. + */ + +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) +{ + unsigned long esp; + + /* Default to using normal stack */ + esp = get_sp_from_cpustate(env); +#ifdef TARGET_X86_64 + esp -= 128; /* this is the redzone */ +#endif + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa_flags & TARGET_SA_ONSTACK) { + esp = target_sigsp(esp, ka); + } else { +#ifndef TARGET_X86_64 + /* This is the legacy signal stack switching. */ + if ((env->segs[R_SS].selector & 0xffff) != __USER_DS && + !(ka->sa_flags & TARGET_SA_RESTORER) && + ka->sa_restorer) { + esp = (unsigned long) ka->sa_restorer; + } +#endif + } + +#ifndef TARGET_X86_64 + return (esp - frame_size) & -8ul; +#else + return ((esp - frame_size) & (~15ul)) - 8; +#endif +} + +#ifndef TARGET_X86_64 +/* compare linux/arch/i386/kernel/signal.c:setup_frame() */ +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUX86State *env) +{ + abi_ulong frame_addr; + struct sigframe *frame; + 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)) + goto give_sigsegv; + + __put_user(sig, &frame->sig); + + setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0], + frame_addr + offsetof(struct sigframe, fpstate)); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + __put_user(ka->sa_restorer, &frame->pretcode); + } else { + uint16_t val16; + abi_ulong retcode_addr; + retcode_addr = frame_addr + offsetof(struct sigframe, retcode); + __put_user(retcode_addr, &frame->pretcode); + /* This is popl %eax ; movl $,%eax ; int $0x80 */ + val16 = 0xb858; + __put_user(val16, (uint16_t *)(frame->retcode+0)); + __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2)); + val16 = 0x80cd; + __put_user(val16, (uint16_t *)(frame->retcode+6)); + } + + /* Set up registers for signal handler */ + env->regs[R_ESP] = frame_addr; + env->eip = ka->_sa_handler; + + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_SS, __USER_DS); + cpu_x86_load_seg(env, R_CS, __USER_CS); + env->eflags &= ~TF_MASK; + + unlock_user_struct(frame, frame_addr, 1); + + return; + +give_sigsegv: + force_sigsegv(sig); +} +#endif + +/* compare linux/arch/x86/kernel/signal.c:setup_rt_frame() */ +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUX86State *env) +{ + abi_ulong frame_addr; +#ifndef TARGET_X86_64 + abi_ulong addr; +#endif + struct rt_sigframe *frame; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) + goto give_sigsegv; + + /* These fields are only in rt_sigframe on 32 bit */ +#ifndef TARGET_X86_64 + __put_user(sig, &frame->sig); + addr = frame_addr + offsetof(struct rt_sigframe, info); + __put_user(addr, &frame->pinfo); + addr = frame_addr + offsetof(struct rt_sigframe, uc); + __put_user(addr, &frame->puc); +#endif + if (ka->sa_flags & TARGET_SA_SIGINFO) { + tswap_siginfo(&frame->info, info); + } + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + target_save_altstack(&frame->uc.tuc_stack, env); + setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env, + set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate)); + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ +#ifndef TARGET_X86_64 + if (ka->sa_flags & TARGET_SA_RESTORER) { + __put_user(ka->sa_restorer, &frame->pretcode); + } else { + uint16_t val16; + addr = frame_addr + offsetof(struct rt_sigframe, retcode); + __put_user(addr, &frame->pretcode); + /* This is movl $,%eax ; int $0x80 */ + __put_user(0xb8, (char *)(frame->retcode+0)); + __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1)); + val16 = 0x80cd; + __put_user(val16, (uint16_t *)(frame->retcode+5)); + } +#else + /* XXX: Would be slightly better to return -EFAULT here if test fails + assert(ka->sa_flags & TARGET_SA_RESTORER); */ + __put_user(ka->sa_restorer, &frame->pretcode); +#endif + + /* Set up registers for signal handler */ + env->regs[R_ESP] = frame_addr; + env->eip = ka->_sa_handler; + +#ifndef TARGET_X86_64 + env->regs[R_EAX] = sig; + env->regs[R_EDX] = (unsigned long)&frame->info; + env->regs[R_ECX] = (unsigned long)&frame->uc; +#else + env->regs[R_EAX] = 0; + env->regs[R_EDI] = sig; + env->regs[R_ESI] = (unsigned long)&frame->info; + env->regs[R_EDX] = (unsigned long)&frame->uc; +#endif + + cpu_x86_load_seg(env, R_DS, __USER_DS); + cpu_x86_load_seg(env, R_ES, __USER_DS); + cpu_x86_load_seg(env, R_CS, __USER_CS); + cpu_x86_load_seg(env, R_SS, __USER_DS); + env->eflags &= ~TF_MASK; + + unlock_user_struct(frame, frame_addr, 1); + + return; + +give_sigsegv: + force_sigsegv(sig); +} + +static int +restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) +{ + unsigned int err = 0; + abi_ulong fpstate_addr; + unsigned int tmpflags; + +#ifndef TARGET_X86_64 + cpu_x86_load_seg(env, R_GS, tswap16(sc->gs)); + cpu_x86_load_seg(env, R_FS, tswap16(sc->fs)); + cpu_x86_load_seg(env, R_ES, tswap16(sc->es)); + cpu_x86_load_seg(env, R_DS, tswap16(sc->ds)); + + env->regs[R_EDI] = tswapl(sc->edi); + env->regs[R_ESI] = tswapl(sc->esi); + env->regs[R_EBP] = tswapl(sc->ebp); + env->regs[R_ESP] = tswapl(sc->esp); + env->regs[R_EBX] = tswapl(sc->ebx); + env->regs[R_EDX] = tswapl(sc->edx); + env->regs[R_ECX] = tswapl(sc->ecx); + env->regs[R_EAX] = tswapl(sc->eax); + + env->eip = tswapl(sc->eip); +#else + env->regs[8] = tswapl(sc->r8); + env->regs[9] = tswapl(sc->r9); + env->regs[10] = tswapl(sc->r10); + env->regs[11] = tswapl(sc->r11); + env->regs[12] = tswapl(sc->r12); + env->regs[13] = tswapl(sc->r13); + env->regs[14] = tswapl(sc->r14); + env->regs[15] = tswapl(sc->r15); + + env->regs[R_EDI] = tswapl(sc->rdi); + env->regs[R_ESI] = tswapl(sc->rsi); + env->regs[R_EBP] = tswapl(sc->rbp); + env->regs[R_EBX] = tswapl(sc->rbx); + env->regs[R_EDX] = tswapl(sc->rdx); + env->regs[R_EAX] = tswapl(sc->rax); + env->regs[R_ECX] = tswapl(sc->rcx); + env->regs[R_ESP] = tswapl(sc->rsp); + + env->eip = tswapl(sc->rip); +#endif + + cpu_x86_load_seg(env, R_CS, lduw_p(&sc->cs) | 3); + cpu_x86_load_seg(env, R_SS, lduw_p(&sc->ss) | 3); + + tmpflags = tswapl(sc->eflags); + env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); + // regs->orig_eax = -1; /* disable syscall checks */ + + fpstate_addr = tswapl(sc->fpstate); + if (fpstate_addr != 0) { + if (!access_ok(VERIFY_READ, fpstate_addr, + sizeof(struct target_fpstate))) + goto badframe; +#ifndef TARGET_X86_64 + cpu_x86_frstor(env, fpstate_addr, 1); +#else + cpu_x86_fxrstor(env, fpstate_addr); +#endif + } + + return err; +badframe: + return 1; +} + +/* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */ +#ifndef TARGET_X86_64 +long do_sigreturn(CPUX86State *env) +{ + struct sigframe *frame; + abi_ulong frame_addr = env->regs[R_ESP] - 8; + target_sigset_t target_set; + sigset_t set; + int i; + + trace_user_do_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + /* set blocked signals */ + __get_user(target_set.sig[0], &frame->sc.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + /* restore registers */ + if (restore_sigcontext(env, &frame->sc)) + goto badframe; + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} +#endif + +long do_rt_sigreturn(CPUX86State *env) +{ + abi_ulong frame_addr; + struct rt_sigframe *frame; + sigset_t set; + + frame_addr = env->regs[R_ESP] - sizeof(abi_ulong); + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe, uc.tuc_stack), 0, + get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/i386/sockbits.h b/linux-user/i386/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/i386/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/i386/target_cpu.h b/linux-user/i386/target_cpu.h index 7fbcf9bb57..ece04d0966 100644 --- a/linux-user/i386/target_cpu.h +++ b/linux-user/i386/target_cpu.h @@ -45,4 +45,8 @@ static inline void cpu_set_tls(CPUX86State *env, target_ulong newtls) } #endif /* defined(TARGET_ABI32) */ +static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) +{ + return state->regs[R_ESP]; +} #endif /* I386_TARGET_CPU_H */ diff --git a/linux-user/i386/target_fcntl.h b/linux-user/i386/target_fcntl.h new file mode 100644 index 0000000000..4819743dae --- /dev/null +++ b/linux-user/i386/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef I386_TARGET_FCNTL_H +#define I386_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/i386/target_signal.h b/linux-user/i386/target_signal.h index 837e90fc4c..f55e78fd33 100644 --- a/linux-user/i386/target_signal.h +++ b/linux-user/i386/target_signal.h @@ -1,8 +1,6 @@ #ifndef I386_TARGET_SIGNAL_H #define I386_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,9 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) -{ - return state->regs[R_ESP]; -} +#include "../generic/signal.h" +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* I386_TARGET_SIGNAL_H */ diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c new file mode 100644 index 0000000000..b4d3d8af3d --- /dev/null +++ b/linux-user/m68k/cpu_loop.c @@ -0,0 +1,170 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void cpu_loop(CPUM68KState *env) +{ + CPUState *cs = CPU(m68k_env_get_cpu(env)); + int trapnr; + unsigned int n; + target_siginfo_t info; + TaskState *ts = cs->opaque; + + for(;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch(trapnr) { + case EXCP_ILLEGAL: + { + if (ts->sim_syscalls) { + uint16_t nr; + get_user_u16(nr, env->pc + 2); + env->pc += 4; + do_m68k_simcall(env, nr); + } else { + goto do_sigill; + } + } + break; + case EXCP_HALT_INSN: + /* Semihosing syscall. */ + env->pc += 4; + do_m68k_semihosting(env, env->dregs[0]); + break; + case EXCP_LINEA: + case EXCP_LINEF: + case EXCP_UNSUPPORTED: + do_sigill: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_CHK: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTOVF; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_DIV0: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTDIV; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_TRAP0: + { + abi_long ret; + ts->sim_syscalls = 0; + n = env->dregs[0]; + env->pc += 2; + ret = do_syscall(env, + n, + env->dregs[1], + env->dregs[2], + env->dregs[3], + env->dregs[4], + env->dregs[5], + env->aregs[0], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->dregs[0] = ret; + } + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_ACCESS: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->mmu.ar; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + abort(); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + + env->pc = regs->pc; + env->dregs[0] = regs->d0; + env->dregs[1] = regs->d1; + env->dregs[2] = regs->d2; + env->dregs[3] = regs->d3; + env->dregs[4] = regs->d4; + env->dregs[5] = regs->d5; + env->dregs[6] = regs->d6; + env->dregs[7] = regs->d7; + env->aregs[0] = regs->a0; + env->aregs[1] = regs->a1; + env->aregs[2] = regs->a2; + env->aregs[3] = regs->a3; + env->aregs[4] = regs->a4; + env->aregs[5] = regs->a5; + env->aregs[6] = regs->a6; + env->aregs[7] = regs->usp; + env->sr = regs->sr; + + ts->sim_syscalls = 1; + ts->stack_base = info->start_stack; + ts->heap_base = info->brk; + /* This will be filled in on the first SYS_HEAPINFO call. */ + ts->heap_limit = 0; +} diff --git a/linux-user/m68k/signal.c b/linux-user/m68k/signal.c new file mode 100644 index 0000000000..38bd77ec16 --- /dev/null +++ b/linux-user/m68k/signal.c @@ -0,0 +1,418 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_ulong sc_mask; + abi_ulong sc_usp; + abi_ulong sc_d0; + abi_ulong sc_d1; + abi_ulong sc_a0; + abi_ulong sc_a1; + unsigned short sc_sr; + abi_ulong sc_pc; +}; + +struct target_sigframe +{ + abi_ulong pretcode; + int sig; + int code; + abi_ulong psc; + char retcode[8]; + abi_ulong extramask[TARGET_NSIG_WORDS-1]; + struct target_sigcontext sc; +}; + +typedef int target_greg_t; +#define TARGET_NGREG 18 +typedef target_greg_t target_gregset_t[TARGET_NGREG]; + +typedef struct target_fpregset { + int f_fpcntl[3]; + int f_fpregs[8*3]; +} target_fpregset_t; + +struct target_mcontext { + int version; + target_gregset_t gregs; + target_fpregset_t fpregs; +}; + +#define TARGET_MCONTEXT_VERSION 2 + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_mcontext tuc_mcontext; + abi_long tuc_filler[80]; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe +{ + abi_ulong pretcode; + int sig; + abi_ulong pinfo; + abi_ulong puc; + char retcode[8]; + struct target_siginfo info; + struct target_ucontext uc; +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUM68KState *env, + abi_ulong mask) +{ + uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); + __put_user(mask, &sc->sc_mask); + __put_user(env->aregs[7], &sc->sc_usp); + __put_user(env->dregs[0], &sc->sc_d0); + __put_user(env->dregs[1], &sc->sc_d1); + __put_user(env->aregs[0], &sc->sc_a0); + __put_user(env->aregs[1], &sc->sc_a1); + __put_user(sr, &sc->sc_sr); + __put_user(env->pc, &sc->sc_pc); +} + +static void +restore_sigcontext(CPUM68KState *env, struct target_sigcontext *sc) +{ + int temp; + + __get_user(env->aregs[7], &sc->sc_usp); + __get_user(env->dregs[0], &sc->sc_d0); + __get_user(env->dregs[1], &sc->sc_d1); + __get_user(env->aregs[0], &sc->sc_a0); + __get_user(env->aregs[1], &sc->sc_a1); + __get_user(env->pc, &sc->sc_pc); + __get_user(temp, &sc->sc_sr); + cpu_m68k_set_ccr(env, temp); +} + +/* + * Determine which stack to use.. + */ +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUM68KState *regs, + size_t frame_size) +{ + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(regs), ka); + + + return ((sp - frame_size) & -8UL); +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUM68KState *env) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + abi_ulong retcode_addr; + abi_ulong sc_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)) { + goto give_sigsegv; + } + + __put_user(sig, &frame->sig); + + sc_addr = frame_addr + offsetof(struct target_sigframe, sc); + __put_user(sc_addr, &frame->psc); + + setup_sigcontext(&frame->sc, env, set->sig[0]); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + /* Set up to return from userspace. */ + + retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); + __put_user(retcode_addr, &frame->pretcode); + + /* moveq #,d0; trap #0 */ + + __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), + (uint32_t *)(frame->retcode)); + + /* Set up to return from userspace */ + + env->aregs[7] = frame_addr; + env->pc = ka->_sa_handler; + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +static inline void target_rt_save_fpu_state(struct target_ucontext *uc, + CPUM68KState *env) +{ + int i; + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; + + __put_user(env->fpcr, &fpregs->f_fpcntl[0]); + __put_user(env->fpsr, &fpregs->f_fpcntl[1]); + /* fpiar is not emulated */ + + for (i = 0; i < 8; i++) { + uint32_t high = env->fregs[i].d.high << 16; + __put_user(high, &fpregs->f_fpregs[i * 3]); + __put_user(env->fregs[i].d.low, + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); + } +} + +static inline int target_rt_setup_ucontext(struct target_ucontext *uc, + CPUM68KState *env) +{ + target_greg_t *gregs = uc->tuc_mcontext.gregs; + uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); + + __put_user(TARGET_MCONTEXT_VERSION, &uc->tuc_mcontext.version); + __put_user(env->dregs[0], &gregs[0]); + __put_user(env->dregs[1], &gregs[1]); + __put_user(env->dregs[2], &gregs[2]); + __put_user(env->dregs[3], &gregs[3]); + __put_user(env->dregs[4], &gregs[4]); + __put_user(env->dregs[5], &gregs[5]); + __put_user(env->dregs[6], &gregs[6]); + __put_user(env->dregs[7], &gregs[7]); + __put_user(env->aregs[0], &gregs[8]); + __put_user(env->aregs[1], &gregs[9]); + __put_user(env->aregs[2], &gregs[10]); + __put_user(env->aregs[3], &gregs[11]); + __put_user(env->aregs[4], &gregs[12]); + __put_user(env->aregs[5], &gregs[13]); + __put_user(env->aregs[6], &gregs[14]); + __put_user(env->aregs[7], &gregs[15]); + __put_user(env->pc, &gregs[16]); + __put_user(sr, &gregs[17]); + + target_rt_save_fpu_state(uc, env); + + return 0; +} + +static inline void target_rt_restore_fpu_state(CPUM68KState *env, + struct target_ucontext *uc) +{ + int i; + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; + uint32_t fpcr; + + __get_user(fpcr, &fpregs->f_fpcntl[0]); + cpu_m68k_set_fpcr(env, fpcr); + __get_user(env->fpsr, &fpregs->f_fpcntl[1]); + /* fpiar is not emulated */ + + for (i = 0; i < 8; i++) { + uint32_t high; + __get_user(high, &fpregs->f_fpregs[i * 3]); + env->fregs[i].d.high = high >> 16; + __get_user(env->fregs[i].d.low, + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); + } +} + +static inline int target_rt_restore_ucontext(CPUM68KState *env, + struct target_ucontext *uc) +{ + int temp; + target_greg_t *gregs = uc->tuc_mcontext.gregs; + + __get_user(temp, &uc->tuc_mcontext.version); + if (temp != TARGET_MCONTEXT_VERSION) + goto badframe; + + /* restore passed registers */ + __get_user(env->dregs[0], &gregs[0]); + __get_user(env->dregs[1], &gregs[1]); + __get_user(env->dregs[2], &gregs[2]); + __get_user(env->dregs[3], &gregs[3]); + __get_user(env->dregs[4], &gregs[4]); + __get_user(env->dregs[5], &gregs[5]); + __get_user(env->dregs[6], &gregs[6]); + __get_user(env->dregs[7], &gregs[7]); + __get_user(env->aregs[0], &gregs[8]); + __get_user(env->aregs[1], &gregs[9]); + __get_user(env->aregs[2], &gregs[10]); + __get_user(env->aregs[3], &gregs[11]); + __get_user(env->aregs[4], &gregs[12]); + __get_user(env->aregs[5], &gregs[13]); + __get_user(env->aregs[6], &gregs[14]); + __get_user(env->aregs[7], &gregs[15]); + __get_user(env->pc, &gregs[16]); + __get_user(temp, &gregs[17]); + cpu_m68k_set_ccr(env, temp); + + target_rt_restore_fpu_state(env, uc); + + return 0; + +badframe: + return 1; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUM68KState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + abi_ulong retcode_addr; + abi_ulong info_addr; + abi_ulong uc_addr; + int err = 0; + int i; + + frame_addr = get_sigframe(ka, env, sizeof *frame); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + __put_user(sig, &frame->sig); + + info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); + __put_user(info_addr, &frame->pinfo); + + uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); + __put_user(uc_addr, &frame->puc); + + tswap_siginfo(&frame->info, info); + + /* Create the ucontext */ + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + target_save_altstack(&frame->uc.tuc_stack, env); + err |= target_rt_setup_ucontext(&frame->uc, env); + + if (err) + goto give_sigsegv; + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + /* Set up to return from userspace. */ + + retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); + __put_user(retcode_addr, &frame->pretcode); + + /* moveq #,d0; notb d0; trap #0 */ + + __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16), + (uint32_t *)(frame->retcode + 0)); + __put_user(0x4e40, (uint16_t *)(frame->retcode + 4)); + + if (err) + goto give_sigsegv; + + /* Set up to return from userspace */ + + env->aregs[7] = frame_addr; + env->pc = ka->_sa_handler; + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +long do_sigreturn(CPUM68KState *env) +{ + struct target_sigframe *frame; + abi_ulong frame_addr = env->aregs[7] - 4; + target_sigset_t target_set; + sigset_t set; + int i; + + trace_user_do_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + + /* set blocked signals */ + + __get_user(target_set.sig[0], &frame->sc.sc_mask); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + /* restore registers */ + + restore_sigcontext(env, &frame->sc); + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUM68KState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr = env->aregs[7] - 4; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + /* restore registers */ + + if (target_rt_restore_ucontext(env, &frame->uc)) + goto badframe; + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) + goto badframe; + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/m68k/sockbits.h b/linux-user/m68k/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/m68k/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/m68k/target_cpu.h b/linux-user/m68k/target_cpu.h index cc0bfc298e..611df065ca 100644 --- a/linux-user/m68k/target_cpu.h +++ b/linux-user/m68k/target_cpu.h @@ -37,4 +37,8 @@ static inline void cpu_set_tls(CPUM68KState *env, target_ulong newtls) ts->tp_value = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUM68KState *state) +{ + return state->aregs[7]; +} #endif diff --git a/linux-user/m68k/target_fcntl.h b/linux-user/m68k/target_fcntl.h new file mode 100644 index 0000000000..068bc3243e --- /dev/null +++ b/linux-user/m68k/target_fcntl.h @@ -0,0 +1,17 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef M68K_TARGET_FCNTL_H +#define M68K_TARGET_FCNTL_H + +#define TARGET_O_DIRECTORY 040000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ +#define TARGET_O_DIRECT 0200000 /* direct disk access hint */ +#define TARGET_O_LARGEFILE 0400000 + +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/m68k/target_signal.h b/linux-user/m68k/target_signal.h index 9d2d7343f8..314e808844 100644 --- a/linux-user/m68k/target_signal.h +++ b/linux-user/m68k/target_signal.h @@ -1,8 +1,6 @@ #ifndef M68K_TARGET_SIGNAL_H #define M68K_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUM68KState *state) -{ - return state->aregs[7]; -} - +#include "../generic/signal.h" +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* M68K_TARGET_SIGNAL_H */ diff --git a/linux-user/main.c b/linux-user/main.c index 8907a84114..ea00dd9057 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu-version.h" #include #include @@ -33,9 +34,9 @@ #include "qemu/timer.h" #include "qemu/envlist.h" #include "elf.h" -#include "exec/log.h" #include "trace/control.h" #include "target_elf.h" +#include "cpu_loop-common.h" char *exec_path; @@ -50,17 +51,6 @@ unsigned long mmap_min_addr; unsigned long guest_base; int have_guest_base; -#define EXCP_DUMP(env, fmt, ...) \ -do { \ - CPUState *cs = ENV_GET_CPU(env); \ - fprintf(stderr, fmt , ## __VA_ARGS__); \ - cpu_dump_state(cs, stderr, fprintf, 0); \ - if (qemu_log_separate()) { \ - qemu_log(fmt, ## __VA_ARGS__); \ - log_cpu_state(cs, 0); \ - } \ -} while (0) - /* * When running 32-on-64 we should make sure we can fit all of the possible * guest address space into a contiguous chunk of virtual host memory. @@ -88,14 +78,7 @@ do { \ # endif #endif -/* That said, reserving *too* much vm space via mmap can run into problems - with rlimits, oom due to page table creation, etc. We will still try it, - if directed by the command-line option, but not by default. */ -#if HOST_LONG_BITS == 64 && TARGET_VIRT_ADDR_SPACE_BITS <= 32 -unsigned long reserved_va = MAX_RESERVED_VA; -#else unsigned long reserved_va; -#endif static void usage(int exitcode); @@ -131,7 +114,6 @@ void fork_start(void) { start_exclusive(); mmap_fork_start(); - qemu_mutex_lock(&tb_ctx.tb_lock); cpu_list_lock(); } @@ -147,3940 +129,17 @@ void fork_end(int child) QTAILQ_REMOVE(&cpus, cpu, node); } } - qemu_mutex_init(&tb_ctx.tb_lock); qemu_init_cpu_list(); gdbserver_fork(thread_cpu); /* qemu_init_cpu_list() takes care of reinitializing the * exclusive state, so we don't need to end_exclusive() here. */ } else { - qemu_mutex_unlock(&tb_ctx.tb_lock); cpu_list_unlock(); end_exclusive(); } } -#ifdef TARGET_I386 -/***********************************************************/ -/* CPUX86 core interface */ - -uint64_t cpu_get_tsc(CPUX86State *env) -{ - return cpu_get_host_ticks(); -} - -static void write_dt(void *ptr, unsigned long addr, unsigned long limit, - int flags) -{ - unsigned int e1, e2; - uint32_t *p; - e1 = (addr << 16) | (limit & 0xffff); - e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); - e2 |= flags; - p = ptr; - p[0] = tswap32(e1); - p[1] = tswap32(e2); -} - -static uint64_t *idt_table; -#ifdef TARGET_X86_64 -static void set_gate64(void *ptr, unsigned int type, unsigned int dpl, - uint64_t addr, unsigned int sel) -{ - uint32_t *p, e1, e2; - e1 = (addr & 0xffff) | (sel << 16); - e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); - p = ptr; - p[0] = tswap32(e1); - p[1] = tswap32(e2); - p[2] = tswap32(addr >> 32); - p[3] = 0; -} -/* only dpl matters as we do only user space emulation */ -static void set_idt(int n, unsigned int dpl) -{ - set_gate64(idt_table + n * 2, 0, dpl, 0, 0); -} -#else -static void set_gate(void *ptr, unsigned int type, unsigned int dpl, - uint32_t addr, unsigned int sel) -{ - uint32_t *p, e1, e2; - e1 = (addr & 0xffff) | (sel << 16); - e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8); - p = ptr; - p[0] = tswap32(e1); - p[1] = tswap32(e2); -} - -/* only dpl matters as we do only user space emulation */ -static void set_idt(int n, unsigned int dpl) -{ - set_gate(idt_table + n, 0, dpl, 0, 0); -} -#endif - -void cpu_loop(CPUX86State *env) -{ - CPUState *cs = CPU(x86_env_get_cpu(env)); - int trapnr; - abi_ulong pc; - abi_ulong ret; - target_siginfo_t info; - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case 0x80: - /* linux syscall from int $0x80 */ - ret = do_syscall(env, - env->regs[R_EAX], - env->regs[R_EBX], - env->regs[R_ECX], - env->regs[R_EDX], - env->regs[R_ESI], - env->regs[R_EDI], - env->regs[R_EBP], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->eip -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[R_EAX] = ret; - } - break; -#ifndef TARGET_ABI32 - case EXCP_SYSCALL: - /* linux syscall from syscall instruction */ - ret = do_syscall(env, - env->regs[R_EAX], - env->regs[R_EDI], - env->regs[R_ESI], - env->regs[R_EDX], - env->regs[10], - env->regs[8], - env->regs[9], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->eip -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[R_EAX] = ret; - } - break; -#endif - case EXCP0B_NOSEG: - case EXCP0C_STACK: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP0D_GPF: - /* XXX: potential problem if ABI32 */ -#ifndef TARGET_X86_64 - if (env->eflags & VM_MASK) { - handle_vm86_fault(env); - } else -#endif - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP0E_PAGE: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - if (!(env->error_code & 1)) - info.si_code = TARGET_SEGV_MAPERR; - else - info.si_code = TARGET_SEGV_ACCERR; - info._sifields._sigfault._addr = env->cr[2]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP00_DIVZ: -#ifndef TARGET_X86_64 - if (env->eflags & VM_MASK) { - handle_vm86_trap(env, trapnr); - } else -#endif - { - /* division by zero */ - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTDIV; - info._sifields._sigfault._addr = env->eip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP01_DB: - case EXCP03_INT3: -#ifndef TARGET_X86_64 - if (env->eflags & VM_MASK) { - handle_vm86_trap(env, trapnr); - } else -#endif - { - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - if (trapnr == EXCP01_DB) { - info.si_code = TARGET_TRAP_BRKPT; - info._sifields._sigfault._addr = env->eip; - } else { - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - } - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP04_INTO: - case EXCP05_BOUND: -#ifndef TARGET_X86_64 - if (env->eflags & VM_MASK) { - handle_vm86_trap(env, trapnr); - } else -#endif - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SI_KERNEL; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP06_ILLOP: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->eip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - pc = env->segs[R_CS].base + env->eip; - EXCP_DUMP(env, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n", - (long)pc, trapnr); - abort(); - } - process_pending_signals(env); - } -} -#endif - -#ifdef TARGET_ARM - -#define get_user_code_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_code_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && bswap_code(arm_sctlr_b(env))) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define get_user_data_u32(x, gaddr, env) \ - ({ abi_long __r = get_user_u32((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap32(x); \ - } \ - __r; \ - }) - -#define get_user_data_u16(x, gaddr, env) \ - ({ abi_long __r = get_user_u16((x), (gaddr)); \ - if (!__r && arm_cpu_bswap_data(env)) { \ - (x) = bswap16(x); \ - } \ - __r; \ - }) - -#define put_user_data_u32(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap32(__x); \ - } \ - put_user_u32(__x, (gaddr)); \ - }) - -#define put_user_data_u16(x, gaddr, env) \ - ({ typeof(x) __x = (x); \ - if (arm_cpu_bswap_data(env)) { \ - __x = bswap16(__x); \ - } \ - put_user_u16(__x, (gaddr)); \ - }) - -#ifdef TARGET_ABI32 -/* Commpage handling -- there is no commpage for AArch64 */ - -/* - * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt - * Input: - * r0 = pointer to oldval - * r1 = pointer to newval - * r2 = pointer to target value - * - * Output: - * r0 = 0 if *ptr was changed, non-0 if no exchange happened - * C set if *ptr was changed, clear if no exchange happened - * - * Note segv's in kernel helpers are a bit tricky, we can set the - * data address sensibly but the PC address is just the entry point. - */ -static void arm_kernel_cmpxchg64_helper(CPUARMState *env) -{ - uint64_t oldval, newval, val; - uint32_t addr, cpsr; - target_siginfo_t info; - - /* Based on the 32 bit code in do_kernel_trap */ - - /* XXX: This only works between threads, not between processes. - It's probably possible to implement this with native host - operations. However things like ldrex/strex are much harder so - there's not much point trying. */ - start_exclusive(); - cpsr = cpsr_read(env); - addr = env->regs[2]; - - if (get_user_u64(oldval, env->regs[0])) { - env->exception.vaddress = env->regs[0]; - goto segv; - }; - - if (get_user_u64(newval, env->regs[1])) { - env->exception.vaddress = env->regs[1]; - goto segv; - }; - - if (get_user_u64(val, addr)) { - env->exception.vaddress = addr; - goto segv; - } - - if (val == oldval) { - val = newval; - - if (put_user_u64(val, addr)) { - env->exception.vaddress = addr; - goto segv; - }; - - env->regs[0] = 0; - cpsr |= CPSR_C; - } else { - env->regs[0] = -1; - cpsr &= ~CPSR_C; - } - cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); - end_exclusive(); - return; - -segv: - end_exclusive(); - /* We get the PC of the entry address - which is as good as anything, - on a real kernel what you get depends on which mode it uses. */ - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->exception.vaddress; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); -} - -/* Handle a jump to the kernel code page. */ -static int -do_kernel_trap(CPUARMState *env) -{ - uint32_t addr; - uint32_t cpsr; - uint32_t val; - - switch (env->regs[15]) { - case 0xffff0fa0: /* __kernel_memory_barrier */ - /* ??? No-op. Will need to do better for SMP. */ - break; - case 0xffff0fc0: /* __kernel_cmpxchg */ - /* XXX: This only works between threads, not between processes. - It's probably possible to implement this with native host - operations. However things like ldrex/strex are much harder so - there's not much point trying. */ - start_exclusive(); - cpsr = cpsr_read(env); - addr = env->regs[2]; - /* FIXME: This should SEGV if the access fails. */ - if (get_user_u32(val, addr)) - val = ~env->regs[0]; - if (val == env->regs[0]) { - val = env->regs[1]; - /* FIXME: Check for segfaults. */ - put_user_u32(val, addr); - env->regs[0] = 0; - cpsr |= CPSR_C; - } else { - env->regs[0] = -1; - cpsr &= ~CPSR_C; - } - cpsr_write(env, cpsr, CPSR_C, CPSRWriteByInstr); - end_exclusive(); - break; - case 0xffff0fe0: /* __kernel_get_tls */ - env->regs[0] = cpu_get_tls(env); - break; - case 0xffff0f60: /* __kernel_cmpxchg64 */ - arm_kernel_cmpxchg64_helper(env); - break; - - default: - return 1; - } - /* Jump back to the caller. */ - addr = env->regs[14]; - if (addr & 1) { - env->thumb = 1; - addr &= ~1; - } - env->regs[15] = addr; - - return 0; -} - -void cpu_loop(CPUARMState *env) -{ - CPUState *cs = CPU(arm_env_get_cpu(env)); - int trapnr; - unsigned int n, insn; - target_siginfo_t info; - uint32_t addr; - abi_ulong ret; - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case EXCP_UDEF: - case EXCP_NOCP: - case EXCP_INVSTATE: - { - TaskState *ts = cs->opaque; - uint32_t opcode; - int rc; - - /* we handle the FPU emulation here, as Linux */ - /* we get the opcode */ - /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(opcode, env->regs[15], env); - - rc = EmulateAll(opcode, &ts->fpa, env); - if (rc == 0) { /* illegal instruction */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->regs[15]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else if (rc < 0) { /* FP exception */ - int arm_fpe=0; - - /* translate softfloat flags to FPSR flags */ - if (-rc & float_flag_invalid) - arm_fpe |= BIT_IOC; - if (-rc & float_flag_divbyzero) - arm_fpe |= BIT_DZC; - if (-rc & float_flag_overflow) - arm_fpe |= BIT_OFC; - if (-rc & float_flag_underflow) - arm_fpe |= BIT_UFC; - if (-rc & float_flag_inexact) - arm_fpe |= BIT_IXC; - - FPSR fpsr = ts->fpa.fpsr; - //printf("fpsr 0x%x, arm_fpe 0x%x\n",fpsr,arm_fpe); - - if (fpsr & (arm_fpe << 16)) { /* exception enabled? */ - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - - /* ordered by priority, least first */ - if (arm_fpe & BIT_IXC) info.si_code = TARGET_FPE_FLTRES; - if (arm_fpe & BIT_UFC) info.si_code = TARGET_FPE_FLTUND; - if (arm_fpe & BIT_OFC) info.si_code = TARGET_FPE_FLTOVF; - if (arm_fpe & BIT_DZC) info.si_code = TARGET_FPE_FLTDIV; - if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV; - - info._sifields._sigfault._addr = env->regs[15]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else { - env->regs[15] += 4; - } - - /* accumulate unenabled exceptions */ - if ((!(fpsr & BIT_IXE)) && (arm_fpe & BIT_IXC)) - fpsr |= BIT_IXC; - if ((!(fpsr & BIT_UFE)) && (arm_fpe & BIT_UFC)) - fpsr |= BIT_UFC; - if ((!(fpsr & BIT_OFE)) && (arm_fpe & BIT_OFC)) - fpsr |= BIT_OFC; - if ((!(fpsr & BIT_DZE)) && (arm_fpe & BIT_DZC)) - fpsr |= BIT_DZC; - if ((!(fpsr & BIT_IOE)) && (arm_fpe & BIT_IOC)) - fpsr |= BIT_IOC; - ts->fpa.fpsr=fpsr; - } else { /* everything OK */ - /* increment PC */ - env->regs[15] += 4; - } - } - break; - case EXCP_SWI: - case EXCP_BKPT: - { - env->eabi = 1; - /* system call */ - if (trapnr == EXCP_BKPT) { - if (env->thumb) { - /* FIXME - what to do if get_user() fails? */ - get_user_code_u16(insn, env->regs[15], env); - n = insn & 0xff; - env->regs[15] += 2; - } else { - /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(insn, env->regs[15], env); - n = (insn & 0xf) | ((insn >> 4) & 0xff0); - env->regs[15] += 4; - } - } else { - if (env->thumb) { - /* FIXME - what to do if get_user() fails? */ - get_user_code_u16(insn, env->regs[15] - 2, env); - n = insn & 0xff; - } else { - /* FIXME - what to do if get_user() fails? */ - get_user_code_u32(insn, env->regs[15] - 4, env); - n = insn & 0xffffff; - } - } - - if (n == ARM_NR_cacheflush) { - /* nop */ - } else if (n == ARM_NR_semihosting - || n == ARM_NR_thumb_semihosting) { - env->regs[0] = do_arm_semihosting (env); - } else if (n == 0 || n >= ARM_SYSCALL_BASE || env->thumb) { - /* linux syscall */ - if (env->thumb || n == 0) { - n = env->regs[7]; - } else { - n -= ARM_SYSCALL_BASE; - env->eabi = 0; - } - if ( n > ARM_NR_BASE) { - switch (n) { - case ARM_NR_cacheflush: - /* nop */ - break; - case ARM_NR_set_tls: - cpu_set_tls(env, env->regs[0]); - env->regs[0] = 0; - break; - case ARM_NR_breakpoint: - env->regs[15] -= env->thumb ? 2 : 4; - goto excp_debug; - default: - gemu_log("qemu: Unsupported ARM syscall: 0x%x\n", - n); - env->regs[0] = -TARGET_ENOSYS; - break; - } - } else { - ret = do_syscall(env, - n, - env->regs[0], - env->regs[1], - env->regs[2], - env->regs[3], - env->regs[4], - env->regs[5], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->regs[15] -= env->thumb ? 2 : 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[0] = ret; - } - } - } else { - goto error; - } - } - break; - case EXCP_SEMIHOST: - env->regs[0] = do_arm_semihosting(env); - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_PREFETCH_ABORT: - case EXCP_DATA_ABORT: - addr = env->exception.vaddress; - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = addr; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DEBUG: - excp_debug: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_KERNEL_TRAP: - if (do_kernel_trap(env)) - goto error; - 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: - error: - EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - abort(); - } - process_pending_signals(env); - } -} - -#else - -/* AArch64 main loop */ -void cpu_loop(CPUARMState *env) -{ - CPUState *cs = CPU(arm_env_get_cpu(env)); - int trapnr, sig; - abi_long ret; - target_siginfo_t info; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_SWI: - ret = do_syscall(env, - env->xregs[8], - env->xregs[0], - env->xregs[1], - env->xregs[2], - env->xregs[3], - env->xregs[4], - env->xregs[5], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->xregs[0] = ret; - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_UDEF: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_PREFETCH_ABORT: - case EXCP_DATA_ABORT: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->exception.vaddress; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_DEBUG: - case EXCP_BKPT: - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_SEMIHOST: - env->xregs[0] = do_arm_semihosting(env); - 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: - EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - abort(); - } - process_pending_signals(env); - /* Exception return on AArch64 always clears the exclusive monitor, - * so any return to running guest code implies this. - */ - env->exclusive_addr = -1; - } -} -#endif /* ndef TARGET_ABI32 */ - -#endif - -#ifdef TARGET_SPARC -#define SPARC64_STACK_BIAS 2047 - -//#define DEBUG_WIN - -/* WARNING: dealing with register windows _is_ complicated. More info - can be found at http://www.sics.se/~psm/sparcstack.html */ -static inline int get_reg_index(CPUSPARCState *env, int cwp, int index) -{ - index = (index + cwp * 16) % (16 * env->nwindows); - /* wrap handling : if cwp is on the last window, then we use the - registers 'after' the end */ - if (index < 8 && env->cwp == env->nwindows - 1) - index += 16 * env->nwindows; - return index; -} - -/* save the register window 'cwp1' */ -static inline void save_window_offset(CPUSPARCState *env, int cwp1) -{ - unsigned int i; - abi_ulong sp_ptr; - - sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; -#ifdef TARGET_SPARC64 - if (sp_ptr & 3) - sp_ptr += SPARC64_STACK_BIAS; -#endif -#if defined(DEBUG_WIN) - printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n", - sp_ptr, cwp1); -#endif - for(i = 0; i < 16; i++) { - /* FIXME - what to do if put_user() fails? */ - put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); - sp_ptr += sizeof(abi_ulong); - } -} - -static void save_window(CPUSPARCState *env) -{ -#ifndef TARGET_SPARC64 - unsigned int new_wim; - new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) & - ((1LL << env->nwindows) - 1); - save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); - env->wim = new_wim; -#else - save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); - env->cansave++; - env->canrestore--; -#endif -} - -static void restore_window(CPUSPARCState *env) -{ -#ifndef TARGET_SPARC64 - unsigned int new_wim; -#endif - unsigned int i, cwp1; - abi_ulong sp_ptr; - -#ifndef TARGET_SPARC64 - new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) & - ((1LL << env->nwindows) - 1); -#endif - - /* restore the invalid window */ - cwp1 = cpu_cwp_inc(env, env->cwp + 1); - sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; -#ifdef TARGET_SPARC64 - if (sp_ptr & 3) - sp_ptr += SPARC64_STACK_BIAS; -#endif -#if defined(DEBUG_WIN) - printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n", - sp_ptr, cwp1); -#endif - for(i = 0; i < 16; i++) { - /* FIXME - what to do if get_user() fails? */ - get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); - sp_ptr += sizeof(abi_ulong); - } -#ifdef TARGET_SPARC64 - env->canrestore++; - if (env->cleanwin < env->nwindows - 1) - env->cleanwin++; - env->cansave--; -#else - env->wim = new_wim; -#endif -} - -static void flush_windows(CPUSPARCState *env) -{ - int offset, cwp1; - - offset = 1; - for(;;) { - /* if restore would invoke restore_window(), then we can stop */ - cwp1 = cpu_cwp_inc(env, env->cwp + offset); -#ifndef TARGET_SPARC64 - if (env->wim & (1 << cwp1)) - break; -#else - if (env->canrestore == 0) - break; - env->cansave++; - env->canrestore--; -#endif - save_window_offset(env, cwp1); - offset++; - } - cwp1 = cpu_cwp_inc(env, env->cwp + 1); -#ifndef TARGET_SPARC64 - /* set wim so that restore will reload the registers */ - env->wim = 1 << cwp1; -#endif -#if defined(DEBUG_WIN) - printf("flush_windows: nb=%d\n", offset - 1); -#endif -} - -void cpu_loop (CPUSPARCState *env) -{ - CPUState *cs = CPU(sparc_env_get_cpu(env)); - int trapnr; - abi_long ret; - target_siginfo_t info; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - /* Compute PSR before exposing state. */ - if (env->cc_op != CC_OP_FLAGS) { - cpu_get_psr(env); - } - - switch (trapnr) { -#ifndef TARGET_SPARC64 - case 0x88: - case 0x90: -#else - case 0x110: - case 0x16d: -#endif - ret = do_syscall (env, env->gregs[1], - env->regwptr[0], env->regwptr[1], - env->regwptr[2], env->regwptr[3], - env->regwptr[4], env->regwptr[5], - 0, 0); - if (ret == -TARGET_ERESTARTSYS || ret == -TARGET_QEMU_ESIGRETURN) { - break; - } - if ((abi_ulong)ret >= (abi_ulong)(-515)) { -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - env->xcc |= PSR_CARRY; -#else - env->psr |= PSR_CARRY; -#endif - ret = -ret; - } else { -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) - env->xcc &= ~PSR_CARRY; -#else - env->psr &= ~PSR_CARRY; -#endif - } - env->regwptr[0] = ret; - /* next instruction */ - env->pc = env->npc; - env->npc = env->npc + 4; - break; - case 0x83: /* flush windows */ -#ifdef TARGET_ABI32 - case 0x103: -#endif - flush_windows(env); - /* next instruction */ - env->pc = env->npc; - env->npc = env->npc + 4; - break; -#ifndef TARGET_SPARC64 - case TT_WIN_OVF: /* window overflow */ - save_window(env); - break; - case TT_WIN_UNF: /* window underflow */ - restore_window(env); - break; - case TT_TFAULT: - case TT_DFAULT: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->mmuregs[4]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; -#else - case TT_SPILL: /* window overflow */ - save_window(env); - break; - case TT_FILL: /* window underflow */ - restore_window(env); - break; - case TT_TFAULT: - case TT_DFAULT: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - if (trapnr == TT_DFAULT) - info._sifields._sigfault._addr = env->dmmu.mmuregs[4]; - else - info._sifields._sigfault._addr = cpu_tsptr(env)->tpc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; -#ifndef TARGET_ABI32 - case 0x16e: - flush_windows(env); - sparc64_get_context(env); - break; - case 0x16f: - flush_windows(env); - sparc64_set_context(env); - break; -#endif -#endif - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case TT_ILL_INSN: - { - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - } -} - -#endif - -#ifdef TARGET_PPC -static inline uint64_t cpu_ppc_get_tb(CPUPPCState *env) -{ - return cpu_get_host_ticks(); -} - -uint64_t cpu_ppc_load_tbl(CPUPPCState *env) -{ - return cpu_ppc_get_tb(env); -} - -uint32_t cpu_ppc_load_tbu(CPUPPCState *env) -{ - return cpu_ppc_get_tb(env) >> 32; -} - -uint64_t cpu_ppc_load_atbl(CPUPPCState *env) -{ - return cpu_ppc_get_tb(env); -} - -uint32_t cpu_ppc_load_atbu(CPUPPCState *env) -{ - return cpu_ppc_get_tb(env) >> 32; -} - -uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env) -__attribute__ (( alias ("cpu_ppc_load_tbu") )); - -uint32_t cpu_ppc601_load_rtcl(CPUPPCState *env) -{ - return cpu_ppc_load_tbl(env) & 0x3FFFFF80; -} - -/* XXX: to be fixed */ -int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) -{ - return -1; -} - -int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) -{ - return -1; -} - -static int do_store_exclusive(CPUPPCState *env) -{ - target_ulong addr; - target_ulong page_addr; - target_ulong val, val2 __attribute__((unused)) = 0; - int flags; - int segv = 0; - - addr = env->reserve_ea; - page_addr = addr & TARGET_PAGE_MASK; - start_exclusive(); - mmap_lock(); - flags = page_get_flags(page_addr); - if ((flags & PAGE_READ) == 0) { - segv = 1; - } else { - int reg = env->reserve_info & 0x1f; - int size = env->reserve_info >> 5; - int stored = 0; - - if (addr == env->reserve_addr) { - switch (size) { - case 1: segv = get_user_u8(val, addr); break; - case 2: segv = get_user_u16(val, addr); break; - case 4: segv = get_user_u32(val, addr); break; -#if defined(TARGET_PPC64) - case 8: segv = get_user_u64(val, addr); break; - case 16: { - segv = get_user_u64(val, addr); - if (!segv) { - segv = get_user_u64(val2, addr + 8); - } - break; - } -#endif - default: abort(); - } - if (!segv && val == env->reserve_val) { - val = env->gpr[reg]; - switch (size) { - case 1: segv = put_user_u8(val, addr); break; - case 2: segv = put_user_u16(val, addr); break; - case 4: segv = put_user_u32(val, addr); break; -#if defined(TARGET_PPC64) - case 8: segv = put_user_u64(val, addr); break; - case 16: { - if (val2 == env->reserve_val2) { - if (msr_le) { - val2 = val; - val = env->gpr[reg+1]; - } else { - val2 = env->gpr[reg+1]; - } - segv = put_user_u64(val, addr); - if (!segv) { - segv = put_user_u64(val2, addr + 8); - } - } - break; - } -#endif - default: abort(); - } - if (!segv) { - stored = 1; - } - } - } - env->crf[0] = (stored << 1) | xer_so; - env->reserve_addr = (target_ulong)-1; - } - if (!segv) { - env->nip += 4; - } - mmap_unlock(); - end_exclusive(); - return segv; -} - -void cpu_loop(CPUPPCState *env) -{ - CPUState *cs = CPU(ppc_env_get_cpu(env)); - target_siginfo_t info; - int trapnr; - target_ulong ret; - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case POWERPC_EXCP_NONE: - /* Just go on */ - break; - case POWERPC_EXCP_CRITICAL: /* Critical input */ - cpu_abort(cs, "Critical interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_MCHECK: /* Machine check exception */ - cpu_abort(cs, "Machine check exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_DSI: /* Data storage exception */ - /* XXX: check this. Seems bugged */ - switch (env->error_code & 0xFF000000) { - case 0x40000000: - case 0x42000000: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - break; - case 0x04000000: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLADR; - break; - case 0x08000000: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - break; - default: - /* Let's send a regular segfault... */ - EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", - env->error_code); - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - break; - } - info._sifields._sigfault._addr = env->spr[SPR_DAR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_ISI: /* Instruction storage exception */ - /* XXX: check this */ - switch (env->error_code & 0xFF000000) { - case 0x40000000: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - break; - case 0x10000000: - case 0x08000000: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - break; - default: - /* Let's send a regular segfault... */ - EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", - env->error_code); - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - break; - } - info._sifields._sigfault._addr = env->nip - 4; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_EXTERNAL: /* External input */ - cpu_abort(cs, "External interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_ALIGN: /* Alignment exception */ - /* XXX: check this */ - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_BUS_ADRALN; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_PROGRAM: /* Program exception */ - case POWERPC_EXCP_HV_EMU: /* HV emulation */ - /* XXX: check this */ - switch (env->error_code & ~0xF) { - case POWERPC_EXCP_FP: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - switch (env->error_code & 0xF) { - case POWERPC_EXCP_FP_OX: - info.si_code = TARGET_FPE_FLTOVF; - break; - case POWERPC_EXCP_FP_UX: - info.si_code = TARGET_FPE_FLTUND; - break; - case POWERPC_EXCP_FP_ZX: - case POWERPC_EXCP_FP_VXZDZ: - info.si_code = TARGET_FPE_FLTDIV; - break; - case POWERPC_EXCP_FP_XX: - info.si_code = TARGET_FPE_FLTRES; - break; - case POWERPC_EXCP_FP_VXSOFT: - info.si_code = TARGET_FPE_FLTINV; - break; - case POWERPC_EXCP_FP_VXSNAN: - case POWERPC_EXCP_FP_VXISI: - case POWERPC_EXCP_FP_VXIDI: - case POWERPC_EXCP_FP_VXIMZ: - case POWERPC_EXCP_FP_VXVC: - case POWERPC_EXCP_FP_VXSQRT: - case POWERPC_EXCP_FP_VXCVI: - info.si_code = TARGET_FPE_FLTSUB; - break; - default: - EXCP_DUMP(env, "Unknown floating point exception (%02x)\n", - env->error_code); - break; - } - break; - case POWERPC_EXCP_INVAL: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - switch (env->error_code & 0xF) { - case POWERPC_EXCP_INVAL_INVAL: - info.si_code = TARGET_ILL_ILLOPC; - break; - case POWERPC_EXCP_INVAL_LSWX: - info.si_code = TARGET_ILL_ILLOPN; - break; - case POWERPC_EXCP_INVAL_SPR: - info.si_code = TARGET_ILL_PRVREG; - break; - case POWERPC_EXCP_INVAL_FP: - info.si_code = TARGET_ILL_COPROC; - break; - default: - EXCP_DUMP(env, "Unknown invalid operation (%02x)\n", - env->error_code & 0xF); - info.si_code = TARGET_ILL_ILLADR; - break; - } - break; - case POWERPC_EXCP_PRIV: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - switch (env->error_code & 0xF) { - case POWERPC_EXCP_PRIV_OPC: - info.si_code = TARGET_ILL_PRVOPC; - break; - case POWERPC_EXCP_PRIV_REG: - info.si_code = TARGET_ILL_PRVREG; - break; - default: - EXCP_DUMP(env, "Unknown privilege violation (%02x)\n", - env->error_code & 0xF); - info.si_code = TARGET_ILL_PRVOPC; - break; - } - break; - case POWERPC_EXCP_TRAP: - cpu_abort(cs, "Tried to call a TRAP\n"); - break; - default: - /* Should not happen ! */ - cpu_abort(cs, "Unknown program exception (%02x)\n", - env->error_code); - break; - } - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_SYSCALL: /* System call exception */ - cpu_abort(cs, "Syscall exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_DECR: /* Decrementer exception */ - cpu_abort(cs, "Decrementer interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ - cpu_abort(cs, "Fix interval timer interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ - cpu_abort(cs, "Watchdog timer interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_DTLB: /* Data TLB error */ - cpu_abort(cs, "Data TLB exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - cpu_abort(cs, "Instruction TLB exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavail. */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */ - cpu_abort(cs, "Embedded floating-point data IRQ not handled\n"); - break; - case POWERPC_EXCP_EFPRI: /* Embedded floating-point round IRQ */ - cpu_abort(cs, "Embedded floating-point round IRQ not handled\n"); - break; - case POWERPC_EXCP_EPERFM: /* Embedded performance monitor IRQ */ - cpu_abort(cs, "Performance monitor exception not handled\n"); - break; - case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - cpu_abort(cs, "Doorbell interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ - cpu_abort(cs, "Doorbell critical interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_RESET: /* System reset exception */ - cpu_abort(cs, "Reset interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_DSEG: /* Data segment exception */ - cpu_abort(cs, "Data segment exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - cpu_abort(cs, "Instruction segment exception " - "while in user mode. Aborting\n"); - break; - /* PowerPC 64 with hypervisor mode support */ - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - cpu_abort(cs, "Hypervisor decrementer interrupt " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_TRACE: /* Trace exception */ - /* Nothing to do: - * we use this exception to emulate step-by-step execution mode. - */ - break; - /* PowerPC 64 with hypervisor mode support */ - case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - cpu_abort(cs, "Hypervisor data storage exception " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_HISI: /* Hypervisor instruction storage excp */ - cpu_abort(cs, "Hypervisor instruction storage exception " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - cpu_abort(cs, "Hypervisor data segment exception " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment excp */ - cpu_abort(cs, "Hypervisor instruction segment exception " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */ - cpu_abort(cs, "Programmable interval timer interrupt " - "while in user mode. Aborting\n"); - break; - case POWERPC_EXCP_IO: /* IO error exception */ - cpu_abort(cs, "IO error exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_RUNM: /* Run mode exception */ - cpu_abort(cs, "Run mode exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_EMUL: /* Emulation trap exception */ - cpu_abort(cs, "Emulation trap exception not handled\n"); - break; - case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - cpu_abort(cs, "Instruction fetch TLB exception " - "while in user-mode. Aborting"); - break; - case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - cpu_abort(cs, "Data load TLB exception while in user-mode. " - "Aborting"); - break; - case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - cpu_abort(cs, "Data store TLB exception while in user-mode. " - "Aborting"); - break; - case POWERPC_EXCP_FPA: /* Floating-point assist exception */ - cpu_abort(cs, "Floating-point assist exception not handled\n"); - break; - case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ - cpu_abort(cs, "Instruction address breakpoint exception " - "not handled\n"); - break; - case POWERPC_EXCP_SMI: /* System management interrupt */ - cpu_abort(cs, "System management interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_THERM: /* Thermal interrupt */ - cpu_abort(cs, "Thermal interrupt interrupt while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_PERFM: /* Embedded performance monitor IRQ */ - cpu_abort(cs, "Performance monitor exception not handled\n"); - break; - case POWERPC_EXCP_VPUA: /* Vector assist exception */ - cpu_abort(cs, "Vector assist exception not handled\n"); - break; - case POWERPC_EXCP_SOFTP: /* Soft patch exception */ - cpu_abort(cs, "Soft patch exception not handled\n"); - break; - case POWERPC_EXCP_MAINT: /* Maintenance exception */ - cpu_abort(cs, "Maintenance exception while in user mode. " - "Aborting\n"); - break; - case POWERPC_EXCP_STOP: /* stop translation */ - /* We did invalidate the instruction cache. Go on */ - break; - case POWERPC_EXCP_BRANCH: /* branch instruction: */ - /* We just stopped because of a branch. Go on */ - break; - case POWERPC_EXCP_SYSCALL_USER: - /* system call in user-mode emulation */ - /* WARNING: - * PPC ABI uses overflow flag in cr0 to signal an error - * in syscalls. - */ - env->crf[0] &= ~0x1; - env->nip += 4; - ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], - env->gpr[5], env->gpr[6], env->gpr[7], - env->gpr[8], 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->nip -= 4; - break; - } - if (ret == (target_ulong)(-TARGET_QEMU_ESIGRETURN)) { - /* Returning from a successful sigreturn syscall. - Avoid corrupting register state. */ - break; - } - if (ret > (target_ulong)(-515)) { - env->crf[0] |= 0x1; - ret = -ret; - } - env->gpr[3] = ret; - break; - case POWERPC_EXCP_STCX: - if (do_store_exclusive(env)) { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->nip; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - cpu_abort(cs, "Unknown exception 0x%x. Aborting\n", trapnr); - break; - } - process_pending_signals(env); - } -} -#endif - -#ifdef TARGET_MIPS - -# ifdef TARGET_ABI_MIPSO32 -# define MIPS_SYS(name, args) args, -static const uint8_t mips_syscall_args[] = { - MIPS_SYS(sys_syscall , 8) /* 4000 */ - MIPS_SYS(sys_exit , 1) - MIPS_SYS(sys_fork , 0) - MIPS_SYS(sys_read , 3) - MIPS_SYS(sys_write , 3) - MIPS_SYS(sys_open , 3) /* 4005 */ - MIPS_SYS(sys_close , 1) - MIPS_SYS(sys_waitpid , 3) - MIPS_SYS(sys_creat , 2) - MIPS_SYS(sys_link , 2) - MIPS_SYS(sys_unlink , 1) /* 4010 */ - MIPS_SYS(sys_execve , 0) - MIPS_SYS(sys_chdir , 1) - MIPS_SYS(sys_time , 1) - MIPS_SYS(sys_mknod , 3) - MIPS_SYS(sys_chmod , 2) /* 4015 */ - MIPS_SYS(sys_lchown , 3) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_stat */ - MIPS_SYS(sys_lseek , 3) - MIPS_SYS(sys_getpid , 0) /* 4020 */ - MIPS_SYS(sys_mount , 5) - MIPS_SYS(sys_umount , 1) - MIPS_SYS(sys_setuid , 1) - MIPS_SYS(sys_getuid , 0) - MIPS_SYS(sys_stime , 1) /* 4025 */ - MIPS_SYS(sys_ptrace , 4) - MIPS_SYS(sys_alarm , 1) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_fstat */ - MIPS_SYS(sys_pause , 0) - MIPS_SYS(sys_utime , 2) /* 4030 */ - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_access , 2) - MIPS_SYS(sys_nice , 1) - MIPS_SYS(sys_ni_syscall , 0) /* 4035 */ - MIPS_SYS(sys_sync , 0) - MIPS_SYS(sys_kill , 2) - MIPS_SYS(sys_rename , 2) - MIPS_SYS(sys_mkdir , 2) - MIPS_SYS(sys_rmdir , 1) /* 4040 */ - MIPS_SYS(sys_dup , 1) - MIPS_SYS(sys_pipe , 0) - MIPS_SYS(sys_times , 1) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_brk , 1) /* 4045 */ - MIPS_SYS(sys_setgid , 1) - MIPS_SYS(sys_getgid , 0) - MIPS_SYS(sys_ni_syscall , 0) /* was signal(2) */ - MIPS_SYS(sys_geteuid , 0) - MIPS_SYS(sys_getegid , 0) /* 4050 */ - MIPS_SYS(sys_acct , 0) - MIPS_SYS(sys_umount2 , 2) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ioctl , 3) - MIPS_SYS(sys_fcntl , 3) /* 4055 */ - MIPS_SYS(sys_ni_syscall , 2) - MIPS_SYS(sys_setpgid , 2) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_olduname , 1) - MIPS_SYS(sys_umask , 1) /* 4060 */ - MIPS_SYS(sys_chroot , 1) - MIPS_SYS(sys_ustat , 2) - MIPS_SYS(sys_dup2 , 2) - MIPS_SYS(sys_getppid , 0) - MIPS_SYS(sys_getpgrp , 0) /* 4065 */ - MIPS_SYS(sys_setsid , 0) - MIPS_SYS(sys_sigaction , 3) - MIPS_SYS(sys_sgetmask , 0) - MIPS_SYS(sys_ssetmask , 1) - MIPS_SYS(sys_setreuid , 2) /* 4070 */ - MIPS_SYS(sys_setregid , 2) - MIPS_SYS(sys_sigsuspend , 0) - MIPS_SYS(sys_sigpending , 1) - MIPS_SYS(sys_sethostname , 2) - MIPS_SYS(sys_setrlimit , 2) /* 4075 */ - MIPS_SYS(sys_getrlimit , 2) - MIPS_SYS(sys_getrusage , 2) - MIPS_SYS(sys_gettimeofday, 2) - MIPS_SYS(sys_settimeofday, 2) - MIPS_SYS(sys_getgroups , 2) /* 4080 */ - MIPS_SYS(sys_setgroups , 2) - MIPS_SYS(sys_ni_syscall , 0) /* old_select */ - MIPS_SYS(sys_symlink , 2) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_lstat */ - MIPS_SYS(sys_readlink , 3) /* 4085 */ - MIPS_SYS(sys_uselib , 1) - MIPS_SYS(sys_swapon , 2) - MIPS_SYS(sys_reboot , 3) - MIPS_SYS(old_readdir , 3) - MIPS_SYS(old_mmap , 6) /* 4090 */ - MIPS_SYS(sys_munmap , 2) - MIPS_SYS(sys_truncate , 2) - MIPS_SYS(sys_ftruncate , 2) - MIPS_SYS(sys_fchmod , 2) - MIPS_SYS(sys_fchown , 3) /* 4095 */ - MIPS_SYS(sys_getpriority , 2) - MIPS_SYS(sys_setpriority , 3) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_statfs , 2) - MIPS_SYS(sys_fstatfs , 2) /* 4100 */ - MIPS_SYS(sys_ni_syscall , 0) /* was ioperm(2) */ - MIPS_SYS(sys_socketcall , 2) - MIPS_SYS(sys_syslog , 3) - MIPS_SYS(sys_setitimer , 3) - MIPS_SYS(sys_getitimer , 2) /* 4105 */ - MIPS_SYS(sys_newstat , 2) - MIPS_SYS(sys_newlstat , 2) - MIPS_SYS(sys_newfstat , 2) - MIPS_SYS(sys_uname , 1) - MIPS_SYS(sys_ni_syscall , 0) /* 4110 was iopl(2) */ - MIPS_SYS(sys_vhangup , 0) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_idle() */ - MIPS_SYS(sys_ni_syscall , 0) /* was sys_vm86 */ - MIPS_SYS(sys_wait4 , 4) - MIPS_SYS(sys_swapoff , 1) /* 4115 */ - MIPS_SYS(sys_sysinfo , 1) - MIPS_SYS(sys_ipc , 6) - MIPS_SYS(sys_fsync , 1) - MIPS_SYS(sys_sigreturn , 0) - MIPS_SYS(sys_clone , 6) /* 4120 */ - MIPS_SYS(sys_setdomainname, 2) - MIPS_SYS(sys_newuname , 1) - MIPS_SYS(sys_ni_syscall , 0) /* sys_modify_ldt */ - MIPS_SYS(sys_adjtimex , 1) - MIPS_SYS(sys_mprotect , 3) /* 4125 */ - MIPS_SYS(sys_sigprocmask , 3) - MIPS_SYS(sys_ni_syscall , 0) /* was create_module */ - MIPS_SYS(sys_init_module , 5) - MIPS_SYS(sys_delete_module, 1) - MIPS_SYS(sys_ni_syscall , 0) /* 4130 was get_kernel_syms */ - MIPS_SYS(sys_quotactl , 0) - MIPS_SYS(sys_getpgid , 1) - MIPS_SYS(sys_fchdir , 1) - MIPS_SYS(sys_bdflush , 2) - MIPS_SYS(sys_sysfs , 3) /* 4135 */ - MIPS_SYS(sys_personality , 1) - MIPS_SYS(sys_ni_syscall , 0) /* for afs_syscall */ - MIPS_SYS(sys_setfsuid , 1) - MIPS_SYS(sys_setfsgid , 1) - MIPS_SYS(sys_llseek , 5) /* 4140 */ - MIPS_SYS(sys_getdents , 3) - MIPS_SYS(sys_select , 5) - MIPS_SYS(sys_flock , 2) - MIPS_SYS(sys_msync , 3) - MIPS_SYS(sys_readv , 3) /* 4145 */ - MIPS_SYS(sys_writev , 3) - MIPS_SYS(sys_cacheflush , 3) - MIPS_SYS(sys_cachectl , 3) - MIPS_SYS(sys_sysmips , 4) - MIPS_SYS(sys_ni_syscall , 0) /* 4150 */ - MIPS_SYS(sys_getsid , 1) - MIPS_SYS(sys_fdatasync , 0) - MIPS_SYS(sys_sysctl , 1) - MIPS_SYS(sys_mlock , 2) - MIPS_SYS(sys_munlock , 2) /* 4155 */ - MIPS_SYS(sys_mlockall , 1) - MIPS_SYS(sys_munlockall , 0) - MIPS_SYS(sys_sched_setparam, 2) - MIPS_SYS(sys_sched_getparam, 2) - MIPS_SYS(sys_sched_setscheduler, 3) /* 4160 */ - MIPS_SYS(sys_sched_getscheduler, 1) - MIPS_SYS(sys_sched_yield , 0) - MIPS_SYS(sys_sched_get_priority_max, 1) - MIPS_SYS(sys_sched_get_priority_min, 1) - MIPS_SYS(sys_sched_rr_get_interval, 2) /* 4165 */ - MIPS_SYS(sys_nanosleep, 2) - MIPS_SYS(sys_mremap , 5) - MIPS_SYS(sys_accept , 3) - MIPS_SYS(sys_bind , 3) - MIPS_SYS(sys_connect , 3) /* 4170 */ - MIPS_SYS(sys_getpeername , 3) - MIPS_SYS(sys_getsockname , 3) - MIPS_SYS(sys_getsockopt , 5) - MIPS_SYS(sys_listen , 2) - MIPS_SYS(sys_recv , 4) /* 4175 */ - MIPS_SYS(sys_recvfrom , 6) - MIPS_SYS(sys_recvmsg , 3) - MIPS_SYS(sys_send , 4) - MIPS_SYS(sys_sendmsg , 3) - MIPS_SYS(sys_sendto , 6) /* 4180 */ - MIPS_SYS(sys_setsockopt , 5) - MIPS_SYS(sys_shutdown , 2) - MIPS_SYS(sys_socket , 3) - MIPS_SYS(sys_socketpair , 4) - MIPS_SYS(sys_setresuid , 3) /* 4185 */ - MIPS_SYS(sys_getresuid , 3) - MIPS_SYS(sys_ni_syscall , 0) /* was sys_query_module */ - MIPS_SYS(sys_poll , 3) - MIPS_SYS(sys_nfsservctl , 3) - MIPS_SYS(sys_setresgid , 3) /* 4190 */ - MIPS_SYS(sys_getresgid , 3) - MIPS_SYS(sys_prctl , 5) - MIPS_SYS(sys_rt_sigreturn, 0) - MIPS_SYS(sys_rt_sigaction, 4) - MIPS_SYS(sys_rt_sigprocmask, 4) /* 4195 */ - MIPS_SYS(sys_rt_sigpending, 2) - MIPS_SYS(sys_rt_sigtimedwait, 4) - MIPS_SYS(sys_rt_sigqueueinfo, 3) - MIPS_SYS(sys_rt_sigsuspend, 0) - MIPS_SYS(sys_pread64 , 6) /* 4200 */ - MIPS_SYS(sys_pwrite64 , 6) - MIPS_SYS(sys_chown , 3) - MIPS_SYS(sys_getcwd , 2) - MIPS_SYS(sys_capget , 2) - MIPS_SYS(sys_capset , 2) /* 4205 */ - MIPS_SYS(sys_sigaltstack , 2) - MIPS_SYS(sys_sendfile , 4) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_mmap2 , 6) /* 4210 */ - MIPS_SYS(sys_truncate64 , 4) - MIPS_SYS(sys_ftruncate64 , 4) - MIPS_SYS(sys_stat64 , 2) - MIPS_SYS(sys_lstat64 , 2) - MIPS_SYS(sys_fstat64 , 2) /* 4215 */ - MIPS_SYS(sys_pivot_root , 2) - MIPS_SYS(sys_mincore , 3) - MIPS_SYS(sys_madvise , 3) - MIPS_SYS(sys_getdents64 , 3) - MIPS_SYS(sys_fcntl64 , 3) /* 4220 */ - MIPS_SYS(sys_ni_syscall , 0) - MIPS_SYS(sys_gettid , 0) - MIPS_SYS(sys_readahead , 5) - MIPS_SYS(sys_setxattr , 5) - MIPS_SYS(sys_lsetxattr , 5) /* 4225 */ - MIPS_SYS(sys_fsetxattr , 5) - MIPS_SYS(sys_getxattr , 4) - MIPS_SYS(sys_lgetxattr , 4) - MIPS_SYS(sys_fgetxattr , 4) - MIPS_SYS(sys_listxattr , 3) /* 4230 */ - MIPS_SYS(sys_llistxattr , 3) - MIPS_SYS(sys_flistxattr , 3) - MIPS_SYS(sys_removexattr , 2) - MIPS_SYS(sys_lremovexattr, 2) - MIPS_SYS(sys_fremovexattr, 2) /* 4235 */ - MIPS_SYS(sys_tkill , 2) - MIPS_SYS(sys_sendfile64 , 5) - MIPS_SYS(sys_futex , 6) - MIPS_SYS(sys_sched_setaffinity, 3) - MIPS_SYS(sys_sched_getaffinity, 3) /* 4240 */ - MIPS_SYS(sys_io_setup , 2) - MIPS_SYS(sys_io_destroy , 1) - MIPS_SYS(sys_io_getevents, 5) - MIPS_SYS(sys_io_submit , 3) - MIPS_SYS(sys_io_cancel , 3) /* 4245 */ - MIPS_SYS(sys_exit_group , 1) - MIPS_SYS(sys_lookup_dcookie, 3) - MIPS_SYS(sys_epoll_create, 1) - MIPS_SYS(sys_epoll_ctl , 4) - MIPS_SYS(sys_epoll_wait , 3) /* 4250 */ - MIPS_SYS(sys_remap_file_pages, 5) - MIPS_SYS(sys_set_tid_address, 1) - MIPS_SYS(sys_restart_syscall, 0) - MIPS_SYS(sys_fadvise64_64, 7) - MIPS_SYS(sys_statfs64 , 3) /* 4255 */ - MIPS_SYS(sys_fstatfs64 , 2) - MIPS_SYS(sys_timer_create, 3) - MIPS_SYS(sys_timer_settime, 4) - MIPS_SYS(sys_timer_gettime, 2) - MIPS_SYS(sys_timer_getoverrun, 1) /* 4260 */ - MIPS_SYS(sys_timer_delete, 1) - MIPS_SYS(sys_clock_settime, 2) - MIPS_SYS(sys_clock_gettime, 2) - MIPS_SYS(sys_clock_getres, 2) - MIPS_SYS(sys_clock_nanosleep, 4) /* 4265 */ - MIPS_SYS(sys_tgkill , 3) - MIPS_SYS(sys_utimes , 2) - MIPS_SYS(sys_mbind , 4) - MIPS_SYS(sys_ni_syscall , 0) /* sys_get_mempolicy */ - MIPS_SYS(sys_ni_syscall , 0) /* 4270 sys_set_mempolicy */ - MIPS_SYS(sys_mq_open , 4) - MIPS_SYS(sys_mq_unlink , 1) - MIPS_SYS(sys_mq_timedsend, 5) - MIPS_SYS(sys_mq_timedreceive, 5) - MIPS_SYS(sys_mq_notify , 2) /* 4275 */ - MIPS_SYS(sys_mq_getsetattr, 3) - MIPS_SYS(sys_ni_syscall , 0) /* sys_vserver */ - MIPS_SYS(sys_waitid , 4) - MIPS_SYS(sys_ni_syscall , 0) /* available, was setaltroot */ - MIPS_SYS(sys_add_key , 5) - MIPS_SYS(sys_request_key, 4) - MIPS_SYS(sys_keyctl , 5) - MIPS_SYS(sys_set_thread_area, 1) - MIPS_SYS(sys_inotify_init, 0) - MIPS_SYS(sys_inotify_add_watch, 3) /* 4285 */ - MIPS_SYS(sys_inotify_rm_watch, 2) - MIPS_SYS(sys_migrate_pages, 4) - MIPS_SYS(sys_openat, 4) - MIPS_SYS(sys_mkdirat, 3) - MIPS_SYS(sys_mknodat, 4) /* 4290 */ - MIPS_SYS(sys_fchownat, 5) - MIPS_SYS(sys_futimesat, 3) - MIPS_SYS(sys_fstatat64, 4) - MIPS_SYS(sys_unlinkat, 3) - MIPS_SYS(sys_renameat, 4) /* 4295 */ - MIPS_SYS(sys_linkat, 5) - MIPS_SYS(sys_symlinkat, 3) - MIPS_SYS(sys_readlinkat, 4) - MIPS_SYS(sys_fchmodat, 3) - MIPS_SYS(sys_faccessat, 3) /* 4300 */ - MIPS_SYS(sys_pselect6, 6) - MIPS_SYS(sys_ppoll, 5) - MIPS_SYS(sys_unshare, 1) - MIPS_SYS(sys_splice, 6) - MIPS_SYS(sys_sync_file_range, 7) /* 4305 */ - MIPS_SYS(sys_tee, 4) - MIPS_SYS(sys_vmsplice, 4) - MIPS_SYS(sys_move_pages, 6) - MIPS_SYS(sys_set_robust_list, 2) - MIPS_SYS(sys_get_robust_list, 3) /* 4310 */ - MIPS_SYS(sys_kexec_load, 4) - MIPS_SYS(sys_getcpu, 3) - MIPS_SYS(sys_epoll_pwait, 6) - MIPS_SYS(sys_ioprio_set, 3) - MIPS_SYS(sys_ioprio_get, 2) - MIPS_SYS(sys_utimensat, 4) - MIPS_SYS(sys_signalfd, 3) - MIPS_SYS(sys_ni_syscall, 0) /* was timerfd */ - MIPS_SYS(sys_eventfd, 1) - MIPS_SYS(sys_fallocate, 6) /* 4320 */ - MIPS_SYS(sys_timerfd_create, 2) - MIPS_SYS(sys_timerfd_gettime, 2) - MIPS_SYS(sys_timerfd_settime, 4) - MIPS_SYS(sys_signalfd4, 4) - MIPS_SYS(sys_eventfd2, 2) /* 4325 */ - MIPS_SYS(sys_epoll_create1, 1) - MIPS_SYS(sys_dup3, 3) - MIPS_SYS(sys_pipe2, 2) - MIPS_SYS(sys_inotify_init1, 1) - MIPS_SYS(sys_preadv, 5) /* 4330 */ - MIPS_SYS(sys_pwritev, 5) - MIPS_SYS(sys_rt_tgsigqueueinfo, 4) - MIPS_SYS(sys_perf_event_open, 5) - MIPS_SYS(sys_accept4, 4) - MIPS_SYS(sys_recvmmsg, 5) /* 4335 */ - MIPS_SYS(sys_fanotify_init, 2) - MIPS_SYS(sys_fanotify_mark, 6) - MIPS_SYS(sys_prlimit64, 4) - MIPS_SYS(sys_name_to_handle_at, 5) - MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */ - MIPS_SYS(sys_clock_adjtime, 2) - MIPS_SYS(sys_syncfs, 1) - MIPS_SYS(sys_sendmmsg, 4) - MIPS_SYS(sys_setns, 2) - MIPS_SYS(sys_process_vm_readv, 6) /* 345 */ - MIPS_SYS(sys_process_vm_writev, 6) - MIPS_SYS(sys_kcmp, 5) - MIPS_SYS(sys_finit_module, 3) - MIPS_SYS(sys_sched_setattr, 2) - MIPS_SYS(sys_sched_getattr, 3) /* 350 */ - MIPS_SYS(sys_renameat2, 5) - MIPS_SYS(sys_seccomp, 3) - MIPS_SYS(sys_getrandom, 3) - MIPS_SYS(sys_memfd_create, 2) - MIPS_SYS(sys_bpf, 3) /* 355 */ - MIPS_SYS(sys_execveat, 5) - MIPS_SYS(sys_userfaultfd, 1) - MIPS_SYS(sys_membarrier, 2) - MIPS_SYS(sys_mlock2, 3) - MIPS_SYS(sys_copy_file_range, 6) /* 360 */ - MIPS_SYS(sys_preadv2, 6) - MIPS_SYS(sys_pwritev2, 6) -}; -# undef MIPS_SYS -# endif /* O32 */ - -static int do_store_exclusive(CPUMIPSState *env) -{ - target_ulong addr; - target_ulong page_addr; - target_ulong val; - int flags; - int segv = 0; - int reg; - int d; - - addr = env->lladdr; - page_addr = addr & TARGET_PAGE_MASK; - start_exclusive(); - mmap_lock(); - flags = page_get_flags(page_addr); - if ((flags & PAGE_READ) == 0) { - segv = 1; - } else { - reg = env->llreg & 0x1f; - d = (env->llreg & 0x20) != 0; - if (d) { - segv = get_user_s64(val, addr); - } else { - segv = get_user_s32(val, addr); - } - if (!segv) { - if (val != env->llval) { - env->active_tc.gpr[reg] = 0; - } else { - if (d) { - segv = put_user_u64(env->llnewval, addr); - } else { - segv = put_user_u32(env->llnewval, addr); - } - if (!segv) { - env->active_tc.gpr[reg] = 1; - } - } - } - } - env->lladdr = -1; - if (!segv) { - env->active_tc.PC += 4; - } - mmap_unlock(); - end_exclusive(); - return segv; -} - -/* Break codes */ -enum { - BRK_OVERFLOW = 6, - BRK_DIVZERO = 7 -}; - -static int do_break(CPUMIPSState *env, target_siginfo_t *info, - unsigned int code) -{ - int ret = -1; - - switch (code) { - case BRK_OVERFLOW: - case BRK_DIVZERO: - info->si_signo = TARGET_SIGFPE; - info->si_errno = 0; - info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; - queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); - ret = 0; - break; - default: - info->si_signo = TARGET_SIGTRAP; - info->si_errno = 0; - queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); - ret = 0; - break; - } - - return ret; -} - -void cpu_loop(CPUMIPSState *env) -{ - CPUState *cs = CPU(mips_env_get_cpu(env)); - target_siginfo_t info; - int trapnr; - abi_long ret; -# ifdef TARGET_ABI_MIPSO32 - unsigned int syscall_num; -# endif - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case EXCP_SYSCALL: - env->active_tc.PC += 4; -# ifdef TARGET_ABI_MIPSO32 - syscall_num = env->active_tc.gpr[2] - 4000; - if (syscall_num >= sizeof(mips_syscall_args)) { - ret = -TARGET_ENOSYS; - } else { - int nb_args; - abi_ulong sp_reg; - abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0; - - nb_args = mips_syscall_args[syscall_num]; - sp_reg = env->active_tc.gpr[29]; - switch (nb_args) { - /* these arguments are taken from the stack */ - case 8: - if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) { - goto done_syscall; - } - case 7: - if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) { - goto done_syscall; - } - case 6: - if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) { - goto done_syscall; - } - case 5: - if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) { - goto done_syscall; - } - default: - break; - } - ret = do_syscall(env, env->active_tc.gpr[2], - env->active_tc.gpr[4], - env->active_tc.gpr[5], - env->active_tc.gpr[6], - env->active_tc.gpr[7], - arg5, arg6, arg7, arg8); - } -done_syscall: -# else - ret = do_syscall(env, env->active_tc.gpr[2], - env->active_tc.gpr[4], env->active_tc.gpr[5], - env->active_tc.gpr[6], env->active_tc.gpr[7], - env->active_tc.gpr[8], env->active_tc.gpr[9], - env->active_tc.gpr[10], env->active_tc.gpr[11]); -# endif /* O32 */ - if (ret == -TARGET_ERESTARTSYS) { - env->active_tc.PC -= 4; - break; - } - if (ret == -TARGET_QEMU_ESIGRETURN) { - /* Returning from a successful sigreturn syscall. - Avoid clobbering register state. */ - break; - } - if ((abi_ulong)ret >= (abi_ulong)-1133) { - env->active_tc.gpr[7] = 1; /* error flag */ - ret = -ret; - } else { - env->active_tc.gpr[7] = 0; /* error flag */ - } - env->active_tc.gpr[2] = ret; - break; - case EXCP_TLBL: - case EXCP_TLBS: - case EXCP_AdEL: - case EXCP_AdES: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->CP0_BadVAddr; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_CpU: - case EXCP_RI: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_SC: - if (do_store_exclusive(env)) { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->active_tc.PC; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DSPDIS: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - /* The code below was inspired by the MIPS Linux kernel trap - * handling code in arch/mips/kernel/traps.c. - */ - case EXCP_BREAK: - { - abi_ulong trap_instr; - unsigned int code; - - if (env->hflags & MIPS_HFLAG_M16) { - if (env->insn_flags & ASE_MICROMIPS) { - /* microMIPS mode */ - ret = get_user_u16(trap_instr, env->active_tc.PC); - if (ret != 0) { - goto error; - } - - if ((trap_instr >> 10) == 0x11) { - /* 16-bit instruction */ - code = trap_instr & 0xf; - } else { - /* 32-bit instruction */ - abi_ulong instr_lo; - - ret = get_user_u16(instr_lo, - env->active_tc.PC + 2); - if (ret != 0) { - goto error; - } - trap_instr = (trap_instr << 16) | instr_lo; - code = ((trap_instr >> 6) & ((1 << 20) - 1)); - /* Unfortunately, microMIPS also suffers from - the old assembler bug... */ - if (code >= (1 << 10)) { - code >>= 10; - } - } - } else { - /* MIPS16e mode */ - ret = get_user_u16(trap_instr, env->active_tc.PC); - if (ret != 0) { - goto error; - } - code = (trap_instr >> 6) & 0x3f; - } - } else { - ret = get_user_u32(trap_instr, env->active_tc.PC); - if (ret != 0) { - goto error; - } - - /* As described in the original Linux kernel code, the - * below checks on 'code' are to work around an old - * assembly bug. - */ - code = ((trap_instr >> 6) & ((1 << 20) - 1)); - if (code >= (1 << 10)) { - code >>= 10; - } - } - - if (do_break(env, &info, code) != 0) { - goto error; - } - } - break; - case EXCP_TRAP: - { - abi_ulong trap_instr; - unsigned int code = 0; - - if (env->hflags & MIPS_HFLAG_M16) { - /* microMIPS mode */ - abi_ulong instr[2]; - - ret = get_user_u16(instr[0], env->active_tc.PC) || - get_user_u16(instr[1], env->active_tc.PC + 2); - - trap_instr = (instr[0] << 16) | instr[1]; - } else { - ret = get_user_u32(trap_instr, env->active_tc.PC); - } - - if (ret != 0) { - goto error; - } - - /* The immediate versions don't provide a code. */ - if (!(trap_instr & 0xFC000000)) { - if (env->hflags & MIPS_HFLAG_M16) { - /* microMIPS mode */ - code = ((trap_instr >> 12) & ((1 << 4) - 1)); - } else { - code = ((trap_instr >> 6) & ((1 << 10) - 1)); - } - } - - if (do_break(env, &info, code) != 0) { - goto error; - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: -error: - EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - abort(); - } - process_pending_signals(env); - } -} -#endif - -#ifdef TARGET_NIOS2 - -void cpu_loop(CPUNios2State *env) -{ - CPUState *cs = ENV_GET_CPU(env); - Nios2CPU *cpu = NIOS2_CPU(cs); - target_siginfo_t info; - int trapnr, gdbsig, ret; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - gdbsig = 0; - - switch (trapnr) { - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_TRAP: - if (env->regs[R_AT] == 0) { - abi_long ret; - qemu_log_mask(CPU_LOG_INT, "\nSyscall\n"); - - ret = do_syscall(env, env->regs[2], - env->regs[4], env->regs[5], env->regs[6], - env->regs[7], env->regs[8], env->regs[9], - 0, 0); - - if (env->regs[2] == 0) { /* FIXME: syscall 0 workaround */ - ret = 0; - } - - env->regs[2] = abs(ret); - /* Return value is 0..4096 */ - env->regs[7] = (ret > 0xfffffffffffff000ULL); - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] &= ~0x3; - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] += 4; - break; - } else { - qemu_log_mask(CPU_LOG_INT, "\nTrap\n"); - - env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; - env->regs[CR_STATUS] &= ~0x3; - env->regs[R_EA] = env->regs[R_PC] + 4; - env->regs[R_PC] = cpu->exception_addr; - - gdbsig = TARGET_SIGTRAP; - break; - } - case 0xaa: - switch (env->regs[R_PC]) { - /*case 0x1000:*/ /* TODO:__kuser_helper_version */ - case 0x1004: /* __kuser_cmpxchg */ - start_exclusive(); - if (env->regs[4] & 0x3) { - goto kuser_fail; - } - ret = get_user_u32(env->regs[2], env->regs[4]); - if (ret) { - end_exclusive(); - goto kuser_fail; - } - env->regs[2] -= env->regs[5]; - if (env->regs[2] == 0) { - put_user_u32(env->regs[6], env->regs[4]); - } - end_exclusive(); - env->regs[R_PC] = env->regs[R_RA]; - break; - /*case 0x1040:*/ /* TODO:__kuser_sigtramp */ - default: - ; -kuser_fail: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* TODO: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->regs[R_PC]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - default: - EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", - trapnr); - gdbsig = TARGET_SIGILL; - break; - } - if (gdbsig) { - gdb_handlesig(cs, gdbsig); - if (gdbsig != TARGET_SIGTRAP) { - exit(EXIT_FAILURE); - } - } - - process_pending_signals(env); - } -} - -#endif /* TARGET_NIOS2 */ - -#ifdef TARGET_OPENRISC - -void cpu_loop(CPUOpenRISCState *env) -{ - CPUState *cs = CPU(openrisc_env_get_cpu(env)); - int trapnr; - abi_long ret; - target_siginfo_t info; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_SYSCALL: - env->pc += 4; /* 0xc00; */ - ret = do_syscall(env, - cpu_get_gpr(env, 11), /* return value */ - cpu_get_gpr(env, 3), /* r3 - r7 are params */ - cpu_get_gpr(env, 4), - cpu_get_gpr(env, 5), - cpu_get_gpr(env, 6), - cpu_get_gpr(env, 7), - cpu_get_gpr(env, 8), 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - cpu_set_gpr(env, 11, ret); - } - break; - case EXCP_DPF: - case EXCP_IPF: - case EXCP_RANGE: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ALIGN: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_BUS_ADRALN; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ILLEGAL: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_FPE: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_INTERRUPT: - /* We processed the pending cpu work above. */ - break; - case EXCP_DEBUG: - trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); - if (trapnr) { - info.si_signo = trapnr; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - g_assert_not_reached(); - } - process_pending_signals(env); - } -} - -#endif /* TARGET_OPENRISC */ - -#ifdef TARGET_SH4 -void cpu_loop(CPUSH4State *env) -{ - CPUState *cs = CPU(sh_env_get_cpu(env)); - int trapnr, ret; - target_siginfo_t info; - - while (1) { - bool arch_interrupt = true; - - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case 0x160: - env->pc += 2; - ret = do_syscall(env, - env->gregs[3], - env->gregs[4], - env->gregs[5], - env->gregs[6], - env->gregs[7], - env->gregs[0], - env->gregs[1], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->gregs[0] = ret; - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else { - arch_interrupt = false; - } - } - break; - case 0xa0: - case 0xc0: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->tea; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - arch_interrupt = false; - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - - /* Most of the traps imply an exception or interrupt, which - implies an REI instruction has been executed. Which means - that LDST (aka LOK_ADDR) should be cleared. But there are - a few exceptions for traps internal to QEMU. */ - if (arch_interrupt) { - env->lock_addr = -1; - } - } -} -#endif - -#ifdef TARGET_CRIS -void cpu_loop(CPUCRISState *env) -{ - CPUState *cs = CPU(cris_env_get_cpu(env)); - int trapnr, ret; - target_siginfo_t info; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case 0xaa: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->pregs[PR_EDA]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_BREAK: - ret = do_syscall(env, - env->regs[9], - env->regs[10], - env->regs[11], - env->regs[12], - env->regs[13], - env->pregs[7], - env->pregs[11], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[10] = ret; - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - } -} -#endif - -#ifdef TARGET_MICROBLAZE -void cpu_loop(CPUMBState *env) -{ - CPUState *cs = CPU(mb_env_get_cpu(env)); - int trapnr, ret; - target_siginfo_t info; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case 0xaa: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_BREAK: - /* Return address is 4 bytes after the call. */ - env->regs[14] += 4; - env->sregs[SR_PC] = env->regs[14]; - ret = do_syscall(env, - env->regs[12], - env->regs[5], - env->regs[6], - env->regs[7], - env->regs[8], - env->regs[9], - env->regs[10], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - /* Wind back to before the syscall. */ - env->sregs[SR_PC] -= 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[3] = ret; - } - /* All syscall exits result in guest r14 being equal to the - * PC we return to, because the kernel syscall exit "rtbd" does - * this. (This is true even for sigreturn(); note that r14 is - * not a userspace-usable register, as the kernel may clobber it - * at any point.) - */ - env->regs[14] = env->sregs[SR_PC]; - break; - case EXCP_HW_EXCP: - env->regs[17] = env->sregs[SR_PC] + 4; - if (env->iflags & D_FLAG) { - env->sregs[SR_ESR] |= 1 << 12; - env->sregs[SR_PC] -= 4; - /* FIXME: if branch was immed, replay the imm as well. */ - } - - env->iflags &= ~(IMM_FLAG | D_FLAG); - - switch (env->sregs[SR_ESR] & 31) { - case ESR_EC_DIVZERO: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_FLTDIV; - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case ESR_EC_FPU: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - if (env->sregs[SR_FSR] & FSR_IO) { - info.si_code = TARGET_FPE_FLTINV; - } - if (env->sregs[SR_FSR] & FSR_DZ) { - info.si_code = TARGET_FPE_FLTDIV; - } - info._sifields._sigfault._addr = 0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - default: - printf ("Unhandled hw-exception: 0x%x\n", - env->sregs[SR_ESR] & ESR_EC_MASK); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - break; - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - } -} -#endif - -#ifdef TARGET_M68K - -void cpu_loop(CPUM68KState *env) -{ - CPUState *cs = CPU(m68k_env_get_cpu(env)); - int trapnr; - unsigned int n; - target_siginfo_t info; - TaskState *ts = cs->opaque; - - for(;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch(trapnr) { - case EXCP_ILLEGAL: - { - if (ts->sim_syscalls) { - uint16_t nr; - get_user_u16(nr, env->pc + 2); - env->pc += 4; - do_m68k_simcall(env, nr); - } else { - goto do_sigill; - } - } - break; - case EXCP_HALT_INSN: - /* Semihosing syscall. */ - env->pc += 4; - do_m68k_semihosting(env, env->dregs[0]); - break; - case EXCP_LINEA: - case EXCP_LINEF: - case EXCP_UNSUPPORTED: - do_sigill: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_CHK: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTOVF; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_DIV0: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTDIV; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_TRAP0: - { - abi_long ret; - ts->sim_syscalls = 0; - n = env->dregs[0]; - env->pc += 2; - ret = do_syscall(env, - n, - env->dregs[1], - env->dregs[2], - env->dregs[3], - env->dregs[4], - env->dregs[5], - env->aregs[0], - 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 2; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->dregs[0] = ret; - } - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_ACCESS: - { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - /* XXX: check env->error_code */ - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->mmu.ar; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - break; - case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); - abort(); - } - process_pending_signals(env); - } -} -#endif /* TARGET_M68K */ - -#ifdef TARGET_ALPHA -void cpu_loop(CPUAlphaState *env) -{ - CPUState *cs = CPU(alpha_env_get_cpu(env)); - int trapnr; - target_siginfo_t info; - abi_long sysret; - - while (1) { - bool arch_interrupt = true; - - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_RESET: - fprintf(stderr, "Reset requested. Exit\n"); - exit(EXIT_FAILURE); - break; - case EXCP_MCHK: - fprintf(stderr, "Machine check exception. Exit\n"); - exit(EXIT_FAILURE); - break; - case EXCP_SMP_INTERRUPT: - case EXCP_CLK_INTERRUPT: - case EXCP_DEV_INTERRUPT: - fprintf(stderr, "External interrupt. Exit\n"); - exit(EXIT_FAILURE); - break; - case EXCP_MMFAULT: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID - ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR); - info._sifields._sigfault._addr = env->trap_arg0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_UNALIGN: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_BUS_ADRALN; - info._sifields._sigfault._addr = env->trap_arg0; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_OPCDEC: - do_sigill: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPC; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ARITH: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_FLTINV; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_FEN: - /* No-op. Linux simply re-enables the FPU. */ - break; - case EXCP_CALL_PAL: - switch (env->error_code) { - case 0x80: - /* BPT */ - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case 0x81: - /* BUGCHK */ - info.si_signo = TARGET_SIGTRAP; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case 0x83: - /* CALLSYS */ - trapnr = env->ir[IR_V0]; - sysret = do_syscall(env, trapnr, - env->ir[IR_A0], env->ir[IR_A1], - env->ir[IR_A2], env->ir[IR_A3], - env->ir[IR_A4], env->ir[IR_A5], - 0, 0); - if (sysret == -TARGET_ERESTARTSYS) { - env->pc -= 4; - break; - } - if (sysret == -TARGET_QEMU_ESIGRETURN) { - break; - } - /* Syscall writes 0 to V0 to bypass error check, similar - to how this is handled internal to Linux kernel. - (Ab)use trapnr temporarily as boolean indicating error. */ - trapnr = (env->ir[IR_V0] != 0 && sysret < 0); - env->ir[IR_V0] = (trapnr ? -sysret : sysret); - env->ir[IR_A3] = trapnr; - break; - case 0x86: - /* IMB */ - /* ??? We can probably elide the code using page_unprotect - that is checking for self-modifying code. Instead we - could simply call tb_flush here. Until we work out the - changes required to turn off the extra write protection, - this can be a no-op. */ - break; - case 0x9E: - /* RDUNIQUE */ - /* Handled in the translator for usermode. */ - abort(); - case 0x9F: - /* WRUNIQUE */ - /* Handled in the translator for usermode. */ - abort(); - case 0xAA: - /* GENTRAP */ - info.si_signo = TARGET_SIGFPE; - switch (env->ir[IR_A0]) { - case TARGET_GEN_INTOVF: - info.si_code = TARGET_FPE_INTOVF; - break; - case TARGET_GEN_INTDIV: - info.si_code = TARGET_FPE_INTDIV; - break; - case TARGET_GEN_FLTOVF: - info.si_code = TARGET_FPE_FLTOVF; - break; - case TARGET_GEN_FLTUND: - info.si_code = TARGET_FPE_FLTUND; - break; - case TARGET_GEN_FLTINV: - info.si_code = TARGET_FPE_FLTINV; - break; - case TARGET_GEN_FLTINE: - info.si_code = TARGET_FPE_FLTRES; - break; - case TARGET_GEN_ROPRAND: - info.si_code = 0; - break; - default: - info.si_signo = TARGET_SIGTRAP; - info.si_code = 0; - break; - } - info.si_errno = 0; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - default: - goto do_sigill; - } - break; - case EXCP_DEBUG: - info.si_signo = gdb_handlesig(cs, TARGET_SIGTRAP); - if (info.si_signo) { - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else { - arch_interrupt = false; - } - break; - case EXCP_INTERRUPT: - /* Just indicate that signals should be handled asap. */ - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - arch_interrupt = false; - break; - default: - printf ("Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - - /* Most of the traps imply a transition through PALcode, which - implies an REI instruction has been executed. Which means - that RX and LOCK_ADDR should be cleared. But there are a - few exceptions for traps internal to QEMU. */ - if (arch_interrupt) { - env->flags &= ~ENV_FLAG_RX_FLAG; - env->lock_addr = -1; - } - } -} -#endif /* TARGET_ALPHA */ - -#ifdef TARGET_S390X - -/* s390x masks the fault address it reports in si_addr for SIGSEGV and SIGBUS */ -#define S390X_FAIL_ADDR_MASK -4096LL - -void cpu_loop(CPUS390XState *env) -{ - CPUState *cs = CPU(s390_env_get_cpu(env)); - int trapnr, n, sig; - target_siginfo_t info; - target_ulong addr; - abi_long ret; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_INTERRUPT: - /* Just indicate that signals should be handled asap. */ - break; - - case EXCP_SVC: - n = env->int_svc_code; - if (!n) { - /* syscalls > 255 */ - n = env->regs[1]; - } - env->psw.addr += env->int_svc_ilen; - ret = do_syscall(env, n, env->regs[2], env->regs[3], - env->regs[4], env->regs[5], - env->regs[6], env->regs[7], 0, 0); - if (ret == -TARGET_ERESTARTSYS) { - env->psw.addr -= env->int_svc_ilen; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[2] = ret; - } - break; - - case EXCP_DEBUG: - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - n = TARGET_TRAP_BRKPT; - goto do_signal_pc; - } - break; - case EXCP_PGM: - n = env->int_pgm_code; - switch (n) { - case PGM_OPERATION: - case PGM_PRIVILEGED: - sig = TARGET_SIGILL; - n = TARGET_ILL_ILLOPC; - goto do_signal_pc; - case PGM_PROTECTION: - case PGM_ADDRESSING: - sig = TARGET_SIGSEGV; - /* XXX: check env->error_code */ - n = TARGET_SEGV_MAPERR; - addr = env->__excp_addr & S390X_FAIL_ADDR_MASK; - goto do_signal; - case PGM_EXECUTE: - case PGM_SPECIFICATION: - case PGM_SPECIAL_OP: - case PGM_OPERAND: - do_sigill_opn: - sig = TARGET_SIGILL; - n = TARGET_ILL_ILLOPN; - goto do_signal_pc; - - case PGM_FIXPT_OVERFLOW: - sig = TARGET_SIGFPE; - n = TARGET_FPE_INTOVF; - goto do_signal_pc; - case PGM_FIXPT_DIVIDE: - sig = TARGET_SIGFPE; - n = TARGET_FPE_INTDIV; - goto do_signal_pc; - - case PGM_DATA: - n = (env->fpc >> 8) & 0xff; - if (n == 0xff) { - /* compare-and-trap */ - goto do_sigill_opn; - } else { - /* An IEEE exception, simulated or otherwise. */ - if (n & 0x80) { - n = TARGET_FPE_FLTINV; - } else if (n & 0x40) { - n = TARGET_FPE_FLTDIV; - } else if (n & 0x20) { - n = TARGET_FPE_FLTOVF; - } else if (n & 0x10) { - n = TARGET_FPE_FLTUND; - } else if (n & 0x08) { - n = TARGET_FPE_FLTRES; - } else { - /* ??? Quantum exception; BFP, DFP error. */ - goto do_sigill_opn; - } - sig = TARGET_SIGFPE; - goto do_signal_pc; - } - - default: - fprintf(stderr, "Unhandled program exception: %#x\n", n); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - break; - - do_signal_pc: - addr = env->psw.addr; - do_signal: - info.si_signo = sig; - info.si_errno = 0; - info.si_code = n; - info._sifields._sigfault._addr = addr; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); - cpu_dump_state(cs, stderr, fprintf, 0); - exit(EXIT_FAILURE); - } - process_pending_signals (env); - } -} - -#endif /* TARGET_S390X */ - -#ifdef TARGET_TILEGX - -static void gen_sigill_reg(CPUTLGState *env) -{ - target_siginfo_t info; - - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_PRVREG; - info._sifields._sigfault._addr = env->pc; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); -} - -static void do_signal(CPUTLGState *env, int signo, int sigcode) -{ - target_siginfo_t info; - - info.si_signo = signo; - info.si_errno = 0; - info._sifields._sigfault._addr = env->pc; - - if (signo == TARGET_SIGSEGV) { - /* The passed in sigcode is a dummy; check for a page mapping - and pass either MAPERR or ACCERR. */ - target_ulong addr = env->excaddr; - info._sifields._sigfault._addr = addr; - if (page_check_range(addr, 1, PAGE_VALID) < 0) { - sigcode = TARGET_SEGV_MAPERR; - } else { - sigcode = TARGET_SEGV_ACCERR; - } - } - info.si_code = sigcode; - - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); -} - -static void gen_sigsegv_maperr(CPUTLGState *env, target_ulong addr) -{ - env->excaddr = addr; - do_signal(env, TARGET_SIGSEGV, 0); -} - -static void set_regval(CPUTLGState *env, uint8_t reg, uint64_t val) -{ - if (unlikely(reg >= TILEGX_R_COUNT)) { - switch (reg) { - case TILEGX_R_SN: - case TILEGX_R_ZERO: - return; - case TILEGX_R_IDN0: - case TILEGX_R_IDN1: - case TILEGX_R_UDN0: - case TILEGX_R_UDN1: - case TILEGX_R_UDN2: - case TILEGX_R_UDN3: - gen_sigill_reg(env); - return; - default: - g_assert_not_reached(); - } - } - env->regs[reg] = val; -} - -/* - * Compare the 8-byte contents of the CmpValue SPR with the 8-byte value in - * memory at the address held in the first source register. If the values are - * not equal, then no memory operation is performed. If the values are equal, - * the 8-byte quantity from the second source register is written into memory - * at the address held in the first source register. In either case, the result - * of the instruction is the value read from memory. The compare and write to - * memory are atomic and thus can be used for synchronization purposes. This - * instruction only operates for addresses aligned to a 8-byte boundary. - * Unaligned memory access causes an Unaligned Data Reference interrupt. - * - * Functional Description (64-bit) - * uint64_t memVal = memoryReadDoubleWord (rf[SrcA]); - * rf[Dest] = memVal; - * if (memVal == SPR[CmpValueSPR]) - * memoryWriteDoubleWord (rf[SrcA], rf[SrcB]); - * - * Functional Description (32-bit) - * uint64_t memVal = signExtend32 (memoryReadWord (rf[SrcA])); - * rf[Dest] = memVal; - * if (memVal == signExtend32 (SPR[CmpValueSPR])) - * memoryWriteWord (rf[SrcA], rf[SrcB]); - * - * - * This function also processes exch and exch4 which need not process SPR. - */ -static void do_exch(CPUTLGState *env, bool quad, bool cmp) -{ - target_ulong addr; - target_long val, sprval; - - start_exclusive(); - - addr = env->atomic_srca; - if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { - goto sigsegv_maperr; - } - - if (cmp) { - if (quad) { - sprval = env->spregs[TILEGX_SPR_CMPEXCH]; - } else { - sprval = sextract64(env->spregs[TILEGX_SPR_CMPEXCH], 0, 32); - } - } - - if (!cmp || val == sprval) { - target_long valb = env->atomic_srcb; - if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { - goto sigsegv_maperr; - } - } - - set_regval(env, env->atomic_dstr, val); - end_exclusive(); - return; - - sigsegv_maperr: - end_exclusive(); - gen_sigsegv_maperr(env, addr); -} - -static void do_fetch(CPUTLGState *env, int trapnr, bool quad) -{ - int8_t write = 1; - target_ulong addr; - target_long val, valb; - - start_exclusive(); - - addr = env->atomic_srca; - valb = env->atomic_srcb; - if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { - goto sigsegv_maperr; - } - - switch (trapnr) { - case TILEGX_EXCP_OPCODE_FETCHADD: - case TILEGX_EXCP_OPCODE_FETCHADD4: - valb += val; - break; - case TILEGX_EXCP_OPCODE_FETCHADDGEZ: - valb += val; - if (valb < 0) { - write = 0; - } - break; - case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: - valb += val; - if ((int32_t)valb < 0) { - write = 0; - } - break; - case TILEGX_EXCP_OPCODE_FETCHAND: - case TILEGX_EXCP_OPCODE_FETCHAND4: - valb &= val; - break; - case TILEGX_EXCP_OPCODE_FETCHOR: - case TILEGX_EXCP_OPCODE_FETCHOR4: - valb |= val; - break; - default: - g_assert_not_reached(); - } - - if (write) { - if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { - goto sigsegv_maperr; - } - } - - set_regval(env, env->atomic_dstr, val); - end_exclusive(); - return; - - sigsegv_maperr: - end_exclusive(); - gen_sigsegv_maperr(env, addr); -} - -void cpu_loop(CPUTLGState *env) -{ - CPUState *cs = CPU(tilegx_env_get_cpu(env)); - int trapnr; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case TILEGX_EXCP_SYSCALL: - { - abi_ulong ret = do_syscall(env, env->regs[TILEGX_R_NR], - env->regs[0], env->regs[1], - env->regs[2], env->regs[3], - env->regs[4], env->regs[5], - env->regs[6], env->regs[7]); - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 8; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->regs[TILEGX_R_RE] = ret; - env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(ret) ? -ret : 0; - } - break; - } - case TILEGX_EXCP_OPCODE_EXCH: - do_exch(env, true, false); - break; - case TILEGX_EXCP_OPCODE_EXCH4: - do_exch(env, false, false); - break; - case TILEGX_EXCP_OPCODE_CMPEXCH: - do_exch(env, true, true); - break; - case TILEGX_EXCP_OPCODE_CMPEXCH4: - do_exch(env, false, true); - break; - case TILEGX_EXCP_OPCODE_FETCHADD: - case TILEGX_EXCP_OPCODE_FETCHADDGEZ: - case TILEGX_EXCP_OPCODE_FETCHAND: - case TILEGX_EXCP_OPCODE_FETCHOR: - do_fetch(env, trapnr, true); - break; - case TILEGX_EXCP_OPCODE_FETCHADD4: - case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: - case TILEGX_EXCP_OPCODE_FETCHAND4: - case TILEGX_EXCP_OPCODE_FETCHOR4: - do_fetch(env, trapnr, false); - break; - case TILEGX_EXCP_SIGNAL: - do_signal(env, env->signo, env->sigcode); - break; - case TILEGX_EXCP_REG_IDN_ACCESS: - case TILEGX_EXCP_REG_UDN_ACCESS: - gen_sigill_reg(env); - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - default: - fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr); - g_assert_not_reached(); - } - process_pending_signals(env); - } -} - -#endif - -#ifdef TARGET_RISCV - -void cpu_loop(CPURISCVState *env) -{ - CPUState *cs = CPU(riscv_env_get_cpu(env)); - int trapnr, signum, sigcode; - target_ulong sigaddr; - target_ulong ret; - - for (;;) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - signum = 0; - sigcode = 0; - sigaddr = 0; - - switch (trapnr) { - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - case EXCP_ATOMIC: - cpu_exec_step_atomic(cs); - break; - case RISCV_EXCP_U_ECALL: - env->pc += 4; - if (env->gpr[xA7] == TARGET_NR_arch_specific_syscall + 15) { - /* riscv_flush_icache_syscall is a no-op in QEMU as - self-modifying code is automatically detected */ - ret = 0; - } else { - ret = do_syscall(env, - env->gpr[xA7], - env->gpr[xA0], - env->gpr[xA1], - env->gpr[xA2], - env->gpr[xA3], - env->gpr[xA4], - env->gpr[xA5], - 0, 0); - } - if (ret == -TARGET_ERESTARTSYS) { - env->pc -= 4; - } else if (ret != -TARGET_QEMU_ESIGRETURN) { - env->gpr[xA0] = ret; - } - if (cs->singlestep_enabled) { - goto gdbstep; - } - break; - case RISCV_EXCP_ILLEGAL_INST: - signum = TARGET_SIGILL; - sigcode = TARGET_ILL_ILLOPC; - break; - case RISCV_EXCP_BREAKPOINT: - signum = TARGET_SIGTRAP; - sigcode = TARGET_TRAP_BRKPT; - sigaddr = env->pc; - break; - case RISCV_EXCP_INST_PAGE_FAULT: - case RISCV_EXCP_LOAD_PAGE_FAULT: - case RISCV_EXCP_STORE_PAGE_FAULT: - signum = TARGET_SIGSEGV; - sigcode = TARGET_SEGV_MAPERR; - break; - case EXCP_DEBUG: - gdbstep: - signum = gdb_handlesig(cs, TARGET_SIGTRAP); - sigcode = TARGET_TRAP_BRKPT; - break; - default: - EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", - trapnr); - exit(EXIT_FAILURE); - } - - if (signum) { - target_siginfo_t info = { - .si_signo = signum, - .si_errno = 0, - .si_code = sigcode, - ._sifields._sigfault._addr = sigaddr - }; - queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); - } - - process_pending_signals(env); - } -} - -#endif /* TARGET_RISCV */ - -#ifdef TARGET_HPPA - -static abi_ulong hppa_lws(CPUHPPAState *env) -{ - uint32_t which = env->gr[20]; - abi_ulong addr = env->gr[26]; - abi_ulong old = env->gr[25]; - abi_ulong new = env->gr[24]; - abi_ulong size, ret; - - switch (which) { - default: - return -TARGET_ENOSYS; - - case 0: /* elf32 atomic 32bit cmpxchg */ - if ((addr & 3) || !access_ok(VERIFY_WRITE, addr, 4)) { - return -TARGET_EFAULT; - } - old = tswap32(old); - new = tswap32(new); - ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); - ret = tswap32(ret); - break; - - case 2: /* elf32 atomic "new" cmpxchg */ - size = env->gr[23]; - if (size >= 4) { - return -TARGET_ENOSYS; - } - if (((addr | old | new) & ((1 << size) - 1)) - || !access_ok(VERIFY_WRITE, addr, 1 << size) - || !access_ok(VERIFY_READ, old, 1 << size) - || !access_ok(VERIFY_READ, new, 1 << size)) { - return -TARGET_EFAULT; - } - /* Note that below we use host-endian loads so that the cmpxchg - can be host-endian as well. */ - switch (size) { - case 0: - old = *(uint8_t *)g2h(old); - new = *(uint8_t *)g2h(new); - ret = atomic_cmpxchg((uint8_t *)g2h(addr), old, new); - ret = ret != old; - break; - case 1: - old = *(uint16_t *)g2h(old); - new = *(uint16_t *)g2h(new); - ret = atomic_cmpxchg((uint16_t *)g2h(addr), old, new); - ret = ret != old; - break; - case 2: - old = *(uint32_t *)g2h(old); - new = *(uint32_t *)g2h(new); - ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); - ret = ret != old; - break; - case 3: - { - uint64_t o64, n64, r64; - o64 = *(uint64_t *)g2h(old); - n64 = *(uint64_t *)g2h(new); -#ifdef CONFIG_ATOMIC64 - r64 = atomic_cmpxchg__nocheck((uint64_t *)g2h(addr), o64, n64); - ret = r64 != o64; -#else - start_exclusive(); - r64 = *(uint64_t *)g2h(addr); - ret = 1; - if (r64 == o64) { - *(uint64_t *)g2h(addr) = n64; - ret = 0; - } - end_exclusive(); -#endif - } - break; - } - break; - } - - env->gr[28] = ret; - return 0; -} - -void cpu_loop(CPUHPPAState *env) -{ - CPUState *cs = CPU(hppa_env_get_cpu(env)); - target_siginfo_t info; - abi_ulong ret; - int trapnr; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - switch (trapnr) { - case EXCP_SYSCALL: - ret = do_syscall(env, env->gr[20], - env->gr[26], env->gr[25], - env->gr[24], env->gr[23], - env->gr[22], env->gr[21], 0, 0); - switch (ret) { - default: - env->gr[28] = ret; - /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; - break; - case -TARGET_ERESTARTSYS: - case -TARGET_QEMU_ESIGRETURN: - break; - } - break; - case EXCP_SYSCALL_LWS: - env->gr[21] = hppa_lws(env); - /* We arrived here by faking the gateway page. Return. */ - env->iaoq_f = env->gr[31]; - env->iaoq_b = env->gr[31] + 4; - break; - case EXCP_ITLB_MISS: - case EXCP_DTLB_MISS: - case EXCP_NA_ITLB_MISS: - case EXCP_NA_DTLB_MISS: - case EXCP_IMP: - case EXCP_DMP: - case EXCP_DMB: - case EXCP_PAGE_REF: - case EXCP_DMAR: - case EXCP_DMPI: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - info._sifields._sigfault._addr = env->cr[CR_IOR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_UNALIGN: - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->cr[CR_IOR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_ILL: - case EXCP_PRIV_OPR: - case EXCP_PRIV_REG: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = TARGET_ILL_ILLOPN; - info._sifields._sigfault._addr = env->iaoq_f; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_OVERFLOW: - case EXCP_COND: - case EXCP_ASSIST: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = 0; - info._sifields._sigfault._addr = env->iaoq_f; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - case EXCP_DEBUG: - trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); - if (trapnr) { - info.si_signo = trapnr; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, trapnr, QEMU_SI_FAULT, &info); - } - break; - case EXCP_INTERRUPT: - /* just indicate that signals should be handled asap */ - break; - default: - g_assert_not_reached(); - } - process_pending_signals(env); - } -} - -#endif /* TARGET_HPPA */ - -#ifdef TARGET_XTENSA - -static void xtensa_rfw(CPUXtensaState *env) -{ - xtensa_restore_owb(env); - env->pc = env->sregs[EPC1]; -} - -static void xtensa_rfwu(CPUXtensaState *env) -{ - env->sregs[WINDOW_START] |= (1 << env->sregs[WINDOW_BASE]); - xtensa_rfw(env); -} - -static void xtensa_rfwo(CPUXtensaState *env) -{ - env->sregs[WINDOW_START] &= ~(1 << env->sregs[WINDOW_BASE]); - xtensa_rfw(env); -} - -static void xtensa_overflow4(CPUXtensaState *env) -{ - put_user_ual(env->regs[0], env->regs[5] - 16); - put_user_ual(env->regs[1], env->regs[5] - 12); - put_user_ual(env->regs[2], env->regs[5] - 8); - put_user_ual(env->regs[3], env->regs[5] - 4); - xtensa_rfwo(env); -} - -static void xtensa_underflow4(CPUXtensaState *env) -{ - get_user_ual(env->regs[0], env->regs[5] - 16); - get_user_ual(env->regs[1], env->regs[5] - 12); - get_user_ual(env->regs[2], env->regs[5] - 8); - get_user_ual(env->regs[3], env->regs[5] - 4); - xtensa_rfwu(env); -} - -static void xtensa_overflow8(CPUXtensaState *env) -{ - put_user_ual(env->regs[0], env->regs[9] - 16); - get_user_ual(env->regs[0], env->regs[1] - 12); - put_user_ual(env->regs[1], env->regs[9] - 12); - put_user_ual(env->regs[2], env->regs[9] - 8); - put_user_ual(env->regs[3], env->regs[9] - 4); - put_user_ual(env->regs[4], env->regs[0] - 32); - put_user_ual(env->regs[5], env->regs[0] - 28); - put_user_ual(env->regs[6], env->regs[0] - 24); - put_user_ual(env->regs[7], env->regs[0] - 20); - xtensa_rfwo(env); -} - -static void xtensa_underflow8(CPUXtensaState *env) -{ - get_user_ual(env->regs[0], env->regs[9] - 16); - get_user_ual(env->regs[1], env->regs[9] - 12); - get_user_ual(env->regs[2], env->regs[9] - 8); - get_user_ual(env->regs[7], env->regs[1] - 12); - get_user_ual(env->regs[3], env->regs[9] - 4); - get_user_ual(env->regs[4], env->regs[7] - 32); - get_user_ual(env->regs[5], env->regs[7] - 28); - get_user_ual(env->regs[6], env->regs[7] - 24); - get_user_ual(env->regs[7], env->regs[7] - 20); - xtensa_rfwu(env); -} - -static void xtensa_overflow12(CPUXtensaState *env) -{ - put_user_ual(env->regs[0], env->regs[13] - 16); - get_user_ual(env->regs[0], env->regs[1] - 12); - put_user_ual(env->regs[1], env->regs[13] - 12); - put_user_ual(env->regs[2], env->regs[13] - 8); - put_user_ual(env->regs[3], env->regs[13] - 4); - put_user_ual(env->regs[4], env->regs[0] - 48); - put_user_ual(env->regs[5], env->regs[0] - 44); - put_user_ual(env->regs[6], env->regs[0] - 40); - put_user_ual(env->regs[7], env->regs[0] - 36); - put_user_ual(env->regs[8], env->regs[0] - 32); - put_user_ual(env->regs[9], env->regs[0] - 28); - put_user_ual(env->regs[10], env->regs[0] - 24); - put_user_ual(env->regs[11], env->regs[0] - 20); - xtensa_rfwo(env); -} - -static void xtensa_underflow12(CPUXtensaState *env) -{ - get_user_ual(env->regs[0], env->regs[13] - 16); - get_user_ual(env->regs[1], env->regs[13] - 12); - get_user_ual(env->regs[2], env->regs[13] - 8); - get_user_ual(env->regs[11], env->regs[1] - 12); - get_user_ual(env->regs[3], env->regs[13] - 4); - get_user_ual(env->regs[4], env->regs[11] - 48); - get_user_ual(env->regs[5], env->regs[11] - 44); - get_user_ual(env->regs[6], env->regs[11] - 40); - get_user_ual(env->regs[7], env->regs[11] - 36); - get_user_ual(env->regs[8], env->regs[11] - 32); - get_user_ual(env->regs[9], env->regs[11] - 28); - get_user_ual(env->regs[10], env->regs[11] - 24); - get_user_ual(env->regs[11], env->regs[11] - 20); - xtensa_rfwu(env); -} - -void cpu_loop(CPUXtensaState *env) -{ - CPUState *cs = CPU(xtensa_env_get_cpu(env)); - target_siginfo_t info; - abi_ulong ret; - int trapnr; - - while (1) { - cpu_exec_start(cs); - trapnr = cpu_exec(cs); - cpu_exec_end(cs); - process_queued_cpu_work(cs); - - env->sregs[PS] &= ~PS_EXCM; - switch (trapnr) { - case EXCP_INTERRUPT: - break; - - case EXC_WINDOW_OVERFLOW4: - xtensa_overflow4(env); - break; - case EXC_WINDOW_UNDERFLOW4: - xtensa_underflow4(env); - break; - case EXC_WINDOW_OVERFLOW8: - xtensa_overflow8(env); - break; - case EXC_WINDOW_UNDERFLOW8: - xtensa_underflow8(env); - break; - case EXC_WINDOW_OVERFLOW12: - xtensa_overflow12(env); - break; - case EXC_WINDOW_UNDERFLOW12: - xtensa_underflow12(env); - break; - - case EXC_USER: - switch (env->sregs[EXCCAUSE]) { - case ILLEGAL_INSTRUCTION_CAUSE: - case PRIVILEGED_CAUSE: - info.si_signo = TARGET_SIGILL; - info.si_errno = 0; - info.si_code = - env->sregs[EXCCAUSE] == ILLEGAL_INSTRUCTION_CAUSE ? - TARGET_ILL_ILLOPC : TARGET_ILL_PRVOPC; - info._sifields._sigfault._addr = env->sregs[EPC1]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - - case SYSCALL_CAUSE: - env->pc += 3; - ret = do_syscall(env, env->regs[2], - env->regs[6], env->regs[3], - env->regs[4], env->regs[5], - env->regs[8], env->regs[9], 0, 0); - switch (ret) { - default: - env->regs[2] = ret; - break; - - case -TARGET_ERESTARTSYS: - env->pc -= 3; - break; - - case -TARGET_QEMU_ESIGRETURN: - break; - } - break; - - case ALLOCA_CAUSE: - env->sregs[PS] = deposit32(env->sregs[PS], - PS_OWB_SHIFT, - PS_OWB_LEN, - env->sregs[WINDOW_BASE]); - - switch (env->regs[0] & 0xc0000000) { - case 0x00000000: - case 0x40000000: - xtensa_rotate_window(env, -1); - xtensa_underflow4(env); - break; - - case 0x80000000: - xtensa_rotate_window(env, -2); - xtensa_underflow8(env); - break; - - case 0xc0000000: - xtensa_rotate_window(env, -3); - xtensa_underflow12(env); - break; - } - break; - - case INTEGER_DIVIDE_BY_ZERO_CAUSE: - info.si_signo = TARGET_SIGFPE; - info.si_errno = 0; - info.si_code = TARGET_FPE_INTDIV; - info._sifields._sigfault._addr = env->sregs[EPC1]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - - case LOAD_PROHIBITED_CAUSE: - case STORE_PROHIBITED_CAUSE: - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_ACCERR; - info._sifields._sigfault._addr = env->sregs[EXCVADDR]; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - break; - - default: - fprintf(stderr, "exccause = %d\n", env->sregs[EXCCAUSE]); - g_assert_not_reached(); - } - break; - case EXCP_DEBUG: - trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); - if (trapnr) { - info.si_signo = trapnr; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, trapnr, QEMU_SI_FAULT, &info); - } - break; - case EXC_DEBUG: - default: - fprintf(stderr, "trapnr = %d\n", trapnr); - g_assert_not_reached(); - } - process_pending_signals(env); - } -} - -#endif /* TARGET_XTENSA */ - __thread CPUState *thread_cpu; bool qemu_cpu_is_self(CPUState *cpu) @@ -4209,9 +268,9 @@ static void handle_arg_stack_size(const char *arg) } if (*p == 'M') { - guest_stack_size *= 1024 * 1024; + guest_stack_size *= MiB; } else if (*p == 'k' || *p == 'K') { - guest_stack_size *= 1024; + guest_stack_size *= KiB; } } @@ -4603,9 +662,20 @@ int main(int argc, char **argv, char **envp) } cpu_type = parse_cpu_model(cpu_model); + /* init tcg before creating CPUs and to get qemu_host_page_size */ tcg_exec_init(0); - /* NOTE: we need to init the CPU at this stage to get - qemu_host_page_size */ + + /* Reserving *too* much vm space via mmap can run into problems + with rlimits, oom due to page table creation, etc. We will still try it, + if directed by the command-line option, but not by default. */ + if (HOST_LONG_BITS == 64 && + TARGET_VIRT_ADDR_SPACE_BITS <= 32 && + reserved_va == 0) { + /* reserved_va must be aligned with the host page size + * as it is used with mmap() + */ + reserved_va = MAX_RESERVED_VA & qemu_host_page_mask; + } cpu = cpu_create(cpu_type); env = cpu->env_ptr; @@ -4625,7 +695,7 @@ int main(int argc, char **argv, char **envp) envlist_free(envlist); /* - * Now that page sizes are configured in cpu_init() we can do + * Now that page sizes are configured in tcg_exec_init() we can do * proper page alignment for guest_base. */ guest_base = HOST_PAGE_ALIGN(guest_base); @@ -4736,405 +806,7 @@ int main(int argc, char **argv, char **envp) tcg_prologue_init(tcg_ctx); tcg_region_init(); -#if defined(TARGET_I386) - env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; - env->hflags |= HF_PE_MASK | HF_CPL_MASK; - if (env->features[FEAT_1_EDX] & CPUID_SSE) { - env->cr[4] |= CR4_OSFXSR_MASK; - env->hflags |= HF_OSFXSR_MASK; - } -#ifndef TARGET_ABI32 - /* enable 64 bit mode if possible */ - if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) { - fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n"); - exit(EXIT_FAILURE); - } - env->cr[4] |= CR4_PAE_MASK; - env->efer |= MSR_EFER_LMA | MSR_EFER_LME; - env->hflags |= HF_LMA_MASK; -#endif - - /* flags setup : we activate the IRQs by default as in user mode */ - env->eflags |= IF_MASK; - - /* linux register setup */ -#ifndef TARGET_ABI32 - env->regs[R_EAX] = regs->rax; - env->regs[R_EBX] = regs->rbx; - env->regs[R_ECX] = regs->rcx; - env->regs[R_EDX] = regs->rdx; - env->regs[R_ESI] = regs->rsi; - env->regs[R_EDI] = regs->rdi; - env->regs[R_EBP] = regs->rbp; - env->regs[R_ESP] = regs->rsp; - env->eip = regs->rip; -#else - env->regs[R_EAX] = regs->eax; - env->regs[R_EBX] = regs->ebx; - env->regs[R_ECX] = regs->ecx; - env->regs[R_EDX] = regs->edx; - env->regs[R_ESI] = regs->esi; - env->regs[R_EDI] = regs->edi; - env->regs[R_EBP] = regs->ebp; - env->regs[R_ESP] = regs->esp; - env->eip = regs->eip; -#endif - - /* linux interrupt setup */ -#ifndef TARGET_ABI32 - env->idt.limit = 511; -#else - env->idt.limit = 255; -#endif - env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1), - PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - idt_table = g2h(env->idt.base); - set_idt(0, 0); - set_idt(1, 0); - set_idt(2, 0); - set_idt(3, 3); - set_idt(4, 3); - set_idt(5, 0); - set_idt(6, 0); - set_idt(7, 0); - set_idt(8, 0); - set_idt(9, 0); - set_idt(10, 0); - set_idt(11, 0); - set_idt(12, 0); - set_idt(13, 0); - set_idt(14, 0); - set_idt(15, 0); - set_idt(16, 0); - set_idt(17, 0); - set_idt(18, 0); - set_idt(19, 0); - set_idt(0x80, 3); - - /* linux segment setup */ - { - uint64_t *gdt_table; - env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES, - PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1; - gdt_table = g2h(env->gdt.base); -#ifdef TARGET_ABI32 - write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | - (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); -#else - /* 64 bit code segment */ - write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | - DESC_L_MASK | - (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT)); -#endif - write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK | - (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT)); - } - cpu_x86_load_seg(env, R_CS, __USER_CS); - cpu_x86_load_seg(env, R_SS, __USER_DS); -#ifdef TARGET_ABI32 - cpu_x86_load_seg(env, R_DS, __USER_DS); - cpu_x86_load_seg(env, R_ES, __USER_DS); - cpu_x86_load_seg(env, R_FS, __USER_DS); - cpu_x86_load_seg(env, R_GS, __USER_DS); - /* This hack makes Wine work... */ - env->segs[R_FS].selector = 0; -#else - cpu_x86_load_seg(env, R_DS, 0); - cpu_x86_load_seg(env, R_ES, 0); - cpu_x86_load_seg(env, R_FS, 0); - cpu_x86_load_seg(env, R_GS, 0); -#endif -#elif defined(TARGET_AARCH64) - { - int i; - - if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { - fprintf(stderr, - "The selected ARM CPU does not support 64 bit mode\n"); - exit(EXIT_FAILURE); - } - - for (i = 0; i < 31; i++) { - env->xregs[i] = regs->regs[i]; - } - env->pc = regs->pc; - env->xregs[31] = regs->sp; -#ifdef TARGET_WORDS_BIGENDIAN - env->cp15.sctlr_el[1] |= SCTLR_E0E; - for (i = 1; i < 4; ++i) { - env->cp15.sctlr_el[i] |= SCTLR_EE; - } -#endif - } -#elif defined(TARGET_ARM) - { - int i; - cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, - CPSRWriteByInstr); - for(i = 0; i < 16; i++) { - env->regs[i] = regs->uregs[i]; - } -#ifdef TARGET_WORDS_BIGENDIAN - /* Enable BE8. */ - if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 - && (info->elf_flags & EF_ARM_BE8)) { - env->uncached_cpsr |= CPSR_E; - env->cp15.sctlr_el[1] |= SCTLR_E0E; - } else { - env->cp15.sctlr_el[1] |= SCTLR_B; - } -#endif - } -#elif defined(TARGET_SPARC) - { - int i; - env->pc = regs->pc; - env->npc = regs->npc; - env->y = regs->y; - for(i = 0; i < 8; i++) - env->gregs[i] = regs->u_regs[i]; - for(i = 0; i < 8; i++) - env->regwptr[i] = regs->u_regs[i + 8]; - } -#elif defined(TARGET_PPC) - { - int i; - -#if defined(TARGET_PPC64) - int flag = (env->insns_flags2 & PPC2_BOOKE206) ? MSR_CM : MSR_SF; -#if defined(TARGET_ABI32) - env->msr &= ~((target_ulong)1 << flag); -#else - env->msr |= (target_ulong)1 << flag; -#endif -#endif - env->nip = regs->nip; - for(i = 0; i < 32; i++) { - env->gpr[i] = regs->gpr[i]; - } - } -#elif defined(TARGET_M68K) - { - env->pc = regs->pc; - env->dregs[0] = regs->d0; - env->dregs[1] = regs->d1; - env->dregs[2] = regs->d2; - env->dregs[3] = regs->d3; - env->dregs[4] = regs->d4; - env->dregs[5] = regs->d5; - env->dregs[6] = regs->d6; - env->dregs[7] = regs->d7; - env->aregs[0] = regs->a0; - env->aregs[1] = regs->a1; - env->aregs[2] = regs->a2; - env->aregs[3] = regs->a3; - env->aregs[4] = regs->a4; - env->aregs[5] = regs->a5; - env->aregs[6] = regs->a6; - env->aregs[7] = regs->usp; - env->sr = regs->sr; - ts->sim_syscalls = 1; - } -#elif defined(TARGET_MICROBLAZE) - { - env->regs[0] = regs->r0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = regs->r14; - env->regs[15] = regs->r15; - env->regs[16] = regs->r16; - env->regs[17] = regs->r17; - env->regs[18] = regs->r18; - env->regs[19] = regs->r19; - env->regs[20] = regs->r20; - env->regs[21] = regs->r21; - env->regs[22] = regs->r22; - env->regs[23] = regs->r23; - env->regs[24] = regs->r24; - env->regs[25] = regs->r25; - env->regs[26] = regs->r26; - env->regs[27] = regs->r27; - env->regs[28] = regs->r28; - env->regs[29] = regs->r29; - env->regs[30] = regs->r30; - env->regs[31] = regs->r31; - env->sregs[SR_PC] = regs->pc; - } -#elif defined(TARGET_MIPS) - { - int i; - - for(i = 0; i < 32; i++) { - env->active_tc.gpr[i] = regs->regs[i]; - } - env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; - if (regs->cp0_epc & 1) { - env->hflags |= MIPS_HFLAG_M16; - } - if (((info->elf_flags & EF_MIPS_NAN2008) != 0) != - ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) { - if ((env->active_fpu.fcr31_rw_bitmask & - (1 << FCR31_NAN2008)) == 0) { - fprintf(stderr, "ELF binary's NaN mode not supported by CPU\n"); - exit(1); - } - if ((info->elf_flags & EF_MIPS_NAN2008) != 0) { - env->active_fpu.fcr31 |= (1 << FCR31_NAN2008); - } else { - env->active_fpu.fcr31 &= ~(1 << FCR31_NAN2008); - } - restore_snan_bit_mode(env); - } - } -#elif defined(TARGET_NIOS2) - { - env->regs[0] = 0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = regs->r14; - env->regs[15] = regs->r15; - /* TODO: unsigned long orig_r2; */ - env->regs[R_RA] = regs->ra; - env->regs[R_FP] = regs->fp; - env->regs[R_SP] = regs->sp; - env->regs[R_GP] = regs->gp; - env->regs[CR_ESTATUS] = regs->estatus; - env->regs[R_EA] = regs->ea; - /* TODO: unsigned long orig_r7; */ - - /* Emulate eret when starting thread. */ - env->regs[R_PC] = regs->ea; - } -#elif defined(TARGET_OPENRISC) - { - int i; - - for (i = 0; i < 32; i++) { - cpu_set_gpr(env, i, regs->gpr[i]); - } - env->pc = regs->pc; - cpu_set_sr(env, regs->sr); - } -#elif defined(TARGET_RISCV) - { - env->pc = regs->sepc; - env->gpr[xSP] = regs->sp; - } -#elif defined(TARGET_SH4) - { - int i; - - for(i = 0; i < 16; i++) { - env->gregs[i] = regs->regs[i]; - } - env->pc = regs->pc; - } -#elif defined(TARGET_ALPHA) - { - int i; - - for(i = 0; i < 28; i++) { - env->ir[i] = ((abi_ulong *)regs)[i]; - } - env->ir[IR_SP] = regs->usp; - env->pc = regs->pc; - } -#elif defined(TARGET_CRIS) - { - env->regs[0] = regs->r0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = info->start_stack; - env->regs[15] = regs->acr; - env->pc = regs->erp; - } -#elif defined(TARGET_S390X) - { - int i; - for (i = 0; i < 16; i++) { - env->regs[i] = regs->gprs[i]; - } - env->psw.mask = regs->psw.mask; - env->psw.addr = regs->psw.addr; - } -#elif defined(TARGET_TILEGX) - { - int i; - for (i = 0; i < TILEGX_R_COUNT; i++) { - env->regs[i] = regs->regs[i]; - } - for (i = 0; i < TILEGX_SPR_COUNT; i++) { - env->spregs[i] = 0; - } - env->pc = regs->pc; - } -#elif defined(TARGET_HPPA) - { - int i; - for (i = 1; i < 32; i++) { - env->gr[i] = regs->gr[i]; - } - env->iaoq_f = regs->iaoq[0]; - env->iaoq_b = regs->iaoq[1]; - } -#elif defined(TARGET_XTENSA) - { - int i; - for (i = 0; i < 16; ++i) { - env->regs[i] = regs->areg[i]; - } - env->sregs[WINDOW_START] = regs->windowstart; - env->pc = regs->pc; - } -#else -#error unsupported target CPU -#endif - -#if defined(TARGET_ARM) || defined(TARGET_M68K) - ts->stack_base = info->start_stack; - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; -#endif + target_cpu_copy_regs(env, regs); if (gdbstub_port) { if (gdbserver_start(gdbstub_port) < 0) { diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c new file mode 100644 index 0000000000..2af93eb39a --- /dev/null +++ b/linux-user/microblaze/cpu_loop.c @@ -0,0 +1,176 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void cpu_loop(CPUMBState *env) +{ + CPUState *cs = CPU(mb_env_get_cpu(env)); + int trapnr, ret; + target_siginfo_t info; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case 0xaa: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_BREAK: + /* Return address is 4 bytes after the call. */ + env->regs[14] += 4; + env->sregs[SR_PC] = env->regs[14]; + ret = do_syscall(env, + env->regs[12], + env->regs[5], + env->regs[6], + env->regs[7], + env->regs[8], + env->regs[9], + env->regs[10], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + /* Wind back to before the syscall. */ + env->sregs[SR_PC] -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[3] = ret; + } + /* All syscall exits result in guest r14 being equal to the + * PC we return to, because the kernel syscall exit "rtbd" does + * this. (This is true even for sigreturn(); note that r14 is + * not a userspace-usable register, as the kernel may clobber it + * at any point.) + */ + env->regs[14] = env->sregs[SR_PC]; + break; + case EXCP_HW_EXCP: + env->regs[17] = env->sregs[SR_PC] + 4; + if (env->iflags & D_FLAG) { + env->sregs[SR_ESR] |= 1 << 12; + env->sregs[SR_PC] -= 4; + /* FIXME: if branch was immed, replay the imm as well. */ + } + + env->iflags &= ~(IMM_FLAG | D_FLAG); + + switch (env->sregs[SR_ESR] & 31) { + case ESR_EC_DIVZERO: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_FLTDIV; + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case ESR_EC_FPU: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + if (env->sregs[SR_FSR] & FSR_IO) { + info.si_code = TARGET_FPE_FLTINV; + } + if (env->sregs[SR_FSR] & FSR_DZ) { + info.si_code = TARGET_FPE_FLTDIV; + } + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + default: + fprintf(stderr, "Unhandled hw-exception: 0x%" PRIx64 "\n", + env->sregs[SR_ESR] & ESR_EC_MASK); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + break; + } + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + env->regs[0] = regs->r0; + env->regs[1] = regs->r1; + env->regs[2] = regs->r2; + env->regs[3] = regs->r3; + env->regs[4] = regs->r4; + env->regs[5] = regs->r5; + env->regs[6] = regs->r6; + env->regs[7] = regs->r7; + env->regs[8] = regs->r8; + env->regs[9] = regs->r9; + env->regs[10] = regs->r10; + env->regs[11] = regs->r11; + env->regs[12] = regs->r12; + env->regs[13] = regs->r13; + env->regs[14] = regs->r14; + env->regs[15] = regs->r15; + env->regs[16] = regs->r16; + env->regs[17] = regs->r17; + env->regs[18] = regs->r18; + env->regs[19] = regs->r19; + env->regs[20] = regs->r20; + env->regs[21] = regs->r21; + env->regs[22] = regs->r22; + env->regs[23] = regs->r23; + env->regs[24] = regs->r24; + env->regs[25] = regs->r25; + env->regs[26] = regs->r26; + env->regs[27] = regs->r27; + env->regs[28] = regs->r28; + env->regs[29] = regs->r29; + env->regs[30] = regs->r30; + env->regs[31] = regs->r31; + env->sregs[SR_PC] = regs->pc; +} diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c new file mode 100644 index 0000000000..80950c2181 --- /dev/null +++ b/linux-user/microblaze/signal.c @@ -0,0 +1,245 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + struct target_pt_regs regs; /* needs to be first */ + uint32_t oldmask; +}; + +struct target_stack_t { + abi_ulong ss_sp; + int ss_flags; + unsigned int ss_size; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + struct target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + uint32_t tuc_extramask[TARGET_NSIG_WORDS - 1]; +}; + +/* Signal frames. */ +struct target_signal_frame { + struct target_ucontext uc; + uint32_t extramask[TARGET_NSIG_WORDS - 1]; + uint32_t tramp[2]; +}; + +struct rt_signal_frame { + siginfo_t info; + ucontext_t uc; + uint32_t tramp[2]; +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUMBState *env) +{ + __put_user(env->regs[0], &sc->regs.r0); + __put_user(env->regs[1], &sc->regs.r1); + __put_user(env->regs[2], &sc->regs.r2); + __put_user(env->regs[3], &sc->regs.r3); + __put_user(env->regs[4], &sc->regs.r4); + __put_user(env->regs[5], &sc->regs.r5); + __put_user(env->regs[6], &sc->regs.r6); + __put_user(env->regs[7], &sc->regs.r7); + __put_user(env->regs[8], &sc->regs.r8); + __put_user(env->regs[9], &sc->regs.r9); + __put_user(env->regs[10], &sc->regs.r10); + __put_user(env->regs[11], &sc->regs.r11); + __put_user(env->regs[12], &sc->regs.r12); + __put_user(env->regs[13], &sc->regs.r13); + __put_user(env->regs[14], &sc->regs.r14); + __put_user(env->regs[15], &sc->regs.r15); + __put_user(env->regs[16], &sc->regs.r16); + __put_user(env->regs[17], &sc->regs.r17); + __put_user(env->regs[18], &sc->regs.r18); + __put_user(env->regs[19], &sc->regs.r19); + __put_user(env->regs[20], &sc->regs.r20); + __put_user(env->regs[21], &sc->regs.r21); + __put_user(env->regs[22], &sc->regs.r22); + __put_user(env->regs[23], &sc->regs.r23); + __put_user(env->regs[24], &sc->regs.r24); + __put_user(env->regs[25], &sc->regs.r25); + __put_user(env->regs[26], &sc->regs.r26); + __put_user(env->regs[27], &sc->regs.r27); + __put_user(env->regs[28], &sc->regs.r28); + __put_user(env->regs[29], &sc->regs.r29); + __put_user(env->regs[30], &sc->regs.r30); + __put_user(env->regs[31], &sc->regs.r31); + __put_user(env->sregs[SR_PC], &sc->regs.pc); +} + +static void restore_sigcontext(struct target_sigcontext *sc, CPUMBState *env) +{ + __get_user(env->regs[0], &sc->regs.r0); + __get_user(env->regs[1], &sc->regs.r1); + __get_user(env->regs[2], &sc->regs.r2); + __get_user(env->regs[3], &sc->regs.r3); + __get_user(env->regs[4], &sc->regs.r4); + __get_user(env->regs[5], &sc->regs.r5); + __get_user(env->regs[6], &sc->regs.r6); + __get_user(env->regs[7], &sc->regs.r7); + __get_user(env->regs[8], &sc->regs.r8); + __get_user(env->regs[9], &sc->regs.r9); + __get_user(env->regs[10], &sc->regs.r10); + __get_user(env->regs[11], &sc->regs.r11); + __get_user(env->regs[12], &sc->regs.r12); + __get_user(env->regs[13], &sc->regs.r13); + __get_user(env->regs[14], &sc->regs.r14); + __get_user(env->regs[15], &sc->regs.r15); + __get_user(env->regs[16], &sc->regs.r16); + __get_user(env->regs[17], &sc->regs.r17); + __get_user(env->regs[18], &sc->regs.r18); + __get_user(env->regs[19], &sc->regs.r19); + __get_user(env->regs[20], &sc->regs.r20); + __get_user(env->regs[21], &sc->regs.r21); + __get_user(env->regs[22], &sc->regs.r22); + __get_user(env->regs[23], &sc->regs.r23); + __get_user(env->regs[24], &sc->regs.r24); + __get_user(env->regs[25], &sc->regs.r25); + __get_user(env->regs[26], &sc->regs.r26); + __get_user(env->regs[27], &sc->regs.r27); + __get_user(env->regs[28], &sc->regs.r28); + __get_user(env->regs[29], &sc->regs.r29); + __get_user(env->regs[30], &sc->regs.r30); + __get_user(env->regs[31], &sc->regs.r31); + __get_user(env->sregs[SR_PC], &sc->regs.pc); +} + +static abi_ulong get_sigframe(struct target_sigaction *ka, + CPUMBState *env, int frame_size) +{ + abi_ulong sp = env->regs[1]; + + sp = target_sigsp(sp, ka); + + return ((sp - frame_size) & -8UL); +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUMBState *env) +{ + struct target_signal_frame *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)) + goto badframe; + + /* Save the mask. */ + __put_user(set->sig[0], &frame->uc.tuc_mcontext.oldmask); + + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->extramask[i - 1]); + } + + setup_sigcontext(&frame->uc.tuc_mcontext, env); + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + /* minus 8 is offset to cater for "rtsd r15,8" offset */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + env->regs[15] = ((unsigned long)ka->sa_restorer)-8; + } else { + uint32_t t; + /* Note, these encodings are _big endian_! */ + /* addi r12, r0, __NR_sigreturn */ + t = 0x31800000UL | TARGET_NR_sigreturn; + __put_user(t, frame->tramp + 0); + /* brki r14, 0x8 */ + t = 0xb9cc0008UL; + __put_user(t, frame->tramp + 1); + + /* Return from sighandler will jump to the tramp. + Negative 8 offset because return is rtsd r15, 8 */ + env->regs[15] = frame_addr + offsetof(struct target_signal_frame, tramp) + - 8; + } + + /* Set up registers for signal handler */ + env->regs[1] = frame_addr; + /* Signal handler args: */ + env->regs[5] = sig; /* Arg 0: signum */ + env->regs[6] = 0; + /* arg 1: sigcontext */ + env->regs[7] = frame_addr += offsetof(typeof(*frame), uc); + + /* Offset of 4 to handle microblaze rtid r14, 0 */ + env->sregs[SR_PC] = (unsigned long)ka->_sa_handler; + + unlock_user_struct(frame, frame_addr, 1); + return; +badframe: + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUMBState *env) +{ + qemu_log_mask(LOG_UNIMP, "setup_rt_frame: not implemented\n"); +} + +long do_sigreturn(CPUMBState *env) +{ + struct target_signal_frame *frame; + abi_ulong frame_addr; + target_sigset_t target_set; + sigset_t set; + int i; + + frame_addr = env->regs[R_SP]; + trace_user_do_sigreturn(env, frame_addr); + /* Make sure the guest isn't playing games. */ + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) + goto badframe; + + /* Restore blocked signals */ + __get_user(target_set.sig[0], &frame->uc.tuc_mcontext.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + + restore_sigcontext(&frame->uc.tuc_mcontext, env); + /* We got here through a sigreturn syscall, our path back is via an + rtb insn so setup r14 for that. */ + env->regs[14] = env->sregs[SR_PC]; + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUMBState *env) +{ + trace_user_do_rt_sigreturn(env, 0); + qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} diff --git a/linux-user/microblaze/sockbits.h b/linux-user/microblaze/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/microblaze/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/microblaze/syscall_nr.h b/linux-user/microblaze/syscall_nr.h index 0704449bae..5d1a47a9a9 100644 --- a/linux-user/microblaze/syscall_nr.h +++ b/linux-user/microblaze/syscall_nr.h @@ -363,7 +363,7 @@ #define TARGET_NR_shutdown 359 /* new */ #define TARGET_NR_sendmsg 360 /* new */ #define TARGET_NR_recvmsg 361 /* new */ -#define TARGET_NR_accept04 362 /* new */ +#define TARGET_NR_accept4 362 /* new */ #define TARGET_NR_preadv 363 /* new */ #define TARGET_NR_pwritev 364 /* new */ #define TARGET_NR_rt_tgsigqueueinfo 365 /* new */ diff --git a/linux-user/microblaze/target_cpu.h b/linux-user/microblaze/target_cpu.h index 7dd979f960..73e139938c 100644 --- a/linux-user/microblaze/target_cpu.h +++ b/linux-user/microblaze/target_cpu.h @@ -32,4 +32,8 @@ static inline void cpu_set_tls(CPUMBState *env, target_ulong newtls) env->regs[21] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUMBState *state) +{ + return state->regs[1]; +} #endif diff --git a/linux-user/microblaze/target_fcntl.h b/linux-user/microblaze/target_fcntl.h new file mode 100644 index 0000000000..45402275ff --- /dev/null +++ b/linux-user/microblaze/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef MICROBLAZE_TARGET_FCNTL_H +#define MICROBLAZE_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/microblaze/target_signal.h b/linux-user/microblaze/target_signal.h index 642865f12e..35efd5e928 100644 --- a/linux-user/microblaze/target_signal.h +++ b/linux-user/microblaze/target_signal.h @@ -1,8 +1,6 @@ #ifndef MICROBLAZE_TARGET_SIGNAL_H #define MICROBLAZE_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUMBState *state) -{ - return state->regs[1]; -} - +#include "../generic/signal.h" +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* MICROBLAZE_TARGET_SIGNAL_H */ diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c new file mode 100644 index 0000000000..084ad6a041 --- /dev/null +++ b/linux-user/mips/cpu_loop.c @@ -0,0 +1,749 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" +#include "elf.h" + +# ifdef TARGET_ABI_MIPSO32 +# define MIPS_SYS(name, args) args, +static const uint8_t mips_syscall_args[] = { + MIPS_SYS(sys_syscall , 8) /* 4000 */ + MIPS_SYS(sys_exit , 1) + MIPS_SYS(sys_fork , 0) + MIPS_SYS(sys_read , 3) + MIPS_SYS(sys_write , 3) + MIPS_SYS(sys_open , 3) /* 4005 */ + MIPS_SYS(sys_close , 1) + MIPS_SYS(sys_waitpid , 3) + MIPS_SYS(sys_creat , 2) + MIPS_SYS(sys_link , 2) + MIPS_SYS(sys_unlink , 1) /* 4010 */ + MIPS_SYS(sys_execve , 0) + MIPS_SYS(sys_chdir , 1) + MIPS_SYS(sys_time , 1) + MIPS_SYS(sys_mknod , 3) + MIPS_SYS(sys_chmod , 2) /* 4015 */ + MIPS_SYS(sys_lchown , 3) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_stat */ + MIPS_SYS(sys_lseek , 3) + MIPS_SYS(sys_getpid , 0) /* 4020 */ + MIPS_SYS(sys_mount , 5) + MIPS_SYS(sys_umount , 1) + MIPS_SYS(sys_setuid , 1) + MIPS_SYS(sys_getuid , 0) + MIPS_SYS(sys_stime , 1) /* 4025 */ + MIPS_SYS(sys_ptrace , 4) + MIPS_SYS(sys_alarm , 1) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_fstat */ + MIPS_SYS(sys_pause , 0) + MIPS_SYS(sys_utime , 2) /* 4030 */ + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_access , 2) + MIPS_SYS(sys_nice , 1) + MIPS_SYS(sys_ni_syscall , 0) /* 4035 */ + MIPS_SYS(sys_sync , 0) + MIPS_SYS(sys_kill , 2) + MIPS_SYS(sys_rename , 2) + MIPS_SYS(sys_mkdir , 2) + MIPS_SYS(sys_rmdir , 1) /* 4040 */ + MIPS_SYS(sys_dup , 1) + MIPS_SYS(sys_pipe , 0) + MIPS_SYS(sys_times , 1) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_brk , 1) /* 4045 */ + MIPS_SYS(sys_setgid , 1) + MIPS_SYS(sys_getgid , 0) + MIPS_SYS(sys_ni_syscall , 0) /* was signal(2) */ + MIPS_SYS(sys_geteuid , 0) + MIPS_SYS(sys_getegid , 0) /* 4050 */ + MIPS_SYS(sys_acct , 0) + MIPS_SYS(sys_umount2 , 2) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_ioctl , 3) + MIPS_SYS(sys_fcntl , 3) /* 4055 */ + MIPS_SYS(sys_ni_syscall , 2) + MIPS_SYS(sys_setpgid , 2) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_olduname , 1) + MIPS_SYS(sys_umask , 1) /* 4060 */ + MIPS_SYS(sys_chroot , 1) + MIPS_SYS(sys_ustat , 2) + MIPS_SYS(sys_dup2 , 2) + MIPS_SYS(sys_getppid , 0) + MIPS_SYS(sys_getpgrp , 0) /* 4065 */ + MIPS_SYS(sys_setsid , 0) + MIPS_SYS(sys_sigaction , 3) + MIPS_SYS(sys_sgetmask , 0) + MIPS_SYS(sys_ssetmask , 1) + MIPS_SYS(sys_setreuid , 2) /* 4070 */ + MIPS_SYS(sys_setregid , 2) + MIPS_SYS(sys_sigsuspend , 0) + MIPS_SYS(sys_sigpending , 1) + MIPS_SYS(sys_sethostname , 2) + MIPS_SYS(sys_setrlimit , 2) /* 4075 */ + MIPS_SYS(sys_getrlimit , 2) + MIPS_SYS(sys_getrusage , 2) + MIPS_SYS(sys_gettimeofday, 2) + MIPS_SYS(sys_settimeofday, 2) + MIPS_SYS(sys_getgroups , 2) /* 4080 */ + MIPS_SYS(sys_setgroups , 2) + MIPS_SYS(sys_ni_syscall , 0) /* old_select */ + MIPS_SYS(sys_symlink , 2) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_lstat */ + MIPS_SYS(sys_readlink , 3) /* 4085 */ + MIPS_SYS(sys_uselib , 1) + MIPS_SYS(sys_swapon , 2) + MIPS_SYS(sys_reboot , 3) + MIPS_SYS(old_readdir , 3) + MIPS_SYS(old_mmap , 6) /* 4090 */ + MIPS_SYS(sys_munmap , 2) + MIPS_SYS(sys_truncate , 2) + MIPS_SYS(sys_ftruncate , 2) + MIPS_SYS(sys_fchmod , 2) + MIPS_SYS(sys_fchown , 3) /* 4095 */ + MIPS_SYS(sys_getpriority , 2) + MIPS_SYS(sys_setpriority , 3) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_statfs , 2) + MIPS_SYS(sys_fstatfs , 2) /* 4100 */ + MIPS_SYS(sys_ni_syscall , 0) /* was ioperm(2) */ + MIPS_SYS(sys_socketcall , 2) + MIPS_SYS(sys_syslog , 3) + MIPS_SYS(sys_setitimer , 3) + MIPS_SYS(sys_getitimer , 2) /* 4105 */ + MIPS_SYS(sys_newstat , 2) + MIPS_SYS(sys_newlstat , 2) + MIPS_SYS(sys_newfstat , 2) + MIPS_SYS(sys_uname , 1) + MIPS_SYS(sys_ni_syscall , 0) /* 4110 was iopl(2) */ + MIPS_SYS(sys_vhangup , 0) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_idle() */ + MIPS_SYS(sys_ni_syscall , 0) /* was sys_vm86 */ + MIPS_SYS(sys_wait4 , 4) + MIPS_SYS(sys_swapoff , 1) /* 4115 */ + MIPS_SYS(sys_sysinfo , 1) + MIPS_SYS(sys_ipc , 6) + MIPS_SYS(sys_fsync , 1) + MIPS_SYS(sys_sigreturn , 0) + MIPS_SYS(sys_clone , 6) /* 4120 */ + MIPS_SYS(sys_setdomainname, 2) + MIPS_SYS(sys_newuname , 1) + MIPS_SYS(sys_ni_syscall , 0) /* sys_modify_ldt */ + MIPS_SYS(sys_adjtimex , 1) + MIPS_SYS(sys_mprotect , 3) /* 4125 */ + MIPS_SYS(sys_sigprocmask , 3) + MIPS_SYS(sys_ni_syscall , 0) /* was create_module */ + MIPS_SYS(sys_init_module , 5) + MIPS_SYS(sys_delete_module, 1) + MIPS_SYS(sys_ni_syscall , 0) /* 4130 was get_kernel_syms */ + MIPS_SYS(sys_quotactl , 0) + MIPS_SYS(sys_getpgid , 1) + MIPS_SYS(sys_fchdir , 1) + MIPS_SYS(sys_bdflush , 2) + MIPS_SYS(sys_sysfs , 3) /* 4135 */ + MIPS_SYS(sys_personality , 1) + MIPS_SYS(sys_ni_syscall , 0) /* for afs_syscall */ + MIPS_SYS(sys_setfsuid , 1) + MIPS_SYS(sys_setfsgid , 1) + MIPS_SYS(sys_llseek , 5) /* 4140 */ + MIPS_SYS(sys_getdents , 3) + MIPS_SYS(sys_select , 5) + MIPS_SYS(sys_flock , 2) + MIPS_SYS(sys_msync , 3) + MIPS_SYS(sys_readv , 3) /* 4145 */ + MIPS_SYS(sys_writev , 3) + MIPS_SYS(sys_cacheflush , 3) + MIPS_SYS(sys_cachectl , 3) + MIPS_SYS(sys_sysmips , 4) + MIPS_SYS(sys_ni_syscall , 0) /* 4150 */ + MIPS_SYS(sys_getsid , 1) + MIPS_SYS(sys_fdatasync , 0) + MIPS_SYS(sys_sysctl , 1) + MIPS_SYS(sys_mlock , 2) + MIPS_SYS(sys_munlock , 2) /* 4155 */ + MIPS_SYS(sys_mlockall , 1) + MIPS_SYS(sys_munlockall , 0) + MIPS_SYS(sys_sched_setparam, 2) + MIPS_SYS(sys_sched_getparam, 2) + MIPS_SYS(sys_sched_setscheduler, 3) /* 4160 */ + MIPS_SYS(sys_sched_getscheduler, 1) + MIPS_SYS(sys_sched_yield , 0) + MIPS_SYS(sys_sched_get_priority_max, 1) + MIPS_SYS(sys_sched_get_priority_min, 1) + MIPS_SYS(sys_sched_rr_get_interval, 2) /* 4165 */ + MIPS_SYS(sys_nanosleep, 2) + MIPS_SYS(sys_mremap , 5) + MIPS_SYS(sys_accept , 3) + MIPS_SYS(sys_bind , 3) + MIPS_SYS(sys_connect , 3) /* 4170 */ + MIPS_SYS(sys_getpeername , 3) + MIPS_SYS(sys_getsockname , 3) + MIPS_SYS(sys_getsockopt , 5) + MIPS_SYS(sys_listen , 2) + MIPS_SYS(sys_recv , 4) /* 4175 */ + MIPS_SYS(sys_recvfrom , 6) + MIPS_SYS(sys_recvmsg , 3) + MIPS_SYS(sys_send , 4) + MIPS_SYS(sys_sendmsg , 3) + MIPS_SYS(sys_sendto , 6) /* 4180 */ + MIPS_SYS(sys_setsockopt , 5) + MIPS_SYS(sys_shutdown , 2) + MIPS_SYS(sys_socket , 3) + MIPS_SYS(sys_socketpair , 4) + MIPS_SYS(sys_setresuid , 3) /* 4185 */ + MIPS_SYS(sys_getresuid , 3) + MIPS_SYS(sys_ni_syscall , 0) /* was sys_query_module */ + MIPS_SYS(sys_poll , 3) + MIPS_SYS(sys_nfsservctl , 3) + MIPS_SYS(sys_setresgid , 3) /* 4190 */ + MIPS_SYS(sys_getresgid , 3) + MIPS_SYS(sys_prctl , 5) + MIPS_SYS(sys_rt_sigreturn, 0) + MIPS_SYS(sys_rt_sigaction, 4) + MIPS_SYS(sys_rt_sigprocmask, 4) /* 4195 */ + MIPS_SYS(sys_rt_sigpending, 2) + MIPS_SYS(sys_rt_sigtimedwait, 4) + MIPS_SYS(sys_rt_sigqueueinfo, 3) + MIPS_SYS(sys_rt_sigsuspend, 0) + MIPS_SYS(sys_pread64 , 6) /* 4200 */ + MIPS_SYS(sys_pwrite64 , 6) + MIPS_SYS(sys_chown , 3) + MIPS_SYS(sys_getcwd , 2) + MIPS_SYS(sys_capget , 2) + MIPS_SYS(sys_capset , 2) /* 4205 */ + MIPS_SYS(sys_sigaltstack , 2) + MIPS_SYS(sys_sendfile , 4) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_mmap2 , 6) /* 4210 */ + MIPS_SYS(sys_truncate64 , 4) + MIPS_SYS(sys_ftruncate64 , 4) + MIPS_SYS(sys_stat64 , 2) + MIPS_SYS(sys_lstat64 , 2) + MIPS_SYS(sys_fstat64 , 2) /* 4215 */ + MIPS_SYS(sys_pivot_root , 2) + MIPS_SYS(sys_mincore , 3) + MIPS_SYS(sys_madvise , 3) + MIPS_SYS(sys_getdents64 , 3) + MIPS_SYS(sys_fcntl64 , 3) /* 4220 */ + MIPS_SYS(sys_ni_syscall , 0) + MIPS_SYS(sys_gettid , 0) + MIPS_SYS(sys_readahead , 5) + MIPS_SYS(sys_setxattr , 5) + MIPS_SYS(sys_lsetxattr , 5) /* 4225 */ + MIPS_SYS(sys_fsetxattr , 5) + MIPS_SYS(sys_getxattr , 4) + MIPS_SYS(sys_lgetxattr , 4) + MIPS_SYS(sys_fgetxattr , 4) + MIPS_SYS(sys_listxattr , 3) /* 4230 */ + MIPS_SYS(sys_llistxattr , 3) + MIPS_SYS(sys_flistxattr , 3) + MIPS_SYS(sys_removexattr , 2) + MIPS_SYS(sys_lremovexattr, 2) + MIPS_SYS(sys_fremovexattr, 2) /* 4235 */ + MIPS_SYS(sys_tkill , 2) + MIPS_SYS(sys_sendfile64 , 5) + MIPS_SYS(sys_futex , 6) + MIPS_SYS(sys_sched_setaffinity, 3) + MIPS_SYS(sys_sched_getaffinity, 3) /* 4240 */ + MIPS_SYS(sys_io_setup , 2) + MIPS_SYS(sys_io_destroy , 1) + MIPS_SYS(sys_io_getevents, 5) + MIPS_SYS(sys_io_submit , 3) + MIPS_SYS(sys_io_cancel , 3) /* 4245 */ + MIPS_SYS(sys_exit_group , 1) + MIPS_SYS(sys_lookup_dcookie, 3) + MIPS_SYS(sys_epoll_create, 1) + MIPS_SYS(sys_epoll_ctl , 4) + MIPS_SYS(sys_epoll_wait , 3) /* 4250 */ + MIPS_SYS(sys_remap_file_pages, 5) + MIPS_SYS(sys_set_tid_address, 1) + MIPS_SYS(sys_restart_syscall, 0) + MIPS_SYS(sys_fadvise64_64, 7) + MIPS_SYS(sys_statfs64 , 3) /* 4255 */ + MIPS_SYS(sys_fstatfs64 , 2) + MIPS_SYS(sys_timer_create, 3) + MIPS_SYS(sys_timer_settime, 4) + MIPS_SYS(sys_timer_gettime, 2) + MIPS_SYS(sys_timer_getoverrun, 1) /* 4260 */ + MIPS_SYS(sys_timer_delete, 1) + MIPS_SYS(sys_clock_settime, 2) + MIPS_SYS(sys_clock_gettime, 2) + MIPS_SYS(sys_clock_getres, 2) + MIPS_SYS(sys_clock_nanosleep, 4) /* 4265 */ + MIPS_SYS(sys_tgkill , 3) + MIPS_SYS(sys_utimes , 2) + MIPS_SYS(sys_mbind , 4) + MIPS_SYS(sys_ni_syscall , 0) /* sys_get_mempolicy */ + MIPS_SYS(sys_ni_syscall , 0) /* 4270 sys_set_mempolicy */ + MIPS_SYS(sys_mq_open , 4) + MIPS_SYS(sys_mq_unlink , 1) + MIPS_SYS(sys_mq_timedsend, 5) + MIPS_SYS(sys_mq_timedreceive, 5) + MIPS_SYS(sys_mq_notify , 2) /* 4275 */ + MIPS_SYS(sys_mq_getsetattr, 3) + MIPS_SYS(sys_ni_syscall , 0) /* sys_vserver */ + MIPS_SYS(sys_waitid , 4) + MIPS_SYS(sys_ni_syscall , 0) /* available, was setaltroot */ + MIPS_SYS(sys_add_key , 5) + MIPS_SYS(sys_request_key, 4) + MIPS_SYS(sys_keyctl , 5) + MIPS_SYS(sys_set_thread_area, 1) + MIPS_SYS(sys_inotify_init, 0) + MIPS_SYS(sys_inotify_add_watch, 3) /* 4285 */ + MIPS_SYS(sys_inotify_rm_watch, 2) + MIPS_SYS(sys_migrate_pages, 4) + MIPS_SYS(sys_openat, 4) + MIPS_SYS(sys_mkdirat, 3) + MIPS_SYS(sys_mknodat, 4) /* 4290 */ + MIPS_SYS(sys_fchownat, 5) + MIPS_SYS(sys_futimesat, 3) + MIPS_SYS(sys_fstatat64, 4) + MIPS_SYS(sys_unlinkat, 3) + MIPS_SYS(sys_renameat, 4) /* 4295 */ + MIPS_SYS(sys_linkat, 5) + MIPS_SYS(sys_symlinkat, 3) + MIPS_SYS(sys_readlinkat, 4) + MIPS_SYS(sys_fchmodat, 3) + MIPS_SYS(sys_faccessat, 3) /* 4300 */ + MIPS_SYS(sys_pselect6, 6) + MIPS_SYS(sys_ppoll, 5) + MIPS_SYS(sys_unshare, 1) + MIPS_SYS(sys_splice, 6) + MIPS_SYS(sys_sync_file_range, 7) /* 4305 */ + MIPS_SYS(sys_tee, 4) + MIPS_SYS(sys_vmsplice, 4) + MIPS_SYS(sys_move_pages, 6) + MIPS_SYS(sys_set_robust_list, 2) + MIPS_SYS(sys_get_robust_list, 3) /* 4310 */ + MIPS_SYS(sys_kexec_load, 4) + MIPS_SYS(sys_getcpu, 3) + MIPS_SYS(sys_epoll_pwait, 6) + MIPS_SYS(sys_ioprio_set, 3) + MIPS_SYS(sys_ioprio_get, 2) + MIPS_SYS(sys_utimensat, 4) + MIPS_SYS(sys_signalfd, 3) + MIPS_SYS(sys_ni_syscall, 0) /* was timerfd */ + MIPS_SYS(sys_eventfd, 1) + MIPS_SYS(sys_fallocate, 6) /* 4320 */ + MIPS_SYS(sys_timerfd_create, 2) + MIPS_SYS(sys_timerfd_gettime, 2) + MIPS_SYS(sys_timerfd_settime, 4) + MIPS_SYS(sys_signalfd4, 4) + MIPS_SYS(sys_eventfd2, 2) /* 4325 */ + MIPS_SYS(sys_epoll_create1, 1) + MIPS_SYS(sys_dup3, 3) + MIPS_SYS(sys_pipe2, 2) + MIPS_SYS(sys_inotify_init1, 1) + MIPS_SYS(sys_preadv, 5) /* 4330 */ + MIPS_SYS(sys_pwritev, 5) + MIPS_SYS(sys_rt_tgsigqueueinfo, 4) + MIPS_SYS(sys_perf_event_open, 5) + MIPS_SYS(sys_accept4, 4) + MIPS_SYS(sys_recvmmsg, 5) /* 4335 */ + MIPS_SYS(sys_fanotify_init, 2) + MIPS_SYS(sys_fanotify_mark, 6) + MIPS_SYS(sys_prlimit64, 4) + MIPS_SYS(sys_name_to_handle_at, 5) + MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */ + MIPS_SYS(sys_clock_adjtime, 2) + MIPS_SYS(sys_syncfs, 1) + MIPS_SYS(sys_sendmmsg, 4) + MIPS_SYS(sys_setns, 2) + MIPS_SYS(sys_process_vm_readv, 6) /* 345 */ + MIPS_SYS(sys_process_vm_writev, 6) + MIPS_SYS(sys_kcmp, 5) + MIPS_SYS(sys_finit_module, 3) + MIPS_SYS(sys_sched_setattr, 2) + MIPS_SYS(sys_sched_getattr, 3) /* 350 */ + MIPS_SYS(sys_renameat2, 5) + MIPS_SYS(sys_seccomp, 3) + MIPS_SYS(sys_getrandom, 3) + MIPS_SYS(sys_memfd_create, 2) + MIPS_SYS(sys_bpf, 3) /* 355 */ + MIPS_SYS(sys_execveat, 5) + MIPS_SYS(sys_userfaultfd, 1) + MIPS_SYS(sys_membarrier, 2) + MIPS_SYS(sys_mlock2, 3) + MIPS_SYS(sys_copy_file_range, 6) /* 360 */ + MIPS_SYS(sys_preadv2, 6) + MIPS_SYS(sys_pwritev2, 6) +}; +# undef MIPS_SYS +# endif /* O32 */ + +static int do_store_exclusive(CPUMIPSState *env) +{ + target_ulong addr; + target_ulong page_addr; + target_ulong val; + int flags; + int segv = 0; + int reg; + int d; + + addr = env->lladdr; + page_addr = addr & TARGET_PAGE_MASK; + start_exclusive(); + mmap_lock(); + flags = page_get_flags(page_addr); + if ((flags & PAGE_READ) == 0) { + segv = 1; + } else { + reg = env->llreg & 0x1f; + d = (env->llreg & 0x20) != 0; + if (d) { + segv = get_user_s64(val, addr); + } else { + segv = get_user_s32(val, addr); + } + if (!segv) { + if (val != env->llval) { + env->active_tc.gpr[reg] = 0; + } else { + if (d) { + segv = put_user_u64(env->llnewval, addr); + } else { + segv = put_user_u32(env->llnewval, addr); + } + if (!segv) { + env->active_tc.gpr[reg] = 1; + } + } + } + } + env->lladdr = -1; + if (!segv) { + env->active_tc.PC += 4; + } + mmap_unlock(); + end_exclusive(); + return segv; +} + +/* Break codes */ +enum { + BRK_OVERFLOW = 6, + BRK_DIVZERO = 7 +}; + +static int do_break(CPUMIPSState *env, target_siginfo_t *info, + unsigned int code) +{ + int ret = -1; + + switch (code) { + case BRK_OVERFLOW: + case BRK_DIVZERO: + info->si_signo = TARGET_SIGFPE; + info->si_errno = 0; + info->si_code = (code == BRK_OVERFLOW) ? FPE_INTOVF : FPE_INTDIV; + queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); + ret = 0; + break; + default: + info->si_signo = TARGET_SIGTRAP; + info->si_errno = 0; + queue_signal(env, info->si_signo, QEMU_SI_FAULT, &*info); + ret = 0; + break; + } + + return ret; +} + +void cpu_loop(CPUMIPSState *env) +{ + CPUState *cs = CPU(mips_env_get_cpu(env)); + target_siginfo_t info; + int trapnr; + abi_long ret; +# ifdef TARGET_ABI_MIPSO32 + unsigned int syscall_num; +# endif + + for(;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch(trapnr) { + case EXCP_SYSCALL: + env->active_tc.PC += 4; +# ifdef TARGET_ABI_MIPSO32 + syscall_num = env->active_tc.gpr[2] - 4000; + if (syscall_num >= sizeof(mips_syscall_args)) { + ret = -TARGET_ENOSYS; + } else { + int nb_args; + abi_ulong sp_reg; + abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0; + + nb_args = mips_syscall_args[syscall_num]; + sp_reg = env->active_tc.gpr[29]; + switch (nb_args) { + /* these arguments are taken from the stack */ + case 8: + if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) { + goto done_syscall; + } + case 7: + if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) { + goto done_syscall; + } + case 6: + if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) { + goto done_syscall; + } + case 5: + if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) { + goto done_syscall; + } + default: + break; + } + ret = do_syscall(env, env->active_tc.gpr[2], + env->active_tc.gpr[4], + env->active_tc.gpr[5], + env->active_tc.gpr[6], + env->active_tc.gpr[7], + arg5, arg6, arg7, arg8); + } +done_syscall: +# else + ret = do_syscall(env, env->active_tc.gpr[2], + env->active_tc.gpr[4], env->active_tc.gpr[5], + env->active_tc.gpr[6], env->active_tc.gpr[7], + env->active_tc.gpr[8], env->active_tc.gpr[9], + env->active_tc.gpr[10], env->active_tc.gpr[11]); +# endif /* O32 */ + if (ret == -TARGET_ERESTARTSYS) { + env->active_tc.PC -= 4; + break; + } + if (ret == -TARGET_QEMU_ESIGRETURN) { + /* Returning from a successful sigreturn syscall. + Avoid clobbering register state. */ + break; + } + if ((abi_ulong)ret >= (abi_ulong)-1133) { + env->active_tc.gpr[7] = 1; /* error flag */ + ret = -ret; + } else { + env->active_tc.gpr[7] = 0; /* error flag */ + } + env->active_tc.gpr[2] = ret; + break; + case EXCP_TLBL: + case EXCP_TLBS: + case EXCP_AdEL: + case EXCP_AdES: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->CP0_BadVAddr; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_CpU: + case EXCP_RI: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = 0; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_SC: + if (do_store_exclusive(env)) { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->active_tc.PC; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_DSPDIS: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + /* The code below was inspired by the MIPS Linux kernel trap + * handling code in arch/mips/kernel/traps.c. + */ + case EXCP_BREAK: + { + abi_ulong trap_instr; + unsigned int code; + + if (env->hflags & MIPS_HFLAG_M16) { + if (env->insn_flags & ASE_MICROMIPS) { + /* microMIPS mode */ + ret = get_user_u16(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } + + if ((trap_instr >> 10) == 0x11) { + /* 16-bit instruction */ + code = trap_instr & 0xf; + } else { + /* 32-bit instruction */ + abi_ulong instr_lo; + + ret = get_user_u16(instr_lo, + env->active_tc.PC + 2); + if (ret != 0) { + goto error; + } + trap_instr = (trap_instr << 16) | instr_lo; + code = ((trap_instr >> 6) & ((1 << 20) - 1)); + /* Unfortunately, microMIPS also suffers from + the old assembler bug... */ + if (code >= (1 << 10)) { + code >>= 10; + } + } + } else { + /* MIPS16e mode */ + ret = get_user_u16(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } + code = (trap_instr >> 6) & 0x3f; + } + } else { + ret = get_user_u32(trap_instr, env->active_tc.PC); + if (ret != 0) { + goto error; + } + + /* As described in the original Linux kernel code, the + * below checks on 'code' are to work around an old + * assembly bug. + */ + code = ((trap_instr >> 6) & ((1 << 20) - 1)); + if (code >= (1 << 10)) { + code >>= 10; + } + } + + if (do_break(env, &info, code) != 0) { + goto error; + } + } + break; + case EXCP_TRAP: + { + abi_ulong trap_instr; + unsigned int code = 0; + + if (env->hflags & MIPS_HFLAG_M16) { + /* microMIPS mode */ + abi_ulong instr[2]; + + ret = get_user_u16(instr[0], env->active_tc.PC) || + get_user_u16(instr[1], env->active_tc.PC + 2); + + trap_instr = (instr[0] << 16) | instr[1]; + } else { + ret = get_user_u32(trap_instr, env->active_tc.PC); + } + + if (ret != 0) { + goto error; + } + + /* The immediate versions don't provide a code. */ + if (!(trap_instr & 0xFC000000)) { + if (env->hflags & MIPS_HFLAG_M16) { + /* microMIPS mode */ + code = ((trap_instr >> 12) & ((1 << 4) - 1)); + } else { + code = ((trap_instr >> 6) & ((1 << 10) - 1)); + } + } + + if (do_break(env, &info, code) != 0) { + goto error; + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: +error: + EXCP_DUMP(env, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr); + abort(); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + int i; + + for(i = 0; i < 32; i++) { + env->active_tc.gpr[i] = regs->regs[i]; + } + env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; + if (regs->cp0_epc & 1) { + env->hflags |= MIPS_HFLAG_M16; + } + if (((info->elf_flags & EF_MIPS_NAN2008) != 0) != + ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) { + if ((env->active_fpu.fcr31_rw_bitmask & + (1 << FCR31_NAN2008)) == 0) { + fprintf(stderr, "ELF binary's NaN mode not supported by CPU\n"); + exit(1); + } + if ((info->elf_flags & EF_MIPS_NAN2008) != 0) { + env->active_fpu.fcr31 |= (1 << FCR31_NAN2008); + } else { + env->active_fpu.fcr31 &= ~(1 << FCR31_NAN2008); + } + restore_snan_bit_mode(env); + } +} diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c new file mode 100644 index 0000000000..6aa303ec9c --- /dev/null +++ b/linux-user/mips/signal.c @@ -0,0 +1,388 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +# if defined(TARGET_ABI_MIPSO32) +struct target_sigcontext { + uint32_t sc_regmask; /* Unused */ + uint32_t sc_status; + uint64_t sc_pc; + uint64_t sc_regs[32]; + uint64_t sc_fpregs[32]; + uint32_t sc_ownedfp; /* Unused */ + uint32_t sc_fpc_csr; + uint32_t sc_fpc_eir; /* Unused */ + uint32_t sc_used_math; + uint32_t sc_dsp; /* dsp status, was sc_ssflags */ + uint32_t pad0; + uint64_t sc_mdhi; + uint64_t sc_mdlo; + target_ulong sc_hi1; /* Was sc_cause */ + target_ulong sc_lo1; /* Was sc_badvaddr */ + target_ulong sc_hi2; /* Was sc_sigset[4] */ + target_ulong sc_lo2; + target_ulong sc_hi3; + target_ulong sc_lo3; +}; +# else /* N32 || N64 */ +struct target_sigcontext { + uint64_t sc_regs[32]; + uint64_t sc_fpregs[32]; + uint64_t sc_mdhi; + uint64_t sc_hi1; + uint64_t sc_hi2; + uint64_t sc_hi3; + uint64_t sc_mdlo; + uint64_t sc_lo1; + uint64_t sc_lo2; + uint64_t sc_lo3; + uint64_t sc_pc; + uint32_t sc_fpc_csr; + uint32_t sc_used_math; + uint32_t sc_dsp; + uint32_t sc_reserved; +}; +# endif /* O32 */ + +struct sigframe { + uint32_t sf_ass[4]; /* argument save space for o32 */ + uint32_t sf_code[2]; /* signal trampoline */ + struct target_sigcontext sf_sc; + target_sigset_t sf_mask; +}; + +struct target_ucontext { + target_ulong tuc_flags; + target_ulong tuc_link; + target_stack_t tuc_stack; + target_ulong pad0; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe { + uint32_t rs_ass[4]; /* argument save space for o32 */ + uint32_t rs_code[2]; /* signal trampoline */ + struct target_siginfo rs_info; + struct target_ucontext rs_uc; +}; + +/* Install trampoline to jump back from signal handler */ +static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) +{ + int err = 0; + + /* + * Set up the return code ... + * + * li v0, __NR__foo_sigreturn + * syscall + */ + + __put_user(0x24020000 + syscall, tramp + 0); + __put_user(0x0000000c , tramp + 1); + return err; +} + +static inline void setup_sigcontext(CPUMIPSState *regs, + struct target_sigcontext *sc) +{ + int i; + + __put_user(exception_resume_pc(regs), &sc->sc_pc); + regs->hflags &= ~MIPS_HFLAG_BMASK; + + __put_user(0, &sc->sc_regs[0]); + for (i = 1; i < 32; ++i) { + __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); + } + + __put_user(regs->active_tc.HI[0], &sc->sc_mdhi); + __put_user(regs->active_tc.LO[0], &sc->sc_mdlo); + + /* Rather than checking for dsp existence, always copy. The storage + would just be garbage otherwise. */ + __put_user(regs->active_tc.HI[1], &sc->sc_hi1); + __put_user(regs->active_tc.HI[2], &sc->sc_hi2); + __put_user(regs->active_tc.HI[3], &sc->sc_hi3); + __put_user(regs->active_tc.LO[1], &sc->sc_lo1); + __put_user(regs->active_tc.LO[2], &sc->sc_lo2); + __put_user(regs->active_tc.LO[3], &sc->sc_lo3); + { + uint32_t dsp = cpu_rddsp(0x3ff, regs); + __put_user(dsp, &sc->sc_dsp); + } + + __put_user(1, &sc->sc_used_math); + + for (i = 0; i < 32; ++i) { + __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); + } +} + +static inline void +restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc) +{ + int i; + + __get_user(regs->CP0_EPC, &sc->sc_pc); + + __get_user(regs->active_tc.HI[0], &sc->sc_mdhi); + __get_user(regs->active_tc.LO[0], &sc->sc_mdlo); + + for (i = 1; i < 32; ++i) { + __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); + } + + __get_user(regs->active_tc.HI[1], &sc->sc_hi1); + __get_user(regs->active_tc.HI[2], &sc->sc_hi2); + __get_user(regs->active_tc.HI[3], &sc->sc_hi3); + __get_user(regs->active_tc.LO[1], &sc->sc_lo1); + __get_user(regs->active_tc.LO[2], &sc->sc_lo2); + __get_user(regs->active_tc.LO[3], &sc->sc_lo3); + { + uint32_t dsp; + __get_user(dsp, &sc->sc_dsp); + cpu_wrdsp(dsp, 0x3ff, regs); + } + + for (i = 0; i < 32; ++i) { + __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); + } +} + +/* + * Determine which stack to use.. + */ +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size) +{ + unsigned long sp; + + /* + * FPU emulator may have its own trampoline active just + * above the user stack, 16-bytes before the next lowest + * 16 byte boundary. Try to avoid trashing it. + */ + sp = target_sigsp(get_sp_from_cpustate(regs) - 32, ka); + + return (sp - frame_size) & ~7; +} + +static void mips_set_hflags_isa_mode_from_pc(CPUMIPSState *env) +{ + if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { + env->hflags &= ~MIPS_HFLAG_M16; + env->hflags |= (env->active_tc.PC & 1) << MIPS_HFLAG_M16_SHIFT; + env->active_tc.PC &= ~(target_ulong) 1; + } +} + +# if defined(TARGET_ABI_MIPSO32) +/* compare linux/arch/mips/kernel/signal.c:setup_frame() */ +void setup_frame(int sig, struct target_sigaction * ka, + target_sigset_t *set, CPUMIPSState *regs) +{ + struct sigframe *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(ka, regs, sizeof(*frame)); + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + install_sigtramp(frame->sf_code, TARGET_NR_sigreturn); + + setup_sigcontext(regs, &frame->sf_sc); + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->sf_mask.sig[i]); + } + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = 0 (should be cause) + * a2 = pointer to struct sigcontext + * + * $25 and PC point to the signal handler, $29 points to the + * struct sigframe. + */ + regs->active_tc.gpr[ 4] = sig; + regs->active_tc.gpr[ 5] = 0; + regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc); + regs->active_tc.gpr[29] = frame_addr; + regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code); + /* The original kernel code sets CP0_EPC to the handler + * since it returns to userland using eret + * we cannot do this here, and we must set PC directly */ + regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler; + mips_set_hflags_isa_mode_from_pc(regs); + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +long do_sigreturn(CPUMIPSState *regs) +{ + struct sigframe *frame; + abi_ulong frame_addr; + sigset_t blocked; + target_sigset_t target_set; + int i; + + frame_addr = regs->active_tc.gpr[29]; + trace_user_do_sigreturn(regs, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) + goto badframe; + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->sf_mask.sig[i]); + } + + target_to_host_sigset_internal(&blocked, &target_set); + set_sigmask(&blocked); + + restore_sigcontext(regs, &frame->sf_sc); + +#if 0 + /* + * Don't let your children do this ... + */ + __asm__ __volatile__( + "move\t$29, %0\n\t" + "j\tsyscall_exit" + :/* no outputs */ + :"r" (®s)); + /* Unreached */ +#endif + + regs->active_tc.PC = regs->CP0_EPC; + mips_set_hflags_isa_mode_from_pc(regs); + /* I am not sure this is right, but it seems to work + * maybe a problem with nested signals ? */ + regs->CP0_EPC = 0; + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} +# endif /* O32 */ + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUMIPSState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn); + + tswap_siginfo(&frame->rs_info, info); + + __put_user(0, &frame->rs_uc.tuc_flags); + __put_user(0, &frame->rs_uc.tuc_link); + target_save_altstack(&frame->rs_uc.tuc_stack, env); + + setup_sigcontext(env, &frame->rs_uc.tuc_mcontext); + + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]); + } + + /* + * Arguments to signal handler: + * + * a0 = signal number + * a1 = pointer to siginfo_t + * a2 = pointer to ucontext_t + * + * $25 and PC point to the signal handler, $29 points to the + * struct sigframe. + */ + env->active_tc.gpr[ 4] = sig; + env->active_tc.gpr[ 5] = frame_addr + + offsetof(struct target_rt_sigframe, rs_info); + env->active_tc.gpr[ 6] = frame_addr + + offsetof(struct target_rt_sigframe, rs_uc); + env->active_tc.gpr[29] = frame_addr; + env->active_tc.gpr[31] = frame_addr + + offsetof(struct target_rt_sigframe, rs_code); + /* The original kernel code sets CP0_EPC to the handler + * since it returns to userland using eret + * we cannot do this here, and we must set PC directly */ + env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler; + mips_set_hflags_isa_mode_from_pc(env); + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +long do_rt_sigreturn(CPUMIPSState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + sigset_t blocked; + + frame_addr = env->active_tc.gpr[29]; + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); + set_sigmask(&blocked); + + restore_sigcontext(env, &frame->rs_uc.tuc_mcontext); + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, rs_uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -EFAULT) + goto badframe; + + env->active_tc.PC = env->CP0_EPC; + mips_set_hflags_isa_mode_from_pc(env); + /* I am not sure this is right, but it seems to work + * maybe a problem with nested signals ? */ + env->CP0_EPC = 0; + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/mips/sockbits.h b/linux-user/mips/sockbits.h new file mode 100644 index 0000000000..0f022cd598 --- /dev/null +++ b/linux-user/mips/sockbits.h @@ -0,0 +1,110 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef MIPS_SOCKBITS_H +#define MIPS_SOCKBITS_H +/* MIPS special values for constants */ + +/* + * For setsockopt(2) + * + * This defines are ABI conformant as far as Linux supports these ... + */ +#define TARGET_SOL_SOCKET 0xffff + +#define TARGET_SO_DEBUG 0x0001 /* Record debugging information. */ +#define TARGET_SO_REUSEADDR 0x0004 /* Allow reuse of local addresses. */ +#define TARGET_SO_KEEPALIVE 0x0008 /* Keep connections alive and send + SIGPIPE when they die. */ +#define TARGET_SO_DONTROUTE 0x0010 /* Don't do local routing. */ +#define TARGET_SO_BROADCAST 0x0020 /* Allow transmission of + broadcast messages. */ +#define TARGET_SO_LINGER 0x0080 /* Block on close of a reliable + * socket to transmit pending data. + */ +#define TARGET_SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. + */ +#define TARGET_SO_REUSEPORT 0x0200 + +#define TARGET_SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */ +#define TARGET_SO_STYLE SO_TYPE /* Synonym */ +#define TARGET_SO_ERROR 0x1007 /* get error status and clear */ +#define TARGET_SO_SNDBUF 0x1001 /* Send buffer size. */ +#define TARGET_SO_RCVBUF 0x1002 /* Receive buffer. */ +#define TARGET_SO_SNDLOWAT 0x1003 /* send low-water mark */ +#define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */ +#define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */ +#define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */ +#define TARGET_SO_ACCEPTCONN 0x1009 + +/* linux-specific, might as well be the same as on i386 */ +#define TARGET_SO_NO_CHECK 11 +#define TARGET_SO_PRIORITY 12 +#define TARGET_SO_BSDCOMPAT 14 + +#define TARGET_SO_PASSCRED 17 +#define TARGET_SO_PEERCRED 18 + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 22 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 + +#define TARGET_SO_BINDTODEVICE 25 + +/* Socket filtering */ +#define TARGET_SO_ATTACH_FILTER 26 +#define TARGET_SO_DETACH_FILTER 27 + +#define TARGET_SO_PEERNAME 28 +#define TARGET_SO_TIMESTAMP 29 +#define SCM_TIMESTAMP SO_TIMESTAMP + +#define TARGET_SO_PEERSEC 30 +#define TARGET_SO_SNDBUFFORCE 31 +#define TARGET_SO_RCVBUFFORCE 33 +#define TARGET_SO_PASSSEC 34 + +/** sock_type - Socket types + * + * Please notice that for binary compat reasons MIPS has to + * override the enum sock_type in include/linux/net.h, so + * we define ARCH_HAS_SOCKET_TYPES here. + * + * @SOCK_DGRAM - datagram (conn.less) socket + * @SOCK_STREAM - stream (connection) socket + * @SOCK_RAW - raw socket + * @SOCK_RDM - reliably-delivered message + * @SOCK_SEQPACKET - sequential packet socket + * @SOCK_DCCP - Datagram Congestion Control Protocol socket + * @SOCK_PACKET - linux specific way of getting packets at the dev level. + * For writing rarp and other similar things on the user + * level. + * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. + * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. + */ + +#define TARGET_ARCH_HAS_SOCKET_TYPES 1 + +enum sock_type { + TARGET_SOCK_DGRAM = 1, + TARGET_SOCK_STREAM = 2, + TARGET_SOCK_RAW = 3, + TARGET_SOCK_RDM = 4, + TARGET_SOCK_SEQPACKET = 5, + TARGET_SOCK_DCCP = 6, + TARGET_SOCK_PACKET = 10, +}; + +#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) +#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ + +/* Flags for socket, socketpair, paccept */ +#define TARGET_SOCK_CLOEXEC TARGET_O_CLOEXEC +#define TARGET_SOCK_NONBLOCK TARGET_O_NONBLOCK + +#endif diff --git a/linux-user/mips/target_cpu.h b/linux-user/mips/target_cpu.h index 2002920312..02cf5eeff7 100644 --- a/linux-user/mips/target_cpu.h +++ b/linux-user/mips/target_cpu.h @@ -33,4 +33,8 @@ static inline void cpu_set_tls(CPUMIPSState *env, target_ulong newtls) env->active_tc.CP0_UserLocal = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) +{ + return state->active_tc.gpr[29]; +} #endif diff --git a/linux-user/mips/target_fcntl.h b/linux-user/mips/target_fcntl.h new file mode 100644 index 0000000000..000527cc95 --- /dev/null +++ b/linux-user/mips/target_fcntl.h @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef MIPS_TARGET_FCNTL_H +#define MIPS_TARGET_FCNTL_H + +#define TARGET_O_APPEND 0x0008 +#define TARGET_O_DSYNC 0x0010 +#define TARGET_O_NONBLOCK 0x0080 +#define TARGET_O_CREAT 0x0100 /* not fcntl */ +#define TARGET_O_TRUNC 0x0200 /* not fcntl */ +#define TARGET_O_EXCL 0x0400 /* not fcntl */ +#define TARGET_O_NOCTTY 0x0800 /* not fcntl */ +#define TARGET_FASYNC 0x1000 /* fcntl, for BSD compatibility */ +#define TARGET_O_LARGEFILE 0x2000 /* allow large file opens */ +#define TARGET___O_SYNC 0x4000 +#define TARGET_O_DIRECT 0x8000 /* direct disk access hint */ + +#define TARGET_F_GETLK 14 +#define TARGET_F_SETLK 6 +#define TARGET_F_SETLKW 7 + +#define TARGET_F_SETOWN 24 /* for sockets. */ +#define TARGET_F_GETOWN 23 /* for sockets. */ + +#define TARGET_ARCH_FLOCK_PAD abi_long pad[4]; +#define TARGET_ARCH_FLOCK64_PAD + +#define TARGET_F_GETLK64 33 /* using 'struct flock64' */ +#define TARGET_F_SETLK64 34 +#define TARGET_F_SETLKW64 35 + +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h index 8dd27cef35..66e1ad44a6 100644 --- a/linux-user/mips/target_signal.h +++ b/linux-user/mips/target_signal.h @@ -1,7 +1,46 @@ #ifndef MIPS_TARGET_SIGNAL_H #define MIPS_TARGET_SIGNAL_H -#include "cpu.h" +#define TARGET_SIGHUP 1 /* Hangup (POSIX). */ +#define TARGET_SIGINT 2 /* Interrupt (ANSI). */ +#define TARGET_SIGQUIT 3 /* Quit (POSIX). */ +#define TARGET_SIGILL 4 /* Illegal instruction (ANSI). */ +#define TARGET_SIGTRAP 5 /* Trace trap (POSIX). */ +#define TARGET_SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define TARGET_SIGABRT TARGET_SIGIOT /* Abort (ANSI). */ +#define TARGET_SIGEMT 7 +#define TARGET_SIGSTKFLT 7 /* XXX: incorrect */ +#define TARGET_SIGFPE 8 /* Floating-point exception (ANSI). */ +#define TARGET_SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define TARGET_SIGBUS 10 /* BUS error (4.2 BSD). */ +#define TARGET_SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define TARGET_SIGSYS 12 +#define TARGET_SIGPIPE 13 /* Broken pipe (POSIX). */ +#define TARGET_SIGALRM 14 /* Alarm clock (POSIX). */ +#define TARGET_SIGTERM 15 /* Termination (ANSI). */ +#define TARGET_SIGUSR1 16 /* User-defined signal 1 (POSIX). */ +#define TARGET_SIGUSR2 17 /* User-defined signal 2 (POSIX). */ +#define TARGET_SIGCHLD 18 /* Child status has changed (POSIX). */ +#define TARGET_SIGCLD TARGET_SIGCHLD /* Same as TARGET_SIGCHLD (System V). */ +#define TARGET_SIGPWR 19 /* Power failure restart (System V). */ +#define TARGET_SIGWINCH 20 /* Window size change (4.3 BSD, Sun). */ +#define TARGET_SIGURG 21 /* Urgent condition on socket (4.2 BSD). */ +#define TARGET_SIGIO 22 /* I/O now possible (4.2 BSD). */ +#define TARGET_SIGPOLL TARGET_SIGIO /* Pollable event occurred (System V). */ +#define TARGET_SIGSTOP 23 /* Stop, unblockable (POSIX). */ +#define TARGET_SIGTSTP 24 /* Keyboard stop (POSIX). */ +#define TARGET_SIGCONT 25 /* Continue (POSIX). */ +#define TARGET_SIGTTIN 26 /* Background read from tty (POSIX). */ +#define TARGET_SIGTTOU 27 /* Background write to tty (POSIX). */ +#define TARGET_SIGVTALRM 28 /* Virtual alarm clock (4.2 BSD). */ +#define TARGET_SIGPROF 29 /* Profiling alarm clock (4.2 BSD). */ +#define TARGET_SIGXCPU 30 /* CPU limit exceeded (4.2 BSD). */ +#define TARGET_SIGXFSZ 31 /* File size limit exceeded (4.2 BSD). */ +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 1 /* for blocking signals */ +#define TARGET_SIG_UNBLOCK 2 /* for unblocking signals */ +#define TARGET_SIG_SETMASK 3 /* for setting the signal mask */ /* this struct defines a stack used during syscall handling */ @@ -18,13 +57,20 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00010000 +#define TARGET_SA_SIGINFO 0x00000008 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_RESETHAND 0x80000000 +#define TARGET_SA_RESTORER 0x04000000 /* Only for O32 */ + #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) -{ - return state->active_tc.gpr[29]; -} - - +#if defined(TARGET_ABI_MIPSO32) +/* compare linux/arch/mips/kernel/signal.c:setup_frame() */ +#define TARGET_ARCH_HAS_SETUP_FRAME +#endif #endif /* MIPS_TARGET_SIGNAL_H */ diff --git a/linux-user/mips64/cpu_loop.c b/linux-user/mips64/cpu_loop.c new file mode 100644 index 0000000000..858bc5be78 --- /dev/null +++ b/linux-user/mips64/cpu_loop.c @@ -0,0 +1,20 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "../mips/cpu_loop.c" diff --git a/linux-user/mips64/signal.c b/linux-user/mips64/signal.c new file mode 100644 index 0000000000..4ed0ed90b3 --- /dev/null +++ b/linux-user/mips64/signal.c @@ -0,0 +1,20 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#define MIPS_TARGET_SIGNAL_H /* to only include mips64/target_signal.h */ +#include "../mips/signal.c" diff --git a/linux-user/mips64/sockbits.h b/linux-user/mips64/sockbits.h new file mode 100644 index 0000000000..e6b6d31ac9 --- /dev/null +++ b/linux-user/mips64/sockbits.h @@ -0,0 +1 @@ +#include "../mips/sockbits.h" diff --git a/linux-user/mips64/target_fcntl.h b/linux-user/mips64/target_fcntl.h new file mode 100644 index 0000000000..a511bc0e6c --- /dev/null +++ b/linux-user/mips64/target_fcntl.h @@ -0,0 +1 @@ +#include "../mips/target_fcntl.h" diff --git a/linux-user/mips64/target_signal.h b/linux-user/mips64/target_signal.h index 67ef5a18f4..753e91fbd6 100644 --- a/linux-user/mips64/target_signal.h +++ b/linux-user/mips64/target_signal.h @@ -1,7 +1,46 @@ #ifndef MIPS64_TARGET_SIGNAL_H #define MIPS64_TARGET_SIGNAL_H -#include "cpu.h" +#define TARGET_SIGHUP 1 /* Hangup (POSIX). */ +#define TARGET_SIGINT 2 /* Interrupt (ANSI). */ +#define TARGET_SIGQUIT 3 /* Quit (POSIX). */ +#define TARGET_SIGILL 4 /* Illegal instruction (ANSI). */ +#define TARGET_SIGTRAP 5 /* Trace trap (POSIX). */ +#define TARGET_SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define TARGET_SIGABRT TARGET_SIGIOT /* Abort (ANSI). */ +#define TARGET_SIGEMT 7 +#define TARGET_SIGSTKFLT 7 /* XXX: incorrect */ +#define TARGET_SIGFPE 8 /* Floating-point exception (ANSI). */ +#define TARGET_SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define TARGET_SIGBUS 10 /* BUS error (4.2 BSD). */ +#define TARGET_SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define TARGET_SIGSYS 12 +#define TARGET_SIGPIPE 13 /* Broken pipe (POSIX). */ +#define TARGET_SIGALRM 14 /* Alarm clock (POSIX). */ +#define TARGET_SIGTERM 15 /* Termination (ANSI). */ +#define TARGET_SIGUSR1 16 /* User-defined signal 1 (POSIX). */ +#define TARGET_SIGUSR2 17 /* User-defined signal 2 (POSIX). */ +#define TARGET_SIGCHLD 18 /* Child status has changed (POSIX). */ +#define TARGET_SIGCLD TARGET_SIGCHLD /* Same as TARGET_SIGCHLD (System V). */ +#define TARGET_SIGPWR 19 /* Power failure restart (System V). */ +#define TARGET_SIGWINCH 20 /* Window size change (4.3 BSD, Sun). */ +#define TARGET_SIGURG 21 /* Urgent condition on socket (4.2 BSD). */ +#define TARGET_SIGIO 22 /* I/O now possible (4.2 BSD). */ +#define TARGET_SIGPOLL TARGET_SIGIO /* Pollable event occurred (System V). */ +#define TARGET_SIGSTOP 23 /* Stop, unblockable (POSIX). */ +#define TARGET_SIGTSTP 24 /* Keyboard stop (POSIX). */ +#define TARGET_SIGCONT 25 /* Continue (POSIX). */ +#define TARGET_SIGTTIN 26 /* Background read from tty (POSIX). */ +#define TARGET_SIGTTOU 27 /* Background write to tty (POSIX). */ +#define TARGET_SIGVTALRM 28 /* Virtual alarm clock (4.2 BSD). */ +#define TARGET_SIGPROF 29 /* Profiling alarm clock (4.2 BSD). */ +#define TARGET_SIGXCPU 30 /* CPU limit exceeded (4.2 BSD). */ +#define TARGET_SIGXFSZ 31 /* File size limit exceeded (4.2 BSD). */ +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 1 /* for blocking signals */ +#define TARGET_SIG_UNBLOCK 2 /* for unblocking signals */ +#define TARGET_SIG_SETMASK 3 /* for setting the signal mask */ /* this struct defines a stack used during syscall handling */ @@ -18,13 +57,15 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00010000 +#define TARGET_SA_SIGINFO 0x00000008 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_RESETHAND 0x80000000 + #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUMIPSState *state) -{ - return state->active_tc.gpr[29]; -} - - #endif /* MIPS64_TARGET_SIGNAL_H */ diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 9168a2051c..41e0983ce8 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -20,7 +20,6 @@ #include "qemu.h" #include "qemu-common.h" -#include "translate-all.h" //#define DEBUG_MMAP @@ -392,14 +391,23 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, } #endif + if (!len) { + errno = EINVAL; + goto fail; + } + + /* Also check for overflows... */ + len = TARGET_PAGE_ALIGN(len); + if (!len) { + errno = ENOMEM; + goto fail; + } + if (offset & ~TARGET_PAGE_MASK) { errno = EINVAL; goto fail; } - len = TARGET_PAGE_ALIGN(len); - if (len == 0) - goto the_end; real_start = start & qemu_host_page_mask; host_offset = offset & qemu_host_page_mask; diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c new file mode 100644 index 0000000000..dac7a06181 --- /dev/null +++ b/linux-user/nios2/cpu_loop.c @@ -0,0 +1,152 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void cpu_loop(CPUNios2State *env) +{ + CPUState *cs = ENV_GET_CPU(env); + Nios2CPU *cpu = NIOS2_CPU(cs); + target_siginfo_t info; + int trapnr, gdbsig, ret; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + gdbsig = 0; + + switch (trapnr) { + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_TRAP: + if (env->regs[R_AT] == 0) { + abi_long ret; + qemu_log_mask(CPU_LOG_INT, "\nSyscall\n"); + + ret = do_syscall(env, env->regs[2], + env->regs[4], env->regs[5], env->regs[6], + env->regs[7], env->regs[8], env->regs[9], + 0, 0); + + if (env->regs[2] == 0) { /* FIXME: syscall 0 workaround */ + ret = 0; + } + + env->regs[2] = abs(ret); + /* Return value is 0..4096 */ + env->regs[7] = (ret > 0xfffffffffffff000ULL); + env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; + env->regs[CR_STATUS] &= ~0x3; + env->regs[R_EA] = env->regs[R_PC] + 4; + env->regs[R_PC] += 4; + break; + } else { + qemu_log_mask(CPU_LOG_INT, "\nTrap\n"); + + env->regs[CR_ESTATUS] = env->regs[CR_STATUS]; + env->regs[CR_STATUS] &= ~0x3; + env->regs[R_EA] = env->regs[R_PC] + 4; + env->regs[R_PC] = cpu->exception_addr; + + gdbsig = TARGET_SIGTRAP; + break; + } + case 0xaa: + switch (env->regs[R_PC]) { + /*case 0x1000:*/ /* TODO:__kuser_helper_version */ + case 0x1004: /* __kuser_cmpxchg */ + start_exclusive(); + if (env->regs[4] & 0x3) { + goto kuser_fail; + } + ret = get_user_u32(env->regs[2], env->regs[4]); + if (ret) { + end_exclusive(); + goto kuser_fail; + } + env->regs[2] -= env->regs[5]; + if (env->regs[2] == 0) { + put_user_u32(env->regs[6], env->regs[4]); + } + end_exclusive(); + env->regs[R_PC] = env->regs[R_RA]; + break; + /*case 0x1040:*/ /* TODO:__kuser_sigtramp */ + default: + ; +kuser_fail: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* TODO: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->regs[R_PC]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + default: + EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", + trapnr); + gdbsig = TARGET_SIGILL; + break; + } + if (gdbsig) { + gdb_handlesig(cs, gdbsig); + if (gdbsig != TARGET_SIGTRAP) { + exit(EXIT_FAILURE); + } + } + + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + env->regs[0] = 0; + env->regs[1] = regs->r1; + env->regs[2] = regs->r2; + env->regs[3] = regs->r3; + env->regs[4] = regs->r4; + env->regs[5] = regs->r5; + env->regs[6] = regs->r6; + env->regs[7] = regs->r7; + env->regs[8] = regs->r8; + env->regs[9] = regs->r9; + env->regs[10] = regs->r10; + env->regs[11] = regs->r11; + env->regs[12] = regs->r12; + env->regs[13] = regs->r13; + env->regs[14] = regs->r14; + env->regs[15] = regs->r15; + /* TODO: unsigned long orig_r2; */ + env->regs[R_RA] = regs->ra; + env->regs[R_FP] = regs->fp; + env->regs[R_SP] = regs->sp; + env->regs[R_GP] = regs->gp; + env->regs[CR_ESTATUS] = regs->estatus; + env->regs[R_EA] = regs->ea; + /* TODO: unsigned long orig_r7; */ + + /* Emulate eret when starting thread. */ + env->regs[R_PC] = regs->ea; +} diff --git a/linux-user/nios2/signal.c b/linux-user/nios2/signal.c new file mode 100644 index 0000000000..7d535065ed --- /dev/null +++ b/linux-user/nios2/signal.c @@ -0,0 +1,236 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +#define MCONTEXT_VERSION 2 + +struct target_sigcontext { + int version; + unsigned long gregs[32]; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_rt_sigframe { + struct target_siginfo info; + struct target_ucontext uc; +}; + +static int rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env) +{ + unsigned long *gregs = uc->tuc_mcontext.gregs; + + __put_user(MCONTEXT_VERSION, &uc->tuc_mcontext.version); + __put_user(env->regs[1], &gregs[0]); + __put_user(env->regs[2], &gregs[1]); + __put_user(env->regs[3], &gregs[2]); + __put_user(env->regs[4], &gregs[3]); + __put_user(env->regs[5], &gregs[4]); + __put_user(env->regs[6], &gregs[5]); + __put_user(env->regs[7], &gregs[6]); + __put_user(env->regs[8], &gregs[7]); + __put_user(env->regs[9], &gregs[8]); + __put_user(env->regs[10], &gregs[9]); + __put_user(env->regs[11], &gregs[10]); + __put_user(env->regs[12], &gregs[11]); + __put_user(env->regs[13], &gregs[12]); + __put_user(env->regs[14], &gregs[13]); + __put_user(env->regs[15], &gregs[14]); + __put_user(env->regs[16], &gregs[15]); + __put_user(env->regs[17], &gregs[16]); + __put_user(env->regs[18], &gregs[17]); + __put_user(env->regs[19], &gregs[18]); + __put_user(env->regs[20], &gregs[19]); + __put_user(env->regs[21], &gregs[20]); + __put_user(env->regs[22], &gregs[21]); + __put_user(env->regs[23], &gregs[22]); + __put_user(env->regs[R_RA], &gregs[23]); + __put_user(env->regs[R_FP], &gregs[24]); + __put_user(env->regs[R_GP], &gregs[25]); + __put_user(env->regs[R_EA], &gregs[27]); + __put_user(env->regs[R_SP], &gregs[28]); + + return 0; +} + +static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc, + int *pr2) +{ + int temp; + abi_ulong off, frame_addr = env->regs[R_SP]; + unsigned long *gregs = uc->tuc_mcontext.gregs; + int err; + + /* Always make any pending restarted system calls return -EINTR */ + /* current->restart_block.fn = do_no_restart_syscall; */ + + __get_user(temp, &uc->tuc_mcontext.version); + if (temp != MCONTEXT_VERSION) { + return 1; + } + + /* restore passed registers */ + __get_user(env->regs[1], &gregs[0]); + __get_user(env->regs[2], &gregs[1]); + __get_user(env->regs[3], &gregs[2]); + __get_user(env->regs[4], &gregs[3]); + __get_user(env->regs[5], &gregs[4]); + __get_user(env->regs[6], &gregs[5]); + __get_user(env->regs[7], &gregs[6]); + __get_user(env->regs[8], &gregs[7]); + __get_user(env->regs[9], &gregs[8]); + __get_user(env->regs[10], &gregs[9]); + __get_user(env->regs[11], &gregs[10]); + __get_user(env->regs[12], &gregs[11]); + __get_user(env->regs[13], &gregs[12]); + __get_user(env->regs[14], &gregs[13]); + __get_user(env->regs[15], &gregs[14]); + __get_user(env->regs[16], &gregs[15]); + __get_user(env->regs[17], &gregs[16]); + __get_user(env->regs[18], &gregs[17]); + __get_user(env->regs[19], &gregs[18]); + __get_user(env->regs[20], &gregs[19]); + __get_user(env->regs[21], &gregs[20]); + __get_user(env->regs[22], &gregs[21]); + __get_user(env->regs[23], &gregs[22]); + /* gregs[23] is handled below */ + /* Verify, should this be settable */ + __get_user(env->regs[R_FP], &gregs[24]); + /* Verify, should this be settable */ + __get_user(env->regs[R_GP], &gregs[25]); + /* Not really necessary no user settable bits */ + __get_user(temp, &gregs[26]); + __get_user(env->regs[R_EA], &gregs[27]); + + __get_user(env->regs[R_RA], &gregs[23]); + __get_user(env->regs[R_SP], &gregs[28]); + + off = offsetof(struct target_rt_sigframe, uc.tuc_stack); + err = do_sigaltstack(frame_addr + off, 0, get_sp_from_cpustate(env)); + if (err == -EFAULT) { + return 1; + } + + *pr2 = env->regs[2]; + return 0; +} + +static void *get_sigframe(struct target_sigaction *ka, CPUNios2State *env, + size_t frame_size) +{ + unsigned long usp; + + /* This is the X/Open sanctioned signal stack switching. */ + usp = target_sigsp(get_sp_from_cpustate(env), ka); + + /* Verify, is it 32 or 64 bit aligned */ + return (void *)((usp - frame_size) & -8UL); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, + CPUNios2State *env) +{ + struct target_rt_sigframe *frame; + int i, err = 0; + + frame = get_sigframe(ka, env, sizeof(*frame)); + + if (ka->sa_flags & SA_SIGINFO) { + tswap_siginfo(&frame->info, info); + } + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + target_save_altstack(&frame->uc.tuc_stack, env); + err |= rt_setup_ucontext(&frame->uc, env); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user((abi_ulong)set->sig[i], + (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); + } + + if (err) { + goto give_sigsegv; + } + + /* Set up to return from userspace; jump to fixed address sigreturn + trampoline on kuser page. */ + env->regs[R_RA] = (unsigned long) (0x1044); + + /* Set up registers for signal handler */ + env->regs[R_SP] = (unsigned long) frame; + env->regs[4] = (unsigned long) sig; + env->regs[5] = (unsigned long) &frame->info; + env->regs[6] = (unsigned long) &frame->uc; + env->regs[R_EA] = (unsigned long) ka->_sa_handler; + return; + +give_sigsegv: + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sigsegv(sig); + return; +} + +long do_sigreturn(CPUNios2State *env) +{ + trace_user_do_sigreturn(env, 0); + qemu_log_mask(LOG_UNIMP, "do_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} + +long do_rt_sigreturn(CPUNios2State *env) +{ + /* Verify, can we follow the stack back */ + abi_ulong frame_addr = env->regs[R_SP]; + struct target_rt_sigframe *frame; + sigset_t set; + int rval; + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + do_sigprocmask(SIG_SETMASK, &set, NULL); + + if (rt_restore_ucontext(env, &frame->uc, &rval)) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return rval; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} diff --git a/linux-user/nios2/sockbits.h b/linux-user/nios2/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/nios2/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/nios2/target_cpu.h b/linux-user/nios2/target_cpu.h index 20ab4790a9..14f63338fa 100644 --- a/linux-user/nios2/target_cpu.h +++ b/linux-user/nios2/target_cpu.h @@ -36,4 +36,8 @@ static inline void cpu_set_tls(CPUNios2State *env, target_ulong newtls) */ } +static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state) +{ + return state->regs[R_SP]; +} #endif diff --git a/linux-user/nios2/target_fcntl.h b/linux-user/nios2/target_fcntl.h new file mode 100644 index 0000000000..714583215d --- /dev/null +++ b/linux-user/nios2/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef NIOS2_TARGET_FCNTL_H +#define NIOS2_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/nios2/target_signal.h b/linux-user/nios2/target_signal.h index 23a8267696..7776bcdbfd 100644 --- a/linux-user/nios2/target_signal.h +++ b/linux-user/nios2/target_signal.h @@ -1,8 +1,6 @@ #ifndef TARGET_SIGNAL_H #define TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -18,9 +16,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUNios2State *state) -{ - return state->regs[R_SP]; -} +#include "../generic/signal.h" #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c new file mode 100644 index 0000000000..6c6ea871e1 --- /dev/null +++ b/linux-user/openrisc/cpu_loop.c @@ -0,0 +1,115 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void cpu_loop(CPUOpenRISCState *env) +{ + CPUState *cs = CPU(openrisc_env_get_cpu(env)); + int trapnr; + abi_long ret; + target_siginfo_t info; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_SYSCALL: + env->pc += 4; /* 0xc00; */ + ret = do_syscall(env, + cpu_get_gpr(env, 11), /* return value */ + cpu_get_gpr(env, 3), /* r3 - r7 are params */ + cpu_get_gpr(env, 4), + cpu_get_gpr(env, 5), + cpu_get_gpr(env, 6), + cpu_get_gpr(env, 7), + cpu_get_gpr(env, 8), 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + cpu_set_gpr(env, 11, ret); + } + break; + case EXCP_DPF: + case EXCP_IPF: + case EXCP_RANGE: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ALIGN: + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_BUS_ADRALN; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ILLEGAL: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_FPE: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_INTERRUPT: + /* We processed the pending cpu work above. */ + break; + case EXCP_DEBUG: + trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); + if (trapnr) { + info.si_signo = trapnr; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + + for (i = 0; i < 32; i++) { + cpu_set_gpr(env, i, regs->gpr[i]); + } + env->pc = regs->pc; + cpu_set_sr(env, regs->sr); +} diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c new file mode 100644 index 0000000000..232ad82b98 --- /dev/null +++ b/linux-user/openrisc/signal.c @@ -0,0 +1,173 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +typedef struct target_sigcontext { + struct target_pt_regs regs; + abi_ulong oldmask; +} target_sigcontext; + +typedef struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +} target_ucontext; + +typedef struct target_rt_sigframe { + struct target_siginfo info; + target_ucontext uc; + uint32_t retcode[4]; /* trampoline code */ +} target_rt_sigframe; + +static void restore_sigcontext(CPUOpenRISCState *env, target_sigcontext *sc) +{ + int i; + abi_ulong v; + + for (i = 0; i < 32; ++i) { + __get_user(v, &sc->regs.gpr[i]); + cpu_set_gpr(env, i, v); + } + __get_user(env->pc, &sc->regs.pc); + + /* Make sure the supervisor flag is clear. */ + __get_user(v, &sc->regs.sr); + cpu_set_sr(env, v & ~SR_SM); +} + +/* Set up a signal frame. */ + +static void setup_sigcontext(target_sigcontext *sc, CPUOpenRISCState *env) +{ + int i; + + for (i = 0; i < 32; ++i) { + __put_user(cpu_get_gpr(env, i), &sc->regs.gpr[i]); + } + + __put_user(env->pc, &sc->regs.pc); + __put_user(cpu_get_sr(env), &sc->regs.sr); +} + +static inline abi_ulong get_sigframe(struct target_sigaction *ka, + CPUOpenRISCState *env, + size_t frame_size) +{ + target_ulong sp = get_sp_from_cpustate(env); + + /* Honor redzone now. If we swap to signal stack, no need to waste + * the 128 bytes by subtracting afterward. + */ + sp -= 128; + + sp = target_sigsp(sp, ka); + sp -= frame_size; + sp = QEMU_ALIGN_DOWN(sp, 4); + + return sp; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUOpenRISCState *env) +{ + abi_ulong frame_addr; + target_rt_sigframe *frame; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + if (ka->sa_flags & SA_SIGINFO) { + tswap_siginfo(&frame->info, info); + } + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + + target_save_altstack(&frame->uc.tuc_stack, env); + setup_sigcontext(&frame->uc.tuc_mcontext, env); + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + /* This is l.ori r11,r0,__NR_sigreturn; l.sys 1; l.nop; l.nop */ + __put_user(0xa9600000 | TARGET_NR_rt_sigreturn, frame->retcode + 0); + __put_user(0x20000001, frame->retcode + 1); + __put_user(0x15000000, frame->retcode + 2); + __put_user(0x15000000, frame->retcode + 3); + + /* Set up registers for signal handler */ + cpu_set_gpr(env, 9, frame_addr + offsetof(target_rt_sigframe, retcode)); + cpu_set_gpr(env, 3, sig); + cpu_set_gpr(env, 4, frame_addr + offsetof(target_rt_sigframe, info)); + cpu_set_gpr(env, 5, frame_addr + offsetof(target_rt_sigframe, uc)); + cpu_set_gpr(env, 1, frame_addr); + + /* For debugging convenience, set ppc to the insn that faulted. */ + env->ppc = env->pc; + /* When setting the PC for the signal handler, exit delay slot. */ + env->pc = ka->_sa_handler; + env->dflag = 0; + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +long do_rt_sigreturn(CPUOpenRISCState *env) +{ + abi_ulong frame_addr = get_sp_from_cpustate(env); + target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, 0); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + if (frame_addr & 3) { + goto badframe; + } + + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + if (do_sigaltstack(frame_addr + offsetof(target_rt_sigframe, uc.tuc_stack), + 0, frame_addr) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return cpu_get_gpr(env, 11); + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} diff --git a/linux-user/openrisc/sockbits.h b/linux-user/openrisc/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/openrisc/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/openrisc/target_cpu.h b/linux-user/openrisc/target_cpu.h index 606ad6f695..d1ea4506e2 100644 --- a/linux-user/openrisc/target_cpu.h +++ b/linux-user/openrisc/target_cpu.h @@ -33,4 +33,8 @@ static inline void cpu_set_tls(CPUOpenRISCState *env, target_ulong newtls) cpu_set_gpr(env, 10, newtls); } +static inline abi_ulong get_sp_from_cpustate(CPUOpenRISCState *state) +{ + return cpu_get_gpr(state, 1); +} #endif diff --git a/linux-user/openrisc/target_fcntl.h b/linux-user/openrisc/target_fcntl.h new file mode 100644 index 0000000000..ea31bf8b70 --- /dev/null +++ b/linux-user/openrisc/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef OPENRISC_TARGET_FCNTL_H +#define OPENRISC_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/openrisc/target_signal.h b/linux-user/openrisc/target_signal.h index 95a733e15a..8283eaf544 100644 --- a/linux-user/openrisc/target_signal.h +++ b/linux-user/openrisc/target_signal.h @@ -1,27 +1,29 @@ #ifndef OPENRISC_TARGET_SIGNAL_H #define OPENRISC_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { abi_long ss_sp; + abi_int ss_flags; abi_ulong ss_size; - abi_long ss_flags; } target_stack_t; /* sigaltstack controls */ #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00000002 +#define TARGET_SA_SIGINFO 0x00000004 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESETHAND 0x80000000 + #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUOpenRISCState *state) -{ - return cpu_get_gpr(state, 1); -} - +#include "../generic/signal.h" #endif /* OPENRISC_TARGET_SIGNAL_H */ diff --git a/linux-user/openrisc/target_syscall.h b/linux-user/openrisc/target_syscall.h index 03104f80af..d586d2a018 100644 --- a/linux-user/openrisc/target_syscall.h +++ b/linux-user/openrisc/target_syscall.h @@ -1,27 +1,15 @@ #ifndef OPENRISC_TARGET_SYSCALL_H #define OPENRISC_TARGET_SYSCALL_H +/* Note that in linux/arch/openrisc/include/uapi/asm/ptrace.h, + * this is called user_regs_struct. Given that this is what + * is used within struct sigcontext we need this definition. + * However, elfload.c wants this name. + */ struct target_pt_regs { - union { - struct { - /* Named registers */ - uint32_t sr; /* Stored in place of r0 */ - target_ulong sp; /* r1 */ - }; - struct { - /* Old style */ - target_ulong offset[2]; - target_ulong gprs[30]; - }; - struct { - /* New style */ - target_ulong gpr[32]; - }; - }; - target_ulong pc; - target_ulong orig_gpr11; /* For restarting system calls */ - uint32_t syscallno; /* Syscall number (used by strace) */ - target_ulong dummy; /* Cheap alignment fix */ + abi_ulong gpr[32]; + abi_ulong pc; + abi_ulong sr; }; #define UNAME_MACHINE "openrisc" diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c new file mode 100644 index 0000000000..133a87f349 --- /dev/null +++ b/linux-user/ppc/cpu_loop.c @@ -0,0 +1,502 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +static inline uint64_t cpu_ppc_get_tb(CPUPPCState *env) +{ + return cpu_get_host_ticks(); +} + +uint64_t cpu_ppc_load_tbl(CPUPPCState *env) +{ + return cpu_ppc_get_tb(env); +} + +uint32_t cpu_ppc_load_tbu(CPUPPCState *env) +{ + return cpu_ppc_get_tb(env) >> 32; +} + +uint64_t cpu_ppc_load_atbl(CPUPPCState *env) +{ + return cpu_ppc_get_tb(env); +} + +uint32_t cpu_ppc_load_atbu(CPUPPCState *env) +{ + return cpu_ppc_get_tb(env) >> 32; +} + +uint32_t cpu_ppc601_load_rtcu(CPUPPCState *env) +__attribute__ (( alias ("cpu_ppc_load_tbu") )); + +uint32_t cpu_ppc601_load_rtcl(CPUPPCState *env) +{ + return cpu_ppc_load_tbl(env) & 0x3FFFFF80; +} + +/* XXX: to be fixed */ +int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) +{ + return -1; +} + +int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) +{ + return -1; +} + +void cpu_loop(CPUPPCState *env) +{ + CPUState *cs = CPU(ppc_env_get_cpu(env)); + target_siginfo_t info; + int trapnr, sig; + target_ulong ret; + + for(;;) { + bool arch_interrupt; + + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + arch_interrupt = true; + switch (trapnr) { + case POWERPC_EXCP_NONE: + /* Just go on */ + break; + case POWERPC_EXCP_CRITICAL: /* Critical input */ + cpu_abort(cs, "Critical interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + cpu_abort(cs, "Machine check exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + /* XXX: check this. Seems bugged */ + switch (env->error_code & 0xFF000000) { + case 0x40000000: + case 0x42000000: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + break; + case 0x04000000: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLADR; + break; + case 0x08000000: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_ACCERR; + break; + default: + /* Let's send a regular segfault... */ + EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", + env->error_code); + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + break; + } + info._sifields._sigfault._addr = env->spr[SPR_DAR]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + /* XXX: check this */ + switch (env->error_code & 0xFF000000) { + case 0x40000000: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + break; + case 0x10000000: + case 0x08000000: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_ACCERR; + break; + default: + /* Let's send a regular segfault... */ + EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", + env->error_code); + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + break; + } + info._sifields._sigfault._addr = env->nip - 4; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_EXTERNAL: /* External input */ + cpu_abort(cs, "External interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + /* XXX: check this */ + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_BUS_ADRALN; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_PROGRAM: /* Program exception */ + case POWERPC_EXCP_HV_EMU: /* HV emulation */ + /* XXX: check this */ + switch (env->error_code & ~0xF) { + case POWERPC_EXCP_FP: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + switch (env->error_code & 0xF) { + case POWERPC_EXCP_FP_OX: + info.si_code = TARGET_FPE_FLTOVF; + break; + case POWERPC_EXCP_FP_UX: + info.si_code = TARGET_FPE_FLTUND; + break; + case POWERPC_EXCP_FP_ZX: + case POWERPC_EXCP_FP_VXZDZ: + info.si_code = TARGET_FPE_FLTDIV; + break; + case POWERPC_EXCP_FP_XX: + info.si_code = TARGET_FPE_FLTRES; + break; + case POWERPC_EXCP_FP_VXSOFT: + info.si_code = TARGET_FPE_FLTINV; + break; + case POWERPC_EXCP_FP_VXSNAN: + case POWERPC_EXCP_FP_VXISI: + case POWERPC_EXCP_FP_VXIDI: + case POWERPC_EXCP_FP_VXIMZ: + case POWERPC_EXCP_FP_VXVC: + case POWERPC_EXCP_FP_VXSQRT: + case POWERPC_EXCP_FP_VXCVI: + info.si_code = TARGET_FPE_FLTSUB; + break; + default: + EXCP_DUMP(env, "Unknown floating point exception (%02x)\n", + env->error_code); + break; + } + break; + case POWERPC_EXCP_INVAL: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + switch (env->error_code & 0xF) { + case POWERPC_EXCP_INVAL_INVAL: + info.si_code = TARGET_ILL_ILLOPC; + break; + case POWERPC_EXCP_INVAL_LSWX: + info.si_code = TARGET_ILL_ILLOPN; + break; + case POWERPC_EXCP_INVAL_SPR: + info.si_code = TARGET_ILL_PRVREG; + break; + case POWERPC_EXCP_INVAL_FP: + info.si_code = TARGET_ILL_COPROC; + break; + default: + EXCP_DUMP(env, "Unknown invalid operation (%02x)\n", + env->error_code & 0xF); + info.si_code = TARGET_ILL_ILLADR; + break; + } + break; + case POWERPC_EXCP_PRIV: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + switch (env->error_code & 0xF) { + case POWERPC_EXCP_PRIV_OPC: + info.si_code = TARGET_ILL_PRVOPC; + break; + case POWERPC_EXCP_PRIV_REG: + info.si_code = TARGET_ILL_PRVREG; + break; + default: + EXCP_DUMP(env, "Unknown privilege violation (%02x)\n", + env->error_code & 0xF); + info.si_code = TARGET_ILL_PRVOPC; + break; + } + break; + case POWERPC_EXCP_TRAP: + cpu_abort(cs, "Tried to call a TRAP\n"); + break; + default: + /* Should not happen ! */ + cpu_abort(cs, "Unknown program exception (%02x)\n", + env->error_code); + break; + } + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_SYSCALL: /* System call exception */ + cpu_abort(cs, "Syscall exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_DECR: /* Decrementer exception */ + cpu_abort(cs, "Decrementer interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + cpu_abort(cs, "Fix interval timer interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + cpu_abort(cs, "Watchdog timer interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + cpu_abort(cs, "Data TLB exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + cpu_abort(cs, "Instruction TLB exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavail. */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */ + cpu_abort(cs, "Embedded floating-point data IRQ not handled\n"); + break; + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round IRQ */ + cpu_abort(cs, "Embedded floating-point round IRQ not handled\n"); + break; + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor IRQ */ + cpu_abort(cs, "Performance monitor exception not handled\n"); + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + cpu_abort(cs, "Doorbell interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + cpu_abort(cs, "Doorbell critical interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + cpu_abort(cs, "Reset interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DSEG: /* Data segment exception */ + cpu_abort(cs, "Data segment exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + cpu_abort(cs, "Instruction segment exception " + "while in user mode. Aborting\n"); + break; + /* PowerPC 64 with hypervisor mode support */ + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + cpu_abort(cs, "Hypervisor decrementer interrupt " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_TRACE: /* Trace exception */ + /* Nothing to do: + * we use this exception to emulate step-by-step execution mode. + */ + break; + /* PowerPC 64 with hypervisor mode support */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + cpu_abort(cs, "Hypervisor data storage exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage excp */ + cpu_abort(cs, "Hypervisor instruction storage exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + cpu_abort(cs, "Hypervisor data segment exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment excp */ + cpu_abort(cs, "Hypervisor instruction segment exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */ + cpu_abort(cs, "Programmable interval timer interrupt " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_IO: /* IO error exception */ + cpu_abort(cs, "IO error exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_RUNM: /* Run mode exception */ + cpu_abort(cs, "Run mode exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + cpu_abort(cs, "Emulation trap exception not handled\n"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + cpu_abort(cs, "Instruction fetch TLB exception " + "while in user-mode. Aborting"); + break; + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + cpu_abort(cs, "Data load TLB exception while in user-mode. " + "Aborting"); + break; + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + cpu_abort(cs, "Data store TLB exception while in user-mode. " + "Aborting"); + break; + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + cpu_abort(cs, "Floating-point assist exception not handled\n"); + break; + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + cpu_abort(cs, "Instruction address breakpoint exception " + "not handled\n"); + break; + case POWERPC_EXCP_SMI: /* System management interrupt */ + cpu_abort(cs, "System management interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + cpu_abort(cs, "Thermal interrupt interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_PERFM: /* Embedded performance monitor IRQ */ + cpu_abort(cs, "Performance monitor exception not handled\n"); + break; + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + cpu_abort(cs, "Vector assist exception not handled\n"); + break; + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + cpu_abort(cs, "Soft patch exception not handled\n"); + break; + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + cpu_abort(cs, "Maintenance exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_STOP: /* stop translation */ + /* We did invalidate the instruction cache. Go on */ + break; + case POWERPC_EXCP_BRANCH: /* branch instruction: */ + /* We just stopped because of a branch. Go on */ + break; + case POWERPC_EXCP_SYSCALL_USER: + /* system call in user-mode emulation */ + /* WARNING: + * PPC ABI uses overflow flag in cr0 to signal an error + * in syscalls. + */ + env->crf[0] &= ~0x1; + env->nip += 4; + ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], + env->gpr[5], env->gpr[6], env->gpr[7], + env->gpr[8], 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->nip -= 4; + break; + } + if (ret == (target_ulong)(-TARGET_QEMU_ESIGRETURN)) { + /* Returning from a successful sigreturn syscall. + Avoid corrupting register state. */ + break; + } + if (ret > (target_ulong)(-515)) { + env->crf[0] |= 0x1; + ret = -ret; + } + env->gpr[3] = ret; + break; + case EXCP_DEBUG: + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else { + arch_interrupt = false; + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + arch_interrupt = false; + break; + default: + cpu_abort(cs, "Unknown exception 0x%x. Aborting\n", trapnr); + break; + } + process_pending_signals(env); + + /* Most of the traps imply a transition through kernel mode, + * which implies an REI instruction has been executed. Which + * means that RX and LOCK_ADDR should be cleared. But there + * are a few exceptions for traps internal to QEMU. + */ + if (arch_interrupt) { + env->reserve_addr = -1; + } + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + +#if defined(TARGET_PPC64) + int flag = (env->insns_flags2 & PPC2_BOOKE206) ? MSR_CM : MSR_SF; +#if defined(TARGET_ABI32) + env->msr &= ~((target_ulong)1 << flag); +#else + env->msr |= (target_ulong)1 << flag; +#endif +#endif + env->nip = regs->nip; + for(i = 0; i < 32; i++) { + env->gpr[i] = regs->gpr[i]; + } +} diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c new file mode 100644 index 0000000000..2ae120a2bc --- /dev/null +++ b/linux-user/ppc/signal.c @@ -0,0 +1,733 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +/* Size of dummy stack frame allocated when calling signal handler. + See arch/powerpc/include/asm/ptrace.h. */ +#if defined(TARGET_PPC64) +#define SIGNAL_FRAMESIZE 128 +#else +#define SIGNAL_FRAMESIZE 64 +#endif + +/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; + on 64-bit PPC, sigcontext and mcontext are one and the same. */ +struct target_mcontext { + target_ulong mc_gregs[48]; + /* Includes fpscr. */ + uint64_t mc_fregs[33]; +#if defined(TARGET_PPC64) + /* Pointer to the vector regs */ + target_ulong v_regs; +#else + target_ulong mc_pad[2]; +#endif + /* We need to handle Altivec and SPE at the same time, which no + kernel needs to do. Fortunately, the kernel defines this bit to + be Altivec-register-large all the time, rather than trying to + twiddle it based on the specific platform. */ + union { + /* SPE vector registers. One extra for SPEFSCR. */ + uint32_t spe[33]; + /* Altivec vector registers. The packing of VSCR and VRSAVE + varies depending on whether we're PPC64 or not: PPC64 splits + them apart; PPC32 stuffs them together. + We also need to account for the VSX registers on PPC64 + */ +#if defined(TARGET_PPC64) +#define QEMU_NVRREG (34 + 16) + /* On ppc64, this mcontext structure is naturally *unaligned*, + * or rather it is aligned on a 8 bytes boundary but not on + * a 16 bytes one. This pad fixes it up. This is also why the + * vector regs are referenced by the v_regs pointer above so + * any amount of padding can be added here + */ + target_ulong pad; +#else + /* On ppc32, we are already aligned to 16 bytes */ +#define QEMU_NVRREG 33 +#endif + /* We cannot use ppc_avr_t here as we do *not* want the implied + * 16-bytes alignment that would result from it. This would have + * the effect of making the whole struct target_mcontext aligned + * which breaks the layout of struct target_ucontext on ppc64. + */ + uint64_t altivec[QEMU_NVRREG][2]; +#undef QEMU_NVRREG + } mc_vregs; +}; + +/* See arch/powerpc/include/asm/sigcontext.h. */ +struct target_sigcontext { + target_ulong _unused[4]; + int32_t signal; +#if defined(TARGET_PPC64) + int32_t pad0; +#endif + target_ulong handler; + target_ulong oldmask; + target_ulong regs; /* struct pt_regs __user * */ +#if defined(TARGET_PPC64) + struct target_mcontext mcontext; +#endif +}; + +/* Indices for target_mcontext.mc_gregs, below. + See arch/powerpc/include/asm/ptrace.h for details. */ +enum { + TARGET_PT_R0 = 0, + TARGET_PT_R1 = 1, + TARGET_PT_R2 = 2, + TARGET_PT_R3 = 3, + TARGET_PT_R4 = 4, + TARGET_PT_R5 = 5, + TARGET_PT_R6 = 6, + TARGET_PT_R7 = 7, + TARGET_PT_R8 = 8, + TARGET_PT_R9 = 9, + TARGET_PT_R10 = 10, + TARGET_PT_R11 = 11, + TARGET_PT_R12 = 12, + TARGET_PT_R13 = 13, + TARGET_PT_R14 = 14, + TARGET_PT_R15 = 15, + TARGET_PT_R16 = 16, + TARGET_PT_R17 = 17, + TARGET_PT_R18 = 18, + TARGET_PT_R19 = 19, + TARGET_PT_R20 = 20, + TARGET_PT_R21 = 21, + TARGET_PT_R22 = 22, + TARGET_PT_R23 = 23, + TARGET_PT_R24 = 24, + TARGET_PT_R25 = 25, + TARGET_PT_R26 = 26, + TARGET_PT_R27 = 27, + TARGET_PT_R28 = 28, + TARGET_PT_R29 = 29, + TARGET_PT_R30 = 30, + TARGET_PT_R31 = 31, + TARGET_PT_NIP = 32, + TARGET_PT_MSR = 33, + TARGET_PT_ORIG_R3 = 34, + TARGET_PT_CTR = 35, + TARGET_PT_LNK = 36, + TARGET_PT_XER = 37, + TARGET_PT_CCR = 38, + /* Yes, there are two registers with #39. One is 64-bit only. */ + TARGET_PT_MQ = 39, + TARGET_PT_SOFTE = 39, + TARGET_PT_TRAP = 40, + TARGET_PT_DAR = 41, + TARGET_PT_DSISR = 42, + TARGET_PT_RESULT = 43, + TARGET_PT_REGS_COUNT = 44 +}; + + +struct target_ucontext { + target_ulong tuc_flags; + target_ulong tuc_link; /* ucontext_t __user * */ + struct target_sigaltstack tuc_stack; +#if !defined(TARGET_PPC64) + int32_t tuc_pad[7]; + target_ulong tuc_regs; /* struct mcontext __user * + points to uc_mcontext field */ +#endif + target_sigset_t tuc_sigmask; +#if defined(TARGET_PPC64) + target_sigset_t unused[15]; /* Allow for uc_sigmask growth */ + struct target_sigcontext tuc_sigcontext; +#else + int32_t tuc_maskext[30]; + int32_t tuc_pad2[3]; + struct target_mcontext tuc_mcontext; +#endif +}; + +/* See arch/powerpc/kernel/signal_32.c. */ +struct target_sigframe { + struct target_sigcontext sctx; + struct target_mcontext mctx; + int32_t abigap[56]; +}; + +#if defined(TARGET_PPC64) + +#define TARGET_TRAMP_SIZE 6 + +struct target_rt_sigframe { + /* sys_rt_sigreturn requires the ucontext be the first field */ + struct target_ucontext uc; + target_ulong _unused[2]; + uint32_t trampoline[TARGET_TRAMP_SIZE]; + target_ulong pinfo; /* struct siginfo __user * */ + target_ulong puc; /* void __user * */ + struct target_siginfo info; + /* 64 bit ABI allows for 288 bytes below sp before decrementing it. */ + char abigap[288]; +} __attribute__((aligned(16))); + +#else + +struct target_rt_sigframe { + struct target_siginfo info; + struct target_ucontext uc; + int32_t abigap[56]; +}; + +#endif + +#if defined(TARGET_PPC64) + +struct target_func_ptr { + target_ulong entry; + target_ulong toc; +}; + +#endif + +/* We use the mc_pad field for the signal return trampoline. */ +#define tramp mc_pad + +/* See arch/powerpc/kernel/signal.c. */ +static target_ulong get_sigframe(struct target_sigaction *ka, + CPUPPCState *env, + int frame_size) +{ + target_ulong oldsp; + + oldsp = target_sigsp(get_sp_from_cpustate(env), ka); + + return (oldsp - frame_size) & ~0xFUL; +} + +#if ((defined(TARGET_WORDS_BIGENDIAN) && defined(HOST_WORDS_BIGENDIAN)) || \ + (!defined(HOST_WORDS_BIGENDIAN) && !defined(TARGET_WORDS_BIGENDIAN))) +#define PPC_VEC_HI 0 +#define PPC_VEC_LO 1 +#else +#define PPC_VEC_HI 1 +#define PPC_VEC_LO 0 +#endif + + +static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) +{ + target_ulong msr = env->msr; + int i; + target_ulong ccr = 0; + + /* In general, the kernel attempts to be intelligent about what it + needs to save for Altivec/FP/SPE registers. We don't care that + much, so we just go ahead and save everything. */ + + /* Save general registers. */ + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + __put_user(env->gpr[i], &frame->mc_gregs[i]); + } + __put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); + __put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); + __put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); + __put_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); + + for (i = 0; i < ARRAY_SIZE(env->crf); i++) { + ccr |= env->crf[i] << (32 - ((i + 1) * 4)); + } + __put_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); + + /* Save Altivec registers if necessary. */ + if (env->insns_flags & PPC_ALTIVEC) { + uint32_t *vrsave; + for (i = 0; i < ARRAY_SIZE(env->avr); i++) { + ppc_avr_t *avr = &env->avr[i]; + ppc_avr_t *vreg = (ppc_avr_t *)&frame->mc_vregs.altivec[i]; + + __put_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); + __put_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]); + } + /* Set MSR_VR in the saved MSR value to indicate that + frame->mc_vregs contains valid data. */ + msr |= MSR_VR; +#if defined(TARGET_PPC64) + vrsave = (uint32_t *)&frame->mc_vregs.altivec[33]; + /* 64-bit needs to put a pointer to the vectors in the frame */ + __put_user(h2g(frame->mc_vregs.altivec), &frame->v_regs); +#else + vrsave = (uint32_t *)&frame->mc_vregs.altivec[32]; +#endif + __put_user((uint32_t)env->spr[SPR_VRSAVE], vrsave); + } + + /* Save VSX second halves */ + if (env->insns_flags2 & PPC2_VSX) { + uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; + for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { + __put_user(env->vsr[i], &vsregs[i]); + } + } + + /* Save floating point registers. */ + if (env->insns_flags & PPC_FLOAT) { + for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { + __put_user(env->fpr[i], &frame->mc_fregs[i]); + } + __put_user((uint64_t) env->fpscr, &frame->mc_fregs[32]); + } + + /* Save SPE registers. The kernel only saves the high half. */ + if (env->insns_flags & PPC_SPE) { +#if defined(TARGET_PPC64) + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + __put_user(env->gpr[i] >> 32, &frame->mc_vregs.spe[i]); + } +#else + for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { + __put_user(env->gprh[i], &frame->mc_vregs.spe[i]); + } +#endif + /* Set MSR_SPE in the saved MSR value to indicate that + frame->mc_vregs contains valid data. */ + msr |= MSR_SPE; + __put_user(env->spe_fscr, &frame->mc_vregs.spe[32]); + } + + /* Store MSR. */ + __put_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); +} + +static void encode_trampoline(int sigret, uint32_t *tramp) +{ + /* Set up the sigreturn trampoline: li r0,sigret; sc. */ + if (sigret) { + __put_user(0x38000000 | sigret, &tramp[0]); + __put_user(0x44000002, &tramp[1]); + } +} + +static void restore_user_regs(CPUPPCState *env, + struct target_mcontext *frame, int sig) +{ + target_ulong save_r2 = 0; + target_ulong msr; + target_ulong ccr; + + int i; + + if (!sig) { + save_r2 = env->gpr[2]; + } + + /* Restore general registers. */ + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + __get_user(env->gpr[i], &frame->mc_gregs[i]); + } + __get_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); + __get_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); + __get_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); + __get_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); + __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); + + for (i = 0; i < ARRAY_SIZE(env->crf); i++) { + env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf; + } + + if (!sig) { + env->gpr[2] = save_r2; + } + /* Restore MSR. */ + __get_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); + + /* If doing signal return, restore the previous little-endian mode. */ + if (sig) + env->msr = (env->msr & ~(1ull << MSR_LE)) | (msr & (1ull << MSR_LE)); + + /* Restore Altivec registers if necessary. */ + if (env->insns_flags & PPC_ALTIVEC) { + ppc_avr_t *v_regs; + uint32_t *vrsave; +#if defined(TARGET_PPC64) + uint64_t v_addr; + /* 64-bit needs to recover the pointer to the vectors from the frame */ + __get_user(v_addr, &frame->v_regs); + v_regs = g2h(v_addr); +#else + v_regs = (ppc_avr_t *)frame->mc_vregs.altivec; +#endif + for (i = 0; i < ARRAY_SIZE(env->avr); i++) { + ppc_avr_t *avr = &env->avr[i]; + ppc_avr_t *vreg = &v_regs[i]; + + __get_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); + __get_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]); + } + /* Set MSR_VEC in the saved MSR value to indicate that + frame->mc_vregs contains valid data. */ +#if defined(TARGET_PPC64) + vrsave = (uint32_t *)&v_regs[33]; +#else + vrsave = (uint32_t *)&v_regs[32]; +#endif + __get_user(env->spr[SPR_VRSAVE], vrsave); + } + + /* Restore VSX second halves */ + if (env->insns_flags2 & PPC2_VSX) { + uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; + for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { + __get_user(env->vsr[i], &vsregs[i]); + } + } + + /* Restore floating point registers. */ + if (env->insns_flags & PPC_FLOAT) { + uint64_t fpscr; + for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { + __get_user(env->fpr[i], &frame->mc_fregs[i]); + } + __get_user(fpscr, &frame->mc_fregs[32]); + env->fpscr = (uint32_t) fpscr; + } + + /* Save SPE registers. The kernel only saves the high half. */ + if (env->insns_flags & PPC_SPE) { +#if defined(TARGET_PPC64) + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + uint32_t hi; + + __get_user(hi, &frame->mc_vregs.spe[i]); + env->gpr[i] = ((uint64_t)hi << 32) | ((uint32_t) env->gpr[i]); + } +#else + for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { + __get_user(env->gprh[i], &frame->mc_vregs.spe[i]); + } +#endif + __get_user(env->spe_fscr, &frame->mc_vregs.spe[32]); + } +} + +#if !defined(TARGET_PPC64) +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUPPCState *env) +{ + struct target_sigframe *frame; + struct target_sigcontext *sc; + target_ulong frame_addr, newsp; + int err = 0; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) + goto sigsegv; + sc = &frame->sctx; + + __put_user(ka->_sa_handler, &sc->handler); + __put_user(set->sig[0], &sc->oldmask); + __put_user(set->sig[1], &sc->_unused[3]); + __put_user(h2g(&frame->mctx), &sc->regs); + __put_user(sig, &sc->signal); + + /* Save user regs. */ + save_user_regs(env, &frame->mctx); + + /* Construct the trampoline code on the stack. */ + encode_trampoline(TARGET_NR_sigreturn, (uint32_t *)&frame->mctx.tramp); + + /* The kernel checks for the presence of a VDSO here. We don't + emulate a vdso, so use a sigreturn system call. */ + env->lr = (target_ulong) h2g(frame->mctx.tramp); + + /* Turn off all fp exceptions. */ + env->fpscr = 0; + + /* Create a stack frame for the caller of the handler. */ + newsp = frame_addr - SIGNAL_FRAMESIZE; + err |= put_user(env->gpr[1], newsp, target_ulong); + + if (err) + goto sigsegv; + + /* Set up registers for signal handler. */ + env->gpr[1] = newsp; + env->gpr[3] = sig; + env->gpr[4] = frame_addr + offsetof(struct target_sigframe, sctx); + + env->nip = (target_ulong) ka->_sa_handler; + + /* Signal handlers are entered in big-endian mode. */ + env->msr &= ~(1ull << MSR_LE); + + unlock_user_struct(frame, frame_addr, 1); + return; + +sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} +#endif /* !defined(TARGET_PPC64) */ + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUPPCState *env) +{ + struct target_rt_sigframe *rt_sf; + uint32_t *trampptr = 0; + struct target_mcontext *mctx = 0; + target_ulong rt_sf_addr, newsp = 0; + int i, err = 0; +#if defined(TARGET_PPC64) + struct target_sigcontext *sc = 0; + struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; +#endif + + rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf)); + if (!lock_user_struct(VERIFY_WRITE, rt_sf, rt_sf_addr, 1)) + goto sigsegv; + + tswap_siginfo(&rt_sf->info, info); + + __put_user(0, &rt_sf->uc.tuc_flags); + __put_user(0, &rt_sf->uc.tuc_link); + target_save_altstack(&rt_sf->uc.tuc_stack, env); +#if !defined(TARGET_PPC64) + __put_user(h2g (&rt_sf->uc.tuc_mcontext), + &rt_sf->uc.tuc_regs); +#endif + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]); + } + +#if defined(TARGET_PPC64) + mctx = &rt_sf->uc.tuc_sigcontext.mcontext; + trampptr = &rt_sf->trampoline[0]; + + sc = &rt_sf->uc.tuc_sigcontext; + __put_user(h2g(mctx), &sc->regs); + __put_user(sig, &sc->signal); +#else + mctx = &rt_sf->uc.tuc_mcontext; + trampptr = (uint32_t *)&rt_sf->uc.tuc_mcontext.tramp; +#endif + + save_user_regs(env, mctx); + encode_trampoline(TARGET_NR_rt_sigreturn, trampptr); + + /* The kernel checks for the presence of a VDSO here. We don't + emulate a vdso, so use a sigreturn system call. */ + env->lr = (target_ulong) h2g(trampptr); + + /* Turn off all fp exceptions. */ + env->fpscr = 0; + + /* Create a stack frame for the caller of the handler. */ + newsp = rt_sf_addr - (SIGNAL_FRAMESIZE + 16); + err |= put_user(env->gpr[1], newsp, target_ulong); + + if (err) + goto sigsegv; + + /* Set up registers for signal handler. */ + env->gpr[1] = newsp; + env->gpr[3] = (target_ulong) sig; + env->gpr[4] = (target_ulong) h2g(&rt_sf->info); + env->gpr[5] = (target_ulong) h2g(&rt_sf->uc); + env->gpr[6] = (target_ulong) h2g(rt_sf); + +#if defined(TARGET_PPC64) + if (get_ppc64_abi(image) < 2) { + /* ELFv1 PPC64 function pointers are pointers to OPD entries. */ + struct target_func_ptr *handler = + (struct target_func_ptr *)g2h(ka->_sa_handler); + env->nip = tswapl(handler->entry); + env->gpr[2] = tswapl(handler->toc); + } else { + /* ELFv2 PPC64 function pointers are entry points, but R12 + * must also be set */ + env->nip = tswapl((target_ulong) ka->_sa_handler); + env->gpr[12] = env->nip; + } +#else + env->nip = (target_ulong) ka->_sa_handler; +#endif + + /* Signal handlers are entered in big-endian mode. */ + env->msr &= ~(1ull << MSR_LE); + + unlock_user_struct(rt_sf, rt_sf_addr, 1); + return; + +sigsegv: + unlock_user_struct(rt_sf, rt_sf_addr, 1); + force_sigsegv(sig); + +} + +#if !defined(TARGET_PPC64) +long do_sigreturn(CPUPPCState *env) +{ + struct target_sigcontext *sc = NULL; + struct target_mcontext *sr = NULL; + target_ulong sr_addr = 0, sc_addr; + sigset_t blocked; + target_sigset_t set; + + sc_addr = env->gpr[1] + SIGNAL_FRAMESIZE; + if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) + goto sigsegv; + +#if defined(TARGET_PPC64) + set.sig[0] = sc->oldmask + ((uint64_t)(sc->_unused[3]) << 32); +#else + __get_user(set.sig[0], &sc->oldmask); + __get_user(set.sig[1], &sc->_unused[3]); +#endif + target_to_host_sigset_internal(&blocked, &set); + set_sigmask(&blocked); + + __get_user(sr_addr, &sc->regs); + if (!lock_user_struct(VERIFY_READ, sr, sr_addr, 1)) + goto sigsegv; + restore_user_regs(env, sr, 1); + + unlock_user_struct(sr, sr_addr, 1); + unlock_user_struct(sc, sc_addr, 1); + return -TARGET_QEMU_ESIGRETURN; + +sigsegv: + unlock_user_struct(sr, sr_addr, 1); + unlock_user_struct(sc, sc_addr, 1); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} +#endif /* !defined(TARGET_PPC64) */ + +/* See arch/powerpc/kernel/signal_32.c. */ +static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) +{ + struct target_mcontext *mcp; + target_ulong mcp_addr; + sigset_t blocked; + target_sigset_t set; + + if (copy_from_user(&set, h2g(ucp) + offsetof(struct target_ucontext, tuc_sigmask), + sizeof (set))) + return 1; + +#if defined(TARGET_PPC64) + mcp_addr = h2g(ucp) + + offsetof(struct target_ucontext, tuc_sigcontext.mcontext); +#else + __get_user(mcp_addr, &ucp->tuc_regs); +#endif + + if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) + return 1; + + target_to_host_sigset_internal(&blocked, &set); + set_sigmask(&blocked); + restore_user_regs(env, mcp, sig); + + unlock_user_struct(mcp, mcp_addr, 1); + return 0; +} + +long do_rt_sigreturn(CPUPPCState *env) +{ + struct target_rt_sigframe *rt_sf = NULL; + target_ulong rt_sf_addr; + + rt_sf_addr = env->gpr[1] + SIGNAL_FRAMESIZE + 16; + if (!lock_user_struct(VERIFY_READ, rt_sf, rt_sf_addr, 1)) + goto sigsegv; + + if (do_setcontext(&rt_sf->uc, env, 1)) + goto sigsegv; + + do_sigaltstack(rt_sf_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, env->gpr[1]); + + unlock_user_struct(rt_sf, rt_sf_addr, 1); + return -TARGET_QEMU_ESIGRETURN; + +sigsegv: + unlock_user_struct(rt_sf, rt_sf_addr, 1); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +/* This syscall implements {get,set,swap}context for userland. */ +abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx, + abi_ulong unew_ctx, abi_long ctx_size) +{ + struct target_ucontext *uctx; + struct target_mcontext *mctx; + + /* For ppc32, ctx_size is "reserved for future use". + * For ppc64, we do not yet support the VSX extension. + */ + if (ctx_size < sizeof(struct target_ucontext)) { + return -TARGET_EINVAL; + } + + if (uold_ctx) { + TaskState *ts = (TaskState *)thread_cpu->opaque; + + if (!lock_user_struct(VERIFY_WRITE, uctx, uold_ctx, 1)) { + return -TARGET_EFAULT; + } + +#ifdef TARGET_PPC64 + mctx = &uctx->tuc_sigcontext.mcontext; +#else + /* ??? The kernel aligns the pointer down here into padding, but + * in setup_rt_frame we don't. Be self-compatible for now. + */ + mctx = &uctx->tuc_mcontext; + __put_user(h2g(mctx), &uctx->tuc_regs); +#endif + + save_user_regs(env, mctx); + host_to_target_sigset(&uctx->tuc_sigmask, &ts->signal_mask); + + unlock_user_struct(uctx, uold_ctx, 1); + } + + if (unew_ctx) { + int err; + + if (!lock_user_struct(VERIFY_READ, uctx, unew_ctx, 1)) { + return -TARGET_EFAULT; + } + err = do_setcontext(uctx, env, 0); + unlock_user_struct(uctx, unew_ctx, 1); + + if (err) { + /* We cannot return to a partially updated context. */ + force_sig(TARGET_SIGSEGV); + } + return -TARGET_QEMU_ESIGRETURN; + } + + return 0; +} diff --git a/linux-user/ppc/sockbits.h b/linux-user/ppc/sockbits.h new file mode 100644 index 0000000000..ee453347a3 --- /dev/null +++ b/linux-user/ppc/sockbits.h @@ -0,0 +1,26 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef PPC_SOCKBITS_H +#define PPC_SOCKBITS_H + +#include "../generic/sockbits.h" + +#undef TARGET_SO_RCVLOWAT +#define TARGET_SO_RCVLOWAT 16 +#undef TARGET_SO_SNDLOWAT +#define TARGET_SO_SNDLOWAT 17 +#undef TARGET_SO_RCVTIMEO +#define TARGET_SO_RCVTIMEO 18 +#undef TARGET_SO_SNDTIMEO +#define TARGET_SO_SNDTIMEO 19 +#undef TARGET_SO_PASSCRED +#define TARGET_SO_PASSCRED 20 +#undef TARGET_SO_PEERCRED +#define TARGET_SO_PEERCRED 21 + +#endif diff --git a/linux-user/ppc/target_cpu.h b/linux-user/ppc/target_cpu.h index 3aab3d185d..c4641834e7 100644 --- a/linux-user/ppc/target_cpu.h +++ b/linux-user/ppc/target_cpu.h @@ -47,5 +47,8 @@ static inline uint32_t get_ppc64_abi(struct image_info *infop) return infop->elf_flags & EF_PPC64_ABI; } - +static inline abi_ulong get_sp_from_cpustate(CPUPPCState *state) +{ + return state->gpr[1]; +} #endif diff --git a/linux-user/ppc/target_fcntl.h b/linux-user/ppc/target_fcntl.h new file mode 100644 index 0000000000..d74ab710cf --- /dev/null +++ b/linux-user/ppc/target_fcntl.h @@ -0,0 +1,17 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef PPC_TARGET_FCNTL_H +#define PPC_TARGET_FCNTL_H + +#define TARGET_O_DIRECTORY 040000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ +#define TARGET_O_LARGEFILE 0200000 +#define TARGET_O_DIRECT 0400000 /* direct disk access hint */ + +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/ppc/target_signal.h b/linux-user/ppc/target_signal.h index 865c52f3e8..4453e2e7ef 100644 --- a/linux-user/ppc/target_signal.h +++ b/linux-user/ppc/target_signal.h @@ -1,8 +1,6 @@ #ifndef PPC_TARGET_SIGNAL_H #define PPC_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,9 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUPPCState *state) -{ - return state->gpr[1]; -} - +#include "../generic/signal.h" +#if !defined(TARGET_PPC64) +#define TARGET_ARCH_HAS_SETUP_FRAME +#endif #endif /* PPC_TARGET_SIGNAL_H */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 192a0d2fef..b4959e41c6 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -51,13 +51,16 @@ struct image_info { abi_ulong file_string; uint32_t elf_flags; int personality; -#ifdef CONFIG_USE_FDPIC + abi_ulong alignment; + + /* The fields below are used in FDPIC mode. */ abi_ulong loadmap_addr; uint16_t nsegs; void *loadsegs; abi_ulong pt_dynamic_addr; + abi_ulong interpreter_loadmap_addr; + abi_ulong interpreter_pt_dynamic_addr; struct image_info *other_info; -#endif }; #ifdef TARGET_I386 @@ -183,6 +186,13 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, struct target_pt_regs * regs, struct image_info *infop, struct linux_binprm *); +/* Returns true if the image uses the FDPIC ABI. If this is the case, + * we have to provide some information (loadmap, pt_dynamic_info) such + * that the program can be relocated adequately. This is also useful + * when handling signals. + */ +int info_is_fdpic(struct image_info *info); + uint32_t get_elf_eflags(int fd); int load_elf_binary(struct linux_binprm *bprm, struct image_info *info); int load_flt_binary(struct linux_binprm *bprm, struct image_info *info); @@ -386,6 +396,8 @@ long do_sigreturn(CPUArchState *env); long do_rt_sigreturn(CPUArchState *env); abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp); int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset); +abi_long do_swapcontext(CPUArchState *env, abi_ulong uold_ctx, + abi_ulong unew_ctx, abi_long ctx_size); /** * block_signals: block all signals while handling this guest syscall * @@ -609,12 +621,24 @@ static inline void *lock_user_string(abi_ulong guest_addr) #include +static inline int is_error(abi_long ret) +{ + return (abi_ulong)ret >= (abi_ulong)(-4096); +} + +/** + * preexit_cleanup: housekeeping before the guest exits + * + * env: the CPU state + * code: the exit code + */ +void preexit_cleanup(CPUArchState *env, int code); + /* Include target-specific struct and function definitions; * they may need access to the target-independent structures * above, so include them last. */ #include "target_cpu.h" -#include "target_signal.h" #include "target_structs.h" #endif /* QEMU_H */ diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c new file mode 100644 index 0000000000..f137d39d7e --- /dev/null +++ b/linux-user/riscv/cpu_loop.c @@ -0,0 +1,118 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void cpu_loop(CPURISCVState *env) +{ + CPUState *cs = CPU(riscv_env_get_cpu(env)); + int trapnr, signum, sigcode; + target_ulong sigaddr; + target_ulong ret; + + for (;;) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + signum = 0; + sigcode = 0; + sigaddr = 0; + + switch (trapnr) { + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + case RISCV_EXCP_U_ECALL: + env->pc += 4; + if (env->gpr[xA7] == TARGET_NR_arch_specific_syscall + 15) { + /* riscv_flush_icache_syscall is a no-op in QEMU as + self-modifying code is automatically detected */ + ret = 0; + } else { + ret = do_syscall(env, + env->gpr[xA7], + env->gpr[xA0], + env->gpr[xA1], + env->gpr[xA2], + env->gpr[xA3], + env->gpr[xA4], + env->gpr[xA5], + 0, 0); + } + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 4; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->gpr[xA0] = ret; + } + if (cs->singlestep_enabled) { + goto gdbstep; + } + break; + case RISCV_EXCP_ILLEGAL_INST: + signum = TARGET_SIGILL; + sigcode = TARGET_ILL_ILLOPC; + break; + case RISCV_EXCP_BREAKPOINT: + signum = TARGET_SIGTRAP; + sigcode = TARGET_TRAP_BRKPT; + sigaddr = env->pc; + break; + case RISCV_EXCP_INST_PAGE_FAULT: + case RISCV_EXCP_LOAD_PAGE_FAULT: + case RISCV_EXCP_STORE_PAGE_FAULT: + signum = TARGET_SIGSEGV; + sigcode = TARGET_SEGV_MAPERR; + break; + case EXCP_DEBUG: + gdbstep: + signum = gdb_handlesig(cs, TARGET_SIGTRAP); + sigcode = TARGET_TRAP_BRKPT; + break; + default: + EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", + trapnr); + exit(EXIT_FAILURE); + } + + if (signum) { + target_siginfo_t info = { + .si_signo = signum, + .si_errno = 0, + .si_code = sigcode, + ._sifields._sigfault._addr = sigaddr + }; + queue_signal(env, info.si_signo, QEMU_SI_KILL, &info); + } + + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + env->pc = regs->sepc; + env->gpr[xSP] = regs->sp; +} diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c new file mode 100644 index 0000000000..f598d41891 --- /dev/null +++ b/linux-user/riscv/signal.c @@ -0,0 +1,207 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +/* Signal handler invocation must be transparent for the code being + interrupted. Complete CPU (hart) state is saved on entry and restored + before returning from the handler. Process sigmask is also saved to block + signals while the handler is running. The handler gets its own stack, + which also doubles as storage for the CPU state and sigmask. + + The code below is qemu re-implementation of arch/riscv/kernel/signal.c */ + +struct target_sigcontext { + abi_long pc; + abi_long gpr[31]; /* x0 is not present, so all offsets must be -1 */ + uint64_t fpr[32]; + uint32_t fcsr; +}; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */ + +struct target_ucontext { + unsigned long uc_flags; + struct target_ucontext *uc_link; + target_stack_t uc_stack; + struct target_sigcontext uc_mcontext; + target_sigset_t uc_sigmask; +}; + +struct target_rt_sigframe { + uint32_t tramp[2]; /* not in kernel, which uses VDSO instead */ + struct target_siginfo info; + struct target_ucontext uc; +}; + +static abi_ulong get_sigframe(struct target_sigaction *ka, + CPURISCVState *regs, size_t framesize) +{ + abi_ulong sp = get_sp_from_cpustate(regs); + + /* If we are on the alternate signal stack and would overflow it, don't. + Return an always-bogus address instead so we will die with SIGSEGV. */ + if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) { + return -1L; + } + + /* This is the X/Open sanctioned signal stack switching. */ + sp = target_sigsp(sp, ka) - framesize; + + /* XXX: kernel aligns with 0xf ? */ + sp &= ~3UL; /* align sp on 4-byte boundary */ + + return sp; +} + +static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) +{ + int i; + + __put_user(env->pc, &sc->pc); + + for (i = 1; i < 32; i++) { + __put_user(env->gpr[i], &sc->gpr[i - 1]); + } + for (i = 0; i < 32; i++) { + __put_user(env->fpr[i], &sc->fpr[i]); + } + + uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/ + __put_user(fcsr, &sc->fcsr); +} + +static void setup_ucontext(struct target_ucontext *uc, + CPURISCVState *env, target_sigset_t *set) +{ + __put_user(0, &(uc->uc_flags)); + __put_user(0, &(uc->uc_link)); + + target_save_altstack(&uc->uc_stack, env); + + int i; + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &(uc->uc_sigmask.sig[i])); + } + + setup_sigcontext(&uc->uc_mcontext, env); +} + +static inline void install_sigtramp(uint32_t *tramp) +{ + __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */ + __put_user(0x00000073, tramp + 1); /* ecall */ +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPURISCVState *env) +{ + abi_ulong frame_addr; + struct target_rt_sigframe *frame; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto badframe; + } + + setup_ucontext(&frame->uc, env, set); + tswap_siginfo(&frame->info, info); + install_sigtramp(frame->tramp); + + env->pc = ka->_sa_handler; + env->gpr[xSP] = frame_addr; + env->gpr[xA0] = sig; + env->gpr[xA1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->gpr[xA2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + env->gpr[xRA] = frame_addr + offsetof(struct target_rt_sigframe, tramp); + + return; + +badframe: + unlock_user_struct(frame, frame_addr, 1); + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sig(TARGET_SIGSEGV); +} + +static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc) +{ + int i; + + __get_user(env->pc, &sc->pc); + + for (i = 1; i < 32; ++i) { + __get_user(env->gpr[i], &sc->gpr[i - 1]); + } + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i], &sc->fpr[i]); + } + + uint32_t fcsr; + __get_user(fcsr, &sc->fcsr); + csr_write_helper(env, fcsr, CSR_FCSR); +} + +static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc) +{ + sigset_t blocked; + target_sigset_t target_set; + int i; + + target_sigemptyset(&target_set); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &(uc->uc_sigmask.sig[i])); + } + + target_to_host_sigset_internal(&blocked, &target_set); + set_sigmask(&blocked); + + restore_sigcontext(env, &uc->uc_mcontext); +} + +long do_rt_sigreturn(CPURISCVState *env) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + + frame_addr = env->gpr[xSP]; + trace_user_do_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + restore_ucontext(env, &frame->uc); + + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.uc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return 0; +} diff --git a/linux-user/riscv/sockbits.h b/linux-user/riscv/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/riscv/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/riscv/target_cpu.h b/linux-user/riscv/target_cpu.h index c5549b1120..7e090f376a 100644 --- a/linux-user/riscv/target_cpu.h +++ b/linux-user/riscv/target_cpu.h @@ -15,4 +15,8 @@ static inline void cpu_set_tls(CPURISCVState *env, target_ulong newtls) env->gpr[xTP] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state) +{ + return state->gpr[xSP]; +} #endif diff --git a/linux-user/riscv/target_fcntl.h b/linux-user/riscv/target_fcntl.h new file mode 100644 index 0000000000..9c3d0fbe2b --- /dev/null +++ b/linux-user/riscv/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef RISCV_TARGET_FCNTL_H +#define RISCV_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h index ce77f752e3..c8b1455800 100644 --- a/linux-user/riscv/target_signal.h +++ b/linux-user/riscv/target_signal.h @@ -1,8 +1,6 @@ #ifndef TARGET_SIGNAL_H #define TARGET_SIGNAL_H -#include "cpu.h" - typedef struct target_sigaltstack { abi_ulong ss_sp; abi_int ss_flags; @@ -15,9 +13,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state) -{ - return state->gpr[xSP]; -} +#include "../generic/signal.h" #endif /* TARGET_SIGNAL_H */ diff --git a/linux-user/riscv/target_syscall.h b/linux-user/riscv/target_syscall.h index d4e109a27f..ee81d8bc88 100644 --- a/linux-user/riscv/target_syscall.h +++ b/linux-user/riscv/target_syscall.h @@ -45,7 +45,7 @@ struct target_pt_regs { #else #define UNAME_MACHINE "riscv64" #endif -#define UNAME_MINIMUM_RELEASE "3.8.0" +#define UNAME_MINIMUM_RELEASE "4.15.0" #define TARGET_MINSIGSTKSZ 2048 #define TARGET_MLOCKALL_MCL_CURRENT 1 diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c new file mode 100644 index 0000000000..99f5f1594f --- /dev/null +++ b/linux-user/s390x/cpu_loop.c @@ -0,0 +1,165 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +/* s390x masks the fault address it reports in si_addr for SIGSEGV and SIGBUS */ +#define S390X_FAIL_ADDR_MASK -4096LL + +void cpu_loop(CPUS390XState *env) +{ + CPUState *cs = CPU(s390_env_get_cpu(env)); + int trapnr, n, sig; + target_siginfo_t info; + target_ulong addr; + abi_long ret; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_INTERRUPT: + /* Just indicate that signals should be handled asap. */ + break; + + case EXCP_SVC: + n = env->int_svc_code; + if (!n) { + /* syscalls > 255 */ + n = env->regs[1]; + } + env->psw.addr += env->int_svc_ilen; + ret = do_syscall(env, n, env->regs[2], env->regs[3], + env->regs[4], env->regs[5], + env->regs[6], env->regs[7], 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->psw.addr -= env->int_svc_ilen; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[2] = ret; + } + break; + + case EXCP_DEBUG: + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + n = TARGET_TRAP_BRKPT; + goto do_signal_pc; + } + break; + case EXCP_PGM: + n = env->int_pgm_code; + switch (n) { + case PGM_OPERATION: + case PGM_PRIVILEGED: + sig = TARGET_SIGILL; + n = TARGET_ILL_ILLOPC; + goto do_signal_pc; + case PGM_PROTECTION: + case PGM_ADDRESSING: + sig = TARGET_SIGSEGV; + /* XXX: check env->error_code */ + n = TARGET_SEGV_MAPERR; + addr = env->__excp_addr & S390X_FAIL_ADDR_MASK; + goto do_signal; + case PGM_EXECUTE: + case PGM_SPECIFICATION: + case PGM_SPECIAL_OP: + case PGM_OPERAND: + do_sigill_opn: + sig = TARGET_SIGILL; + n = TARGET_ILL_ILLOPN; + goto do_signal_pc; + + case PGM_FIXPT_OVERFLOW: + sig = TARGET_SIGFPE; + n = TARGET_FPE_INTOVF; + goto do_signal_pc; + case PGM_FIXPT_DIVIDE: + sig = TARGET_SIGFPE; + n = TARGET_FPE_INTDIV; + goto do_signal_pc; + + case PGM_DATA: + n = (env->fpc >> 8) & 0xff; + if (n == 0xff) { + /* compare-and-trap */ + goto do_sigill_opn; + } else { + /* An IEEE exception, simulated or otherwise. */ + if (n & 0x80) { + n = TARGET_FPE_FLTINV; + } else if (n & 0x40) { + n = TARGET_FPE_FLTDIV; + } else if (n & 0x20) { + n = TARGET_FPE_FLTOVF; + } else if (n & 0x10) { + n = TARGET_FPE_FLTUND; + } else if (n & 0x08) { + n = TARGET_FPE_FLTRES; + } else { + /* ??? Quantum exception; BFP, DFP error. */ + goto do_sigill_opn; + } + sig = TARGET_SIGFPE; + goto do_signal_pc; + } + + default: + fprintf(stderr, "Unhandled program exception: %#x\n", n); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + break; + + do_signal_pc: + addr = env->psw.addr; + do_signal: + info.si_signo = sig; + info.si_errno = 0; + info.si_code = n; + info._sifields._sigfault._addr = addr; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + for (i = 0; i < 16; i++) { + env->regs[i] = regs->gprs[i]; + } + env->psw.mask = regs->psw.mask; + env->psw.addr = regs->psw.addr; +} diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c new file mode 100644 index 0000000000..3d3cb67bbe --- /dev/null +++ b/linux-user/s390x/signal.c @@ -0,0 +1,320 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +#define __NUM_GPRS 16 +#define __NUM_FPRS 16 +#define __NUM_ACRS 16 + +#define S390_SYSCALL_SIZE 2 +#define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */ + +#define _SIGCONTEXT_NSIG 64 +#define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */ +#define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW) +#define _SIGMASK_COPY_SIZE (sizeof(unsigned long)*_SIGCONTEXT_NSIG_WORDS) +#define PSW_ADDR_AMODE 0x0000000000000000UL /* 0x80000000UL for 31-bit */ +#define S390_SYSCALL_OPCODE ((uint16_t)0x0a00) + +typedef struct { + target_psw_t psw; + target_ulong gprs[__NUM_GPRS]; + unsigned int acrs[__NUM_ACRS]; +} target_s390_regs_common; + +typedef struct { + unsigned int fpc; + double fprs[__NUM_FPRS]; +} target_s390_fp_regs; + +typedef struct { + target_s390_regs_common regs; + target_s390_fp_regs fpregs; +} target_sigregs; + +struct target_sigcontext { + target_ulong oldmask[_SIGCONTEXT_NSIG_WORDS]; + target_sigregs *sregs; +}; + +typedef struct { + uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + struct target_sigcontext sc; + target_sigregs sregs; + int signo; + uint8_t retcode[S390_SYSCALL_SIZE]; +} sigframe; + +struct target_ucontext { + target_ulong tuc_flags; + struct target_ucontext *tuc_link; + target_stack_t tuc_stack; + target_sigregs tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +typedef struct { + uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; + uint8_t retcode[S390_SYSCALL_SIZE]; + struct target_siginfo info; + struct target_ucontext uc; +} rt_sigframe; + +static inline abi_ulong +get_sigframe(struct target_sigaction *ka, CPUS390XState *env, size_t frame_size) +{ + abi_ulong sp; + + /* Default to using normal stack */ + sp = get_sp_from_cpustate(env); + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa_flags & TARGET_SA_ONSTACK) { + sp = target_sigsp(sp, ka); + } + + /* This is the legacy signal stack switching. */ + else if (/* FIXME !user_mode(regs) */ 0 && + !(ka->sa_flags & TARGET_SA_RESTORER) && + ka->sa_restorer) { + sp = (abi_ulong) ka->sa_restorer; + } + + return (sp - frame_size) & -8ul; +} + +static void save_sigregs(CPUS390XState *env, target_sigregs *sregs) +{ + int i; + //save_access_regs(current->thread.acrs); FIXME + + /* Copy a 'clean' PSW mask to the user to avoid leaking + information about whether PER is currently on. */ + __put_user(env->psw.mask, &sregs->regs.psw.mask); + __put_user(env->psw.addr, &sregs->regs.psw.addr); + for (i = 0; i < 16; i++) { + __put_user(env->regs[i], &sregs->regs.gprs[i]); + } + for (i = 0; i < 16; i++) { + __put_user(env->aregs[i], &sregs->regs.acrs[i]); + } + /* + * We have to store the fp registers to current->thread.fp_regs + * to merge them with the emulated registers. + */ + //save_fp_regs(¤t->thread.fp_regs); FIXME + for (i = 0; i < 16; i++) { + __put_user(get_freg(env, i)->ll, &sregs->fpregs.fprs[i]); + } +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUS390XState *env) +{ + sigframe *frame; + abi_ulong frame_addr; + + 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)) { + goto give_sigsegv; + } + + __put_user(set->sig[0], &frame->sc.oldmask[0]); + + save_sigregs(env, &frame->sregs); + + __put_user((abi_ulong)(unsigned long)&frame->sregs, + (abi_ulong *)&frame->sc.sregs); + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + env->regs[14] = (unsigned long) + ka->sa_restorer | PSW_ADDR_AMODE; + } else { + env->regs[14] = (frame_addr + offsetof(sigframe, retcode)) + | PSW_ADDR_AMODE; + __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, + (uint16_t *)(frame->retcode)); + } + + /* Set up backchain. */ + __put_user(env->regs[15], (abi_ulong *) frame); + + /* Set up registers for signal handler */ + env->regs[15] = frame_addr; + env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; + + env->regs[2] = sig; //map_signal(sig); + env->regs[3] = frame_addr += offsetof(typeof(*frame), sc); + + /* We forgot to include these in the sigcontext. + To avoid breaking binary compatibility, they are passed as args. */ + env->regs[4] = 0; // FIXME: no clue... current->thread.trap_no; + env->regs[5] = 0; // FIXME: no clue... current->thread.prot_addr; + + /* Place signal number on stack to allow backtrace from handler. */ + __put_user(env->regs[2], &frame->signo); + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUS390XState *env) +{ + int i; + rt_sigframe *frame; + abi_ulong frame_addr; + + frame_addr = get_sigframe(ka, env, sizeof *frame); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.tuc_link); + target_save_altstack(&frame->uc.tuc_stack, env); + save_sigregs(env, &frame->uc.tuc_mcontext); + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user((abi_ulong)set->sig[i], + (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + env->regs[14] = (unsigned long) ka->sa_restorer | PSW_ADDR_AMODE; + } else { + env->regs[14] = (unsigned long) frame->retcode | PSW_ADDR_AMODE; + __put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn, + (uint16_t *)(frame->retcode)); + } + + /* Set up backchain. */ + __put_user(env->regs[15], (abi_ulong *) frame); + + /* Set up registers for signal handler */ + env->regs[15] = frame_addr; + env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; + + env->regs[2] = sig; //map_signal(sig); + env->regs[3] = frame_addr + offsetof(typeof(*frame), info); + env->regs[4] = frame_addr + offsetof(typeof(*frame), uc); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +static int +restore_sigregs(CPUS390XState *env, target_sigregs *sc) +{ + int err = 0; + int i; + + for (i = 0; i < 16; i++) { + __get_user(env->regs[i], &sc->regs.gprs[i]); + } + + __get_user(env->psw.mask, &sc->regs.psw.mask); + trace_user_s390x_restore_sigregs(env, (unsigned long long)sc->regs.psw.addr, + (unsigned long long)env->psw.addr); + __get_user(env->psw.addr, &sc->regs.psw.addr); + /* FIXME: 31-bit -> | PSW_ADDR_AMODE */ + + for (i = 0; i < 16; i++) { + __get_user(env->aregs[i], &sc->regs.acrs[i]); + } + for (i = 0; i < 16; i++) { + __get_user(get_freg(env, i)->ll, &sc->fpregs.fprs[i]); + } + + return err; +} + +long do_sigreturn(CPUS390XState *env) +{ + sigframe *frame; + abi_ulong frame_addr = env->regs[15]; + target_sigset_t target_set; + sigset_t set; + + trace_user_do_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + __get_user(target_set.sig[0], &frame->sc.oldmask[0]); + + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); /* ~_BLOCKABLE? */ + + if (restore_sigregs(env, &frame->sregs)) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUS390XState *env) +{ + rt_sigframe *frame; + abi_ulong frame_addr = env->regs[15]; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + + set_sigmask(&set); /* ~_BLOCKABLE? */ + + if (restore_sigregs(env, &frame->uc.tuc_mcontext)) { + goto badframe; + } + + if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.tuc_stack), 0, + get_sp_from_cpustate(env)) == -EFAULT) { + goto badframe; + } + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/s390x/sockbits.h b/linux-user/s390x/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/s390x/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/s390x/target_cpu.h b/linux-user/s390x/target_cpu.h index 87ea4d2d9b..66ef8aa8c2 100644 --- a/linux-user/s390x/target_cpu.h +++ b/linux-user/s390x/target_cpu.h @@ -36,4 +36,8 @@ static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls) env->aregs[1] = newtls & 0xffffffffULL; } +static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state) +{ + return state->regs[15]; +} #endif diff --git a/linux-user/s390x/target_fcntl.h b/linux-user/s390x/target_fcntl.h new file mode 100644 index 0000000000..36dc50fba0 --- /dev/null +++ b/linux-user/s390x/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef S390X_TARGET_FCNTL_H +#define S390X_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/s390x/target_signal.h b/linux-user/s390x/target_signal.h index 6f7b6abafe..b58bc7c20f 100644 --- a/linux-user/s390x/target_signal.h +++ b/linux-user/s390x/target_signal.h @@ -1,8 +1,6 @@ #ifndef S390X_TARGET_SIGNAL_H #define S390X_TARGET_SIGNAL_H -#include "cpu.h" - typedef struct target_sigaltstack { abi_ulong ss_sp; int ss_flags; @@ -18,10 +16,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUS390XState *state) -{ - return state->regs[15]; -} - +#include "../generic/signal.h" +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* S390X_TARGET_SIGNAL_H */ diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c new file mode 100644 index 0000000000..fdd348170b --- /dev/null +++ b/linux-user/sh4/cpu_loop.c @@ -0,0 +1,111 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +void cpu_loop(CPUSH4State *env) +{ + CPUState *cs = CPU(sh_env_get_cpu(env)); + int trapnr, ret; + target_siginfo_t info; + + while (1) { + bool arch_interrupt = true; + + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case 0x160: + env->pc += 2; + ret = do_syscall(env, + env->gregs[3], + env->gregs[4], + env->gregs[5], + env->gregs[6], + env->gregs[7], + env->gregs[0], + env->gregs[1], + 0, 0); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 2; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->gregs[0] = ret; + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } else { + arch_interrupt = false; + } + } + break; + case 0xa0: + case 0xc0: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->tea; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + arch_interrupt = false; + break; + default: + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + + /* Most of the traps imply an exception or interrupt, which + implies an REI instruction has been executed. Which means + that LDST (aka LOK_ADDR) should be cleared. But there are + a few exceptions for traps internal to QEMU. */ + if (arch_interrupt) { + env->lock_addr = -1; + } + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + + for(i = 0; i < 16; i++) { + env->gregs[i] = regs->regs[i]; + } + env->pc = regs->pc; +} diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c new file mode 100644 index 0000000000..c6752baa7e --- /dev/null +++ b/linux-user/sh4/signal.c @@ -0,0 +1,342 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +/* + * code and data structures from linux kernel: + * include/asm-sh/sigcontext.h + * arch/sh/kernel/signal.c + */ + +struct target_sigcontext { + target_ulong oldmask; + + /* CPU registers */ + target_ulong sc_gregs[16]; + target_ulong sc_pc; + target_ulong sc_pr; + target_ulong sc_sr; + target_ulong sc_gbr; + target_ulong sc_mach; + target_ulong sc_macl; + + /* FPU registers */ + target_ulong sc_fpregs[16]; + target_ulong sc_xfpregs[16]; + unsigned int sc_fpscr; + unsigned int sc_fpul; + unsigned int sc_ownedfp; +}; + +struct target_sigframe +{ + struct target_sigcontext sc; + target_ulong extramask[TARGET_NSIG_WORDS-1]; + uint16_t retcode[3]; +}; + + +struct target_ucontext { + target_ulong tuc_flags; + struct target_ucontext *tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_rt_sigframe +{ + struct target_siginfo info; + struct target_ucontext uc; + uint16_t retcode[3]; +}; + + +#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */ +#define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) SH3/4 */ + +static abi_ulong get_sigframe(struct target_sigaction *ka, + unsigned long sp, size_t frame_size) +{ + sp = target_sigsp(sp, ka); + + return (sp - frame_size) & -8ul; +} + +/* Notice when we're in the middle of a gUSA region and reset. + Note that this will only occur for !parallel_cpus, as we will + translate such sequences differently in a parallel context. */ +static void unwind_gusa(CPUSH4State *regs) +{ + /* If the stack pointer is sufficiently negative, and we haven't + completed the sequence, then reset to the entry to the region. */ + /* ??? The SH4 kernel checks for and address above 0xC0000000. + However, the page mappings in qemu linux-user aren't as restricted + and we wind up with the normal stack mapped above 0xF0000000. + That said, there is no reason why the kernel should be allowing + a gUSA region that spans 1GB. Use a tighter check here, for what + can actually be enabled by the immediate move. */ + if (regs->gregs[15] >= -128u && regs->pc < regs->gregs[0]) { + /* Reset the PC to before the gUSA region, as computed from + R0 = region end, SP = -(region size), plus one more for the + insn that actually initializes SP to the region size. */ + regs->pc = regs->gregs[0] + regs->gregs[15] - 2; + + /* Reset the SP to the saved version in R1. */ + regs->gregs[15] = regs->gregs[1]; + } +} + +static void setup_sigcontext(struct target_sigcontext *sc, + CPUSH4State *regs, unsigned long mask) +{ + int i; + +#define COPY(x) __put_user(regs->x, &sc->sc_##x) + COPY(gregs[0]); COPY(gregs[1]); + COPY(gregs[2]); COPY(gregs[3]); + COPY(gregs[4]); COPY(gregs[5]); + COPY(gregs[6]); COPY(gregs[7]); + COPY(gregs[8]); COPY(gregs[9]); + COPY(gregs[10]); COPY(gregs[11]); + COPY(gregs[12]); COPY(gregs[13]); + COPY(gregs[14]); COPY(gregs[15]); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); +#undef COPY + + for (i=0; i<16; i++) { + __put_user(regs->fregs[i], &sc->sc_fpregs[i]); + } + __put_user(regs->fpscr, &sc->sc_fpscr); + __put_user(regs->fpul, &sc->sc_fpul); + + /* non-iBCS2 extensions.. */ + __put_user(mask, &sc->oldmask); +} + +static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc) +{ + int i; + +#define COPY(x) __get_user(regs->x, &sc->sc_##x) + COPY(gregs[0]); COPY(gregs[1]); + COPY(gregs[2]); COPY(gregs[3]); + COPY(gregs[4]); COPY(gregs[5]); + COPY(gregs[6]); COPY(gregs[7]); + COPY(gregs[8]); COPY(gregs[9]); + COPY(gregs[10]); COPY(gregs[11]); + COPY(gregs[12]); COPY(gregs[13]); + COPY(gregs[14]); COPY(gregs[15]); + COPY(gbr); COPY(mach); + COPY(macl); COPY(pr); + COPY(sr); COPY(pc); +#undef COPY + + for (i=0; i<16; i++) { + __get_user(regs->fregs[i], &sc->sc_fpregs[i]); + } + __get_user(regs->fpscr, &sc->sc_fpscr); + __get_user(regs->fpul, &sc->sc_fpul); + + regs->tra = -1; /* disable syscall checks */ + regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); +} + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUSH4State *regs) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + int i; + + unwind_gusa(regs); + + frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); + trace_user_setup_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + setup_sigcontext(&frame->sc, regs, set->sig[0]); + + for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { + __put_user(set->sig[i + 1], &frame->extramask[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + regs->pr = (unsigned long) ka->sa_restorer; + } else { + /* Generate return code (system call to sigreturn) */ + abi_ulong retcode_addr = frame_addr + + offsetof(struct target_sigframe, retcode); + __put_user(MOVW(2), &frame->retcode[0]); + __put_user(TRAP_NOARG, &frame->retcode[1]); + __put_user((TARGET_NR_sigreturn), &frame->retcode[2]); + regs->pr = (unsigned long) retcode_addr; + } + + /* Set up registers for signal handler */ + regs->gregs[15] = frame_addr; + regs->gregs[4] = sig; /* Arg for signal handler */ + regs->gregs[5] = 0; + regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc); + regs->pc = (unsigned long) ka->_sa_handler; + regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUSH4State *regs) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + int i; + + unwind_gusa(regs); + + frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); + trace_user_setup_rt_frame(regs, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, (unsigned long *)&frame->uc.tuc_link); + target_save_altstack(&frame->uc.tuc_stack, regs); + setup_sigcontext(&frame->uc.tuc_mcontext, + regs, set->sig[0]); + for(i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa_flags & TARGET_SA_RESTORER) { + regs->pr = (unsigned long) ka->sa_restorer; + } else { + /* Generate return code (system call to sigreturn) */ + abi_ulong retcode_addr = frame_addr + + offsetof(struct target_rt_sigframe, retcode); + __put_user(MOVW(2), &frame->retcode[0]); + __put_user(TRAP_NOARG, &frame->retcode[1]); + __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]); + regs->pr = (unsigned long) retcode_addr; + } + + /* Set up registers for signal handler */ + regs->gregs[15] = frame_addr; + regs->gregs[4] = sig; /* Arg for signal handler */ + regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info); + regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc); + regs->pc = (unsigned long) ka->_sa_handler; + regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + unlock_user_struct(frame, frame_addr, 1); + force_sigsegv(sig); +} + +long do_sigreturn(CPUSH4State *regs) +{ + struct target_sigframe *frame; + abi_ulong frame_addr; + sigset_t blocked; + target_sigset_t target_set; + int i; + int err = 0; + + frame_addr = regs->gregs[15]; + trace_user_do_sigreturn(regs, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + __get_user(target_set.sig[0], &frame->sc.oldmask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(target_set.sig[i], &frame->extramask[i - 1]); + } + + if (err) + goto badframe; + + target_to_host_sigset_internal(&blocked, &target_set); + set_sigmask(&blocked); + + restore_sigcontext(regs, &frame->sc); + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUSH4State *regs) +{ + struct target_rt_sigframe *frame; + abi_ulong frame_addr; + sigset_t blocked; + + frame_addr = regs->gregs[15]; + trace_user_do_rt_sigreturn(regs, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + + target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask); + set_sigmask(&blocked); + + restore_sigcontext(regs, &frame->uc.tuc_mcontext); + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(regs)) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/sh4/sockbits.h b/linux-user/sh4/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/sh4/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/sh4/target_cpu.h b/linux-user/sh4/target_cpu.h index 9d305d2833..1a647ddb98 100644 --- a/linux-user/sh4/target_cpu.h +++ b/linux-user/sh4/target_cpu.h @@ -32,4 +32,8 @@ static inline void cpu_set_tls(CPUSH4State *env, target_ulong newtls) env->gbr = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state) +{ + return state->gregs[15]; +} #endif diff --git a/linux-user/sh4/target_fcntl.h b/linux-user/sh4/target_fcntl.h new file mode 100644 index 0000000000..2622d95539 --- /dev/null +++ b/linux-user/sh4/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef SH4_TARGET_FCNTL_H +#define SH4_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/sh4/target_signal.h b/linux-user/sh4/target_signal.h index cbf23b6a31..434970a990 100644 --- a/linux-user/sh4/target_signal.h +++ b/linux-user/sh4/target_signal.h @@ -1,8 +1,6 @@ #ifndef SH4_TARGET_SIGNAL_H #define SH4_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,10 +19,7 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUSH4State *state) -{ - return state->gregs[15]; -} - +#include "../generic/signal.h" +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* SH4_TARGET_SIGNAL_H */ diff --git a/linux-user/signal-common.h b/linux-user/signal-common.h new file mode 100644 index 0000000000..51030a9306 --- /dev/null +++ b/linux-user/signal-common.h @@ -0,0 +1,50 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ + +#ifndef SIGNAL_COMMON_H +#define SIGNAL_COMMON_H +extern struct target_sigaltstack target_sigaltstack_used; + +int on_sig_stack(unsigned long sp); +int sas_ss_flags(unsigned long sp); +abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka); +void target_save_altstack(target_stack_t *uss, CPUArchState *env); + +static inline void target_sigemptyset(target_sigset_t *set) +{ + memset(set, 0, sizeof(*set)); +} + +void host_to_target_sigset_internal(target_sigset_t *d, + const sigset_t *s); +void target_to_host_sigset_internal(sigset_t *d, + const target_sigset_t *s); +void tswap_siginfo(target_siginfo_t *tinfo, + const target_siginfo_t *info); +void set_sigmask(const sigset_t *set); +void force_sig(int sig); +void force_sigsegv(int oldsig); +#if defined(TARGET_ARCH_HAS_SETUP_FRAME) +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUArchState *env); +#endif +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env); +#endif diff --git a/linux-user/signal.c b/linux-user/signal.c index b283270391..602b631b92 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -23,10 +23,10 @@ #include "qemu.h" #include "qemu-common.h" -#include "target_signal.h" #include "trace.h" +#include "signal-common.h" -static struct target_sigaltstack target_sigaltstack_used = { +struct target_sigaltstack target_sigaltstack_used = { .ss_sp = 0, .ss_size = 0, .ss_flags = TARGET_SS_DISABLE, @@ -82,18 +82,6 @@ static uint8_t host_to_target_signal_table[_NSIG] = { }; static uint8_t target_to_host_signal_table[_NSIG]; -static inline int on_sig_stack(unsigned long sp) -{ - return (sp - target_sigaltstack_used.ss_sp - < target_sigaltstack_used.ss_size); -} - -static inline int sas_ss_flags(unsigned long sp) -{ - return (target_sigaltstack_used.ss_size == 0 ? SS_DISABLE - : on_sig_stack(sp) ? SS_ONSTACK : 0); -} - int host_to_target_signal(int sig) { if (sig < 0 || sig >= _NSIG) @@ -108,11 +96,6 @@ int target_to_host_signal(int sig) return target_to_host_signal_table[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--; @@ -127,8 +110,8 @@ static inline int target_sigismember(const target_sigset_t *set, int signum) return ((set->sig[signum / TARGET_NSIG_BPW] & mask) != 0); } -static void host_to_target_sigset_internal(target_sigset_t *d, - const sigset_t *s) +void host_to_target_sigset_internal(target_sigset_t *d, + const sigset_t *s) { int i; target_sigemptyset(d); @@ -149,8 +132,8 @@ void host_to_target_sigset(target_sigset_t *d, const sigset_t *s) d->sig[i] = tswapal(d1.sig[i]); } -static void target_to_host_sigset_internal(sigset_t *d, - const target_sigset_t *s) +void target_to_host_sigset_internal(sigset_t *d, + const target_sigset_t *s) { int i; sigemptyset(d); @@ -253,11 +236,11 @@ int do_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) return 0; } -#if !defined(TARGET_OPENRISC) && !defined(TARGET_NIOS2) +#if !defined(TARGET_NIOS2) /* Just set the guest's signal mask to the specified value; the * caller is assumed to have called block_signals() already. */ -static void set_sigmask(const sigset_t *set) +void set_sigmask(const sigset_t *set) { TaskState *ts = (TaskState *)thread_cpu->opaque; @@ -265,6 +248,38 @@ static void set_sigmask(const sigset_t *set) } #endif +/* sigaltstack management */ + +int on_sig_stack(unsigned long sp) +{ + return (sp - target_sigaltstack_used.ss_sp + < target_sigaltstack_used.ss_size); +} + +int sas_ss_flags(unsigned long sp) +{ + return (target_sigaltstack_used.ss_size == 0 ? SS_DISABLE + : on_sig_stack(sp) ? SS_ONSTACK : 0); +} + +abi_ulong target_sigsp(abi_ulong sp, struct target_sigaction *ka) +{ + /* + * This is the X/Open sanctioned signal stack switching. + */ + if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { + return target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + return sp; +} + +void target_save_altstack(target_stack_t *uss, CPUArchState *env) +{ + __put_user(target_sigaltstack_used.ss_sp, &uss->ss_sp); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &uss->ss_flags); + __put_user(target_sigaltstack_used.ss_size, &uss->ss_size); +} + /* siginfo conversion */ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, @@ -344,8 +359,8 @@ static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo, tinfo->si_code = deposit32(si_code, 16, 16, si_type); } -static void tswap_siginfo(target_siginfo_t *tinfo, - const target_siginfo_t *info) +void tswap_siginfo(target_siginfo_t *tinfo, + const target_siginfo_t *info) { int si_type = extract32(info->si_code, 16, 16); int si_code = sextract32(info->si_code, 0, 16); @@ -515,7 +530,7 @@ void signal_init(void) * also forces the signal to "not blocked, not ignored", but for QEMU * that work is done in process_pending_signals(). */ -static void force_sig(int sig) +void force_sig(int sig) { CPUState *cpu = thread_cpu; CPUArchState *env = cpu->env_ptr; @@ -534,7 +549,7 @@ static void force_sig(int sig) * at the point of failure. */ #if !defined(TARGET_RISCV) -static void force_sigsegv(int oldsig) +void force_sigsegv(int oldsig) { if (oldsig == SIGSEGV) { /* Make sure we don't try to deliver the signal again; this will @@ -819,6493 +834,6 @@ int do_sigaction(int sig, const struct target_sigaction *act, return ret; } -#if defined(TARGET_I386) -/* from the Linux kernel - /arch/x86/include/uapi/asm/sigcontext.h */ - -struct target_fpreg { - uint16_t significand[4]; - uint16_t exponent; -}; - -struct target_fpxreg { - uint16_t significand[4]; - uint16_t exponent; - uint16_t padding[3]; -}; - -struct target_xmmreg { - uint32_t element[4]; -}; - -struct target_fpstate_32 { - /* Regular FPU environment */ - uint32_t cw; - uint32_t sw; - uint32_t tag; - uint32_t ipoff; - uint32_t cssel; - uint32_t dataoff; - uint32_t datasel; - struct target_fpreg st[8]; - uint16_t status; - uint16_t magic; /* 0xffff = regular FPU data only */ - - /* FXSR FPU environment */ - uint32_t _fxsr_env[6]; /* FXSR FPU env is ignored */ - uint32_t mxcsr; - uint32_t reserved; - struct target_fpxreg fxsr_st[8]; /* FXSR FPU reg data is ignored */ - struct target_xmmreg xmm[8]; - uint32_t padding[56]; -}; - -struct target_fpstate_64 { - /* FXSAVE format */ - uint16_t cw; - uint16_t sw; - uint16_t twd; - uint16_t fop; - uint64_t rip; - uint64_t rdp; - uint32_t mxcsr; - uint32_t mxcsr_mask; - uint32_t st_space[32]; - uint32_t xmm_space[64]; - uint32_t reserved[24]; -}; - -#ifndef TARGET_X86_64 -# define target_fpstate target_fpstate_32 -#else -# define target_fpstate target_fpstate_64 -#endif - -struct target_sigcontext_32 { - uint16_t gs, __gsh; - uint16_t fs, __fsh; - uint16_t es, __esh; - uint16_t ds, __dsh; - uint32_t edi; - uint32_t esi; - uint32_t ebp; - uint32_t esp; - uint32_t ebx; - uint32_t edx; - uint32_t ecx; - uint32_t eax; - uint32_t trapno; - uint32_t err; - uint32_t eip; - uint16_t cs, __csh; - uint32_t eflags; - uint32_t esp_at_signal; - uint16_t ss, __ssh; - uint32_t fpstate; /* pointer */ - uint32_t oldmask; - uint32_t cr2; -}; - -struct target_sigcontext_64 { - uint64_t r8; - uint64_t r9; - uint64_t r10; - uint64_t r11; - uint64_t r12; - uint64_t r13; - uint64_t r14; - uint64_t r15; - - uint64_t rdi; - uint64_t rsi; - uint64_t rbp; - uint64_t rbx; - uint64_t rdx; - uint64_t rax; - uint64_t rcx; - uint64_t rsp; - uint64_t rip; - - uint64_t eflags; - - uint16_t cs; - uint16_t gs; - uint16_t fs; - uint16_t ss; - - uint64_t err; - uint64_t trapno; - uint64_t oldmask; - uint64_t cr2; - - uint64_t fpstate; /* pointer */ - uint64_t padding[8]; -}; - -#ifndef TARGET_X86_64 -# define target_sigcontext target_sigcontext_32 -#else -# define target_sigcontext target_sigcontext_64 -#endif - -/* see Linux/include/uapi/asm-generic/ucontext.h */ -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -#ifndef TARGET_X86_64 -struct sigframe { - abi_ulong pretcode; - int sig; - struct target_sigcontext sc; - struct target_fpstate fpstate; - abi_ulong extramask[TARGET_NSIG_WORDS-1]; - char retcode[8]; -}; - -struct rt_sigframe { - abi_ulong pretcode; - int sig; - abi_ulong pinfo; - abi_ulong puc; - struct target_siginfo info; - struct target_ucontext uc; - struct target_fpstate fpstate; - char retcode[8]; -}; - -#else - -struct rt_sigframe { - abi_ulong pretcode; - struct target_ucontext uc; - struct target_siginfo info; - struct target_fpstate fpstate; -}; - -#endif - -/* - * Set up a signal frame. - */ - -/* XXX: save x87 state */ -static void setup_sigcontext(struct target_sigcontext *sc, - struct target_fpstate *fpstate, CPUX86State *env, abi_ulong mask, - abi_ulong fpstate_addr) -{ - CPUState *cs = CPU(x86_env_get_cpu(env)); -#ifndef TARGET_X86_64 - uint16_t magic; - - /* already locked in setup_frame() */ - __put_user(env->segs[R_GS].selector, (unsigned int *)&sc->gs); - __put_user(env->segs[R_FS].selector, (unsigned int *)&sc->fs); - __put_user(env->segs[R_ES].selector, (unsigned int *)&sc->es); - __put_user(env->segs[R_DS].selector, (unsigned int *)&sc->ds); - __put_user(env->regs[R_EDI], &sc->edi); - __put_user(env->regs[R_ESI], &sc->esi); - __put_user(env->regs[R_EBP], &sc->ebp); - __put_user(env->regs[R_ESP], &sc->esp); - __put_user(env->regs[R_EBX], &sc->ebx); - __put_user(env->regs[R_EDX], &sc->edx); - __put_user(env->regs[R_ECX], &sc->ecx); - __put_user(env->regs[R_EAX], &sc->eax); - __put_user(cs->exception_index, &sc->trapno); - __put_user(env->error_code, &sc->err); - __put_user(env->eip, &sc->eip); - __put_user(env->segs[R_CS].selector, (unsigned int *)&sc->cs); - __put_user(env->eflags, &sc->eflags); - __put_user(env->regs[R_ESP], &sc->esp_at_signal); - __put_user(env->segs[R_SS].selector, (unsigned int *)&sc->ss); - - cpu_x86_fsave(env, fpstate_addr, 1); - fpstate->status = fpstate->sw; - magic = 0xffff; - __put_user(magic, &fpstate->magic); - __put_user(fpstate_addr, &sc->fpstate); - - /* non-iBCS2 extensions.. */ - __put_user(mask, &sc->oldmask); - __put_user(env->cr[2], &sc->cr2); -#else - __put_user(env->regs[R_EDI], &sc->rdi); - __put_user(env->regs[R_ESI], &sc->rsi); - __put_user(env->regs[R_EBP], &sc->rbp); - __put_user(env->regs[R_ESP], &sc->rsp); - __put_user(env->regs[R_EBX], &sc->rbx); - __put_user(env->regs[R_EDX], &sc->rdx); - __put_user(env->regs[R_ECX], &sc->rcx); - __put_user(env->regs[R_EAX], &sc->rax); - - __put_user(env->regs[8], &sc->r8); - __put_user(env->regs[9], &sc->r9); - __put_user(env->regs[10], &sc->r10); - __put_user(env->regs[11], &sc->r11); - __put_user(env->regs[12], &sc->r12); - __put_user(env->regs[13], &sc->r13); - __put_user(env->regs[14], &sc->r14); - __put_user(env->regs[15], &sc->r15); - - __put_user(cs->exception_index, &sc->trapno); - __put_user(env->error_code, &sc->err); - __put_user(env->eip, &sc->rip); - - __put_user(env->eflags, &sc->eflags); - __put_user(env->segs[R_CS].selector, &sc->cs); - __put_user((uint16_t)0, &sc->gs); - __put_user((uint16_t)0, &sc->fs); - __put_user(env->segs[R_SS].selector, &sc->ss); - - __put_user(mask, &sc->oldmask); - __put_user(env->cr[2], &sc->cr2); - - /* fpstate_addr must be 16 byte aligned for fxsave */ - assert(!(fpstate_addr & 0xf)); - - cpu_x86_fxsave(env, fpstate_addr); - __put_user(fpstate_addr, &sc->fpstate); -#endif -} - -/* - * Determine which stack to use.. - */ - -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUX86State *env, size_t frame_size) -{ - unsigned long esp; - - /* Default to using normal stack */ - esp = env->regs[R_ESP]; -#ifdef TARGET_X86_64 - esp -= 128; /* this is the redzone */ -#endif - - /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa_flags & TARGET_SA_ONSTACK) { - if (sas_ss_flags(esp) == 0) { - esp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - } else { -#ifndef TARGET_X86_64 - /* This is the legacy signal stack switching. */ - if ((env->segs[R_SS].selector & 0xffff) != __USER_DS && - !(ka->sa_flags & TARGET_SA_RESTORER) && - ka->sa_restorer) { - esp = (unsigned long) ka->sa_restorer; - } -#endif - } - -#ifndef TARGET_X86_64 - return (esp - frame_size) & -8ul; -#else - return ((esp - frame_size) & (~15ul)) - 8; -#endif -} - -#ifndef TARGET_X86_64 -/* compare linux/arch/i386/kernel/signal.c:setup_frame() */ -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUX86State *env) -{ - abi_ulong frame_addr; - struct sigframe *frame; - 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)) - goto give_sigsegv; - - __put_user(sig, &frame->sig); - - setup_sigcontext(&frame->sc, &frame->fpstate, env, set->sig[0], - frame_addr + offsetof(struct sigframe, fpstate)); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - __put_user(ka->sa_restorer, &frame->pretcode); - } else { - uint16_t val16; - abi_ulong retcode_addr; - retcode_addr = frame_addr + offsetof(struct sigframe, retcode); - __put_user(retcode_addr, &frame->pretcode); - /* This is popl %eax ; movl $,%eax ; int $0x80 */ - val16 = 0xb858; - __put_user(val16, (uint16_t *)(frame->retcode+0)); - __put_user(TARGET_NR_sigreturn, (int *)(frame->retcode+2)); - val16 = 0x80cd; - __put_user(val16, (uint16_t *)(frame->retcode+6)); - } - - /* Set up registers for signal handler */ - env->regs[R_ESP] = frame_addr; - env->eip = ka->_sa_handler; - - cpu_x86_load_seg(env, R_DS, __USER_DS); - cpu_x86_load_seg(env, R_ES, __USER_DS); - cpu_x86_load_seg(env, R_SS, __USER_DS); - cpu_x86_load_seg(env, R_CS, __USER_CS); - env->eflags &= ~TF_MASK; - - unlock_user_struct(frame, frame_addr, 1); - - return; - -give_sigsegv: - force_sigsegv(sig); -} -#endif - -/* compare linux/arch/x86/kernel/signal.c:setup_rt_frame() */ -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUX86State *env) -{ - abi_ulong frame_addr; -#ifndef TARGET_X86_64 - abi_ulong addr; -#endif - struct rt_sigframe *frame; - int i; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto give_sigsegv; - - /* These fields are only in rt_sigframe on 32 bit */ -#ifndef TARGET_X86_64 - __put_user(sig, &frame->sig); - addr = frame_addr + offsetof(struct rt_sigframe, info); - __put_user(addr, &frame->pinfo); - addr = frame_addr + offsetof(struct rt_sigframe, uc); - __put_user(addr, &frame->puc); -#endif - if (ka->sa_flags & TARGET_SA_SIGINFO) { - tswap_siginfo(&frame->info, info); - } - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->uc.tuc_mcontext, &frame->fpstate, env, - set->sig[0], frame_addr + offsetof(struct rt_sigframe, fpstate)); - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ -#ifndef TARGET_X86_64 - if (ka->sa_flags & TARGET_SA_RESTORER) { - __put_user(ka->sa_restorer, &frame->pretcode); - } else { - uint16_t val16; - addr = frame_addr + offsetof(struct rt_sigframe, retcode); - __put_user(addr, &frame->pretcode); - /* This is movl $,%eax ; int $0x80 */ - __put_user(0xb8, (char *)(frame->retcode+0)); - __put_user(TARGET_NR_rt_sigreturn, (int *)(frame->retcode+1)); - val16 = 0x80cd; - __put_user(val16, (uint16_t *)(frame->retcode+5)); - } -#else - /* XXX: Would be slightly better to return -EFAULT here if test fails - assert(ka->sa_flags & TARGET_SA_RESTORER); */ - __put_user(ka->sa_restorer, &frame->pretcode); -#endif - - /* Set up registers for signal handler */ - env->regs[R_ESP] = frame_addr; - env->eip = ka->_sa_handler; - -#ifndef TARGET_X86_64 - env->regs[R_EAX] = sig; - env->regs[R_EDX] = (unsigned long)&frame->info; - env->regs[R_ECX] = (unsigned long)&frame->uc; -#else - env->regs[R_EAX] = 0; - env->regs[R_EDI] = sig; - env->regs[R_ESI] = (unsigned long)&frame->info; - env->regs[R_EDX] = (unsigned long)&frame->uc; -#endif - - cpu_x86_load_seg(env, R_DS, __USER_DS); - cpu_x86_load_seg(env, R_ES, __USER_DS); - cpu_x86_load_seg(env, R_CS, __USER_CS); - cpu_x86_load_seg(env, R_SS, __USER_DS); - env->eflags &= ~TF_MASK; - - unlock_user_struct(frame, frame_addr, 1); - - return; - -give_sigsegv: - force_sigsegv(sig); -} - -static int -restore_sigcontext(CPUX86State *env, struct target_sigcontext *sc) -{ - unsigned int err = 0; - abi_ulong fpstate_addr; - unsigned int tmpflags; - -#ifndef TARGET_X86_64 - cpu_x86_load_seg(env, R_GS, tswap16(sc->gs)); - cpu_x86_load_seg(env, R_FS, tswap16(sc->fs)); - cpu_x86_load_seg(env, R_ES, tswap16(sc->es)); - cpu_x86_load_seg(env, R_DS, tswap16(sc->ds)); - - env->regs[R_EDI] = tswapl(sc->edi); - env->regs[R_ESI] = tswapl(sc->esi); - env->regs[R_EBP] = tswapl(sc->ebp); - env->regs[R_ESP] = tswapl(sc->esp); - env->regs[R_EBX] = tswapl(sc->ebx); - env->regs[R_EDX] = tswapl(sc->edx); - env->regs[R_ECX] = tswapl(sc->ecx); - env->regs[R_EAX] = tswapl(sc->eax); - - env->eip = tswapl(sc->eip); -#else - env->regs[8] = tswapl(sc->r8); - env->regs[9] = tswapl(sc->r9); - env->regs[10] = tswapl(sc->r10); - env->regs[11] = tswapl(sc->r11); - env->regs[12] = tswapl(sc->r12); - env->regs[13] = tswapl(sc->r13); - env->regs[14] = tswapl(sc->r14); - env->regs[15] = tswapl(sc->r15); - - env->regs[R_EDI] = tswapl(sc->rdi); - env->regs[R_ESI] = tswapl(sc->rsi); - env->regs[R_EBP] = tswapl(sc->rbp); - env->regs[R_EBX] = tswapl(sc->rbx); - env->regs[R_EDX] = tswapl(sc->rdx); - env->regs[R_EAX] = tswapl(sc->rax); - env->regs[R_ECX] = tswapl(sc->rcx); - env->regs[R_ESP] = tswapl(sc->rsp); - - env->eip = tswapl(sc->rip); -#endif - - cpu_x86_load_seg(env, R_CS, lduw_p(&sc->cs) | 3); - cpu_x86_load_seg(env, R_SS, lduw_p(&sc->ss) | 3); - - tmpflags = tswapl(sc->eflags); - env->eflags = (env->eflags & ~0x40DD5) | (tmpflags & 0x40DD5); - // regs->orig_eax = -1; /* disable syscall checks */ - - fpstate_addr = tswapl(sc->fpstate); - if (fpstate_addr != 0) { - if (!access_ok(VERIFY_READ, fpstate_addr, - sizeof(struct target_fpstate))) - goto badframe; -#ifndef TARGET_X86_64 - cpu_x86_frstor(env, fpstate_addr, 1); -#else - cpu_x86_fxrstor(env, fpstate_addr); -#endif - } - - return err; -badframe: - return 1; -} - -/* Note: there is no sigreturn on x86_64, there is only rt_sigreturn */ -#ifndef TARGET_X86_64 -long do_sigreturn(CPUX86State *env) -{ - struct sigframe *frame; - abi_ulong frame_addr = env->regs[R_ESP] - 8; - target_sigset_t target_set; - sigset_t set; - int i; - - trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - /* set blocked signals */ - __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - /* restore registers */ - if (restore_sigcontext(env, &frame->sc)) - goto badframe; - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} -#endif - -long do_rt_sigreturn(CPUX86State *env) -{ - abi_ulong frame_addr; - struct rt_sigframe *frame; - sigset_t set; - - frame_addr = env->regs[R_ESP] - sizeof(abi_ulong); - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { - goto badframe; - } - - if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe, uc.tuc_stack), 0, - get_sp_from_cpustate(env)) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_AARCH64) - -struct target_sigcontext { - uint64_t fault_address; - /* AArch64 registers */ - uint64_t regs[31]; - uint64_t sp; - uint64_t pc; - uint64_t pstate; - /* 4K reserved for FP/SIMD state and future expansion */ - char __reserved[4096] __attribute__((__aligned__(16))); -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - target_sigset_t tuc_sigmask; - /* glibc uses a 1024-bit sigset_t */ - char __unused[1024 / 8 - sizeof(target_sigset_t)]; - /* last for future expansion */ - struct target_sigcontext tuc_mcontext; -}; - -/* - * Header to be used at the beginning of structures extending the user - * context. Such structures must be placed after the rt_sigframe on the stack - * and be 16-byte aligned. The last structure must be a dummy one with the - * magic and size set to 0. - */ -struct target_aarch64_ctx { - uint32_t magic; - uint32_t size; -}; - -#define TARGET_FPSIMD_MAGIC 0x46508001 - -struct target_fpsimd_context { - struct target_aarch64_ctx head; - uint32_t fpsr; - uint32_t fpcr; - uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */ -}; - -#define TARGET_EXTRA_MAGIC 0x45585401 - -struct target_extra_context { - struct target_aarch64_ctx head; - uint64_t datap; /* 16-byte aligned pointer to extra space cast to __u64 */ - uint32_t size; /* size in bytes of the extra space */ - uint32_t reserved[3]; -}; - -#define TARGET_SVE_MAGIC 0x53564501 - -struct target_sve_context { - struct target_aarch64_ctx head; - uint16_t vl; - uint16_t reserved[3]; - /* The actual SVE data immediately follows. It is layed out - * according to TARGET_SVE_SIG_{Z,P}REG_OFFSET, based off of - * the original struct pointer. - */ -}; - -#define TARGET_SVE_VQ_BYTES 16 - -#define TARGET_SVE_SIG_ZREG_SIZE(VQ) ((VQ) * TARGET_SVE_VQ_BYTES) -#define TARGET_SVE_SIG_PREG_SIZE(VQ) ((VQ) * (TARGET_SVE_VQ_BYTES / 8)) - -#define TARGET_SVE_SIG_REGS_OFFSET \ - QEMU_ALIGN_UP(sizeof(struct target_sve_context), TARGET_SVE_VQ_BYTES) -#define TARGET_SVE_SIG_ZREG_OFFSET(VQ, N) \ - (TARGET_SVE_SIG_REGS_OFFSET + TARGET_SVE_SIG_ZREG_SIZE(VQ) * (N)) -#define TARGET_SVE_SIG_PREG_OFFSET(VQ, N) \ - (TARGET_SVE_SIG_ZREG_OFFSET(VQ, 32) + TARGET_SVE_SIG_PREG_SIZE(VQ) * (N)) -#define TARGET_SVE_SIG_FFR_OFFSET(VQ) \ - (TARGET_SVE_SIG_PREG_OFFSET(VQ, 16)) -#define TARGET_SVE_SIG_CONTEXT_SIZE(VQ) \ - (TARGET_SVE_SIG_PREG_OFFSET(VQ, 17)) - -struct target_rt_sigframe { - struct target_siginfo info; - struct target_ucontext uc; -}; - -struct target_rt_frame_record { - uint64_t fp; - uint64_t lr; - uint32_t tramp[2]; -}; - -static void target_setup_general_frame(struct target_rt_sigframe *sf, - CPUARMState *env, target_sigset_t *set) -{ - int i; - - __put_user(0, &sf->uc.tuc_flags); - __put_user(0, &sf->uc.tuc_link); - - __put_user(target_sigaltstack_used.ss_sp, &sf->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->xregs[31]), &sf->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &sf->uc.tuc_stack.ss_size); - - for (i = 0; i < 31; i++) { - __put_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); - } - __put_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); - __put_user(env->pc, &sf->uc.tuc_mcontext.pc); - __put_user(pstate_read(env), &sf->uc.tuc_mcontext.pstate); - - __put_user(env->exception.vaddress, &sf->uc.tuc_mcontext.fault_address); - - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &sf->uc.tuc_sigmask.sig[i]); - } -} - -static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd, - CPUARMState *env) -{ - int i; - - __put_user(TARGET_FPSIMD_MAGIC, &fpsimd->head.magic); - __put_user(sizeof(struct target_fpsimd_context), &fpsimd->head.size); - __put_user(vfp_get_fpsr(env), &fpsimd->fpsr); - __put_user(vfp_get_fpcr(env), &fpsimd->fpcr); - - for (i = 0; i < 32; i++) { - uint64_t *q = aa64_vfp_qreg(env, i); -#ifdef TARGET_WORDS_BIGENDIAN - __put_user(q[0], &fpsimd->vregs[i * 2 + 1]); - __put_user(q[1], &fpsimd->vregs[i * 2]); -#else - __put_user(q[0], &fpsimd->vregs[i * 2]); - __put_user(q[1], &fpsimd->vregs[i * 2 + 1]); -#endif - } -} - -static void target_setup_extra_record(struct target_extra_context *extra, - uint64_t datap, uint32_t extra_size) -{ - __put_user(TARGET_EXTRA_MAGIC, &extra->head.magic); - __put_user(sizeof(struct target_extra_context), &extra->head.size); - __put_user(datap, &extra->datap); - __put_user(extra_size, &extra->size); -} - -static void target_setup_end_record(struct target_aarch64_ctx *end) -{ - __put_user(0, &end->magic); - __put_user(0, &end->size); -} - -static void target_setup_sve_record(struct target_sve_context *sve, - CPUARMState *env, int vq, int size) -{ - int i, j; - - __put_user(TARGET_SVE_MAGIC, &sve->head.magic); - __put_user(size, &sve->head.size); - __put_user(vq * TARGET_SVE_VQ_BYTES, &sve->vl); - - /* Note that SVE regs are stored as a byte stream, with each byte element - * at a subsequent address. This corresponds to a little-endian store - * of our 64-bit hunks. - */ - for (i = 0; i < 32; ++i) { - uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); - for (j = 0; j < vq * 2; ++j) { - __put_user_e(env->vfp.zregs[i].d[j], z + j, le); - } - } - for (i = 0; i <= 16; ++i) { - uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); - for (j = 0; j < vq; ++j) { - uint64_t r = env->vfp.pregs[i].p[j >> 2]; - __put_user_e(r >> ((j & 3) * 16), p + j, le); - } - } -} - -static void target_restore_general_frame(CPUARMState *env, - struct target_rt_sigframe *sf) -{ - sigset_t set; - uint64_t pstate; - int i; - - target_to_host_sigset(&set, &sf->uc.tuc_sigmask); - set_sigmask(&set); - - for (i = 0; i < 31; i++) { - __get_user(env->xregs[i], &sf->uc.tuc_mcontext.regs[i]); - } - - __get_user(env->xregs[31], &sf->uc.tuc_mcontext.sp); - __get_user(env->pc, &sf->uc.tuc_mcontext.pc); - __get_user(pstate, &sf->uc.tuc_mcontext.pstate); - pstate_write(env, pstate); -} - -static void target_restore_fpsimd_record(CPUARMState *env, - struct target_fpsimd_context *fpsimd) -{ - uint32_t fpsr, fpcr; - int i; - - __get_user(fpsr, &fpsimd->fpsr); - vfp_set_fpsr(env, fpsr); - __get_user(fpcr, &fpsimd->fpcr); - vfp_set_fpcr(env, fpcr); - - for (i = 0; i < 32; i++) { - uint64_t *q = aa64_vfp_qreg(env, i); -#ifdef TARGET_WORDS_BIGENDIAN - __get_user(q[0], &fpsimd->vregs[i * 2 + 1]); - __get_user(q[1], &fpsimd->vregs[i * 2]); -#else - __get_user(q[0], &fpsimd->vregs[i * 2]); - __get_user(q[1], &fpsimd->vregs[i * 2 + 1]); -#endif - } -} - -static void target_restore_sve_record(CPUARMState *env, - struct target_sve_context *sve, int vq) -{ - int i, j; - - /* Note that SVE regs are stored as a byte stream, with each byte element - * at a subsequent address. This corresponds to a little-endian load - * of our 64-bit hunks. - */ - for (i = 0; i < 32; ++i) { - uint64_t *z = (void *)sve + TARGET_SVE_SIG_ZREG_OFFSET(vq, i); - for (j = 0; j < vq * 2; ++j) { - __get_user_e(env->vfp.zregs[i].d[j], z + j, le); - } - } - for (i = 0; i <= 16; ++i) { - uint16_t *p = (void *)sve + TARGET_SVE_SIG_PREG_OFFSET(vq, i); - for (j = 0; j < vq; ++j) { - uint16_t r; - __get_user_e(r, p + j, le); - if (j & 3) { - env->vfp.pregs[i].p[j >> 2] |= (uint64_t)r << ((j & 3) * 16); - } else { - env->vfp.pregs[i].p[j >> 2] = r; - } - } - } -} - -static int target_restore_sigframe(CPUARMState *env, - struct target_rt_sigframe *sf) -{ - struct target_aarch64_ctx *ctx, *extra = NULL; - struct target_fpsimd_context *fpsimd = NULL; - struct target_sve_context *sve = NULL; - uint64_t extra_datap = 0; - bool used_extra = false; - bool err = false; - int vq = 0, sve_size = 0; - - target_restore_general_frame(env, sf); - - ctx = (struct target_aarch64_ctx *)sf->uc.tuc_mcontext.__reserved; - while (ctx) { - uint32_t magic, size, extra_size; - - __get_user(magic, &ctx->magic); - __get_user(size, &ctx->size); - switch (magic) { - case 0: - if (size != 0) { - err = true; - goto exit; - } - if (used_extra) { - ctx = NULL; - } else { - ctx = extra; - used_extra = true; - } - continue; - - case TARGET_FPSIMD_MAGIC: - if (fpsimd || size != sizeof(struct target_fpsimd_context)) { - err = true; - goto exit; - } - fpsimd = (struct target_fpsimd_context *)ctx; - break; - - case TARGET_SVE_MAGIC: - if (arm_feature(env, ARM_FEATURE_SVE)) { - vq = (env->vfp.zcr_el[1] & 0xf) + 1; - sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); - if (!sve && size == sve_size) { - sve = (struct target_sve_context *)ctx; - break; - } - } - err = true; - goto exit; - - case TARGET_EXTRA_MAGIC: - if (extra || size != sizeof(struct target_extra_context)) { - err = true; - goto exit; - } - __get_user(extra_datap, - &((struct target_extra_context *)ctx)->datap); - __get_user(extra_size, - &((struct target_extra_context *)ctx)->size); - extra = lock_user(VERIFY_READ, extra_datap, extra_size, 0); - break; - - default: - /* Unknown record -- we certainly didn't generate it. - * Did we in fact get out of sync? - */ - err = true; - goto exit; - } - ctx = (void *)ctx + size; - } - - /* Require FPSIMD always. */ - if (fpsimd) { - target_restore_fpsimd_record(env, fpsimd); - } else { - err = true; - } - - /* SVE data, if present, overwrites FPSIMD data. */ - if (sve) { - target_restore_sve_record(env, sve, vq); - } - - exit: - unlock_user(extra, extra_datap, 0); - return err; -} - -static abi_ulong get_sigframe(struct target_sigaction *ka, - CPUARMState *env, int size) -{ - abi_ulong sp; - - sp = env->xregs[31]; - - /* - * This is the X/Open sanctioned signal stack switching. - */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp = (sp - size) & ~15; - - return sp; -} - -typedef struct { - int total_size; - int extra_base; - int extra_size; - int std_end_ofs; - int extra_ofs; - int extra_end_ofs; -} target_sigframe_layout; - -static int alloc_sigframe_space(int this_size, target_sigframe_layout *l) -{ - /* Make sure there will always be space for the end marker. */ - const int std_size = sizeof(struct target_rt_sigframe) - - sizeof(struct target_aarch64_ctx); - int this_loc = l->total_size; - - if (l->extra_base) { - /* Once we have begun an extra space, all allocations go there. */ - l->extra_size += this_size; - } else if (this_size + this_loc > std_size) { - /* This allocation does not fit in the standard space. */ - /* Allocate the extra record. */ - l->extra_ofs = this_loc; - l->total_size += sizeof(struct target_extra_context); - - /* Allocate the standard end record. */ - l->std_end_ofs = l->total_size; - l->total_size += sizeof(struct target_aarch64_ctx); - - /* Allocate the requested record. */ - l->extra_base = this_loc = l->total_size; - l->extra_size = this_size; - } - l->total_size += this_size; - - return this_loc; -} - -static void target_setup_frame(int usig, struct target_sigaction *ka, - target_siginfo_t *info, target_sigset_t *set, - CPUARMState *env) -{ - target_sigframe_layout layout = { - /* Begin with the size pointing to the reserved space. */ - .total_size = offsetof(struct target_rt_sigframe, - uc.tuc_mcontext.__reserved), - }; - int fpsimd_ofs, fr_ofs, sve_ofs = 0, vq = 0, sve_size = 0; - struct target_rt_sigframe *frame; - struct target_rt_frame_record *fr; - abi_ulong frame_addr, return_addr; - - /* FPSIMD record is always in the standard space. */ - fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context), - &layout); - - /* SVE state needs saving only if it exists. */ - if (arm_feature(env, ARM_FEATURE_SVE)) { - vq = (env->vfp.zcr_el[1] & 0xf) + 1; - sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); - sve_ofs = alloc_sigframe_space(sve_size, &layout); - } - - if (layout.extra_ofs) { - /* Reserve space for the extra end marker. The standard end marker - * will have been allocated when we allocated the extra record. - */ - layout.extra_end_ofs - = alloc_sigframe_space(sizeof(struct target_aarch64_ctx), &layout); - } else { - /* Reserve space for the standard end marker. - * Do not use alloc_sigframe_space because we cheat - * std_size therein to reserve space for this. - */ - layout.std_end_ofs = layout.total_size; - layout.total_size += sizeof(struct target_aarch64_ctx); - } - - /* We must always provide at least the standard 4K reserved space, - * even if we don't use all of it (this is part of the ABI) - */ - layout.total_size = MAX(layout.total_size, - sizeof(struct target_rt_sigframe)); - - /* Reserve space for the return code. On a real system this would - * be within the VDSO. So, despite the name this is not a "real" - * record within the frame. - */ - fr_ofs = layout.total_size; - layout.total_size += sizeof(struct target_rt_frame_record); - - frame_addr = get_sigframe(ka, env, layout.total_size); - trace_user_setup_frame(env, frame_addr); - frame = lock_user(VERIFY_WRITE, frame_addr, layout.total_size, 0); - if (!frame) { - goto give_sigsegv; - } - - target_setup_general_frame(frame, env, set); - target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env); - target_setup_end_record((void *)frame + layout.std_end_ofs); - if (layout.extra_ofs) { - target_setup_extra_record((void *)frame + layout.extra_ofs, - frame_addr + layout.extra_base, - layout.extra_size); - target_setup_end_record((void *)frame + layout.extra_end_ofs); - } - if (sve_ofs) { - target_setup_sve_record((void *)frame + sve_ofs, env, vq, sve_size); - } - - /* Set up the stack frame for unwinding. */ - fr = (void *)frame + fr_ofs; - __put_user(env->xregs[29], &fr->fp); - __put_user(env->xregs[30], &fr->lr); - - if (ka->sa_flags & TARGET_SA_RESTORER) { - return_addr = ka->sa_restorer; - } else { - /* - * mov x8,#__NR_rt_sigreturn; svc #0 - * Since these are instructions they need to be put as little-endian - * regardless of target default or current CPU endianness. - */ - __put_user_e(0xd2801168, &fr->tramp[0], le); - __put_user_e(0xd4000001, &fr->tramp[1], le); - return_addr = frame_addr + fr_ofs - + offsetof(struct target_rt_frame_record, tramp); - } - env->xregs[0] = usig; - env->xregs[31] = frame_addr; - env->xregs[29] = frame_addr + fr_ofs; - env->pc = ka->_sa_handler; - env->xregs[30] = return_addr; - if (info) { - tswap_siginfo(&frame->info, info); - env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->xregs[2] = frame_addr + offsetof(struct target_rt_sigframe, uc); - } - - unlock_user(frame, frame_addr, layout.total_size); - return; - - give_sigsegv: - unlock_user(frame, frame_addr, layout.total_size); - force_sigsegv(usig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, target_sigset_t *set, - CPUARMState *env) -{ - target_setup_frame(sig, ka, info, set, env); -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *env) -{ - target_setup_frame(sig, ka, 0, set, env); -} - -long do_rt_sigreturn(CPUARMState *env) -{ - struct target_rt_sigframe *frame = NULL; - abi_ulong frame_addr = env->xregs[31]; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (frame_addr & 15) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - if (target_restore_sigframe(env, frame)) { - goto badframe; - } - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, get_sp_from_cpustate(env)) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - - badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_sigreturn(CPUARMState *env) -{ - return do_rt_sigreturn(env); -} - -#elif defined(TARGET_ARM) - -struct target_sigcontext { - abi_ulong trap_no; - abi_ulong error_code; - abi_ulong oldmask; - abi_ulong arm_r0; - abi_ulong arm_r1; - abi_ulong arm_r2; - abi_ulong arm_r3; - abi_ulong arm_r4; - abi_ulong arm_r5; - abi_ulong arm_r6; - abi_ulong arm_r7; - abi_ulong arm_r8; - abi_ulong arm_r9; - abi_ulong arm_r10; - abi_ulong arm_fp; - abi_ulong arm_ip; - abi_ulong arm_sp; - abi_ulong arm_lr; - abi_ulong arm_pc; - abi_ulong arm_cpsr; - abi_ulong fault_address; -}; - -struct target_ucontext_v1 { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_ucontext_v2 { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ - char __unused[128 - sizeof(target_sigset_t)]; - abi_ulong tuc_regspace[128] __attribute__((__aligned__(8))); -}; - -struct target_user_vfp { - uint64_t fpregs[32]; - abi_ulong fpscr; -}; - -struct target_user_vfp_exc { - abi_ulong fpexc; - abi_ulong fpinst; - abi_ulong fpinst2; -}; - -struct target_vfp_sigframe { - abi_ulong magic; - abi_ulong size; - struct target_user_vfp ufp; - struct target_user_vfp_exc ufp_exc; -} __attribute__((__aligned__(8))); - -struct target_iwmmxt_sigframe { - abi_ulong magic; - abi_ulong size; - uint64_t regs[16]; - /* Note that not all the coprocessor control registers are stored here */ - uint32_t wcssf; - uint32_t wcasf; - uint32_t wcgr0; - uint32_t wcgr1; - uint32_t wcgr2; - uint32_t wcgr3; -} __attribute__((__aligned__(8))); - -#define TARGET_VFP_MAGIC 0x56465001 -#define TARGET_IWMMXT_MAGIC 0x12ef842a - -struct sigframe_v1 -{ - struct target_sigcontext sc; - abi_ulong extramask[TARGET_NSIG_WORDS-1]; - abi_ulong retcode; -}; - -struct sigframe_v2 -{ - struct target_ucontext_v2 uc; - abi_ulong retcode; -}; - -struct rt_sigframe_v1 -{ - abi_ulong pinfo; - abi_ulong puc; - struct target_siginfo info; - struct target_ucontext_v1 uc; - abi_ulong retcode; -}; - -struct rt_sigframe_v2 -{ - struct target_siginfo info; - struct target_ucontext_v2 uc; - abi_ulong retcode; -}; - -#define TARGET_CONFIG_CPU_32 1 - -/* - * For ARM syscalls, we encode the syscall number into the instruction. - */ -#define SWI_SYS_SIGRETURN (0xef000000|(TARGET_NR_sigreturn + ARM_SYSCALL_BASE)) -#define SWI_SYS_RT_SIGRETURN (0xef000000|(TARGET_NR_rt_sigreturn + ARM_SYSCALL_BASE)) - -/* - * For Thumb syscalls, we pass the syscall number via r7. We therefore - * need two 16-bit instructions. - */ -#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_sigreturn)) -#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (TARGET_NR_rt_sigreturn)) - -static const abi_ulong retcodes[4] = { - SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN, - SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN -}; - - -static inline int valid_user_regs(CPUARMState *regs) -{ - return 1; -} - -static void -setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ - CPUARMState *env, abi_ulong mask) -{ - __put_user(env->regs[0], &sc->arm_r0); - __put_user(env->regs[1], &sc->arm_r1); - __put_user(env->regs[2], &sc->arm_r2); - __put_user(env->regs[3], &sc->arm_r3); - __put_user(env->regs[4], &sc->arm_r4); - __put_user(env->regs[5], &sc->arm_r5); - __put_user(env->regs[6], &sc->arm_r6); - __put_user(env->regs[7], &sc->arm_r7); - __put_user(env->regs[8], &sc->arm_r8); - __put_user(env->regs[9], &sc->arm_r9); - __put_user(env->regs[10], &sc->arm_r10); - __put_user(env->regs[11], &sc->arm_fp); - __put_user(env->regs[12], &sc->arm_ip); - __put_user(env->regs[13], &sc->arm_sp); - __put_user(env->regs[14], &sc->arm_lr); - __put_user(env->regs[15], &sc->arm_pc); -#ifdef TARGET_CONFIG_CPU_32 - __put_user(cpsr_read(env), &sc->arm_cpsr); -#endif - - __put_user(/* current->thread.trap_no */ 0, &sc->trap_no); - __put_user(/* current->thread.error_code */ 0, &sc->error_code); - __put_user(/* current->thread.address */ 0, &sc->fault_address); - __put_user(mask, &sc->oldmask); -} - -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUARMState *regs, int framesize) -{ - unsigned long sp = regs->regs[13]; - - /* - * This is the X/Open sanctioned signal stack switching. - */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - /* - * ATPCS B01 mandates 8-byte alignment - */ - return (sp - framesize) & ~7; -} - -static void -setup_return(CPUARMState *env, struct target_sigaction *ka, - abi_ulong *rc, abi_ulong frame_addr, int usig, abi_ulong rc_addr) -{ - abi_ulong handler = ka->_sa_handler; - abi_ulong retcode; - int thumb = handler & 1; - uint32_t cpsr = cpsr_read(env); - - cpsr &= ~CPSR_IT; - if (thumb) { - cpsr |= CPSR_T; - } else { - cpsr &= ~CPSR_T; - } - - if (ka->sa_flags & TARGET_SA_RESTORER) { - retcode = ka->sa_restorer; - } else { - unsigned int idx = thumb; - - if (ka->sa_flags & TARGET_SA_SIGINFO) { - idx += 2; - } - - __put_user(retcodes[idx], rc); - - retcode = rc_addr + thumb; - } - - env->regs[0] = usig; - env->regs[13] = frame_addr; - env->regs[14] = retcode; - env->regs[15] = handler & (thumb ? ~1 : ~3); - cpsr_write(env, cpsr, CPSR_IT | CPSR_T, CPSRWriteByInstr); -} - -static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUARMState *env) -{ - int i; - struct target_vfp_sigframe *vfpframe; - vfpframe = (struct target_vfp_sigframe *)regspace; - __put_user(TARGET_VFP_MAGIC, &vfpframe->magic); - __put_user(sizeof(*vfpframe), &vfpframe->size); - for (i = 0; i < 32; i++) { - __put_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]); - } - __put_user(vfp_get_fpscr(env), &vfpframe->ufp.fpscr); - __put_user(env->vfp.xregs[ARM_VFP_FPEXC], &vfpframe->ufp_exc.fpexc); - __put_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); - __put_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); - return (abi_ulong*)(vfpframe+1); -} - -static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, - CPUARMState *env) -{ - int i; - struct target_iwmmxt_sigframe *iwmmxtframe; - iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; - __put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic); - __put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size); - for (i = 0; i < 16; i++) { - __put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); - } - __put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); - return (abi_ulong*)(iwmmxtframe+1); -} - -static void setup_sigframe_v2(struct target_ucontext_v2 *uc, - target_sigset_t *set, CPUARMState *env) -{ - struct target_sigaltstack stack; - int i; - abi_ulong *regspace; - - /* Clear all the bits of the ucontext we don't use. */ - memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext)); - - memset(&stack, 0, sizeof(stack)); - __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &stack.ss_size); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags); - memcpy(&uc->tuc_stack, &stack, sizeof(stack)); - - setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]); - /* Save coprocessor signal frame. */ - regspace = uc->tuc_regspace; - if (arm_feature(env, ARM_FEATURE_VFP)) { - regspace = setup_sigframe_v2_vfp(regspace, env); - } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = setup_sigframe_v2_iwmmxt(regspace, env); - } - - /* Write terminating magic word */ - __put_user(0, regspace); - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &uc->tuc_sigmask.sig[i]); - } -} - -/* compare linux/arch/arm/kernel/signal.c:setup_frame() */ -static void setup_frame_v1(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs) -{ - struct sigframe_v1 *frame; - abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); - int i; - - trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - setup_sigcontext(&frame->sc, regs, set->sig[0]); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - setup_return(regs, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe_v1, retcode)); - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - force_sigsegv(usig); -} - -static void setup_frame_v2(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs) -{ - struct sigframe_v2 *frame; - abi_ulong frame_addr = get_sigframe(ka, regs, sizeof(*frame)); - - trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - setup_sigframe_v2(&frame->uc, set, regs); - - setup_return(regs, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct sigframe_v2, retcode)); - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - force_sigsegv(usig); -} - -static void setup_frame(int usig, struct target_sigaction *ka, - target_sigset_t *set, CPUARMState *regs) -{ - if (get_osversion() >= 0x020612) { - setup_frame_v2(usig, ka, set, regs); - } else { - setup_frame_v1(usig, ka, set, regs); - } -} - -/* compare linux/arch/arm/kernel/signal.c:setup_rt_frame() */ -static void setup_rt_frame_v1(int usig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUARMState *env) -{ - struct rt_sigframe_v1 *frame; - abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); - struct target_sigaltstack stack; - int i; - abi_ulong info_addr, uc_addr; - - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - info_addr = frame_addr + offsetof(struct rt_sigframe_v1, info); - __put_user(info_addr, &frame->pinfo); - uc_addr = frame_addr + offsetof(struct rt_sigframe_v1, uc); - __put_user(uc_addr, &frame->puc); - tswap_siginfo(&frame->info, info); - - /* Clear all the bits of the ucontext we don't use. */ - memset(&frame->uc, 0, offsetof(struct target_ucontext_v1, tuc_mcontext)); - - memset(&stack, 0, sizeof(stack)); - __put_user(target_sigaltstack_used.ss_sp, &stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &stack.ss_size); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), &stack.ss_flags); - memcpy(&frame->uc.tuc_stack, &stack, sizeof(stack)); - - setup_sigcontext(&frame->uc.tuc_mcontext, env, set->sig[0]); - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - setup_return(env, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe_v1, retcode)); - - env->regs[1] = info_addr; - env->regs[2] = uc_addr; - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - force_sigsegv(usig); -} - -static void setup_rt_frame_v2(int usig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUARMState *env) -{ - struct rt_sigframe_v2 *frame; - abi_ulong frame_addr = get_sigframe(ka, env, sizeof(*frame)); - abi_ulong info_addr, uc_addr; - - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto sigsegv; - } - - info_addr = frame_addr + offsetof(struct rt_sigframe_v2, info); - uc_addr = frame_addr + offsetof(struct rt_sigframe_v2, uc); - tswap_siginfo(&frame->info, info); - - setup_sigframe_v2(&frame->uc, set, env); - - setup_return(env, ka, &frame->retcode, frame_addr, usig, - frame_addr + offsetof(struct rt_sigframe_v2, retcode)); - - env->regs[1] = info_addr; - env->regs[2] = uc_addr; - - unlock_user_struct(frame, frame_addr, 1); - return; -sigsegv: - force_sigsegv(usig); -} - -static void setup_rt_frame(int usig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUARMState *env) -{ - if (get_osversion() >= 0x020612) { - setup_rt_frame_v2(usig, ka, info, set, env); - } else { - setup_rt_frame_v1(usig, ka, info, set, env); - } -} - -static int -restore_sigcontext(CPUARMState *env, struct target_sigcontext *sc) -{ - int err = 0; - uint32_t cpsr; - - __get_user(env->regs[0], &sc->arm_r0); - __get_user(env->regs[1], &sc->arm_r1); - __get_user(env->regs[2], &sc->arm_r2); - __get_user(env->regs[3], &sc->arm_r3); - __get_user(env->regs[4], &sc->arm_r4); - __get_user(env->regs[5], &sc->arm_r5); - __get_user(env->regs[6], &sc->arm_r6); - __get_user(env->regs[7], &sc->arm_r7); - __get_user(env->regs[8], &sc->arm_r8); - __get_user(env->regs[9], &sc->arm_r9); - __get_user(env->regs[10], &sc->arm_r10); - __get_user(env->regs[11], &sc->arm_fp); - __get_user(env->regs[12], &sc->arm_ip); - __get_user(env->regs[13], &sc->arm_sp); - __get_user(env->regs[14], &sc->arm_lr); - __get_user(env->regs[15], &sc->arm_pc); -#ifdef TARGET_CONFIG_CPU_32 - __get_user(cpsr, &sc->arm_cpsr); - cpsr_write(env, cpsr, CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); -#endif - - err |= !valid_user_regs(env); - - return err; -} - -static long do_sigreturn_v1(CPUARMState *env) -{ - abi_ulong frame_addr; - struct sigframe_v1 *frame = NULL; - target_sigset_t set; - sigset_t host_set; - int i; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - __get_user(set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(set.sig[i], &frame->extramask[i - 1]); - } - - target_to_host_sigset_internal(&host_set, &set); - set_sigmask(&host_set); - - if (restore_sigcontext(env, &frame->sc)) { - goto badframe; - } - -#if 0 - /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); -#endif - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -static abi_ulong *restore_sigframe_v2_vfp(CPUARMState *env, abi_ulong *regspace) -{ - int i; - abi_ulong magic, sz; - uint32_t fpscr, fpexc; - struct target_vfp_sigframe *vfpframe; - vfpframe = (struct target_vfp_sigframe *)regspace; - - __get_user(magic, &vfpframe->magic); - __get_user(sz, &vfpframe->size); - if (magic != TARGET_VFP_MAGIC || sz != sizeof(*vfpframe)) { - return 0; - } - for (i = 0; i < 32; i++) { - __get_user(*aa32_vfp_dreg(env, i), &vfpframe->ufp.fpregs[i]); - } - __get_user(fpscr, &vfpframe->ufp.fpscr); - vfp_set_fpscr(env, fpscr); - __get_user(fpexc, &vfpframe->ufp_exc.fpexc); - /* Sanitise FPEXC: ensure VFP is enabled, FPINST2 is invalid - * and the exception flag is cleared - */ - fpexc |= (1 << 30); - fpexc &= ~((1 << 31) | (1 << 28)); - env->vfp.xregs[ARM_VFP_FPEXC] = fpexc; - __get_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); - __get_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); - return (abi_ulong*)(vfpframe + 1); -} - -static abi_ulong *restore_sigframe_v2_iwmmxt(CPUARMState *env, - abi_ulong *regspace) -{ - int i; - abi_ulong magic, sz; - struct target_iwmmxt_sigframe *iwmmxtframe; - iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; - - __get_user(magic, &iwmmxtframe->magic); - __get_user(sz, &iwmmxtframe->size); - if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) { - return 0; - } - for (i = 0; i < 16; i++) { - __get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); - } - __get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); - return (abi_ulong*)(iwmmxtframe + 1); -} - -static int do_sigframe_return_v2(CPUARMState *env, - target_ulong context_addr, - struct target_ucontext_v2 *uc) -{ - sigset_t host_set; - abi_ulong *regspace; - - target_to_host_sigset(&host_set, &uc->tuc_sigmask); - set_sigmask(&host_set); - - if (restore_sigcontext(env, &uc->tuc_mcontext)) - return 1; - - /* Restore coprocessor signal frame */ - regspace = uc->tuc_regspace; - if (arm_feature(env, ARM_FEATURE_VFP)) { - regspace = restore_sigframe_v2_vfp(env, regspace); - if (!regspace) { - return 1; - } - } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = restore_sigframe_v2_iwmmxt(env, regspace); - if (!regspace) { - return 1; - } - } - - if (do_sigaltstack(context_addr - + offsetof(struct target_ucontext_v2, tuc_stack), - 0, get_sp_from_cpustate(env)) == -EFAULT) { - return 1; - } - -#if 0 - /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); -#endif - - return 0; -} - -static long do_sigreturn_v2(CPUARMState *env) -{ - abi_ulong frame_addr; - struct sigframe_v2 *frame = NULL; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - if (do_sigframe_return_v2(env, - frame_addr - + offsetof(struct sigframe_v2, uc), - &frame->uc)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_sigreturn(CPUARMState *env) -{ - if (get_osversion() >= 0x020612) { - return do_sigreturn_v2(env); - } else { - return do_sigreturn_v1(env); - } -} - -static long do_rt_sigreturn_v1(CPUARMState *env) -{ - abi_ulong frame_addr; - struct rt_sigframe_v1 *frame = NULL; - sigset_t host_set; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_rt_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&host_set, &frame->uc.tuc_sigmask); - set_sigmask(&host_set); - - if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { - goto badframe; - } - - if (do_sigaltstack(frame_addr + offsetof(struct rt_sigframe_v1, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) - goto badframe; - -#if 0 - /* Send SIGTRAP if we're single-stepping */ - if (ptrace_cancel_bpt(current)) - send_sig(SIGTRAP, current, 1); -#endif - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -static long do_rt_sigreturn_v2(CPUARMState *env) -{ - abi_ulong frame_addr; - struct rt_sigframe_v2 *frame = NULL; - - /* - * Since we stacked the signal on a 64-bit boundary, - * then 'sp' should be word aligned here. If it's - * not, then the user is trying to mess with us. - */ - frame_addr = env->regs[13]; - trace_user_do_rt_sigreturn(env, frame_addr); - if (frame_addr & 7) { - goto badframe; - } - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - if (do_sigframe_return_v2(env, - frame_addr - + offsetof(struct rt_sigframe_v2, uc), - &frame->uc)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUARMState *env) -{ - if (get_osversion() >= 0x020612) { - return do_rt_sigreturn_v2(env); - } else { - return do_rt_sigreturn_v1(env); - } -} - -#elif defined(TARGET_SPARC) - -#define __SUNOS_MAXWIN 31 - -/* This is what SunOS does, so shall I. */ -struct target_sigcontext { - abi_ulong sigc_onstack; /* state to restore */ - - abi_ulong sigc_mask; /* sigmask to restore */ - abi_ulong sigc_sp; /* stack pointer */ - abi_ulong sigc_pc; /* program counter */ - abi_ulong sigc_npc; /* next program counter */ - abi_ulong sigc_psr; /* for condition codes etc */ - abi_ulong sigc_g1; /* User uses these two registers */ - abi_ulong sigc_o0; /* within the trampoline code. */ - - /* Now comes information regarding the users window set - * at the time of the signal. - */ - abi_ulong sigc_oswins; /* outstanding windows */ - - /* stack ptrs for each regwin buf */ - char *sigc_spbuf[__SUNOS_MAXWIN]; - - /* Windows to restore after signal */ - struct { - abi_ulong locals[8]; - abi_ulong ins[8]; - } sigc_wbuf[__SUNOS_MAXWIN]; -}; -/* A Sparc stack frame */ -struct sparc_stackf { - abi_ulong locals[8]; - abi_ulong ins[8]; - /* It's simpler to treat fp and callers_pc as elements of ins[] - * since we never need to access them ourselves. - */ - char *structptr; - abi_ulong xargs[6]; - abi_ulong xxargs[1]; -}; - -typedef struct { - struct { - abi_ulong psr; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong u_regs[16]; /* globals and ins */ - } si_regs; - int si_mask; -} __siginfo_t; - -typedef struct { - abi_ulong si_float_regs[32]; - unsigned long si_fsr; - unsigned long si_fpqdepth; - struct { - unsigned long *insn_addr; - unsigned long insn; - } si_fpqueue [16]; -} qemu_siginfo_fpu_t; - - -struct target_signal_frame { - struct sparc_stackf ss; - __siginfo_t info; - abi_ulong fpu_save; - abi_ulong insns[2] __attribute__ ((aligned (8))); - abi_ulong extramask[TARGET_NSIG_WORDS - 1]; - abi_ulong extra_size; /* Should be 0 */ - qemu_siginfo_fpu_t fpu_state; -}; -struct target_rt_signal_frame { - struct sparc_stackf ss; - siginfo_t info; - abi_ulong regs[20]; - sigset_t mask; - abi_ulong fpu_save; - unsigned int insns[2]; - stack_t stack; - unsigned int extra_size; /* Should be 0 */ - qemu_siginfo_fpu_t fpu_state; -}; - -#define UREG_O0 16 -#define UREG_O6 22 -#define UREG_I0 0 -#define UREG_I1 1 -#define UREG_I2 2 -#define UREG_I3 3 -#define UREG_I4 4 -#define UREG_I5 5 -#define UREG_I6 6 -#define UREG_I7 7 -#define UREG_L0 8 -#define UREG_FP UREG_I6 -#define UREG_SP UREG_O6 - -static inline abi_ulong get_sigframe(struct target_sigaction *sa, - CPUSPARCState *env, - unsigned long framesize) -{ - abi_ulong sp; - - sp = env->regwptr[UREG_FP]; - - /* This is the X/Open sanctioned signal stack switching. */ - if (sa->sa_flags & TARGET_SA_ONSTACK) { - if (!on_sig_stack(sp) - && !((target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size) & 7)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - } - return sp - framesize; -} - -static int -setup___siginfo(__siginfo_t *si, CPUSPARCState *env, abi_ulong mask) -{ - int err = 0, i; - - __put_user(env->psr, &si->si_regs.psr); - __put_user(env->pc, &si->si_regs.pc); - __put_user(env->npc, &si->si_regs.npc); - __put_user(env->y, &si->si_regs.y); - for (i=0; i < 8; i++) { - __put_user(env->gregs[i], &si->si_regs.u_regs[i]); - } - for (i=0; i < 8; i++) { - __put_user(env->regwptr[UREG_I0 + i], &si->si_regs.u_regs[i+8]); - } - __put_user(mask, &si->si_mask); - return err; -} - -#if 0 -static int -setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ - CPUSPARCState *env, unsigned long mask) -{ - int err = 0; - - __put_user(mask, &sc->sigc_mask); - __put_user(env->regwptr[UREG_SP], &sc->sigc_sp); - __put_user(env->pc, &sc->sigc_pc); - __put_user(env->npc, &sc->sigc_npc); - __put_user(env->psr, &sc->sigc_psr); - __put_user(env->gregs[1], &sc->sigc_g1); - __put_user(env->regwptr[UREG_O0], &sc->sigc_o0); - - return err; -} -#endif -#define NF_ALIGNEDSZ (((sizeof(struct target_signal_frame) + 7) & (~7))) - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUSPARCState *env) -{ - abi_ulong sf_addr; - struct target_signal_frame *sf; - int sigframe_size, err, i; - - /* 1. Make sure everything is clean */ - //synchronize_user_stack(); - - sigframe_size = NF_ALIGNEDSZ; - sf_addr = get_sigframe(ka, env, sigframe_size); - trace_user_setup_frame(env, sf_addr); - - sf = lock_user(VERIFY_WRITE, sf_addr, - sizeof(struct target_signal_frame), 0); - if (!sf) { - goto sigsegv; - } -#if 0 - if (invalid_frame_pointer(sf, sigframe_size)) - goto sigill_and_return; -#endif - /* 2. Save the current process state */ - err = setup___siginfo(&sf->info, env, set->sig[0]); - __put_user(0, &sf->extra_size); - - //save_fpu_state(regs, &sf->fpu_state); - //__put_user(&sf->fpu_state, &sf->fpu_save); - - __put_user(set->sig[0], &sf->info.si_mask); - for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { - __put_user(set->sig[i + 1], &sf->extramask[i]); - } - - for (i = 0; i < 8; i++) { - __put_user(env->regwptr[i + UREG_L0], &sf->ss.locals[i]); - } - for (i = 0; i < 8; i++) { - __put_user(env->regwptr[i + UREG_I0], &sf->ss.ins[i]); - } - if (err) - goto sigsegv; - - /* 3. signal handler back-trampoline and parameters */ - env->regwptr[UREG_FP] = sf_addr; - env->regwptr[UREG_I0] = sig; - env->regwptr[UREG_I1] = sf_addr + - offsetof(struct target_signal_frame, info); - env->regwptr[UREG_I2] = sf_addr + - offsetof(struct target_signal_frame, info); - - /* 4. signal handler */ - env->pc = ka->_sa_handler; - env->npc = (env->pc + 4); - /* 5. return to kernel instructions */ - if (ka->ka_restorer) { - env->regwptr[UREG_I7] = ka->ka_restorer; - } else { - uint32_t val32; - - env->regwptr[UREG_I7] = sf_addr + - offsetof(struct target_signal_frame, insns) - 2 * 4; - - /* mov __NR_sigreturn, %g1 */ - val32 = 0x821020d8; - __put_user(val32, &sf->insns[0]); - - /* t 0x10 */ - val32 = 0x91d02010; - __put_user(val32, &sf->insns[1]); - if (err) - goto sigsegv; - - /* Flush instruction space. */ - // flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); - // tb_flush(env); - } - unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); - return; -#if 0 -sigill_and_return: - force_sig(TARGET_SIGILL); -#endif -sigsegv: - unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUSPARCState *env) -{ - fprintf(stderr, "setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUSPARCState *env) -{ - abi_ulong sf_addr; - struct target_signal_frame *sf; - uint32_t up_psr, pc, npc; - target_sigset_t set; - sigset_t host_set; - int err=0, i; - - sf_addr = env->regwptr[UREG_FP]; - trace_user_do_sigreturn(env, sf_addr); - if (!lock_user_struct(VERIFY_READ, sf, sf_addr, 1)) { - goto segv_and_exit; - } - - /* 1. Make sure we are not getting garbage from the user */ - - if (sf_addr & 3) - goto segv_and_exit; - - __get_user(pc, &sf->info.si_regs.pc); - __get_user(npc, &sf->info.si_regs.npc); - - if ((pc | npc) & 3) { - goto segv_and_exit; - } - - /* 2. Restore the state */ - __get_user(up_psr, &sf->info.si_regs.psr); - - /* User can only change condition codes and FPU enabling in %psr. */ - env->psr = (up_psr & (PSR_ICC /* | PSR_EF */)) - | (env->psr & ~(PSR_ICC /* | PSR_EF */)); - - env->pc = pc; - env->npc = npc; - __get_user(env->y, &sf->info.si_regs.y); - for (i=0; i < 8; i++) { - __get_user(env->gregs[i], &sf->info.si_regs.u_regs[i]); - } - for (i=0; i < 8; i++) { - __get_user(env->regwptr[i + UREG_I0], &sf->info.si_regs.u_regs[i+8]); - } - - /* FIXME: implement FPU save/restore: - * __get_user(fpu_save, &sf->fpu_save); - * if (fpu_save) - * err |= restore_fpu_state(env, fpu_save); - */ - - /* This is pretty much atomic, no amount locking would prevent - * the races which exist anyways. - */ - __get_user(set.sig[0], &sf->info.si_mask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(set.sig[i], &sf->extramask[i - 1]); - } - - target_to_host_sigset_internal(&host_set, &set); - set_sigmask(&host_set); - - if (err) { - goto segv_and_exit; - } - unlock_user_struct(sf, sf_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -segv_and_exit: - unlock_user_struct(sf, sf_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUSPARCState *env) -{ - trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) -#define SPARC_MC_TSTATE 0 -#define SPARC_MC_PC 1 -#define SPARC_MC_NPC 2 -#define SPARC_MC_Y 3 -#define SPARC_MC_G1 4 -#define SPARC_MC_G2 5 -#define SPARC_MC_G3 6 -#define SPARC_MC_G4 7 -#define SPARC_MC_G5 8 -#define SPARC_MC_G6 9 -#define SPARC_MC_G7 10 -#define SPARC_MC_O0 11 -#define SPARC_MC_O1 12 -#define SPARC_MC_O2 13 -#define SPARC_MC_O3 14 -#define SPARC_MC_O4 15 -#define SPARC_MC_O5 16 -#define SPARC_MC_O6 17 -#define SPARC_MC_O7 18 -#define SPARC_MC_NGREG 19 - -typedef abi_ulong target_mc_greg_t; -typedef target_mc_greg_t target_mc_gregset_t[SPARC_MC_NGREG]; - -struct target_mc_fq { - abi_ulong *mcfq_addr; - uint32_t mcfq_insn; -}; - -struct target_mc_fpu { - union { - uint32_t sregs[32]; - uint64_t dregs[32]; - //uint128_t qregs[16]; - } mcfpu_fregs; - abi_ulong mcfpu_fsr; - abi_ulong mcfpu_fprs; - abi_ulong mcfpu_gsr; - struct target_mc_fq *mcfpu_fq; - unsigned char mcfpu_qcnt; - unsigned char mcfpu_qentsz; - unsigned char mcfpu_enab; -}; -typedef struct target_mc_fpu target_mc_fpu_t; - -typedef struct { - target_mc_gregset_t mc_gregs; - target_mc_greg_t mc_fp; - target_mc_greg_t mc_i7; - target_mc_fpu_t mc_fpregs; -} target_mcontext_t; - -struct target_ucontext { - struct target_ucontext *tuc_link; - abi_ulong tuc_flags; - target_sigset_t tuc_sigmask; - target_mcontext_t tuc_mcontext; -}; - -/* A V9 register window */ -struct target_reg_window { - abi_ulong locals[8]; - abi_ulong ins[8]; -}; - -#define TARGET_STACK_BIAS 2047 - -/* {set, get}context() needed for 64-bit SparcLinux userland. */ -void sparc64_set_context(CPUSPARCState *env) -{ - abi_ulong ucp_addr; - struct target_ucontext *ucp; - target_mc_gregset_t *grp; - abi_ulong pc, npc, tstate; - abi_ulong fp, i7, w_addr; - unsigned int i; - - ucp_addr = env->regwptr[UREG_I0]; - if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) { - goto do_sigsegv; - } - grp = &ucp->tuc_mcontext.mc_gregs; - __get_user(pc, &((*grp)[SPARC_MC_PC])); - __get_user(npc, &((*grp)[SPARC_MC_NPC])); - if ((pc | npc) & 3) { - goto do_sigsegv; - } - if (env->regwptr[UREG_I1]) { - target_sigset_t target_set; - sigset_t set; - - if (TARGET_NSIG_WORDS == 1) { - __get_user(target_set.sig[0], &ucp->tuc_sigmask.sig[0]); - } else { - abi_ulong *src, *dst; - src = ucp->tuc_sigmask.sig; - dst = target_set.sig; - for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { - __get_user(*dst, src); - } - } - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - } - env->pc = pc; - env->npc = npc; - __get_user(env->y, &((*grp)[SPARC_MC_Y])); - __get_user(tstate, &((*grp)[SPARC_MC_TSTATE])); - env->asi = (tstate >> 24) & 0xff; - cpu_put_ccr(env, tstate >> 32); - cpu_put_cwp64(env, tstate & 0x1f); - __get_user(env->gregs[1], (&(*grp)[SPARC_MC_G1])); - __get_user(env->gregs[2], (&(*grp)[SPARC_MC_G2])); - __get_user(env->gregs[3], (&(*grp)[SPARC_MC_G3])); - __get_user(env->gregs[4], (&(*grp)[SPARC_MC_G4])); - __get_user(env->gregs[5], (&(*grp)[SPARC_MC_G5])); - __get_user(env->gregs[6], (&(*grp)[SPARC_MC_G6])); - __get_user(env->gregs[7], (&(*grp)[SPARC_MC_G7])); - __get_user(env->regwptr[UREG_I0], (&(*grp)[SPARC_MC_O0])); - __get_user(env->regwptr[UREG_I1], (&(*grp)[SPARC_MC_O1])); - __get_user(env->regwptr[UREG_I2], (&(*grp)[SPARC_MC_O2])); - __get_user(env->regwptr[UREG_I3], (&(*grp)[SPARC_MC_O3])); - __get_user(env->regwptr[UREG_I4], (&(*grp)[SPARC_MC_O4])); - __get_user(env->regwptr[UREG_I5], (&(*grp)[SPARC_MC_O5])); - __get_user(env->regwptr[UREG_I6], (&(*grp)[SPARC_MC_O6])); - __get_user(env->regwptr[UREG_I7], (&(*grp)[SPARC_MC_O7])); - - __get_user(fp, &(ucp->tuc_mcontext.mc_fp)); - __get_user(i7, &(ucp->tuc_mcontext.mc_i7)); - - w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; - if (put_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), - abi_ulong) != 0) { - goto do_sigsegv; - } - if (put_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), - abi_ulong) != 0) { - goto do_sigsegv; - } - /* FIXME this does not match how the kernel handles the FPU in - * its sparc64_set_context implementation. In particular the FPU - * is only restored if fenab is non-zero in: - * __get_user(fenab, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_enab)); - */ - __get_user(env->fprs, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fprs)); - { - uint32_t *src = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; - for (i = 0; i < 64; i++, src++) { - if (i & 1) { - __get_user(env->fpr[i/2].l.lower, src); - } else { - __get_user(env->fpr[i/2].l.upper, src); - } - } - } - __get_user(env->fsr, - &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fsr)); - __get_user(env->gsr, - &(ucp->tuc_mcontext.mc_fpregs.mcfpu_gsr)); - unlock_user_struct(ucp, ucp_addr, 0); - return; -do_sigsegv: - unlock_user_struct(ucp, ucp_addr, 0); - force_sig(TARGET_SIGSEGV); -} - -void sparc64_get_context(CPUSPARCState *env) -{ - abi_ulong ucp_addr; - struct target_ucontext *ucp; - target_mc_gregset_t *grp; - target_mcontext_t *mcp; - abi_ulong fp, i7, w_addr; - int err; - unsigned int i; - target_sigset_t target_set; - sigset_t set; - - ucp_addr = env->regwptr[UREG_I0]; - if (!lock_user_struct(VERIFY_WRITE, ucp, ucp_addr, 0)) { - goto do_sigsegv; - } - - mcp = &ucp->tuc_mcontext; - grp = &mcp->mc_gregs; - - /* Skip over the trap instruction, first. */ - env->pc = env->npc; - env->npc += 4; - - /* If we're only reading the signal mask then do_sigprocmask() - * is guaranteed not to fail, which is important because we don't - * have any way to signal a failure or restart this operation since - * this is not a normal syscall. - */ - err = do_sigprocmask(0, NULL, &set); - assert(err == 0); - host_to_target_sigset_internal(&target_set, &set); - if (TARGET_NSIG_WORDS == 1) { - __put_user(target_set.sig[0], - (abi_ulong *)&ucp->tuc_sigmask); - } else { - abi_ulong *src, *dst; - src = target_set.sig; - dst = ucp->tuc_sigmask.sig; - for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { - __put_user(*src, dst); - } - if (err) - goto do_sigsegv; - } - - /* XXX: tstate must be saved properly */ - // __put_user(env->tstate, &((*grp)[SPARC_MC_TSTATE])); - __put_user(env->pc, &((*grp)[SPARC_MC_PC])); - __put_user(env->npc, &((*grp)[SPARC_MC_NPC])); - __put_user(env->y, &((*grp)[SPARC_MC_Y])); - __put_user(env->gregs[1], &((*grp)[SPARC_MC_G1])); - __put_user(env->gregs[2], &((*grp)[SPARC_MC_G2])); - __put_user(env->gregs[3], &((*grp)[SPARC_MC_G3])); - __put_user(env->gregs[4], &((*grp)[SPARC_MC_G4])); - __put_user(env->gregs[5], &((*grp)[SPARC_MC_G5])); - __put_user(env->gregs[6], &((*grp)[SPARC_MC_G6])); - __put_user(env->gregs[7], &((*grp)[SPARC_MC_G7])); - __put_user(env->regwptr[UREG_I0], &((*grp)[SPARC_MC_O0])); - __put_user(env->regwptr[UREG_I1], &((*grp)[SPARC_MC_O1])); - __put_user(env->regwptr[UREG_I2], &((*grp)[SPARC_MC_O2])); - __put_user(env->regwptr[UREG_I3], &((*grp)[SPARC_MC_O3])); - __put_user(env->regwptr[UREG_I4], &((*grp)[SPARC_MC_O4])); - __put_user(env->regwptr[UREG_I5], &((*grp)[SPARC_MC_O5])); - __put_user(env->regwptr[UREG_I6], &((*grp)[SPARC_MC_O6])); - __put_user(env->regwptr[UREG_I7], &((*grp)[SPARC_MC_O7])); - - w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; - fp = i7 = 0; - if (get_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), - abi_ulong) != 0) { - goto do_sigsegv; - } - if (get_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), - abi_ulong) != 0) { - goto do_sigsegv; - } - __put_user(fp, &(mcp->mc_fp)); - __put_user(i7, &(mcp->mc_i7)); - - { - uint32_t *dst = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; - for (i = 0; i < 64; i++, dst++) { - if (i & 1) { - __put_user(env->fpr[i/2].l.lower, dst); - } else { - __put_user(env->fpr[i/2].l.upper, dst); - } - } - } - __put_user(env->fsr, &(mcp->mc_fpregs.mcfpu_fsr)); - __put_user(env->gsr, &(mcp->mc_fpregs.mcfpu_gsr)); - __put_user(env->fprs, &(mcp->mc_fpregs.mcfpu_fprs)); - - if (err) - goto do_sigsegv; - unlock_user_struct(ucp, ucp_addr, 1); - return; -do_sigsegv: - unlock_user_struct(ucp, ucp_addr, 1); - force_sig(TARGET_SIGSEGV); -} -#endif -#elif defined(TARGET_MIPS) || defined(TARGET_MIPS64) - -# if defined(TARGET_ABI_MIPSO32) -struct target_sigcontext { - uint32_t sc_regmask; /* Unused */ - uint32_t sc_status; - uint64_t sc_pc; - uint64_t sc_regs[32]; - uint64_t sc_fpregs[32]; - uint32_t sc_ownedfp; /* Unused */ - uint32_t sc_fpc_csr; - uint32_t sc_fpc_eir; /* Unused */ - uint32_t sc_used_math; - uint32_t sc_dsp; /* dsp status, was sc_ssflags */ - uint32_t pad0; - uint64_t sc_mdhi; - uint64_t sc_mdlo; - target_ulong sc_hi1; /* Was sc_cause */ - target_ulong sc_lo1; /* Was sc_badvaddr */ - target_ulong sc_hi2; /* Was sc_sigset[4] */ - target_ulong sc_lo2; - target_ulong sc_hi3; - target_ulong sc_lo3; -}; -# else /* N32 || N64 */ -struct target_sigcontext { - uint64_t sc_regs[32]; - uint64_t sc_fpregs[32]; - uint64_t sc_mdhi; - uint64_t sc_hi1; - uint64_t sc_hi2; - uint64_t sc_hi3; - uint64_t sc_mdlo; - uint64_t sc_lo1; - uint64_t sc_lo2; - uint64_t sc_lo3; - uint64_t sc_pc; - uint32_t sc_fpc_csr; - uint32_t sc_used_math; - uint32_t sc_dsp; - uint32_t sc_reserved; -}; -# endif /* O32 */ - -struct sigframe { - uint32_t sf_ass[4]; /* argument save space for o32 */ - uint32_t sf_code[2]; /* signal trampoline */ - struct target_sigcontext sf_sc; - target_sigset_t sf_mask; -}; - -struct target_ucontext { - target_ulong tuc_flags; - target_ulong tuc_link; - target_stack_t tuc_stack; - target_ulong pad0; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; -}; - -struct target_rt_sigframe { - uint32_t rs_ass[4]; /* argument save space for o32 */ - uint32_t rs_code[2]; /* signal trampoline */ - struct target_siginfo rs_info; - struct target_ucontext rs_uc; -}; - -/* Install trampoline to jump back from signal handler */ -static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall) -{ - int err = 0; - - /* - * Set up the return code ... - * - * li v0, __NR__foo_sigreturn - * syscall - */ - - __put_user(0x24020000 + syscall, tramp + 0); - __put_user(0x0000000c , tramp + 1); - return err; -} - -static inline void setup_sigcontext(CPUMIPSState *regs, - struct target_sigcontext *sc) -{ - int i; - - __put_user(exception_resume_pc(regs), &sc->sc_pc); - regs->hflags &= ~MIPS_HFLAG_BMASK; - - __put_user(0, &sc->sc_regs[0]); - for (i = 1; i < 32; ++i) { - __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); - } - - __put_user(regs->active_tc.HI[0], &sc->sc_mdhi); - __put_user(regs->active_tc.LO[0], &sc->sc_mdlo); - - /* Rather than checking for dsp existence, always copy. The storage - would just be garbage otherwise. */ - __put_user(regs->active_tc.HI[1], &sc->sc_hi1); - __put_user(regs->active_tc.HI[2], &sc->sc_hi2); - __put_user(regs->active_tc.HI[3], &sc->sc_hi3); - __put_user(regs->active_tc.LO[1], &sc->sc_lo1); - __put_user(regs->active_tc.LO[2], &sc->sc_lo2); - __put_user(regs->active_tc.LO[3], &sc->sc_lo3); - { - uint32_t dsp = cpu_rddsp(0x3ff, regs); - __put_user(dsp, &sc->sc_dsp); - } - - __put_user(1, &sc->sc_used_math); - - for (i = 0; i < 32; ++i) { - __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); - } -} - -static inline void -restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc) -{ - int i; - - __get_user(regs->CP0_EPC, &sc->sc_pc); - - __get_user(regs->active_tc.HI[0], &sc->sc_mdhi); - __get_user(regs->active_tc.LO[0], &sc->sc_mdlo); - - for (i = 1; i < 32; ++i) { - __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); - } - - __get_user(regs->active_tc.HI[1], &sc->sc_hi1); - __get_user(regs->active_tc.HI[2], &sc->sc_hi2); - __get_user(regs->active_tc.HI[3], &sc->sc_hi3); - __get_user(regs->active_tc.LO[1], &sc->sc_lo1); - __get_user(regs->active_tc.LO[2], &sc->sc_lo2); - __get_user(regs->active_tc.LO[3], &sc->sc_lo3); - { - uint32_t dsp; - __get_user(dsp, &sc->sc_dsp); - cpu_wrdsp(dsp, 0x3ff, regs); - } - - for (i = 0; i < 32; ++i) { - __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); - } -} - -/* - * Determine which stack to use.. - */ -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size) -{ - unsigned long sp; - - /* Default to using normal stack */ - sp = regs->active_tc.gpr[29]; - - /* - * FPU emulator may have its own trampoline active just - * above the user stack, 16-bytes before the next lowest - * 16 byte boundary. Try to avoid trashing it. - */ - sp -= 32; - - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - return (sp - frame_size) & ~7; -} - -static void mips_set_hflags_isa_mode_from_pc(CPUMIPSState *env) -{ - if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { - env->hflags &= ~MIPS_HFLAG_M16; - env->hflags |= (env->active_tc.PC & 1) << MIPS_HFLAG_M16_SHIFT; - env->active_tc.PC &= ~(target_ulong) 1; - } -} - -# if defined(TARGET_ABI_MIPSO32) -/* compare linux/arch/mips/kernel/signal.c:setup_frame() */ -static void setup_frame(int sig, struct target_sigaction * ka, - target_sigset_t *set, CPUMIPSState *regs) -{ - struct sigframe *frame; - abi_ulong frame_addr; - int i; - - frame_addr = get_sigframe(ka, regs, sizeof(*frame)); - trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - install_sigtramp(frame->sf_code, TARGET_NR_sigreturn); - - setup_sigcontext(regs, &frame->sf_sc); - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->sf_mask.sig[i]); - } - - /* - * Arguments to signal handler: - * - * a0 = signal number - * a1 = 0 (should be cause) - * a2 = pointer to struct sigcontext - * - * $25 and PC point to the signal handler, $29 points to the - * struct sigframe. - */ - regs->active_tc.gpr[ 4] = sig; - regs->active_tc.gpr[ 5] = 0; - regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc); - regs->active_tc.gpr[29] = frame_addr; - regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code); - /* The original kernel code sets CP0_EPC to the handler - * since it returns to userland using eret - * we cannot do this here, and we must set PC directly */ - regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler; - mips_set_hflags_isa_mode_from_pc(regs); - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -long do_sigreturn(CPUMIPSState *regs) -{ - struct sigframe *frame; - abi_ulong frame_addr; - sigset_t blocked; - target_sigset_t target_set; - int i; - - frame_addr = regs->active_tc.gpr[29]; - trace_user_do_sigreturn(regs, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->sf_mask.sig[i]); - } - - target_to_host_sigset_internal(&blocked, &target_set); - set_sigmask(&blocked); - - restore_sigcontext(regs, &frame->sf_sc); - -#if 0 - /* - * Don't let your children do this ... - */ - __asm__ __volatile__( - "move\t$29, %0\n\t" - "j\tsyscall_exit" - :/* no outputs */ - :"r" (®s)); - /* Unreached */ -#endif - - regs->active_tc.PC = regs->CP0_EPC; - mips_set_hflags_isa_mode_from_pc(regs); - /* I am not sure this is right, but it seems to work - * maybe a problem with nested signals ? */ - regs->CP0_EPC = 0; - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} -# endif /* O32 */ - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUMIPSState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - int i; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn); - - tswap_siginfo(&frame->rs_info, info); - - __put_user(0, &frame->rs_uc.tuc_flags); - __put_user(0, &frame->rs_uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->rs_uc.tuc_stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &frame->rs_uc.tuc_stack.ss_size); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->rs_uc.tuc_stack.ss_flags); - - setup_sigcontext(env, &frame->rs_uc.tuc_mcontext); - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]); - } - - /* - * Arguments to signal handler: - * - * a0 = signal number - * a1 = pointer to siginfo_t - * a2 = pointer to ucontext_t - * - * $25 and PC point to the signal handler, $29 points to the - * struct sigframe. - */ - env->active_tc.gpr[ 4] = sig; - env->active_tc.gpr[ 5] = frame_addr - + offsetof(struct target_rt_sigframe, rs_info); - env->active_tc.gpr[ 6] = frame_addr - + offsetof(struct target_rt_sigframe, rs_uc); - env->active_tc.gpr[29] = frame_addr; - env->active_tc.gpr[31] = frame_addr - + offsetof(struct target_rt_sigframe, rs_code); - /* The original kernel code sets CP0_EPC to the handler - * since it returns to userland using eret - * we cannot do this here, and we must set PC directly */ - env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler; - mips_set_hflags_isa_mode_from_pc(env); - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -long do_rt_sigreturn(CPUMIPSState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - sigset_t blocked; - - frame_addr = env->active_tc.gpr[29]; - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); - set_sigmask(&blocked); - - restore_sigcontext(env, &frame->rs_uc.tuc_mcontext); - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, rs_uc.tuc_stack), - 0, get_sp_from_cpustate(env)) == -EFAULT) - goto badframe; - - env->active_tc.PC = env->CP0_EPC; - mips_set_hflags_isa_mode_from_pc(env); - /* I am not sure this is right, but it seems to work - * maybe a problem with nested signals ? */ - env->CP0_EPC = 0; - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_SH4) - -/* - * code and data structures from linux kernel: - * include/asm-sh/sigcontext.h - * arch/sh/kernel/signal.c - */ - -struct target_sigcontext { - target_ulong oldmask; - - /* CPU registers */ - target_ulong sc_gregs[16]; - target_ulong sc_pc; - target_ulong sc_pr; - target_ulong sc_sr; - target_ulong sc_gbr; - target_ulong sc_mach; - target_ulong sc_macl; - - /* FPU registers */ - target_ulong sc_fpregs[16]; - target_ulong sc_xfpregs[16]; - unsigned int sc_fpscr; - unsigned int sc_fpul; - unsigned int sc_ownedfp; -}; - -struct target_sigframe -{ - struct target_sigcontext sc; - target_ulong extramask[TARGET_NSIG_WORDS-1]; - uint16_t retcode[3]; -}; - - -struct target_ucontext { - target_ulong tuc_flags; - struct target_ucontext *tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe -{ - struct target_siginfo info; - struct target_ucontext uc; - uint16_t retcode[3]; -}; - - -#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */ -#define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) SH3/4 */ - -static abi_ulong get_sigframe(struct target_sigaction *ka, - unsigned long sp, size_t frame_size) -{ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - return (sp - frame_size) & -8ul; -} - -/* Notice when we're in the middle of a gUSA region and reset. - Note that this will only occur for !parallel_cpus, as we will - translate such sequences differently in a parallel context. */ -static void unwind_gusa(CPUSH4State *regs) -{ - /* If the stack pointer is sufficiently negative, and we haven't - completed the sequence, then reset to the entry to the region. */ - /* ??? The SH4 kernel checks for and address above 0xC0000000. - However, the page mappings in qemu linux-user aren't as restricted - and we wind up with the normal stack mapped above 0xF0000000. - That said, there is no reason why the kernel should be allowing - a gUSA region that spans 1GB. Use a tighter check here, for what - can actually be enabled by the immediate move. */ - if (regs->gregs[15] >= -128u && regs->pc < regs->gregs[0]) { - /* Reset the PC to before the gUSA region, as computed from - R0 = region end, SP = -(region size), plus one more for the - insn that actually initializes SP to the region size. */ - regs->pc = regs->gregs[0] + regs->gregs[15] - 2; - - /* Reset the SP to the saved version in R1. */ - regs->gregs[15] = regs->gregs[1]; - } -} - -static void setup_sigcontext(struct target_sigcontext *sc, - CPUSH4State *regs, unsigned long mask) -{ - int i; - -#define COPY(x) __put_user(regs->x, &sc->sc_##x) - COPY(gregs[0]); COPY(gregs[1]); - COPY(gregs[2]); COPY(gregs[3]); - COPY(gregs[4]); COPY(gregs[5]); - COPY(gregs[6]); COPY(gregs[7]); - COPY(gregs[8]); COPY(gregs[9]); - COPY(gregs[10]); COPY(gregs[11]); - COPY(gregs[12]); COPY(gregs[13]); - COPY(gregs[14]); COPY(gregs[15]); - COPY(gbr); COPY(mach); - COPY(macl); COPY(pr); - COPY(sr); COPY(pc); -#undef COPY - - for (i=0; i<16; i++) { - __put_user(regs->fregs[i], &sc->sc_fpregs[i]); - } - __put_user(regs->fpscr, &sc->sc_fpscr); - __put_user(regs->fpul, &sc->sc_fpul); - - /* non-iBCS2 extensions.. */ - __put_user(mask, &sc->oldmask); -} - -static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc) -{ - int i; - -#define COPY(x) __get_user(regs->x, &sc->sc_##x) - COPY(gregs[0]); COPY(gregs[1]); - COPY(gregs[2]); COPY(gregs[3]); - COPY(gregs[4]); COPY(gregs[5]); - COPY(gregs[6]); COPY(gregs[7]); - COPY(gregs[8]); COPY(gregs[9]); - COPY(gregs[10]); COPY(gregs[11]); - COPY(gregs[12]); COPY(gregs[13]); - COPY(gregs[14]); COPY(gregs[15]); - COPY(gbr); COPY(mach); - COPY(macl); COPY(pr); - COPY(sr); COPY(pc); -#undef COPY - - for (i=0; i<16; i++) { - __get_user(regs->fregs[i], &sc->sc_fpregs[i]); - } - __get_user(regs->fpscr, &sc->sc_fpscr); - __get_user(regs->fpul, &sc->sc_fpul); - - regs->tra = -1; /* disable syscall checks */ - regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUSH4State *regs) -{ - struct target_sigframe *frame; - abi_ulong frame_addr; - int i; - - unwind_gusa(regs); - - frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); - trace_user_setup_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - setup_sigcontext(&frame->sc, regs, set->sig[0]); - - for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { - __put_user(set->sig[i + 1], &frame->extramask[i]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - regs->pr = (unsigned long) ka->sa_restorer; - } else { - /* Generate return code (system call to sigreturn) */ - abi_ulong retcode_addr = frame_addr + - offsetof(struct target_sigframe, retcode); - __put_user(MOVW(2), &frame->retcode[0]); - __put_user(TRAP_NOARG, &frame->retcode[1]); - __put_user((TARGET_NR_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) retcode_addr; - } - - /* Set up registers for signal handler */ - regs->gregs[15] = frame_addr; - regs->gregs[4] = sig; /* Arg for signal handler */ - regs->gregs[5] = 0; - regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc); - regs->pc = (unsigned long) ka->_sa_handler; - regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUSH4State *regs) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - int i; - - unwind_gusa(regs); - - frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); - trace_user_setup_rt_frame(regs, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - tswap_siginfo(&frame->info, info); - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, (unsigned long *)&frame->uc.tuc_link); - __put_user((unsigned long)target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(regs->gregs[15]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->uc.tuc_mcontext, - regs, set->sig[0]); - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - regs->pr = (unsigned long) ka->sa_restorer; - } else { - /* Generate return code (system call to sigreturn) */ - abi_ulong retcode_addr = frame_addr + - offsetof(struct target_rt_sigframe, retcode); - __put_user(MOVW(2), &frame->retcode[0]); - __put_user(TRAP_NOARG, &frame->retcode[1]); - __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]); - regs->pr = (unsigned long) retcode_addr; - } - - /* Set up registers for signal handler */ - regs->gregs[15] = frame_addr; - regs->gregs[4] = sig; /* Arg for signal handler */ - regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info); - regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc); - regs->pc = (unsigned long) ka->_sa_handler; - regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -long do_sigreturn(CPUSH4State *regs) -{ - struct target_sigframe *frame; - abi_ulong frame_addr; - sigset_t blocked; - target_sigset_t target_set; - int i; - int err = 0; - - frame_addr = regs->gregs[15]; - trace_user_do_sigreturn(regs, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - - if (err) - goto badframe; - - target_to_host_sigset_internal(&blocked, &target_set); - set_sigmask(&blocked); - - restore_sigcontext(regs, &frame->sc); - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUSH4State *regs) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - sigset_t blocked; - - frame_addr = regs->gregs[15]; - trace_user_do_rt_sigreturn(regs, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask); - set_sigmask(&blocked); - - restore_sigcontext(regs, &frame->uc.tuc_mcontext); - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, get_sp_from_cpustate(regs)) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} -#elif defined(TARGET_MICROBLAZE) - -struct target_sigcontext { - struct target_pt_regs regs; /* needs to be first */ - uint32_t oldmask; -}; - -struct target_stack_t { - abi_ulong ss_sp; - int ss_flags; - unsigned int ss_size; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - struct target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - uint32_t tuc_extramask[TARGET_NSIG_WORDS - 1]; -}; - -/* Signal frames. */ -struct target_signal_frame { - struct target_ucontext uc; - uint32_t extramask[TARGET_NSIG_WORDS - 1]; - uint32_t tramp[2]; -}; - -struct rt_signal_frame { - siginfo_t info; - ucontext_t uc; - uint32_t tramp[2]; -}; - -static void setup_sigcontext(struct target_sigcontext *sc, CPUMBState *env) -{ - __put_user(env->regs[0], &sc->regs.r0); - __put_user(env->regs[1], &sc->regs.r1); - __put_user(env->regs[2], &sc->regs.r2); - __put_user(env->regs[3], &sc->regs.r3); - __put_user(env->regs[4], &sc->regs.r4); - __put_user(env->regs[5], &sc->regs.r5); - __put_user(env->regs[6], &sc->regs.r6); - __put_user(env->regs[7], &sc->regs.r7); - __put_user(env->regs[8], &sc->regs.r8); - __put_user(env->regs[9], &sc->regs.r9); - __put_user(env->regs[10], &sc->regs.r10); - __put_user(env->regs[11], &sc->regs.r11); - __put_user(env->regs[12], &sc->regs.r12); - __put_user(env->regs[13], &sc->regs.r13); - __put_user(env->regs[14], &sc->regs.r14); - __put_user(env->regs[15], &sc->regs.r15); - __put_user(env->regs[16], &sc->regs.r16); - __put_user(env->regs[17], &sc->regs.r17); - __put_user(env->regs[18], &sc->regs.r18); - __put_user(env->regs[19], &sc->regs.r19); - __put_user(env->regs[20], &sc->regs.r20); - __put_user(env->regs[21], &sc->regs.r21); - __put_user(env->regs[22], &sc->regs.r22); - __put_user(env->regs[23], &sc->regs.r23); - __put_user(env->regs[24], &sc->regs.r24); - __put_user(env->regs[25], &sc->regs.r25); - __put_user(env->regs[26], &sc->regs.r26); - __put_user(env->regs[27], &sc->regs.r27); - __put_user(env->regs[28], &sc->regs.r28); - __put_user(env->regs[29], &sc->regs.r29); - __put_user(env->regs[30], &sc->regs.r30); - __put_user(env->regs[31], &sc->regs.r31); - __put_user(env->sregs[SR_PC], &sc->regs.pc); -} - -static void restore_sigcontext(struct target_sigcontext *sc, CPUMBState *env) -{ - __get_user(env->regs[0], &sc->regs.r0); - __get_user(env->regs[1], &sc->regs.r1); - __get_user(env->regs[2], &sc->regs.r2); - __get_user(env->regs[3], &sc->regs.r3); - __get_user(env->regs[4], &sc->regs.r4); - __get_user(env->regs[5], &sc->regs.r5); - __get_user(env->regs[6], &sc->regs.r6); - __get_user(env->regs[7], &sc->regs.r7); - __get_user(env->regs[8], &sc->regs.r8); - __get_user(env->regs[9], &sc->regs.r9); - __get_user(env->regs[10], &sc->regs.r10); - __get_user(env->regs[11], &sc->regs.r11); - __get_user(env->regs[12], &sc->regs.r12); - __get_user(env->regs[13], &sc->regs.r13); - __get_user(env->regs[14], &sc->regs.r14); - __get_user(env->regs[15], &sc->regs.r15); - __get_user(env->regs[16], &sc->regs.r16); - __get_user(env->regs[17], &sc->regs.r17); - __get_user(env->regs[18], &sc->regs.r18); - __get_user(env->regs[19], &sc->regs.r19); - __get_user(env->regs[20], &sc->regs.r20); - __get_user(env->regs[21], &sc->regs.r21); - __get_user(env->regs[22], &sc->regs.r22); - __get_user(env->regs[23], &sc->regs.r23); - __get_user(env->regs[24], &sc->regs.r24); - __get_user(env->regs[25], &sc->regs.r25); - __get_user(env->regs[26], &sc->regs.r26); - __get_user(env->regs[27], &sc->regs.r27); - __get_user(env->regs[28], &sc->regs.r28); - __get_user(env->regs[29], &sc->regs.r29); - __get_user(env->regs[30], &sc->regs.r30); - __get_user(env->regs[31], &sc->regs.r31); - __get_user(env->sregs[SR_PC], &sc->regs.pc); -} - -static abi_ulong get_sigframe(struct target_sigaction *ka, - CPUMBState *env, int frame_size) -{ - abi_ulong sp = env->regs[1]; - - if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !on_sig_stack(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - return ((sp - frame_size) & -8UL); -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUMBState *env) -{ - struct target_signal_frame *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)) - goto badframe; - - /* Save the mask. */ - __put_user(set->sig[0], &frame->uc.tuc_mcontext.oldmask); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - setup_sigcontext(&frame->uc.tuc_mcontext, env); - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - /* minus 8 is offset to cater for "rtsd r15,8" offset */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - env->regs[15] = ((unsigned long)ka->sa_restorer)-8; - } else { - uint32_t t; - /* Note, these encodings are _big endian_! */ - /* addi r12, r0, __NR_sigreturn */ - t = 0x31800000UL | TARGET_NR_sigreturn; - __put_user(t, frame->tramp + 0); - /* brki r14, 0x8 */ - t = 0xb9cc0008UL; - __put_user(t, frame->tramp + 1); - - /* Return from sighandler will jump to the tramp. - Negative 8 offset because return is rtsd r15, 8 */ - env->regs[15] = frame_addr + offsetof(struct target_signal_frame, tramp) - - 8; - } - - /* Set up registers for signal handler */ - env->regs[1] = frame_addr; - /* Signal handler args: */ - env->regs[5] = sig; /* Arg 0: signum */ - env->regs[6] = 0; - /* arg 1: sigcontext */ - env->regs[7] = frame_addr += offsetof(typeof(*frame), uc); - - /* Offset of 4 to handle microblaze rtid r14, 0 */ - env->sregs[SR_PC] = (unsigned long)ka->_sa_handler; - - unlock_user_struct(frame, frame_addr, 1); - return; -badframe: - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUMBState *env) -{ - fprintf(stderr, "Microblaze setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUMBState *env) -{ - struct target_signal_frame *frame; - abi_ulong frame_addr; - target_sigset_t target_set; - sigset_t set; - int i; - - frame_addr = env->regs[R_SP]; - trace_user_do_sigreturn(env, frame_addr); - /* Make sure the guest isn't playing games. */ - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) - goto badframe; - - /* Restore blocked signals */ - __get_user(target_set.sig[0], &frame->uc.tuc_mcontext.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - restore_sigcontext(&frame->uc.tuc_mcontext, env); - /* We got here through a sigreturn syscall, our path back is via an - rtb insn so setup r14 for that. */ - env->regs[14] = env->sregs[SR_PC]; - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUMBState *env) -{ - trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "Microblaze do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -#elif defined(TARGET_CRIS) - -struct target_sigcontext { - struct target_pt_regs regs; /* needs to be first */ - uint32_t oldmask; - uint32_t usp; /* usp before stacking this gunk on it */ -}; - -/* Signal frames. */ -struct target_signal_frame { - struct target_sigcontext sc; - uint32_t extramask[TARGET_NSIG_WORDS - 1]; - uint16_t retcode[4]; /* Trampoline code. */ -}; - -struct rt_signal_frame { - siginfo_t *pinfo; - void *puc; - siginfo_t info; - ucontext_t uc; - uint16_t retcode[4]; /* Trampoline code. */ -}; - -static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) -{ - __put_user(env->regs[0], &sc->regs.r0); - __put_user(env->regs[1], &sc->regs.r1); - __put_user(env->regs[2], &sc->regs.r2); - __put_user(env->regs[3], &sc->regs.r3); - __put_user(env->regs[4], &sc->regs.r4); - __put_user(env->regs[5], &sc->regs.r5); - __put_user(env->regs[6], &sc->regs.r6); - __put_user(env->regs[7], &sc->regs.r7); - __put_user(env->regs[8], &sc->regs.r8); - __put_user(env->regs[9], &sc->regs.r9); - __put_user(env->regs[10], &sc->regs.r10); - __put_user(env->regs[11], &sc->regs.r11); - __put_user(env->regs[12], &sc->regs.r12); - __put_user(env->regs[13], &sc->regs.r13); - __put_user(env->regs[14], &sc->usp); - __put_user(env->regs[15], &sc->regs.acr); - __put_user(env->pregs[PR_MOF], &sc->regs.mof); - __put_user(env->pregs[PR_SRP], &sc->regs.srp); - __put_user(env->pc, &sc->regs.erp); -} - -static void restore_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) -{ - __get_user(env->regs[0], &sc->regs.r0); - __get_user(env->regs[1], &sc->regs.r1); - __get_user(env->regs[2], &sc->regs.r2); - __get_user(env->regs[3], &sc->regs.r3); - __get_user(env->regs[4], &sc->regs.r4); - __get_user(env->regs[5], &sc->regs.r5); - __get_user(env->regs[6], &sc->regs.r6); - __get_user(env->regs[7], &sc->regs.r7); - __get_user(env->regs[8], &sc->regs.r8); - __get_user(env->regs[9], &sc->regs.r9); - __get_user(env->regs[10], &sc->regs.r10); - __get_user(env->regs[11], &sc->regs.r11); - __get_user(env->regs[12], &sc->regs.r12); - __get_user(env->regs[13], &sc->regs.r13); - __get_user(env->regs[14], &sc->usp); - __get_user(env->regs[15], &sc->regs.acr); - __get_user(env->pregs[PR_MOF], &sc->regs.mof); - __get_user(env->pregs[PR_SRP], &sc->regs.srp); - __get_user(env->pc, &sc->regs.erp); -} - -static abi_ulong get_sigframe(CPUCRISState *env, int framesize) -{ - abi_ulong sp; - /* Align the stack downwards to 4. */ - sp = (env->regs[R_SP] & ~3); - return sp - framesize; -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUCRISState *env) -{ - struct target_signal_frame *frame; - abi_ulong frame_addr; - int i; - - frame_addr = get_sigframe(env, sizeof *frame); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) - goto badframe; - - /* - * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't - * use this trampoline anymore but it sets it up for GDB. - * In QEMU, using the trampoline simplifies things a bit so we use it. - * - * This is movu.w __NR_sigreturn, r9; break 13; - */ - __put_user(0x9c5f, frame->retcode+0); - __put_user(TARGET_NR_sigreturn, - frame->retcode + 1); - __put_user(0xe93d, frame->retcode + 2); - - /* Save the mask. */ - __put_user(set->sig[0], &frame->sc.oldmask); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - setup_sigcontext(&frame->sc, env); - - /* Move the stack and setup the arguments for the handler. */ - env->regs[R_SP] = frame_addr; - env->regs[10] = sig; - env->pc = (unsigned long) ka->_sa_handler; - /* Link SRP so the guest returns through the trampoline. */ - env->pregs[PR_SRP] = frame_addr + offsetof(typeof(*frame), retcode); - - unlock_user_struct(frame, frame_addr, 1); - return; -badframe: - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUCRISState *env) -{ - fprintf(stderr, "CRIS setup_rt_frame: not implemented\n"); -} - -long do_sigreturn(CPUCRISState *env) -{ - struct target_signal_frame *frame; - abi_ulong frame_addr; - target_sigset_t target_set; - sigset_t set; - int i; - - frame_addr = env->regs[R_SP]; - trace_user_do_sigreturn(env, frame_addr); - /* Make sure the guest isn't playing games. */ - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) { - goto badframe; - } - - /* Restore blocked signals */ - __get_user(target_set.sig[0], &frame->sc.oldmask); - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - restore_sigcontext(&frame->sc, env); - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUCRISState *env) -{ - trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "CRIS do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -#elif defined(TARGET_NIOS2) - -#define MCONTEXT_VERSION 2 - -struct target_sigcontext { - int version; - unsigned long gregs[32]; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe { - struct target_siginfo info; - struct target_ucontext uc; -}; - -static unsigned long sigsp(unsigned long sp, struct target_sigaction *ka) -{ - if (unlikely((ka->sa_flags & SA_ONSTACK)) && !sas_ss_flags(sp)) { -#ifdef CONFIG_STACK_GROWSUP - return target_sigaltstack_used.ss_sp; -#else - return target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; -#endif - } - return sp; -} - -static int rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env) -{ - unsigned long *gregs = uc->tuc_mcontext.gregs; - - __put_user(MCONTEXT_VERSION, &uc->tuc_mcontext.version); - __put_user(env->regs[1], &gregs[0]); - __put_user(env->regs[2], &gregs[1]); - __put_user(env->regs[3], &gregs[2]); - __put_user(env->regs[4], &gregs[3]); - __put_user(env->regs[5], &gregs[4]); - __put_user(env->regs[6], &gregs[5]); - __put_user(env->regs[7], &gregs[6]); - __put_user(env->regs[8], &gregs[7]); - __put_user(env->regs[9], &gregs[8]); - __put_user(env->regs[10], &gregs[9]); - __put_user(env->regs[11], &gregs[10]); - __put_user(env->regs[12], &gregs[11]); - __put_user(env->regs[13], &gregs[12]); - __put_user(env->regs[14], &gregs[13]); - __put_user(env->regs[15], &gregs[14]); - __put_user(env->regs[16], &gregs[15]); - __put_user(env->regs[17], &gregs[16]); - __put_user(env->regs[18], &gregs[17]); - __put_user(env->regs[19], &gregs[18]); - __put_user(env->regs[20], &gregs[19]); - __put_user(env->regs[21], &gregs[20]); - __put_user(env->regs[22], &gregs[21]); - __put_user(env->regs[23], &gregs[22]); - __put_user(env->regs[R_RA], &gregs[23]); - __put_user(env->regs[R_FP], &gregs[24]); - __put_user(env->regs[R_GP], &gregs[25]); - __put_user(env->regs[R_EA], &gregs[27]); - __put_user(env->regs[R_SP], &gregs[28]); - - return 0; -} - -static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc, - int *pr2) -{ - int temp; - abi_ulong off, frame_addr = env->regs[R_SP]; - unsigned long *gregs = uc->tuc_mcontext.gregs; - int err; - - /* Always make any pending restarted system calls return -EINTR */ - /* current->restart_block.fn = do_no_restart_syscall; */ - - __get_user(temp, &uc->tuc_mcontext.version); - if (temp != MCONTEXT_VERSION) { - return 1; - } - - /* restore passed registers */ - __get_user(env->regs[1], &gregs[0]); - __get_user(env->regs[2], &gregs[1]); - __get_user(env->regs[3], &gregs[2]); - __get_user(env->regs[4], &gregs[3]); - __get_user(env->regs[5], &gregs[4]); - __get_user(env->regs[6], &gregs[5]); - __get_user(env->regs[7], &gregs[6]); - __get_user(env->regs[8], &gregs[7]); - __get_user(env->regs[9], &gregs[8]); - __get_user(env->regs[10], &gregs[9]); - __get_user(env->regs[11], &gregs[10]); - __get_user(env->regs[12], &gregs[11]); - __get_user(env->regs[13], &gregs[12]); - __get_user(env->regs[14], &gregs[13]); - __get_user(env->regs[15], &gregs[14]); - __get_user(env->regs[16], &gregs[15]); - __get_user(env->regs[17], &gregs[16]); - __get_user(env->regs[18], &gregs[17]); - __get_user(env->regs[19], &gregs[18]); - __get_user(env->regs[20], &gregs[19]); - __get_user(env->regs[21], &gregs[20]); - __get_user(env->regs[22], &gregs[21]); - __get_user(env->regs[23], &gregs[22]); - /* gregs[23] is handled below */ - /* Verify, should this be settable */ - __get_user(env->regs[R_FP], &gregs[24]); - /* Verify, should this be settable */ - __get_user(env->regs[R_GP], &gregs[25]); - /* Not really necessary no user settable bits */ - __get_user(temp, &gregs[26]); - __get_user(env->regs[R_EA], &gregs[27]); - - __get_user(env->regs[R_RA], &gregs[23]); - __get_user(env->regs[R_SP], &gregs[28]); - - off = offsetof(struct target_rt_sigframe, uc.tuc_stack); - err = do_sigaltstack(frame_addr + off, 0, get_sp_from_cpustate(env)); - if (err == -EFAULT) { - return 1; - } - - *pr2 = env->regs[2]; - return 0; -} - -static void *get_sigframe(struct target_sigaction *ka, CPUNios2State *env, - size_t frame_size) -{ - unsigned long usp; - - /* Default to using normal stack. */ - usp = env->regs[R_SP]; - - /* This is the X/Open sanctioned signal stack switching. */ - usp = sigsp(usp, ka); - - /* Verify, is it 32 or 64 bit aligned */ - return (void *)((usp - frame_size) & -8UL); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, - CPUNios2State *env) -{ - struct target_rt_sigframe *frame; - int i, err = 0; - - frame = get_sigframe(ka, env, sizeof(*frame)); - - if (ka->sa_flags & SA_SIGINFO) { - tswap_siginfo(&frame->info, info); - } - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->regs[R_SP]), &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); - err |= rt_setup_ucontext(&frame->uc, env); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user((abi_ulong)set->sig[i], - (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); - } - - if (err) { - goto give_sigsegv; - } - - /* Set up to return from userspace; jump to fixed address sigreturn - trampoline on kuser page. */ - env->regs[R_RA] = (unsigned long) (0x1044); - - /* Set up registers for signal handler */ - env->regs[R_SP] = (unsigned long) frame; - env->regs[4] = (unsigned long) sig; - env->regs[5] = (unsigned long) &frame->info; - env->regs[6] = (unsigned long) &frame->uc; - env->regs[R_EA] = (unsigned long) ka->_sa_handler; - return; - -give_sigsegv: - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sigsegv(sig); - return; -} - -long do_sigreturn(CPUNios2State *env) -{ - trace_user_do_sigreturn(env, 0); - fprintf(stderr, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -long do_rt_sigreturn(CPUNios2State *env) -{ - /* Verify, can we follow the stack back */ - abi_ulong frame_addr = env->regs[R_SP]; - struct target_rt_sigframe *frame; - sigset_t set; - int rval; - - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - do_sigprocmask(SIG_SETMASK, &set, NULL); - - if (rt_restore_ucontext(env, &frame->uc, &rval)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return rval; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return 0; -} -/* TARGET_NIOS2 */ - -#elif defined(TARGET_OPENRISC) - -struct target_sigcontext { - struct target_pt_regs regs; - abi_ulong oldmask; - abi_ulong usp; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe { - abi_ulong pinfo; - uint64_t puc; - struct target_siginfo info; - struct target_sigcontext sc; - struct target_ucontext uc; - unsigned char retcode[16]; /* trampoline code */ -}; - -/* This is the asm-generic/ucontext.h version */ -#if 0 -static int restore_sigcontext(CPUOpenRISCState *regs, - struct target_sigcontext *sc) -{ - unsigned int err = 0; - unsigned long old_usp; - - /* Alwys make any pending restarted system call return -EINTR */ - current_thread_info()->restart_block.fn = do_no_restart_syscall; - - /* restore the regs from &sc->regs (same as sc, since regs is first) - * (sc is already checked for VERIFY_READ since the sigframe was - * checked in sys_sigreturn previously) - */ - - if (copy_from_user(regs, &sc, sizeof(struct target_pt_regs))) { - goto badframe; - } - - /* make sure the U-flag is set so user-mode cannot fool us */ - - regs->sr &= ~SR_SM; - - /* restore the old USP as it was before we stacked the sc etc. - * (we cannot just pop the sigcontext since we aligned the sp and - * stuff after pushing it) - */ - - __get_user(old_usp, &sc->usp); - phx_signal("old_usp 0x%lx", old_usp); - - __PHX__ REALLY /* ??? */ - wrusp(old_usp); - regs->gpr[1] = old_usp; - - /* TODO: the other ports use regs->orig_XX to disable syscall checks - * after this completes, but we don't use that mechanism. maybe we can - * use it now ? - */ - - return err; - -badframe: - return 1; -} -#endif - -/* Set up a signal frame. */ - -static void setup_sigcontext(struct target_sigcontext *sc, - CPUOpenRISCState *regs, - unsigned long mask) -{ - unsigned long usp = cpu_get_gpr(regs, 1); - - /* copy the regs. they are first in sc so we can use sc directly */ - - /*copy_to_user(&sc, regs, sizeof(struct target_pt_regs));*/ - - /* Set the frametype to CRIS_FRAME_NORMAL for the execution of - the signal handler. The frametype will be restored to its previous - value in restore_sigcontext. */ - /*regs->frametype = CRIS_FRAME_NORMAL;*/ - - /* then some other stuff */ - __put_user(mask, &sc->oldmask); - __put_user(usp, &sc->usp); -} - -static inline unsigned long align_sigframe(unsigned long sp) -{ - return sp & ~3UL; -} - -static inline abi_ulong get_sigframe(struct target_sigaction *ka, - CPUOpenRISCState *regs, - size_t frame_size) -{ - unsigned long sp = cpu_get_gpr(regs, 1); - int onsigstack = on_sig_stack(sp); - - /* redzone */ - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp = align_sigframe(sp - frame_size); - - /* - * If we are on the alternate signal stack and would overflow it, don't. - * Return an always-bogus address instead so we will die with SIGSEGV. - */ - - if (onsigstack && !likely(on_sig_stack(sp))) { - return -1L; - } - - return sp; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUOpenRISCState *env) -{ - int err = 0; - abi_ulong frame_addr; - unsigned long return_ip; - struct target_rt_sigframe *frame; - abi_ulong info_addr, uc_addr; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); - __put_user(info_addr, &frame->pinfo); - uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); - __put_user(uc_addr, &frame->puc); - - if (ka->sa_flags & SA_SIGINFO) { - tswap_siginfo(&frame->info, info); - } - - /*err |= __clear_user(&frame->uc, offsetof(ucontext_t, uc_mcontext));*/ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(cpu_get_gpr(env, 1)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->sc, env, set->sig[0]); - - /*err |= copy_to_user(frame->uc.tuc_sigmask, set, sizeof(*set));*/ - - /* trampoline - the desired return ip is the retcode itself */ - return_ip = (unsigned long)&frame->retcode; - /* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */ - __put_user(0xa960, (short *)(frame->retcode + 0)); - __put_user(TARGET_NR_rt_sigreturn, (short *)(frame->retcode + 2)); - __put_user(0x20000001, (unsigned long *)(frame->retcode + 4)); - __put_user(0x15000000, (unsigned long *)(frame->retcode + 8)); - - if (err) { - goto give_sigsegv; - } - - /* TODO what is the current->exec_domain stuff and invmap ? */ - - /* Set up registers for signal handler */ - env->pc = (unsigned long)ka->_sa_handler; /* what we enter NOW */ - cpu_set_gpr(env, 9, (unsigned long)return_ip); /* what we enter LATER */ - cpu_set_gpr(env, 3, (unsigned long)sig); /* arg 1: signo */ - cpu_set_gpr(env, 4, (unsigned long)&frame->info); /* arg 2: (siginfo_t*) */ - cpu_set_gpr(env, 5, (unsigned long)&frame->uc); /* arg 3: ucontext */ - - /* actually move the usp to reflect the stacked frame */ - cpu_set_gpr(env, 1, (unsigned long)frame); - - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -long do_sigreturn(CPUOpenRISCState *env) -{ - trace_user_do_sigreturn(env, 0); - fprintf(stderr, "do_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} - -long do_rt_sigreturn(CPUOpenRISCState *env) -{ - trace_user_do_rt_sigreturn(env, 0); - fprintf(stderr, "do_rt_sigreturn: not implemented\n"); - return -TARGET_ENOSYS; -} -/* TARGET_OPENRISC */ - -#elif defined(TARGET_S390X) - -#define __NUM_GPRS 16 -#define __NUM_FPRS 16 -#define __NUM_ACRS 16 - -#define S390_SYSCALL_SIZE 2 -#define __SIGNAL_FRAMESIZE 160 /* FIXME: 31-bit mode -> 96 */ - -#define _SIGCONTEXT_NSIG 64 -#define _SIGCONTEXT_NSIG_BPW 64 /* FIXME: 31-bit mode -> 32 */ -#define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW) -#define _SIGMASK_COPY_SIZE (sizeof(unsigned long)*_SIGCONTEXT_NSIG_WORDS) -#define PSW_ADDR_AMODE 0x0000000000000000UL /* 0x80000000UL for 31-bit */ -#define S390_SYSCALL_OPCODE ((uint16_t)0x0a00) - -typedef struct { - target_psw_t psw; - target_ulong gprs[__NUM_GPRS]; - unsigned int acrs[__NUM_ACRS]; -} target_s390_regs_common; - -typedef struct { - unsigned int fpc; - double fprs[__NUM_FPRS]; -} target_s390_fp_regs; - -typedef struct { - target_s390_regs_common regs; - target_s390_fp_regs fpregs; -} target_sigregs; - -struct target_sigcontext { - target_ulong oldmask[_SIGCONTEXT_NSIG_WORDS]; - target_sigregs *sregs; -}; - -typedef struct { - uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; - struct target_sigcontext sc; - target_sigregs sregs; - int signo; - uint8_t retcode[S390_SYSCALL_SIZE]; -} sigframe; - -struct target_ucontext { - target_ulong tuc_flags; - struct target_ucontext *tuc_link; - target_stack_t tuc_stack; - target_sigregs tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -typedef struct { - uint8_t callee_used_stack[__SIGNAL_FRAMESIZE]; - uint8_t retcode[S390_SYSCALL_SIZE]; - struct target_siginfo info; - struct target_ucontext uc; -} rt_sigframe; - -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUS390XState *env, size_t frame_size) -{ - abi_ulong sp; - - /* Default to using normal stack */ - sp = env->regs[15]; - - /* This is the X/Open sanctioned signal stack switching. */ - if (ka->sa_flags & TARGET_SA_ONSTACK) { - if (!sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + - target_sigaltstack_used.ss_size; - } - } - - /* This is the legacy signal stack switching. */ - else if (/* FIXME !user_mode(regs) */ 0 && - !(ka->sa_flags & TARGET_SA_RESTORER) && - ka->sa_restorer) { - sp = (abi_ulong) ka->sa_restorer; - } - - return (sp - frame_size) & -8ul; -} - -static void save_sigregs(CPUS390XState *env, target_sigregs *sregs) -{ - int i; - //save_access_regs(current->thread.acrs); FIXME - - /* Copy a 'clean' PSW mask to the user to avoid leaking - information about whether PER is currently on. */ - __put_user(env->psw.mask, &sregs->regs.psw.mask); - __put_user(env->psw.addr, &sregs->regs.psw.addr); - for (i = 0; i < 16; i++) { - __put_user(env->regs[i], &sregs->regs.gprs[i]); - } - for (i = 0; i < 16; i++) { - __put_user(env->aregs[i], &sregs->regs.acrs[i]); - } - /* - * We have to store the fp registers to current->thread.fp_regs - * to merge them with the emulated registers. - */ - //save_fp_regs(¤t->thread.fp_regs); FIXME - for (i = 0; i < 16; i++) { - __put_user(get_freg(env, i)->ll, &sregs->fpregs.fprs[i]); - } -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUS390XState *env) -{ - sigframe *frame; - abi_ulong frame_addr; - - 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)) { - goto give_sigsegv; - } - - __put_user(set->sig[0], &frame->sc.oldmask[0]); - - save_sigregs(env, &frame->sregs); - - __put_user((abi_ulong)(unsigned long)&frame->sregs, - (abi_ulong *)&frame->sc.sregs); - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - env->regs[14] = (unsigned long) - ka->sa_restorer | PSW_ADDR_AMODE; - } else { - env->regs[14] = (frame_addr + offsetof(sigframe, retcode)) - | PSW_ADDR_AMODE; - __put_user(S390_SYSCALL_OPCODE | TARGET_NR_sigreturn, - (uint16_t *)(frame->retcode)); - } - - /* Set up backchain. */ - __put_user(env->regs[15], (abi_ulong *) frame); - - /* Set up registers for signal handler */ - env->regs[15] = frame_addr; - env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; - - env->regs[2] = sig; //map_signal(sig); - env->regs[3] = frame_addr += offsetof(typeof(*frame), sc); - - /* We forgot to include these in the sigcontext. - To avoid breaking binary compatibility, they are passed as args. */ - env->regs[4] = 0; // FIXME: no clue... current->thread.trap_no; - env->regs[5] = 0; // FIXME: no clue... current->thread.prot_addr; - - /* Place signal number on stack to allow backtrace from handler. */ - __put_user(env->regs[2], &frame->signo); - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUS390XState *env) -{ - int i; - rt_sigframe *frame; - abi_ulong frame_addr; - - frame_addr = get_sigframe(ka, env, sizeof *frame); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - tswap_siginfo(&frame->info, info); - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user((abi_ulong)0, (abi_ulong *)&frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); - save_sigregs(env, &frame->uc.tuc_mcontext); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user((abi_ulong)set->sig[i], - (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace. If provided, use a stub - already in userspace. */ - if (ka->sa_flags & TARGET_SA_RESTORER) { - env->regs[14] = (unsigned long) ka->sa_restorer | PSW_ADDR_AMODE; - } else { - env->regs[14] = (unsigned long) frame->retcode | PSW_ADDR_AMODE; - __put_user(S390_SYSCALL_OPCODE | TARGET_NR_rt_sigreturn, - (uint16_t *)(frame->retcode)); - } - - /* Set up backchain. */ - __put_user(env->regs[15], (abi_ulong *) frame); - - /* Set up registers for signal handler */ - env->regs[15] = frame_addr; - env->psw.addr = (target_ulong) ka->_sa_handler | PSW_ADDR_AMODE; - - env->regs[2] = sig; //map_signal(sig); - env->regs[3] = frame_addr + offsetof(typeof(*frame), info); - env->regs[4] = frame_addr + offsetof(typeof(*frame), uc); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -static int -restore_sigregs(CPUS390XState *env, target_sigregs *sc) -{ - int err = 0; - int i; - - for (i = 0; i < 16; i++) { - __get_user(env->regs[i], &sc->regs.gprs[i]); - } - - __get_user(env->psw.mask, &sc->regs.psw.mask); - trace_user_s390x_restore_sigregs(env, (unsigned long long)sc->regs.psw.addr, - (unsigned long long)env->psw.addr); - __get_user(env->psw.addr, &sc->regs.psw.addr); - /* FIXME: 31-bit -> | PSW_ADDR_AMODE */ - - for (i = 0; i < 16; i++) { - __get_user(env->aregs[i], &sc->regs.acrs[i]); - } - for (i = 0; i < 16; i++) { - __get_user(get_freg(env, i)->ll, &sc->fpregs.fprs[i]); - } - - return err; -} - -long do_sigreturn(CPUS390XState *env) -{ - sigframe *frame; - abi_ulong frame_addr = env->regs[15]; - target_sigset_t target_set; - sigset_t set; - - trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - __get_user(target_set.sig[0], &frame->sc.oldmask[0]); - - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); /* ~_BLOCKABLE? */ - - if (restore_sigregs(env, &frame->sregs)) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUS390XState *env) -{ - rt_sigframe *frame; - abi_ulong frame_addr = env->regs[15]; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - - set_sigmask(&set); /* ~_BLOCKABLE? */ - - if (restore_sigregs(env, &frame->uc.tuc_mcontext)) { - goto badframe; - } - - if (do_sigaltstack(frame_addr + offsetof(rt_sigframe, uc.tuc_stack), 0, - get_sp_from_cpustate(env)) == -EFAULT) { - goto badframe; - } - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_PPC) - -/* Size of dummy stack frame allocated when calling signal handler. - See arch/powerpc/include/asm/ptrace.h. */ -#if defined(TARGET_PPC64) -#define SIGNAL_FRAMESIZE 128 -#else -#define SIGNAL_FRAMESIZE 64 -#endif - -/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; - on 64-bit PPC, sigcontext and mcontext are one and the same. */ -struct target_mcontext { - target_ulong mc_gregs[48]; - /* Includes fpscr. */ - uint64_t mc_fregs[33]; -#if defined(TARGET_PPC64) - /* Pointer to the vector regs */ - target_ulong v_regs; -#else - target_ulong mc_pad[2]; -#endif - /* We need to handle Altivec and SPE at the same time, which no - kernel needs to do. Fortunately, the kernel defines this bit to - be Altivec-register-large all the time, rather than trying to - twiddle it based on the specific platform. */ - union { - /* SPE vector registers. One extra for SPEFSCR. */ - uint32_t spe[33]; - /* Altivec vector registers. The packing of VSCR and VRSAVE - varies depending on whether we're PPC64 or not: PPC64 splits - them apart; PPC32 stuffs them together. - We also need to account for the VSX registers on PPC64 - */ -#if defined(TARGET_PPC64) -#define QEMU_NVRREG (34 + 16) - /* On ppc64, this mcontext structure is naturally *unaligned*, - * or rather it is aligned on a 8 bytes boundary but not on - * a 16 bytes one. This pad fixes it up. This is also why the - * vector regs are referenced by the v_regs pointer above so - * any amount of padding can be added here - */ - target_ulong pad; -#else - /* On ppc32, we are already aligned to 16 bytes */ -#define QEMU_NVRREG 33 -#endif - /* We cannot use ppc_avr_t here as we do *not* want the implied - * 16-bytes alignment that would result from it. This would have - * the effect of making the whole struct target_mcontext aligned - * which breaks the layout of struct target_ucontext on ppc64. - */ - uint64_t altivec[QEMU_NVRREG][2]; -#undef QEMU_NVRREG - } mc_vregs; -}; - -/* See arch/powerpc/include/asm/sigcontext.h. */ -struct target_sigcontext { - target_ulong _unused[4]; - int32_t signal; -#if defined(TARGET_PPC64) - int32_t pad0; -#endif - target_ulong handler; - target_ulong oldmask; - target_ulong regs; /* struct pt_regs __user * */ -#if defined(TARGET_PPC64) - struct target_mcontext mcontext; -#endif -}; - -/* Indices for target_mcontext.mc_gregs, below. - See arch/powerpc/include/asm/ptrace.h for details. */ -enum { - TARGET_PT_R0 = 0, - TARGET_PT_R1 = 1, - TARGET_PT_R2 = 2, - TARGET_PT_R3 = 3, - TARGET_PT_R4 = 4, - TARGET_PT_R5 = 5, - TARGET_PT_R6 = 6, - TARGET_PT_R7 = 7, - TARGET_PT_R8 = 8, - TARGET_PT_R9 = 9, - TARGET_PT_R10 = 10, - TARGET_PT_R11 = 11, - TARGET_PT_R12 = 12, - TARGET_PT_R13 = 13, - TARGET_PT_R14 = 14, - TARGET_PT_R15 = 15, - TARGET_PT_R16 = 16, - TARGET_PT_R17 = 17, - TARGET_PT_R18 = 18, - TARGET_PT_R19 = 19, - TARGET_PT_R20 = 20, - TARGET_PT_R21 = 21, - TARGET_PT_R22 = 22, - TARGET_PT_R23 = 23, - TARGET_PT_R24 = 24, - TARGET_PT_R25 = 25, - TARGET_PT_R26 = 26, - TARGET_PT_R27 = 27, - TARGET_PT_R28 = 28, - TARGET_PT_R29 = 29, - TARGET_PT_R30 = 30, - TARGET_PT_R31 = 31, - TARGET_PT_NIP = 32, - TARGET_PT_MSR = 33, - TARGET_PT_ORIG_R3 = 34, - TARGET_PT_CTR = 35, - TARGET_PT_LNK = 36, - TARGET_PT_XER = 37, - TARGET_PT_CCR = 38, - /* Yes, there are two registers with #39. One is 64-bit only. */ - TARGET_PT_MQ = 39, - TARGET_PT_SOFTE = 39, - TARGET_PT_TRAP = 40, - TARGET_PT_DAR = 41, - TARGET_PT_DSISR = 42, - TARGET_PT_RESULT = 43, - TARGET_PT_REGS_COUNT = 44 -}; - - -struct target_ucontext { - target_ulong tuc_flags; - target_ulong tuc_link; /* ucontext_t __user * */ - struct target_sigaltstack tuc_stack; -#if !defined(TARGET_PPC64) - int32_t tuc_pad[7]; - target_ulong tuc_regs; /* struct mcontext __user * - points to uc_mcontext field */ -#endif - target_sigset_t tuc_sigmask; -#if defined(TARGET_PPC64) - target_sigset_t unused[15]; /* Allow for uc_sigmask growth */ - struct target_sigcontext tuc_sigcontext; -#else - int32_t tuc_maskext[30]; - int32_t tuc_pad2[3]; - struct target_mcontext tuc_mcontext; -#endif -}; - -/* See arch/powerpc/kernel/signal_32.c. */ -struct target_sigframe { - struct target_sigcontext sctx; - struct target_mcontext mctx; - int32_t abigap[56]; -}; - -#if defined(TARGET_PPC64) - -#define TARGET_TRAMP_SIZE 6 - -struct target_rt_sigframe { - /* sys_rt_sigreturn requires the ucontext be the first field */ - struct target_ucontext uc; - target_ulong _unused[2]; - uint32_t trampoline[TARGET_TRAMP_SIZE]; - target_ulong pinfo; /* struct siginfo __user * */ - target_ulong puc; /* void __user * */ - struct target_siginfo info; - /* 64 bit ABI allows for 288 bytes below sp before decrementing it. */ - char abigap[288]; -} __attribute__((aligned(16))); - -#else - -struct target_rt_sigframe { - struct target_siginfo info; - struct target_ucontext uc; - int32_t abigap[56]; -}; - -#endif - -#if defined(TARGET_PPC64) - -struct target_func_ptr { - target_ulong entry; - target_ulong toc; -}; - -#endif - -/* We use the mc_pad field for the signal return trampoline. */ -#define tramp mc_pad - -/* See arch/powerpc/kernel/signal.c. */ -static target_ulong get_sigframe(struct target_sigaction *ka, - CPUPPCState *env, - int frame_size) -{ - target_ulong oldsp; - - oldsp = env->gpr[1]; - - if ((ka->sa_flags & TARGET_SA_ONSTACK) && - (sas_ss_flags(oldsp) == 0)) { - oldsp = (target_sigaltstack_used.ss_sp - + target_sigaltstack_used.ss_size); - } - - return (oldsp - frame_size) & ~0xFUL; -} - -#if ((defined(TARGET_WORDS_BIGENDIAN) && defined(HOST_WORDS_BIGENDIAN)) || \ - (!defined(HOST_WORDS_BIGENDIAN) && !defined(TARGET_WORDS_BIGENDIAN))) -#define PPC_VEC_HI 0 -#define PPC_VEC_LO 1 -#else -#define PPC_VEC_HI 1 -#define PPC_VEC_LO 0 -#endif - - -static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) -{ - target_ulong msr = env->msr; - int i; - target_ulong ccr = 0; - - /* In general, the kernel attempts to be intelligent about what it - needs to save for Altivec/FP/SPE registers. We don't care that - much, so we just go ahead and save everything. */ - - /* Save general registers. */ - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - __put_user(env->gpr[i], &frame->mc_gregs[i]); - } - __put_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); - __put_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); - __put_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __put_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); - - for (i = 0; i < ARRAY_SIZE(env->crf); i++) { - ccr |= env->crf[i] << (32 - ((i + 1) * 4)); - } - __put_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); - - /* Save Altivec registers if necessary. */ - if (env->insns_flags & PPC_ALTIVEC) { - uint32_t *vrsave; - for (i = 0; i < ARRAY_SIZE(env->avr); i++) { - ppc_avr_t *avr = &env->avr[i]; - ppc_avr_t *vreg = (ppc_avr_t *)&frame->mc_vregs.altivec[i]; - - __put_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); - __put_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]); - } - /* Set MSR_VR in the saved MSR value to indicate that - frame->mc_vregs contains valid data. */ - msr |= MSR_VR; -#if defined(TARGET_PPC64) - vrsave = (uint32_t *)&frame->mc_vregs.altivec[33]; - /* 64-bit needs to put a pointer to the vectors in the frame */ - __put_user(h2g(frame->mc_vregs.altivec), &frame->v_regs); -#else - vrsave = (uint32_t *)&frame->mc_vregs.altivec[32]; -#endif - __put_user((uint32_t)env->spr[SPR_VRSAVE], vrsave); - } - - /* Save VSX second halves */ - if (env->insns_flags2 & PPC2_VSX) { - uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; - for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { - __put_user(env->vsr[i], &vsregs[i]); - } - } - - /* Save floating point registers. */ - if (env->insns_flags & PPC_FLOAT) { - for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { - __put_user(env->fpr[i], &frame->mc_fregs[i]); - } - __put_user((uint64_t) env->fpscr, &frame->mc_fregs[32]); - } - - /* Save SPE registers. The kernel only saves the high half. */ - if (env->insns_flags & PPC_SPE) { -#if defined(TARGET_PPC64) - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - __put_user(env->gpr[i] >> 32, &frame->mc_vregs.spe[i]); - } -#else - for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { - __put_user(env->gprh[i], &frame->mc_vregs.spe[i]); - } -#endif - /* Set MSR_SPE in the saved MSR value to indicate that - frame->mc_vregs contains valid data. */ - msr |= MSR_SPE; - __put_user(env->spe_fscr, &frame->mc_vregs.spe[32]); - } - - /* Store MSR. */ - __put_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); -} - -static void encode_trampoline(int sigret, uint32_t *tramp) -{ - /* Set up the sigreturn trampoline: li r0,sigret; sc. */ - if (sigret) { - __put_user(0x38000000 | sigret, &tramp[0]); - __put_user(0x44000002, &tramp[1]); - } -} - -static void restore_user_regs(CPUPPCState *env, - struct target_mcontext *frame, int sig) -{ - target_ulong save_r2 = 0; - target_ulong msr; - target_ulong ccr; - - int i; - - if (!sig) { - save_r2 = env->gpr[2]; - } - - /* Restore general registers. */ - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - __get_user(env->gpr[i], &frame->mc_gregs[i]); - } - __get_user(env->nip, &frame->mc_gregs[TARGET_PT_NIP]); - __get_user(env->ctr, &frame->mc_gregs[TARGET_PT_CTR]); - __get_user(env->lr, &frame->mc_gregs[TARGET_PT_LNK]); - __get_user(env->xer, &frame->mc_gregs[TARGET_PT_XER]); - __get_user(ccr, &frame->mc_gregs[TARGET_PT_CCR]); - - for (i = 0; i < ARRAY_SIZE(env->crf); i++) { - env->crf[i] = (ccr >> (32 - ((i + 1) * 4))) & 0xf; - } - - if (!sig) { - env->gpr[2] = save_r2; - } - /* Restore MSR. */ - __get_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); - - /* If doing signal return, restore the previous little-endian mode. */ - if (sig) - env->msr = (env->msr & ~(1ull << MSR_LE)) | (msr & (1ull << MSR_LE)); - - /* Restore Altivec registers if necessary. */ - if (env->insns_flags & PPC_ALTIVEC) { - ppc_avr_t *v_regs; - uint32_t *vrsave; -#if defined(TARGET_PPC64) - uint64_t v_addr; - /* 64-bit needs to recover the pointer to the vectors from the frame */ - __get_user(v_addr, &frame->v_regs); - v_regs = g2h(v_addr); -#else - v_regs = (ppc_avr_t *)frame->mc_vregs.altivec; -#endif - for (i = 0; i < ARRAY_SIZE(env->avr); i++) { - ppc_avr_t *avr = &env->avr[i]; - ppc_avr_t *vreg = &v_regs[i]; - - __get_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); - __get_user(avr->u64[PPC_VEC_LO], &vreg->u64[1]); - } - /* Set MSR_VEC in the saved MSR value to indicate that - frame->mc_vregs contains valid data. */ -#if defined(TARGET_PPC64) - vrsave = (uint32_t *)&v_regs[33]; -#else - vrsave = (uint32_t *)&v_regs[32]; -#endif - __get_user(env->spr[SPR_VRSAVE], vrsave); - } - - /* Restore VSX second halves */ - if (env->insns_flags2 & PPC2_VSX) { - uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; - for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { - __get_user(env->vsr[i], &vsregs[i]); - } - } - - /* Restore floating point registers. */ - if (env->insns_flags & PPC_FLOAT) { - uint64_t fpscr; - for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { - __get_user(env->fpr[i], &frame->mc_fregs[i]); - } - __get_user(fpscr, &frame->mc_fregs[32]); - env->fpscr = (uint32_t) fpscr; - } - - /* Save SPE registers. The kernel only saves the high half. */ - if (env->insns_flags & PPC_SPE) { -#if defined(TARGET_PPC64) - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - uint32_t hi; - - __get_user(hi, &frame->mc_vregs.spe[i]); - env->gpr[i] = ((uint64_t)hi << 32) | ((uint32_t) env->gpr[i]); - } -#else - for (i = 0; i < ARRAY_SIZE(env->gprh); i++) { - __get_user(env->gprh[i], &frame->mc_vregs.spe[i]); - } -#endif - __get_user(env->spe_fscr, &frame->mc_vregs.spe[32]); - } -} - -#if !defined(TARGET_PPC64) -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUPPCState *env) -{ - struct target_sigframe *frame; - struct target_sigcontext *sc; - target_ulong frame_addr, newsp; - int err = 0; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) - goto sigsegv; - sc = &frame->sctx; - - __put_user(ka->_sa_handler, &sc->handler); - __put_user(set->sig[0], &sc->oldmask); - __put_user(set->sig[1], &sc->_unused[3]); - __put_user(h2g(&frame->mctx), &sc->regs); - __put_user(sig, &sc->signal); - - /* Save user regs. */ - save_user_regs(env, &frame->mctx); - - /* Construct the trampoline code on the stack. */ - encode_trampoline(TARGET_NR_sigreturn, (uint32_t *)&frame->mctx.tramp); - - /* The kernel checks for the presence of a VDSO here. We don't - emulate a vdso, so use a sigreturn system call. */ - env->lr = (target_ulong) h2g(frame->mctx.tramp); - - /* Turn off all fp exceptions. */ - env->fpscr = 0; - - /* Create a stack frame for the caller of the handler. */ - newsp = frame_addr - SIGNAL_FRAMESIZE; - err |= put_user(env->gpr[1], newsp, target_ulong); - - if (err) - goto sigsegv; - - /* Set up registers for signal handler. */ - env->gpr[1] = newsp; - env->gpr[3] = sig; - env->gpr[4] = frame_addr + offsetof(struct target_sigframe, sctx); - - env->nip = (target_ulong) ka->_sa_handler; - - /* Signal handlers are entered in big-endian mode. */ - env->msr &= ~(1ull << MSR_LE); - - unlock_user_struct(frame, frame_addr, 1); - return; - -sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} -#endif /* !defined(TARGET_PPC64) */ - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUPPCState *env) -{ - struct target_rt_sigframe *rt_sf; - uint32_t *trampptr = 0; - struct target_mcontext *mctx = 0; - target_ulong rt_sf_addr, newsp = 0; - int i, err = 0; -#if defined(TARGET_PPC64) - struct target_sigcontext *sc = 0; - struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; -#endif - - rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf)); - if (!lock_user_struct(VERIFY_WRITE, rt_sf, rt_sf_addr, 1)) - goto sigsegv; - - tswap_siginfo(&rt_sf->info, info); - - __put_user(0, &rt_sf->uc.tuc_flags); - __put_user(0, &rt_sf->uc.tuc_link); - __put_user((target_ulong)target_sigaltstack_used.ss_sp, - &rt_sf->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->gpr[1]), - &rt_sf->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &rt_sf->uc.tuc_stack.ss_size); -#if !defined(TARGET_PPC64) - __put_user(h2g (&rt_sf->uc.tuc_mcontext), - &rt_sf->uc.tuc_regs); -#endif - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]); - } - -#if defined(TARGET_PPC64) - mctx = &rt_sf->uc.tuc_sigcontext.mcontext; - trampptr = &rt_sf->trampoline[0]; - - sc = &rt_sf->uc.tuc_sigcontext; - __put_user(h2g(mctx), &sc->regs); - __put_user(sig, &sc->signal); -#else - mctx = &rt_sf->uc.tuc_mcontext; - trampptr = (uint32_t *)&rt_sf->uc.tuc_mcontext.tramp; -#endif - - save_user_regs(env, mctx); - encode_trampoline(TARGET_NR_rt_sigreturn, trampptr); - - /* The kernel checks for the presence of a VDSO here. We don't - emulate a vdso, so use a sigreturn system call. */ - env->lr = (target_ulong) h2g(trampptr); - - /* Turn off all fp exceptions. */ - env->fpscr = 0; - - /* Create a stack frame for the caller of the handler. */ - newsp = rt_sf_addr - (SIGNAL_FRAMESIZE + 16); - err |= put_user(env->gpr[1], newsp, target_ulong); - - if (err) - goto sigsegv; - - /* Set up registers for signal handler. */ - env->gpr[1] = newsp; - env->gpr[3] = (target_ulong) sig; - env->gpr[4] = (target_ulong) h2g(&rt_sf->info); - env->gpr[5] = (target_ulong) h2g(&rt_sf->uc); - env->gpr[6] = (target_ulong) h2g(rt_sf); - -#if defined(TARGET_PPC64) - if (get_ppc64_abi(image) < 2) { - /* ELFv1 PPC64 function pointers are pointers to OPD entries. */ - struct target_func_ptr *handler = - (struct target_func_ptr *)g2h(ka->_sa_handler); - env->nip = tswapl(handler->entry); - env->gpr[2] = tswapl(handler->toc); - } else { - /* ELFv2 PPC64 function pointers are entry points, but R12 - * must also be set */ - env->nip = tswapl((target_ulong) ka->_sa_handler); - env->gpr[12] = env->nip; - } -#else - env->nip = (target_ulong) ka->_sa_handler; -#endif - - /* Signal handlers are entered in big-endian mode. */ - env->msr &= ~(1ull << MSR_LE); - - unlock_user_struct(rt_sf, rt_sf_addr, 1); - return; - -sigsegv: - unlock_user_struct(rt_sf, rt_sf_addr, 1); - force_sigsegv(sig); - -} - -#if !defined(TARGET_PPC64) -long do_sigreturn(CPUPPCState *env) -{ - struct target_sigcontext *sc = NULL; - struct target_mcontext *sr = NULL; - target_ulong sr_addr = 0, sc_addr; - sigset_t blocked; - target_sigset_t set; - - sc_addr = env->gpr[1] + SIGNAL_FRAMESIZE; - if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) - goto sigsegv; - -#if defined(TARGET_PPC64) - set.sig[0] = sc->oldmask + ((uint64_t)(sc->_unused[3]) << 32); -#else - __get_user(set.sig[0], &sc->oldmask); - __get_user(set.sig[1], &sc->_unused[3]); -#endif - target_to_host_sigset_internal(&blocked, &set); - set_sigmask(&blocked); - - __get_user(sr_addr, &sc->regs); - if (!lock_user_struct(VERIFY_READ, sr, sr_addr, 1)) - goto sigsegv; - restore_user_regs(env, sr, 1); - - unlock_user_struct(sr, sr_addr, 1); - unlock_user_struct(sc, sc_addr, 1); - return -TARGET_QEMU_ESIGRETURN; - -sigsegv: - unlock_user_struct(sr, sr_addr, 1); - unlock_user_struct(sc, sc_addr, 1); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} -#endif /* !defined(TARGET_PPC64) */ - -/* See arch/powerpc/kernel/signal_32.c. */ -static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) -{ - struct target_mcontext *mcp; - target_ulong mcp_addr; - sigset_t blocked; - target_sigset_t set; - - if (copy_from_user(&set, h2g(ucp) + offsetof(struct target_ucontext, tuc_sigmask), - sizeof (set))) - return 1; - -#if defined(TARGET_PPC64) - mcp_addr = h2g(ucp) + - offsetof(struct target_ucontext, tuc_sigcontext.mcontext); -#else - __get_user(mcp_addr, &ucp->tuc_regs); -#endif - - if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) - return 1; - - target_to_host_sigset_internal(&blocked, &set); - set_sigmask(&blocked); - restore_user_regs(env, mcp, sig); - - unlock_user_struct(mcp, mcp_addr, 1); - return 0; -} - -long do_rt_sigreturn(CPUPPCState *env) -{ - struct target_rt_sigframe *rt_sf = NULL; - target_ulong rt_sf_addr; - - rt_sf_addr = env->gpr[1] + SIGNAL_FRAMESIZE + 16; - if (!lock_user_struct(VERIFY_READ, rt_sf, rt_sf_addr, 1)) - goto sigsegv; - - if (do_setcontext(&rt_sf->uc, env, 1)) - goto sigsegv; - - do_sigaltstack(rt_sf_addr - + offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, env->gpr[1]); - - unlock_user_struct(rt_sf, rt_sf_addr, 1); - return -TARGET_QEMU_ESIGRETURN; - -sigsegv: - unlock_user_struct(rt_sf, rt_sf_addr, 1); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_M68K) - -struct target_sigcontext { - abi_ulong sc_mask; - abi_ulong sc_usp; - abi_ulong sc_d0; - abi_ulong sc_d1; - abi_ulong sc_a0; - abi_ulong sc_a1; - unsigned short sc_sr; - abi_ulong sc_pc; -}; - -struct target_sigframe -{ - abi_ulong pretcode; - int sig; - int code; - abi_ulong psc; - char retcode[8]; - abi_ulong extramask[TARGET_NSIG_WORDS-1]; - struct target_sigcontext sc; -}; - -typedef int target_greg_t; -#define TARGET_NGREG 18 -typedef target_greg_t target_gregset_t[TARGET_NGREG]; - -typedef struct target_fpregset { - int f_fpcntl[3]; - int f_fpregs[8*3]; -} target_fpregset_t; - -struct target_mcontext { - int version; - target_gregset_t gregs; - target_fpregset_t fpregs; -}; - -#define TARGET_MCONTEXT_VERSION 2 - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_mcontext tuc_mcontext; - abi_long tuc_filler[80]; - target_sigset_t tuc_sigmask; -}; - -struct target_rt_sigframe -{ - abi_ulong pretcode; - int sig; - abi_ulong pinfo; - abi_ulong puc; - char retcode[8]; - struct target_siginfo info; - struct target_ucontext uc; -}; - -static void setup_sigcontext(struct target_sigcontext *sc, CPUM68KState *env, - abi_ulong mask) -{ - uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); - __put_user(mask, &sc->sc_mask); - __put_user(env->aregs[7], &sc->sc_usp); - __put_user(env->dregs[0], &sc->sc_d0); - __put_user(env->dregs[1], &sc->sc_d1); - __put_user(env->aregs[0], &sc->sc_a0); - __put_user(env->aregs[1], &sc->sc_a1); - __put_user(sr, &sc->sc_sr); - __put_user(env->pc, &sc->sc_pc); -} - -static void -restore_sigcontext(CPUM68KState *env, struct target_sigcontext *sc) -{ - int temp; - - __get_user(env->aregs[7], &sc->sc_usp); - __get_user(env->dregs[0], &sc->sc_d0); - __get_user(env->dregs[1], &sc->sc_d1); - __get_user(env->aregs[0], &sc->sc_a0); - __get_user(env->aregs[1], &sc->sc_a1); - __get_user(env->pc, &sc->sc_pc); - __get_user(temp, &sc->sc_sr); - cpu_m68k_set_ccr(env, temp); -} - -/* - * Determine which stack to use.. - */ -static inline abi_ulong -get_sigframe(struct target_sigaction *ka, CPUM68KState *regs, - size_t frame_size) -{ - unsigned long sp; - - sp = regs->aregs[7]; - - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags (sp) == 0)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - return ((sp - frame_size) & -8UL); -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUM68KState *env) -{ - struct target_sigframe *frame; - abi_ulong frame_addr; - abi_ulong retcode_addr; - abi_ulong sc_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)) { - goto give_sigsegv; - } - - __put_user(sig, &frame->sig); - - sc_addr = frame_addr + offsetof(struct target_sigframe, sc); - __put_user(sc_addr, &frame->psc); - - setup_sigcontext(&frame->sc, env, set->sig[0]); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->extramask[i - 1]); - } - - /* Set up to return from userspace. */ - - retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); - __put_user(retcode_addr, &frame->pretcode); - - /* moveq #,d0; trap #0 */ - - __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), - (uint32_t *)(frame->retcode)); - - /* Set up to return from userspace */ - - env->aregs[7] = frame_addr; - env->pc = ka->_sa_handler; - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -static inline void target_rt_save_fpu_state(struct target_ucontext *uc, - CPUM68KState *env) -{ - int i; - target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; - - __put_user(env->fpcr, &fpregs->f_fpcntl[0]); - __put_user(env->fpsr, &fpregs->f_fpcntl[1]); - /* fpiar is not emulated */ - - for (i = 0; i < 8; i++) { - uint32_t high = env->fregs[i].d.high << 16; - __put_user(high, &fpregs->f_fpregs[i * 3]); - __put_user(env->fregs[i].d.low, - (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); - } -} - -static inline int target_rt_setup_ucontext(struct target_ucontext *uc, - CPUM68KState *env) -{ - target_greg_t *gregs = uc->tuc_mcontext.gregs; - uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); - - __put_user(TARGET_MCONTEXT_VERSION, &uc->tuc_mcontext.version); - __put_user(env->dregs[0], &gregs[0]); - __put_user(env->dregs[1], &gregs[1]); - __put_user(env->dregs[2], &gregs[2]); - __put_user(env->dregs[3], &gregs[3]); - __put_user(env->dregs[4], &gregs[4]); - __put_user(env->dregs[5], &gregs[5]); - __put_user(env->dregs[6], &gregs[6]); - __put_user(env->dregs[7], &gregs[7]); - __put_user(env->aregs[0], &gregs[8]); - __put_user(env->aregs[1], &gregs[9]); - __put_user(env->aregs[2], &gregs[10]); - __put_user(env->aregs[3], &gregs[11]); - __put_user(env->aregs[4], &gregs[12]); - __put_user(env->aregs[5], &gregs[13]); - __put_user(env->aregs[6], &gregs[14]); - __put_user(env->aregs[7], &gregs[15]); - __put_user(env->pc, &gregs[16]); - __put_user(sr, &gregs[17]); - - target_rt_save_fpu_state(uc, env); - - return 0; -} - -static inline void target_rt_restore_fpu_state(CPUM68KState *env, - struct target_ucontext *uc) -{ - int i; - target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; - uint32_t fpcr; - - __get_user(fpcr, &fpregs->f_fpcntl[0]); - cpu_m68k_set_fpcr(env, fpcr); - __get_user(env->fpsr, &fpregs->f_fpcntl[1]); - /* fpiar is not emulated */ - - for (i = 0; i < 8; i++) { - uint32_t high; - __get_user(high, &fpregs->f_fpregs[i * 3]); - env->fregs[i].d.high = high >> 16; - __get_user(env->fregs[i].d.low, - (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); - } -} - -static inline int target_rt_restore_ucontext(CPUM68KState *env, - struct target_ucontext *uc) -{ - int temp; - target_greg_t *gregs = uc->tuc_mcontext.gregs; - - __get_user(temp, &uc->tuc_mcontext.version); - if (temp != TARGET_MCONTEXT_VERSION) - goto badframe; - - /* restore passed registers */ - __get_user(env->dregs[0], &gregs[0]); - __get_user(env->dregs[1], &gregs[1]); - __get_user(env->dregs[2], &gregs[2]); - __get_user(env->dregs[3], &gregs[3]); - __get_user(env->dregs[4], &gregs[4]); - __get_user(env->dregs[5], &gregs[5]); - __get_user(env->dregs[6], &gregs[6]); - __get_user(env->dregs[7], &gregs[7]); - __get_user(env->aregs[0], &gregs[8]); - __get_user(env->aregs[1], &gregs[9]); - __get_user(env->aregs[2], &gregs[10]); - __get_user(env->aregs[3], &gregs[11]); - __get_user(env->aregs[4], &gregs[12]); - __get_user(env->aregs[5], &gregs[13]); - __get_user(env->aregs[6], &gregs[14]); - __get_user(env->aregs[7], &gregs[15]); - __get_user(env->pc, &gregs[16]); - __get_user(temp, &gregs[17]); - cpu_m68k_set_ccr(env, temp); - - target_rt_restore_fpu_state(env, uc); - - return 0; - -badframe: - return 1; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUM68KState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - abi_ulong retcode_addr; - abi_ulong info_addr; - abi_ulong uc_addr; - int err = 0; - int i; - - frame_addr = get_sigframe(ka, env, sizeof *frame); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - __put_user(sig, &frame->sig); - - info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); - __put_user(info_addr, &frame->pinfo); - - uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); - __put_user(uc_addr, &frame->puc); - - tswap_siginfo(&frame->info, info); - - /* Create the ucontext */ - - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->aregs[7]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - err |= target_rt_setup_ucontext(&frame->uc, env); - - if (err) - goto give_sigsegv; - - for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - /* Set up to return from userspace. */ - - retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); - __put_user(retcode_addr, &frame->pretcode); - - /* moveq #,d0; notb d0; trap #0 */ - - __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16), - (uint32_t *)(frame->retcode + 0)); - __put_user(0x4e40, (uint16_t *)(frame->retcode + 4)); - - if (err) - goto give_sigsegv; - - /* Set up to return from userspace */ - - env->aregs[7] = frame_addr; - env->pc = ka->_sa_handler; - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - unlock_user_struct(frame, frame_addr, 1); - force_sigsegv(sig); -} - -long do_sigreturn(CPUM68KState *env) -{ - struct target_sigframe *frame; - abi_ulong frame_addr = env->aregs[7] - 4; - target_sigset_t target_set; - sigset_t set; - int i; - - trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - - /* set blocked signals */ - - __get_user(target_set.sig[0], &frame->sc.sc_mask); - - for(i = 1; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &frame->extramask[i - 1]); - } - - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - /* restore registers */ - - restore_sigcontext(env, &frame->sc); - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUM68KState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr = env->aregs[7] - 4; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) - goto badframe; - - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - /* restore registers */ - - if (target_rt_restore_ucontext(env, &frame->uc)) - goto badframe; - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, get_sp_from_cpustate(env)) == -EFAULT) - goto badframe; - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_ALPHA) - -struct target_sigcontext { - abi_long sc_onstack; - abi_long sc_mask; - abi_long sc_pc; - abi_long sc_ps; - abi_long sc_regs[32]; - abi_long sc_ownedfp; - abi_long sc_fpregs[32]; - abi_ulong sc_fpcr; - abi_ulong sc_fp_control; - abi_ulong sc_reserved1; - abi_ulong sc_reserved2; - abi_ulong sc_ssize; - abi_ulong sc_sbase; - abi_ulong sc_traparg_a0; - abi_ulong sc_traparg_a1; - abi_ulong sc_traparg_a2; - abi_ulong sc_fp_trap_pc; - abi_ulong sc_fp_trigger_sum; - abi_ulong sc_fp_trigger_inst; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - abi_ulong tuc_osf_sigmask; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; -}; - -struct target_sigframe { - struct target_sigcontext sc; - unsigned int retcode[3]; -}; - -struct target_rt_sigframe { - target_siginfo_t info; - struct target_ucontext uc; - unsigned int retcode[3]; -}; - -#define INSN_MOV_R30_R16 0x47fe0410 -#define INSN_LDI_R0 0x201f0000 -#define INSN_CALLSYS 0x00000083 - -static void setup_sigcontext(struct target_sigcontext *sc, CPUAlphaState *env, - abi_ulong frame_addr, target_sigset_t *set) -{ - int i; - - __put_user(on_sig_stack(frame_addr), &sc->sc_onstack); - __put_user(set->sig[0], &sc->sc_mask); - __put_user(env->pc, &sc->sc_pc); - __put_user(8, &sc->sc_ps); - - for (i = 0; i < 31; ++i) { - __put_user(env->ir[i], &sc->sc_regs[i]); - } - __put_user(0, &sc->sc_regs[31]); - - for (i = 0; i < 31; ++i) { - __put_user(env->fir[i], &sc->sc_fpregs[i]); - } - __put_user(0, &sc->sc_fpregs[31]); - __put_user(cpu_alpha_load_fpcr(env), &sc->sc_fpcr); - - __put_user(0, &sc->sc_traparg_a0); /* FIXME */ - __put_user(0, &sc->sc_traparg_a1); /* FIXME */ - __put_user(0, &sc->sc_traparg_a2); /* FIXME */ -} - -static void restore_sigcontext(CPUAlphaState *env, - struct target_sigcontext *sc) -{ - uint64_t fpcr; - int i; - - __get_user(env->pc, &sc->sc_pc); - - for (i = 0; i < 31; ++i) { - __get_user(env->ir[i], &sc->sc_regs[i]); - } - for (i = 0; i < 31; ++i) { - __get_user(env->fir[i], &sc->sc_fpregs[i]); - } - - __get_user(fpcr, &sc->sc_fpcr); - cpu_alpha_store_fpcr(env, fpcr); -} - -static inline abi_ulong get_sigframe(struct target_sigaction *sa, - CPUAlphaState *env, - unsigned long framesize) -{ - abi_ulong sp = env->ir[IR_SP]; - - /* This is the X/Open sanctioned signal stack switching. */ - if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - return (sp - framesize) & -32; -} - -static void setup_frame(int sig, struct target_sigaction *ka, - target_sigset_t *set, CPUAlphaState *env) -{ - abi_ulong frame_addr, r26; - struct target_sigframe *frame; - int err = 0; - - 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)) { - goto give_sigsegv; - } - - setup_sigcontext(&frame->sc, env, frame_addr, set); - - if (ka->sa_restorer) { - r26 = ka->sa_restorer; - } else { - __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); - __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn, - &frame->retcode[1]); - __put_user(INSN_CALLSYS, &frame->retcode[2]); - /* imb() */ - r26 = frame_addr + offsetof(struct target_sigframe, retcode); - } - - unlock_user_struct(frame, frame_addr, 1); - - if (err) { -give_sigsegv: - force_sigsegv(sig); - return; - } - - env->ir[IR_RA] = r26; - env->ir[IR_PV] = env->pc = ka->_sa_handler; - env->ir[IR_A0] = sig; - env->ir[IR_A1] = 0; - env->ir[IR_A2] = frame_addr + offsetof(struct target_sigframe, sc); - env->ir[IR_SP] = frame_addr; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUAlphaState *env) -{ - abi_ulong frame_addr, r26; - struct target_rt_sigframe *frame; - int i, err = 0; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - tswap_siginfo(&frame->info, info); - - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(set->sig[0], &frame->uc.tuc_osf_sigmask); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->ir[IR_SP]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->uc.tuc_mcontext, env, frame_addr, set); - for (i = 0; i < TARGET_NSIG_WORDS; ++i) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - if (ka->sa_restorer) { - r26 = ka->sa_restorer; - } else { - __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); - __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn, - &frame->retcode[1]); - __put_user(INSN_CALLSYS, &frame->retcode[2]); - /* imb(); */ - r26 = frame_addr + offsetof(struct target_sigframe, retcode); - } - - if (err) { -give_sigsegv: - force_sigsegv(sig); - return; - } - - env->ir[IR_RA] = r26; - env->ir[IR_PV] = env->pc = ka->_sa_handler; - env->ir[IR_A0] = sig; - env->ir[IR_A1] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->ir[IR_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->ir[IR_SP] = frame_addr; -} - -long do_sigreturn(CPUAlphaState *env) -{ - struct target_sigcontext *sc; - abi_ulong sc_addr = env->ir[IR_A0]; - target_sigset_t target_set; - sigset_t set; - - if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) { - goto badframe; - } - - target_sigemptyset(&target_set); - __get_user(target_set.sig[0], &sc->sc_mask); - - target_to_host_sigset_internal(&set, &target_set); - set_sigmask(&set); - - restore_sigcontext(env, sc); - unlock_user_struct(sc, sc_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -long do_rt_sigreturn(CPUAlphaState *env) -{ - abi_ulong frame_addr = env->ir[IR_A0]; - struct target_rt_sigframe *frame; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - restore_sigcontext(env, &frame->uc.tuc_mcontext); - if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, - uc.tuc_stack), - 0, env->ir[IR_SP]) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_TILEGX) - -struct target_sigcontext { - union { - /* General-purpose registers. */ - abi_ulong gregs[56]; - struct { - abi_ulong __gregs[53]; - abi_ulong tp; /* Aliases gregs[TREG_TP]. */ - abi_ulong sp; /* Aliases gregs[TREG_SP]. */ - abi_ulong lr; /* Aliases gregs[TREG_LR]. */ - }; - }; - abi_ulong pc; /* Program counter. */ - abi_ulong ics; /* In Interrupt Critical Section? */ - abi_ulong faultnum; /* Fault number. */ - abi_ulong pad[5]; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; /* mask last for extensibility */ -}; - -struct target_rt_sigframe { - unsigned char save_area[16]; /* caller save area */ - struct target_siginfo info; - struct target_ucontext uc; - abi_ulong retcode[2]; -}; - -#define INSN_MOVELI_R10_139 0x00045fe551483000ULL /* { moveli r10, 139 } */ -#define INSN_SWINT1 0x286b180051485000ULL /* { swint1 } */ - - -static void setup_sigcontext(struct target_sigcontext *sc, - CPUArchState *env, int signo) -{ - int i; - - for (i = 0; i < TILEGX_R_COUNT; ++i) { - __put_user(env->regs[i], &sc->gregs[i]); - } - - __put_user(env->pc, &sc->pc); - __put_user(0, &sc->ics); - __put_user(signo, &sc->faultnum); -} - -static void restore_sigcontext(CPUTLGState *env, struct target_sigcontext *sc) -{ - int i; - - for (i = 0; i < TILEGX_R_COUNT; ++i) { - __get_user(env->regs[i], &sc->gregs[i]); - } - - __get_user(env->pc, &sc->pc); -} - -static abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, - size_t frame_size) -{ - unsigned long sp = env->regs[TILEGX_R_SP]; - - if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size))) { - return -1UL; - } - - if ((ka->sa_flags & SA_ONSTACK) && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp -= frame_size; - sp &= -16UL; - return sp; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUArchState *env) -{ - abi_ulong frame_addr; - struct target_rt_sigframe *frame; - unsigned long restorer; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - /* Always write at least the signal number for the stack backtracer. */ - if (ka->sa_flags & TARGET_SA_SIGINFO) { - /* At sigreturn time, restore the callee-save registers too. */ - tswap_siginfo(&frame->info, info); - /* regs->flags |= PT_FLAGS_RESTORE_REGS; FIXME: we can skip it? */ - } else { - __put_user(info->si_signo, &frame->info.si_signo); - } - - /* Create the ucontext. */ - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->regs[TILEGX_R_SP]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, &frame->uc.tuc_stack.ss_size); - setup_sigcontext(&frame->uc.tuc_mcontext, env, info->si_signo); - - if (ka->sa_flags & TARGET_SA_RESTORER) { - restorer = (unsigned long) ka->sa_restorer; - } else { - __put_user(INSN_MOVELI_R10_139, &frame->retcode[0]); - __put_user(INSN_SWINT1, &frame->retcode[1]); - restorer = frame_addr + offsetof(struct target_rt_sigframe, retcode); - } - env->pc = (unsigned long) ka->_sa_handler; - env->regs[TILEGX_R_SP] = (unsigned long) frame; - env->regs[TILEGX_R_LR] = restorer; - env->regs[0] = (unsigned long) sig; - env->regs[1] = (unsigned long) &frame->info; - env->regs[2] = (unsigned long) &frame->uc; - /* regs->flags |= PT_FLAGS_CALLER_SAVES; FIXME: we can skip it? */ - - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); -} - -long do_rt_sigreturn(CPUTLGState *env) -{ - abi_ulong frame_addr = env->regs[TILEGX_R_SP]; - struct target_rt_sigframe *frame; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - restore_sigcontext(env, &frame->uc.tuc_mcontext); - if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, - uc.tuc_stack), - 0, env->regs[TILEGX_R_SP]) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - - - badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_RISCV) - -/* Signal handler invocation must be transparent for the code being - interrupted. Complete CPU (hart) state is saved on entry and restored - before returning from the handler. Process sigmask is also saved to block - signals while the handler is running. The handler gets its own stack, - which also doubles as storage for the CPU state and sigmask. - - The code below is qemu re-implementation of arch/riscv/kernel/signal.c */ - -struct target_sigcontext { - abi_long pc; - abi_long gpr[31]; /* x0 is not present, so all offsets must be -1 */ - uint64_t fpr[32]; - uint32_t fcsr; -}; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */ - -struct target_ucontext { - unsigned long uc_flags; - struct target_ucontext *uc_link; - target_stack_t uc_stack; - struct target_sigcontext uc_mcontext; - target_sigset_t uc_sigmask; -}; - -struct target_rt_sigframe { - uint32_t tramp[2]; /* not in kernel, which uses VDSO instead */ - struct target_siginfo info; - struct target_ucontext uc; -}; - -static abi_ulong get_sigframe(struct target_sigaction *ka, - CPURISCVState *regs, size_t framesize) -{ - abi_ulong sp = regs->gpr[xSP]; - int onsigstack = on_sig_stack(sp); - - /* redzone */ - /* This is the X/Open sanctioned signal stack switching. */ - if ((ka->sa_flags & TARGET_SA_ONSTACK) != 0 && !onsigstack) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - - sp -= framesize; - sp &= ~3UL; /* align sp on 4-byte boundary */ - - /* If we are on the alternate signal stack and would overflow it, don't. - Return an always-bogus address instead so we will die with SIGSEGV. */ - if (onsigstack && !likely(on_sig_stack(sp))) { - return -1L; - } - - return sp; -} - -static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) -{ - int i; - - __put_user(env->pc, &sc->pc); - - for (i = 1; i < 32; i++) { - __put_user(env->gpr[i], &sc->gpr[i - 1]); - } - for (i = 0; i < 32; i++) { - __put_user(env->fpr[i], &sc->fpr[i]); - } - - uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/ - __put_user(fcsr, &sc->fcsr); -} - -static void setup_ucontext(struct target_ucontext *uc, - CPURISCVState *env, target_sigset_t *set) -{ - abi_ulong ss_sp = (target_ulong)target_sigaltstack_used.ss_sp; - abi_ulong ss_flags = sas_ss_flags(env->gpr[xSP]); - abi_ulong ss_size = target_sigaltstack_used.ss_size; - - __put_user(0, &(uc->uc_flags)); - __put_user(0, &(uc->uc_link)); - - __put_user(ss_sp, &(uc->uc_stack.ss_sp)); - __put_user(ss_flags, &(uc->uc_stack.ss_flags)); - __put_user(ss_size, &(uc->uc_stack.ss_size)); - - int i; - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &(uc->uc_sigmask.sig[i])); - } - - setup_sigcontext(&uc->uc_mcontext, env); -} - -static inline void install_sigtramp(uint32_t *tramp) -{ - __put_user(0x08b00893, tramp + 0); /* li a7, 139 = __NR_rt_sigreturn */ - __put_user(0x00000073, tramp + 1); /* ecall */ -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPURISCVState *env) -{ - abi_ulong frame_addr; - struct target_rt_sigframe *frame; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto badframe; - } - - setup_ucontext(&frame->uc, env, set); - tswap_siginfo(&frame->info, info); - install_sigtramp(frame->tramp); - - env->pc = ka->_sa_handler; - env->gpr[xSP] = frame_addr; - env->gpr[xA0] = sig; - env->gpr[xA1] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->gpr[xA2] = frame_addr + offsetof(struct target_rt_sigframe, uc); - env->gpr[xRA] = frame_addr + offsetof(struct target_rt_sigframe, tramp); - - return; - -badframe: - unlock_user_struct(frame, frame_addr, 1); - if (sig == TARGET_SIGSEGV) { - ka->_sa_handler = TARGET_SIG_DFL; - } - force_sig(TARGET_SIGSEGV); -} - -static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc) -{ - int i; - - __get_user(env->pc, &sc->pc); - - for (i = 1; i < 32; ++i) { - __get_user(env->gpr[i], &sc->gpr[i - 1]); - } - for (i = 0; i < 32; ++i) { - __get_user(env->fpr[i], &sc->fpr[i]); - } - - uint32_t fcsr; - __get_user(fcsr, &sc->fcsr); - csr_write_helper(env, fcsr, CSR_FCSR); -} - -static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc) -{ - sigset_t blocked; - target_sigset_t target_set; - int i; - - target_sigemptyset(&target_set); - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __get_user(target_set.sig[i], &(uc->uc_sigmask.sig[i])); - } - - target_to_host_sigset_internal(&blocked, &target_set); - set_sigmask(&blocked); - - restore_sigcontext(env, &uc->uc_mcontext); -} - -long do_rt_sigreturn(CPURISCVState *env) -{ - struct target_rt_sigframe *frame; - abi_ulong frame_addr; - - frame_addr = env->gpr[xSP]; - trace_user_do_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - - restore_ucontext(env, &frame->uc); - - if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, - uc.uc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return 0; -} - -#elif defined(TARGET_HPPA) - -struct target_sigcontext { - abi_ulong sc_flags; - abi_ulong sc_gr[32]; - uint64_t sc_fr[32]; - abi_ulong sc_iasq[2]; - abi_ulong sc_iaoq[2]; - abi_ulong sc_sar; -}; - -struct target_ucontext { - abi_uint tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - abi_uint pad[1]; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; -}; - -struct target_rt_sigframe { - abi_uint tramp[9]; - target_siginfo_t info; - struct target_ucontext uc; - /* hidden location of upper halves of pa2.0 64-bit gregs */ -}; - -static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env) -{ - int flags = 0; - int i; - - /* ??? if on_sig_stack, flags |= 1 (PARISC_SC_FLAG_ONSTACK). */ - - if (env->iaoq_f < TARGET_PAGE_SIZE) { - /* In the gateway page, executing a syscall. */ - flags |= 2; /* PARISC_SC_FLAG_IN_SYSCALL */ - __put_user(env->gr[31], &sc->sc_iaoq[0]); - __put_user(env->gr[31] + 4, &sc->sc_iaoq[1]); - } else { - __put_user(env->iaoq_f, &sc->sc_iaoq[0]); - __put_user(env->iaoq_b, &sc->sc_iaoq[1]); - } - __put_user(0, &sc->sc_iasq[0]); - __put_user(0, &sc->sc_iasq[1]); - __put_user(flags, &sc->sc_flags); - - __put_user(cpu_hppa_get_psw(env), &sc->sc_gr[0]); - for (i = 1; i < 32; ++i) { - __put_user(env->gr[i], &sc->sc_gr[i]); - } - - __put_user((uint64_t)env->fr0_shadow << 32, &sc->sc_fr[0]); - for (i = 1; i < 32; ++i) { - __put_user(env->fr[i], &sc->sc_fr[i]); - } - - __put_user(env->cr[CR_SAR], &sc->sc_sar); -} - -static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) -{ - target_ulong psw; - int i; - - __get_user(psw, &sc->sc_gr[0]); - cpu_hppa_put_psw(env, psw); - - for (i = 1; i < 32; ++i) { - __get_user(env->gr[i], &sc->sc_gr[i]); - } - for (i = 0; i < 32; ++i) { - __get_user(env->fr[i], &sc->sc_fr[i]); - } - cpu_hppa_loaded_fr0(env); - - __get_user(env->iaoq_f, &sc->sc_iaoq[0]); - __get_user(env->iaoq_b, &sc->sc_iaoq[1]); - __get_user(env->cr[CR_SAR], &sc->sc_sar); -} - -/* No, this doesn't look right, but it's copied straight from the kernel. */ -#define PARISC_RT_SIGFRAME_SIZE32 \ - ((sizeof(struct target_rt_sigframe) + 48 + 64) & -64) - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUArchState *env) -{ - abi_ulong frame_addr, sp, haddr; - struct target_rt_sigframe *frame; - int i; - - sp = env->gr[30]; - if (ka->sa_flags & TARGET_SA_ONSTACK) { - if (sas_ss_flags(sp) == 0) { - sp = (target_sigaltstack_used.ss_sp + 0x7f) & ~0x3f; - } - } - frame_addr = QEMU_ALIGN_UP(sp, 64); - sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32; - - trace_user_setup_rt_frame(env, frame_addr); - - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - tswap_siginfo(&frame->info, info); - frame->uc.tuc_flags = 0; - frame->uc.tuc_link = 0; - - __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - - for (i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - setup_sigcontext(&frame->uc.tuc_mcontext, env); - - __put_user(0x34190000, frame->tramp + 0); /* ldi 0,%r25 */ - __put_user(0x3414015a, frame->tramp + 1); /* ldi __NR_rt_sigreturn,%r20 */ - __put_user(0xe4008200, frame->tramp + 2); /* be,l 0x100(%sr2,%r0) */ - __put_user(0x08000240, frame->tramp + 3); /* nop */ - - unlock_user_struct(frame, frame_addr, 1); - - env->gr[2] = h2g(frame->tramp); - env->gr[30] = sp; - env->gr[26] = sig; - env->gr[25] = h2g(&frame->info); - env->gr[24] = h2g(&frame->uc); - - haddr = ka->_sa_handler; - if (haddr & 2) { - /* Function descriptor. */ - target_ulong *fdesc, dest; - - haddr &= -4; - if (!lock_user_struct(VERIFY_READ, fdesc, haddr, 1)) { - goto give_sigsegv; - } - __get_user(dest, fdesc); - __get_user(env->gr[19], fdesc + 1); - unlock_user_struct(fdesc, haddr, 1); - haddr = dest; - } - env->iaoq_f = haddr; - env->iaoq_b = haddr + 4; - return; - - give_sigsegv: - force_sigsegv(sig); -} - -long do_rt_sigreturn(CPUArchState *env) -{ - abi_ulong frame_addr = env->gr[30] - PARISC_RT_SIGFRAME_SIZE32; - struct target_rt_sigframe *frame; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - restore_sigcontext(env, &frame->uc.tuc_mcontext); - unlock_user_struct(frame, frame_addr, 0); - - if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, - uc.tuc_stack), - 0, env->gr[30]) == -EFAULT) { - goto badframe; - } - - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - - badframe: - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#elif defined(TARGET_XTENSA) - -struct target_sigcontext { - abi_ulong sc_pc; - abi_ulong sc_ps; - abi_ulong sc_lbeg; - abi_ulong sc_lend; - abi_ulong sc_lcount; - abi_ulong sc_sar; - abi_ulong sc_acclo; - abi_ulong sc_acchi; - abi_ulong sc_a[16]; - abi_ulong sc_xtregs; -}; - -struct target_ucontext { - abi_ulong tuc_flags; - abi_ulong tuc_link; - target_stack_t tuc_stack; - struct target_sigcontext tuc_mcontext; - target_sigset_t tuc_sigmask; -}; - -struct target_rt_sigframe { - target_siginfo_t info; - struct target_ucontext uc; - /* TODO: xtregs */ - uint8_t retcode[6]; - abi_ulong window[4]; -}; - -static abi_ulong get_sigframe(struct target_sigaction *sa, - CPUXtensaState *env, - unsigned long framesize) -{ - abi_ulong sp = env->regs[1]; - - /* This is the X/Open sanctioned signal stack switching. */ - if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) { - sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; - } - return (sp - framesize) & -16; -} - -static int flush_window_regs(CPUXtensaState *env) -{ - uint32_t wb = env->sregs[WINDOW_BASE]; - uint32_t ws = xtensa_replicate_windowstart(env) >> (wb + 1); - unsigned d = ctz32(ws) + 1; - unsigned i; - int ret = 0; - - for (i = d; i < env->config->nareg / 4; i += d) { - uint32_t ssp, osp; - unsigned j; - - ws >>= d; - xtensa_rotate_window(env, d); - - if (ws & 0x1) { - ssp = env->regs[5]; - d = 1; - } else if (ws & 0x2) { - ssp = env->regs[9]; - ret |= get_user_ual(osp, env->regs[1] - 12); - osp -= 32; - d = 2; - } else if (ws & 0x4) { - ssp = env->regs[13]; - ret |= get_user_ual(osp, env->regs[1] - 12); - osp -= 48; - d = 3; - } else { - g_assert_not_reached(); - } - - for (j = 0; j < 4; ++j) { - ret |= put_user_ual(env->regs[j], ssp - 16 + j * 4); - } - for (j = 4; j < d * 4; ++j) { - ret |= put_user_ual(env->regs[j], osp - 16 + j * 4); - } - } - xtensa_rotate_window(env, d); - g_assert(env->sregs[WINDOW_BASE] == wb); - return ret == 0; -} - -static int setup_sigcontext(struct target_rt_sigframe *frame, - CPUXtensaState *env) -{ - struct target_sigcontext *sc = &frame->uc.tuc_mcontext; - int i; - - __put_user(env->pc, &sc->sc_pc); - __put_user(env->sregs[PS], &sc->sc_ps); - __put_user(env->sregs[LBEG], &sc->sc_lbeg); - __put_user(env->sregs[LEND], &sc->sc_lend); - __put_user(env->sregs[LCOUNT], &sc->sc_lcount); - if (!flush_window_regs(env)) { - return 0; - } - for (i = 0; i < 16; ++i) { - __put_user(env->regs[i], sc->sc_a + i); - } - __put_user(0, &sc->sc_xtregs); - /* TODO: xtregs */ - return 1; -} - -static void setup_rt_frame(int sig, struct target_sigaction *ka, - target_siginfo_t *info, - target_sigset_t *set, CPUXtensaState *env) -{ - abi_ulong frame_addr; - struct target_rt_sigframe *frame; - uint32_t ra; - int i; - - frame_addr = get_sigframe(ka, env, sizeof(*frame)); - trace_user_setup_rt_frame(env, frame_addr); - - if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { - goto give_sigsegv; - } - - if (ka->sa_flags & SA_SIGINFO) { - tswap_siginfo(&frame->info, info); - } - - __put_user(0, &frame->uc.tuc_flags); - __put_user(0, &frame->uc.tuc_link); - __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.tuc_stack.ss_sp); - __put_user(sas_ss_flags(env->regs[1]), - &frame->uc.tuc_stack.ss_flags); - __put_user(target_sigaltstack_used.ss_size, - &frame->uc.tuc_stack.ss_size); - if (!setup_sigcontext(frame, env)) { - unlock_user_struct(frame, frame_addr, 0); - goto give_sigsegv; - } - for (i = 0; i < TARGET_NSIG_WORDS; ++i) { - __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); - } - - if (ka->sa_flags & TARGET_SA_RESTORER) { - ra = ka->sa_restorer; - } else { - ra = frame_addr + offsetof(struct target_rt_sigframe, retcode); -#ifdef TARGET_WORDS_BIGENDIAN - /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ - __put_user(0x22, &frame->retcode[0]); - __put_user(0x0a, &frame->retcode[1]); - __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); - /* Generate instruction: SYSCALL */ - __put_user(0x00, &frame->retcode[3]); - __put_user(0x05, &frame->retcode[4]); - __put_user(0x00, &frame->retcode[5]); -#else - /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ - __put_user(0x22, &frame->retcode[0]); - __put_user(0xa0, &frame->retcode[1]); - __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); - /* Generate instruction: SYSCALL */ - __put_user(0x00, &frame->retcode[3]); - __put_user(0x50, &frame->retcode[4]); - __put_user(0x00, &frame->retcode[5]); -#endif - } - env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT); - if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER)) { - env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT); - } - memset(env->regs, 0, sizeof(env->regs)); - env->pc = ka->_sa_handler; - env->regs[1] = frame_addr; - env->sregs[WINDOW_BASE] = 0; - env->sregs[WINDOW_START] = 1; - - env->regs[4] = (ra & 0x3fffffff) | 0x40000000; - env->regs[6] = sig; - env->regs[7] = frame_addr + offsetof(struct target_rt_sigframe, info); - env->regs[8] = frame_addr + offsetof(struct target_rt_sigframe, uc); - unlock_user_struct(frame, frame_addr, 1); - return; - -give_sigsegv: - force_sigsegv(sig); - return; -} - -static void restore_sigcontext(CPUXtensaState *env, - struct target_rt_sigframe *frame) -{ - struct target_sigcontext *sc = &frame->uc.tuc_mcontext; - uint32_t ps; - int i; - - __get_user(env->pc, &sc->sc_pc); - __get_user(ps, &sc->sc_ps); - __get_user(env->sregs[LBEG], &sc->sc_lbeg); - __get_user(env->sregs[LEND], &sc->sc_lend); - __get_user(env->sregs[LCOUNT], &sc->sc_lcount); - - env->sregs[WINDOW_BASE] = 0; - env->sregs[WINDOW_START] = 1; - env->sregs[PS] = deposit32(env->sregs[PS], - PS_CALLINC_SHIFT, - PS_CALLINC_LEN, - extract32(ps, PS_CALLINC_SHIFT, - PS_CALLINC_LEN)); - for (i = 0; i < 16; ++i) { - __get_user(env->regs[i], sc->sc_a + i); - } - /* TODO: xtregs */ -} - -long do_rt_sigreturn(CPUXtensaState *env) -{ - abi_ulong frame_addr = env->regs[1]; - struct target_rt_sigframe *frame; - sigset_t set; - - trace_user_do_rt_sigreturn(env, frame_addr); - if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { - goto badframe; - } - target_to_host_sigset(&set, &frame->uc.tuc_sigmask); - set_sigmask(&set); - - restore_sigcontext(env, frame); - - if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.tuc_stack), - 0, get_sp_from_cpustate(env)) == -TARGET_EFAULT) { - goto badframe; - } - unlock_user_struct(frame, frame_addr, 0); - return -TARGET_QEMU_ESIGRETURN; - -badframe: - unlock_user_struct(frame, frame_addr, 0); - force_sig(TARGET_SIGSEGV); - return -TARGET_QEMU_ESIGRETURN; -} - -#else -#error Target needs to add support for signal handling -#endif - static void handle_pending_signal(CPUArchState *cpu_env, int sig, struct emulated_sigtable *k) { @@ -7376,18 +904,15 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, } #endif /* prepare the stack frame of the virtual CPU */ -#if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) \ - || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \ - || defined(TARGET_PPC64) || defined(TARGET_HPPA) \ - || defined(TARGET_NIOS2) || defined(TARGET_X86_64) \ - || defined(TARGET_RISCV) || defined(TARGET_XTENSA) +#if defined(TARGET_ARCH_HAS_SETUP_FRAME) + if (sa->sa_flags & TARGET_SA_SIGINFO) { + setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env); + } else { + setup_frame(sig, sa, &target_old_set, cpu_env); + } +#else /* These targets do not have traditional signals. */ setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env); -#else - if (sa->sa_flags & TARGET_SA_SIGINFO) - setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env); - else - setup_frame(sig, sa, &target_old_set, cpu_env); #endif if (sa->sa_flags & TARGET_SA_RESETHAND) { sa->_sa_handler = TARGET_SIG_DFL; diff --git a/linux-user/socket.h b/linux-user/socket.h index 7051cd2cf4..4c0b5c2dfa 100644 --- a/linux-user/socket.h +++ b/linux-user/socket.h @@ -1,342 +1,37 @@ +#include "sockbits.h" -#if defined(TARGET_MIPS) - /* MIPS special values for constants */ +#ifndef TARGET_ARCH_HAS_SOCKET_TYPES +/** sock_type - Socket types - default values + * + * + * @SOCK_STREAM - stream (connection) socket + * @SOCK_DGRAM - datagram (conn.less) socket + * @SOCK_RAW - raw socket + * @SOCK_RDM - reliably-delivered message + * @SOCK_SEQPACKET - sequential packet socket + * @SOCK_DCCP - Datagram Congestion Control Protocol socket + * @SOCK_PACKET - linux specific way of getting packets at the dev level. + * For writing rarp and other similar things on the user + * level. + * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. + * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. + */ +enum sock_type { + TARGET_SOCK_STREAM = 1, + TARGET_SOCK_DGRAM = 2, + TARGET_SOCK_RAW = 3, + TARGET_SOCK_RDM = 4, + TARGET_SOCK_SEQPACKET = 5, + TARGET_SOCK_DCCP = 6, + TARGET_SOCK_PACKET = 10, +}; - /* - * For setsockopt(2) - * - * This defines are ABI conformant as far as Linux supports these ... - */ - #define TARGET_SOL_SOCKET 0xffff - - #define TARGET_SO_DEBUG 0x0001 /* Record debugging information. */ - #define TARGET_SO_REUSEADDR 0x0004 /* Allow reuse of local addresses. */ - #define TARGET_SO_KEEPALIVE 0x0008 /* Keep connections alive and send - SIGPIPE when they die. */ - #define TARGET_SO_DONTROUTE 0x0010 /* Don't do local routing. */ - #define TARGET_SO_BROADCAST 0x0020 /* Allow transmission of - broadcast messages. */ - #define TARGET_SO_LINGER 0x0080 /* Block on close of a reliable - * socket to transmit pending data. - */ - #define TARGET_SO_OOBINLINE 0x0100 /* Receive out-of-band data in-band. - */ - #if 0 - /* To add: Allow local address and port reuse. */ - #define TARGET_SO_REUSEPORT 0x0200 - #endif - - #define TARGET_SO_TYPE 0x1008 /* Compatible name for SO_STYLE. */ - #define TARGET_SO_STYLE SO_TYPE /* Synonym */ - #define TARGET_SO_ERROR 0x1007 /* get error status and clear */ - #define TARGET_SO_SNDBUF 0x1001 /* Send buffer size. */ - #define TARGET_SO_RCVBUF 0x1002 /* Receive buffer. */ - #define TARGET_SO_SNDLOWAT 0x1003 /* send low-water mark */ - #define TARGET_SO_RCVLOWAT 0x1004 /* receive low-water mark */ - #define TARGET_SO_SNDTIMEO 0x1005 /* send timeout */ - #define TARGET_SO_RCVTIMEO 0x1006 /* receive timeout */ - #define TARGET_SO_ACCEPTCONN 0x1009 - - /* linux-specific, might as well be the same as on i386 */ - #define TARGET_SO_NO_CHECK 11 - #define TARGET_SO_PRIORITY 12 - #define TARGET_SO_BSDCOMPAT 14 - - #define TARGET_SO_PASSCRED 17 - #define TARGET_SO_PEERCRED 18 - - /* Security levels - as per NRL IPv6 - don't actually do anything */ - #define TARGET_SO_SECURITY_AUTHENTICATION 22 - #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 - #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 - - #define TARGET_SO_BINDTODEVICE 25 - - /* Socket filtering */ - #define TARGET_SO_ATTACH_FILTER 26 - #define TARGET_SO_DETACH_FILTER 27 - - #define TARGET_SO_PEERNAME 28 - #define TARGET_SO_TIMESTAMP 29 - #define SCM_TIMESTAMP SO_TIMESTAMP - - #define TARGET_SO_PEERSEC 30 - #define TARGET_SO_SNDBUFFORCE 31 - #define TARGET_SO_RCVBUFFORCE 33 - #define TARGET_SO_PASSSEC 34 - - /** sock_type - Socket types - * - * Please notice that for binary compat reasons MIPS has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - - #define ARCH_HAS_SOCKET_TYPES 1 - - enum sock_type { - TARGET_SOCK_DGRAM = 1, - TARGET_SOCK_STREAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 02000000, - TARGET_SOCK_NONBLOCK = 0200, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - -#elif defined(TARGET_ALPHA) - - /* For setsockopt(2) */ - #define TARGET_SOL_SOCKET 0xffff - - #define TARGET_SO_DEBUG 0x0001 - #define TARGET_SO_REUSEADDR 0x0004 - #define TARGET_SO_KEEPALIVE 0x0008 - #define TARGET_SO_DONTROUTE 0x0010 - #define TARGET_SO_BROADCAST 0x0020 - #define TARGET_SO_LINGER 0x0080 - #define TARGET_SO_OOBINLINE 0x0100 - /* To add :#define TARGET_SO_REUSEPORT 0x0200 */ - - #define TARGET_SO_TYPE 0x1008 - #define TARGET_SO_ERROR 0x1007 - #define TARGET_SO_SNDBUF 0x1001 - #define TARGET_SO_RCVBUF 0x1002 - #define TARGET_SO_SNDBUFFORCE 0x100a - #define TARGET_SO_RCVBUFFORCE 0x100b - #define TARGET_SO_RCVLOWAT 0x1010 - #define TARGET_SO_SNDLOWAT 0x1011 - #define TARGET_SO_RCVTIMEO 0x1012 - #define TARGET_SO_SNDTIMEO 0x1013 - #define TARGET_SO_ACCEPTCONN 0x1014 - #define TARGET_SO_PROTOCOL 0x1028 - #define TARGET_SO_DOMAIN 0x1029 - - /* linux-specific, might as well be the same as on i386 */ - #define TARGET_SO_NO_CHECK 11 - #define TARGET_SO_PRIORITY 12 - #define TARGET_SO_BSDCOMPAT 14 - - #define TARGET_SO_PASSCRED 17 - #define TARGET_SO_PEERCRED 18 - #define TARGET_SO_BINDTODEVICE 25 - - /* Socket filtering */ - #define TARGET_SO_ATTACH_FILTER 26 - #define TARGET_SO_DETACH_FILTER 27 - - #define TARGET_SO_PEERNAME 28 - #define TARGET_SO_TIMESTAMP 29 - #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP - - #define TARGET_SO_PEERSEC 30 - #define TARGET_SO_PASSSEC 34 - #define TARGET_SO_TIMESTAMPNS 35 - #define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS - - /* Security levels - as per NRL IPv6 - don't actually do anything */ - #define TARGET_SO_SECURITY_AUTHENTICATION 19 - #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 20 - #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 21 - - #define TARGET_SO_MARK 36 - - #define TARGET_SO_TIMESTAMPING 37 - #define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING - - #define TARGET_SO_RXQ_OVFL 40 - - #define TARGET_SO_WIFI_STATUS 41 - #define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS - #define TARGET_SO_PEEK_OFF 42 - - /* Instruct lower device to use last 4-bytes of skb data as FCS */ - #define TARGET_SO_NOFCS 43 - - /** sock_type - Socket types - * - * Please notice that for binary compat reasons ALPHA has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - - #define ARCH_HAS_SOCKET_TYPES 1 - - enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 010000000, - TARGET_SOCK_NONBLOCK = 010000000000, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ -#elif defined(TARGET_HPPA) -#include -#else - -#if defined(TARGET_SPARC) - /** sock_type - Socket types - * - * Please notice that for binary compat reasons SPARC has to - * override the enum sock_type in include/linux/net.h, so - * we define ARCH_HAS_SOCKET_TYPES here. - * - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_STREAM - stream (connection) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - - #define ARCH_HAS_SOCKET_TYPES 1 - - enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 020000000, - TARGET_SOCK_NONBLOCK = 040000, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ - - #define TARGET_SO_PASSSEC 31 -#else - #define TARGET_SO_PASSSEC 34 -#endif - - /* For setsockopt(2) */ - #define TARGET_SOL_SOCKET 1 - - #define TARGET_SO_DEBUG 1 - #define TARGET_SO_REUSEADDR 2 - #define TARGET_SO_TYPE 3 - #define TARGET_SO_ERROR 4 - #define TARGET_SO_DONTROUTE 5 - #define TARGET_SO_BROADCAST 6 - #define TARGET_SO_SNDBUF 7 - #define TARGET_SO_RCVBUF 8 - #define TARGET_SO_SNDBUFFORCE 32 - #define TARGET_SO_RCVBUFFORCE 33 - #define TARGET_SO_KEEPALIVE 9 - #define TARGET_SO_OOBINLINE 10 - #define TARGET_SO_NO_CHECK 11 - #define TARGET_SO_PRIORITY 12 - #define TARGET_SO_LINGER 13 - #define TARGET_SO_BSDCOMPAT 14 - /* To add :#define TARGET_SO_REUSEPORT 15 */ -#if defined(TARGET_PPC) - #define TARGET_SO_RCVLOWAT 16 - #define TARGET_SO_SNDLOWAT 17 - #define TARGET_SO_RCVTIMEO 18 - #define TARGET_SO_SNDTIMEO 19 - #define TARGET_SO_PASSCRED 20 - #define TARGET_SO_PEERCRED 21 -#else - #define TARGET_SO_PASSCRED 16 - #define TARGET_SO_PEERCRED 17 - #define TARGET_SO_RCVLOWAT 18 - #define TARGET_SO_SNDLOWAT 19 - #define TARGET_SO_RCVTIMEO 20 - #define TARGET_SO_SNDTIMEO 21 -#endif - - /* Security levels - as per NRL IPv6 - don't actually do anything */ - #define TARGET_SO_SECURITY_AUTHENTICATION 22 - #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 23 - #define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 24 - - #define TARGET_SO_BINDTODEVICE 25 - - /* Socket filtering */ - #define TARGET_SO_ATTACH_FILTER 26 - #define TARGET_SO_DETACH_FILTER 27 - - #define TARGET_SO_PEERNAME 28 - #define TARGET_SO_TIMESTAMP 29 - #define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP - - #define TARGET_SO_ACCEPTCONN 30 - - #define TARGET_SO_PEERSEC 31 - -#endif - -#ifndef ARCH_HAS_SOCKET_TYPES - /** sock_type - Socket types - default values - * - * - * @SOCK_STREAM - stream (connection) socket - * @SOCK_DGRAM - datagram (conn.less) socket - * @SOCK_RAW - raw socket - * @SOCK_RDM - reliably-delivered message - * @SOCK_SEQPACKET - sequential packet socket - * @SOCK_DCCP - Datagram Congestion Control Protocol socket - * @SOCK_PACKET - linux specific way of getting packets at the dev level. - * For writing rarp and other similar things on the user - * level. - * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. - * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. - */ - enum sock_type { - TARGET_SOCK_STREAM = 1, - TARGET_SOCK_DGRAM = 2, - TARGET_SOCK_RAW = 3, - TARGET_SOCK_RDM = 4, - TARGET_SOCK_SEQPACKET = 5, - TARGET_SOCK_DCCP = 6, - TARGET_SOCK_PACKET = 10, - TARGET_SOCK_CLOEXEC = 02000000, - TARGET_SOCK_NONBLOCK = 04000, - }; - - #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) - #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ +#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) +#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ +/* Flags for socket, socketpair, accept4 */ +#define TARGET_SOCK_CLOEXEC TARGET_O_CLOEXEC +#ifndef TARGET_SOCK_NONBLOCK +#define TARGET_SOCK_NONBLOCK TARGET_O_NONBLOCK #endif +#endif /* TARGET_ARCH_HAS_SOCKET_TYPES */ diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c new file mode 100644 index 0000000000..91f714afc6 --- /dev/null +++ b/linux-user/sparc/cpu_loop.c @@ -0,0 +1,306 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +#define SPARC64_STACK_BIAS 2047 + +//#define DEBUG_WIN + +/* WARNING: dealing with register windows _is_ complicated. More info + can be found at http://www.sics.se/~psm/sparcstack.html */ +static inline int get_reg_index(CPUSPARCState *env, int cwp, int index) +{ + index = (index + cwp * 16) % (16 * env->nwindows); + /* wrap handling : if cwp is on the last window, then we use the + registers 'after' the end */ + if (index < 8 && env->cwp == env->nwindows - 1) + index += 16 * env->nwindows; + return index; +} + +/* save the register window 'cwp1' */ +static inline void save_window_offset(CPUSPARCState *env, int cwp1) +{ + unsigned int i; + abi_ulong sp_ptr; + + sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; +#ifdef TARGET_SPARC64 + if (sp_ptr & 3) + sp_ptr += SPARC64_STACK_BIAS; +#endif +#if defined(DEBUG_WIN) + printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n", + sp_ptr, cwp1); +#endif + for(i = 0; i < 16; i++) { + /* FIXME - what to do if put_user() fails? */ + put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); + sp_ptr += sizeof(abi_ulong); + } +} + +static void save_window(CPUSPARCState *env) +{ +#ifndef TARGET_SPARC64 + unsigned int new_wim; + new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) & + ((1LL << env->nwindows) - 1); + save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); + env->wim = new_wim; +#else + save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2)); + env->cansave++; + env->canrestore--; +#endif +} + +static void restore_window(CPUSPARCState *env) +{ +#ifndef TARGET_SPARC64 + unsigned int new_wim; +#endif + unsigned int i, cwp1; + abi_ulong sp_ptr; + +#ifndef TARGET_SPARC64 + new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) & + ((1LL << env->nwindows) - 1); +#endif + + /* restore the invalid window */ + cwp1 = cpu_cwp_inc(env, env->cwp + 1); + sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)]; +#ifdef TARGET_SPARC64 + if (sp_ptr & 3) + sp_ptr += SPARC64_STACK_BIAS; +#endif +#if defined(DEBUG_WIN) + printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n", + sp_ptr, cwp1); +#endif + for(i = 0; i < 16; i++) { + /* FIXME - what to do if get_user() fails? */ + get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr); + sp_ptr += sizeof(abi_ulong); + } +#ifdef TARGET_SPARC64 + env->canrestore++; + if (env->cleanwin < env->nwindows - 1) + env->cleanwin++; + env->cansave--; +#else + env->wim = new_wim; +#endif +} + +static void flush_windows(CPUSPARCState *env) +{ + int offset, cwp1; + + offset = 1; + for(;;) { + /* if restore would invoke restore_window(), then we can stop */ + cwp1 = cpu_cwp_inc(env, env->cwp + offset); +#ifndef TARGET_SPARC64 + if (env->wim & (1 << cwp1)) + break; +#else + if (env->canrestore == 0) + break; + env->cansave++; + env->canrestore--; +#endif + save_window_offset(env, cwp1); + offset++; + } + cwp1 = cpu_cwp_inc(env, env->cwp + 1); +#ifndef TARGET_SPARC64 + /* set wim so that restore will reload the registers */ + env->wim = 1 << cwp1; +#endif +#if defined(DEBUG_WIN) + printf("flush_windows: nb=%d\n", offset - 1); +#endif +} + +void cpu_loop (CPUSPARCState *env) +{ + CPUState *cs = CPU(sparc_env_get_cpu(env)); + int trapnr; + abi_long ret; + target_siginfo_t info; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + /* Compute PSR before exposing state. */ + if (env->cc_op != CC_OP_FLAGS) { + cpu_get_psr(env); + } + + switch (trapnr) { +#ifndef TARGET_SPARC64 + case 0x88: + case 0x90: +#else + case 0x110: + case 0x16d: +#endif + ret = do_syscall (env, env->gregs[1], + env->regwptr[0], env->regwptr[1], + env->regwptr[2], env->regwptr[3], + env->regwptr[4], env->regwptr[5], + 0, 0); + if (ret == -TARGET_ERESTARTSYS || ret == -TARGET_QEMU_ESIGRETURN) { + break; + } + if ((abi_ulong)ret >= (abi_ulong)(-515)) { +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + env->xcc |= PSR_CARRY; +#else + env->psr |= PSR_CARRY; +#endif + ret = -ret; + } else { +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + env->xcc &= ~PSR_CARRY; +#else + env->psr &= ~PSR_CARRY; +#endif + } + env->regwptr[0] = ret; + /* next instruction */ + env->pc = env->npc; + env->npc = env->npc + 4; + break; + case 0x83: /* flush windows */ +#ifdef TARGET_ABI32 + case 0x103: +#endif + flush_windows(env); + /* next instruction */ + env->pc = env->npc; + env->npc = env->npc + 4; + break; +#ifndef TARGET_SPARC64 + case TT_WIN_OVF: /* window overflow */ + save_window(env); + break; + case TT_WIN_UNF: /* window underflow */ + restore_window(env); + break; + case TT_TFAULT: + case TT_DFAULT: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = env->mmuregs[4]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; +#else + case TT_SPILL: /* window overflow */ + save_window(env); + break; + case TT_FILL: /* window underflow */ + restore_window(env); + break; + case TT_TFAULT: + case TT_DFAULT: + { + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + /* XXX: check env->error_code */ + info.si_code = TARGET_SEGV_MAPERR; + if (trapnr == TT_DFAULT) + info._sifields._sigfault._addr = env->dmmu.mmuregs[4]; + else + info._sifields._sigfault._addr = cpu_tsptr(env)->tpc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; +#ifndef TARGET_ABI32 + case 0x16e: + flush_windows(env); + sparc64_get_context(env); + break; + case 0x16f: + flush_windows(env); + sparc64_set_context(env); + break; +#endif +#endif + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + case TT_ILL_INSN: + { + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + break; + case EXCP_DEBUG: + { + int sig; + + sig = gdb_handlesig(cs, TARGET_SIGTRAP); + if (sig) + { + info.si_signo = sig; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + } + } + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr); + cpu_dump_state(cs, stderr, fprintf, 0); + exit(EXIT_FAILURE); + } + process_pending_signals (env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + env->pc = regs->pc; + env->npc = regs->npc; + env->y = regs->y; + for(i = 0; i < 8; i++) + env->gregs[i] = regs->u_regs[i]; + for(i = 0; i < 8; i++) + env->regwptr[i] = regs->u_regs[i + 8]; +} diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c new file mode 100644 index 0000000000..b4c60aa446 --- /dev/null +++ b/linux-user/sparc/signal.c @@ -0,0 +1,633 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +#define __SUNOS_MAXWIN 31 + +/* This is what SunOS does, so shall I. */ +struct target_sigcontext { + abi_ulong sigc_onstack; /* state to restore */ + + abi_ulong sigc_mask; /* sigmask to restore */ + abi_ulong sigc_sp; /* stack pointer */ + abi_ulong sigc_pc; /* program counter */ + abi_ulong sigc_npc; /* next program counter */ + abi_ulong sigc_psr; /* for condition codes etc */ + abi_ulong sigc_g1; /* User uses these two registers */ + abi_ulong sigc_o0; /* within the trampoline code. */ + + /* Now comes information regarding the users window set + * at the time of the signal. + */ + abi_ulong sigc_oswins; /* outstanding windows */ + + /* stack ptrs for each regwin buf */ + char *sigc_spbuf[__SUNOS_MAXWIN]; + + /* Windows to restore after signal */ + struct { + abi_ulong locals[8]; + abi_ulong ins[8]; + } sigc_wbuf[__SUNOS_MAXWIN]; +}; +/* A Sparc stack frame */ +struct sparc_stackf { + abi_ulong locals[8]; + abi_ulong ins[8]; + /* It's simpler to treat fp and callers_pc as elements of ins[] + * since we never need to access them ourselves. + */ + char *structptr; + abi_ulong xargs[6]; + abi_ulong xxargs[1]; +}; + +typedef struct { + struct { + abi_ulong psr; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong u_regs[16]; /* globals and ins */ + } si_regs; + int si_mask; +} __siginfo_t; + +typedef struct { + abi_ulong si_float_regs[32]; + unsigned long si_fsr; + unsigned long si_fpqdepth; + struct { + unsigned long *insn_addr; + unsigned long insn; + } si_fpqueue [16]; +} qemu_siginfo_fpu_t; + + +struct target_signal_frame { + struct sparc_stackf ss; + __siginfo_t info; + abi_ulong fpu_save; + abi_ulong insns[2] __attribute__ ((aligned (8))); + abi_ulong extramask[TARGET_NSIG_WORDS - 1]; + abi_ulong extra_size; /* Should be 0 */ + qemu_siginfo_fpu_t fpu_state; +}; +struct target_rt_signal_frame { + struct sparc_stackf ss; + siginfo_t info; + abi_ulong regs[20]; + sigset_t mask; + abi_ulong fpu_save; + unsigned int insns[2]; + stack_t stack; + unsigned int extra_size; /* Should be 0 */ + qemu_siginfo_fpu_t fpu_state; +}; + +#define UREG_O0 16 +#define UREG_O6 22 +#define UREG_I0 0 +#define UREG_I1 1 +#define UREG_I2 2 +#define UREG_I3 3 +#define UREG_I4 4 +#define UREG_I5 5 +#define UREG_I6 6 +#define UREG_I7 7 +#define UREG_L0 8 +#define UREG_FP UREG_I6 +#define UREG_SP UREG_O6 + +static inline abi_ulong get_sigframe(struct target_sigaction *sa, + CPUSPARCState *env, + unsigned long framesize) +{ + abi_ulong sp = get_sp_from_cpustate(env); + + /* + * If we are on the alternate signal stack and would overflow it, don't. + * Return an always-bogus address instead so we will die with SIGSEGV. + */ + if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) { + return -1; + } + + /* This is the X/Open sanctioned signal stack switching. */ + sp = target_sigsp(sp, sa) - framesize; + + /* Always align the stack frame. This handles two cases. First, + * sigaltstack need not be mindful of platform specific stack + * alignment. Second, if we took this signal because the stack + * is not aligned properly, we'd like to take the signal cleanly + * and report that. + */ + sp &= ~15UL; + + return sp; +} + +static int +setup___siginfo(__siginfo_t *si, CPUSPARCState *env, abi_ulong mask) +{ + int err = 0, i; + + __put_user(env->psr, &si->si_regs.psr); + __put_user(env->pc, &si->si_regs.pc); + __put_user(env->npc, &si->si_regs.npc); + __put_user(env->y, &si->si_regs.y); + for (i=0; i < 8; i++) { + __put_user(env->gregs[i], &si->si_regs.u_regs[i]); + } + for (i=0; i < 8; i++) { + __put_user(env->regwptr[UREG_I0 + i], &si->si_regs.u_regs[i+8]); + } + __put_user(mask, &si->si_mask); + return err; +} + +#if 0 +static int +setup_sigcontext(struct target_sigcontext *sc, /*struct _fpstate *fpstate,*/ + CPUSPARCState *env, unsigned long mask) +{ + int err = 0; + + __put_user(mask, &sc->sigc_mask); + __put_user(env->regwptr[UREG_SP], &sc->sigc_sp); + __put_user(env->pc, &sc->sigc_pc); + __put_user(env->npc, &sc->sigc_npc); + __put_user(env->psr, &sc->sigc_psr); + __put_user(env->gregs[1], &sc->sigc_g1); + __put_user(env->regwptr[UREG_O0], &sc->sigc_o0); + + return err; +} +#endif +#define NF_ALIGNEDSZ (((sizeof(struct target_signal_frame) + 7) & (~7))) + +void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUSPARCState *env) +{ + abi_ulong sf_addr; + struct target_signal_frame *sf; + int sigframe_size, err, i; + + /* 1. Make sure everything is clean */ + //synchronize_user_stack(); + + sigframe_size = NF_ALIGNEDSZ; + sf_addr = get_sigframe(ka, env, sigframe_size); + trace_user_setup_frame(env, sf_addr); + + sf = lock_user(VERIFY_WRITE, sf_addr, + sizeof(struct target_signal_frame), 0); + if (!sf) { + goto sigsegv; + } +#if 0 + if (invalid_frame_pointer(sf, sigframe_size)) + goto sigill_and_return; +#endif + /* 2. Save the current process state */ + err = setup___siginfo(&sf->info, env, set->sig[0]); + __put_user(0, &sf->extra_size); + + //save_fpu_state(regs, &sf->fpu_state); + //__put_user(&sf->fpu_state, &sf->fpu_save); + + __put_user(set->sig[0], &sf->info.si_mask); + for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) { + __put_user(set->sig[i + 1], &sf->extramask[i]); + } + + for (i = 0; i < 8; i++) { + __put_user(env->regwptr[i + UREG_L0], &sf->ss.locals[i]); + } + for (i = 0; i < 8; i++) { + __put_user(env->regwptr[i + UREG_I0], &sf->ss.ins[i]); + } + if (err) + goto sigsegv; + + /* 3. signal handler back-trampoline and parameters */ + env->regwptr[UREG_FP] = sf_addr; + env->regwptr[UREG_I0] = sig; + env->regwptr[UREG_I1] = sf_addr + + offsetof(struct target_signal_frame, info); + env->regwptr[UREG_I2] = sf_addr + + offsetof(struct target_signal_frame, info); + + /* 4. signal handler */ + env->pc = ka->_sa_handler; + env->npc = (env->pc + 4); + /* 5. return to kernel instructions */ + if (ka->ka_restorer) { + env->regwptr[UREG_I7] = ka->ka_restorer; + } else { + uint32_t val32; + + env->regwptr[UREG_I7] = sf_addr + + offsetof(struct target_signal_frame, insns) - 2 * 4; + + /* mov __NR_sigreturn, %g1 */ + val32 = 0x821020d8; + __put_user(val32, &sf->insns[0]); + + /* t 0x10 */ + val32 = 0x91d02010; + __put_user(val32, &sf->insns[1]); + if (err) + goto sigsegv; + + /* Flush instruction space. */ + // flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); + // tb_flush(env); + } + unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); + return; +#if 0 +sigill_and_return: + force_sig(TARGET_SIGILL); +#endif +sigsegv: + unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); + force_sigsegv(sig); +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUSPARCState *env) +{ + qemu_log_mask(LOG_UNIMP, "setup_rt_frame: not implemented\n"); +} + +long do_sigreturn(CPUSPARCState *env) +{ + abi_ulong sf_addr; + struct target_signal_frame *sf; + uint32_t up_psr, pc, npc; + target_sigset_t set; + sigset_t host_set; + int err=0, i; + + sf_addr = env->regwptr[UREG_FP]; + trace_user_do_sigreturn(env, sf_addr); + if (!lock_user_struct(VERIFY_READ, sf, sf_addr, 1)) { + goto segv_and_exit; + } + + /* 1. Make sure we are not getting garbage from the user */ + + if (sf_addr & 3) + goto segv_and_exit; + + __get_user(pc, &sf->info.si_regs.pc); + __get_user(npc, &sf->info.si_regs.npc); + + if ((pc | npc) & 3) { + goto segv_and_exit; + } + + /* 2. Restore the state */ + __get_user(up_psr, &sf->info.si_regs.psr); + + /* User can only change condition codes and FPU enabling in %psr. */ + env->psr = (up_psr & (PSR_ICC /* | PSR_EF */)) + | (env->psr & ~(PSR_ICC /* | PSR_EF */)); + + env->pc = pc; + env->npc = npc; + __get_user(env->y, &sf->info.si_regs.y); + for (i=0; i < 8; i++) { + __get_user(env->gregs[i], &sf->info.si_regs.u_regs[i]); + } + for (i=0; i < 8; i++) { + __get_user(env->regwptr[i + UREG_I0], &sf->info.si_regs.u_regs[i+8]); + } + + /* FIXME: implement FPU save/restore: + * __get_user(fpu_save, &sf->fpu_save); + * if (fpu_save) + * err |= restore_fpu_state(env, fpu_save); + */ + + /* This is pretty much atomic, no amount locking would prevent + * the races which exist anyways. + */ + __get_user(set.sig[0], &sf->info.si_mask); + for(i = 1; i < TARGET_NSIG_WORDS; i++) { + __get_user(set.sig[i], &sf->extramask[i - 1]); + } + + target_to_host_sigset_internal(&host_set, &set); + set_sigmask(&host_set); + + if (err) { + goto segv_and_exit; + } + unlock_user_struct(sf, sf_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +segv_and_exit: + unlock_user_struct(sf, sf_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + +long do_rt_sigreturn(CPUSPARCState *env) +{ + trace_user_do_rt_sigreturn(env, 0); + qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n"); + return -TARGET_ENOSYS; +} + +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) +#define SPARC_MC_TSTATE 0 +#define SPARC_MC_PC 1 +#define SPARC_MC_NPC 2 +#define SPARC_MC_Y 3 +#define SPARC_MC_G1 4 +#define SPARC_MC_G2 5 +#define SPARC_MC_G3 6 +#define SPARC_MC_G4 7 +#define SPARC_MC_G5 8 +#define SPARC_MC_G6 9 +#define SPARC_MC_G7 10 +#define SPARC_MC_O0 11 +#define SPARC_MC_O1 12 +#define SPARC_MC_O2 13 +#define SPARC_MC_O3 14 +#define SPARC_MC_O4 15 +#define SPARC_MC_O5 16 +#define SPARC_MC_O6 17 +#define SPARC_MC_O7 18 +#define SPARC_MC_NGREG 19 + +typedef abi_ulong target_mc_greg_t; +typedef target_mc_greg_t target_mc_gregset_t[SPARC_MC_NGREG]; + +struct target_mc_fq { + abi_ulong *mcfq_addr; + uint32_t mcfq_insn; +}; + +struct target_mc_fpu { + union { + uint32_t sregs[32]; + uint64_t dregs[32]; + //uint128_t qregs[16]; + } mcfpu_fregs; + abi_ulong mcfpu_fsr; + abi_ulong mcfpu_fprs; + abi_ulong mcfpu_gsr; + struct target_mc_fq *mcfpu_fq; + unsigned char mcfpu_qcnt; + unsigned char mcfpu_qentsz; + unsigned char mcfpu_enab; +}; +typedef struct target_mc_fpu target_mc_fpu_t; + +typedef struct { + target_mc_gregset_t mc_gregs; + target_mc_greg_t mc_fp; + target_mc_greg_t mc_i7; + target_mc_fpu_t mc_fpregs; +} target_mcontext_t; + +struct target_ucontext { + struct target_ucontext *tuc_link; + abi_ulong tuc_flags; + target_sigset_t tuc_sigmask; + target_mcontext_t tuc_mcontext; +}; + +/* A V9 register window */ +struct target_reg_window { + abi_ulong locals[8]; + abi_ulong ins[8]; +}; + +#define TARGET_STACK_BIAS 2047 + +/* {set, get}context() needed for 64-bit SparcLinux userland. */ +void sparc64_set_context(CPUSPARCState *env) +{ + abi_ulong ucp_addr; + struct target_ucontext *ucp; + target_mc_gregset_t *grp; + abi_ulong pc, npc, tstate; + abi_ulong fp, i7, w_addr; + unsigned int i; + + ucp_addr = env->regwptr[UREG_I0]; + if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) { + goto do_sigsegv; + } + grp = &ucp->tuc_mcontext.mc_gregs; + __get_user(pc, &((*grp)[SPARC_MC_PC])); + __get_user(npc, &((*grp)[SPARC_MC_NPC])); + if ((pc | npc) & 3) { + goto do_sigsegv; + } + if (env->regwptr[UREG_I1]) { + target_sigset_t target_set; + sigset_t set; + + if (TARGET_NSIG_WORDS == 1) { + __get_user(target_set.sig[0], &ucp->tuc_sigmask.sig[0]); + } else { + abi_ulong *src, *dst; + src = ucp->tuc_sigmask.sig; + dst = target_set.sig; + for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { + __get_user(*dst, src); + } + } + target_to_host_sigset_internal(&set, &target_set); + set_sigmask(&set); + } + env->pc = pc; + env->npc = npc; + __get_user(env->y, &((*grp)[SPARC_MC_Y])); + __get_user(tstate, &((*grp)[SPARC_MC_TSTATE])); + env->asi = (tstate >> 24) & 0xff; + cpu_put_ccr(env, tstate >> 32); + cpu_put_cwp64(env, tstate & 0x1f); + __get_user(env->gregs[1], (&(*grp)[SPARC_MC_G1])); + __get_user(env->gregs[2], (&(*grp)[SPARC_MC_G2])); + __get_user(env->gregs[3], (&(*grp)[SPARC_MC_G3])); + __get_user(env->gregs[4], (&(*grp)[SPARC_MC_G4])); + __get_user(env->gregs[5], (&(*grp)[SPARC_MC_G5])); + __get_user(env->gregs[6], (&(*grp)[SPARC_MC_G6])); + __get_user(env->gregs[7], (&(*grp)[SPARC_MC_G7])); + __get_user(env->regwptr[UREG_I0], (&(*grp)[SPARC_MC_O0])); + __get_user(env->regwptr[UREG_I1], (&(*grp)[SPARC_MC_O1])); + __get_user(env->regwptr[UREG_I2], (&(*grp)[SPARC_MC_O2])); + __get_user(env->regwptr[UREG_I3], (&(*grp)[SPARC_MC_O3])); + __get_user(env->regwptr[UREG_I4], (&(*grp)[SPARC_MC_O4])); + __get_user(env->regwptr[UREG_I5], (&(*grp)[SPARC_MC_O5])); + __get_user(env->regwptr[UREG_I6], (&(*grp)[SPARC_MC_O6])); + __get_user(env->regwptr[UREG_I7], (&(*grp)[SPARC_MC_O7])); + + __get_user(fp, &(ucp->tuc_mcontext.mc_fp)); + __get_user(i7, &(ucp->tuc_mcontext.mc_i7)); + + w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; + if (put_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), + abi_ulong) != 0) { + goto do_sigsegv; + } + if (put_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), + abi_ulong) != 0) { + goto do_sigsegv; + } + /* FIXME this does not match how the kernel handles the FPU in + * its sparc64_set_context implementation. In particular the FPU + * is only restored if fenab is non-zero in: + * __get_user(fenab, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_enab)); + */ + __get_user(env->fprs, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fprs)); + { + uint32_t *src = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; + for (i = 0; i < 64; i++, src++) { + if (i & 1) { + __get_user(env->fpr[i/2].l.lower, src); + } else { + __get_user(env->fpr[i/2].l.upper, src); + } + } + } + __get_user(env->fsr, + &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fsr)); + __get_user(env->gsr, + &(ucp->tuc_mcontext.mc_fpregs.mcfpu_gsr)); + unlock_user_struct(ucp, ucp_addr, 0); + return; +do_sigsegv: + unlock_user_struct(ucp, ucp_addr, 0); + force_sig(TARGET_SIGSEGV); +} + +void sparc64_get_context(CPUSPARCState *env) +{ + abi_ulong ucp_addr; + struct target_ucontext *ucp; + target_mc_gregset_t *grp; + target_mcontext_t *mcp; + abi_ulong fp, i7, w_addr; + int err; + unsigned int i; + target_sigset_t target_set; + sigset_t set; + + ucp_addr = env->regwptr[UREG_I0]; + if (!lock_user_struct(VERIFY_WRITE, ucp, ucp_addr, 0)) { + goto do_sigsegv; + } + + mcp = &ucp->tuc_mcontext; + grp = &mcp->mc_gregs; + + /* Skip over the trap instruction, first. */ + env->pc = env->npc; + env->npc += 4; + + /* If we're only reading the signal mask then do_sigprocmask() + * is guaranteed not to fail, which is important because we don't + * have any way to signal a failure or restart this operation since + * this is not a normal syscall. + */ + err = do_sigprocmask(0, NULL, &set); + assert(err == 0); + host_to_target_sigset_internal(&target_set, &set); + if (TARGET_NSIG_WORDS == 1) { + __put_user(target_set.sig[0], + (abi_ulong *)&ucp->tuc_sigmask); + } else { + abi_ulong *src, *dst; + src = target_set.sig; + dst = ucp->tuc_sigmask.sig; + for (i = 0; i < TARGET_NSIG_WORDS; i++, dst++, src++) { + __put_user(*src, dst); + } + if (err) + goto do_sigsegv; + } + + /* XXX: tstate must be saved properly */ + // __put_user(env->tstate, &((*grp)[SPARC_MC_TSTATE])); + __put_user(env->pc, &((*grp)[SPARC_MC_PC])); + __put_user(env->npc, &((*grp)[SPARC_MC_NPC])); + __put_user(env->y, &((*grp)[SPARC_MC_Y])); + __put_user(env->gregs[1], &((*grp)[SPARC_MC_G1])); + __put_user(env->gregs[2], &((*grp)[SPARC_MC_G2])); + __put_user(env->gregs[3], &((*grp)[SPARC_MC_G3])); + __put_user(env->gregs[4], &((*grp)[SPARC_MC_G4])); + __put_user(env->gregs[5], &((*grp)[SPARC_MC_G5])); + __put_user(env->gregs[6], &((*grp)[SPARC_MC_G6])); + __put_user(env->gregs[7], &((*grp)[SPARC_MC_G7])); + __put_user(env->regwptr[UREG_I0], &((*grp)[SPARC_MC_O0])); + __put_user(env->regwptr[UREG_I1], &((*grp)[SPARC_MC_O1])); + __put_user(env->regwptr[UREG_I2], &((*grp)[SPARC_MC_O2])); + __put_user(env->regwptr[UREG_I3], &((*grp)[SPARC_MC_O3])); + __put_user(env->regwptr[UREG_I4], &((*grp)[SPARC_MC_O4])); + __put_user(env->regwptr[UREG_I5], &((*grp)[SPARC_MC_O5])); + __put_user(env->regwptr[UREG_I6], &((*grp)[SPARC_MC_O6])); + __put_user(env->regwptr[UREG_I7], &((*grp)[SPARC_MC_O7])); + + w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; + fp = i7 = 0; + if (get_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), + abi_ulong) != 0) { + goto do_sigsegv; + } + if (get_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), + abi_ulong) != 0) { + goto do_sigsegv; + } + __put_user(fp, &(mcp->mc_fp)); + __put_user(i7, &(mcp->mc_i7)); + + { + uint32_t *dst = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; + for (i = 0; i < 64; i++, dst++) { + if (i & 1) { + __put_user(env->fpr[i/2].l.lower, dst); + } else { + __put_user(env->fpr[i/2].l.upper, dst); + } + } + } + __put_user(env->fsr, &(mcp->mc_fpregs.mcfpu_fsr)); + __put_user(env->gsr, &(mcp->mc_fpregs.mcfpu_gsr)); + __put_user(env->fprs, &(mcp->mc_fpregs.mcfpu_fprs)); + + if (err) + goto do_sigsegv; + unlock_user_struct(ucp, ucp_addr, 1); + return; +do_sigsegv: + unlock_user_struct(ucp, ucp_addr, 1); + force_sig(TARGET_SIGSEGV); +} +#endif diff --git a/linux-user/sparc/sockbits.h b/linux-user/sparc/sockbits.h new file mode 100644 index 0000000000..0a822e3e1f --- /dev/null +++ b/linux-user/sparc/sockbits.h @@ -0,0 +1,111 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef SPARC_SOCKBITS_H +#define SPARC_SOCKBITS_H + +/* For setsockopt(2) */ +#define TARGET_SOL_SOCKET 0xffff + +#define TARGET_SO_DEBUG 0x0001 +#define TARGET_SO_PASSCRED 0x0002 +#define TARGET_SO_REUSEADDR 0x0004 +#define TARGET_SO_KEEPALIVE 0x0008 +#define TARGET_SO_DONTROUTE 0x0010 +#define TARGET_SO_BROADCAST 0x0020 +#define TARGET_SO_PEERCRED 0x0040 +#define TARGET_SO_LINGER 0x0080 +#define TARGET_SO_OOBINLINE 0x0100 +#define TARGET_SO_REUSEPORT 0x0200 +#define TARGET_SO_BSDCOMPAT 0x0400 +#define TARGET_SO_RCVLOWAT 0x0800 +#define TARGET_SO_SNDLOWAT 0x1000 +#define TARGET_SO_RCVTIMEO 0x2000 +#define TARGET_SO_SNDTIMEO 0x4000 +#define TARGET_SO_ACCEPTCONN 0x8000 + +#define TARGET_SO_SNDBUF 0x1001 +#define TARGET_SO_RCVBUF 0x1002 +#define TARGET_SO_SNDBUFFORCE 0x100a +#define TARGET_SO_RCVBUFFORCE 0x100b +#define TARGET_SO_ERROR 0x1007 +#define TARGET_SO_TYPE 0x1008 +#define TARGET_SO_PROTOCOL 0x1028 +#define TARGET_SO_DOMAIN 0x1029 + +/* Linux specific, keep the same. */ +#define TARGET_SO_NO_CHECK 0x000b +#define TARGET_SO_PRIORITY 0x000c + +#define TARGET_SO_BINDTODEVICE 0x000d + +#define TARGET_SO_ATTACH_FILTER 0x001a +#define TARGET_SO_DETACH_FILTER 0x001b +#define TARGET_SO_GET_FILTER TARGET_SO_ATTACH_FILTER + +#define TARGET_SO_PEERNAME 0x001c +#define TARGET_SO_TIMESTAMP 0x001d +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP + +#define TARGET_SO_PEERSEC 0x001e +#define TARGET_SO_PASSSEC 0x001f +#define TARGET_SO_TIMESTAMPNS 0x0021 +#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS + +#define TARGET_SO_MARK 0x0022 + +#define TARGET_SO_TIMESTAMPING 0x0023 +#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING + +#define TARGET_SO_RXQ_OVFL 0x0024 + +#define TARGET_SO_WIFI_STATUS 0x0025 +#define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS +#define TARGET_SO_PEEK_OFF 0x0026 + +/* Instruct lower device to use last 4-bytes of skb data as FCS */ +#define TARGET_SO_NOFCS 0x0027 + +#define TARGET_SO_LOCK_FILTER 0x0028 + +#define TARGET_SO_SELECT_ERR_QUEUE 0x0029 + +#define TARGET_SO_BUSY_POLL 0x0030 + +#define TARGET_SO_MAX_PACING_RATE 0x0031 + +#define TARGET_SO_BPF_EXTENSIONS 0x0032 + +#define TARGET_SO_INCOMING_CPU 0x0033 + +#define TARGET_SO_ATTACH_BPF 0x0034 +#define TARGET_SO_DETACH_BPF TARGET_SO_DETACH_FILTER + +#define TARGET_SO_ATTACH_REUSEPORT_CBPF 0x0035 +#define TARGET_SO_ATTACH_REUSEPORT_EBPF 0x0036 + +#define TARGET_SO_CNX_ADVICE 0x0037 + +#define TARGET_SCM_TIMESTAMPING_OPT_STATS 0x0038 + +#define TARGET_SO_MEMINFO 0x0039 + +#define TARGET_SO_INCOMING_NAPI_ID 0x003a + +#define TARGET_SO_COOKIE 0x003b + +#define TARGET_SCM_TIMESTAMPING_PKTINFO 0x003c + +#define TARGET_SO_PEERGROUPS 0x003d + +#define TARGET_SO_ZEROCOPY 0x003e + +/* Security levels - as per NRL IPv6 - don't actually do anything */ +#define TARGET_SO_SECURITY_AUTHENTICATION 0x5001 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 0x5004 +#endif diff --git a/linux-user/sparc/syscall_nr.h b/linux-user/sparc/syscall_nr.h index e713c9d5f4..2d77e19bec 100644 --- a/linux-user/sparc/syscall_nr.h +++ b/linux-user/sparc/syscall_nr.h @@ -22,6 +22,7 @@ #define TARGET_NR_capset 22 /* Linux Specific */ #define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */ #define TARGET_NR_getuid 24 /* Common */ +#define TARGET_NR_vmsplice 25 #define TARGET_NR_ptrace 26 /* Common */ #define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */ #define TARGET_NR_sigaltstack 28 /* Common */ @@ -135,6 +136,7 @@ #define TARGET_NR_rmdir 137 /* Common */ #define TARGET_NR_utimes 138 /* SunOS Specific */ #define TARGET_NR_stat64 139 /* Linux sparc32 Specific */ +#define TARGET_NR_sendfile64 140 #define TARGET_NR_getpeername 141 /* Common */ #define TARGET_NR_futex 142 /* gethostid under SunOS */ #define TARGET_NR_gettid 143 /* ENOSYS under SunOS */ @@ -145,29 +147,51 @@ #define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */ #define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */ #define TARGET_NR_getsockname 150 /* Common */ +#define TARGET_NR_inotify_init 151 +#define TARGET_NR_inotify_add_watch 152 #define TARGET_NR_poll 153 /* Common */ #define TARGET_NR_getdents64 154 /* Linux specific */ #define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */ +#define TARGET_NR_inotify_rm_watch 156 #define TARGET_NR_statfs 157 /* Common */ #define TARGET_NR_fstatfs 158 /* Common */ #define TARGET_NR_umount 159 /* Common */ +#define TARGET_NR_sched_set_affinity 160 +#define TARGET_NR_sched_get_affinity 161 #define TARGET_NR_getdomainname 162 /* SunOS Specific */ #define TARGET_NR_setdomainname 163 /* Common */ #define TARGET_NR_quotactl 165 /* Common */ #define TARGET_NR_set_tid_address 166 /* Linux specific, exportfs under SunOS */ #define TARGET_NR_mount 167 /* Common */ #define TARGET_NR_ustat 168 /* Common */ +#define TARGET_NR_setxattr 169 +#define TARGET_NR_lsetxattr 170 +#define TARGET_NR_fsetxattr 171 +#define TARGET_NR_getxattr 172 +#define TARGET_NR_lgetxattr 173 #define TARGET_NR_getdents 174 /* Common */ #define TARGET_NR_setsid 175 /* Common */ #define TARGET_NR_fchdir 176 /* Common */ +#define TARGET_NR_fgetxattr 177 +#define TARGET_NR_listxattr 178 +#define TARGET_NR_llistxattr 179 +#define TARGET_NR_flistxattr 180 +#define TARGET_NR_removexattr 181 +#define TARGET_NR_lremovexattr 182 #define TARGET_NR_sigpending 183 /* Common */ #define TARGET_NR_query_module 184 /* Linux Specific */ #define TARGET_NR_setpgid 185 /* Common */ +#define TARGET_NR_fremovexattr 186 #define TARGET_NR_tkill 187 /* SunOS: fpathconf */ #define TARGET_NR_exit_group 188 /* Linux specific, sysconf undef SunOS */ #define TARGET_NR_uname 189 /* Linux Specific */ #define TARGET_NR_init_module 190 /* Linux Specific */ #define TARGET_NR_personality 191 /* Linux Specific */ +#define TARGET_NR_remap_file_pages 192 +#define TARGET_NR_epoll_create 193 +#define TARGET_NR_epoll_ctl 194 +#define TARGET_NR_epoll_wait 195 +#define TARGET_NR_ioprio_set 196 #define TARGET_NR_getppid 197 /* Linux Specific */ #define TARGET_NR_sigaction 198 /* Linux Specific */ #define TARGET_NR_sgetmask 199 /* Linux Specific */ @@ -189,6 +213,7 @@ #define TARGET_NR_ipc 215 /* Linux Specific */ #define TARGET_NR_sigreturn 216 /* Linux Specific */ #define TARGET_NR_clone 217 /* Linux Specific */ +#define TARGET_NR_ioprio_get 218 #define TARGET_NR_adjtimex 219 /* Linux Specific */ #define TARGET_NR_sigprocmask 220 /* Linux Specific */ #define TARGET_NR_create_module 221 /* Linux Specific */ @@ -202,6 +227,7 @@ #define TARGET_NR_setfsgid 229 /* Linux Specific */ #define TARGET_NR__newselect 230 /* Linux Specific */ #define TARGET_NR_time 231 /* Linux Specific */ +#define TARGET_NR_splice 232 #define TARGET_NR_stime 233 /* Linux Specific */ #define TARGET_NR_statfs64 234 /* Linux Specific */ #define TARGET_NR_fstatfs64 235 /* Linux Specific */ @@ -224,7 +250,7 @@ #define TARGET_NR_getsid 252 #define TARGET_NR_fdatasync 253 #define TARGET_NR_nfsservctl 254 -#define TARGET_NR_aplib 255 +#define TARGET_NR_sync_file_range 255 #define TARGET_NR_clock_settime 256 #define TARGET_NR_clock_gettime 257 #define TARGET_NR_clock_getres 258 @@ -326,3 +352,7 @@ #define TARGET_NR_listen 354 #define TARGET_NR_setsockopt 355 #define TARGET_NR_mlock2 356 +#define TARGET_NR_copy_file_range 357 +#define TARGET_NR_preadv2 358 +#define TARGET_NR_pwritev2 359 +#define TARGET_NR_statx 360 diff --git a/linux-user/sparc/target_cpu.h b/linux-user/sparc/target_cpu.h index f2fe526204..1ffc0ae9f2 100644 --- a/linux-user/sparc/target_cpu.h +++ b/linux-user/sparc/target_cpu.h @@ -41,4 +41,15 @@ static inline void cpu_set_tls(CPUSPARCState *env, target_ulong newtls) env->gregs[7] = newtls; } +#ifndef UREG_I6 +#define UREG_I6 6 +#endif +#ifndef UREG_FP +#define UREG_FP UREG_I6 +#endif + +static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) +{ + return state->regwptr[UREG_FP]; +} #endif diff --git a/linux-user/sparc/target_errno.h b/linux-user/sparc/target_errno.h new file mode 100644 index 0000000000..9b846899cd --- /dev/null +++ b/linux-user/sparc/target_errno.h @@ -0,0 +1,207 @@ +#ifndef SPARC_TARGET_ERRNO_H +#define SPARC_TARGET_ERRNO_H + +/* Target errno definitions taken from asm-sparc/errno.h */ +#undef TARGET_EWOULDBLOCK +#define TARGET_EWOULDBLOCK TARGET_EAGAIN /* Operation would block */ +#undef TARGET_EINPROGRESS +#define TARGET_EINPROGRESS 36 /* Operation now in progress */ +#undef TARGET_EALREADY +#define TARGET_EALREADY 37 /* Operation already in progress */ +#undef TARGET_ENOTSOCK +#define TARGET_ENOTSOCK 38 /* Socket operation on non-socket */ +#undef TARGET_EDESTADDRREQ +#define TARGET_EDESTADDRREQ 39 /* Destination address required */ +#undef TARGET_EMSGSIZE +#define TARGET_EMSGSIZE 40 /* Message too long */ +#undef TARGET_EPROTOTYPE +#define TARGET_EPROTOTYPE 41 /* Protocol wrong type for socket */ +#undef TARGET_ENOPROTOOPT +#define TARGET_ENOPROTOOPT 42 /* Protocol not available */ +#undef TARGET_EPROTONOSUPPORT +#define TARGET_EPROTONOSUPPORT 43 /* Protocol not supported */ +#undef TARGET_ESOCKTNOSUPPORT +#define TARGET_ESOCKTNOSUPPORT 44 /* Socket type not supported */ +#undef TARGET_EOPNOTSUPP +#define TARGET_EOPNOTSUPP 45 /* Op not supported on transport endpoint */ +#undef TARGET_EPFNOSUPPORT +#define TARGET_EPFNOSUPPORT 46 /* Protocol family not supported */ +#undef TARGET_EAFNOSUPPORT +#define TARGET_EAFNOSUPPORT 47 /* Address family not supported by protocol */ +#undef TARGET_EADDRINUSE +#define TARGET_EADDRINUSE 48 /* Address already in use */ +#undef TARGET_EADDRNOTAVAIL +#define TARGET_EADDRNOTAVAIL 49 /* Cannot assign requested address */ +#undef TARGET_ENETDOWN +#define TARGET_ENETDOWN 50 /* Network is down */ +#undef TARGET_ENETUNREACH +#define TARGET_ENETUNREACH 51 /* Network is unreachable */ +#undef TARGET_ENETRESET +#define TARGET_ENETRESET 52 /* Net dropped connection because of reset */ +#undef TARGET_ECONNABORTED +#define TARGET_ECONNABORTED 53 /* Software caused connection abort */ +#undef TARGET_ECONNRESET +#define TARGET_ECONNRESET 54 /* Connection reset by peer */ +#undef TARGET_ENOBUFS +#define TARGET_ENOBUFS 55 /* No buffer space available */ +#undef TARGET_EISCONN +#define TARGET_EISCONN 56 /* Transport endpoint is already connected */ +#undef TARGET_ENOTCONN +#define TARGET_ENOTCONN 57 /* Transport endpoint is not connected */ +#undef TARGET_ESHUTDOWN +#define TARGET_ESHUTDOWN 58 /* No send after transport endpoint shutdown*/ +#undef TARGET_ETOOMANYREFS +#define TARGET_ETOOMANYREFS 59 /* Too many references: cannot splice */ +#undef TARGET_ETIMEDOUT +#define TARGET_ETIMEDOUT 60 /* Connection timed out */ +#undef TARGET_ECONNREFUSED +#define TARGET_ECONNREFUSED 61 /* Connection refused */ +#undef TARGET_ELOOP +#define TARGET_ELOOP 62 /* Too many symbolic links encountered */ +#undef TARGET_ENAMETOOLONG +#define TARGET_ENAMETOOLONG 63 /* File name too long */ +#undef TARGET_EHOSTDOWN +#define TARGET_EHOSTDOWN 64 /* Host is down */ +#undef TARGET_EHOSTUNREACH +#define TARGET_EHOSTUNREACH 65 /* No route to host */ +#undef TARGET_ENOTEMPTY +#define TARGET_ENOTEMPTY 66 /* Directory not empty */ +#undef TARGET_EPROCLIM +#define TARGET_EPROCLIM 67 /* SUNOS: Too many processes */ +#undef TARGET_EUSERS +#define TARGET_EUSERS 68 /* Too many users */ +#undef TARGET_EDQUOT +#define TARGET_EDQUOT 69 /* Quota exceeded */ +#undef TARGET_ESTALE +#define TARGET_ESTALE 70 /* Stale file handle */ +#undef TARGET_EREMOTE +#define TARGET_EREMOTE 71 /* Object is remote */ +#undef TARGET_ENOSTR +#define TARGET_ENOSTR 72 /* Device not a stream */ +#undef TARGET_ETIME +#define TARGET_ETIME 73 /* Timer expired */ +#undef TARGET_ENOSR +#define TARGET_ENOSR 74 /* Out of streams resources */ +#undef TARGET_ENOMSG +#define TARGET_ENOMSG 75 /* No message of desired type */ +#undef TARGET_EBADMSG +#define TARGET_EBADMSG 76 /* Not a data message */ +#undef TARGET_EIDRM +#define TARGET_EIDRM 77 /* Identifier removed */ +#undef TARGET_EDEADLK +#define TARGET_EDEADLK 78 /* Resource deadlock would occur */ +#undef TARGET_ENOLCK +#define TARGET_ENOLCK 79 /* No record locks available */ +#undef TARGET_ENONET +#define TARGET_ENONET 80 /* Machine is not on the network */ +#undef TARGET_ERREMOTE +#define TARGET_ERREMOTE 81 /* SunOS: Too many lvls of remote in path */ +#undef TARGET_ENOLINK +#define TARGET_ENOLINK 82 /* Link has been severed */ +#undef TARGET_EADV +#define TARGET_EADV 83 /* Advertise error */ +#undef TARGET_ESRMNT +#define TARGET_ESRMNT 84 /* Srmount error */ +#undef TARGET_ECOMM +#define TARGET_ECOMM 85 /* Communication error on send */ +#undef TARGET_EPROTO +#define TARGET_EPROTO 86 /* Protocol error */ +#undef TARGET_EMULTIHOP +#define TARGET_EMULTIHOP 87 /* Multihop attempted */ +#undef TARGET_EDOTDOT +#define TARGET_EDOTDOT 88 /* RFS specific error */ +#undef TARGET_EREMCHG +#define TARGET_EREMCHG 89 /* Remote address changed */ +#undef TARGET_ENOSYS +#define TARGET_ENOSYS 90 /* Function not implemented */ +#undef TARGET_ESTRPIPE +#define TARGET_ESTRPIPE 91 /* Streams pipe error */ +#undef TARGET_EOVERFLOW +#define TARGET_EOVERFLOW 92 /* Value too large for defined data type */ +#undef TARGET_EBADFD +#define TARGET_EBADFD 93 /* File descriptor in bad state */ +#undef TARGET_ECHRNG +#define TARGET_ECHRNG 94 /* Channel number out of range */ +#undef TARGET_EL2NSYNC +#define TARGET_EL2NSYNC 95 /* Level 2 not synchronized */ +#undef TARGET_EL3HLT +#define TARGET_EL3HLT 96 /* Level 3 halted */ +#undef TARGET_EL3RST +#define TARGET_EL3RST 97 /* Level 3 reset */ +#undef TARGET_ELNRNG +#define TARGET_ELNRNG 98 /* Link number out of range */ +#undef TARGET_EUNATCH +#define TARGET_EUNATCH 99 /* Protocol driver not attached */ +#undef TARGET_ENOCSI +#define TARGET_ENOCSI 100 /* No CSI structure available */ +#undef TARGET_EL2HLT +#define TARGET_EL2HLT 101 /* Level 2 halted */ +#undef TARGET_EBADE +#define TARGET_EBADE 102 /* Invalid exchange */ +#undef TARGET_EBADR +#define TARGET_EBADR 103 /* Invalid request descriptor */ +#undef TARGET_EXFULL +#define TARGET_EXFULL 104 /* Exchange full */ +#undef TARGET_ENOANO +#define TARGET_ENOANO 105 /* No anode */ +#undef TARGET_EBADRQC +#define TARGET_EBADRQC 106 /* Invalid request code */ +#undef TARGET_EBADSLT +#define TARGET_EBADSLT 107 /* Invalid slot */ +#undef TARGET_EDEADLOCK +#define TARGET_EDEADLOCK 108 /* File locking deadlock error */ +#undef TARGET_EBFONT +#define TARGET_EBFONT 109 /* Bad font file format */ +#undef TARGET_ELIBEXEC +#define TARGET_ELIBEXEC 110 /* Cannot exec a shared library directly */ +#undef TARGET_ENODATA +#define TARGET_ENODATA 111 /* No data available */ +#undef TARGET_ELIBBAD +#define TARGET_ELIBBAD 112 /* Accessing a corrupted shared library */ +#undef TARGET_ENOPKG +#define TARGET_ENOPKG 113 /* Package not installed */ +#undef TARGET_ELIBACC +#define TARGET_ELIBACC 114 /* Can not access a needed shared library */ +#undef TARGET_ENOTUNIQ +#define TARGET_ENOTUNIQ 115 /* Name not unique on network */ +#undef TARGET_ERESTART +#define TARGET_ERESTART 116 /* Interrupted syscall should be restarted */ +#undef TARGET_EUCLEAN +#define TARGET_EUCLEAN 117 /* Structure needs cleaning */ +#undef TARGET_ENOTNAM +#define TARGET_ENOTNAM 118 /* Not a XENIX named type file */ +#undef TARGET_ENAVAIL +#define TARGET_ENAVAIL 119 /* No XENIX semaphores available */ +#undef TARGET_EISNAM +#define TARGET_EISNAM 120 /* Is a named type file */ +#undef TARGET_EREMOTEIO +#define TARGET_EREMOTEIO 121 /* Remote I/O error */ +#undef TARGET_EILSEQ +#define TARGET_EILSEQ 122 /* Illegal byte sequence */ +#undef TARGET_ELIBMAX +#define TARGET_ELIBMAX 123 /* Atmpt to link in too many shared libs */ +#undef TARGET_ELIBSCN +#define TARGET_ELIBSCN 124 /* .lib section in a.out corrupted */ +#undef TARGET_ENOMEDIUM +#define TARGET_ENOMEDIUM 125 /* No medium found */ +#undef TARGET_EMEDIUMTYPE +#define TARGET_EMEDIUMTYPE 126 /* Wrong medium type */ +#undef TARGET_ECANCELED +#define TARGET_ECANCELED 127 /* Operation Cancelled */ +#undef TARGET_ENOKEY +#define TARGET_ENOKEY 128 /* Required key not available */ +#undef TARGET_EKEYEXPIRED +#define TARGET_EKEYEXPIRED 129 /* Key has expired */ +#undef TARGET_EKEYREVOKED +#define TARGET_EKEYREVOKED 130 /* Key has been revoked */ +#undef TARGET_EKEYREJECTED +#define TARGET_EKEYREJECTED 131 /* Key was rejected by service */ +#undef TARGET_EOWNERDEAD +#define TARGET_EOWNERDEAD 132 /* Owner died */ +#undef TARGET_ENOTRECOVERABLE +#define TARGET_ENOTRECOVERABLE 133 /* State not recoverable */ +#undef TARGET_ERFKILL +#define TARGET_ERFKILL 134 /* Operation not possible due to RF-kill */ +#undef TARGET_EHWPOISON +#define TARGET_EHWPOISON 135 /* Memory page has hardware error */ +#endif diff --git a/linux-user/sparc/target_fcntl.h b/linux-user/sparc/target_fcntl.h new file mode 100644 index 0000000000..c2532989e5 --- /dev/null +++ b/linux-user/sparc/target_fcntl.h @@ -0,0 +1,45 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef SPARC_TARGET_FCNTL_H +#define SPARC_TARGET_FCNTL_H + +#define TARGET_O_APPEND 0x0008 +#define TARGET_FASYNC 0x0040 /* fcntl, for BSD compatibility */ +#define TARGET_O_CREAT 0x0200 /* not fcntl */ +#define TARGET_O_TRUNC 0x0400 /* not fcntl */ +#define TARGET_O_EXCL 0x0800 /* not fcntl */ +#define TARGET_O_DSYNC 0x2000 +#define TARGET_O_NONBLOCK 0x4000 +# ifdef TARGET_SPARC64 +# define TARGET_O_NDELAY 0x0004 +# else +# define TARGET_O_NDELAY (0x0004 | TARGET_O_NONBLOCK) +# endif +#define TARGET_O_NOCTTY 0x8000 /* not fcntl */ +#define TARGET_O_LARGEFILE 0x40000 +#define TARGET_O_DIRECT 0x100000 /* direct disk access hint */ +#define TARGET_O_NOATIME 0x200000 +#define TARGET_O_CLOEXEC 0x400000 +#define TARGET___O_SYNC 0x800000 +#define TARGET_O_PATH 0x1000000 +#define TARGET___O_TMPFILE 0x2000000 + +#define TARGET_F_RDLCK 1 +#define TARGET_F_WRLCK 2 +#define TARGET_F_UNLCK 3 +#define TARGET_F_GETOWN 5 /* for sockets. */ +#define TARGET_F_SETOWN 6 /* for sockets. */ +#define TARGET_F_GETLK 7 +#define TARGET_F_SETLK 8 +#define TARGET_F_SETLKW 9 + +#define TARGET_ARCH_FLOCK_PAD abi_short __unused; +#define TARGET_ARCH_FLOCK64_PAD abi_short __unused; + +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/sparc/target_signal.h b/linux-user/sparc/target_signal.h index e445e2b463..5cc40327d2 100644 --- a/linux-user/sparc/target_signal.h +++ b/linux-user/sparc/target_signal.h @@ -1,7 +1,43 @@ #ifndef SPARC_TARGET_SIGNAL_H #define SPARC_TARGET_SIGNAL_H -#include "cpu.h" +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGIOT 6 +#define TARGET_SIGSTKFLT 7 /* actually EMT */ +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGBUS 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGSYS 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGURG 16 +#define TARGET_SIGSTOP 17 +#define TARGET_SIGTSTP 18 +#define TARGET_SIGCONT 19 +#define TARGET_SIGCHLD 20 +#define TARGET_SIGTTIN 21 +#define TARGET_SIGTTOU 22 +#define TARGET_SIGIO 23 +#define TARGET_SIGXCPU 24 +#define TARGET_SIGXFSZ 25 +#define TARGET_SIGVTALRM 26 +#define TARGET_SIGPROF 27 +#define TARGET_SIGWINCH 28 +#define TARGET_SIGPWR 29 +#define TARGET_SIGUSR1 30 +#define TARGET_SIGUSR2 31 +#define TARGET_SIGRTMIN 32 + +#define TARGET_SIG_BLOCK 0x01 /* for blocking signals */ +#define TARGET_SIG_UNBLOCK 0x02 /* for unblocking signals */ +#define TARGET_SIG_SETMASK 0x04 /* for setting the signal mask */ /* this struct defines a stack used during syscall handling */ @@ -18,20 +54,18 @@ typedef struct target_sigaltstack { #define TARGET_SS_ONSTACK 1 #define TARGET_SS_DISABLE 2 +#define TARGET_SA_NOCLDSTOP 8u +#define TARGET_SA_NOCLDWAIT 0x100u +#define TARGET_SA_SIGINFO 0x200u +#define TARGET_SA_ONSTACK 1u +#define TARGET_SA_RESTART 2u +#define TARGET_SA_NODEFER 0x20u +#define TARGET_SA_RESETHAND 4u +#define TARGET_ARCH_HAS_SA_RESTORER 1 +#define TARGET_ARCH_HAS_KA_RESTORER 1 + #define TARGET_MINSIGSTKSZ 4096 #define TARGET_SIGSTKSZ 16384 -#ifndef UREG_I6 -#define UREG_I6 6 -#endif -#ifndef UREG_FP -#define UREG_FP UREG_I6 -#endif - -static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) -{ - return state->regwptr[UREG_FP]; -} - - +#define TARGET_ARCH_HAS_SETUP_FRAME #endif /* SPARC_TARGET_SIGNAL_H */ diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index 5f09abfe89..b9160a771b 100644 --- a/linux-user/sparc/target_syscall.h +++ b/linux-user/sparc/target_syscall.h @@ -1,6 +1,8 @@ #ifndef SPARC_TARGET_SYSCALL_H #define SPARC_TARGET_SYSCALL_H +#include "target_errno.h" + struct target_pt_regs { abi_ulong psr; abi_ulong pc; @@ -9,7 +11,7 @@ struct target_pt_regs { abi_ulong u_regs[16]; }; -#define UNAME_MACHINE "sun4" +#define UNAME_MACHINE "sparc" #define UNAME_MINIMUM_RELEASE "2.6.32" /* SPARC kernels don't define this in their Kconfig, but they have the diff --git a/linux-user/sparc64/cpu_loop.c b/linux-user/sparc64/cpu_loop.c new file mode 100644 index 0000000000..4fd44e1b1e --- /dev/null +++ b/linux-user/sparc64/cpu_loop.c @@ -0,0 +1,20 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "../sparc/cpu_loop.c" diff --git a/linux-user/sparc64/signal.c b/linux-user/sparc64/signal.c new file mode 100644 index 0000000000..170ebac232 --- /dev/null +++ b/linux-user/sparc64/signal.c @@ -0,0 +1,19 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "../sparc/signal.c" diff --git a/linux-user/sparc64/sockbits.h b/linux-user/sparc64/sockbits.h new file mode 100644 index 0000000000..658899e4d3 --- /dev/null +++ b/linux-user/sparc64/sockbits.h @@ -0,0 +1 @@ +#include "../sparc/sockbits.h" diff --git a/linux-user/sparc64/syscall_nr.h b/linux-user/sparc64/syscall_nr.h index 2b49ead267..0b91b896da 100644 --- a/linux-user/sparc64/syscall_nr.h +++ b/linux-user/sparc64/syscall_nr.h @@ -23,7 +23,7 @@ #define TARGET_NR_capset 22 /* Linux Specific */ #define TARGET_NR_setuid 23 /* Implemented via setreuid in SunOS */ #define TARGET_NR_getuid 24 /* Common */ -/* #define TARGET_NR_time alias 25 ENOSYS under SunOS */ +#define TARGET_NR_vmsplice 25 #define TARGET_NR_ptrace 26 /* Common */ #define TARGET_NR_alarm 27 /* Implemented via setitimer in SunOS */ #define TARGET_NR_sigaltstack 28 /* Common */ @@ -149,12 +149,12 @@ #define TARGET_NR_pciconfig_read 148 /* ENOSYS under SunOS */ #define TARGET_NR_pciconfig_write 149 /* ENOSYS under SunOS */ #define TARGET_NR_getsockname 150 /* Common */ -/* #define TARGET_NR_getmsg 151 SunOS Specific */ -/* #define TARGET_NR_putmsg 152 SunOS Specific */ +#define TARGET_NR_inotify_init 151 +#define TARGET_NR_inotify_add_watch 152 #define TARGET_NR_poll 153 /* Common */ #define TARGET_NR_getdents64 154 /* Linux specific */ #define TARGET_NR_fcntl64 155 /* Linux sparc32 Specific */ -/* #define TARGET_NR_getdirentries 156 SunOS Specific */ +#define TARGET_NR_inotify_rm_watch 156 /* Linux specific */ #define TARGET_NR_statfs 157 /* Common */ #define TARGET_NR_fstatfs 158 /* Common */ #define TARGET_NR_umount 159 /* Common */ @@ -194,7 +194,7 @@ #define TARGET_NR_epoll_create 193 /* Linux Specific */ #define TARGET_NR_epoll_ctl 194 /* Linux Specific */ #define TARGET_NR_epoll_wait 195 /* Linux Specific */ -/* #define TARGET_NR_ulimit 196 Linux Specific */ +#define TARGET_NR_ioprio_set 196 #define TARGET_NR_getppid 197 /* Linux Specific */ #define TARGET_NR_sigaction 198 /* Linux Specific */ #define TARGET_NR_sgetmask 199 /* Linux Specific */ @@ -216,7 +216,7 @@ #define TARGET_NR_ipc 215 /* Linux Specific */ #define TARGET_NR_sigreturn 216 /* Linux Specific */ #define TARGET_NR_clone 217 /* Linux Specific */ -/* #define TARGET_NR_modify_ldt 218 Linux Specific - i386 specific, unused */ +#define TARGET_NR_ioprio_get 218 #define TARGET_NR_adjtimex 219 /* Linux Specific */ #define TARGET_NR_sigprocmask 220 /* Linux Specific */ #define TARGET_NR_create_module 221 /* Linux Specific */ @@ -230,7 +230,7 @@ #define TARGET_NR_setfsgid 229 /* Linux Specific */ #define TARGET_NR__newselect 230 /* Linux Specific */ #define TARGET_NR_time 231 /* Linux sparc32 */ -/* #define TARGET_NR_oldstat 232 Linux Specific */ +#define TARGET_NR_splice 232 #define TARGET_NR_stime 233 /* Linux Specific */ #define TARGET_NR_statfs64 234 /* Linux Specific */ #define TARGET_NR_fstatfs64 235 /* Linux Specific */ @@ -253,7 +253,7 @@ #define TARGET_NR_getsid 252 #define TARGET_NR_fdatasync 253 #define TARGET_NR_nfsservctl 254 -#define TARGET_NR_aplib 255 +#define TARGET_NR_sync_file_range 255 #define TARGET_NR_clock_settime 256 #define TARGET_NR_clock_gettime 257 #define TARGET_NR_clock_getres 258 @@ -278,7 +278,7 @@ #define TARGET_NR_mq_notify 277 #define TARGET_NR_mq_getsetattr 278 #define TARGET_NR_waitid 279 -/*#define TARGET_NR_sys_setaltroot 280 available (was setaltroot) */ +#define TARGET_NR_tee 280 #define TARGET_NR_add_key 281 #define TARGET_NR_request_key 282 #define TARGET_NR_keyctl 283 @@ -310,7 +310,7 @@ #define TARGET_NR_epoll_pwait 309 #define TARGET_NR_utimensat 310 #define TARGET_NR_signalfd 311 -#define TARGET_NR_timerfd 312 +#define TARGET_NR_timerfd_create 312 #define TARGET_NR_eventfd 313 #define TARGET_NR_fallocate 314 #define TARGET_NR_timerfd_settime 315 @@ -355,3 +355,7 @@ #define TARGET_NR_listen 354 #define TARGET_NR_setsockopt 355 #define TARGET_NR_mlock2 356 +#define TARGET_NR_copy_file_range 357 +#define TARGET_NR_preadv2 358 +#define TARGET_NR_pwritev2 359 +#define TARGET_NR_statx 360 diff --git a/linux-user/sparc64/target_fcntl.h b/linux-user/sparc64/target_fcntl.h new file mode 100644 index 0000000000..053c774257 --- /dev/null +++ b/linux-user/sparc64/target_fcntl.h @@ -0,0 +1 @@ +#include "../sparc/target_fcntl.h" diff --git a/linux-user/sparc64/target_signal.h b/linux-user/sparc64/target_signal.h index 4449457baf..6a7d57d024 100644 --- a/linux-user/sparc64/target_signal.h +++ b/linux-user/sparc64/target_signal.h @@ -1,37 +1 @@ -#ifndef SPARC64_TARGET_SIGNAL_H -#define SPARC64_TARGET_SIGNAL_H - -#include "cpu.h" - -/* this struct defines a stack used during syscall handling */ - -typedef struct target_sigaltstack { - abi_ulong ss_sp; - abi_long ss_flags; - abi_ulong ss_size; -} target_stack_t; - - -/* - * sigaltstack controls - */ -#define TARGET_SS_ONSTACK 1 -#define TARGET_SS_DISABLE 2 - -#define TARGET_MINSIGSTKSZ 4096 -#define TARGET_SIGSTKSZ 16384 - -#ifndef UREG_I6 -#define UREG_I6 6 -#endif -#ifndef UREG_FP -#define UREG_FP UREG_I6 -#endif - -static inline abi_ulong get_sp_from_cpustate(CPUSPARCState *state) -{ - return state->regwptr[UREG_FP]; -} - - -#endif /* SPARC64_TARGET_SIGNAL_H */ +#include "../sparc/target_signal.h" diff --git a/linux-user/sparc64/target_syscall.h b/linux-user/sparc64/target_syscall.h index 2cbbaaed1b..3073a23e03 100644 --- a/linux-user/sparc64/target_syscall.h +++ b/linux-user/sparc64/target_syscall.h @@ -1,6 +1,8 @@ #ifndef SPARC64_TARGET_SYSCALL_H #define SPARC64_TARGET_SYSCALL_H +#include "../sparc/target_errno.h" + struct target_pt_regs { abi_ulong u_regs[16]; abi_ulong tstate; @@ -10,7 +12,7 @@ struct target_pt_regs { abi_ulong fprs; }; -#define UNAME_MACHINE "sun4u" +#define UNAME_MACHINE "sparc64" #define UNAME_MINIMUM_RELEASE "2.6.32" /* SPARC kernels don't define this in their Kconfig, but they have the @@ -29,5 +31,4 @@ static inline abi_ulong target_shmlba(CPUSPARCState *env) { return MAX(TARGET_PAGE_SIZE, 16 * 1024); } - #endif /* SPARC64_TARGET_SYSCALL_H */ diff --git a/linux-user/strace.list b/linux-user/strace.list index a91e33f7e5..ff8bb19f5f 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -59,10 +59,10 @@ { TARGET_NR_cacheflush, "cacheflush" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_capget -{ TARGET_NR_capget, "capget" , NULL, NULL, NULL }, +{ TARGET_NR_capget, "capget" , "%s(%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_capset -{ TARGET_NR_capset, "capset" , NULL, NULL, NULL }, +{ TARGET_NR_capset, "capset" , "%s(%p,%p)", NULL, NULL }, #endif #ifdef TARGET_NR_chdir { TARGET_NR_chdir, "chdir" , NULL, print_chdir, NULL }, @@ -1107,7 +1107,7 @@ { TARGET_NR_recvmmsg, "recvmmsg" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_recvmsg -{ TARGET_NR_recvmsg, "recvmsg" , NULL, NULL, NULL }, +{ TARGET_NR_recvmsg, "recvmsg" , "%s(%d,%p,%#x)", NULL, NULL }, #endif #ifdef TARGET_NR_remap_file_pages { TARGET_NR_remap_file_pages, "remap_file_pages" , NULL, NULL, NULL }, @@ -1467,15 +1467,6 @@ #ifdef TARGET_NR__sysctl { TARGET_NR__sysctl, "_sysctl" , NULL, NULL, NULL }, #endif -#ifdef TARGET_NR_sys_epoll_create -{ TARGET_NR_sys_epoll_create, "sys_epoll_create" , NULL, NULL, NULL }, -#endif -#ifdef TARGET_NR_sys_epoll_ctl -{ TARGET_NR_sys_epoll_ctl, "sys_epoll_ctl" , NULL, NULL, NULL }, -#endif -#ifdef TARGET_NR_sys_epoll_wait -{ TARGET_NR_sys_epoll_wait, "sys_epoll_wait" , NULL, NULL, NULL }, -#endif #ifdef TARGET_NR_sysfs { TARGET_NR_sysfs, "sysfs" , NULL, NULL, NULL }, #endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 643b8833de..dfc851cc35 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -259,10 +259,22 @@ static int gettid(void) { return -ENOSYS; } #endif -#if defined(TARGET_NR_getdents) && defined(__NR_getdents) + +/* For the 64-bit guest on 32-bit host case we must emulate + * getdents using getdents64, because otherwise the host + * might hand us back more dirent records than we can fit + * into the guest buffer after structure format conversion. + * Otherwise we emulate getdents with getdents if the host has it. + */ +#if defined(__NR_getdents) && HOST_LONG_BITS >= TARGET_ABI_BITS +#define EMULATE_GETDENTS_WITH_GETDENTS +#endif + +#if defined(TARGET_NR_getdents) && defined(EMULATE_GETDENTS_WITH_GETDENTS) _syscall3(int, sys_getdents, uint, fd, struct linux_dirent *, dirp, uint, count); #endif -#if !defined(__NR_getdents) || \ +#if (defined(TARGET_NR_getdents) && \ + !defined(EMULATE_GETDENTS_WITH_GETDENTS)) || \ (defined(TARGET_NR_getdents64) && defined(__NR_getdents64)) _syscall3(int, sys_getdents64, uint, fd, struct linux_dirent64 *, dirp, uint, count); #endif @@ -393,6 +405,8 @@ enum { QEMU_IFLA_BR_PAD, QEMU_IFLA_BR_VLAN_STATS_ENABLED, QEMU_IFLA_BR_MCAST_STATS_ENABLED, + QEMU_IFLA_BR_MCAST_IGMP_VERSION, + QEMU_IFLA_BR_MCAST_MLD_VERSION, QEMU___IFLA_BR_MAX, }; @@ -441,6 +455,12 @@ enum { QEMU_IFLA_GSO_MAX_SIZE, QEMU_IFLA_PAD, QEMU_IFLA_XDP, + QEMU_IFLA_EVENT, + QEMU_IFLA_NEW_NETNSID, + QEMU_IFLA_IF_NETNSID, + QEMU_IFLA_CARRIER_UP_COUNT, + QEMU_IFLA_CARRIER_DOWN_COUNT, + QEMU_IFLA_NEW_IFINDEX, QEMU___IFLA_MAX }; @@ -472,6 +492,12 @@ enum { QEMU_IFLA_BRPORT_FLUSH, QEMU_IFLA_BRPORT_MULTICAST_ROUTER, QEMU_IFLA_BRPORT_PAD, + QEMU_IFLA_BRPORT_MCAST_FLOOD, + QEMU_IFLA_BRPORT_MCAST_TO_UCAST, + QEMU_IFLA_BRPORT_VLAN_TUNNEL, + QEMU_IFLA_BRPORT_BCAST_FLOOD, + QEMU_IFLA_BRPORT_GROUP_FWD_MASK, + QEMU_IFLA_BRPORT_NEIGH_SUPPRESS, QEMU___IFLA_BRPORT_MAX }; @@ -504,6 +530,15 @@ enum { QEMU___IFLA_INET6_MAX }; +enum { + QEMU_IFLA_XDP_UNSPEC, + QEMU_IFLA_XDP_FD, + QEMU_IFLA_XDP_ATTACHED, + QEMU_IFLA_XDP_FLAGS, + QEMU_IFLA_XDP_PROG_ID, + QEMU___IFLA_XDP_MAX, +}; + typedef abi_long (*TargetFdDataFunc)(void *, size_t); typedef abi_long (*TargetFdAddrFunc)(void *, abi_ulong, socklen_t); typedef struct TargetFdTrans { @@ -871,11 +906,6 @@ static inline abi_long get_errno(abi_long ret) return ret; } -static inline int is_error(abi_long ret) -{ - return (abi_ulong)ret >= (abi_ulong)(-4096); -} - const char *target_strerror(int err) { if (err == TARGET_ERESTARTSYS) { @@ -1813,6 +1843,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, /* Payload types which need a different size of payload on * the target must adjust tgt_len here. */ + tgt_len = len; switch (cmsg->cmsg_level) { case SOL_SOCKET: switch (cmsg->cmsg_type) { @@ -1822,8 +1853,8 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, default: break; } + break; default: - tgt_len = len; break; } @@ -2170,6 +2201,10 @@ static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BR_NF_CALL_IPTABLES: case QEMU_IFLA_BR_NF_CALL_IP6TABLES: case QEMU_IFLA_BR_NF_CALL_ARPTABLES: + case QEMU_IFLA_BR_VLAN_STATS_ENABLED: + case QEMU_IFLA_BR_MCAST_STATS_ENABLED: + case QEMU_IFLA_BR_MCAST_IGMP_VERSION: + case QEMU_IFLA_BR_MCAST_MLD_VERSION: break; /* uint16_t */ case QEMU_IFLA_BR_PRIORITY: @@ -2241,6 +2276,11 @@ static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: case QEMU_IFLA_BRPORT_CONFIG_PENDING: case QEMU_IFLA_BRPORT_MULTICAST_ROUTER: + case QEMU_IFLA_BRPORT_MCAST_FLOOD: + case QEMU_IFLA_BRPORT_MCAST_TO_UCAST: + case QEMU_IFLA_BRPORT_VLAN_TUNNEL: + case QEMU_IFLA_BRPORT_BCAST_FLOOD: + case QEMU_IFLA_BRPORT_NEIGH_SUPPRESS: break; /* uint16_t */ case QEMU_IFLA_BRPORT_PRIORITY: @@ -2248,6 +2288,7 @@ static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, case QEMU_IFLA_BRPORT_DESIGNATED_COST: case QEMU_IFLA_BRPORT_ID: case QEMU_IFLA_BRPORT_NO: + case QEMU_IFLA_BRPORT_GROUP_FWD_MASK: u16 = NLA_DATA(nlattr); *u16 = tswap16(*u16); break; @@ -2422,6 +2463,27 @@ static abi_long host_to_target_data_spec_nlattr(struct nlattr *nlattr, return 0; } +static abi_long host_to_target_data_xdp_nlattr(struct nlattr *nlattr, + void *context) +{ + uint32_t *u32; + + switch (nlattr->nla_type) { + /* uint8_t */ + case QEMU_IFLA_XDP_ATTACHED: + break; + /* uint32_t */ + case QEMU_IFLA_XDP_PROG_ID: + u32 = NLA_DATA(nlattr); + *u32 = tswap32(*u32); + break; + default: + gemu_log("Unknown host XDP type: %d\n", nlattr->nla_type); + break; + } + return 0; +} + static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) { uint32_t *u32; @@ -2460,6 +2522,8 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) case QEMU_IFLA_NUM_VF: case QEMU_IFLA_GSO_MAX_SEGS: case QEMU_IFLA_GSO_MAX_SIZE: + case QEMU_IFLA_CARRIER_UP_COUNT: + case QEMU_IFLA_CARRIER_DOWN_COUNT: u32 = RTA_DATA(rtattr); *u32 = tswap32(*u32); break; @@ -2547,6 +2611,10 @@ static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, NULL, host_to_target_data_spec_nlattr); + case QEMU_IFLA_XDP: + return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, + NULL, + host_to_target_data_xdp_nlattr); default: gemu_log("Unknown host QEMU_IFLA type: %d\n", rtattr->rta_type); break; @@ -2951,6 +3019,8 @@ static abi_long do_setsockopt(int sockfd, int level, int optname, case IPV6_V6ONLY: case IPV6_RECVPKTINFO: case IPV6_UNICAST_HOPS: + case IPV6_MULTICAST_HOPS: + case IPV6_MULTICAST_LOOP: case IPV6_RECVERR: case IPV6_RECVHOPLIMIT: case IPV6_2292HOPLIMIT: @@ -3773,6 +3843,8 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, } msg.msg_controllen = 2 * tswapal(msgp->msg_controllen); msg.msg_control = alloca(msg.msg_controllen); + memset(msg.msg_control, 0, msg.msg_controllen); + msg.msg_flags = tswap32(msgp->msg_flags); count = tswapal(msgp->msg_iovlen); @@ -6475,87 +6547,143 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, /* warning : doesn't handle linux specific flags... */ static int target_to_host_fcntl_cmd(int cmd) { + int ret; + switch(cmd) { - case TARGET_F_DUPFD: - case TARGET_F_GETFD: - case TARGET_F_SETFD: - case TARGET_F_GETFL: - case TARGET_F_SETFL: - return cmd; - case TARGET_F_GETLK: - return F_GETLK64; - case TARGET_F_SETLK: - return F_SETLK64; - case TARGET_F_SETLKW: - return F_SETLKW64; - case TARGET_F_GETOWN: - return F_GETOWN; - case TARGET_F_SETOWN: - return F_SETOWN; - case TARGET_F_GETSIG: - return F_GETSIG; - case TARGET_F_SETSIG: - return F_SETSIG; + case TARGET_F_DUPFD: + case TARGET_F_GETFD: + case TARGET_F_SETFD: + case TARGET_F_GETFL: + case TARGET_F_SETFL: + ret = cmd; + break; + case TARGET_F_GETLK: + ret = F_GETLK64; + break; + case TARGET_F_SETLK: + ret = F_SETLK64; + break; + case TARGET_F_SETLKW: + ret = F_SETLKW64; + break; + case TARGET_F_GETOWN: + ret = F_GETOWN; + break; + case TARGET_F_SETOWN: + ret = F_SETOWN; + break; + case TARGET_F_GETSIG: + ret = F_GETSIG; + break; + case TARGET_F_SETSIG: + ret = F_SETSIG; + break; #if TARGET_ABI_BITS == 32 - case TARGET_F_GETLK64: - return F_GETLK64; - case TARGET_F_SETLK64: - return F_SETLK64; - case TARGET_F_SETLKW64: - return F_SETLKW64; + case TARGET_F_GETLK64: + ret = F_GETLK64; + break; + case TARGET_F_SETLK64: + ret = F_SETLK64; + break; + case TARGET_F_SETLKW64: + ret = F_SETLKW64; + break; #endif - case TARGET_F_SETLEASE: - return F_SETLEASE; - case TARGET_F_GETLEASE: - return F_GETLEASE; + case TARGET_F_SETLEASE: + ret = F_SETLEASE; + break; + case TARGET_F_GETLEASE: + ret = F_GETLEASE; + break; #ifdef F_DUPFD_CLOEXEC - case TARGET_F_DUPFD_CLOEXEC: - return F_DUPFD_CLOEXEC; + case TARGET_F_DUPFD_CLOEXEC: + ret = F_DUPFD_CLOEXEC; + break; #endif - case TARGET_F_NOTIFY: - return F_NOTIFY; + case TARGET_F_NOTIFY: + ret = F_NOTIFY; + break; #ifdef F_GETOWN_EX - case TARGET_F_GETOWN_EX: - return F_GETOWN_EX; + case TARGET_F_GETOWN_EX: + ret = F_GETOWN_EX; + break; #endif #ifdef F_SETOWN_EX - case TARGET_F_SETOWN_EX: - return F_SETOWN_EX; + case TARGET_F_SETOWN_EX: + ret = F_SETOWN_EX; + break; #endif #ifdef F_SETPIPE_SZ - case TARGET_F_SETPIPE_SZ: - return F_SETPIPE_SZ; - case TARGET_F_GETPIPE_SZ: - return F_GETPIPE_SZ; + case TARGET_F_SETPIPE_SZ: + ret = F_SETPIPE_SZ; + break; + case TARGET_F_GETPIPE_SZ: + ret = F_GETPIPE_SZ; + break; #endif - default: - return -TARGET_EINVAL; + default: + ret = -TARGET_EINVAL; + break; } + +#if defined(__powerpc64__) + /* On PPC64, glibc headers has the F_*LK* defined to 12, 13 and 14 and + * is not supported by kernel. The glibc fcntl call actually adjusts + * them to 5, 6 and 7 before making the syscall(). Since we make the + * syscall directly, adjust to what is supported by the kernel. + */ + if (ret >= F_GETLK64 && ret <= F_SETLKW64) { + ret -= F_GETLK64 - 5; + } +#endif + + return ret; +} + +#define FLOCK_TRANSTBL \ + switch (type) { \ + TRANSTBL_CONVERT(F_RDLCK); \ + TRANSTBL_CONVERT(F_WRLCK); \ + TRANSTBL_CONVERT(F_UNLCK); \ + TRANSTBL_CONVERT(F_EXLCK); \ + TRANSTBL_CONVERT(F_SHLCK); \ + } + +static int target_to_host_flock(int type) +{ +#define TRANSTBL_CONVERT(a) case TARGET_##a: return a + FLOCK_TRANSTBL +#undef TRANSTBL_CONVERT return -TARGET_EINVAL; } -#define TRANSTBL_CONVERT(a) { -1, TARGET_##a, -1, a } -static const bitmask_transtbl flock_tbl[] = { - TRANSTBL_CONVERT(F_RDLCK), - TRANSTBL_CONVERT(F_WRLCK), - TRANSTBL_CONVERT(F_UNLCK), - TRANSTBL_CONVERT(F_EXLCK), - TRANSTBL_CONVERT(F_SHLCK), - { 0, 0, 0, 0 } -}; +static int host_to_target_flock(int type) +{ +#define TRANSTBL_CONVERT(a) case a: return TARGET_##a + FLOCK_TRANSTBL +#undef TRANSTBL_CONVERT + /* if we don't know how to convert the value coming + * from the host we copy to the target field as-is + */ + return type; +} static inline abi_long copy_from_user_flock(struct flock64 *fl, abi_ulong target_flock_addr) { struct target_flock *target_fl; - short l_type; + int l_type; if (!lock_user_struct(VERIFY_READ, target_fl, target_flock_addr, 1)) { return -TARGET_EFAULT; } __get_user(l_type, &target_fl->l_type); - fl->l_type = target_to_host_bitmask(l_type, flock_tbl); + l_type = target_to_host_flock(l_type); + if (l_type < 0) { + return l_type; + } + fl->l_type = l_type; __get_user(fl->l_whence, &target_fl->l_whence); __get_user(fl->l_start, &target_fl->l_start); __get_user(fl->l_len, &target_fl->l_len); @@ -6574,7 +6702,7 @@ static inline abi_long copy_to_user_flock(abi_ulong target_flock_addr, return -TARGET_EFAULT; } - l_type = host_to_target_bitmask(fl->l_type, flock_tbl); + l_type = host_to_target_flock(fl->l_type); __put_user(l_type, &target_fl->l_type); __put_user(fl->l_whence, &target_fl->l_whence); __put_user(fl->l_start, &target_fl->l_start); @@ -6588,18 +6716,22 @@ typedef abi_long from_flock64_fn(struct flock64 *fl, abi_ulong target_addr); typedef abi_long to_flock64_fn(abi_ulong target_addr, const struct flock64 *fl); #if defined(TARGET_ARM) && TARGET_ABI_BITS == 32 -static inline abi_long copy_from_user_eabi_flock64(struct flock64 *fl, +static inline abi_long copy_from_user_oabi_flock64(struct flock64 *fl, abi_ulong target_flock_addr) { - struct target_eabi_flock64 *target_fl; - short l_type; + struct target_oabi_flock64 *target_fl; + int l_type; if (!lock_user_struct(VERIFY_READ, target_fl, target_flock_addr, 1)) { return -TARGET_EFAULT; } __get_user(l_type, &target_fl->l_type); - fl->l_type = target_to_host_bitmask(l_type, flock_tbl); + l_type = target_to_host_flock(l_type); + if (l_type < 0) { + return l_type; + } + fl->l_type = l_type; __get_user(fl->l_whence, &target_fl->l_whence); __get_user(fl->l_start, &target_fl->l_start); __get_user(fl->l_len, &target_fl->l_len); @@ -6608,17 +6740,17 @@ static inline abi_long copy_from_user_eabi_flock64(struct flock64 *fl, return 0; } -static inline abi_long copy_to_user_eabi_flock64(abi_ulong target_flock_addr, +static inline abi_long copy_to_user_oabi_flock64(abi_ulong target_flock_addr, const struct flock64 *fl) { - struct target_eabi_flock64 *target_fl; + struct target_oabi_flock64 *target_fl; short l_type; if (!lock_user_struct(VERIFY_WRITE, target_fl, target_flock_addr, 0)) { return -TARGET_EFAULT; } - l_type = host_to_target_bitmask(fl->l_type, flock_tbl); + l_type = host_to_target_flock(fl->l_type); __put_user(l_type, &target_fl->l_type); __put_user(fl->l_whence, &target_fl->l_whence); __put_user(fl->l_start, &target_fl->l_start); @@ -6633,14 +6765,18 @@ static inline abi_long copy_from_user_flock64(struct flock64 *fl, abi_ulong target_flock_addr) { struct target_flock64 *target_fl; - short l_type; + int l_type; if (!lock_user_struct(VERIFY_READ, target_fl, target_flock_addr, 1)) { return -TARGET_EFAULT; } __get_user(l_type, &target_fl->l_type); - fl->l_type = target_to_host_bitmask(l_type, flock_tbl); + l_type = target_to_host_flock(l_type); + if (l_type < 0) { + return l_type; + } + fl->l_type = l_type; __get_user(fl->l_whence, &target_fl->l_whence); __get_user(fl->l_start, &target_fl->l_start); __get_user(fl->l_len, &target_fl->l_len); @@ -6659,7 +6795,7 @@ static inline abi_long copy_to_user_flock64(abi_ulong target_flock_addr, return -TARGET_EFAULT; } - l_type = host_to_target_bitmask(fl->l_type, flock_tbl); + l_type = host_to_target_flock(fl->l_type); __put_user(l_type, &target_fl->l_type); __put_user(fl->l_whence, &target_fl->l_whence); __put_user(fl->l_start, &target_fl->l_start); @@ -7922,10 +8058,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } cpu_list_unlock(); -#ifdef TARGET_GPROF - _mcleanup(); -#endif - gdb_exit(cpu_env, arg1); + preexit_cleanup(cpu_env, arg1); _exit(arg1); ret = 0; /* avoid warning */ break; @@ -10031,10 +10164,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef __NR_exit_group /* new thread calls */ case TARGET_NR_exit_group: -#ifdef TARGET_GPROF - _mcleanup(); -#endif - gdb_exit(cpu_env, arg1); + preexit_cleanup(cpu_env, arg1); ret = get_errno(exit_group(arg1)); break; #endif @@ -10055,7 +10185,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret)) { /* Overwrite the native machine name with whatever is being emulated. */ - strcpy (buf->machine, cpu_to_uname_machine(cpu_env)); + g_strlcpy(buf->machine, cpu_to_uname_machine(cpu_env), + sizeof(buf->machine)); /* Allow the user to override the reported release. */ if (qemu_uname_release && *qemu_uname_release) { g_strlcpy(buf->release, qemu_uname_release, @@ -10163,7 +10294,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_getdents case TARGET_NR_getdents: -#ifdef __NR_getdents +#ifdef EMULATE_GETDENTS_WITH_GETDENTS #if TARGET_ABI_BITS == 32 && HOST_LONG_BITS == 64 { struct target_dirent *target_dirp; @@ -11617,9 +11748,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, to_flock64_fn *copyto = copy_to_user_flock64; #ifdef TARGET_ARM - if (((CPUARMState *)cpu_env)->eabi) { - copyfrom = copy_from_user_eabi_flock64; - copyto = copy_to_user_eabi_flock64; + if (!((CPUARMState *)cpu_env)->eabi) { + copyfrom = copy_from_user_oabi_flock64; + copyto = copy_to_user_oabi_flock64; } #endif @@ -11635,7 +11766,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (ret) { break; } - ret = get_errno(fcntl(arg1, cmd, &fl)); + ret = get_errno(safe_fcntl(arg1, cmd, &fl)); if (ret == 0) { ret = copyto(arg3, &fl); } @@ -12659,10 +12790,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(kcmp(arg1, arg2, arg3, arg4, arg5)); break; #endif +#ifdef TARGET_NR_swapcontext + case TARGET_NR_swapcontext: + /* PowerPC specific. */ + ret = do_swapcontext(cpu_env, arg1, arg2, arg3); + break; +#endif default: unimplemented: - gemu_log("qemu: Unsupported syscall: %d\n", num); + qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); #if defined(TARGET_NR_setxattr) || defined(TARGET_NR_get_thread_area) || defined(TARGET_NR_getdomainname) || defined(TARGET_NR_set_robust_list) unimplemented_nowarn: #endif diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 23f5bccf0e..40bb60ef4c 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -418,279 +418,12 @@ struct target_sigaction; int do_sigaction(int sig, const struct target_sigaction *act, struct target_sigaction *oact); -#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \ - || defined(TARGET_PPC) || defined(TARGET_MIPS) || defined(TARGET_SH4) \ - || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \ - || defined(TARGET_MICROBLAZE) \ - || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ - || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \ - || defined(TARGET_RISCV) || defined(TARGET_XTENSA) - -#if defined(TARGET_SPARC) -#define TARGET_SA_NOCLDSTOP 8u -#define TARGET_SA_NOCLDWAIT 0x100u -#define TARGET_SA_SIGINFO 0x200u -#define TARGET_SA_ONSTACK 1u -#define TARGET_SA_RESTART 2u -#define TARGET_SA_NODEFER 0x20u -#define TARGET_SA_RESETHAND 4u -#define TARGET_ARCH_HAS_SA_RESTORER 1 -#define TARGET_ARCH_HAS_KA_RESTORER 1 -#elif defined(TARGET_MIPS) -#define TARGET_SA_NOCLDSTOP 0x00000001 -#define TARGET_SA_NOCLDWAIT 0x00010000 -#define TARGET_SA_SIGINFO 0x00000008 -#define TARGET_SA_ONSTACK 0x08000000 -#define TARGET_SA_NODEFER 0x40000000 -#define TARGET_SA_RESTART 0x10000000 -#define TARGET_SA_RESETHAND 0x80000000 -#if !defined(TARGET_ABI_MIPSN32) && !defined(TARGET_ABI_MIPSN64) -#define TARGET_SA_RESTORER 0x04000000 /* Only for O32 */ -#endif -#elif defined(TARGET_OPENRISC) -#define TARGET_SA_NOCLDSTOP 0x00000001 -#define TARGET_SA_NOCLDWAIT 0x00000002 -#define TARGET_SA_SIGINFO 0x00000004 -#define TARGET_SA_ONSTACK 0x08000000 -#define TARGET_SA_RESTART 0x10000000 -#define TARGET_SA_NODEFER 0x40000000 -#define TARGET_SA_RESETHAND 0x80000000 -#elif defined(TARGET_ALPHA) -#define TARGET_SA_ONSTACK 0x00000001 -#define TARGET_SA_RESTART 0x00000002 -#define TARGET_SA_NOCLDSTOP 0x00000004 -#define TARGET_SA_NODEFER 0x00000008 -#define TARGET_SA_RESETHAND 0x00000010 -#define TARGET_SA_NOCLDWAIT 0x00000020 /* not supported yet */ -#define TARGET_SA_SIGINFO 0x00000040 -#elif defined(TARGET_HPPA) -#define TARGET_SA_ONSTACK 0x00000001 -#define TARGET_SA_RESETHAND 0x00000004 -#define TARGET_SA_NOCLDSTOP 0x00000008 -#define TARGET_SA_SIGINFO 0x00000010 -#define TARGET_SA_NODEFER 0x00000020 -#define TARGET_SA_RESTART 0x00000040 -#define TARGET_SA_NOCLDWAIT 0x00000080 -#else -#define TARGET_SA_NOCLDSTOP 0x00000001 -#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */ -#define TARGET_SA_SIGINFO 0x00000004 -#define TARGET_SA_ONSTACK 0x08000000 -#define TARGET_SA_RESTART 0x10000000 -#define TARGET_SA_NODEFER 0x40000000 -#define TARGET_SA_RESETHAND 0x80000000 -#define TARGET_SA_RESTORER 0x04000000 -#endif +#include "target_signal.h" #ifdef TARGET_SA_RESTORER #define TARGET_ARCH_HAS_SA_RESTORER 1 #endif -#if defined(TARGET_ALPHA) - -#define TARGET_SIGHUP 1 -#define TARGET_SIGINT 2 -#define TARGET_SIGQUIT 3 -#define TARGET_SIGILL 4 -#define TARGET_SIGTRAP 5 -#define TARGET_SIGABRT 6 -#define TARGET_SIGSTKFLT 7 /* actually SIGEMT */ -#define TARGET_SIGFPE 8 -#define TARGET_SIGKILL 9 -#define TARGET_SIGBUS 10 -#define TARGET_SIGSEGV 11 -#define TARGET_SIGSYS 12 -#define TARGET_SIGPIPE 13 -#define TARGET_SIGALRM 14 -#define TARGET_SIGTERM 15 -#define TARGET_SIGURG 16 -#define TARGET_SIGSTOP 17 -#define TARGET_SIGTSTP 18 -#define TARGET_SIGCONT 19 -#define TARGET_SIGCHLD 20 -#define TARGET_SIGTTIN 21 -#define TARGET_SIGTTOU 22 -#define TARGET_SIGIO 23 -#define TARGET_SIGXCPU 24 -#define TARGET_SIGXFSZ 25 -#define TARGET_SIGVTALRM 26 -#define TARGET_SIGPROF 27 -#define TARGET_SIGWINCH 28 -#define TARGET_SIGPWR 29 /* actually SIGINFO */ -#define TARGET_SIGUSR1 30 -#define TARGET_SIGUSR2 31 -#define TARGET_SIGRTMIN 32 - -#define TARGET_SIG_BLOCK 1 -#define TARGET_SIG_UNBLOCK 2 -#define TARGET_SIG_SETMASK 3 - -#elif defined(TARGET_SPARC) - -#define TARGET_SIGHUP 1 -#define TARGET_SIGINT 2 -#define TARGET_SIGQUIT 3 -#define TARGET_SIGILL 4 -#define TARGET_SIGTRAP 5 -#define TARGET_SIGABRT 6 -#define TARGET_SIGIOT 6 -#define TARGET_SIGSTKFLT 7 /* actually EMT */ -#define TARGET_SIGFPE 8 -#define TARGET_SIGKILL 9 -#define TARGET_SIGBUS 10 -#define TARGET_SIGSEGV 11 -#define TARGET_SIGSYS 12 -#define TARGET_SIGPIPE 13 -#define TARGET_SIGALRM 14 -#define TARGET_SIGTERM 15 -#define TARGET_SIGURG 16 -#define TARGET_SIGSTOP 17 -#define TARGET_SIGTSTP 18 -#define TARGET_SIGCONT 19 -#define TARGET_SIGCHLD 20 -#define TARGET_SIGTTIN 21 -#define TARGET_SIGTTOU 22 -#define TARGET_SIGIO 23 -#define TARGET_SIGXCPU 24 -#define TARGET_SIGXFSZ 25 -#define TARGET_SIGVTALRM 26 -#define TARGET_SIGPROF 27 -#define TARGET_SIGWINCH 28 -#define TARGET_SIGPWR 29 -#define TARGET_SIGUSR1 30 -#define TARGET_SIGUSR2 31 -#define TARGET_SIGRTMIN 32 - -#define TARGET_SIG_BLOCK 0x01 /* for blocking signals */ -#define TARGET_SIG_UNBLOCK 0x02 /* for unblocking signals */ -#define TARGET_SIG_SETMASK 0x04 /* for setting the signal mask */ - -#elif defined(TARGET_MIPS) - -#define TARGET_SIGHUP 1 /* Hangup (POSIX). */ -#define TARGET_SIGINT 2 /* Interrupt (ANSI). */ -#define TARGET_SIGQUIT 3 /* Quit (POSIX). */ -#define TARGET_SIGILL 4 /* Illegal instruction (ANSI). */ -#define TARGET_SIGTRAP 5 /* Trace trap (POSIX). */ -#define TARGET_SIGIOT 6 /* IOT trap (4.2 BSD). */ -#define TARGET_SIGABRT TARGET_SIGIOT /* Abort (ANSI). */ -#define TARGET_SIGEMT 7 -#define TARGET_SIGSTKFLT 7 /* XXX: incorrect */ -#define TARGET_SIGFPE 8 /* Floating-point exception (ANSI). */ -#define TARGET_SIGKILL 9 /* Kill, unblockable (POSIX). */ -#define TARGET_SIGBUS 10 /* BUS error (4.2 BSD). */ -#define TARGET_SIGSEGV 11 /* Segmentation violation (ANSI). */ -#define TARGET_SIGSYS 12 -#define TARGET_SIGPIPE 13 /* Broken pipe (POSIX). */ -#define TARGET_SIGALRM 14 /* Alarm clock (POSIX). */ -#define TARGET_SIGTERM 15 /* Termination (ANSI). */ -#define TARGET_SIGUSR1 16 /* User-defined signal 1 (POSIX). */ -#define TARGET_SIGUSR2 17 /* User-defined signal 2 (POSIX). */ -#define TARGET_SIGCHLD 18 /* Child status has changed (POSIX). */ -#define TARGET_SIGCLD TARGET_SIGCHLD /* Same as TARGET_SIGCHLD (System V). */ -#define TARGET_SIGPWR 19 /* Power failure restart (System V). */ -#define TARGET_SIGWINCH 20 /* Window size change (4.3 BSD, Sun). */ -#define TARGET_SIGURG 21 /* Urgent condition on socket (4.2 BSD). */ -#define TARGET_SIGIO 22 /* I/O now possible (4.2 BSD). */ -#define TARGET_SIGPOLL TARGET_SIGIO /* Pollable event occurred (System V). */ -#define TARGET_SIGSTOP 23 /* Stop, unblockable (POSIX). */ -#define TARGET_SIGTSTP 24 /* Keyboard stop (POSIX). */ -#define TARGET_SIGCONT 25 /* Continue (POSIX). */ -#define TARGET_SIGTTIN 26 /* Background read from tty (POSIX). */ -#define TARGET_SIGTTOU 27 /* Background write to tty (POSIX). */ -#define TARGET_SIGVTALRM 28 /* Virtual alarm clock (4.2 BSD). */ -#define TARGET_SIGPROF 29 /* Profiling alarm clock (4.2 BSD). */ -#define TARGET_SIGXCPU 30 /* CPU limit exceeded (4.2 BSD). */ -#define TARGET_SIGXFSZ 31 /* File size limit exceeded (4.2 BSD). */ -#define TARGET_SIGRTMIN 32 - -#define TARGET_SIG_BLOCK 1 /* for blocking signals */ -#define TARGET_SIG_UNBLOCK 2 /* for unblocking signals */ -#define TARGET_SIG_SETMASK 3 /* for setting the signal mask */ - -#elif defined(TARGET_HPPA) - -#define TARGET_SIGHUP 1 -#define TARGET_SIGINT 2 -#define TARGET_SIGQUIT 3 -#define TARGET_SIGILL 4 -#define TARGET_SIGTRAP 5 -#define TARGET_SIGABRT 6 -#define TARGET_SIGIOT 6 -#define TARGET_SIGSTKFLT 7 -#define TARGET_SIGFPE 8 -#define TARGET_SIGKILL 9 -#define TARGET_SIGBUS 10 -#define TARGET_SIGSEGV 11 -#define TARGET_SIGXCPU 12 -#define TARGET_SIGPIPE 13 -#define TARGET_SIGALRM 14 -#define TARGET_SIGTERM 15 -#define TARGET_SIGUSR1 16 -#define TARGET_SIGUSR2 17 -#define TARGET_SIGCHLD 18 -#define TARGET_SIGPWR 19 -#define TARGET_SIGVTALRM 20 -#define TARGET_SIGPROF 21 -#define TARGET_SIGIO 22 -#define TARGET_SIGPOLL TARGET_SIGIO -#define TARGET_SIGWINCH 23 -#define TARGET_SIGSTOP 24 -#define TARGET_SIGTSTP 25 -#define TARGET_SIGCONT 26 -#define TARGET_SIGTTIN 27 -#define TARGET_SIGTTOU 28 -#define TARGET_SIGURG 29 -#define TARGET_SIGXFSZ 30 -#define TARGET_SIGSYS 31 - -#define TARGET_SIG_BLOCK 0 -#define TARGET_SIG_UNBLOCK 1 -#define TARGET_SIG_SETMASK 2 - -#else - -/* OpenRISC Using the general signals */ -#define TARGET_SIGHUP 1 -#define TARGET_SIGINT 2 -#define TARGET_SIGQUIT 3 -#define TARGET_SIGILL 4 -#define TARGET_SIGTRAP 5 -#define TARGET_SIGABRT 6 -#define TARGET_SIGIOT 6 -#define TARGET_SIGBUS 7 -#define TARGET_SIGFPE 8 -#define TARGET_SIGKILL 9 -#define TARGET_SIGUSR1 10 -#define TARGET_SIGSEGV 11 -#define TARGET_SIGUSR2 12 -#define TARGET_SIGPIPE 13 -#define TARGET_SIGALRM 14 -#define TARGET_SIGTERM 15 -#define TARGET_SIGSTKFLT 16 -#define TARGET_SIGCHLD 17 -#define TARGET_SIGCONT 18 -#define TARGET_SIGSTOP 19 -#define TARGET_SIGTSTP 20 -#define TARGET_SIGTTIN 21 -#define TARGET_SIGTTOU 22 -#define TARGET_SIGURG 23 -#define TARGET_SIGXCPU 24 -#define TARGET_SIGXFSZ 25 -#define TARGET_SIGVTALRM 26 -#define TARGET_SIGPROF 27 -#define TARGET_SIGWINCH 28 -#define TARGET_SIGIO 29 -#define TARGET_SIGPWR 30 -#define TARGET_SIGSYS 31 -#define TARGET_SIGRTMIN 32 - -#define TARGET_SIG_BLOCK 0 /* for blocking signals */ -#define TARGET_SIG_UNBLOCK 1 /* for unblocking signals */ -#define TARGET_SIG_SETMASK 2 /* for setting the signal mask */ - -#endif - #if defined(TARGET_ALPHA) struct target_old_sigaction { abi_ulong _sa_handler; @@ -925,8 +658,6 @@ typedef struct target_siginfo { #define TARGET_TRAP_BRANCH (3) /* process taken branch trap */ #define TARGET_TRAP_HWBKPT (4) /* hardware breakpoint/watchpoint */ -#endif /* defined(TARGET_I386) || defined(TARGET_ARM) */ - struct target_rlimit { abi_ulong rlim_cur; abi_ulong rlim_max; @@ -2407,84 +2138,6 @@ struct target_statfs64 { }; #endif - -#define TARGET_F_DUPFD 0 /* dup */ -#define TARGET_F_GETFD 1 /* get close_on_exec */ -#define TARGET_F_SETFD 2 /* set/clear close_on_exec */ -#define TARGET_F_GETFL 3 /* get file->f_flags */ -#define TARGET_F_SETFL 4 /* set file->f_flags */ - -#if defined(TARGET_ALPHA) -#define TARGET_F_GETLK 7 -#define TARGET_F_SETLK 8 -#define TARGET_F_SETLKW 9 -#define TARGET_F_SETOWN 5 /* for sockets. */ -#define TARGET_F_GETOWN 6 /* for sockets. */ - -#define TARGET_F_RDLCK 1 -#define TARGET_F_WRLCK 2 -#define TARGET_F_UNLCK 8 -#define TARGET_F_EXLCK 16 -#define TARGET_F_SHLCK 32 -#elif defined(TARGET_MIPS) -#define TARGET_F_GETLK 14 -#define TARGET_F_SETLK 6 -#define TARGET_F_SETLKW 7 -#define TARGET_F_SETOWN 24 /* for sockets. */ -#define TARGET_F_GETOWN 23 /* for sockets. */ -#elif defined(TARGET_HPPA) -#define TARGET_F_RDLCK 1 -#define TARGET_F_WRLCK 2 -#define TARGET_F_UNLCK 3 -#define TARGET_F_GETLK 5 -#define TARGET_F_SETLK 6 -#define TARGET_F_SETLKW 7 -#define TARGET_F_GETOWN 11 /* for sockets. */ -#define TARGET_F_SETOWN 12 /* for sockets. */ -#else -#define TARGET_F_GETLK 5 -#define TARGET_F_SETLK 6 -#define TARGET_F_SETLKW 7 -#define TARGET_F_SETOWN 8 /* for sockets. */ -#define TARGET_F_GETOWN 9 /* for sockets. */ -#endif -#define TARGET_F_SETOWN_EX 15 -#define TARGET_F_GETOWN_EX 16 - -#ifndef TARGET_F_RDLCK -#define TARGET_F_RDLCK 0 -#define TARGET_F_WRLCK 1 -#define TARGET_F_UNLCK 2 -#endif - -#ifndef TARGET_F_EXLCK -#define TARGET_F_EXLCK 4 -#define TARGET_F_SHLCK 8 -#endif - - -#if defined(TARGET_HPPA) -#define TARGET_F_SETSIG 13 /* for sockets. */ -#define TARGET_F_GETSIG 14 /* for sockets. */ -#else -#define TARGET_F_SETSIG 10 /* for sockets. */ -#define TARGET_F_GETSIG 11 /* for sockets. */ -#endif - -#if defined(TARGET_MIPS) -#define TARGET_F_GETLK64 33 /* using 'struct flock64' */ -#define TARGET_F_SETLK64 34 -#define TARGET_F_SETLKW64 35 -#elif defined(TARGET_HPPA) -#define TARGET_F_GETLK64 8 /* using 'struct flock64' */ -#define TARGET_F_SETLK64 9 -#define TARGET_F_SETLKW64 10 -#else -#define TARGET_F_GETLK64 12 /* using 'struct flock64' */ -#define TARGET_F_SETLK64 13 -#define TARGET_F_SETLKW64 14 -#endif - #define TARGET_F_LINUX_SPECIFIC_BASE 1024 #define TARGET_F_SETLEASE (TARGET_F_LINUX_SPECIFIC_BASE + 0) #define TARGET_F_GETLEASE (TARGET_F_LINUX_SPECIFIC_BASE + 1) @@ -2493,190 +2146,7 @@ struct target_statfs64 { #define TARGET_F_GETPIPE_SZ (TARGET_F_LINUX_SPECIFIC_BASE + 8) #define TARGET_F_NOTIFY (TARGET_F_LINUX_SPECIFIC_BASE+2) -#if defined(TARGET_ALPHA) -#define TARGET_O_NONBLOCK 04 -#define TARGET_O_APPEND 010 -#define TARGET_O_CREAT 01000 /* not fcntl */ -#define TARGET_O_TRUNC 02000 /* not fcntl */ -#define TARGET_O_EXCL 04000 /* not fcntl */ -#define TARGET_O_NOCTTY 010000 /* not fcntl */ -#define TARGET_O_DSYNC 040000 -#define TARGET_O_LARGEFILE 0 /* not necessary, always 64-bit */ -#define TARGET_O_DIRECTORY 0100000 /* must be a directory */ -#define TARGET_O_NOFOLLOW 0200000 /* don't follow links */ -#define TARGET_O_DIRECT 02000000 /* direct disk access hint */ -#define TARGET_O_NOATIME 04000000 -#define TARGET_O_CLOEXEC 010000000 -#define TARGET___O_SYNC 020000000 -#define TARGET_O_PATH 040000000 -#elif defined(TARGET_HPPA) -#define TARGET_O_NONBLOCK 000200004 /* HPUX has separate NDELAY & NONBLOCK */ -#define TARGET_O_APPEND 000000010 -#define TARGET_O_CREAT 000000400 /* not fcntl */ -#define TARGET_O_EXCL 000002000 /* not fcntl */ -#define TARGET_O_NOCTTY 000400000 /* not fcntl */ -#define TARGET_O_DSYNC 001000000 -#define TARGET_O_LARGEFILE 000004000 -#define TARGET_O_DIRECTORY 000010000 /* must be a directory */ -#define TARGET_O_NOFOLLOW 000000200 /* don't follow links */ -#define TARGET_O_NOATIME 004000000 -#define TARGET_O_CLOEXEC 010000000 -#define TARGET___O_SYNC 000100000 -#define TARGET_O_PATH 020000000 -#elif defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_AARCH64) -#define TARGET_O_DIRECTORY 040000 /* must be a directory */ -#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ -#define TARGET_O_DIRECT 0200000 /* direct disk access hint */ -#define TARGET_O_LARGEFILE 0400000 -#elif defined(TARGET_MIPS) -#define TARGET_O_APPEND 0x0008 -#define TARGET_O_DSYNC 0x0010 -#define TARGET_O_NONBLOCK 0x0080 -#define TARGET_O_CREAT 0x0100 /* not fcntl */ -#define TARGET_O_TRUNC 0x0200 /* not fcntl */ -#define TARGET_O_EXCL 0x0400 /* not fcntl */ -#define TARGET_O_NOCTTY 0x0800 /* not fcntl */ -#define TARGET_FASYNC 0x1000 /* fcntl, for BSD compatibility */ -#define TARGET_O_LARGEFILE 0x2000 /* allow large file opens */ -#define TARGET___O_SYNC 0x4000 -#define TARGET_O_DIRECT 0x8000 /* direct disk access hint */ -#elif defined (TARGET_PPC) -#define TARGET_O_DIRECTORY 040000 /* must be a directory */ -#define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ -#define TARGET_O_LARGEFILE 0200000 -#define TARGET_O_DIRECT 0400000 /* direct disk access hint */ -#elif defined (TARGET_SPARC) -#define TARGET_O_APPEND 0x0008 -#define TARGET_FASYNC 0x0040 /* fcntl, for BSD compatibility */ -#define TARGET_O_CREAT 0x0200 /* not fcntl */ -#define TARGET_O_TRUNC 0x0400 /* not fcntl */ -#define TARGET_O_EXCL 0x0800 /* not fcntl */ -#define TARGET_O_DSYNC 0x2000 -#define TARGET_O_NONBLOCK 0x4000 -# ifdef TARGET_SPARC64 -# define TARGET_O_NDELAY 0x0004 -# else -# define TARGET_O_NDELAY (0x0004 | TARGET_O_NONBLOCK) -# endif -#define TARGET_O_NOCTTY 0x8000 /* not fcntl */ -#define TARGET_O_LARGEFILE 0x40000 -#define TARGET_O_DIRECT 0x100000 /* direct disk access hint */ -#define TARGET_O_NOATIME 0x200000 -#define TARGET_O_CLOEXEC 0x400000 -#define TARGET___O_SYNC 0x800000 -#define TARGET_O_PATH 0x1000000 -#define TARGET___O_TMPFILE 0x2000000 -#endif - -/* values follow. */ -#define TARGET_O_ACCMODE 0003 -#define TARGET_O_RDONLY 00 -#define TARGET_O_WRONLY 01 -#define TARGET_O_RDWR 02 -#ifndef TARGET_O_CREAT -#define TARGET_O_CREAT 0100 /* not fcntl */ -#endif -#ifndef TARGET_O_EXCL -#define TARGET_O_EXCL 0200 /* not fcntl */ -#endif -#ifndef TARGET_O_NOCTTY -#define TARGET_O_NOCTTY 0400 /* not fcntl */ -#endif -#ifndef TARGET_O_TRUNC -#define TARGET_O_TRUNC 01000 /* not fcntl */ -#endif -#ifndef TARGET_O_APPEND -#define TARGET_O_APPEND 02000 -#endif -#ifndef TARGET_O_NONBLOCK -#define TARGET_O_NONBLOCK 04000 -#endif -#ifndef TARGET_O_DSYNC -#define TARGET_O_DSYNC 010000 -#endif -#ifndef TARGET_FASYNC -#define TARGET_FASYNC 020000 /* fcntl, for BSD compatibility */ -#endif -#ifndef TARGET_O_DIRECT -#define TARGET_O_DIRECT 040000 /* direct disk access hint */ -#endif -#ifndef TARGET_O_LARGEFILE -#define TARGET_O_LARGEFILE 0100000 -#endif -#ifndef TARGET_O_DIRECTORY -#define TARGET_O_DIRECTORY 0200000 /* must be a directory */ -#endif -#ifndef TARGET_O_NOFOLLOW -#define TARGET_O_NOFOLLOW 0400000 /* don't follow links */ -#endif -#ifndef TARGET_O_NOATIME -#define TARGET_O_NOATIME 01000000 -#endif -#ifndef TARGET_O_CLOEXEC -#define TARGET_O_CLOEXEC 02000000 -#endif -#ifndef TARGET___O_SYNC -#define TARGET___O_SYNC 04000000 -#endif -#ifndef TARGET_O_PATH -#define TARGET_O_PATH 010000000 -#endif -#ifndef TARGET___O_TMPFILE -#define TARGET___O_TMPFILE 020000000 -#endif -#ifndef TARGET_O_TMPFILE -#define TARGET_O_TMPFILE (TARGET___O_TMPFILE | TARGET_O_DIRECTORY) -#endif -#ifndef TARGET_O_NDELAY -#define TARGET_O_NDELAY TARGET_O_NONBLOCK -#endif -#ifndef TARGET_O_SYNC -#define TARGET_O_SYNC (TARGET___O_SYNC | TARGET_O_DSYNC) -#endif - -struct target_flock { - short l_type; - short l_whence; - abi_long l_start; - abi_long l_len; -#if defined(TARGET_MIPS) - abi_long l_sysid; -#endif - int l_pid; -#if defined(TARGET_MIPS) - abi_long pad[4]; -#endif -}; - -struct target_flock64 { - short l_type; - short l_whence; -#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) \ - || defined(TARGET_SPARC) || defined(TARGET_HPPA) \ - || defined(TARGET_MICROBLAZE) || defined(TARGET_TILEGX) \ - || defined(TARGET_XTENSA) - int __pad; -#endif - abi_llong l_start; - abi_llong l_len; - int l_pid; -} QEMU_PACKED; - -#ifdef TARGET_ARM -struct target_eabi_flock64 { - short l_type; - short l_whence; - int __pad; - abi_llong l_start; - abi_llong l_len; - int l_pid; -} QEMU_PACKED; -#endif - -struct target_f_owner_ex { - int type; /* Owner type of ID. */ - int pid; /* ID of owner. */ -}; +#include "target_fcntl.h" /* soundcard defines */ /* XXX: convert them all to arch independent entries */ diff --git a/linux-user/tilegx/cpu_loop.c b/linux-user/tilegx/cpu_loop.c new file mode 100644 index 0000000000..4f39eb9ad3 --- /dev/null +++ b/linux-user/tilegx/cpu_loop.c @@ -0,0 +1,286 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +static void gen_sigill_reg(CPUTLGState *env) +{ + target_siginfo_t info; + + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_PRVREG; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); +} + +static void do_signal(CPUTLGState *env, int signo, int sigcode) +{ + target_siginfo_t info; + + info.si_signo = signo; + info.si_errno = 0; + info._sifields._sigfault._addr = env->pc; + + if (signo == TARGET_SIGSEGV) { + /* The passed in sigcode is a dummy; check for a page mapping + and pass either MAPERR or ACCERR. */ + target_ulong addr = env->excaddr; + info._sifields._sigfault._addr = addr; + if (page_check_range(addr, 1, PAGE_VALID) < 0) { + sigcode = TARGET_SEGV_MAPERR; + } else { + sigcode = TARGET_SEGV_ACCERR; + } + } + info.si_code = sigcode; + + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); +} + +static void gen_sigsegv_maperr(CPUTLGState *env, target_ulong addr) +{ + env->excaddr = addr; + do_signal(env, TARGET_SIGSEGV, 0); +} + +static void set_regval(CPUTLGState *env, uint8_t reg, uint64_t val) +{ + if (unlikely(reg >= TILEGX_R_COUNT)) { + switch (reg) { + case TILEGX_R_SN: + case TILEGX_R_ZERO: + return; + case TILEGX_R_IDN0: + case TILEGX_R_IDN1: + case TILEGX_R_UDN0: + case TILEGX_R_UDN1: + case TILEGX_R_UDN2: + case TILEGX_R_UDN3: + gen_sigill_reg(env); + return; + default: + g_assert_not_reached(); + } + } + env->regs[reg] = val; +} + +/* + * Compare the 8-byte contents of the CmpValue SPR with the 8-byte value in + * memory at the address held in the first source register. If the values are + * not equal, then no memory operation is performed. If the values are equal, + * the 8-byte quantity from the second source register is written into memory + * at the address held in the first source register. In either case, the result + * of the instruction is the value read from memory. The compare and write to + * memory are atomic and thus can be used for synchronization purposes. This + * instruction only operates for addresses aligned to a 8-byte boundary. + * Unaligned memory access causes an Unaligned Data Reference interrupt. + * + * Functional Description (64-bit) + * uint64_t memVal = memoryReadDoubleWord (rf[SrcA]); + * rf[Dest] = memVal; + * if (memVal == SPR[CmpValueSPR]) + * memoryWriteDoubleWord (rf[SrcA], rf[SrcB]); + * + * Functional Description (32-bit) + * uint64_t memVal = signExtend32 (memoryReadWord (rf[SrcA])); + * rf[Dest] = memVal; + * if (memVal == signExtend32 (SPR[CmpValueSPR])) + * memoryWriteWord (rf[SrcA], rf[SrcB]); + * + * + * This function also processes exch and exch4 which need not process SPR. + */ +static void do_exch(CPUTLGState *env, bool quad, bool cmp) +{ + target_ulong addr; + target_long val, sprval; + + start_exclusive(); + + addr = env->atomic_srca; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + if (cmp) { + if (quad) { + sprval = env->spregs[TILEGX_SPR_CMPEXCH]; + } else { + sprval = sextract64(env->spregs[TILEGX_SPR_CMPEXCH], 0, 32); + } + } + + if (!cmp || val == sprval) { + target_long valb = env->atomic_srcb; + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + +static void do_fetch(CPUTLGState *env, int trapnr, bool quad) +{ + int8_t write = 1; + target_ulong addr; + target_long val, valb; + + start_exclusive(); + + addr = env->atomic_srca; + valb = env->atomic_srcb; + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto sigsegv_maperr; + } + + switch (trapnr) { + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADD4: + valb += val; + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + valb += val; + if (valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + valb += val; + if ((int32_t)valb < 0) { + write = 0; + } + break; + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHAND4: + valb &= val; + break; + case TILEGX_EXCP_OPCODE_FETCHOR: + case TILEGX_EXCP_OPCODE_FETCHOR4: + valb |= val; + break; + default: + g_assert_not_reached(); + } + + if (write) { + if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) { + goto sigsegv_maperr; + } + } + + set_regval(env, env->atomic_dstr, val); + end_exclusive(); + return; + + sigsegv_maperr: + end_exclusive(); + gen_sigsegv_maperr(env, addr); +} + +void cpu_loop(CPUTLGState *env) +{ + CPUState *cs = CPU(tilegx_env_get_cpu(env)); + int trapnr; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case TILEGX_EXCP_SYSCALL: + { + abi_ulong ret = do_syscall(env, env->regs[TILEGX_R_NR], + env->regs[0], env->regs[1], + env->regs[2], env->regs[3], + env->regs[4], env->regs[5], + env->regs[6], env->regs[7]); + if (ret == -TARGET_ERESTARTSYS) { + env->pc -= 8; + } else if (ret != -TARGET_QEMU_ESIGRETURN) { + env->regs[TILEGX_R_RE] = ret; + env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(ret) ? -ret : 0; + } + break; + } + case TILEGX_EXCP_OPCODE_EXCH: + do_exch(env, true, false); + break; + case TILEGX_EXCP_OPCODE_EXCH4: + do_exch(env, false, false); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH: + do_exch(env, true, true); + break; + case TILEGX_EXCP_OPCODE_CMPEXCH4: + do_exch(env, false, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ: + case TILEGX_EXCP_OPCODE_FETCHAND: + case TILEGX_EXCP_OPCODE_FETCHOR: + do_fetch(env, trapnr, true); + break; + case TILEGX_EXCP_OPCODE_FETCHADD4: + case TILEGX_EXCP_OPCODE_FETCHADDGEZ4: + case TILEGX_EXCP_OPCODE_FETCHAND4: + case TILEGX_EXCP_OPCODE_FETCHOR4: + do_fetch(env, trapnr, false); + break; + case TILEGX_EXCP_SIGNAL: + do_signal(env, env->signo, env->sigcode); + break; + case TILEGX_EXCP_REG_IDN_ACCESS: + case TILEGX_EXCP_REG_UDN_ACCESS: + gen_sigill_reg(env); + break; + case EXCP_ATOMIC: + cpu_exec_step_atomic(cs); + break; + default: + fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr); + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + for (i = 0; i < TILEGX_R_COUNT; i++) { + env->regs[i] = regs->regs[i]; + } + for (i = 0; i < TILEGX_SPR_COUNT; i++) { + env->spregs[i] = 0; + } + env->pc = regs->pc; +} diff --git a/linux-user/tilegx/signal.c b/linux-user/tilegx/signal.c new file mode 100644 index 0000000000..c5a1c7161d --- /dev/null +++ b/linux-user/tilegx/signal.c @@ -0,0 +1,178 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + union { + /* General-purpose registers. */ + abi_ulong gregs[56]; + struct { + abi_ulong __gregs[53]; + abi_ulong tp; /* Aliases gregs[TREG_TP]. */ + abi_ulong sp; /* Aliases gregs[TREG_SP]. */ + abi_ulong lr; /* Aliases gregs[TREG_LR]. */ + }; + }; + abi_ulong pc; /* Program counter. */ + abi_ulong ics; /* In Interrupt Critical Section? */ + abi_ulong faultnum; /* Fault number. */ + abi_ulong pad[5]; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ +}; + +struct target_rt_sigframe { + unsigned char save_area[16]; /* caller save area */ + struct target_siginfo info; + struct target_ucontext uc; + abi_ulong retcode[2]; +}; + +#define INSN_MOVELI_R10_139 0x00045fe551483000ULL /* { moveli r10, 139 } */ +#define INSN_SWINT1 0x286b180051485000ULL /* { swint1 } */ + + +static void setup_sigcontext(struct target_sigcontext *sc, + CPUArchState *env, int signo) +{ + int i; + + for (i = 0; i < TILEGX_R_COUNT; ++i) { + __put_user(env->regs[i], &sc->gregs[i]); + } + + __put_user(env->pc, &sc->pc); + __put_user(0, &sc->ics); + __put_user(signo, &sc->faultnum); +} + +static void restore_sigcontext(CPUTLGState *env, struct target_sigcontext *sc) +{ + int i; + + for (i = 0; i < TILEGX_R_COUNT; ++i) { + __get_user(env->regs[i], &sc->gregs[i]); + } + + __get_user(env->pc, &sc->pc); +} + +static abi_ulong get_sigframe(struct target_sigaction *ka, CPUArchState *env, + size_t frame_size) +{ + unsigned long sp = get_sp_from_cpustate(env); + + if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size))) { + return -1UL; + } + + sp = target_sigsp(sp, ka) - frame_size; + sp &= -16UL; + return sp; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env) +{ + abi_ulong frame_addr; + struct target_rt_sigframe *frame; + unsigned long restorer; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + /* Always write at least the signal number for the stack backtracer. */ + if (ka->sa_flags & TARGET_SA_SIGINFO) { + /* At sigreturn time, restore the callee-save registers too. */ + tswap_siginfo(&frame->info, info); + /* regs->flags |= PT_FLAGS_RESTORE_REGS; FIXME: we can skip it? */ + } else { + __put_user(info->si_signo, &frame->info.si_signo); + } + + /* Create the ucontext. */ + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + target_save_altstack(&frame->uc.tuc_stack, env); + setup_sigcontext(&frame->uc.tuc_mcontext, env, info->si_signo); + + if (ka->sa_flags & TARGET_SA_RESTORER) { + restorer = (unsigned long) ka->sa_restorer; + } else { + __put_user(INSN_MOVELI_R10_139, &frame->retcode[0]); + __put_user(INSN_SWINT1, &frame->retcode[1]); + restorer = frame_addr + offsetof(struct target_rt_sigframe, retcode); + } + env->pc = (unsigned long) ka->_sa_handler; + env->regs[TILEGX_R_SP] = (unsigned long) frame; + env->regs[TILEGX_R_LR] = restorer; + env->regs[0] = (unsigned long) sig; + env->regs[1] = (unsigned long) &frame->info; + env->regs[2] = (unsigned long) &frame->uc; + /* regs->flags |= PT_FLAGS_CALLER_SAVES; FIXME: we can skip it? */ + + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); +} + +long do_rt_sigreturn(CPUTLGState *env) +{ + abi_ulong frame_addr = env->regs[TILEGX_R_SP]; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->regs[TILEGX_R_SP]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/tilegx/sockbits.h b/linux-user/tilegx/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/tilegx/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/tilegx/target_cpu.h b/linux-user/tilegx/target_cpu.h index 4878e01b03..d1aa5824f2 100644 --- a/linux-user/tilegx/target_cpu.h +++ b/linux-user/tilegx/target_cpu.h @@ -32,4 +32,8 @@ static inline void cpu_set_tls(CPUTLGState *env, target_ulong newtls) env->regs[TILEGX_R_TP] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUTLGState *state) +{ + return state->regs[TILEGX_R_SP]; +} #endif diff --git a/linux-user/tilegx/target_fcntl.h b/linux-user/tilegx/target_fcntl.h new file mode 100644 index 0000000000..5ed7438459 --- /dev/null +++ b/linux-user/tilegx/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef TILEGX_TARGET_FCNTL_H +#define TILEGX_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/tilegx/target_signal.h b/linux-user/tilegx/target_signal.h index f64551a8cf..655be13009 100644 --- a/linux-user/tilegx/target_signal.h +++ b/linux-user/tilegx/target_signal.h @@ -1,8 +1,6 @@ #ifndef TILEGX_TARGET_SIGNAL_H #define TILEGX_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -20,10 +18,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUTLGState *state) -{ - return state->regs[TILEGX_R_SP]; -} - +#include "../generic/signal.h" #endif /* TILEGX_TARGET_SIGNAL_H */ diff --git a/linux-user/x86_64/cpu_loop.c b/linux-user/x86_64/cpu_loop.c new file mode 100644 index 0000000000..8b5af8ea1f --- /dev/null +++ b/linux-user/x86_64/cpu_loop.c @@ -0,0 +1,20 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "../i386/cpu_loop.c" diff --git a/linux-user/x86_64/signal.c b/linux-user/x86_64/signal.c new file mode 100644 index 0000000000..a509a38045 --- /dev/null +++ b/linux-user/x86_64/signal.c @@ -0,0 +1,20 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#define I386_TARGET_SIGNAL_H /* to only include x86_64/target_signal.h */ +#include "../i386/signal.c" diff --git a/linux-user/x86_64/sockbits.h b/linux-user/x86_64/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/x86_64/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/x86_64/target_fcntl.h b/linux-user/x86_64/target_fcntl.h new file mode 100644 index 0000000000..3c7238e56b --- /dev/null +++ b/linux-user/x86_64/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef X86_64_TARGET_FCNTL_H +#define X86_64_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/x86_64/target_signal.h b/linux-user/x86_64/target_signal.h index 1e95f4a684..4c4380f7b9 100644 --- a/linux-user/x86_64/target_signal.h +++ b/linux-user/x86_64/target_signal.h @@ -1,8 +1,6 @@ #ifndef X86_64_TARGET_SIGNAL_H #define X86_64_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -21,9 +19,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUX86State *state) -{ - return state->regs[R_ESP]; -} +#include "../generic/signal.h" #endif /* X86_64_TARGET_SIGNAL_H */ diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c new file mode 100644 index 0000000000..d142988ebe --- /dev/null +++ b/linux-user/xtensa/cpu_loop.c @@ -0,0 +1,267 @@ +/* + * qemu user cpu loop + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "cpu_loop-common.h" + +static void xtensa_rfw(CPUXtensaState *env) +{ + xtensa_restore_owb(env); + env->pc = env->sregs[EPC1]; +} + +static void xtensa_rfwu(CPUXtensaState *env) +{ + env->sregs[WINDOW_START] |= (1 << env->sregs[WINDOW_BASE]); + xtensa_rfw(env); +} + +static void xtensa_rfwo(CPUXtensaState *env) +{ + env->sregs[WINDOW_START] &= ~(1 << env->sregs[WINDOW_BASE]); + xtensa_rfw(env); +} + +static void xtensa_overflow4(CPUXtensaState *env) +{ + put_user_ual(env->regs[0], env->regs[5] - 16); + put_user_ual(env->regs[1], env->regs[5] - 12); + put_user_ual(env->regs[2], env->regs[5] - 8); + put_user_ual(env->regs[3], env->regs[5] - 4); + xtensa_rfwo(env); +} + +static void xtensa_underflow4(CPUXtensaState *env) +{ + get_user_ual(env->regs[0], env->regs[5] - 16); + get_user_ual(env->regs[1], env->regs[5] - 12); + get_user_ual(env->regs[2], env->regs[5] - 8); + get_user_ual(env->regs[3], env->regs[5] - 4); + xtensa_rfwu(env); +} + +static void xtensa_overflow8(CPUXtensaState *env) +{ + put_user_ual(env->regs[0], env->regs[9] - 16); + get_user_ual(env->regs[0], env->regs[1] - 12); + put_user_ual(env->regs[1], env->regs[9] - 12); + put_user_ual(env->regs[2], env->regs[9] - 8); + put_user_ual(env->regs[3], env->regs[9] - 4); + put_user_ual(env->regs[4], env->regs[0] - 32); + put_user_ual(env->regs[5], env->regs[0] - 28); + put_user_ual(env->regs[6], env->regs[0] - 24); + put_user_ual(env->regs[7], env->regs[0] - 20); + xtensa_rfwo(env); +} + +static void xtensa_underflow8(CPUXtensaState *env) +{ + get_user_ual(env->regs[0], env->regs[9] - 16); + get_user_ual(env->regs[1], env->regs[9] - 12); + get_user_ual(env->regs[2], env->regs[9] - 8); + get_user_ual(env->regs[7], env->regs[1] - 12); + get_user_ual(env->regs[3], env->regs[9] - 4); + get_user_ual(env->regs[4], env->regs[7] - 32); + get_user_ual(env->regs[5], env->regs[7] - 28); + get_user_ual(env->regs[6], env->regs[7] - 24); + get_user_ual(env->regs[7], env->regs[7] - 20); + xtensa_rfwu(env); +} + +static void xtensa_overflow12(CPUXtensaState *env) +{ + put_user_ual(env->regs[0], env->regs[13] - 16); + get_user_ual(env->regs[0], env->regs[1] - 12); + put_user_ual(env->regs[1], env->regs[13] - 12); + put_user_ual(env->regs[2], env->regs[13] - 8); + put_user_ual(env->regs[3], env->regs[13] - 4); + put_user_ual(env->regs[4], env->regs[0] - 48); + put_user_ual(env->regs[5], env->regs[0] - 44); + put_user_ual(env->regs[6], env->regs[0] - 40); + put_user_ual(env->regs[7], env->regs[0] - 36); + put_user_ual(env->regs[8], env->regs[0] - 32); + put_user_ual(env->regs[9], env->regs[0] - 28); + put_user_ual(env->regs[10], env->regs[0] - 24); + put_user_ual(env->regs[11], env->regs[0] - 20); + xtensa_rfwo(env); +} + +static void xtensa_underflow12(CPUXtensaState *env) +{ + get_user_ual(env->regs[0], env->regs[13] - 16); + get_user_ual(env->regs[1], env->regs[13] - 12); + get_user_ual(env->regs[2], env->regs[13] - 8); + get_user_ual(env->regs[11], env->regs[1] - 12); + get_user_ual(env->regs[3], env->regs[13] - 4); + get_user_ual(env->regs[4], env->regs[11] - 48); + get_user_ual(env->regs[5], env->regs[11] - 44); + get_user_ual(env->regs[6], env->regs[11] - 40); + get_user_ual(env->regs[7], env->regs[11] - 36); + get_user_ual(env->regs[8], env->regs[11] - 32); + get_user_ual(env->regs[9], env->regs[11] - 28); + get_user_ual(env->regs[10], env->regs[11] - 24); + get_user_ual(env->regs[11], env->regs[11] - 20); + xtensa_rfwu(env); +} + +void cpu_loop(CPUXtensaState *env) +{ + CPUState *cs = CPU(xtensa_env_get_cpu(env)); + target_siginfo_t info; + abi_ulong ret; + int trapnr; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + env->sregs[PS] &= ~PS_EXCM; + switch (trapnr) { + case EXCP_INTERRUPT: + break; + + case EXC_WINDOW_OVERFLOW4: + xtensa_overflow4(env); + break; + case EXC_WINDOW_UNDERFLOW4: + xtensa_underflow4(env); + break; + case EXC_WINDOW_OVERFLOW8: + xtensa_overflow8(env); + break; + case EXC_WINDOW_UNDERFLOW8: + xtensa_underflow8(env); + break; + case EXC_WINDOW_OVERFLOW12: + xtensa_overflow12(env); + break; + case EXC_WINDOW_UNDERFLOW12: + xtensa_underflow12(env); + break; + + case EXC_USER: + switch (env->sregs[EXCCAUSE]) { + case ILLEGAL_INSTRUCTION_CAUSE: + case PRIVILEGED_CAUSE: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = + env->sregs[EXCCAUSE] == ILLEGAL_INSTRUCTION_CAUSE ? + TARGET_ILL_ILLOPC : TARGET_ILL_PRVOPC; + info._sifields._sigfault._addr = env->sregs[EPC1]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + + case SYSCALL_CAUSE: + env->pc += 3; + ret = do_syscall(env, env->regs[2], + env->regs[6], env->regs[3], + env->regs[4], env->regs[5], + env->regs[8], env->regs[9], 0, 0); + switch (ret) { + default: + env->regs[2] = ret; + break; + + case -TARGET_ERESTARTSYS: + env->pc -= 3; + break; + + case -TARGET_QEMU_ESIGRETURN: + break; + } + break; + + case ALLOCA_CAUSE: + env->sregs[PS] = deposit32(env->sregs[PS], + PS_OWB_SHIFT, + PS_OWB_LEN, + env->sregs[WINDOW_BASE]); + + switch (env->regs[0] & 0xc0000000) { + case 0x00000000: + case 0x40000000: + xtensa_rotate_window(env, -1); + xtensa_underflow4(env); + break; + + case 0x80000000: + xtensa_rotate_window(env, -2); + xtensa_underflow8(env); + break; + + case 0xc0000000: + xtensa_rotate_window(env, -3); + xtensa_underflow12(env); + break; + } + break; + + case INTEGER_DIVIDE_BY_ZERO_CAUSE: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_INTDIV; + info._sifields._sigfault._addr = env->sregs[EPC1]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + + case LOAD_PROHIBITED_CAUSE: + case STORE_PROHIBITED_CAUSE: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_ACCERR; + info._sifields._sigfault._addr = env->sregs[EXCVADDR]; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + + default: + fprintf(stderr, "exccause = %d\n", env->sregs[EXCCAUSE]); + g_assert_not_reached(); + } + break; + case EXCP_DEBUG: + trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); + if (trapnr) { + info.si_signo = trapnr; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, trapnr, QEMU_SI_FAULT, &info); + } + break; + case EXC_DEBUG: + default: + fprintf(stderr, "trapnr = %d\n", trapnr); + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + +void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) +{ + int i; + for (i = 0; i < 16; ++i) { + env->regs[i] = regs->areg[i]; + } + env->sregs[WINDOW_START] = regs->windowstart; + env->pc = regs->pc; +} diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c new file mode 100644 index 0000000000..8d54ef3ae3 --- /dev/null +++ b/linux-user/xtensa/signal.c @@ -0,0 +1,260 @@ +/* + * Emulation of Linux signals + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 . + */ +#include "qemu/osdep.h" +#include "qemu.h" +#include "signal-common.h" +#include "linux-user/trace.h" + +struct target_sigcontext { + abi_ulong sc_pc; + abi_ulong sc_ps; + abi_ulong sc_lbeg; + abi_ulong sc_lend; + abi_ulong sc_lcount; + abi_ulong sc_sar; + abi_ulong sc_acclo; + abi_ulong sc_acchi; + abi_ulong sc_a[16]; + abi_ulong sc_xtregs; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe { + target_siginfo_t info; + struct target_ucontext uc; + /* TODO: xtregs */ + uint8_t retcode[6]; + abi_ulong window[4]; +}; + +static abi_ulong get_sigframe(struct target_sigaction *sa, + CPUXtensaState *env, + unsigned long framesize) +{ + abi_ulong sp; + + sp = target_sigsp(get_sp_from_cpustate(env), sa); + + return (sp - framesize) & -16; +} + +static int flush_window_regs(CPUXtensaState *env) +{ + uint32_t wb = env->sregs[WINDOW_BASE]; + uint32_t ws = xtensa_replicate_windowstart(env) >> (wb + 1); + unsigned d = ctz32(ws) + 1; + unsigned i; + int ret = 0; + + for (i = d; i < env->config->nareg / 4; i += d) { + uint32_t ssp, osp; + unsigned j; + + ws >>= d; + xtensa_rotate_window(env, d); + + if (ws & 0x1) { + ssp = env->regs[5]; + d = 1; + } else if (ws & 0x2) { + ssp = env->regs[9]; + ret |= get_user_ual(osp, env->regs[1] - 12); + osp -= 32; + d = 2; + } else if (ws & 0x4) { + ssp = env->regs[13]; + ret |= get_user_ual(osp, env->regs[1] - 12); + osp -= 48; + d = 3; + } else { + g_assert_not_reached(); + } + + for (j = 0; j < 4; ++j) { + ret |= put_user_ual(env->regs[j], ssp - 16 + j * 4); + } + for (j = 4; j < d * 4; ++j) { + ret |= put_user_ual(env->regs[j], osp - 16 + j * 4); + } + } + xtensa_rotate_window(env, d); + g_assert(env->sregs[WINDOW_BASE] == wb); + return ret == 0; +} + +static int setup_sigcontext(struct target_rt_sigframe *frame, + CPUXtensaState *env) +{ + struct target_sigcontext *sc = &frame->uc.tuc_mcontext; + int i; + + __put_user(env->pc, &sc->sc_pc); + __put_user(env->sregs[PS], &sc->sc_ps); + __put_user(env->sregs[LBEG], &sc->sc_lbeg); + __put_user(env->sregs[LEND], &sc->sc_lend); + __put_user(env->sregs[LCOUNT], &sc->sc_lcount); + if (!flush_window_regs(env)) { + return 0; + } + for (i = 0; i < 16; ++i) { + __put_user(env->regs[i], sc->sc_a + i); + } + __put_user(0, &sc->sc_xtregs); + /* TODO: xtregs */ + return 1; +} + +void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUXtensaState *env) +{ + abi_ulong frame_addr; + struct target_rt_sigframe *frame; + uint32_t ra; + int i; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + if (ka->sa_flags & SA_SIGINFO) { + tswap_siginfo(&frame->info, info); + } + + __put_user(0, &frame->uc.tuc_flags); + __put_user(0, &frame->uc.tuc_link); + target_save_altstack(&frame->uc.tuc_stack, env); + if (!setup_sigcontext(frame, env)) { + unlock_user_struct(frame, frame_addr, 0); + goto give_sigsegv; + } + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + if (ka->sa_flags & TARGET_SA_RESTORER) { + ra = ka->sa_restorer; + } else { + ra = frame_addr + offsetof(struct target_rt_sigframe, retcode); +#ifdef TARGET_WORDS_BIGENDIAN + /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ + __put_user(0x22, &frame->retcode[0]); + __put_user(0x0a, &frame->retcode[1]); + __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); + /* Generate instruction: SYSCALL */ + __put_user(0x00, &frame->retcode[3]); + __put_user(0x05, &frame->retcode[4]); + __put_user(0x00, &frame->retcode[5]); +#else + /* Generate instruction: MOVI a2, __NR_rt_sigreturn */ + __put_user(0x22, &frame->retcode[0]); + __put_user(0xa0, &frame->retcode[1]); + __put_user(TARGET_NR_rt_sigreturn, &frame->retcode[2]); + /* Generate instruction: SYSCALL */ + __put_user(0x00, &frame->retcode[3]); + __put_user(0x50, &frame->retcode[4]); + __put_user(0x00, &frame->retcode[5]); +#endif + } + env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT); + if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER)) { + env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT); + } + memset(env->regs, 0, sizeof(env->regs)); + env->pc = ka->_sa_handler; + env->regs[1] = frame_addr; + env->sregs[WINDOW_BASE] = 0; + env->sregs[WINDOW_START] = 1; + + env->regs[4] = (ra & 0x3fffffff) | 0x40000000; + env->regs[6] = sig; + env->regs[7] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->regs[8] = frame_addr + offsetof(struct target_rt_sigframe, uc); + unlock_user_struct(frame, frame_addr, 1); + return; + +give_sigsegv: + force_sigsegv(sig); + return; +} + +static void restore_sigcontext(CPUXtensaState *env, + struct target_rt_sigframe *frame) +{ + struct target_sigcontext *sc = &frame->uc.tuc_mcontext; + uint32_t ps; + int i; + + __get_user(env->pc, &sc->sc_pc); + __get_user(ps, &sc->sc_ps); + __get_user(env->sregs[LBEG], &sc->sc_lbeg); + __get_user(env->sregs[LEND], &sc->sc_lend); + __get_user(env->sregs[LCOUNT], &sc->sc_lcount); + + env->sregs[WINDOW_BASE] = 0; + env->sregs[WINDOW_START] = 1; + env->sregs[PS] = deposit32(env->sregs[PS], + PS_CALLINC_SHIFT, + PS_CALLINC_LEN, + extract32(ps, PS_CALLINC_SHIFT, + PS_CALLINC_LEN)); + for (i = 0; i < 16; ++i) { + __get_user(env->regs[i], sc->sc_a + i); + } + /* TODO: xtregs */ +} + +long do_rt_sigreturn(CPUXtensaState *env) +{ + abi_ulong frame_addr = env->regs[1]; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, frame); + + if (do_sigaltstack(frame_addr + + offsetof(struct target_rt_sigframe, uc.tuc_stack), + 0, get_sp_from_cpustate(env)) == -TARGET_EFAULT) { + goto badframe; + } + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + +badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} diff --git a/linux-user/xtensa/sockbits.h b/linux-user/xtensa/sockbits.h new file mode 100644 index 0000000000..0e4c8f012d --- /dev/null +++ b/linux-user/xtensa/sockbits.h @@ -0,0 +1 @@ +#include "../generic/sockbits.h" diff --git a/linux-user/xtensa/target_cpu.h b/linux-user/xtensa/target_cpu.h index 747d828614..e31efe3ea0 100644 --- a/linux-user/xtensa/target_cpu.h +++ b/linux-user/xtensa/target_cpu.h @@ -19,4 +19,8 @@ static inline void cpu_set_tls(CPUXtensaState *env, target_ulong newtls) env->uregs[THREADPTR] = newtls; } +static inline abi_ulong get_sp_from_cpustate(CPUXtensaState *state) +{ + return state->regs[1]; +} #endif diff --git a/linux-user/xtensa/target_fcntl.h b/linux-user/xtensa/target_fcntl.h new file mode 100644 index 0000000000..dc1ca7eaa5 --- /dev/null +++ b/linux-user/xtensa/target_fcntl.h @@ -0,0 +1,11 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation, or (at your option) any + * later version. See the COPYING file in the top-level directory. + */ + +#ifndef XTENSA_TARGET_FCNTL_H +#define XTENSA_TARGET_FCNTL_H +#include "../generic/fcntl.h" +#endif diff --git a/linux-user/xtensa/target_signal.h b/linux-user/xtensa/target_signal.h index c6962e70af..c60bf656f6 100644 --- a/linux-user/xtensa/target_signal.h +++ b/linux-user/xtensa/target_signal.h @@ -1,8 +1,6 @@ #ifndef XTENSA_TARGET_SIGNAL_H #define XTENSA_TARGET_SIGNAL_H -#include "cpu.h" - /* this struct defines a stack used during syscall handling */ typedef struct target_sigaltstack { @@ -20,9 +18,6 @@ typedef struct target_sigaltstack { #define TARGET_MINSIGSTKSZ 2048 #define TARGET_SIGSTKSZ 8192 -static inline abi_ulong get_sp_from_cpustate(CPUXtensaState *state) -{ - return state->regs[1]; -} +#include "../generic/signal.h" #endif diff --git a/memory.c b/memory.c index b273531e1c..316b37ac05 100644 --- a/memory.c +++ b/memory.c @@ -19,7 +19,6 @@ #include "cpu.h" #include "exec/memory.h" #include "exec/address-spaces.h" -#include "exec/ioport.h" #include "qapi/visitor.h" #include "qemu/bitops.h" #include "qemu/error-report.h" @@ -173,38 +172,38 @@ struct MemoryRegionIoeventfd { EventNotifier *e; }; -static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd a, - MemoryRegionIoeventfd b) +static bool memory_region_ioeventfd_before(MemoryRegionIoeventfd *a, + MemoryRegionIoeventfd *b) { - if (int128_lt(a.addr.start, b.addr.start)) { + if (int128_lt(a->addr.start, b->addr.start)) { return true; - } else if (int128_gt(a.addr.start, b.addr.start)) { + } else if (int128_gt(a->addr.start, b->addr.start)) { return false; - } else if (int128_lt(a.addr.size, b.addr.size)) { + } else if (int128_lt(a->addr.size, b->addr.size)) { return true; - } else if (int128_gt(a.addr.size, b.addr.size)) { + } else if (int128_gt(a->addr.size, b->addr.size)) { return false; - } else if (a.match_data < b.match_data) { + } else if (a->match_data < b->match_data) { return true; - } else if (a.match_data > b.match_data) { + } else if (a->match_data > b->match_data) { return false; - } else if (a.match_data) { - if (a.data < b.data) { + } else if (a->match_data) { + if (a->data < b->data) { return true; - } else if (a.data > b.data) { + } else if (a->data > b->data) { return false; } } - if (a.e < b.e) { + if (a->e < b->e) { return true; - } else if (a.e > b.e) { + } else if (a->e > b->e) { return false; } return false; } -static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd a, - MemoryRegionIoeventfd b) +static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd *a, + MemoryRegionIoeventfd *b) { return !memory_region_ioeventfd_before(a, b) && !memory_region_ioeventfd_before(b, a); @@ -220,8 +219,6 @@ struct FlatRange { bool readonly; }; -typedef struct AddressSpaceOps AddressSpaceOps; - #define FOR_EACH_FLAT_RANGE(var, view) \ for (var = (view)->ranges; var < (view)->ranges + (view)->nr; ++var) @@ -298,7 +295,7 @@ static bool flatview_ref(FlatView *view) return atomic_fetch_inc_nonzero(&view->ref) > 0; } -static void flatview_unref(FlatView *view) +void flatview_unref(FlatView *view) { if (atomic_fetch_dec(&view->ref) == 1) { trace_flatview_destroy_rcu(view, view->root); @@ -791,8 +788,8 @@ static void address_space_add_del_ioeventfds(AddressSpace *as, while (iold < fds_old_nb || inew < fds_new_nb) { if (iold < fds_old_nb && (inew == fds_new_nb - || memory_region_ioeventfd_before(fds_old[iold], - fds_new[inew]))) { + || memory_region_ioeventfd_before(&fds_old[iold], + &fds_new[inew]))) { fd = &fds_old[iold]; section = (MemoryRegionSection) { .fv = address_space_to_flatview(as), @@ -804,8 +801,8 @@ static void address_space_add_del_ioeventfds(AddressSpace *as, ++iold; } else if (inew < fds_new_nb && (iold == fds_old_nb - || memory_region_ioeventfd_before(fds_new[inew], - fds_old[iold]))) { + || memory_region_ioeventfd_before(&fds_new[inew], + &fds_old[iold]))) { fd = &fds_new[inew]; section = (MemoryRegionSection) { .fv = address_space_to_flatview(as), @@ -822,7 +819,7 @@ static void address_space_add_del_ioeventfds(AddressSpace *as, } } -static FlatView *address_space_get_flatview(AddressSpace *as) +FlatView *address_space_get_flatview(AddressSpace *as) { FlatView *view; @@ -1269,7 +1266,8 @@ static void unassigned_mem_write(void *opaque, hwaddr addr, } static bool unassigned_mem_accepts(void *opaque, hwaddr addr, - unsigned size, bool is_write) + unsigned size, bool is_write, + MemTxAttrs attrs) { return false; } @@ -1347,7 +1345,8 @@ static const MemoryRegionOps ram_device_mem_ops = { bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr, unsigned size, - bool is_write) + bool is_write, + MemTxAttrs attrs) { int access_size_min, access_size_max; int access_size, i; @@ -1373,7 +1372,7 @@ bool memory_region_access_valid(MemoryRegion *mr, access_size = MAX(MIN(size, access_size_max), access_size_min); for (i = 0; i < size; i += access_size) { if (!mr->ops->valid.accepts(mr->opaque, addr + i, access_size, - is_write)) { + is_write, attrs)) { return false; } } @@ -1416,7 +1415,7 @@ MemTxResult memory_region_dispatch_read(MemoryRegion *mr, { MemTxResult r; - if (!memory_region_access_valid(mr, addr, size, false)) { + if (!memory_region_access_valid(mr, addr, size, false, attrs)) { *pval = unassigned_mem_read(mr, addr, size); return MEMTX_DECODE_ERROR; } @@ -1443,7 +1442,7 @@ static bool memory_region_dispatch_write_eventfds(MemoryRegion *mr, ioeventfd.match_data = mr->ioeventfds[i].match_data; ioeventfd.e = mr->ioeventfds[i].e; - if (memory_region_ioeventfd_equal(ioeventfd, mr->ioeventfds[i])) { + if (memory_region_ioeventfd_equal(&ioeventfd, &mr->ioeventfds[i])) { event_notifier_set(ioeventfd.e); return true; } @@ -1458,7 +1457,7 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr, unsigned size, MemTxAttrs attrs) { - if (!memory_region_access_valid(mr, addr, size, true)) { + if (!memory_region_access_valid(mr, addr, size, true, attrs)) { unassigned_mem_write(mr, addr, data, size); return MEMTX_DECODE_ERROR; } @@ -1810,6 +1809,9 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr, iommu_mr = IOMMU_MEMORY_REGION(mr); assert(n->notifier_flags != IOMMU_NOTIFIER_NONE); assert(n->start <= n->end); + assert(n->iommu_idx >= 0 && + n->iommu_idx < memory_region_iommu_num_indexes(iommu_mr)); + QLIST_INSERT_HEAD(&iommu_mr->iommu_notify, n, node); memory_region_update_iommu_notify_flags(iommu_mr); } @@ -1840,7 +1842,7 @@ void memory_region_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) granularity = memory_region_iommu_get_min_page_size(iommu_mr); for (addr = 0; addr < memory_region_size(mr); addr += granularity) { - iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE); + iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, n->iommu_idx); if (iotlb.perm != IOMMU_NONE) { n->notify(n, &iotlb); } @@ -1902,6 +1904,7 @@ void memory_region_notify_one(IOMMUNotifier *notifier, } void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, + int iommu_idx, IOMMUTLBEntry entry) { IOMMUNotifier *iommu_notifier; @@ -1909,7 +1912,9 @@ void memory_region_notify_iommu(IOMMUMemoryRegion *iommu_mr, assert(memory_region_is_iommu(MEMORY_REGION(iommu_mr))); IOMMU_NOTIFIER_FOREACH(iommu_notifier, iommu_mr) { - memory_region_notify_one(iommu_notifier, &entry); + if (iommu_notifier->iommu_idx == iommu_idx) { + memory_region_notify_one(iommu_notifier, &entry); + } } } @@ -1926,6 +1931,29 @@ int memory_region_iommu_get_attr(IOMMUMemoryRegion *iommu_mr, return imrc->get_attr(iommu_mr, attr, data); } +int memory_region_iommu_attrs_to_index(IOMMUMemoryRegion *iommu_mr, + MemTxAttrs attrs) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + + if (!imrc->attrs_to_index) { + return 0; + } + + return imrc->attrs_to_index(iommu_mr, attrs); +} + +int memory_region_iommu_num_indexes(IOMMUMemoryRegion *iommu_mr) +{ + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + + if (!imrc->num_indexes) { + return 1; + } + + return imrc->num_indexes(iommu_mr); +} + void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) { uint8_t mask = 1 << client; @@ -2269,7 +2297,7 @@ void memory_region_add_eventfd(MemoryRegion *mr, } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { - if (memory_region_ioeventfd_before(mrfd, mr->ioeventfds[i])) { + if (memory_region_ioeventfd_before(&mrfd, &mr->ioeventfds[i])) { break; } } @@ -2304,7 +2332,7 @@ void memory_region_del_eventfd(MemoryRegion *mr, } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { - if (memory_region_ioeventfd_equal(mrfd, mr->ioeventfds[i])) { + if (memory_region_ioeventfd_equal(&mrfd, &mr->ioeventfds[i])) { break; } } @@ -2886,10 +2914,49 @@ typedef QTAILQ_HEAD(mrqueue, MemoryRegionList) MemoryRegionListHead; int128_sub((size), int128_one())) : 0) #define MTREE_INDENT " " +static void mtree_expand_owner(fprintf_function mon_printf, void *f, + const char *label, Object *obj) +{ + DeviceState *dev = (DeviceState *) object_dynamic_cast(obj, TYPE_DEVICE); + + mon_printf(f, " %s:{%s", label, dev ? "dev" : "obj"); + if (dev && dev->id) { + mon_printf(f, " id=%s", dev->id); + } else { + gchar *canonical_path = object_get_canonical_path(obj); + if (canonical_path) { + mon_printf(f, " path=%s", canonical_path); + g_free(canonical_path); + } else { + mon_printf(f, " type=%s", object_get_typename(obj)); + } + } + mon_printf(f, "}"); +} + +static void mtree_print_mr_owner(fprintf_function mon_printf, void *f, + const MemoryRegion *mr) +{ + Object *owner = mr->owner; + Object *parent = memory_region_owner((MemoryRegion *)mr); + + if (!owner && !parent) { + mon_printf(f, " orphan"); + return; + } + if (owner) { + mtree_expand_owner(mon_printf, f, "owner", owner); + } + if (parent && parent != owner) { + mtree_expand_owner(mon_printf, f, "parent", parent); + } +} + static void mtree_print_mr(fprintf_function mon_printf, void *f, const MemoryRegion *mr, unsigned int level, hwaddr base, - MemoryRegionListHead *alias_print_queue) + MemoryRegionListHead *alias_print_queue, + bool owner) { MemoryRegionList *new_ml, *ml, *next_ml; MemoryRegionListHead submr_print_queue; @@ -2935,7 +3002,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, } mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): alias %s @%s " TARGET_FMT_plx - "-" TARGET_FMT_plx "%s\n", + "-" TARGET_FMT_plx "%s", cur_start, cur_end, mr->priority, memory_region_type((MemoryRegion *)mr), @@ -2944,15 +3011,22 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, mr->alias_offset, mr->alias_offset + MR_SIZE(mr->size), mr->enabled ? "" : " [disabled]"); + if (owner) { + mtree_print_mr_owner(mon_printf, f, mr); + } } else { mon_printf(f, - TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): %s%s\n", + TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): %s%s", cur_start, cur_end, mr->priority, memory_region_type((MemoryRegion *)mr), memory_region_name(mr), mr->enabled ? "" : " [disabled]"); + if (owner) { + mtree_print_mr_owner(mon_printf, f, mr); + } } + mon_printf(f, "\n"); QTAILQ_INIT(&submr_print_queue); @@ -2975,7 +3049,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, QTAILQ_FOREACH(ml, &submr_print_queue, mrqueue) { mtree_print_mr(mon_printf, f, ml->mr, level + 1, cur_start, - alias_print_queue); + alias_print_queue, owner); } QTAILQ_FOREACH_SAFE(ml, &submr_print_queue, mrqueue, next_ml) { @@ -2988,6 +3062,7 @@ struct FlatViewInfo { void *f; int counter; bool dispatch_tree; + bool owner; }; static void mtree_print_flatview(gpointer key, gpointer value, @@ -3028,7 +3103,7 @@ static void mtree_print_flatview(gpointer key, gpointer value, mr = range->mr; if (range->offset_in_region) { p(f, MTREE_INDENT TARGET_FMT_plx "-" - TARGET_FMT_plx " (prio %d, %s): %s @" TARGET_FMT_plx "\n", + TARGET_FMT_plx " (prio %d, %s): %s @" TARGET_FMT_plx, int128_get64(range->addr.start), int128_get64(range->addr.start) + MR_SIZE(range->addr.size), mr->priority, @@ -3037,13 +3112,17 @@ static void mtree_print_flatview(gpointer key, gpointer value, range->offset_in_region); } else { p(f, MTREE_INDENT TARGET_FMT_plx "-" - TARGET_FMT_plx " (prio %d, %s): %s\n", + TARGET_FMT_plx " (prio %d, %s): %s", int128_get64(range->addr.start), int128_get64(range->addr.start) + MR_SIZE(range->addr.size), mr->priority, range->readonly ? "rom" : memory_region_type(mr), memory_region_name(mr)); } + if (fvi->owner) { + mtree_print_mr_owner(p, f, mr); + } + p(f, "\n"); range++; } @@ -3069,7 +3148,7 @@ static gboolean mtree_info_flatview_free(gpointer key, gpointer value, } void mtree_info(fprintf_function mon_printf, void *f, bool flatview, - bool dispatch_tree) + bool dispatch_tree, bool owner) { MemoryRegionListHead ml_head; MemoryRegionList *ml, *ml2; @@ -3081,7 +3160,8 @@ void mtree_info(fprintf_function mon_printf, void *f, bool flatview, .mon_printf = mon_printf, .f = f, .counter = 0, - .dispatch_tree = dispatch_tree + .dispatch_tree = dispatch_tree, + .owner = owner, }; GArray *fv_address_spaces; GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal); @@ -3113,14 +3193,14 @@ void mtree_info(fprintf_function mon_printf, void *f, bool flatview, QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) { mon_printf(f, "address-space: %s\n", as->name); - mtree_print_mr(mon_printf, f, as->root, 1, 0, &ml_head); + mtree_print_mr(mon_printf, f, as->root, 1, 0, &ml_head, owner); mon_printf(f, "\n"); } /* print aliased regions */ QTAILQ_FOREACH(ml, &ml_head, mrqueue) { mon_printf(f, "memory-region: %s\n", memory_region_name(ml->mr)); - mtree_print_mr(mon_printf, f, ml->mr, 1, 0, &ml_head); + mtree_print_mr(mon_printf, f, ml->mr, 1, 0, &ml_head, owner); mon_printf(f, "\n"); } diff --git a/memory_ldst.inc.c b/memory_ldst.inc.c index 5dbff9cef8..acf865b900 100644 --- a/memory_ldst.inc.c +++ b/memory_ldst.inc.c @@ -33,8 +33,8 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); - if (l < 4 || !IS_DIRECT(mr, false)) { + mr = TRANSLATE(addr, &addr1, &l, false, attrs); + if (l < 4 || !memory_access_is_direct(mr, false)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -50,7 +50,7 @@ static inline uint32_t glue(address_space_ldl_internal, SUFFIX)(ARG1_DECL, #endif } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: val = ldl_le_p(ptr); @@ -95,24 +95,6 @@ uint32_t glue(address_space_ldl_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -uint32_t glue(ldl_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldl, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint32_t glue(ldl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldl_le, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint32_t glue(ldl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldl_be, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned */ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result, @@ -127,8 +109,8 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); - if (l < 8 || !IS_DIRECT(mr, false)) { + mr = TRANSLATE(addr, &addr1, &l, false, attrs); + if (l < 8 || !memory_access_is_direct(mr, false)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -144,7 +126,7 @@ static inline uint64_t glue(address_space_ldq_internal, SUFFIX)(ARG1_DECL, #endif } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: val = ldq_le_p(ptr); @@ -189,24 +171,6 @@ uint64_t glue(address_space_ldq_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -uint64_t glue(ldq_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldq, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint64_t glue(ldq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldq_le, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint64_t glue(ldq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldq_be, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result) { @@ -219,15 +183,15 @@ uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); - if (!IS_DIRECT(mr, false)) { + mr = TRANSLATE(addr, &addr1, &l, false, attrs); + if (!memory_access_is_direct(mr, false)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ r = memory_region_dispatch_read(mr, addr1, &val, 1, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); val = ldub_p(ptr); r = MEMTX_OK; } @@ -241,12 +205,6 @@ uint32_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, return val; } -uint32_t glue(ldub_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_ldub, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned */ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result, @@ -261,8 +219,8 @@ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, false); - if (l < 2 || !IS_DIRECT(mr, false)) { + mr = TRANSLATE(addr, &addr1, &l, false, attrs); + if (l < 2 || !memory_access_is_direct(mr, false)) { release_lock |= prepare_mmio_access(mr); /* I/O case */ @@ -278,7 +236,7 @@ static inline uint32_t glue(address_space_lduw_internal, SUFFIX)(ARG1_DECL, #endif } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: val = lduw_le_p(ptr); @@ -323,24 +281,6 @@ uint32_t glue(address_space_lduw_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -uint32_t glue(lduw_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_lduw, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint32_t glue(lduw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_lduw_le, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -uint32_t glue(lduw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr) -{ - return glue(address_space_lduw_be, SUFFIX)(ARG1, addr, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned. The ram page is not masked as dirty and the code inside is not invalidated. It is useful if the dirty bits are used to track modified PTEs */ @@ -356,13 +296,13 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); - if (l < 4 || !IS_DIRECT(mr, true)) { + mr = TRANSLATE(addr, &addr1, &l, true, attrs); + if (l < 4 || !memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, 4, attrs); } else { - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); stl_p(ptr, val); dirty_log_mask = memory_region_get_dirty_log_mask(mr); @@ -380,12 +320,6 @@ void glue(address_space_stl_notdirty, SUFFIX)(ARG1_DECL, RCU_READ_UNLOCK(); } -void glue(stl_phys_notdirty, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stl_notdirty, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned */ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, @@ -399,8 +333,8 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); - if (l < 4 || !IS_DIRECT(mr, true)) { + mr = TRANSLATE(addr, &addr1, &l, true, attrs); + if (l < 4 || !memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); #if defined(TARGET_WORDS_BIGENDIAN) @@ -415,7 +349,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, r = memory_region_dispatch_write(mr, addr1, val, 4, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: stl_le_p(ptr, val); @@ -427,7 +361,7 @@ static inline void glue(address_space_stl_internal, SUFFIX)(ARG1_DECL, stl_p(ptr, val); break; } - INVALIDATE(mr, addr1, 4); + invalidate_and_set_dirty(mr, addr1, 4); r = MEMTX_OK; } if (result) { @@ -460,24 +394,6 @@ void glue(address_space_stl_be, SUFFIX)(ARG1_DECL, result, DEVICE_BIG_ENDIAN); } -void glue(stl_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stl, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stl_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stl_le, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stl_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stl_be, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - void glue(address_space_stb, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result) { @@ -489,15 +405,15 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); - if (!IS_DIRECT(mr, true)) { + mr = TRANSLATE(addr, &addr1, &l, true, attrs); + if (!memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); r = memory_region_dispatch_write(mr, addr1, val, 1, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); stb_p(ptr, val); - INVALIDATE(mr, addr1, 1); + invalidate_and_set_dirty(mr, addr1, 1); r = MEMTX_OK; } if (result) { @@ -509,12 +425,6 @@ void glue(address_space_stb, SUFFIX)(ARG1_DECL, RCU_READ_UNLOCK(); } -void glue(stb_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stb, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - /* warning: addr must be aligned */ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, @@ -528,8 +438,8 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); - if (l < 2 || !IS_DIRECT(mr, true)) { + mr = TRANSLATE(addr, &addr1, &l, true, attrs); + if (l < 2 || !memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); #if defined(TARGET_WORDS_BIGENDIAN) @@ -544,7 +454,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, r = memory_region_dispatch_write(mr, addr1, val, 2, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: stw_le_p(ptr, val); @@ -556,7 +466,7 @@ static inline void glue(address_space_stw_internal, SUFFIX)(ARG1_DECL, stw_p(ptr, val); break; } - INVALIDATE(mr, addr1, 2); + invalidate_and_set_dirty(mr, addr1, 2); r = MEMTX_OK; } if (result) { @@ -589,24 +499,6 @@ void glue(address_space_stw_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -void glue(stw_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stw, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stw_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stw_le, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stw_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val) -{ - glue(address_space_stw_be, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result, enum device_endian endian) @@ -619,8 +511,8 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, bool release_lock = false; RCU_READ_LOCK(); - mr = TRANSLATE(addr, &addr1, &l, true); - if (l < 8 || !IS_DIRECT(mr, true)) { + mr = TRANSLATE(addr, &addr1, &l, true, attrs); + if (l < 8 || !memory_access_is_direct(mr, true)) { release_lock |= prepare_mmio_access(mr); #if defined(TARGET_WORDS_BIGENDIAN) @@ -635,7 +527,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, r = memory_region_dispatch_write(mr, addr1, val, 8, attrs); } else { /* RAM case */ - ptr = MAP_RAM(mr, addr1); + ptr = qemu_map_ram_ptr(mr->ram_block, addr1); switch (endian) { case DEVICE_LITTLE_ENDIAN: stq_le_p(ptr, val); @@ -647,7 +539,7 @@ static void glue(address_space_stq_internal, SUFFIX)(ARG1_DECL, stq_p(ptr, val); break; } - INVALIDATE(mr, addr1, 8); + invalidate_and_set_dirty(mr, addr1, 8); r = MEMTX_OK; } if (result) { @@ -680,30 +572,9 @@ void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, DEVICE_BIG_ENDIAN); } -void glue(stq_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) -{ - glue(address_space_stq, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stq_le_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) -{ - glue(address_space_stq_le, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -void glue(stq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) -{ - glue(address_space_stq_be, SUFFIX)(ARG1, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - #undef ARG1_DECL #undef ARG1 #undef SUFFIX #undef TRANSLATE -#undef IS_DIRECT -#undef MAP_RAM -#undef INVALIDATE #undef RCU_READ_LOCK #undef RCU_READ_UNLOCK diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index dd04f102d8..477826330c 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -66,7 +66,7 @@ #include "qemu/error-report.h" #include "migration/misc.h" #include "migration/migration.h" -#include "migration/qemu-file.h" +#include "qemu-file.h" #include "migration/vmstate.h" #include "migration/register.h" #include "qemu/hbitmap.h" @@ -511,7 +511,7 @@ void dirty_bitmap_mig_before_vm_start(void) DirtyBitmapLoadBitmapState *b = item->data; if (b->migrated) { - bdrv_enable_dirty_bitmap(b->bitmap); + bdrv_enable_dirty_bitmap_locked(b->bitmap); } else { bdrv_dirty_bitmap_enable_successor(b->bitmap); } @@ -547,7 +547,7 @@ static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s) if (enabled_bitmaps == NULL) { /* in postcopy */ bdrv_reclaim_dirty_bitmap_locked(s->bs, s->bitmap, &error_abort); - bdrv_enable_dirty_bitmap(s->bitmap); + bdrv_enable_dirty_bitmap_locked(s->bitmap); } else { /* target not started, successor must be empty */ int64_t count = bdrv_get_dirty_count(s->bitmap); @@ -600,6 +600,7 @@ static int dirty_bitmap_load_bits(QEMUFile *f, DirtyBitmapLoadState *s) ret = qemu_get_buffer(f, buf, buf_size); if (ret != buf_size) { error_report("Failed to read bitmap bits"); + g_free(buf); return -EIO; } @@ -671,6 +672,9 @@ static int dirty_bitmap_load(QEMUFile *f, void *opaque, int version_id) do { ret = dirty_bitmap_load_header(f, &s); + if (ret < 0) { + return ret; + } if (s.flags & DIRTY_BITMAP_MIG_FLAG_START) { ret = dirty_bitmap_load_start(f, &s); diff --git a/migration/channel.c b/migration/channel.c index c5eaf0fa0e..33e0e9b82f 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -71,11 +71,21 @@ void migration_channel_connect(MigrationState *s, !object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_TLS)) { migration_tls_channel_connect(s, ioc, hostname, &error); + + if (!error) { + /* tls_channel_connect will call back to this + * function after the TLS handshake, + * so we mustn't call migrate_fd_connect until then + */ + + return; + } } else { QEMUFile *f = qemu_fopen_channel_output(ioc); + qemu_mutex_lock(&s->qemu_file_lock); s->to_dst_file = f; - + qemu_mutex_unlock(&s->qemu_file_lock); } } migrate_fd_connect(s, error); diff --git a/migration/exec.c b/migration/exec.c index 0bc5a427dd..375d2e1b54 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "exec.h" +#include "migration.h" #include "io/channel-command.h" #include "trace.h" @@ -65,9 +66,8 @@ void exec_start_incoming_migration(const char *command, Error **errp) } qio_channel_set_name(ioc, "migration-exec-incoming"); - qio_channel_add_watch(ioc, - G_IO_IN, - exec_accept_incoming_migration, - NULL, - NULL); + qio_channel_add_watch_full(ioc, G_IO_IN, + exec_accept_incoming_migration, + NULL, NULL, + g_main_context_get_thread_default()); } diff --git a/migration/fd.c b/migration/fd.c index cd06182d1e..a7c13df4ad 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "channel.h" #include "fd.h" +#include "migration.h" #include "monitor/monitor.h" #include "io/channel-util.h" #include "trace.h" @@ -66,9 +67,8 @@ void fd_start_incoming_migration(const char *infd, Error **errp) } qio_channel_set_name(QIO_CHANNEL(ioc), "migration-fd-incoming"); - qio_channel_add_watch(ioc, - G_IO_IN, - fd_accept_incoming_migration, - NULL, - NULL); + qio_channel_add_watch_full(ioc, G_IO_IN, + fd_accept_incoming_migration, + NULL, NULL, + g_main_context_get_thread_default()); } diff --git a/migration/migration.c b/migration/migration.c index 52a5092add..b7d9854bda 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -82,6 +82,11 @@ #define DEFAULT_MIGRATE_MULTIFD_CHANNELS 2 #define DEFAULT_MIGRATE_MULTIFD_PAGE_COUNT 16 +/* Background transfer rate for postcopy, 0 means unlimited, note + * that page requests can still exceed this limit. + */ +#define DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH 0 + static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); @@ -95,6 +100,8 @@ enum mig_rp_message_type { MIG_RP_MSG_REQ_PAGES_ID, /* data (start: be64, len: be32, id: string) */ MIG_RP_MSG_REQ_PAGES, /* data (start: be64, len: be32) */ + MIG_RP_MSG_RECV_BITMAP, /* send recved_bitmap back to source */ + MIG_RP_MSG_RESUME_ACK, /* tell source that we are ready to resume */ MIG_RP_MSG_MAX }; @@ -104,6 +111,7 @@ enum mig_rp_message_type { dynamic creation of migration */ static MigrationState *current_migration; +static MigrationIncomingState *current_incoming; static bool migration_object_check(MigrationState *ms, Error **errp); static int migration_maybe_pause(MigrationState *s, @@ -119,6 +127,22 @@ void migration_object_init(void) assert(!current_migration); current_migration = MIGRATION_OBJ(object_new(TYPE_MIGRATION)); + /* + * Init the migrate incoming object as well no matter whether + * we'll use it or not. + */ + assert(!current_incoming); + current_incoming = g_new0(MigrationIncomingState, 1); + current_incoming->state = MIGRATION_STATUS_NONE; + current_incoming->postcopy_remote_fds = + g_array_new(FALSE, TRUE, sizeof(struct PostCopyFD)); + qemu_mutex_init(¤t_incoming->rp_mutex); + qemu_event_init(¤t_incoming->main_thread_load_event, false); + qemu_sem_init(¤t_incoming->postcopy_pause_sem_dst, 0); + qemu_sem_init(¤t_incoming->postcopy_pause_sem_fault, 0); + + init_dirty_bitmap_incoming_migration(); + if (!migration_object_check(current_migration, &err)) { error_report_err(err); exit(1); @@ -149,22 +173,8 @@ MigrationState *migrate_get_current(void) MigrationIncomingState *migration_incoming_get_current(void) { - static bool once; - static MigrationIncomingState mis_current; - - if (!once) { - mis_current.state = MIGRATION_STATUS_NONE; - memset(&mis_current, 0, sizeof(MigrationIncomingState)); - mis_current.postcopy_remote_fds = g_array_new(FALSE, TRUE, - sizeof(struct PostCopyFD)); - qemu_mutex_init(&mis_current.rp_mutex); - qemu_event_init(&mis_current.main_thread_load_event, false); - - init_dirty_bitmap_incoming_migration(); - - once = true; - } - return &mis_current; + assert(current_incoming); + return current_incoming; } void migration_incoming_state_destroy(void) @@ -197,6 +207,16 @@ static void migrate_generate_event(int new_state) } } +static bool migrate_late_block_activate(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[ + MIGRATION_CAPABILITY_LATE_BLOCK_ACTIVATE]; +} + /* * Called on -incoming with a defer: uri. * The migration can be started later after any parameters have been @@ -306,13 +326,23 @@ static void process_incoming_migration_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; - /* Make sure all file formats flush their mutable metadata. - * If we get an error here, just don't restart the VM yet. */ - bdrv_invalidate_cache_all(&local_err); - if (local_err) { - error_report_err(local_err); - local_err = NULL; - autostart = false; + /* If capability late_block_activate is set: + * Only fire up the block code now if we're going to restart the + * VM, else 'cont' will do it. + * This causes file locking to happen; so we don't want it to happen + * unless we really are starting the VM. + */ + if (!migrate_late_block_activate() || + (autostart && (!global_state_received() || + global_state_get_runstate() == RUN_STATE_RUNNING))) { + /* Make sure all file formats flush their mutable metadata. + * If we get an error here, just don't restart the VM yet. */ + bdrv_invalidate_cache_all(&local_err); + if (local_err) { + error_report_err(local_err); + local_err = NULL; + autostart = false; + } } /* @@ -430,14 +460,50 @@ static void migration_incoming_setup(QEMUFile *f) qemu_file_set_blocking(f, false); } -static void migration_incoming_process(void) +void migration_incoming_process(void) { Coroutine *co = qemu_coroutine_create(process_incoming_migration_co, NULL); qemu_coroutine_enter(co); } +/* Returns true if recovered from a paused migration, otherwise false */ +static bool postcopy_try_recover(QEMUFile *f) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + + if (mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + /* Resumed from a paused postcopy migration */ + + mis->from_src_file = f; + /* Postcopy has standalone thread to do vm load */ + qemu_file_set_blocking(f, true); + + /* Re-configure the return path */ + mis->to_src_file = qemu_file_get_return_path(f); + + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_PAUSED, + MIGRATION_STATUS_POSTCOPY_RECOVER); + + /* + * Here, we only wake up the main loading thread (while the + * fault thread will still be waiting), so that we can receive + * commands from source now, and answer it if needed. The + * fault thread will be woken up afterwards until we are sure + * that source is ready to reply to page requests. + */ + qemu_sem_post(&mis->postcopy_pause_sem_dst); + return true; + } + + return false; +} + void migration_fd_process_incoming(QEMUFile *f) { + if (postcopy_try_recover(f)) { + return; + } + migration_incoming_setup(f); migration_incoming_process(); } @@ -445,12 +511,33 @@ void migration_fd_process_incoming(QEMUFile *f) void migration_ioc_process_incoming(QIOChannel *ioc) { MigrationIncomingState *mis = migration_incoming_get_current(); + bool start_migration; if (!mis->from_src_file) { + /* The first connection (multifd may have multiple) */ QEMUFile *f = qemu_fopen_channel_input(ioc); - migration_fd_process_incoming(f); + + /* If it's a recovery, we're done */ + if (postcopy_try_recover(f)) { + return; + } + + migration_incoming_setup(f); + + /* + * Common migration only needs one channel, so we can start + * right now. Multifd needs more than one channel, we wait. + */ + start_migration = !migrate_use_multifd(); + } else { + /* Multiple connections */ + assert(migrate_use_multifd()); + start_migration = multifd_recv_new_channel(ioc); + } + + if (start_migration) { + migration_incoming_process(); } - /* We still only have a single channel. Nothing to do here yet */ } /** @@ -461,7 +548,12 @@ void migration_ioc_process_incoming(QIOChannel *ioc) */ bool migration_has_all_channels(void) { - return true; + MigrationIncomingState *mis = migration_incoming_get_current(); + bool all_channels; + + all_channels = multifd_recv_all_channels_created(); + + return all_channels && mis->from_src_file != NULL; } /* @@ -491,6 +583,53 @@ void migrate_send_rp_pong(MigrationIncomingState *mis, migrate_send_rp_message(mis, MIG_RP_MSG_PONG, sizeof(buf), &buf); } +void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis, + char *block_name) +{ + char buf[512]; + int len; + int64_t res; + + /* + * First, we send the header part. It contains only the len of + * idstr, and the idstr itself. + */ + len = strlen(block_name); + buf[0] = len; + memcpy(buf + 1, block_name, len); + + if (mis->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { + error_report("%s: MSG_RP_RECV_BITMAP only used for recovery", + __func__); + return; + } + + migrate_send_rp_message(mis, MIG_RP_MSG_RECV_BITMAP, len + 1, buf); + + /* + * Next, we dump the received bitmap to the stream. + * + * TODO: currently we are safe since we are the only one that is + * using the to_src_file handle (fault thread is still paused), + * and it's ok even not taking the mutex. However the best way is + * to take the lock before sending the message header, and release + * the lock after sending the bitmap. + */ + qemu_mutex_lock(&mis->rp_mutex); + res = ramblock_recv_bitmap_send(mis->to_src_file, block_name); + qemu_mutex_unlock(&mis->rp_mutex); + + trace_migrate_send_rp_recv_bitmap(block_name, res); +} + +void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value) +{ + uint32_t buf; + + buf = cpu_to_be32(value); + migrate_send_rp_message(mis, MIG_RP_MSG_RESUME_ACK, sizeof(buf), &buf); +} + MigrationCapabilityStatusList *qmp_query_migrate_capabilities(Error **errp) { MigrationCapabilityStatusList *head = NULL; @@ -556,6 +695,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->x_multifd_page_count = s->parameters.x_multifd_page_count; params->has_xbzrle_cache_size = true; params->xbzrle_cache_size = s->parameters.xbzrle_cache_size; + params->has_max_postcopy_bandwidth = true; + params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth; return params; } @@ -569,6 +710,8 @@ static bool migration_is_setup_or_active(int state) switch (state) { case MIGRATION_STATUS_ACTIVE: case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER: case MIGRATION_STATUS_SETUP: case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: @@ -596,6 +739,7 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->ram->dirty_sync_count = ram_counters.dirty_sync_count; info->ram->postcopy_requests = ram_counters.postcopy_requests; info->ram->page_size = qemu_target_page_size(); + info->ram->multifd_bytes = ram_counters.multifd_bytes; if (migrate_use_xbzrle()) { info->has_xbzrle_cache = true; @@ -630,14 +774,15 @@ static void populate_disk_info(MigrationInfo *info) } } -MigrationInfo *qmp_query_migrate(Error **errp) +static void fill_source_migration_info(MigrationInfo *info) { - MigrationInfo *info = g_malloc0(sizeof(*info)); MigrationState *s = migrate_get_current(); switch (s->state) { case MIGRATION_STATUS_NONE: /* no migration has happened ever */ + /* do not overwrite destination migration status */ + return; break; case MIGRATION_STATUS_SETUP: info->has_status = true; @@ -648,6 +793,8 @@ MigrationInfo *qmp_query_migrate(Error **errp) case MIGRATION_STATUS_POSTCOPY_ACTIVE: case MIGRATION_STATUS_PRE_SWITCHOVER: case MIGRATION_STATUS_DEVICE: + case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER: /* TODO add some postcopy stats */ info->has_status = true; info->has_total_time = true; @@ -688,8 +835,6 @@ MigrationInfo *qmp_query_migrate(Error **errp) break; } info->status = s->state; - - return info; } /** @@ -753,6 +898,43 @@ static bool migrate_caps_check(bool *cap_list, return true; } +static void fill_destination_migration_info(MigrationInfo *info) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + + switch (mis->state) { + case MIGRATION_STATUS_NONE: + return; + break; + case MIGRATION_STATUS_SETUP: + case MIGRATION_STATUS_CANCELLING: + case MIGRATION_STATUS_CANCELLED: + case MIGRATION_STATUS_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER: + case MIGRATION_STATUS_FAILED: + case MIGRATION_STATUS_COLO: + info->has_status = true; + break; + case MIGRATION_STATUS_COMPLETED: + info->has_status = true; + fill_destination_postcopy_migration_info(info); + break; + } + info->status = mis->state; +} + +MigrationInfo *qmp_query_migrate(Error **errp) +{ + MigrationInfo *info = g_malloc0(sizeof(*info)); + + fill_destination_migration_info(info); + fill_source_migration_info(info); + + return info; +} + void qmp_migrate_set_capabilities(MigrationCapabilityStatusList *params, Error **errp) { @@ -925,6 +1107,9 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_xbzrle_cache_size) { dest->xbzrle_cache_size = params->xbzrle_cache_size; } + if (params->has_max_postcopy_bandwidth) { + dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth; + } } static void migrate_params_apply(MigrateSetParameters *params, Error **errp) @@ -997,6 +1182,9 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) s->parameters.xbzrle_cache_size = params->xbzrle_cache_size; xbzrle_cache_resize(params->xbzrle_cache_size, errp); } + if (params->has_max_postcopy_bandwidth) { + s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth; + } } void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) @@ -1006,14 +1194,14 @@ void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) /* TODO Rewrite "" to null instead */ if (params->has_tls_creds && params->tls_creds->type == QTYPE_QNULL) { - QDECREF(params->tls_creds->u.n); + qobject_unref(params->tls_creds->u.n); params->tls_creds->type = QTYPE_QSTRING; params->tls_creds->u.s = strdup(""); } /* TODO Rewrite "" to null instead */ if (params->has_tls_hostname && params->tls_hostname->type == QTYPE_QNULL) { - QDECREF(params->tls_hostname->u.n); + qobject_unref(params->tls_hostname->u.n); params->tls_hostname->type = QTYPE_QSTRING; params->tls_hostname->u.s = strdup(""); } @@ -1113,6 +1301,7 @@ static void migrate_fd_cleanup(void *opaque) if (s->to_dst_file) { Error *local_err = NULL; + QEMUFile *tmp; trace_migrate_fd_cleanup(); qemu_mutex_unlock_iothread(); @@ -1125,8 +1314,15 @@ static void migrate_fd_cleanup(void *opaque) if (multifd_save_cleanup(&local_err) != 0) { error_report_err(local_err); } - qemu_fclose(s->to_dst_file); + qemu_mutex_lock(&s->qemu_file_lock); + tmp = s->to_dst_file; s->to_dst_file = NULL; + qemu_mutex_unlock(&s->qemu_file_lock); + /* + * Close the file handle without the lock to make sure the + * critical section won't block for long. + */ + qemu_fclose(tmp); } assert((s->state != MIGRATION_STATUS_ACTIVE) && @@ -1354,6 +1550,59 @@ void qmp_migrate_incoming(const char *uri, Error **errp) once = false; } +void qmp_migrate_recover(const char *uri, Error **errp) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + + if (mis->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { + error_setg(errp, "Migrate recover can only be run " + "when postcopy is paused."); + return; + } + + if (atomic_cmpxchg(&mis->postcopy_recover_triggered, + false, true) == true) { + error_setg(errp, "Migrate recovery is triggered already"); + return; + } + + /* + * Note that this call will never start a real migration; it will + * only re-setup the migration stream and poke existing migration + * to continue using that newly established channel. + */ + qemu_start_incoming_migration(uri, errp); +} + +void qmp_migrate_pause(Error **errp) +{ + MigrationState *ms = migrate_get_current(); + MigrationIncomingState *mis = migration_incoming_get_current(); + int ret; + + if (ms->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + /* Source side, during postcopy */ + qemu_mutex_lock(&ms->qemu_file_lock); + ret = qemu_file_shutdown(ms->to_dst_file); + qemu_mutex_unlock(&ms->qemu_file_lock); + if (ret) { + error_setg(errp, "Failed to pause source migration"); + } + return; + } + + if (mis->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + ret = qemu_file_shutdown(mis->from_src_file); + if (ret) { + error_setg(errp, "Failed to pause destination migration"); + } + return; + } + + error_setg(errp, "migrate-pause is currently only supported " + "during postcopy-active state"); +} + bool migration_is_blocked(Error **errp) { if (qemu_savevm_state_blocked(errp)) { @@ -1368,49 +1617,94 @@ bool migration_is_blocked(Error **errp) return false; } -void qmp_migrate(const char *uri, bool has_blk, bool blk, - bool has_inc, bool inc, bool has_detach, bool detach, - Error **errp) +/* Returns true if continue to migrate, or false if error detected */ +static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, + bool resume, Error **errp) { Error *local_err = NULL; - MigrationState *s = migrate_get_current(); - const char *p; + + if (resume) { + if (s->state != MIGRATION_STATUS_POSTCOPY_PAUSED) { + error_setg(errp, "Cannot resume if there is no " + "paused migration"); + return false; + } + + /* + * Postcopy recovery won't work well with release-ram + * capability since release-ram will drop the page buffer as + * long as the page is put into the send buffer. So if there + * is a network failure happened, any page buffers that have + * not yet reached the destination VM but have already been + * sent from the source VM will be lost forever. Let's refuse + * the client from resuming such a postcopy migration. + * Luckily release-ram was designed to only be used when src + * and destination VMs are on the same host, so it should be + * fine. + */ + if (migrate_release_ram()) { + error_setg(errp, "Postcopy recovery cannot work " + "when release-ram capability is set"); + return false; + } + + /* This is a resume, skip init status */ + return true; + } if (migration_is_setup_or_active(s->state) || s->state == MIGRATION_STATUS_CANCELLING || s->state == MIGRATION_STATUS_COLO) { error_setg(errp, QERR_MIGRATION_ACTIVE); - return; + return false; } + if (runstate_check(RUN_STATE_INMIGRATE)) { error_setg(errp, "Guest is waiting for an incoming migration"); - return; + return false; } if (migration_is_blocked(errp)) { - return; + return false; } - if ((has_blk && blk) || (has_inc && inc)) { + if (blk || blk_inc) { if (migrate_use_block() || migrate_use_block_incremental()) { error_setg(errp, "Command options are incompatible with " "current migration capabilities"); - return; + return false; } migrate_set_block_enabled(true, &local_err); if (local_err) { error_propagate(errp, local_err); - return; + return false; } s->must_remove_block_options = true; } - if (has_inc && inc) { + if (blk_inc) { migrate_set_block_incremental(s, true); } migrate_init(s); + return true; +} + +void qmp_migrate(const char *uri, bool has_blk, bool blk, + bool has_inc, bool inc, bool has_detach, bool detach, + bool has_resume, bool resume, Error **errp) +{ + Error *local_err = NULL; + MigrationState *s = migrate_get_current(); + const char *p; + + if (!migrate_prepare(s, has_blk && blk, has_inc && inc, + has_resume && resume, errp)) { + /* Error detected, put into errp */ + return; + } + if (strstart(uri, "tcp:", &p)) { tcp_start_outgoing_migration(s, p, &local_err); #ifdef CONFIG_RDMA @@ -1541,6 +1835,15 @@ bool migrate_zero_blocks(void) return s->enabled_capabilities[MIGRATION_CAPABILITY_ZERO_BLOCKS]; } +bool migrate_postcopy_blocktime(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME]; +} + bool migrate_use_compression(void) { MigrationState *s; @@ -1650,6 +1953,16 @@ int64_t migrate_xbzrle_cache_size(void) return s->parameters.xbzrle_cache_size; } +static int64_t migrate_max_postcopy_bandwidth(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->parameters.max_postcopy_bandwidth; +} + + bool migrate_use_block(void) { MigrationState *s; @@ -1696,6 +2009,8 @@ static struct rp_cmd_args { [MIG_RP_MSG_PONG] = { .len = 4, .name = "PONG" }, [MIG_RP_MSG_REQ_PAGES] = { .len = 12, .name = "REQ_PAGES" }, [MIG_RP_MSG_REQ_PAGES_ID] = { .len = -1, .name = "REQ_PAGES_ID" }, + [MIG_RP_MSG_RECV_BITMAP] = { .len = -1, .name = "RECV_BITMAP" }, + [MIG_RP_MSG_RESUME_ACK] = { .len = 4, .name = "RESUME_ACK" }, [MIG_RP_MSG_MAX] = { .len = -1, .name = "MAX" }, }; @@ -1728,6 +2043,51 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, } } +/* Return true to retry, false to quit */ +static bool postcopy_pause_return_path_thread(MigrationState *s) +{ + trace_postcopy_pause_return_path(); + + qemu_sem_wait(&s->postcopy_pause_rp_sem); + + trace_postcopy_pause_return_path_continued(); + + return true; +} + +static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name) +{ + RAMBlock *block = qemu_ram_block_by_name(block_name); + + if (!block) { + error_report("%s: invalid block name '%s'", __func__, block_name); + return -EINVAL; + } + + /* Fetch the received bitmap and refresh the dirty bitmap */ + return ram_dirty_bitmap_reload(s, block); +} + +static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value) +{ + trace_source_return_path_thread_resume_ack(value); + + if (value != MIGRATION_RESUME_ACK_VALUE) { + error_report("%s: illegal resume_ack value %"PRIu32, + __func__, value); + return -1; + } + + /* Now both sides are active. */ + migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_RECOVER, + MIGRATION_STATUS_POSTCOPY_ACTIVE); + + /* Notify send thread that time to continue send pages */ + qemu_sem_post(&s->rp_state.rp_sem); + + return 0; +} + /* * Handles messages sent on the return path towards the source VM * @@ -1744,6 +2104,8 @@ static void *source_return_path_thread(void *opaque) int res; trace_source_return_path_thread_entry(); + +retry: while (!ms->rp_state.error && !qemu_file_get_error(rp) && migration_is_setup_or_active(ms->state)) { trace_source_return_path_thread_loop_top(); @@ -1831,23 +2193,61 @@ static void *source_return_path_thread(void *opaque) migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len); break; + case MIG_RP_MSG_RECV_BITMAP: + if (header_len < 1) { + error_report("%s: missing block name", __func__); + mark_source_rp_bad(ms); + goto out; + } + /* Format: len (1B) + idstr (<255B). This ends the idstr. */ + buf[buf[0] + 1] = '\0'; + if (migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1))) { + mark_source_rp_bad(ms); + goto out; + } + break; + + case MIG_RP_MSG_RESUME_ACK: + tmp32 = ldl_be_p(buf); + if (migrate_handle_rp_resume_ack(ms, tmp32)) { + mark_source_rp_bad(ms); + goto out; + } + break; + default: break; } } - if (qemu_file_get_error(rp)) { + +out: + res = qemu_file_get_error(rp); + if (res) { + if (res == -EIO) { + /* + * Maybe there is something we can do: it looks like a + * network down issue, and we pause for a recovery. + */ + if (postcopy_pause_return_path_thread(ms)) { + /* Reload rp, reset the rest */ + rp = ms->rp_state.from_dst_file; + ms->rp_state.error = false; + goto retry; + } + } + trace_source_return_path_thread_bad_end(); mark_source_rp_bad(ms); } trace_source_return_path_thread_end(); -out: ms->rp_state.from_dst_file = NULL; qemu_fclose(rp); return NULL; } -static int open_return_path_on_source(MigrationState *ms) +static int open_return_path_on_source(MigrationState *ms, + bool create_thread) { ms->rp_state.from_dst_file = qemu_file_get_return_path(ms->to_dst_file); @@ -1856,6 +2256,12 @@ static int open_return_path_on_source(MigrationState *ms) } trace_open_return_path_on_source(); + + if (!create_thread) { + /* We're done */ + return 0; + } + qemu_thread_create(&ms->rp_state.rp_thread, "return path", source_return_path_thread, ms, QEMU_THREAD_JOINABLE); @@ -1896,6 +2302,7 @@ static int postcopy_start(MigrationState *ms) QIOChannelBuffer *bioc; QEMUFile *fb; int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + int64_t bandwidth = migrate_max_postcopy_bandwidth(); bool restart_block = false; int cur_state = MIGRATION_STATUS_ACTIVE; if (!migrate_pause_before_switchover()) { @@ -1950,7 +2357,12 @@ static int postcopy_start(MigrationState *ms) * will notice we're in POSTCOPY_ACTIVE and not actually * wrap their state up here */ - qemu_file_set_rate_limit(ms->to_dst_file, INT64_MAX); + /* 0 max-postcopy-bandwidth means unlimited */ + if (!bandwidth) { + qemu_file_set_rate_limit(ms->to_dst_file, INT64_MAX); + } else { + qemu_file_set_rate_limit(ms->to_dst_file, bandwidth / XFER_LIMIT_RATIO); + } if (migrate_postcopy_ram()) { /* Ping just for debugging, helps line traces up */ qemu_savevm_send_ping(ms->to_dst_file, 2); @@ -2195,10 +2607,167 @@ bool migrate_colo_enabled(void) return s->enabled_capabilities[MIGRATION_CAPABILITY_X_COLO]; } +typedef enum MigThrError { + /* No error detected */ + MIG_THR_ERR_NONE = 0, + /* Detected error, but resumed successfully */ + MIG_THR_ERR_RECOVERED = 1, + /* Detected fatal error, need to exit */ + MIG_THR_ERR_FATAL = 2, +} MigThrError; + +static int postcopy_resume_handshake(MigrationState *s) +{ + qemu_savevm_send_postcopy_resume(s->to_dst_file); + + while (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { + qemu_sem_wait(&s->rp_state.rp_sem); + } + + if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + return 0; + } + + return -1; +} + +/* Return zero if success, or <0 for error */ +static int postcopy_do_resume(MigrationState *s) +{ + int ret; + + /* + * Call all the resume_prepare() hooks, so that modules can be + * ready for the migration resume. + */ + ret = qemu_savevm_state_resume_prepare(s); + if (ret) { + error_report("%s: resume_prepare() failure detected: %d", + __func__, ret); + return ret; + } + + /* + * Last handshake with destination on the resume (destination will + * switch to postcopy-active afterwards) + */ + ret = postcopy_resume_handshake(s); + if (ret) { + error_report("%s: handshake failed: %d", __func__, ret); + return ret; + } + + return 0; +} + +/* + * We don't return until we are in a safe state to continue current + * postcopy migration. Returns MIG_THR_ERR_RECOVERED if recovered, or + * MIG_THR_ERR_FATAL if unrecovery failure happened. + */ +static MigThrError postcopy_pause(MigrationState *s) +{ + assert(s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE); + + while (true) { + QEMUFile *file; + + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_POSTCOPY_PAUSED); + + /* Current channel is possibly broken. Release it. */ + assert(s->to_dst_file); + qemu_mutex_lock(&s->qemu_file_lock); + file = s->to_dst_file; + s->to_dst_file = NULL; + qemu_mutex_unlock(&s->qemu_file_lock); + + qemu_file_shutdown(file); + qemu_fclose(file); + + error_report("Detected IO failure for postcopy. " + "Migration paused."); + + /* + * We wait until things fixed up. Then someone will setup the + * status back for us. + */ + while (s->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + qemu_sem_wait(&s->postcopy_pause_sem); + } + + if (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { + /* Woken up by a recover procedure. Give it a shot */ + + /* + * Firstly, let's wake up the return path now, with a new + * return path channel. + */ + qemu_sem_post(&s->postcopy_pause_rp_sem); + + /* Do the resume logic */ + if (postcopy_do_resume(s) == 0) { + /* Let's continue! */ + trace_postcopy_pause_continued(); + return MIG_THR_ERR_RECOVERED; + } else { + /* + * Something wrong happened during the recovery, let's + * pause again. Pause is always better than throwing + * data away. + */ + continue; + } + } else { + /* This is not right... Time to quit. */ + return MIG_THR_ERR_FATAL; + } + } +} + +static MigThrError migration_detect_error(MigrationState *s) +{ + int ret; + + /* Try to detect any file errors */ + ret = qemu_file_get_error(s->to_dst_file); + + if (!ret) { + /* Everything is fine */ + return MIG_THR_ERR_NONE; + } + + if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) { + /* + * For postcopy, we allow the network to be down for a + * while. After that, it can be continued by a + * recovery phase. + */ + return postcopy_pause(s); + } else { + /* + * For precopy (or postcopy with error outside IO), we fail + * with no time. + */ + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + trace_migration_thread_file_err(); + + /* Time to stop the migration, now. */ + return MIG_THR_ERR_FATAL; + } +} + +/* How many bytes have we transferred since the beggining of the migration */ +static uint64_t migration_total_bytes(MigrationState *s) +{ + return qemu_ftell(s->to_dst_file) + ram_counters.multifd_bytes; +} + static void migration_calculate_complete(MigrationState *s) { - uint64_t bytes = qemu_ftell(s->to_dst_file); + uint64_t bytes = migration_total_bytes(s); int64_t end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + int64_t transfer_time; s->total_time = end_time - s->start_time; if (!s->downtime) { @@ -2209,8 +2778,9 @@ static void migration_calculate_complete(MigrationState *s) s->downtime = end_time - s->downtime_start; } - if (s->total_time) { - s->mbps = ((double) bytes * 8.0) / s->total_time / 1000; + transfer_time = s->total_time - s->setup_time; + if (transfer_time) { + s->mbps = ((double) bytes * 8.0) / transfer_time / 1000; } } @@ -2218,13 +2788,15 @@ static void migration_update_counters(MigrationState *s, int64_t current_time) { uint64_t transferred, time_spent; + uint64_t current_bytes; /* bytes transferred since the beginning */ double bandwidth; if (current_time < s->iteration_start_time + BUFFER_DELAY) { return; } - transferred = qemu_ftell(s->to_dst_file) - s->iteration_initial_bytes; + current_bytes = migration_total_bytes(s); + transferred = current_bytes - s->iteration_initial_bytes; time_spent = current_time - s->iteration_start_time; bandwidth = (double)transferred / time_spent; s->threshold_size = bandwidth * s->parameters.downtime_limit; @@ -2237,14 +2809,13 @@ static void migration_update_counters(MigrationState *s, * recalculate. 10000 is a small enough number for our purposes */ if (ram_counters.dirty_pages_rate && transferred > 10000) { - s->expected_downtime = ram_counters.dirty_pages_rate * - qemu_target_page_size() / bandwidth; + s->expected_downtime = ram_counters.remaining / bandwidth; } qemu_file_reset_rate_limit(s->to_dst_file); s->iteration_start_time = current_time; - s->iteration_initial_bytes = qemu_ftell(s->to_dst_file); + s->iteration_initial_bytes = current_bytes; trace_migrate_transferred(transferred, time_spent, bandwidth, s->threshold_size); @@ -2325,6 +2896,7 @@ static void migration_iteration_finish(MigrationState *s) /* Fallthrough */ case MIGRATION_STATUS_FAILED: case MIGRATION_STATUS_CANCELLED: + case MIGRATION_STATUS_CANCELLING: if (s->vm_was_running) { vm_start(); } else { @@ -2343,6 +2915,16 @@ static void migration_iteration_finish(MigrationState *s) qemu_mutex_unlock_iothread(); } +void migration_make_urgent_request(void) +{ + qemu_sem_post(&migrate_get_current()->rate_limit_sem); +} + +void migration_consume_urgent_request(void) +{ + qemu_sem_wait(&migrate_get_current()->rate_limit_sem); +} + /* * Master migration thread on the source VM. * It drives the migration and pumps the data down the outgoing channel. @@ -2351,6 +2933,8 @@ static void *migration_thread(void *opaque) { MigrationState *s = opaque; int64_t setup_start = qemu_clock_get_ms(QEMU_CLOCK_HOST); + MigThrError thr_error; + bool urgent = false; rcu_register_thread(); @@ -2391,7 +2975,7 @@ static void *migration_thread(void *opaque) s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { int64_t current_time; - if (!qemu_file_rate_limit(s->to_dst_file)) { + if (urgent || !qemu_file_rate_limit(s->to_dst_file)) { MigIterateState iter_state = migration_iteration_run(s); if (iter_state == MIG_ITERATE_SKIP) { continue; @@ -2400,23 +2984,46 @@ static void *migration_thread(void *opaque) } } - if (qemu_file_get_error(s->to_dst_file)) { - if (migration_is_setup_or_active(s->state)) { - migrate_set_state(&s->state, s->state, - MIGRATION_STATUS_FAILED); - } - trace_migration_thread_file_err(); + /* + * Try to detect any kind of failures, and see whether we + * should stop the migration now. + */ + thr_error = migration_detect_error(s); + if (thr_error == MIG_THR_ERR_FATAL) { + /* Stop migration */ break; + } else if (thr_error == MIG_THR_ERR_RECOVERED) { + /* + * Just recovered from a e.g. network failure, reset all + * the local variables. This is important to avoid + * breaking transferred_bytes and bandwidth calculation + */ + s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + s->iteration_initial_bytes = 0; } current_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); migration_update_counters(s, current_time); + urgent = false; if (qemu_file_rate_limit(s->to_dst_file)) { - /* usleep expects microseconds */ - g_usleep((s->iteration_start_time + BUFFER_DELAY - - current_time) * 1000); + /* Wait for a delay to do rate limiting OR + * something urgent to post the semaphore. + */ + int ms = s->iteration_start_time + BUFFER_DELAY - current_time; + trace_migration_thread_ratelimit_pre(ms); + if (qemu_sem_timedwait(&s->rate_limit_sem, ms) == 0) { + /* We were worken by one or more urgent things but + * the timedwait will have consumed one of them. + * The service routine for the urgent wake will dec + * the semaphore itself for each item it consumes, + * so add this one we just eat back. + */ + qemu_sem_post(&s->rate_limit_sem); + urgent = true; + } + trace_migration_thread_ratelimit_post(urgent); } } @@ -2428,6 +3035,9 @@ static void *migration_thread(void *opaque) void migrate_fd_connect(MigrationState *s, Error *error_in) { + int64_t rate_limit; + bool resume = s->state == MIGRATION_STATUS_POSTCOPY_PAUSED; + s->expected_downtime = s->parameters.downtime_limit; s->cleanup_bh = qemu_bh_new(migrate_fd_cleanup, s); if (error_in) { @@ -2436,12 +3046,19 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) return; } - qemu_file_set_blocking(s->to_dst_file, true); - qemu_file_set_rate_limit(s->to_dst_file, - s->parameters.max_bandwidth / XFER_LIMIT_RATIO); + if (resume) { + /* This is a resumed migration */ + rate_limit = INT64_MAX; + } else { + /* This is a fresh new migration */ + rate_limit = s->parameters.max_bandwidth / XFER_LIMIT_RATIO; - /* Notify before starting migration thread */ - notifier_list_notify(&migration_state_notifiers, s); + /* Notify before starting migration thread */ + notifier_list_notify(&migration_state_notifiers, s); + } + + qemu_file_set_rate_limit(s->to_dst_file, rate_limit); + qemu_file_set_blocking(s->to_dst_file, true); /* * Open the return path. For postcopy, it is used exclusively. For @@ -2449,15 +3066,22 @@ void migrate_fd_connect(MigrationState *s, Error *error_in) * QEMU uses the return path. */ if (migrate_postcopy_ram() || migrate_use_return_path()) { - if (open_return_path_on_source(s)) { + if (open_return_path_on_source(s, !resume)) { error_report("Unable to open return-path for postcopy"); - migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, - MIGRATION_STATUS_FAILED); + migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); migrate_fd_cleanup(s); return; } } + if (resume) { + /* Wakeup the main migration thread to do the recovery */ + migrate_set_state(&s->state, MIGRATION_STATUS_POSTCOPY_PAUSED, + MIGRATION_STATUS_POSTCOPY_RECOVER); + qemu_sem_post(&s->postcopy_pause_sem); + return; + } + if (multifd_save_setup() != 0) { migrate_set_state(&s->state, MIGRATION_STATUS_SETUP, MIGRATION_STATUS_FAILED); @@ -2482,6 +3106,8 @@ void migration_global_dump(Monitor *mon) ms->send_configuration ? "on" : "off"); monitor_printf(mon, "send-section-footer: %s\n", ms->send_section_footer ? "on" : "off"); + monitor_printf(mon, "decompress-error-check: %s\n", + ms->decompress_error_check ? "on" : "off"); } #define DEFINE_PROP_MIG_CAP(name, x) \ @@ -2495,6 +3121,8 @@ static Property migration_properties[] = { send_configuration, true), DEFINE_PROP_BOOL("send-section-footer", MigrationState, send_section_footer, true), + DEFINE_PROP_BOOL("decompress-error-check", MigrationState, + decompress_error_check, true), /* Migration parameters */ DEFINE_PROP_UINT8("x-compress-level", MigrationState, @@ -2529,6 +3157,9 @@ static Property migration_properties[] = { DEFINE_PROP_SIZE("xbzrle-cache-size", MigrationState, parameters.xbzrle_cache_size, DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE), + DEFINE_PROP_SIZE("max-postcopy-bandwidth", MigrationState, + parameters.max_postcopy_bandwidth, + DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH), /* Migration capabilities */ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), @@ -2561,9 +3192,14 @@ static void migration_instance_finalize(Object *obj) MigrationParameters *params = &ms->parameters; qemu_mutex_destroy(&ms->error_mutex); + qemu_mutex_destroy(&ms->qemu_file_lock); g_free(params->tls_hostname); g_free(params->tls_creds); + qemu_sem_destroy(&ms->rate_limit_sem); qemu_sem_destroy(&ms->pause_sem); + qemu_sem_destroy(&ms->postcopy_pause_sem); + qemu_sem_destroy(&ms->postcopy_pause_rp_sem); + qemu_sem_destroy(&ms->rp_state.rp_sem); error_free(ms->error); } @@ -2593,6 +3229,13 @@ static void migration_instance_init(Object *obj) params->has_x_multifd_channels = true; params->has_x_multifd_page_count = true; params->has_xbzrle_cache_size = true; + params->has_max_postcopy_bandwidth = true; + + qemu_sem_init(&ms->postcopy_pause_sem, 0); + qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); + qemu_sem_init(&ms->rp_state.rp_sem, 0); + qemu_sem_init(&ms->rate_limit_sem, 0); + qemu_mutex_init(&ms->qemu_file_lock); } /* diff --git a/migration/migration.h b/migration/migration.h index 8d2f320c48..64a7b33735 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -22,6 +22,10 @@ #include "hw/qdev.h" #include "io/channel.h" +struct PostcopyBlocktimeContext; + +#define MIGRATION_RESUME_ACK_VALUE (1) + /* State for the incoming migration */ struct MigrationIncomingState { QEMUFile *from_src_file; @@ -65,10 +69,25 @@ struct MigrationIncomingState { /* The coroutine we should enter (back) after failover */ Coroutine *migration_incoming_co; QemuSemaphore colo_incoming_sem; + + /* + * PostcopyBlocktimeContext to keep information for postcopy + * live migration, to calculate vCPU block time + * */ + struct PostcopyBlocktimeContext *blocktime_ctx; + + /* notify PAUSED postcopy incoming migrations to try to continue */ + bool postcopy_recover_triggered; + QemuSemaphore postcopy_pause_sem_dst; + QemuSemaphore postcopy_pause_sem_fault; }; MigrationIncomingState *migration_incoming_get_current(void); void migration_incoming_state_destroy(void); +/* + * Functions to work with blocktime context + */ +void fill_destination_postcopy_migration_info(MigrationInfo *info); #define TYPE_MIGRATION "migration" @@ -95,6 +114,17 @@ struct MigrationState QemuThread thread; QEMUBH *cleanup_bh; QEMUFile *to_dst_file; + /* + * Protects to_dst_file pointer. We need to make sure we won't + * yield or hang during the critical section, since this lock will + * be used in OOB command handler. + */ + QemuMutex qemu_file_lock; + + /* + * Used to allow urgent requests to override rate limiting. + */ + QemuSemaphore rate_limit_sem; /* bytes already send at the beggining of current interation */ uint64_t iteration_initial_bytes; @@ -117,6 +147,7 @@ struct MigrationState QEMUFile *from_dst_file; QemuThread rp_thread; bool error; + QemuSemaphore rp_sem; } rp_state; double mbps; @@ -182,12 +213,24 @@ struct MigrationState bool send_configuration; /* Whether we send section footer during migration */ bool send_section_footer; + + /* Needed by postcopy-pause state */ + QemuSemaphore postcopy_pause_sem; + QemuSemaphore postcopy_pause_rp_sem; + /* + * Whether we abort the migration if decompression errors are + * detected at the destination. It is left at false for qemu + * older than 3.0, since only newer qemu sends streams that + * do not trigger spurious decompression errors. + */ + bool decompress_error_check; }; void migrate_set_state(int *state, int old_state, int new_state); void migration_fd_process_incoming(QEMUFile *f); void migration_ioc_process_incoming(QIOChannel *ioc); +void migration_incoming_process(void); bool migration_has_all_channels(void); @@ -230,6 +273,7 @@ int migrate_compress_level(void); int migrate_compress_threads(void); int migrate_decompress_threads(void); bool migrate_use_events(void); +bool migrate_postcopy_blocktime(void); /* Sending on the return path - generic and then for each message type */ void migrate_send_rp_shut(MigrationIncomingState *mis, @@ -238,8 +282,17 @@ void migrate_send_rp_pong(MigrationIncomingState *mis, uint32_t value); int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char* rbname, ram_addr_t start, size_t len); +void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis, + char *block_name); +void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value); void dirty_bitmap_mig_before_vm_start(void); void init_dirty_bitmap_incoming_migration(void); +#define qemu_ram_foreach_block \ + #warning "Use qemu_ram_foreach_block_migratable in migration code" + +void migration_make_urgent_request(void); +void migration_consume_urgent_request(void); + #endif diff --git a/migration/page_cache.c b/migration/page_cache.c index 96268c3aea..acc252b100 100644 --- a/migration/page_cache.c +++ b/migration/page_cache.c @@ -18,7 +18,7 @@ #include "qapi/error.h" #include "qemu-common.h" #include "qemu/host-utils.h" -#include "migration/page_cache.h" +#include "page_cache.h" #ifdef DEBUG_CACHE #define DPRINTF(fmt, ...) \ diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 4a0b33b373..932f188949 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -90,6 +90,103 @@ int postcopy_notify(enum PostcopyNotifyReason reason, Error **errp) #include #include +typedef struct PostcopyBlocktimeContext { + /* time when page fault initiated per vCPU */ + uint32_t *page_fault_vcpu_time; + /* page address per vCPU */ + uintptr_t *vcpu_addr; + uint32_t total_blocktime; + /* blocktime per vCPU */ + uint32_t *vcpu_blocktime; + /* point in time when last page fault was initiated */ + uint32_t last_begin; + /* number of vCPU are suspended */ + int smp_cpus_down; + uint64_t start_time; + + /* + * Handler for exit event, necessary for + * releasing whole blocktime_ctx + */ + Notifier exit_notifier; +} PostcopyBlocktimeContext; + +static void destroy_blocktime_context(struct PostcopyBlocktimeContext *ctx) +{ + g_free(ctx->page_fault_vcpu_time); + g_free(ctx->vcpu_addr); + g_free(ctx->vcpu_blocktime); + g_free(ctx); +} + +static void migration_exit_cb(Notifier *n, void *data) +{ + PostcopyBlocktimeContext *ctx = container_of(n, PostcopyBlocktimeContext, + exit_notifier); + destroy_blocktime_context(ctx); +} + +static struct PostcopyBlocktimeContext *blocktime_context_new(void) +{ + PostcopyBlocktimeContext *ctx = g_new0(PostcopyBlocktimeContext, 1); + ctx->page_fault_vcpu_time = g_new0(uint32_t, smp_cpus); + ctx->vcpu_addr = g_new0(uintptr_t, smp_cpus); + ctx->vcpu_blocktime = g_new0(uint32_t, smp_cpus); + + ctx->exit_notifier.notify = migration_exit_cb; + ctx->start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + qemu_add_exit_notifier(&ctx->exit_notifier); + return ctx; +} + +static uint32List *get_vcpu_blocktime_list(PostcopyBlocktimeContext *ctx) +{ + uint32List *list = NULL, *entry = NULL; + int i; + + for (i = smp_cpus - 1; i >= 0; i--) { + entry = g_new0(uint32List, 1); + entry->value = ctx->vcpu_blocktime[i]; + entry->next = list; + list = entry; + } + + return list; +} + +/* + * This function just populates MigrationInfo from postcopy's + * blocktime context. It will not populate MigrationInfo, + * unless postcopy-blocktime capability was set. + * + * @info: pointer to MigrationInfo to populate + */ +void fill_destination_postcopy_migration_info(MigrationInfo *info) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + PostcopyBlocktimeContext *bc = mis->blocktime_ctx; + + if (!bc) { + return; + } + + info->has_postcopy_blocktime = true; + info->postcopy_blocktime = bc->total_blocktime; + info->has_postcopy_vcpu_blocktime = true; + info->postcopy_vcpu_blocktime = get_vcpu_blocktime_list(bc); +} + +static uint32_t get_postcopy_total_blocktime(void) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + PostcopyBlocktimeContext *bc = mis->blocktime_ctx; + + if (!bc) { + return 0; + } + + return bc->total_blocktime; +} /** * receive_ufd_features: check userfault fd features, to request only supported @@ -182,6 +279,19 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) } } +#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(); + } + + asked_features |= UFFD_FEATURE_THREAD_ID; + } +#endif + /* * request features, even if asked_features is 0, due to * kernel expects UFFD_API before UFFDIO_REGISTER, per @@ -264,7 +374,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) } /* We don't support postcopy with shared RAM yet */ - if (qemu_ram_foreach_block(test_ramblock_postcopiable, NULL)) { + if (qemu_ram_foreach_migratable_block(test_ramblock_postcopiable, NULL)) { goto out; } @@ -390,9 +500,9 @@ static int cleanup_range(const char *block_name, void *host_addr, * postcopy later; must be called prior to any precopy. * called from arch_init's similarly named ram_postcopy_incoming_init */ -int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) +int postcopy_ram_incoming_init(MigrationIncomingState *mis) { - if (qemu_ram_foreach_block(init_range, NULL)) { + if (qemu_ram_foreach_migratable_block(init_range, NULL)) { return -1; } @@ -414,7 +524,7 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) return -1; } - if (qemu_ram_foreach_block(cleanup_range, mis)) { + if (qemu_ram_foreach_migratable_block(cleanup_range, mis)) { return -1; } /* Let the fault thread quit */ @@ -451,6 +561,9 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) munmap(mis->postcopy_tmp_zero_page, mis->largest_page_size); mis->postcopy_tmp_zero_page = NULL; } + trace_postcopy_ram_incoming_cleanup_blocktime( + get_postcopy_total_blocktime()); + trace_postcopy_ram_incoming_cleanup_exit(); return 0; } @@ -480,7 +593,7 @@ static int nhp_range(const char *block_name, void *host_addr, */ int postcopy_ram_prepare_discard(MigrationIncomingState *mis) { - if (qemu_ram_foreach_block(nhp_range, mis)) { + if (qemu_ram_foreach_migratable_block(nhp_range, mis)) { return -1; } @@ -491,7 +604,7 @@ int postcopy_ram_prepare_discard(MigrationIncomingState *mis) /* * Mark the given area of RAM as requiring notification to unwritten areas - * Used as a callback on qemu_ram_foreach_block. + * Used as a callback on qemu_ram_foreach_migratable_block. * host_addr: Base of area to mark * offset: Offset in the whole ram arena * length: Length of the section @@ -575,6 +688,159 @@ int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, return 0; } +static int get_mem_fault_cpu_index(uint32_t pid) +{ + CPUState *cpu_iter; + + CPU_FOREACH(cpu_iter) { + if (cpu_iter->thread_id == pid) { + trace_get_mem_fault_cpu_index(cpu_iter->cpu_index, pid); + return cpu_iter->cpu_index; + } + } + trace_get_mem_fault_cpu_index(-1, pid); + return -1; +} + +static uint32_t get_low_time_offset(PostcopyBlocktimeContext *dc) +{ + int64_t start_time_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - + dc->start_time; + return start_time_offset < 1 ? 1 : start_time_offset & UINT32_MAX; +} + +/* + * This function is being called when pagefault occurs. It + * tracks down vCPU blocking time. + * + * @addr: faulted host virtual address + * @ptid: faulted process thread id + * @rb: ramblock appropriate to addr + */ +static void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, + RAMBlock *rb) +{ + int cpu, already_received; + MigrationIncomingState *mis = migration_incoming_get_current(); + PostcopyBlocktimeContext *dc = mis->blocktime_ctx; + uint32_t low_time_offset; + + if (!dc || ptid == 0) { + return; + } + cpu = get_mem_fault_cpu_index(ptid); + if (cpu < 0) { + return; + } + + low_time_offset = get_low_time_offset(dc); + if (dc->vcpu_addr[cpu] == 0) { + atomic_inc(&dc->smp_cpus_down); + } + + atomic_xchg(&dc->last_begin, low_time_offset); + atomic_xchg(&dc->page_fault_vcpu_time[cpu], low_time_offset); + atomic_xchg(&dc->vcpu_addr[cpu], addr); + + /* check it here, not at the begining of the function, + * due to, check could accur early than bitmap_set in + * qemu_ufd_copy_ioctl */ + already_received = ramblock_recv_bitmap_test(rb, (void *)addr); + if (already_received) { + atomic_xchg(&dc->vcpu_addr[cpu], 0); + atomic_xchg(&dc->page_fault_vcpu_time[cpu], 0); + atomic_dec(&dc->smp_cpus_down); + } + trace_mark_postcopy_blocktime_begin(addr, dc, dc->page_fault_vcpu_time[cpu], + cpu, already_received); +} + +/* + * This function just provide calculated blocktime per cpu and trace it. + * Total blocktime is calculated in mark_postcopy_blocktime_end. + * + * + * Assume we have 3 CPU + * + * S1 E1 S1 E1 + * -----***********------------xxx***************------------------------> CPU1 + * + * S2 E2 + * ------------****************xxx---------------------------------------> CPU2 + * + * S3 E3 + * ------------------------****xxx********-------------------------------> CPU3 + * + * We have sequence S1,S2,E1,S3,S1,E2,E3,E1 + * S2,E1 - doesn't match condition due to sequence S1,S2,E1 doesn't include CPU3 + * S3,S1,E2 - sequence includes all CPUs, in this case overlap will be S1,E2 - + * it's a part of total blocktime. + * S1 - here is last_begin + * Legend of the picture is following: + * * - means blocktime per vCPU + * x - means overlapped blocktime (total blocktime) + * + * @addr: host virtual address + */ +static void mark_postcopy_blocktime_end(uintptr_t addr) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + PostcopyBlocktimeContext *dc = mis->blocktime_ctx; + int i, affected_cpu = 0; + bool vcpu_total_blocktime = false; + uint32_t read_vcpu_time, low_time_offset; + + if (!dc) { + return; + } + + low_time_offset = get_low_time_offset(dc); + /* lookup cpu, to clear it, + * that algorithm looks straighforward, but it's not + * optimal, more optimal algorithm is keeping tree or hash + * where key is address value is a list of */ + for (i = 0; i < smp_cpus; i++) { + uint32_t vcpu_blocktime = 0; + + read_vcpu_time = atomic_fetch_add(&dc->page_fault_vcpu_time[i], 0); + if (atomic_fetch_add(&dc->vcpu_addr[i], 0) != addr || + read_vcpu_time == 0) { + continue; + } + atomic_xchg(&dc->vcpu_addr[i], 0); + vcpu_blocktime = low_time_offset - read_vcpu_time; + affected_cpu += 1; + /* we need to know is that mark_postcopy_end was due to + * faulted page, another possible case it's prefetched + * page and in that case we shouldn't be here */ + if (!vcpu_total_blocktime && + atomic_fetch_add(&dc->smp_cpus_down, 0) == smp_cpus) { + vcpu_total_blocktime = true; + } + /* continue cycle, due to one page could affect several vCPUs */ + dc->vcpu_blocktime[i] += vcpu_blocktime; + } + + atomic_sub(&dc->smp_cpus_down, affected_cpu); + if (vcpu_total_blocktime) { + dc->total_blocktime += low_time_offset - atomic_fetch_add( + &dc->last_begin, 0); + } + trace_mark_postcopy_blocktime_end(addr, dc, dc->total_blocktime, + affected_cpu); +} + +static bool postcopy_pause_fault_thread(MigrationIncomingState *mis) +{ + trace_postcopy_pause_fault_thread(); + + qemu_sem_wait(&mis->postcopy_pause_sem_fault); + + trace_postcopy_pause_fault_thread_continued(); + + return true; +} + /* * Handle faults detected by the USERFAULT markings */ @@ -625,6 +891,22 @@ static void *postcopy_ram_fault_thread(void *opaque) break; } + if (!mis->to_src_file) { + /* + * Possibly someone tells us that the return path is + * broken already using the event. We should hold until + * the channel is rebuilt. + */ + if (postcopy_pause_fault_thread(mis)) { + mis->last_rb = NULL; + /* Continue to read the userfaultfd */ + } else { + error_report("%s: paused but don't allow to continue", + __func__); + break; + } + } + if (pfd[1].revents) { uint64_t tmp64 = 0; @@ -681,19 +963,43 @@ static void *postcopy_ram_fault_thread(void *opaque) rb_offset &= ~(qemu_ram_pagesize(rb) - 1); trace_postcopy_ram_fault_thread_request(msg.arg.pagefault.address, qemu_ram_get_idstr(rb), - rb_offset); + rb_offset, + msg.arg.pagefault.feat.ptid); + mark_postcopy_blocktime_begin( + (uintptr_t)(msg.arg.pagefault.address), + msg.arg.pagefault.feat.ptid, rb); + +retry: /* * Send the request to the source - we want to request one * of our host page sizes (which is >= TPS) */ if (rb != mis->last_rb) { mis->last_rb = rb; - migrate_send_rp_req_pages(mis, qemu_ram_get_idstr(rb), - rb_offset, qemu_ram_pagesize(rb)); + ret = migrate_send_rp_req_pages(mis, + qemu_ram_get_idstr(rb), + rb_offset, + qemu_ram_pagesize(rb)); } else { /* Save some space */ - migrate_send_rp_req_pages(mis, NULL, - rb_offset, qemu_ram_pagesize(rb)); + ret = migrate_send_rp_req_pages(mis, + NULL, + rb_offset, + qemu_ram_pagesize(rb)); + } + + if (ret) { + /* May be network failure, try to wait for recovery */ + if (ret == -EIO && postcopy_pause_fault_thread(mis)) { + /* We got reconnected somehow, try to continue */ + mis->last_rb = NULL; + goto retry; + } else { + /* This is a unavoidable fault */ + error_report("%s: migrate_send_rp_req_pages() get %d", + __func__, ret); + break; + } } } @@ -793,7 +1099,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis) mis->have_fault_thread = true; /* Mark so that we get notified of accesses to unwritten areas */ - if (qemu_ram_foreach_block(ram_block_enable_notify, mis)) { + if (qemu_ram_foreach_migratable_block(ram_block_enable_notify, mis)) { return -1; } @@ -829,6 +1135,8 @@ static int qemu_ufd_copy_ioctl(int userfault_fd, void *host_addr, if (!ret) { ramblock_recv_bitmap_set_range(rb, host_addr, pagesize / qemu_target_page_size()); + mark_postcopy_blocktime_end((uintptr_t)host_addr); + } return ret; } @@ -947,13 +1255,17 @@ void *postcopy_get_tmp_page(MigrationIncomingState *mis) #else /* No target OS support, stubs just fail */ +void fill_destination_postcopy_migration_info(MigrationInfo *info) +{ +} + bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) { error_report("%s: No OS support", __func__); return false; } -int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages) +int postcopy_ram_incoming_init(MigrationIncomingState *mis) { error_report("postcopy_ram_incoming_init: No OS support"); return -1; diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index d900d9c34f..9d55536fd1 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -27,7 +27,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis); * postcopy later; must be called prior to any precopy. * called from ram.c's similarly named ram_postcopy_incoming_init */ -int postcopy_ram_incoming_init(MigrationIncomingState *mis, size_t ram_pages); +int postcopy_ram_incoming_init(MigrationIncomingState *mis); /* * At the end of a migration where postcopy_ram_incoming_init was called. diff --git a/migration/qemu-file.c b/migration/qemu-file.c index bb63c779cc..0463f4c321 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -658,8 +658,32 @@ uint64_t qemu_get_be64(QEMUFile *f) return v; } -/* Compress size bytes of data start at p with specific compression - * level and store the compressed data to the buffer of f. +/* return the size after compression, or negative value on error */ +static int qemu_compress_data(z_stream *stream, uint8_t *dest, size_t dest_len, + const uint8_t *source, size_t source_len) +{ + int err; + + err = deflateReset(stream); + if (err != Z_OK) { + return -1; + } + + stream->avail_in = source_len; + stream->next_in = (uint8_t *)source; + stream->avail_out = dest_len; + stream->next_out = dest; + + err = deflate(stream, Z_FINISH); + if (err != Z_STREAM_END) { + return -1; + } + + return stream->next_out - dest; +} + +/* Compress size bytes of data start at p and store the compressed + * data to the buffer of f. * * When f is not writable, return -1 if f has no space to save the * compressed data. @@ -667,9 +691,8 @@ uint64_t qemu_get_be64(QEMUFile *f) * do fflush first, if f still has no space to save the compressed * data, return -1. */ - -ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, - int level) +ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, + const uint8_t *p, size_t size) { ssize_t blen = IO_BUF_SIZE - f->buf_index - sizeof(int32_t); @@ -683,11 +706,13 @@ ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, return -1; } } - if (compress2(f->buf + f->buf_index + sizeof(int32_t), (uLongf *)&blen, - (Bytef *)p, size, level) != Z_OK) { - error_report("Compress Failed!"); - return 0; + + blen = qemu_compress_data(stream, f->buf + f->buf_index + sizeof(int32_t), + blen, p, size); + if (blen < 0) { + return -1; } + qemu_put_be32(f, blen); if (f->ops->writev_buffer) { add_to_iovec(f, f->buf + f->buf_index, blen, false); diff --git a/migration/qemu-file.h b/migration/qemu-file.h index f4f356ab12..2ccfcfb2a8 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -25,6 +25,8 @@ #ifndef MIGRATION_QEMU_FILE_H #define MIGRATION_QEMU_FILE_H +#include + /* Read a chunk of data from a file at the given position. The pos argument * can be ignored if the file is only be used for streaming. The number of * bytes actually read should be returned. @@ -132,8 +134,8 @@ bool qemu_file_is_writable(QEMUFile *f); size_t qemu_peek_buffer(QEMUFile *f, uint8_t **buf, size_t size, size_t offset); size_t qemu_get_buffer_in_place(QEMUFile *f, uint8_t **buf, size_t size); -ssize_t qemu_put_compression_data(QEMUFile *f, const uint8_t *p, size_t size, - int level); +ssize_t qemu_put_compression_data(QEMUFile *f, z_stream *stream, + const uint8_t *p, size_t size); int qemu_put_qemu_file(QEMUFile *f_des, QEMUFile *f_src); /* diff --git a/migration/qjson.c b/migration/qjson.c index 9d7f6eb9eb..e9889bdcb0 100644 --- a/migration/qjson.c +++ b/migration/qjson.c @@ -109,6 +109,6 @@ void qjson_finish(QJSON *json) void qjson_destroy(QJSON *json) { - QDECREF(json->str); + qobject_unref(json->str); g_free(json); } diff --git a/migration/qjson.h b/migration/qjson.h index 2978b5f371..41664f2d71 100644 --- a/migration/qjson.h +++ b/migration/qjson.h @@ -13,8 +13,6 @@ #ifndef QEMU_QJSON_H #define QEMU_QJSON_H -typedef struct QJSON QJSON; - QJSON *qjson_new(void); void qjson_destroy(QJSON *json); void json_prop_str(QJSON *json, const char *name, const char *str); diff --git a/migration/ram.c b/migration/ram.c index 0e90efa092..24dea2730c 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -36,11 +36,12 @@ #include "xbzrle.h" #include "ram.h" #include "migration.h" +#include "socket.h" #include "migration/register.h" #include "migration/misc.h" #include "qemu-file.h" #include "postcopy-ram.h" -#include "migration/page_cache.h" +#include "page_cache.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-events-migration.h" @@ -50,7 +51,11 @@ #include "exec/target_page.h" #include "qemu/rcu_queue.h" #include "migration/colo.h" -#include "migration/block.h" +#include "block.h" +#include "sysemu/sysemu.h" +#include "qemu/uuid.h" +#include "savevm.h" +#include "qemu/iov.h" /***********************************************************/ /* ram save/restore */ @@ -153,11 +158,18 @@ out: return ret; } +/* Should be holding either ram_list.mutex, or the RCU lock. */ +#define RAMBLOCK_FOREACH_MIGRATABLE(block) \ + INTERNAL_RAMBLOCK_FOREACH(block) \ + if (!qemu_ram_is_migratable(block)) {} else + +#undef RAMBLOCK_FOREACH + static void ramblock_recv_map_init(void) { RAMBlock *rb; - RAMBLOCK_FOREACH(rb) { + RAMBLOCK_FOREACH_MIGRATABLE(rb) { assert(!rb->receivedmap); rb->receivedmap = bitmap_new(rb->max_length >> qemu_target_page_bits()); } @@ -187,6 +199,70 @@ void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, nr); } +#define RAMBLOCK_RECV_BITMAP_ENDING (0x0123456789abcdefULL) + +/* + * Format: bitmap_size (8 bytes) + whole_bitmap (N bytes). + * + * Returns >0 if success with sent bytes, or <0 if error. + */ +int64_t ramblock_recv_bitmap_send(QEMUFile *file, + const char *block_name) +{ + RAMBlock *block = qemu_ram_block_by_name(block_name); + unsigned long *le_bitmap, nbits; + uint64_t size; + + if (!block) { + error_report("%s: invalid block name: %s", __func__, block_name); + return -1; + } + + nbits = block->used_length >> TARGET_PAGE_BITS; + + /* + * Make sure the tmp bitmap buffer is big enough, e.g., on 32bit + * machines we may need 4 more bytes for padding (see below + * comment). So extend it a bit before hand. + */ + le_bitmap = bitmap_new(nbits + BITS_PER_LONG); + + /* + * Always use little endian when sending the bitmap. This is + * required that when source and destination VMs are not using the + * same endianess. (Note: big endian won't work.) + */ + bitmap_to_le(le_bitmap, block->receivedmap, nbits); + + /* Size of the bitmap, in bytes */ + size = DIV_ROUND_UP(nbits, 8); + + /* + * size is always aligned to 8 bytes for 64bit machines, but it + * may not be true for 32bit machines. We need this padding to + * make sure the migration can survive even between 32bit and + * 64bit machines. + */ + size = ROUND_UP(size, 8); + + qemu_put_be64(file, size); + qemu_put_buffer(file, (const uint8_t *)le_bitmap, size); + /* + * Mark as an end, in case the middle part is screwed up due to + * some "misterious" reason. + */ + qemu_put_be64(file, RAMBLOCK_RECV_BITMAP_ENDING); + qemu_fflush(file); + + g_free(le_bitmap); + + if (qemu_file_get_error(file)) { + return qemu_file_get_error(file); + } + + return size + sizeof(size); +} + /* * An outstanding page request, on the source, having been received * and queued @@ -269,6 +345,10 @@ struct CompressParam { QemuCond cond; RAMBlock *block; ram_addr_t offset; + + /* internally used fields */ + z_stream stream; + uint8_t *originbuf; }; typedef struct CompressParam CompressParam; @@ -280,6 +360,7 @@ struct DecompressParam { void *des; uint8_t *compbuf; int len; + z_stream stream; }; typedef struct DecompressParam DecompressParam; @@ -294,13 +375,14 @@ static QemuCond comp_done_cond; /* The empty QEMUFileOps will be used by file in CompressParam */ static const QEMUFileOps empty_ops = { }; +static QEMUFile *decomp_file; static DecompressParam *decomp_param; static QemuThread *decompress_threads; static QemuMutex decomp_done_lock; static QemuCond decomp_done_cond; -static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, - ram_addr_t offset); +static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, + ram_addr_t offset, uint8_t *source_buf); static void *do_data_compress(void *opaque) { @@ -316,7 +398,8 @@ static void *do_data_compress(void *opaque) param->block = NULL; qemu_mutex_unlock(¶m->mutex); - do_compress_ram_page(param->file, block, offset); + do_compress_ram_page(param->file, ¶m->stream, block, offset, + param->originbuf); qemu_mutex_lock(&comp_done_lock); param->done = true; @@ -357,10 +440,20 @@ static void compress_threads_save_cleanup(void) terminate_compression_threads(); thread_count = migrate_compress_threads(); for (i = 0; i < thread_count; i++) { + /* + * we use it as a indicator which shows if the thread is + * properly init'd or not + */ + if (!comp_param[i].file) { + break; + } qemu_thread_join(compress_threads + i); - qemu_fclose(comp_param[i].file); qemu_mutex_destroy(&comp_param[i].mutex); qemu_cond_destroy(&comp_param[i].cond); + deflateEnd(&comp_param[i].stream); + g_free(comp_param[i].originbuf); + qemu_fclose(comp_param[i].file); + comp_param[i].file = NULL; } qemu_mutex_destroy(&comp_done_lock); qemu_cond_destroy(&comp_done_cond); @@ -370,12 +463,12 @@ static void compress_threads_save_cleanup(void) comp_param = NULL; } -static void compress_threads_save_setup(void) +static int compress_threads_save_setup(void) { int i, thread_count; if (!migrate_use_compression()) { - return; + return 0; } thread_count = migrate_compress_threads(); compress_threads = g_new0(QemuThread, thread_count); @@ -383,6 +476,17 @@ static void compress_threads_save_setup(void) qemu_cond_init(&comp_done_cond); qemu_mutex_init(&comp_done_lock); for (i = 0; i < thread_count; i++) { + comp_param[i].originbuf = g_try_malloc(TARGET_PAGE_SIZE); + if (!comp_param[i].originbuf) { + goto exit; + } + + if (deflateInit(&comp_param[i].stream, + migrate_compress_level()) != Z_OK) { + g_free(comp_param[i].originbuf); + goto exit; + } + /* comp_param[i].file is just used as a dummy buffer to save data, * set its ops to empty. */ @@ -395,31 +499,408 @@ static void compress_threads_save_setup(void) do_data_compress, comp_param + i, QEMU_THREAD_JOINABLE); } + return 0; + +exit: + compress_threads_save_cleanup(); + return -1; } /* Multiple fd's */ -struct MultiFDSendParams { +#define MULTIFD_MAGIC 0x11223344U +#define MULTIFD_VERSION 1 + +#define MULTIFD_FLAG_SYNC (1 << 0) + +typedef struct { + uint32_t magic; + uint32_t version; + unsigned char uuid[16]; /* QemuUUID */ uint8_t id; +} __attribute__((packed)) MultiFDInit_t; + +typedef struct { + uint32_t magic; + uint32_t version; + uint32_t flags; + uint32_t size; + uint32_t used; + uint64_t packet_num; + char ramblock[256]; + uint64_t offset[]; +} __attribute__((packed)) MultiFDPacket_t; + +typedef struct { + /* number of used pages */ + uint32_t used; + /* number of allocated pages */ + uint32_t allocated; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* offset of each page */ + ram_addr_t *offset; + /* pointer to each page */ + struct iovec *iov; + RAMBlock *block; +} MultiFDPages_t; + +typedef struct { + /* this fields are not changed once the thread is created */ + /* channel number */ + uint8_t id; + /* channel thread name */ char *name; + /* channel thread id */ QemuThread thread; + /* communication channel */ + QIOChannel *c; + /* sem where to wait for more work */ QemuSemaphore sem; + /* this mutex protects the following parameters */ QemuMutex mutex; + /* is this channel thread running */ + bool running; + /* should this thread finish */ bool quit; -}; -typedef struct MultiFDSendParams MultiFDSendParams; + /* thread has work to do */ + int pending_job; + /* array of pages to sent */ + MultiFDPages_t *pages; + /* packet allocated len */ + uint32_t packet_len; + /* pointer to the packet */ + MultiFDPacket_t *packet; + /* multifd flags for each packet */ + uint32_t flags; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* thread local variables */ + /* packets sent through this channel */ + uint64_t num_packets; + /* pages sent through this channel */ + uint64_t num_pages; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; +} MultiFDSendParams; + +typedef struct { + /* this fields are not changed once the thread is created */ + /* channel number */ + uint8_t id; + /* channel thread name */ + char *name; + /* channel thread id */ + QemuThread thread; + /* communication channel */ + QIOChannel *c; + /* this mutex protects the following parameters */ + QemuMutex mutex; + /* is this channel thread running */ + bool running; + /* array of pages to receive */ + MultiFDPages_t *pages; + /* packet allocated len */ + uint32_t packet_len; + /* pointer to the packet */ + MultiFDPacket_t *packet; + /* multifd flags for each packet */ + uint32_t flags; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* thread local variables */ + /* packets sent through this channel */ + uint64_t num_packets; + /* pages sent through this channel */ + uint64_t num_pages; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; +} MultiFDRecvParams; + +static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) +{ + MultiFDInit_t msg; + int ret; + + msg.magic = cpu_to_be32(MULTIFD_MAGIC); + msg.version = cpu_to_be32(MULTIFD_VERSION); + msg.id = p->id; + memcpy(msg.uuid, &qemu_uuid.data, sizeof(msg.uuid)); + + ret = qio_channel_write_all(p->c, (char *)&msg, sizeof(msg), errp); + if (ret != 0) { + return -1; + } + return 0; +} + +static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) +{ + MultiFDInit_t msg; + int ret; + + ret = qio_channel_read_all(c, (char *)&msg, sizeof(msg), errp); + if (ret != 0) { + return -1; + } + + be32_to_cpus(&msg.magic); + be32_to_cpus(&msg.version); + + if (msg.magic != MULTIFD_MAGIC) { + error_setg(errp, "multifd: received packet magic %x " + "expected %x", msg.magic, MULTIFD_MAGIC); + return -1; + } + + if (msg.version != MULTIFD_VERSION) { + error_setg(errp, "multifd: received packet version %d " + "expected %d", msg.version, MULTIFD_VERSION); + return -1; + } + + if (memcmp(msg.uuid, &qemu_uuid, sizeof(qemu_uuid))) { + char *uuid = qemu_uuid_unparse_strdup(&qemu_uuid); + char *msg_uuid = qemu_uuid_unparse_strdup((const QemuUUID *)msg.uuid); + + error_setg(errp, "multifd: received uuid '%s' and expected " + "uuid '%s' for channel %hhd", msg_uuid, uuid, msg.id); + g_free(uuid); + g_free(msg_uuid); + return -1; + } + + if (msg.id > migrate_multifd_channels()) { + error_setg(errp, "multifd: received channel version %d " + "expected %d", msg.version, MULTIFD_VERSION); + return -1; + } + + return msg.id; +} + +static MultiFDPages_t *multifd_pages_init(size_t size) +{ + 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; +} + +static void multifd_pages_clear(MultiFDPages_t *pages) +{ + pages->used = 0; + 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); +} + +static void multifd_send_fill_packet(MultiFDSendParams *p) +{ + MultiFDPacket_t *packet = p->packet; + int i; + + packet->magic = cpu_to_be32(MULTIFD_MAGIC); + packet->version = cpu_to_be32(MULTIFD_VERSION); + packet->flags = cpu_to_be32(p->flags); + packet->size = cpu_to_be32(migrate_multifd_page_count()); + packet->used = cpu_to_be32(p->pages->used); + packet->packet_num = cpu_to_be64(p->packet_num); + + if (p->pages->block) { + strncpy(packet->ramblock, p->pages->block->idstr, 256); + } + + for (i = 0; i < p->pages->used; i++) { + packet->offset[i] = cpu_to_be64(p->pages->offset[i]); + } +} + +static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) +{ + MultiFDPacket_t *packet = p->packet; + RAMBlock *block; + int i; + + be32_to_cpus(&packet->magic); + if (packet->magic != MULTIFD_MAGIC) { + error_setg(errp, "multifd: received packet " + "magic %x and expected magic %x", + packet->magic, MULTIFD_MAGIC); + return -1; + } + + be32_to_cpus(&packet->version); + if (packet->version != MULTIFD_VERSION) { + error_setg(errp, "multifd: received packet " + "version %d and expected version %d", + packet->version, MULTIFD_VERSION); + return -1; + } + + p->flags = be32_to_cpu(packet->flags); + + be32_to_cpus(&packet->size); + if (packet->size > migrate_multifd_page_count()) { + error_setg(errp, "multifd: received packet " + "with size %d and expected maximum size %d", + packet->size, migrate_multifd_page_count()) ; + return -1; + } + + p->pages->used = be32_to_cpu(packet->used); + if (p->pages->used > packet->size) { + error_setg(errp, "multifd: received packet " + "with size %d and expected maximum size %d", + p->pages->used, packet->size) ; + return -1; + } + + p->packet_num = be64_to_cpu(packet->packet_num); + + if (p->pages->used) { + /* make sure that ramblock is 0 terminated */ + packet->ramblock[255] = 0; + block = qemu_ram_block_by_name(packet->ramblock); + if (!block) { + error_setg(errp, "multifd: unknown ram block %s", + packet->ramblock); + return -1; + } + } + + for (i = 0; i < p->pages->used; i++) { + ram_addr_t offset = be64_to_cpu(packet->offset[i]); + + if (offset > (block->used_length - TARGET_PAGE_SIZE)) { + error_setg(errp, "multifd: offset too long " RAM_ADDR_FMT + " (max " RAM_ADDR_FMT ")", + offset, block->max_length); + return -1; + } + p->pages->iov[i].iov_base = block->host + offset; + p->pages->iov[i].iov_len = TARGET_PAGE_SIZE; + } + + return 0; +} struct { MultiFDSendParams *params; /* number of created threads */ int count; + /* array of pages to sent */ + MultiFDPages_t *pages; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; + /* global number of generated multifd packets */ + uint64_t packet_num; + /* send channels ready */ + QemuSemaphore channels_ready; } *multifd_send_state; -static void terminate_multifd_send_threads(Error *errp) +/* + * How we use multifd_send_state->pages and channel->pages? + * + * We create a pages for each channel, and a main one. Each time that + * we need to send a batch of pages we interchange the ones between + * multifd_send_state and the channel that is sending it. There are + * two reasons for that: + * - to not have to do so many mallocs during migration + * - to make easier to know what to free at the end of migration + * + * This way we always know who is the owner of each "pages" struct, + * and we don't need any loocking. It belongs to the migration thread + * or to the channel thread. Switching is safe because the migration + * thread is using the channel mutex when changing it, and the channel + * have to had finish with its own, otherwise pending_job can't be + * false. + */ + +static void multifd_send_pages(void) +{ + int i; + static int next_channel; + MultiFDSendParams *p = NULL; /* make happy gcc */ + MultiFDPages_t *pages = multifd_send_state->pages; + uint64_t transferred; + + qemu_sem_wait(&multifd_send_state->channels_ready); + for (i = next_channel;; i = (i + 1) % migrate_multifd_channels()) { + p = &multifd_send_state->params[i]; + + qemu_mutex_lock(&p->mutex); + if (!p->pending_job) { + p->pending_job++; + next_channel = (i + 1) % migrate_multifd_channels(); + break; + } + qemu_mutex_unlock(&p->mutex); + } + p->pages->used = 0; + + p->packet_num = multifd_send_state->packet_num++; + p->pages->block = NULL; + multifd_send_state->pages = p->pages; + p->pages = pages; + transferred = ((uint64_t) pages->used) * TARGET_PAGE_SIZE + p->packet_len; + ram_counters.multifd_bytes += transferred; + ram_counters.transferred += transferred;; + qemu_mutex_unlock(&p->mutex); + qemu_sem_post(&p->sem); +} + +static void multifd_queue_page(RAMBlock *block, ram_addr_t offset) +{ + MultiFDPages_t *pages = multifd_send_state->pages; + + if (!pages->block) { + pages->block = block; + } + + if (pages->block == block) { + pages->offset[pages->used] = offset; + pages->iov[pages->used].iov_base = block->host + offset; + pages->iov[pages->used].iov_len = TARGET_PAGE_SIZE; + pages->used++; + + if (pages->used < pages->allocated) { + return; + } + } + + multifd_send_pages(); + + if (pages->block != block) { + multifd_queue_page(block, offset); + } +} + +static void multifd_send_terminate_threads(Error *err) { int i; - for (i = 0; i < multifd_send_state->count; i++) { + if (err) { + MigrationState *s = migrate_get_current(); + migrate_set_error(s, err); + if (s->state == MIGRATION_STATUS_SETUP || + s->state == MIGRATION_STATUS_PRE_SWITCHOVER || + s->state == MIGRATION_STATUS_DEVICE || + s->state == MIGRATION_STATUS_ACTIVE) { + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_FAILED); + } + } + + for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; qemu_mutex_lock(&p->mutex); @@ -437,43 +918,168 @@ int multifd_save_cleanup(Error **errp) if (!migrate_use_multifd()) { return 0; } - terminate_multifd_send_threads(NULL); - for (i = 0; i < multifd_send_state->count; i++) { + multifd_send_terminate_threads(NULL); + for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; - qemu_thread_join(&p->thread); + if (p->running) { + qemu_thread_join(&p->thread); + } + socket_send_channel_destroy(p->c); + p->c = NULL; qemu_mutex_destroy(&p->mutex); qemu_sem_destroy(&p->sem); + 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; } + qemu_sem_destroy(&multifd_send_state->channels_ready); + qemu_sem_destroy(&multifd_send_state->sem_sync); g_free(multifd_send_state->params); multifd_send_state->params = NULL; + multifd_pages_clear(multifd_send_state->pages); + multifd_send_state->pages = NULL; g_free(multifd_send_state); multifd_send_state = NULL; return ret; } +static void multifd_send_sync_main(void) +{ + int i; + + if (!migrate_use_multifd()) { + return; + } + if (multifd_send_state->pages->used) { + multifd_send_pages(); + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + trace_multifd_send_sync_main_signal(p->id); + + qemu_mutex_lock(&p->mutex); + + p->packet_num = multifd_send_state->packet_num++; + p->flags |= MULTIFD_FLAG_SYNC; + p->pending_job++; + qemu_mutex_unlock(&p->mutex); + qemu_sem_post(&p->sem); + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDSendParams *p = &multifd_send_state->params[i]; + + trace_multifd_send_sync_main_wait(p->id); + qemu_sem_wait(&multifd_send_state->sem_sync); + } + trace_multifd_send_sync_main(multifd_send_state->packet_num); +} + static void *multifd_send_thread(void *opaque) { MultiFDSendParams *p = opaque; + Error *local_err = NULL; + int ret; + + trace_multifd_send_thread_start(p->id); + + if (multifd_send_initial_packet(p, &local_err) < 0) { + goto out; + } + /* initial packet */ + p->num_packets = 1; while (true) { + qemu_sem_wait(&p->sem); qemu_mutex_lock(&p->mutex); - if (p->quit) { + + if (p->pending_job) { + uint32_t used = p->pages->used; + uint64_t packet_num = p->packet_num; + uint32_t flags = p->flags; + + multifd_send_fill_packet(p); + p->flags = 0; + p->num_packets++; + p->num_pages += used; + p->pages->used = 0; + qemu_mutex_unlock(&p->mutex); + + trace_multifd_send(p->id, packet_num, used, flags); + + ret = qio_channel_write_all(p->c, (void *)p->packet, + p->packet_len, &local_err); + if (ret != 0) { + break; + } + + ret = qio_channel_writev_all(p->c, p->pages->iov, used, &local_err); + if (ret != 0) { + break; + } + + qemu_mutex_lock(&p->mutex); + p->pending_job--; + qemu_mutex_unlock(&p->mutex); + + if (flags & MULTIFD_FLAG_SYNC) { + qemu_sem_post(&multifd_send_state->sem_sync); + } + qemu_sem_post(&multifd_send_state->channels_ready); + } else if (p->quit) { qemu_mutex_unlock(&p->mutex); break; + } else { + qemu_mutex_unlock(&p->mutex); + /* sometimes there are spurious wakeups */ } - qemu_mutex_unlock(&p->mutex); - qemu_sem_wait(&p->sem); } +out: + if (local_err) { + multifd_send_terminate_threads(local_err); + } + + qemu_mutex_lock(&p->mutex); + p->running = false; + qemu_mutex_unlock(&p->mutex); + + trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages); + return NULL; } +static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) +{ + MultiFDSendParams *p = opaque; + QIOChannel *sioc = QIO_CHANNEL(qio_task_get_source(task)); + Error *local_err = NULL; + + if (qio_task_propagate_error(task, &local_err)) { + if (multifd_save_cleanup(&local_err) != 0) { + migrate_set_error(migrate_get_current(), local_err); + } + } else { + p->c = QIO_CHANNEL(sioc); + qio_channel_set_delay(p->c, false); + p->running = true; + qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, + QEMU_THREAD_JOINABLE); + + atomic_inc(&multifd_send_state->count); + } +} + int multifd_save_setup(void) { int thread_count; + uint32_t page_count = migrate_multifd_page_count(); uint8_t i; if (!migrate_use_multifd()) { @@ -482,49 +1088,63 @@ int multifd_save_setup(void) thread_count = migrate_multifd_channels(); multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); - multifd_send_state->count = 0; + atomic_set(&multifd_send_state->count, 0); + multifd_send_state->pages = multifd_pages_init(page_count); + qemu_sem_init(&multifd_send_state->sem_sync, 0); + qemu_sem_init(&multifd_send_state->channels_ready, 0); + for (i = 0; i < thread_count; i++) { MultiFDSendParams *p = &multifd_send_state->params[i]; qemu_mutex_init(&p->mutex); qemu_sem_init(&p->sem, 0); + qemu_sem_init(&p->sem_sync, 0); p->quit = false; + p->pending_job = 0; p->id = i; + p->pages = multifd_pages_init(page_count); + p->packet_len = sizeof(MultiFDPacket_t) + + sizeof(ram_addr_t) * page_count; + p->packet = g_malloc0(p->packet_len); p->name = g_strdup_printf("multifdsend_%d", i); - qemu_thread_create(&p->thread, p->name, multifd_send_thread, p, - QEMU_THREAD_JOINABLE); - - multifd_send_state->count++; + socket_send_channel_create(multifd_new_send_channel_async, p); } return 0; } -struct MultiFDRecvParams { - uint8_t id; - char *name; - QemuThread thread; - QemuSemaphore sem; - QemuMutex mutex; - bool quit; -}; -typedef struct MultiFDRecvParams MultiFDRecvParams; - struct { MultiFDRecvParams *params; /* number of created threads */ int count; + /* syncs main thread and channels */ + QemuSemaphore sem_sync; + /* global number of generated multifd packets */ + uint64_t packet_num; } *multifd_recv_state; -static void terminate_multifd_recv_threads(Error *errp) +static void multifd_recv_terminate_threads(Error *err) { int i; - for (i = 0; i < multifd_recv_state->count; i++) { + if (err) { + MigrationState *s = migrate_get_current(); + migrate_set_error(s, err); + if (s->state == MIGRATION_STATUS_SETUP || + s->state == MIGRATION_STATUS_ACTIVE) { + migrate_set_state(&s->state, s->state, + MIGRATION_STATUS_FAILED); + } + } + + for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; qemu_mutex_lock(&p->mutex); - p->quit = true; - qemu_sem_post(&p->sem); + /* We could arrive here for two reasons: + - normal quit, i.e. everything went fine, just finished + - error quit: We close the channels so the channel threads + finish the qio_channel_read_all_eof() */ + qio_channel_shutdown(p->c, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); qemu_mutex_unlock(&p->mutex); } } @@ -537,16 +1157,26 @@ int multifd_load_cleanup(Error **errp) if (!migrate_use_multifd()) { return 0; } - terminate_multifd_recv_threads(NULL); - for (i = 0; i < multifd_recv_state->count; i++) { + multifd_recv_terminate_threads(NULL); + for (i = 0; i < migrate_multifd_channels(); i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; - qemu_thread_join(&p->thread); + if (p->running) { + qemu_thread_join(&p->thread); + } + object_unref(OBJECT(p->c)); + p->c = NULL; qemu_mutex_destroy(&p->mutex); - qemu_sem_destroy(&p->sem); + 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; } + qemu_sem_destroy(&multifd_recv_state->sem_sync); g_free(multifd_recv_state->params); multifd_recv_state->params = NULL; g_free(multifd_recv_state); @@ -555,26 +1185,95 @@ int multifd_load_cleanup(Error **errp) return ret; } +static void multifd_recv_sync_main(void) +{ + int i; + + if (!migrate_use_multifd()) { + return; + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + trace_multifd_recv_sync_main_wait(p->id); + qemu_sem_wait(&multifd_recv_state->sem_sync); + qemu_mutex_lock(&p->mutex); + if (multifd_recv_state->packet_num < p->packet_num) { + multifd_recv_state->packet_num = p->packet_num; + } + qemu_mutex_unlock(&p->mutex); + } + for (i = 0; i < migrate_multifd_channels(); i++) { + MultiFDRecvParams *p = &multifd_recv_state->params[i]; + + trace_multifd_recv_sync_main_signal(p->id); + qemu_sem_post(&p->sem_sync); + } + trace_multifd_recv_sync_main(multifd_recv_state->packet_num); +} + static void *multifd_recv_thread(void *opaque) { MultiFDRecvParams *p = opaque; + Error *local_err = NULL; + int ret; + + trace_multifd_recv_thread_start(p->id); while (true) { + uint32_t used; + uint32_t flags; + + ret = qio_channel_read_all_eof(p->c, (void *)p->packet, + p->packet_len, &local_err); + if (ret == 0) { /* EOF */ + break; + } + if (ret == -1) { /* Error */ + break; + } + qemu_mutex_lock(&p->mutex); - if (p->quit) { + ret = multifd_recv_unfill_packet(p, &local_err); + if (ret) { qemu_mutex_unlock(&p->mutex); break; } + + used = p->pages->used; + flags = p->flags; + trace_multifd_recv(p->id, p->packet_num, used, flags); + p->num_packets++; + p->num_pages += used; qemu_mutex_unlock(&p->mutex); - qemu_sem_wait(&p->sem); + + ret = qio_channel_readv_all(p->c, p->pages->iov, used, &local_err); + if (ret != 0) { + break; + } + + if (flags & MULTIFD_FLAG_SYNC) { + qemu_sem_post(&multifd_recv_state->sem_sync); + qemu_sem_wait(&p->sem_sync); + } } + if (local_err) { + multifd_recv_terminate_threads(local_err); + } + qemu_mutex_lock(&p->mutex); + p->running = false; + qemu_mutex_unlock(&p->mutex); + + trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages); + return NULL; } int multifd_load_setup(void) { int thread_count; + uint32_t page_count = migrate_multifd_page_count(); uint8_t i; if (!migrate_use_multifd()) { @@ -583,22 +1282,67 @@ int multifd_load_setup(void) thread_count = migrate_multifd_channels(); multifd_recv_state = g_malloc0(sizeof(*multifd_recv_state)); multifd_recv_state->params = g_new0(MultiFDRecvParams, thread_count); - multifd_recv_state->count = 0; + atomic_set(&multifd_recv_state->count, 0); + qemu_sem_init(&multifd_recv_state->sem_sync, 0); + for (i = 0; i < thread_count; i++) { MultiFDRecvParams *p = &multifd_recv_state->params[i]; qemu_mutex_init(&p->mutex); - qemu_sem_init(&p->sem, 0); - p->quit = false; + qemu_sem_init(&p->sem_sync, 0); p->id = i; + p->pages = multifd_pages_init(page_count); + p->packet_len = sizeof(MultiFDPacket_t) + + sizeof(ram_addr_t) * page_count; + p->packet = g_malloc0(p->packet_len); p->name = g_strdup_printf("multifdrecv_%d", i); - qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, - QEMU_THREAD_JOINABLE); - multifd_recv_state->count++; } return 0; } +bool multifd_recv_all_channels_created(void) +{ + int thread_count = migrate_multifd_channels(); + + if (!migrate_use_multifd()) { + return true; + } + + return thread_count == atomic_read(&multifd_recv_state->count); +} + +/* Return true if multifd is ready for the migration, otherwise false */ +bool multifd_recv_new_channel(QIOChannel *ioc) +{ + MultiFDRecvParams *p; + Error *local_err = NULL; + int id; + + id = multifd_recv_initial_packet(ioc, &local_err); + if (id < 0) { + multifd_recv_terminate_threads(local_err); + return false; + } + + p = &multifd_recv_state->params[id]; + if (p->c != NULL) { + error_setg(&local_err, "multifd: received id '%d' already setup'", + id); + multifd_recv_terminate_threads(local_err); + return false; + } + p->c = ioc; + object_ref(OBJECT(ioc)); + /* initial packet */ + p->num_packets = 1; + + p->running = true; + qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, + QEMU_THREAD_JOINABLE); + atomic_inc(&multifd_recv_state->count); + return multifd_recv_state->count == migrate_multifd_channels(); +} + /** * save_page_header: write page header to wire * @@ -780,6 +1524,10 @@ unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, unsigned long *bitmap = rb->bmap; unsigned long next; + if (!qemu_ram_is_migratable(rb)) { + return size; + } + if (rs->ram_bulk_stage && start > 0) { next = start + 1; } else { @@ -825,13 +1573,32 @@ uint64_t ram_pagesize_summary(void) RAMBlock *block; uint64_t summary = 0; - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { summary |= block->page_size; } return summary; } +static void migration_update_rates(RAMState *rs, int64_t end_time) +{ + uint64_t iter_count = rs->iterations - rs->iterations_prev; + + /* calculate period counters */ + ram_counters.dirty_pages_rate = rs->num_dirty_pages_period * 1000 + / (end_time - rs->time_last_bitmap_sync); + + if (!iter_count) { + return; + } + + if (migrate_use_xbzrle()) { + xbzrle_counters.cache_miss_rate = (double)(xbzrle_counters.cache_miss - + rs->xbzrle_cache_miss_prev) / iter_count; + rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss; + } +} + static void migration_bitmap_sync(RAMState *rs) { RAMBlock *block; @@ -849,9 +1616,10 @@ static void migration_bitmap_sync(RAMState *rs) qemu_mutex_lock(&rs->bitmap_mutex); rcu_read_lock(); - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { migration_bitmap_sync_range(rs, block, 0, block->used_length); } + ram_counters.remaining = ram_bytes_remaining(); rcu_read_unlock(); qemu_mutex_unlock(&rs->bitmap_mutex); @@ -861,9 +1629,6 @@ static void migration_bitmap_sync(RAMState *rs) /* more than 1 second = 1000 millisecons */ if (end_time > rs->time_last_bitmap_sync + 1000) { - /* calculate period counters */ - ram_counters.dirty_pages_rate = rs->num_dirty_pages_period * 1000 - / (end_time - rs->time_last_bitmap_sync); bytes_xfer_now = ram_counters.transferred; /* During block migration the auto-converge logic incorrectly detects @@ -885,16 +1650,9 @@ static void migration_bitmap_sync(RAMState *rs) } } - if (migrate_use_xbzrle()) { - if (rs->iterations_prev != rs->iterations) { - xbzrle_counters.cache_miss_rate = - (double)(xbzrle_counters.cache_miss - - rs->xbzrle_cache_miss_prev) / - (rs->iterations - rs->iterations_prev); - } - rs->iterations_prev = rs->iterations; - rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss; - } + migration_update_rates(rs, end_time); + + rs->iterations_prev = rs->iterations; /* reset period counters */ rs->time_last_bitmap_sync = end_time; @@ -941,6 +1699,72 @@ static void ram_release_pages(const char *rbname, uint64_t offset, int pages) ram_discard_range(rbname, offset, pages << TARGET_PAGE_BITS); } +/* + * @pages: the number of pages written by the control path, + * < 0 - error + * > 0 - number of pages written + * + * Return true if the pages has been saved, otherwise false is returned. + */ +static bool control_save_page(RAMState *rs, RAMBlock *block, ram_addr_t offset, + int *pages) +{ + uint64_t bytes_xmit = 0; + int ret; + + *pages = -1; + ret = ram_control_save_page(rs->f, block->offset, offset, TARGET_PAGE_SIZE, + &bytes_xmit); + if (ret == RAM_SAVE_CONTROL_NOT_SUPP) { + return false; + } + + if (bytes_xmit) { + ram_counters.transferred += bytes_xmit; + *pages = 1; + } + + if (ret == RAM_SAVE_CONTROL_DELAYED) { + return true; + } + + if (bytes_xmit > 0) { + ram_counters.normal++; + } else if (bytes_xmit == 0) { + ram_counters.duplicate++; + } + + return true; +} + +/* + * directly send the page to the stream + * + * Returns the number of pages written. + * + * @rs: current RAM state + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + * @buf: the page to be sent + * @async: send to page asyncly + */ +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); + if (async) { + qemu_put_buffer_async(rs->f, buf, TARGET_PAGE_SIZE, + migrate_release_ram() & + migration_in_postcopy()); + } else { + qemu_put_buffer(rs->f, buf, TARGET_PAGE_SIZE); + } + ram_counters.transferred += TARGET_PAGE_SIZE; + ram_counters.normal++; + return 1; +} + /** * ram_save_page: send the given page to the stream * @@ -957,73 +1781,31 @@ static void ram_release_pages(const char *rbname, uint64_t offset, int pages) static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) { int pages = -1; - uint64_t bytes_xmit; - ram_addr_t current_addr; uint8_t *p; - int ret; bool send_async = true; RAMBlock *block = pss->block; ram_addr_t offset = pss->page << TARGET_PAGE_BITS; + ram_addr_t current_addr = block->offset + offset; p = block->host + offset; trace_ram_save_page(block->idstr, (uint64_t)offset, p); - /* In doubt sent page as normal */ - bytes_xmit = 0; - ret = ram_control_save_page(rs->f, block->offset, - offset, TARGET_PAGE_SIZE, &bytes_xmit); - if (bytes_xmit) { - ram_counters.transferred += bytes_xmit; - pages = 1; - } - XBZRLE_cache_lock(); - - current_addr = block->offset + offset; - - if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - if (ret != RAM_SAVE_CONTROL_DELAYED) { - if (bytes_xmit > 0) { - ram_counters.normal++; - } else if (bytes_xmit == 0) { - ram_counters.duplicate++; - } - } - } else { - pages = save_zero_page(rs, block, offset); - if (pages > 0) { - /* Must let xbzrle know, otherwise a previous (now 0'd) cached - * page would be stale + if (!rs->ram_bulk_stage && !migration_in_postcopy() && + migrate_use_xbzrle()) { + pages = save_xbzrle_page(rs, &p, current_addr, block, + offset, last_stage); + if (!last_stage) { + /* Can't send this cached data async, since the cache page + * might get updated before it gets to the wire */ - xbzrle_cache_zero_page(rs, current_addr); - ram_release_pages(block->idstr, offset, pages); - } else if (!rs->ram_bulk_stage && - !migration_in_postcopy() && migrate_use_xbzrle()) { - pages = save_xbzrle_page(rs, &p, current_addr, block, - offset, last_stage); - if (!last_stage) { - /* Can't send this cached data async, since the cache page - * might get updated before it gets to the wire - */ - send_async = false; - } + send_async = false; } } /* XBZRLE overflow or normal page */ if (pages == -1) { - ram_counters.transferred += - save_page_header(rs, rs->f, block, offset | RAM_SAVE_FLAG_PAGE); - if (send_async) { - qemu_put_buffer_async(rs->f, p, TARGET_PAGE_SIZE, - migrate_release_ram() & - migration_in_postcopy()); - } else { - qemu_put_buffer(rs->f, p, TARGET_PAGE_SIZE); - } - ram_counters.transferred += TARGET_PAGE_SIZE; - pages = 1; - ram_counters.normal++; + pages = save_normal_page(rs, block, offset, p, send_async); } XBZRLE_cache_unlock(); @@ -1031,8 +1813,17 @@ static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) return pages; } -static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, - ram_addr_t offset) +static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, + ram_addr_t offset) +{ + multifd_queue_page(block, offset); + ram_counters.normal++; + + return 1; +} + +static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, + ram_addr_t offset, uint8_t *source_buf) { RAMState *rs = ram_state; int bytes_sent, blen; @@ -1040,8 +1831,14 @@ static int do_compress_ram_page(QEMUFile *f, RAMBlock *block, bytes_sent = save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); - blen = qemu_put_compression_data(f, p, TARGET_PAGE_SIZE, - migrate_compress_level()); + + /* + * copy it to a internal buffer to avoid it being modified by VM + * so that we can catch up the error during compression and + * decompression + */ + memcpy(source_buf, p, TARGET_PAGE_SIZE); + blen = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); if (blen < 0) { bytes_sent = 0; qemu_file_set_error(migrate_get_current()->to_dst_file, blen); @@ -1121,83 +1918,6 @@ static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block, return pages; } -/** - * ram_save_compressed_page: compress the given page and send it to the stream - * - * Returns the number of pages written. - * - * @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_compressed_page(RAMState *rs, PageSearchStatus *pss, - bool last_stage) -{ - int pages = -1; - uint64_t bytes_xmit = 0; - uint8_t *p; - int ret, blen; - RAMBlock *block = pss->block; - ram_addr_t offset = pss->page << TARGET_PAGE_BITS; - - p = block->host + offset; - - ret = ram_control_save_page(rs->f, block->offset, - offset, TARGET_PAGE_SIZE, &bytes_xmit); - if (bytes_xmit) { - ram_counters.transferred += bytes_xmit; - pages = 1; - } - if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { - if (ret != RAM_SAVE_CONTROL_DELAYED) { - if (bytes_xmit > 0) { - ram_counters.normal++; - } else if (bytes_xmit == 0) { - ram_counters.duplicate++; - } - } - } else { - /* When starting the process of a new block, the first page of - * the block should be sent out before other pages in the same - * block, and all the pages in last block should have been sent - * out, keeping this order is important, because the 'cont' flag - * is used to avoid resending the block name. - */ - if (block != rs->last_sent_block) { - flush_compressed_data(rs); - pages = save_zero_page(rs, block, offset); - if (pages == -1) { - /* Make sure the first page is sent out before other pages */ - bytes_xmit = save_page_header(rs, rs->f, block, offset | - RAM_SAVE_FLAG_COMPRESS_PAGE); - blen = qemu_put_compression_data(rs->f, p, TARGET_PAGE_SIZE, - migrate_compress_level()); - if (blen > 0) { - ram_counters.transferred += bytes_xmit + blen; - ram_counters.normal++; - pages = 1; - } else { - qemu_file_set_error(rs->f, blen); - error_report("compressed data failed!"); - } - } - if (pages > 0) { - ram_release_pages(block->idstr, offset, pages); - } - } else { - pages = save_zero_page(rs, block, offset); - if (pages == -1) { - pages = compress_page_with_multi_thread(rs, block, offset); - } else { - ram_release_pages(block->idstr, offset, pages); - } - } - } - - return pages; -} - /** * find_dirty_block: find the next dirty page and update any state * associated with the search process. @@ -1276,6 +1996,7 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) memory_region_unref(block->mr); QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req); g_free(entry); + migration_consume_urgent_request(); } } qemu_mutex_unlock(&rs->src_page_req_mutex); @@ -1424,6 +2145,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) memory_region_ref(ramblock->mr); qemu_mutex_lock(&rs->src_page_req_mutex); QSIMPLEQ_INSERT_TAIL(&rs->src_page_requests, new_entry, next_req); + migration_make_urgent_request(); qemu_mutex_unlock(&rs->src_page_req_mutex); rcu_read_unlock(); @@ -1434,44 +2156,82 @@ err: return -1; } +static bool save_page_use_compression(RAMState *rs) +{ + if (!migrate_use_compression()) { + return false; + } + + /* + * If xbzrle is on, stop using the data compression after first + * round of migration even if compression is enabled. In theory, + * xbzrle can do better than compression. + */ + if (rs->ram_bulk_stage || !migrate_use_xbzrle()) { + return true; + } + + return false; +} + /** * ram_save_target_page: save one target page * * Returns the number of pages written * * @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_target_page(RAMState *rs, PageSearchStatus *pss, bool last_stage) { - int res = 0; + RAMBlock *block = pss->block; + ram_addr_t offset = pss->page << TARGET_PAGE_BITS; + int res; - /* Check the pages is dirty and if it is send it */ - if (migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { - /* - * If xbzrle is on, stop using the data compression after first - * round of migration even if compression is enabled. In theory, - * xbzrle can do better than compression. - */ - if (migrate_use_compression() && - (rs->ram_bulk_stage || !migrate_use_xbzrle())) { - res = ram_save_compressed_page(rs, pss, last_stage); - } else { - res = ram_save_page(rs, pss, last_stage); - } - - if (res < 0) { - return res; - } - if (pss->block->unsentmap) { - clear_bit(pss->page, pss->block->unsentmap); - } + if (control_save_page(rs, block, offset, &res)) { + return res; } - return res; + /* + * When starting the process of a new block, the first page of + * the block should be sent out before other pages in the same + * block, and all the pages in last block should have been sent + * out, keeping this order is important, because the 'cont' flag + * is used to avoid resending the block name. + */ + if (block != rs->last_sent_block && save_page_use_compression(rs)) { + flush_compressed_data(rs); + } + + res = save_zero_page(rs, block, offset); + if (res > 0) { + /* Must let xbzrle know, otherwise a previous (now 0'd) cached + * page would be stale + */ + if (!save_page_use_compression(rs)) { + XBZRLE_cache_lock(); + xbzrle_cache_zero_page(rs, block->offset + offset); + XBZRLE_cache_unlock(); + } + ram_release_pages(block->idstr, offset, res); + return res; + } + + /* + * Make sure the first page is sent out before other pages. + * + * we post it as normal page as compression will take much + * CPU resource. + */ + if (block == rs->last_sent_block && save_page_use_compression(rs)) { + return compress_page_with_multi_thread(rs, block, offset); + } else if (migrate_use_multifd()) { + return ram_save_multifd_page(rs, block, offset); + } + + return ram_save_page(rs, pss, last_stage); } /** @@ -1499,13 +2259,28 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, size_t pagesize_bits = qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; + if (!qemu_ram_is_migratable(pss->block)) { + error_report("block %s should not be migrated !", pss->block->idstr); + return 0; + } + do { + /* Check the pages is dirty and if it is send it */ + if (!migration_bitmap_clear_dirty(rs, pss->block, pss->page)) { + pss->page++; + continue; + } + tmppages = ram_save_target_page(rs, pss, last_stage); if (tmppages < 0) { return tmppages; } pages += tmppages; + if (pss->block->unsentmap) { + clear_bit(pss->page, pss->block->unsentmap); + } + pss->page++; } while ((pss->page & (pagesize_bits - 1)) && offset_in_ramblock(pss->block, pss->page << TARGET_PAGE_BITS)); @@ -1587,7 +2362,7 @@ uint64_t ram_bytes_total(void) uint64_t total = 0; rcu_read_lock(); - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { total += block->used_length; } rcu_read_unlock(); @@ -1642,7 +2417,7 @@ static void ram_save_cleanup(void *opaque) */ memory_global_dirty_log_stop(); - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { g_free(block->bmap); block->bmap = NULL; g_free(block->unsentmap); @@ -1705,7 +2480,7 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms) { struct RAMBlock *block; - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { unsigned long *bitmap = block->bmap; unsigned long range = block->used_length >> TARGET_PAGE_BITS; unsigned long run_start = find_next_zero_bit(bitmap, range, 0); @@ -1783,7 +2558,7 @@ static int postcopy_each_ram_send_discard(MigrationState *ms) struct RAMBlock *block; int ret; - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { PostcopyDiscardState *pds = postcopy_discard_send_init(ms, block->idstr); @@ -1991,7 +2766,7 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) rs->last_sent_block = NULL; rs->last_page = 0; - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { unsigned long pages = block->used_length >> TARGET_PAGE_BITS; unsigned long *bitmap = block->bmap; unsigned long *unsentmap = block->unsentmap; @@ -2052,8 +2827,15 @@ int ram_discard_range(const char *rbname, uint64_t start, size_t length) goto err; } - bitmap_clear(rb->receivedmap, start >> qemu_target_page_bits(), - length >> qemu_target_page_bits()); + /* + * On source VM, we don't need to update the received bitmap since + * we don't even have one. + */ + if (rb->receivedmap) { + bitmap_clear(rb->receivedmap, start >> qemu_target_page_bits(), + length >> qemu_target_page_bits()); + } + ret = ram_block_discard_range(rb, start, length); err: @@ -2150,7 +2932,7 @@ static void ram_list_init_bitmaps(void) /* Skip setting bitmap if there is no RAM */ if (ram_bytes_total()) { - QLIST_FOREACH_RCU(block, &ram_list.blocks, next) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { pages = block->max_length >> TARGET_PAGE_BITS; block->bmap = bitmap_new(pages); bitmap_set(block->bmap, 0, pages); @@ -2194,6 +2976,41 @@ static int ram_init_all(RAMState **rsp) return 0; } +static void ram_state_resume_prepare(RAMState *rs, QEMUFile *out) +{ + RAMBlock *block; + uint64_t pages = 0; + + /* + * Postcopy is not using xbzrle/compression, so no need for that. + * Also, since source are already halted, we don't need to care + * about dirty page logging as well. + */ + + RAMBLOCK_FOREACH_MIGRATABLE(block) { + pages += bitmap_count_one(block->bmap, + block->used_length >> TARGET_PAGE_BITS); + } + + /* This may not be aligned with current bitmaps. Recalculate. */ + rs->migration_dirty_pages = pages; + + rs->last_seen_block = NULL; + rs->last_sent_block = NULL; + rs->last_page = 0; + rs->last_version = ram_list.version; + /* + * Disable the bulk stage, otherwise we'll resend the whole RAM no + * matter what we have sent. + */ + rs->ram_bulk_stage = false; + + /* Update RAMState cache of output QEMUFile */ + rs->f = out; + + trace_ram_state_resume_prepare(pages); +} + /* * Each of ram_save_setup, ram_save_iterate and ram_save_complete has * long-running RCU critical section. When rcu-reclaims in the code @@ -2214,9 +3031,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque) RAMState **rsp = opaque; RAMBlock *block; + if (compress_threads_save_setup()) { + return -1; + } + /* migration has already setup the bitmap, reuse it. */ if (!migration_in_colo_state()) { if (ram_init_all(rsp) != 0) { + compress_threads_save_cleanup(); return -1; } } @@ -2226,7 +3048,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); - RAMBLOCK_FOREACH(block) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { qemu_put_byte(f, strlen(block->idstr)); qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); qemu_put_be64(f, block->used_length); @@ -2236,12 +3058,13 @@ static int ram_save_setup(QEMUFile *f, void *opaque) } rcu_read_unlock(); - compress_threads_save_setup(); ram_control_before_iterate(f, RAM_CONTROL_SETUP); ram_control_after_iterate(f, RAM_CONTROL_SETUP); + multifd_send_sync_main(); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + qemu_fflush(f); return 0; } @@ -2282,9 +3105,14 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); i = 0; - while ((ret = qemu_file_rate_limit(f)) == 0) { + while ((ret = qemu_file_rate_limit(f)) == 0 || + !QSIMPLEQ_EMPTY(&rs->src_page_requests)) { int pages; + if (qemu_file_get_error(f)) { + break; + } + pages = ram_find_and_save_block(rs, false); /* no more pages to sent */ if (pages == 0) { @@ -2316,8 +3144,10 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) */ ram_control_after_iterate(f, RAM_CONTROL_ROUND); + multifd_send_sync_main(); out: qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + qemu_fflush(f); ram_counters.transferred += 8; ret = qemu_file_get_error(f); @@ -2369,7 +3199,9 @@ static int ram_save_complete(QEMUFile *f, void *opaque) rcu_read_unlock(); + multifd_send_sync_main(); qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + qemu_fflush(f); return 0; } @@ -2471,6 +3303,11 @@ static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags) return NULL; } + if (!qemu_ram_is_migratable(block)) { + error_report("block %s should not be migrated !", id); + return NULL; + } + return block; } @@ -2501,12 +3338,37 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size) } } +/* return the size after decompression, or negative value on error */ +static int +qemu_uncompress_data(z_stream *stream, uint8_t *dest, size_t dest_len, + const uint8_t *source, size_t source_len) +{ + int err; + + err = inflateReset(stream); + if (err != Z_OK) { + return -1; + } + + stream->avail_in = source_len; + stream->next_in = (uint8_t *)source; + stream->avail_out = dest_len; + stream->next_out = dest; + + err = inflate(stream, Z_NO_FLUSH); + if (err != Z_STREAM_END) { + return -1; + } + + return stream->total_out; +} + static void *do_data_decompress(void *opaque) { DecompressParam *param = opaque; unsigned long pagesize; uint8_t *des; - int len; + int len, ret; qemu_mutex_lock(¶m->mutex); while (!param->quit) { @@ -2517,13 +3379,13 @@ static void *do_data_decompress(void *opaque) qemu_mutex_unlock(¶m->mutex); pagesize = TARGET_PAGE_SIZE; - /* uncompress() will return failed in some case, especially - * when the page is dirted when doing the compression, it's - * not a problem because the dirty page will be retransferred - * and uncompress() won't break the data in other pages. - */ - uncompress((Bytef *)des, &pagesize, - (const Bytef *)param->compbuf, len); + + ret = qemu_uncompress_data(¶m->stream, des, pagesize, + param->compbuf, len); + if (ret < 0 && migrate_get_current()->decompress_error_check) { + error_report("decompress data failed"); + qemu_file_set_error(decomp_file, ret); + } qemu_mutex_lock(&decomp_done_lock); param->done = true; @@ -2540,12 +3402,12 @@ static void *do_data_decompress(void *opaque) return NULL; } -static void wait_for_decompress_done(void) +static int wait_for_decompress_done(void) { int idx, thread_count; if (!migrate_use_compression()) { - return; + return 0; } thread_count = migrate_decompress_threads(); @@ -2556,30 +3418,7 @@ static void wait_for_decompress_done(void) } } qemu_mutex_unlock(&decomp_done_lock); -} - -static void compress_threads_load_setup(void) -{ - int i, thread_count; - - if (!migrate_use_compression()) { - return; - } - thread_count = migrate_decompress_threads(); - decompress_threads = g_new0(QemuThread, thread_count); - decomp_param = g_new0(DecompressParam, thread_count); - qemu_mutex_init(&decomp_done_lock); - qemu_cond_init(&decomp_done_cond); - for (i = 0; i < thread_count; i++) { - qemu_mutex_init(&decomp_param[i].mutex); - qemu_cond_init(&decomp_param[i].cond); - decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); - decomp_param[i].done = true; - decomp_param[i].quit = false; - qemu_thread_create(decompress_threads + i, "decompress", - do_data_decompress, decomp_param + i, - QEMU_THREAD_JOINABLE); - } + return qemu_file_get_error(decomp_file); } static void compress_threads_load_cleanup(void) @@ -2591,21 +3430,70 @@ static void compress_threads_load_cleanup(void) } thread_count = migrate_decompress_threads(); for (i = 0; i < thread_count; i++) { + /* + * we use it as a indicator which shows if the thread is + * properly init'd or not + */ + if (!decomp_param[i].compbuf) { + break; + } + qemu_mutex_lock(&decomp_param[i].mutex); decomp_param[i].quit = true; qemu_cond_signal(&decomp_param[i].cond); qemu_mutex_unlock(&decomp_param[i].mutex); } for (i = 0; i < thread_count; i++) { + if (!decomp_param[i].compbuf) { + break; + } + qemu_thread_join(decompress_threads + i); qemu_mutex_destroy(&decomp_param[i].mutex); qemu_cond_destroy(&decomp_param[i].cond); + inflateEnd(&decomp_param[i].stream); g_free(decomp_param[i].compbuf); + decomp_param[i].compbuf = NULL; } g_free(decompress_threads); g_free(decomp_param); decompress_threads = NULL; decomp_param = NULL; + decomp_file = NULL; +} + +static int compress_threads_load_setup(QEMUFile *f) +{ + int i, thread_count; + + if (!migrate_use_compression()) { + return 0; + } + + thread_count = migrate_decompress_threads(); + decompress_threads = g_new0(QemuThread, thread_count); + decomp_param = g_new0(DecompressParam, thread_count); + qemu_mutex_init(&decomp_done_lock); + qemu_cond_init(&decomp_done_cond); + decomp_file = f; + for (i = 0; i < thread_count; i++) { + if (inflateInit(&decomp_param[i].stream) != Z_OK) { + goto exit; + } + + decomp_param[i].compbuf = g_malloc0(compressBound(TARGET_PAGE_SIZE)); + qemu_mutex_init(&decomp_param[i].mutex); + qemu_cond_init(&decomp_param[i].cond); + decomp_param[i].done = true; + decomp_param[i].quit = false; + qemu_thread_create(decompress_threads + i, "decompress", + do_data_decompress, decomp_param + i, + QEMU_THREAD_JOINABLE); + } + return 0; +exit: + compress_threads_load_cleanup(); + return -1; } static void decompress_data_with_multi_threads(QEMUFile *f, @@ -2647,8 +3535,11 @@ static void decompress_data_with_multi_threads(QEMUFile *f, */ static int ram_load_setup(QEMUFile *f, void *opaque) { + if (compress_threads_load_setup(f)) { + return -1; + } + xbzrle_load_setup(); - compress_threads_load_setup(); ramblock_recv_map_init(); return 0; } @@ -2659,7 +3550,7 @@ static int ram_load_cleanup(void *opaque) xbzrle_load_cleanup(); compress_threads_load_cleanup(); - RAMBLOCK_FOREACH(rb) { + RAMBLOCK_FOREACH_MIGRATABLE(rb) { g_free(rb->receivedmap); rb->receivedmap = NULL; } @@ -2679,9 +3570,7 @@ static int ram_load_cleanup(void *opaque) */ int ram_postcopy_incoming_init(MigrationIncomingState *mis) { - unsigned long ram_pages = last_ram_page(); - - return postcopy_ram_incoming_init(mis, ram_pages); + return postcopy_ram_incoming_init(mis); } /** @@ -2698,7 +3587,7 @@ static int ram_load_postcopy(QEMUFile *f) { int flags = 0, ret = 0; bool place_needed = false; - bool matching_page_sizes = false; + bool matches_target_page_size = false; MigrationIncomingState *mis = migration_incoming_get_current(); /* Temporary page that is later 'placed' */ void *postcopy_host_page = postcopy_get_tmp_page(mis); @@ -2738,7 +3627,7 @@ static int ram_load_postcopy(QEMUFile *f) ret = -EINVAL; break; } - matching_page_sizes = block->page_size == TARGET_PAGE_SIZE; + matches_target_page_size = block->page_size == TARGET_PAGE_SIZE; /* * Postcopy requires that we place whole host pages atomically; * these may be huge pages for RAMBlocks that are backed by @@ -2786,12 +3675,17 @@ static int ram_load_postcopy(QEMUFile *f) case RAM_SAVE_FLAG_PAGE: all_zero = false; - if (!place_needed || !matching_page_sizes) { + if (!matches_target_page_size) { + /* For huge pages, we always use temporary buffer */ qemu_get_buffer(f, page_buffer, TARGET_PAGE_SIZE); } else { - /* Avoids the qemu_file copy during postcopy, which is - * going to do a copy later; can only do it when we - * do this read in one go (matching page sizes) + /* + * For small pages that matches target page size, we + * avoid the qemu_file copy. Instead we directly use + * the buffer of QEMUFile to place the page. Note: we + * cannot do any QEMUFile operation before using that + * buffer to make sure the buffer is valid when + * placing the page. */ qemu_get_buffer_in_place(f, (uint8_t **)&place_source, TARGET_PAGE_SIZE); @@ -2799,6 +3693,7 @@ static int ram_load_postcopy(QEMUFile *f) break; case RAM_SAVE_FLAG_EOS: /* normal exit */ + multifd_recv_sync_main(); break; default: error_report("Unknown combination of migration flags: %#x" @@ -2921,7 +3816,10 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) length = qemu_get_be64(f); block = qemu_ram_block_by_name(id); - if (block) { + if (block && !qemu_ram_is_migratable(block)) { + error_report("block %s should not be migrated !", id); + ret = -EINVAL; + } else if (block) { if (length != block->used_length) { Error *local_err = NULL; @@ -2984,6 +3882,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) break; case RAM_SAVE_FLAG_EOS: /* normal exit */ + multifd_recv_sync_main(); break; default: if (flags & RAM_SAVE_FLAG_HOOK) { @@ -2999,7 +3898,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) } } - wait_for_decompress_done(); + ret |= wait_for_decompress_done(); rcu_read_unlock(); trace_ram_load_complete(ret, seq_iter); return ret; @@ -3010,6 +3909,139 @@ static bool ram_has_postcopy(void *opaque) return migrate_postcopy_ram(); } +/* Sync all the dirty bitmap with destination VM. */ +static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) +{ + RAMBlock *block; + QEMUFile *file = s->to_dst_file; + int ramblock_count = 0; + + trace_ram_dirty_bitmap_sync_start(); + + RAMBLOCK_FOREACH_MIGRATABLE(block) { + qemu_savevm_send_recv_bitmap(file, block->idstr); + trace_ram_dirty_bitmap_request(block->idstr); + ramblock_count++; + } + + trace_ram_dirty_bitmap_sync_wait(); + + /* Wait until all the ramblocks' dirty bitmap synced */ + while (ramblock_count--) { + qemu_sem_wait(&s->rp_state.rp_sem); + } + + trace_ram_dirty_bitmap_sync_complete(); + + return 0; +} + +static void ram_dirty_bitmap_reload_notify(MigrationState *s) +{ + qemu_sem_post(&s->rp_state.rp_sem); +} + +/* + * Read the received bitmap, revert it as the initial dirty bitmap. + * This is only used when the postcopy migration is paused but wants + * to resume from a middle point. + */ +int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) +{ + int ret = -EINVAL; + QEMUFile *file = s->rp_state.from_dst_file; + unsigned long *le_bitmap, nbits = block->used_length >> TARGET_PAGE_BITS; + uint64_t local_size = DIV_ROUND_UP(nbits, 8); + uint64_t size, end_mark; + + trace_ram_dirty_bitmap_reload_begin(block->idstr); + + if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { + error_report("%s: incorrect state %s", __func__, + MigrationStatus_str(s->state)); + return -EINVAL; + } + + /* + * Note: see comments in ramblock_recv_bitmap_send() on why we + * need the endianess convertion, and the paddings. + */ + local_size = ROUND_UP(local_size, 8); + + /* Add paddings */ + le_bitmap = bitmap_new(nbits + BITS_PER_LONG); + + size = qemu_get_be64(file); + + /* The size of the bitmap should match with our ramblock */ + if (size != local_size) { + error_report("%s: ramblock '%s' bitmap size mismatch " + "(0x%"PRIx64" != 0x%"PRIx64")", __func__, + block->idstr, size, local_size); + ret = -EINVAL; + goto out; + } + + size = qemu_get_buffer(file, (uint8_t *)le_bitmap, local_size); + end_mark = qemu_get_be64(file); + + ret = qemu_file_get_error(file); + if (ret || size != local_size) { + error_report("%s: read bitmap failed for ramblock '%s': %d" + " (size 0x%"PRIx64", got: 0x%"PRIx64")", + __func__, block->idstr, ret, local_size, size); + ret = -EIO; + goto out; + } + + if (end_mark != RAMBLOCK_RECV_BITMAP_ENDING) { + error_report("%s: ramblock '%s' end mark incorrect: 0x%"PRIu64, + __func__, block->idstr, end_mark); + ret = -EINVAL; + goto out; + } + + /* + * Endianess convertion. We are during postcopy (though paused). + * The dirty bitmap won't change. We can directly modify it. + */ + bitmap_from_le(block->bmap, le_bitmap, nbits); + + /* + * What we received is "received bitmap". Revert it as the initial + * dirty bitmap for this ramblock. + */ + bitmap_complement(block->bmap, block->bmap, nbits); + + trace_ram_dirty_bitmap_reload_complete(block->idstr); + + /* + * We succeeded to sync bitmap for current ramblock. If this is + * the last one to sync, we need to notify the main send thread. + */ + ram_dirty_bitmap_reload_notify(s); + + ret = 0; +out: + g_free(le_bitmap); + return ret; +} + +static int ram_resume_prepare(MigrationState *s, void *opaque) +{ + RAMState *rs = *(RAMState **)opaque; + int ret; + + ret = ram_dirty_bitmap_sync_all(s, rs); + if (ret) { + return ret; + } + + ram_state_resume_prepare(rs, s->to_dst_file); + + return 0; +} + static SaveVMHandlers savevm_ram_handlers = { .save_setup = ram_save_setup, .save_live_iterate = ram_save_iterate, @@ -3021,6 +4053,7 @@ static SaveVMHandlers savevm_ram_handlers = { .save_cleanup = ram_save_cleanup, .load_setup = ram_load_setup, .load_cleanup = ram_load_cleanup, + .resume_prepare = ram_resume_prepare, }; void ram_mig_init(void) diff --git a/migration/ram.h b/migration/ram.h index 5030be110a..457bf54b8c 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -32,6 +32,7 @@ #include "qemu-common.h" #include "qapi/qapi-types-migration.h" #include "exec/cpu-common.h" +#include "io/channel.h" extern MigrationStats ram_counters; extern XBZRLECacheStats xbzrle_counters; @@ -44,6 +45,8 @@ int multifd_save_setup(void); int multifd_save_cleanup(Error **errp); int multifd_load_setup(void); int multifd_load_cleanup(Error **errp); +bool multifd_recv_all_channels_created(void); +bool multifd_recv_new_channel(QIOChannel *ioc); uint64_t ram_pagesize_summary(void); int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); @@ -63,5 +66,8 @@ int ramblock_recv_bitmap_test(RAMBlock *rb, void *host_addr); bool ramblock_recv_bitmap_test_byte_offset(RAMBlock *rb, uint64_t byte_offset); void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr); void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr); +int64_t ramblock_recv_bitmap_send(QEMUFile *file, + const char *block_name); +int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); #endif diff --git a/migration/rdma.c b/migration/rdma.c index da474fc19f..8bd7159059 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -400,7 +400,6 @@ struct QIOChannelRDMA { QIOChannel parent; RDMAContext *rdma; QEMUFile *file; - size_t len; bool blocking; /* XXX we don't actually honour this yet */ }; @@ -636,7 +635,7 @@ static int qemu_rdma_init_ram_blocks(RDMAContext *rdma) assert(rdma->blockmap == NULL); memset(local, 0, sizeof *local); - qemu_ram_foreach_block(qemu_rdma_init_one_block, rdma); + qemu_ram_foreach_migratable_block(qemu_rdma_init_one_block, rdma); trace_qemu_rdma_init_ram_blocks(local->nb_blocks); rdma->dest_blocks = g_new0(RDMADestBlock, rdma->local_ram_blocks.nb_blocks); @@ -708,6 +707,9 @@ static int rdma_delete_block(RDMAContext *rdma, RDMALocalBlock *block) memcpy(local->block + block->index, old + (block->index + 1), sizeof(RDMALocalBlock) * (local->nb_blocks - (block->index + 1))); + for (x = block->index; x < local->nb_blocks - 1; x++) { + local->block[x].index--; + } } } else { assert(block == local->block); @@ -2265,8 +2267,7 @@ static int qemu_rdma_write(QEMUFile *f, RDMAContext *rdma, static void qemu_rdma_cleanup(RDMAContext *rdma) { - struct rdma_cm_event *cm_event; - int ret, idx; + int idx; if (rdma->cm_id && rdma->connected) { if ((rdma->error_state || @@ -2280,14 +2281,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) qemu_rdma_post_send_control(rdma, NULL, &head); } - ret = rdma_disconnect(rdma->cm_id); - if (!ret) { - trace_qemu_rdma_cleanup_waiting_for_disconnect(); - ret = rdma_get_cm_event(rdma->channel, &cm_event); - if (!ret) { - rdma_ack_cm_event(cm_event); - } - } + rdma_disconnect(rdma->cm_id); trace_qemu_rdma_cleanup_disconnect(); rdma->connected = false; } @@ -2605,6 +2599,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, int ret; ssize_t done = 0; size_t i; + size_t len = 0; CHECK_ERROR_STATE(); @@ -2624,10 +2619,10 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, while (remaining) { RDMAControlHeader head; - rioc->len = MIN(remaining, RDMA_SEND_INCREMENT); - remaining -= rioc->len; + len = MIN(remaining, RDMA_SEND_INCREMENT); + remaining -= len; - head.len = rioc->len; + head.len = len; head.type = RDMA_CONTROL_QEMU_FILE; ret = qemu_rdma_exchange_send(rdma, &head, data, NULL, NULL, NULL); @@ -2637,8 +2632,8 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, return ret; } - data += rioc->len; - done += rioc->len; + data += len; + done += len; } } @@ -2733,8 +2728,7 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, } } } - rioc->len = done; - return rioc->len; + return done; } /* @@ -3246,6 +3240,10 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) qsort(rdma->local_ram_blocks.block, rdma->local_ram_blocks.nb_blocks, sizeof(RDMALocalBlock), dest_ram_sort_func); + for (i = 0; i < local->nb_blocks; i++) { + local->block[i].index = i; + } + if (rdma->pin_all) { ret = qemu_rdma_reg_whole_ram_blocks(rdma); if (ret) { diff --git a/migration/savevm.c b/migration/savevm.c index e2be02afe4..7f92567a10 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -55,6 +55,7 @@ #include "io/channel-buffer.h" #include "io/channel-file.h" #include "sysemu/replay.h" +#include "qjson.h" #ifndef ETH_P_RARP #define ETH_P_RARP 0x8035 @@ -81,6 +82,8 @@ enum qemu_vm_cmd { were previously sent during precopy but are dirty. */ MIG_CMD_PACKAGED, /* Send a wrapped stream within this stream */ + MIG_CMD_POSTCOPY_RESUME, /* resume postcopy on dest */ + MIG_CMD_RECV_BITMAP, /* Request for recved bitmap on dst */ MIG_CMD_MAX }; @@ -97,7 +100,9 @@ static struct mig_cmd_args { [MIG_CMD_POSTCOPY_RUN] = { .len = 0, .name = "POSTCOPY_RUN" }, [MIG_CMD_POSTCOPY_RAM_DISCARD] = { .len = -1, .name = "POSTCOPY_RAM_DISCARD" }, + [MIG_CMD_POSTCOPY_RESUME] = { .len = 0, .name = "POSTCOPY_RESUME" }, [MIG_CMD_PACKAGED] = { .len = 4, .name = "PACKAGED" }, + [MIG_CMD_RECV_BITMAP] = { .len = -1, .name = "RECV_BITMAP" }, [MIG_CMD_MAX] = { .len = -1, .name = "MAX" }, }; @@ -956,6 +961,25 @@ void qemu_savevm_send_postcopy_run(QEMUFile *f) qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_RUN, 0, NULL); } +void qemu_savevm_send_postcopy_resume(QEMUFile *f) +{ + trace_savevm_send_postcopy_resume(); + qemu_savevm_command_send(f, MIG_CMD_POSTCOPY_RESUME, 0, NULL); +} + +void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name) +{ + size_t len; + char buf[256]; + + trace_savevm_send_recv_bitmap(block_name); + + buf[0] = len = strlen(block_name); + memcpy(buf + 1, block_name, len); + + qemu_savevm_command_send(f, MIG_CMD_RECV_BITMAP, len + 1, (uint8_t *)buf); +} + bool qemu_savevm_state_blocked(Error **errp) { SaveStateEntry *se; @@ -1008,6 +1032,31 @@ void qemu_savevm_state_setup(QEMUFile *f) } } +int qemu_savevm_state_resume_prepare(MigrationState *s) +{ + SaveStateEntry *se; + int ret; + + trace_savevm_state_resume_prepare(); + + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (!se->ops || !se->ops->resume_prepare) { + continue; + } + if (se->ops && se->ops->is_active) { + if (!se->ops->is_active(se->opaque)) { + continue; + } + } + ret = se->ops->resume_prepare(s, se->opaque); + if (ret < 0) { + return ret; + } + } + + return 0; +} + /* * this function has three return values: * negative: there was one error, and we have -errno. @@ -1564,8 +1613,8 @@ static int loadvm_postcopy_ram_handle_discard(MigrationIncomingState *mis, */ static void *postcopy_ram_listen_thread(void *opaque) { - QEMUFile *f = opaque; MigrationIncomingState *mis = migration_incoming_get_current(); + QEMUFile *f = mis->from_src_file; int load_res; migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, @@ -1579,6 +1628,14 @@ static void *postcopy_ram_listen_thread(void *opaque) */ qemu_file_set_blocking(f, true); load_res = qemu_loadvm_state_main(f, mis); + + /* + * This is tricky, but, mis->from_src_file can change after it + * returns, when postcopy recovery happened. In the future, we may + * want a wrapper for the QEMUFile handle. + */ + f = mis->from_src_file; + /* And non-blocking again so we don't block in any cleanup */ qemu_file_set_blocking(f, false); @@ -1668,7 +1725,7 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) /* Start up the listening thread and wait for it to signal ready */ qemu_sem_init(&mis->listen_thread_sem, 0); qemu_thread_create(&mis->listen_thread, "postcopy/listen", - postcopy_ram_listen_thread, mis->from_src_file, + postcopy_ram_listen_thread, NULL, QEMU_THREAD_DETACHED); qemu_sem_wait(&mis->listen_thread_sem); qemu_sem_destroy(&mis->listen_thread_sem); @@ -1745,6 +1802,31 @@ static int loadvm_postcopy_handle_run(MigrationIncomingState *mis) return LOADVM_QUIT; } +static int loadvm_postcopy_handle_resume(MigrationIncomingState *mis) +{ + if (mis->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { + error_report("%s: illegal resume received", __func__); + /* Don't fail the load, only for this. */ + return 0; + } + + /* + * This means source VM is ready to resume the postcopy migration. + * It's time to switch state and release the fault thread to + * continue service page faults. + */ + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_RECOVER, + MIGRATION_STATUS_POSTCOPY_ACTIVE); + qemu_sem_post(&mis->postcopy_pause_sem_fault); + + trace_loadvm_postcopy_handle_resume(); + + /* Tell source that "we are ready" */ + migrate_send_rp_resume_ack(mis, MIGRATION_RESUME_ACK_VALUE); + + return 0; +} + /** * Immediately following this command is a blob of data containing an embedded * chunk of migration stream; read it and load it. @@ -1793,6 +1875,49 @@ static int loadvm_handle_cmd_packaged(MigrationIncomingState *mis) return ret; } +/* + * Handle request that source requests for recved_bitmap on + * destination. Payload format: + * + * len (1 byte) + ramblock_name (<255 bytes) + */ +static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis, + uint16_t len) +{ + QEMUFile *file = mis->from_src_file; + RAMBlock *rb; + char block_name[256]; + size_t cnt; + + cnt = qemu_get_counted_string(file, block_name); + if (!cnt) { + error_report("%s: failed to read block name", __func__); + return -EINVAL; + } + + /* Validate before using the data */ + if (qemu_file_get_error(file)) { + return qemu_file_get_error(file); + } + + if (len != cnt + 1) { + error_report("%s: invalid payload length (%d)", __func__, len); + return -EINVAL; + } + + rb = qemu_ram_block_by_name(block_name); + if (!rb) { + error_report("%s: block '%s' not found", __func__, block_name); + return -EINVAL; + } + + migrate_send_rp_recv_bitmap(mis, block_name); + + trace_loadvm_handle_recv_bitmap(block_name); + + return 0; +} + /* * Process an incoming 'QEMU_VM_COMMAND' * 0 just a normal return @@ -1866,6 +1991,12 @@ static int loadvm_process_command(QEMUFile *f) case MIG_CMD_POSTCOPY_RAM_DISCARD: return loadvm_postcopy_ram_handle_discard(mis, len); + + case MIG_CMD_POSTCOPY_RESUME: + return loadvm_postcopy_handle_resume(mis); + + case MIG_CMD_RECV_BITMAP: + return loadvm_handle_recv_bitmap(mis, len); } return 0; @@ -2055,11 +2186,50 @@ void qemu_loadvm_state_cleanup(void) } } +/* Return true if we should continue the migration, or false. */ +static bool postcopy_pause_incoming(MigrationIncomingState *mis) +{ + trace_postcopy_pause_incoming(); + + /* Clear the triggered bit to allow one recovery */ + mis->postcopy_recover_triggered = false; + + assert(mis->from_src_file); + qemu_file_shutdown(mis->from_src_file); + qemu_fclose(mis->from_src_file); + mis->from_src_file = NULL; + + assert(mis->to_src_file); + qemu_file_shutdown(mis->to_src_file); + qemu_mutex_lock(&mis->rp_mutex); + qemu_fclose(mis->to_src_file); + mis->to_src_file = NULL; + qemu_mutex_unlock(&mis->rp_mutex); + + migrate_set_state(&mis->state, MIGRATION_STATUS_POSTCOPY_ACTIVE, + MIGRATION_STATUS_POSTCOPY_PAUSED); + + /* Notify the fault thread for the invalidated file handle */ + postcopy_fault_thread_notify(mis); + + error_report("Detected IO failure for postcopy. " + "Migration paused."); + + while (mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + qemu_sem_wait(&mis->postcopy_pause_sem_dst); + } + + trace_postcopy_pause_incoming_continued(); + + return true; +} + static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) { uint8_t section_type; int ret = 0; +retry: while (true) { section_type = qemu_get_byte(f); @@ -2104,6 +2274,20 @@ static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) out: if (ret < 0) { qemu_file_set_error(f, ret); + + /* + * If we are during an active postcopy, then we pause instead + * of bail out to at least keep the VM's dirty data. Note + * that POSTCOPY_INCOMING_LISTENING stage is still not enough, + * during which we're still receiving device states and we + * still haven't yet started the VM on destination. + */ + if (postcopy_state_get() == POSTCOPY_INCOMING_RUNNING && + postcopy_pause_incoming(mis)) { + /* Reset f to point to the newly created channel */ + f = mis->from_src_file; + goto retry; + } } return ret; } @@ -2501,11 +2685,13 @@ void vmstate_register_ram(MemoryRegion *mr, DeviceState *dev) { qemu_ram_set_idstr(mr->ram_block, memory_region_name(mr), dev); + qemu_ram_set_migratable(mr->ram_block); } void vmstate_unregister_ram(MemoryRegion *mr, DeviceState *dev) { qemu_ram_unset_idstr(mr->ram_block); + qemu_ram_unset_migratable(mr->ram_block); } void vmstate_register_ram_global(MemoryRegion *mr) diff --git a/migration/savevm.h b/migration/savevm.h index cf4f0d37ca..a5e65b8ae3 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -31,6 +31,7 @@ bool qemu_savevm_state_blocked(Error **errp); void qemu_savevm_state_setup(QEMUFile *f); +int qemu_savevm_state_resume_prepare(MigrationState *s); void qemu_savevm_state_header(QEMUFile *f); int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy); void qemu_savevm_state_cleanup(void); @@ -47,6 +48,8 @@ int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len); void qemu_savevm_send_postcopy_advise(QEMUFile *f); void qemu_savevm_send_postcopy_listen(QEMUFile *f); void qemu_savevm_send_postcopy_run(QEMUFile *f); +void qemu_savevm_send_postcopy_resume(QEMUFile *f); +void qemu_savevm_send_recv_bitmap(QEMUFile *f, char *block_name); void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name, uint16_t len, diff --git a/migration/socket.c b/migration/socket.c index 122d8ccfbe..f4c8174400 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -28,6 +28,28 @@ #include "trace.h" +struct SocketOutgoingArgs { + SocketAddress *saddr; +} outgoing_args; + +void socket_send_channel_create(QIOTaskFunc f, void *data) +{ + QIOChannelSocket *sioc = qio_channel_socket_new(); + qio_channel_socket_connect_async(sioc, outgoing_args.saddr, + f, data, NULL, NULL); +} + +int socket_send_channel_destroy(QIOChannel *send) +{ + /* Remove channel */ + object_unref(OBJECT(send)); + if (outgoing_args.saddr) { + qapi_free_SocketAddress(outgoing_args.saddr); + outgoing_args.saddr = NULL; + } + return 0; +} + static SocketAddress *tcp_build_address(const char *host_port, Error **errp) { SocketAddress *saddr; @@ -95,6 +117,11 @@ static void socket_start_outgoing_migration(MigrationState *s, struct SocketConnectData *data = g_new0(struct SocketConnectData, 1); data->s = s; + + /* in case previous migration leaked it */ + qapi_free_SocketAddress(outgoing_args.saddr); + outgoing_args.saddr = saddr; + if (saddr->type == SOCKET_ADDRESS_TYPE_INET) { data->hostname = g_strdup(saddr->u.inet.host); } @@ -106,7 +133,6 @@ static void socket_start_outgoing_migration(MigrationState *s, data, socket_connect_data_free, NULL); - qapi_free_SocketAddress(saddr); } void tcp_start_outgoing_migration(MigrationState *s, @@ -142,7 +168,6 @@ static void socket_accept_incoming_migration(QIONetListener *listener, if (migration_has_all_channels()) { /* Close listening socket as its no longer needed */ qio_net_listener_disconnect(listener); - object_unref(OBJECT(listener)); } } @@ -160,9 +185,10 @@ static void socket_start_incoming_migration(SocketAddress *saddr, return; } - qio_net_listener_set_client_func(listener, - socket_accept_incoming_migration, - NULL, NULL); + qio_net_listener_set_client_func_full(listener, + socket_accept_incoming_migration, + NULL, NULL, + g_main_context_get_thread_default()); } void tcp_start_incoming_migration(const char *host_port, Error **errp) diff --git a/migration/socket.h b/migration/socket.h index 6b91e9db38..528c3b0202 100644 --- a/migration/socket.h +++ b/migration/socket.h @@ -16,6 +16,13 @@ #ifndef QEMU_MIGRATION_SOCKET_H #define QEMU_MIGRATION_SOCKET_H + +#include "io/channel.h" +#include "io/task.h" + +void socket_send_channel_create(QIOTaskFunc f, void *data); +int socket_send_channel_destroy(QIOChannel *send); + void tcp_start_incoming_migration(const char *host_port, Error **errp); void tcp_start_outgoing_migration(MigrationState *s, const char *host_port, diff --git a/migration/trace-events b/migration/trace-events index a180d7b008..9430f3cbe0 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -12,11 +12,13 @@ loadvm_state_cleanup(void) "" loadvm_handle_cmd_packaged(unsigned int length) "%u" loadvm_handle_cmd_packaged_main(int ret) "%d" loadvm_handle_cmd_packaged_received(int ret) "%d" +loadvm_handle_recv_bitmap(char *s) "%s" loadvm_postcopy_handle_advise(void) "" loadvm_postcopy_handle_listen(void) "" loadvm_postcopy_handle_run(void) "" loadvm_postcopy_handle_run_cpu_sync(void) "" loadvm_postcopy_handle_run_vmstart(void) "" +loadvm_postcopy_handle_resume(void) "" loadvm_postcopy_ram_handle_discard(void) "" loadvm_postcopy_ram_handle_discard_end(void) "" loadvm_postcopy_ram_handle_discard_header(const char *ramid, uint16_t len) "%s: %ud" @@ -34,7 +36,10 @@ savevm_send_open_return_path(void) "" savevm_send_ping(uint32_t val) "0x%x" savevm_send_postcopy_listen(void) "" savevm_send_postcopy_run(void) "" +savevm_send_postcopy_resume(void) "" +savevm_send_recv_bitmap(char *name) "%s" savevm_state_setup(void) "" +savevm_state_resume_prepare(void) "" savevm_state_header(void) "" savevm_state_iterate(void) "" savevm_state_cleanup(void) "" @@ -71,12 +76,31 @@ get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned migration_bitmap_sync_start(void) "" migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_throttle(void) "" +multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags) "channel %d packet number %" PRIu64 " pages %d flags 0x%x" +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_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) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x" +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_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" ram_discard_range(const char *rbname, uint64_t start, size_t len) "%s: start: %" PRIx64 " %zx" ram_load_loop(const char *rbname, uint64_t addr, int flags, void *host) "%s: addr: 0x%" PRIx64 " flags: 0x%x host: %p" ram_load_postcopy_loop(uint64_t addr, int flags) "@%" PRIx64 " %x" ram_postcopy_send_discard_bitmap(void) "" ram_save_page(const char *rbname, uint64_t offset, void *host) "%s: offset: 0x%" PRIx64 " host: %p" ram_save_queue_pages(const char *rbname, size_t start, size_t len) "%s: start: 0x%zx len: 0x%zx" +ram_dirty_bitmap_request(char *str) "%s" +ram_dirty_bitmap_reload_begin(char *str) "%s" +ram_dirty_bitmap_reload_complete(char *str) "%s" +ram_dirty_bitmap_sync_start(void) "" +ram_dirty_bitmap_sync_wait(void) "" +ram_dirty_bitmap_sync_complete(void) "" +ram_state_resume_prepare(uint64_t v) "%" PRId64 # migration/migration.c await_return_path_close_on_source_close(void) "" @@ -88,6 +112,7 @@ migrate_fd_cancel(void) "" migrate_handle_rp_req_pages(const char *rbname, size_t start, size_t len) "in %s at 0x%zx len 0x%zx" migrate_pending(uint64_t size, uint64_t max, uint64_t pre, uint64_t compat, uint64_t post) "pending size %" PRIu64 " max %" PRIu64 " (pre = %" PRIu64 " compat=%" PRIu64 " post=%" PRIu64 ")" migrate_send_rp_message(int msg_type, uint16_t len) "%d: len %d" +migrate_send_rp_recv_bitmap(char *name, int64_t size) "block '%s' size 0x%"PRIi64 migration_completion_file_err(void) "" migration_completion_postcopy_end(void) "" migration_completion_postcopy_end_after_complete(void) "" @@ -95,10 +120,19 @@ migration_return_path_end_before(void) "" migration_return_path_end_after(int rp_error) "%d" migration_thread_after_loop(void) "" migration_thread_file_err(void) "" +migration_thread_ratelimit_pre(int ms) "%d ms" +migration_thread_ratelimit_post(int urgent) "urgent: %d" migration_thread_setup_complete(void) "" open_return_path_on_source(void) "" open_return_path_on_source_continue(void) "" postcopy_start(void) "" +postcopy_pause_return_path(void) "" +postcopy_pause_return_path_continued(void) "" +postcopy_pause_fault_thread(void) "" +postcopy_pause_fault_thread_continued(void) "" +postcopy_pause_continued(void) "" +postcopy_pause_incoming(void) "" +postcopy_pause_incoming_continued(void) "" postcopy_start_set_run(void) "" source_return_path_thread_bad_end(void) "" source_return_path_thread_end(void) "" @@ -106,15 +140,18 @@ source_return_path_thread_entry(void) "" source_return_path_thread_loop_top(void) "" source_return_path_thread_pong(uint32_t val) "0x%x" source_return_path_thread_shut(uint32_t val) "0x%x" +source_return_path_thread_resume_ack(uint32_t v) "%"PRIu32 migrate_global_state_post_load(const char *state) "loaded state: %s" migrate_global_state_pre_save(const char *state) "saved state: %s" migration_thread_low_pending(uint64_t pending) "%" PRIu64 migrate_state_too_big(void) "" -migrate_transferred(uint64_t tranferred, uint64_t time_spent, double bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %g max_size %" PRId64 +migrate_transferred(uint64_t tranferred, uint64_t time_spent, uint64_t bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " max_size %" PRId64 process_incoming_migration_co_end(int ret, int ps) "ret=%d postcopy-state=%d" process_incoming_migration_co_postcopy_end_main(void) "" migration_set_incoming_channel(void *ioc, const char *ioctype) "ioc=%p ioctype=%s" migration_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname, void *err) "ioc=%p ioctype=%s hostname=%s err=%p" +mark_postcopy_blocktime_begin(uint64_t addr, void *dd, uint32_t time, int cpu, int received) "addr: 0x%" PRIx64 ", dd: %p, time: %u, cpu: %d, already_received: %d" +mark_postcopy_blocktime_end(uint64_t addr, void *dd, uint32_t time, int affected_cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, affected_cpu: %d" # migration/rdma.c qemu_rdma_accept_incoming_migration(void) "" @@ -123,7 +160,6 @@ qemu_rdma_accept_pin_state(bool pin) "%d" qemu_rdma_accept_pin_verbsc(void *verbs) "Verbs context after listen: %p" qemu_rdma_block_for_wrid_miss(const char *wcompstr, int wcomp, const char *gcompstr, uint64_t req) "A Wanted wrid %s (%d) but got %s (%" PRIu64 ")" qemu_rdma_cleanup_disconnect(void) "" -qemu_rdma_cleanup_waiting_for_disconnect(void) "" qemu_rdma_close(void) "" qemu_rdma_connect_pin_all_requested(void) "" qemu_rdma_connect_pin_all_outcome(bool pin) "%d" @@ -193,11 +229,12 @@ postcopy_ram_fault_thread_exit(void) "" postcopy_ram_fault_thread_fds_core(int baseufd, int quitfd) "ufd: %d quitfd: %d" postcopy_ram_fault_thread_fds_extra(size_t index, const char *name, int fd) "%zd/%s: %d" postcopy_ram_fault_thread_quit(void) "" -postcopy_ram_fault_thread_request(uint64_t hostaddr, const char *ramblock, size_t offset) "Request for HVA=0x%" PRIx64 " rb=%s offset=0x%zx" +postcopy_ram_fault_thread_request(uint64_t hostaddr, const char *ramblock, size_t offset, uint32_t pid) "Request for HVA=0x%" PRIx64 " rb=%s offset=0x%zx pid=%u" postcopy_ram_incoming_cleanup_closeuf(void) "" postcopy_ram_incoming_cleanup_entry(void) "" postcopy_ram_incoming_cleanup_exit(void) "" postcopy_ram_incoming_cleanup_join(void) "" +postcopy_ram_incoming_cleanup_blocktime(uint64_t total) "total blocktime %" PRIu64 postcopy_request_shared_page(const char *sharer, const char *rb, uint64_t rb_offset) "for %s in %s offset 0x%"PRIx64 postcopy_request_shared_page_present(const char *sharer, const char *rb, uint64_t rb_offset) "%s already %s offset 0x%"PRIx64 postcopy_wake_shared(uint64_t client_addr, const char *rb) "at 0x%"PRIx64" in %s" @@ -206,6 +243,7 @@ save_xbzrle_page_skipping(void) "" save_xbzrle_page_overflow(void) "" ram_save_iterate_big_wait(uint64_t milliconds, int iterations) "big wait: %" PRIu64 " milliseconds, %d iterations" ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64 +get_mem_fault_cpu_index(int cpu, uint32_t pid) "cpu: %d, pid: %u" # migration/exec.c migration_exec_outgoing(const char *cmd) "cmd=%s" diff --git a/migration/vmstate.c b/migration/vmstate.c index 0b3282c9df..6b9079bb51 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -14,7 +14,7 @@ #include "qemu-common.h" #include "migration.h" #include "migration/vmstate.h" -#include "migration/savevm.h" +#include "savevm.h" #include "qemu-file.h" #include "qemu/bitops.h" #include "qemu/error-report.h" @@ -136,6 +136,9 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, } else if (field->flags & VMS_STRUCT) { ret = vmstate_load_state(f, field->vmsd, curr_elem, field->vmsd->version_id); + } else if (field->flags & VMS_VSTRUCT) { + ret = vmstate_load_state(f, field->vmsd, curr_elem, + field->struct_version_id); } else { ret = field->info->get(f, curr_elem, size, field); } @@ -209,6 +212,8 @@ static const char *vmfield_get_type_name(VMStateField *field) if (field->flags & VMS_STRUCT) { type = "struct"; + } else if (field->flags & VMS_VSTRUCT) { + type = "vstruct"; } else if (field->info->name) { type = field->info->name; } @@ -309,7 +314,13 @@ bool vmstate_save_needed(const VMStateDescription *vmsd, void *opaque) int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, QJSON *vmdesc) + void *opaque, QJSON *vmdesc_id) +{ + return vmstate_save_state_v(f, vmsd, opaque, vmdesc_id, vmsd->version_id); +} + +int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque, QJSON *vmdesc, int version_id) { int ret = 0; VMStateField *field = vmsd->fields; @@ -327,13 +338,15 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, if (vmdesc) { json_prop_str(vmdesc, "vmsd_name", vmsd->name); - json_prop_int(vmdesc, "version", vmsd->version_id); + json_prop_int(vmdesc, "version", version_id); json_start_array(vmdesc, "fields"); } while (field->name) { - if (!field->field_exists || - field->field_exists(opaque, vmsd->version_id)) { + if ((field->field_exists && + field->field_exists(opaque, version_id)) || + (!field->field_exists && + field->version_id <= version_id)) { void *first_elem = opaque + field->offset; int i, n_elems = vmstate_n_elems(opaque, field); int size = vmstate_size(opaque, field); @@ -363,6 +376,10 @@ int vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, } else if (field->flags & VMS_STRUCT) { ret = vmstate_save_state(f, field->vmsd, curr_elem, vmdesc_loop); + } else if (field->flags & VMS_VSTRUCT) { + ret = vmstate_save_state_v(f, field->vmsd, curr_elem, + vmdesc_loop, + field->struct_version_id); } else { ret = field->info->put(f, curr_elem, size, field, vmdesc_loop); diff --git a/monitor.c b/monitor.c index 39f8ee17ba..77861e96af 100644 --- a/monitor.c +++ b/monitor.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include #include "cpu.h" #include "hw/hw.h" @@ -44,7 +45,6 @@ #include "qemu/readline.h" #include "ui/console.h" #include "ui/input.h" -#include "sysemu/blockdev.h" #include "sysemu/block-backend.h" #include "audio/audio.h" #include "disas/disas.h" @@ -129,6 +129,7 @@ typedef struct mon_cmd_t { const char *args_type; const char *params; const char *help; + const char *flags; /* p=preconfig */ void (*cmd)(Monitor *mon, const QDict *qdict); /* @sub_table is a list of 2nd level of commands. If it does not exist, * cmd should be used. If it exists, sub_table[?].cmd should be @@ -168,14 +169,16 @@ typedef struct { JSONMessageParser parser; /* * When a client connects, we're in capabilities negotiation mode. - * When command qmp_capabilities succeeds, we go into command - * mode. + * @commands is &qmp_cap_negotiation_commands then. When command + * qmp_capabilities succeeds, we go into command mode, and + * @command becomes &qmp_commands. */ QmpCommandList *commands; - bool qmp_caps[QMP_CAPABILITY__MAX]; + bool capab_offered[QMP_CAPABILITY__MAX]; /* capabilities offered */ + bool capab[QMP_CAPABILITY__MAX]; /* offered and accepted */ /* - * Protects qmp request/response queue. Please take monitor_lock - * first when used together. + * Protects qmp request/response queue. + * Take monitor_lock first when you need both. */ QemuMutex qmp_queue_lock; /* Input queue that holds all the parsed QMP requests */ @@ -206,42 +209,59 @@ struct Monitor { int flags; int suspend_cnt; /* Needs to be accessed atomically */ bool skip_flush; - bool use_io_thr; - - /* We can't access guest memory when holding the lock */ - QemuMutex out_lock; - QString *outbuf; - guint out_watch; - - /* Read under either BQL or out_lock, written with BQL+out_lock. */ - int mux_out; + bool use_io_thread; + /* + * State used only in the thread "owning" the monitor. + * If @use_io_thread, this is @mon_iothread. + * Else, it's the main thread. + * These members can be safely accessed without locks. + */ ReadLineState *rs; + MonitorQMP qmp; gchar *mon_cpu_path; BlockCompletionFunc *password_completion_cb; void *password_opaque; mon_cmd_t *cmd_table; - QLIST_HEAD(,mon_fd_t) fds; QTAILQ_ENTRY(Monitor) entry; + + /* + * The per-monitor lock. We can't access guest memory when holding + * the lock. + */ + QemuMutex mon_lock; + + /* + * Members that are protected by the per-monitor lock + */ + QLIST_HEAD(, mon_fd_t) fds; + QString *outbuf; + guint out_watch; + /* Read under either BQL or mon_lock, written with BQL+mon_lock. */ + int mux_out; }; -/* Let's add monitor global variables to this struct. */ -static struct { - IOThread *mon_iothread; - /* Bottom half to dispatch the requests received from IO thread */ - QEMUBH *qmp_dispatcher_bh; - /* Bottom half to deliver the responses back to clients */ - QEMUBH *qmp_respond_bh; -} mon_global; +/* Shared monitor I/O thread */ +IOThread *mon_iothread; + +/* Bottom half to dispatch the requests received from I/O thread */ +QEMUBH *qmp_dispatcher_bh; + +/* Bottom half to deliver the responses back to clients */ +QEMUBH *qmp_respond_bh; struct QMPRequest { /* Owner of the request */ Monitor *mon; /* "id" field of the request */ QObject *id; - /* Request object to be handled */ + /* + * Request object to be handled or Error to be reported + * (exactly one of them is non-null) + */ QObject *req; + Error *err; /* * Whether we need to resume the monitor afterward. This flag is * used to emulate the old QMP server behavior that the current @@ -254,11 +274,15 @@ typedef struct QMPRequest QMPRequest; /* QMP checker flags */ #define QMP_ACCEPT_UNKNOWNS 1 -/* Protects mon_list, monitor_event_state. */ +/* Protects mon_list, monitor_qapi_event_state. */ static QemuMutex monitor_lock; - +static GHashTable *monitor_qapi_event_state; static QTAILQ_HEAD(mon_list, Monitor) mon_list; + +/* Protects mon_fdsets */ +static QemuMutex mon_fdsets_lock; static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets; + static int mon_refcount; static mon_cmd_t mon_cmds[]; @@ -266,9 +290,7 @@ static mon_cmd_t info_cmds[]; QmpCommandList qmp_commands, qmp_cap_negotiation_commands; -Monitor *cur_mon; - -static QEMUClockType event_clock_type = QEMU_CLOCK_REALTIME; +__thread Monitor *cur_mon; static void monitor_command_cb(void *opaque, const char *cmdline, void *readline_opaque); @@ -282,9 +304,9 @@ static inline bool monitor_is_qmp(const Monitor *mon) } /** - * Whether @mon is using readline? Note: not all HMP monitors use - * readline, e.g., gdbserver has a non-interactive HMP monitor, so - * readline is not used there. + * Is @mon is using readline? + * Note: not all HMP monitors use readline, e.g., gdbserver has a + * non-interactive HMP monitor, so readline is not used there. */ static inline bool monitor_uses_readline(const Monitor *mon) { @@ -296,6 +318,17 @@ static inline bool monitor_is_hmp_non_interactive(const Monitor *mon) return !monitor_is_qmp(mon) && !monitor_uses_readline(mon); } +/* + * Return the clock to use for recording an event's time. + * It's QEMU_CLOCK_REALTIME, except for qtests it's + * QEMU_CLOCK_VIRTUAL, to support testing rate limits. + * Beware: result is invalid before configure_accelerator(). + */ +static inline QEMUClockType monitor_get_event_clock(void) +{ + return qtest_enabled() ? QEMU_CLOCK_VIRTUAL : QEMU_CLOCK_REALTIME; +} + /** * Is the current monitor, if any, a QMP monitor? */ @@ -329,12 +362,13 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, static void qmp_request_free(QMPRequest *req) { - qobject_decref(req->id); - qobject_decref(req->req); + qobject_unref(req->id); + qobject_unref(req->req); + error_free(req->err); g_free(req); } -/* Must with the mon->qmp.qmp_queue_lock held */ +/* Caller must hold mon->qmp.qmp_queue_lock */ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) { while (!g_queue_is_empty(mon->qmp.qmp_requests)) { @@ -342,11 +376,11 @@ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) } } -/* Must with the mon->qmp.qmp_queue_lock held */ +/* Caller must hold the mon->qmp.qmp_queue_lock */ static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon) { while (!g_queue_is_empty(mon->qmp.qmp_responses)) { - qobject_decref(g_queue_pop_head(mon->qmp.qmp_responses)); + qobject_unref((QDict *)g_queue_pop_head(mon->qmp.qmp_responses)); } } @@ -366,14 +400,14 @@ static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond, { Monitor *mon = opaque; - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); mon->out_watch = 0; monitor_flush_locked(mon); - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); return FALSE; } -/* Called with mon->out_lock held. */ +/* Caller must hold mon->mon_lock */ static void monitor_flush_locked(Monitor *mon) { int rc; @@ -391,14 +425,14 @@ static void monitor_flush_locked(Monitor *mon) rc = qemu_chr_fe_write(&mon->chr, (const uint8_t *) buf, len); if ((rc < 0 && errno != EAGAIN) || (rc == len)) { /* all flushed or error */ - QDECREF(mon->outbuf); + qobject_unref(mon->outbuf); mon->outbuf = qstring_new(); return; } if (rc > 0) { /* partial write */ QString *tmp = qstring_from_str(buf + rc); - QDECREF(mon->outbuf); + qobject_unref(mon->outbuf); mon->outbuf = tmp; } if (mon->out_watch == 0) { @@ -411,9 +445,9 @@ static void monitor_flush_locked(Monitor *mon) void monitor_flush(Monitor *mon) { - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); monitor_flush_locked(mon); - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); } /* flush at every end of line */ @@ -421,7 +455,7 @@ static void monitor_puts(Monitor *mon, const char *str) { char c; - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); for(;;) { c = *str++; if (c == '\0') @@ -434,7 +468,7 @@ static void monitor_puts(Monitor *mon, const char *str) monitor_flush_locked(mon); } } - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); } void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) @@ -470,9 +504,9 @@ int monitor_fprintf(FILE *stream, const char *fmt, ...) return 0; } -static void monitor_json_emitter_raw(Monitor *mon, - QObject *data) +static void qmp_send_response(Monitor *mon, QDict *rsp) { + QObject *data = QOBJECT(rsp); QString *json; json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) : @@ -482,71 +516,85 @@ static void monitor_json_emitter_raw(Monitor *mon, qstring_append_chr(json, '\n'); monitor_puts(mon, qstring_get_str(json)); - QDECREF(json); + qobject_unref(json); } -static void monitor_json_emitter(Monitor *mon, QObject *data) +static void qmp_queue_response(Monitor *mon, QDict *rsp) { - if (mon->use_io_thr) { + if (mon->use_io_thread) { /* - * If using IO thread, we need to queue the item so that IO - * thread will do the rest for us. Take refcount so that - * caller won't free the data (which will be finally freed in - * responder thread). + * Push a reference to the response queue. The I/O thread + * drains that queue and emits. */ - qobject_incref(data); qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - g_queue_push_tail(mon->qmp.qmp_responses, (void *)data); + g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(rsp)); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); - qemu_bh_schedule(mon_global.qmp_respond_bh); + qemu_bh_schedule(qmp_respond_bh); } else { /* - * If not using monitor IO thread, then we are in main thread. - * Do the emission right away. + * Not using monitor I/O thread, i.e. we are in the main thread. + * Emit right away. */ - monitor_json_emitter_raw(mon, data); + qmp_send_response(mon, rsp); } } struct QMPResponse { Monitor *mon; - QObject *data; + QDict *data; }; typedef struct QMPResponse QMPResponse; +static QDict *monitor_qmp_response_pop_one(Monitor *mon) +{ + QDict *data; + + qemu_mutex_lock(&mon->qmp.qmp_queue_lock); + data = g_queue_pop_head(mon->qmp.qmp_responses); + qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); + + return data; +} + +static void monitor_qmp_response_flush(Monitor *mon) +{ + QDict *data; + + while ((data = monitor_qmp_response_pop_one(mon))) { + qmp_send_response(mon, data); + qobject_unref(data); + } +} + /* - * Return one QMPResponse. The response is only valid if - * response.data is not NULL. + * Pop a QMPResponse from any monitor's response queue into @response. + * Return false if all the queues are empty; else true. */ -static QMPResponse monitor_qmp_response_pop_one(void) +static bool monitor_qmp_response_pop_any(QMPResponse *response) { Monitor *mon; - QObject *data = NULL; + QDict *data = NULL; qemu_mutex_lock(&monitor_lock); QTAILQ_FOREACH(mon, &mon_list, entry) { - qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - data = g_queue_pop_head(mon->qmp.qmp_responses); - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); + data = monitor_qmp_response_pop_one(mon); if (data) { + response->mon = mon; + response->data = data; break; } } qemu_mutex_unlock(&monitor_lock); - return (QMPResponse) { .mon = mon, .data = data }; + return data != NULL; } static void monitor_qmp_bh_responder(void *opaque) { QMPResponse response; - while (true) { - response = monitor_qmp_response_pop_one(); - if (!response.data) { - break; - } - monitor_json_emitter_raw(response.mon, response.data); - qobject_decref(response.data); + while (monitor_qmp_response_pop_any(&response)) { + qmp_send_response(response.mon, response.data); + qobject_unref(response.data); } } @@ -560,11 +608,10 @@ static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { [QAPI_EVENT_VSERPORT_CHANGE] = { 1000 * SCALE_MS }, }; -GHashTable *monitor_qapi_event_state; - /* - * Emits the event to every monitor instance, @event is only used for trace - * Called with monitor_lock held. + * Broadcast an event to all monitors. + * @qdict is the event object. Its member "event" must match @event. + * Caller must hold monitor_lock. */ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) { @@ -574,7 +621,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) QTAILQ_FOREACH(mon, &mon_list, entry) { if (monitor_is_qmp(mon) && mon->qmp.commands != &qmp_cap_negotiation_commands) { - monitor_json_emitter(mon, QOBJECT(qdict)); + qmp_queue_response(mon, qdict); } } } @@ -586,7 +633,7 @@ static void monitor_qapi_event_handler(void *opaque); * applying any rate limiting if required. */ static void -monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) +monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict) { MonitorQAPIEventConf *evconf; MonitorQAPIEventState *evstate; @@ -613,9 +660,8 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) * last send. Store event for sending when timer fires, * replacing a prior stored event if any. */ - QDECREF(evstate->qdict); - evstate->qdict = qdict; - QINCREF(evstate->qdict); + qobject_unref(evstate->qdict); + evstate->qdict = qobject_ref(qdict); } else { /* * Last send was (at least) evconf->rate ns ago. @@ -623,16 +669,15 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) * monitor_qapi_event_handler() in evconf->rate ns. Any * events arriving before then will be delayed until then. */ - int64_t now = qemu_clock_get_ns(event_clock_type); + int64_t now = qemu_clock_get_ns(monitor_get_event_clock()); monitor_qapi_event_emit(event, qdict); evstate = g_new(MonitorQAPIEventState, 1); evstate->event = event; - evstate->data = data; - QINCREF(evstate->data); + evstate->data = qobject_ref(data); evstate->qdict = NULL; - evstate->timer = timer_new_ns(event_clock_type, + evstate->timer = timer_new_ns(monitor_get_event_clock(), monitor_qapi_event_handler, evstate); g_hash_table_add(monitor_qapi_event_state, evstate); @@ -643,6 +688,48 @@ monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) qemu_mutex_unlock(&monitor_lock); } +static void +monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) +{ + /* + * monitor_qapi_event_queue_no_reenter() is not reentrant: it + * would deadlock on monitor_lock. Work around by queueing + * events in thread-local storage. + * TODO: remove this, make it re-enter safe. + */ + typedef struct MonitorQapiEvent { + QAPIEvent event; + QDict *qdict; + QSIMPLEQ_ENTRY(MonitorQapiEvent) entry; + } MonitorQapiEvent; + static __thread QSIMPLEQ_HEAD(, MonitorQapiEvent) event_queue; + static __thread bool reentered; + MonitorQapiEvent *ev; + + if (!reentered) { + QSIMPLEQ_INIT(&event_queue); + } + + ev = g_new(MonitorQapiEvent, 1); + ev->qdict = qobject_ref(qdict); + ev->event = event; + QSIMPLEQ_INSERT_TAIL(&event_queue, ev, entry); + if (reentered) { + return; + } + + reentered = true; + + while ((ev = QSIMPLEQ_FIRST(&event_queue)) != NULL) { + QSIMPLEQ_REMOVE_HEAD(&event_queue, entry); + monitor_qapi_event_queue_no_reenter(ev->event, ev->qdict); + qobject_unref(ev->qdict); + g_free(ev); + } + + reentered = false; +} + /* * This function runs evconf->rate ns after sending a throttled * event. @@ -657,15 +744,15 @@ static void monitor_qapi_event_handler(void *opaque) qemu_mutex_lock(&monitor_lock); if (evstate->qdict) { - int64_t now = qemu_clock_get_ns(event_clock_type); + int64_t now = qemu_clock_get_ns(monitor_get_event_clock()); monitor_qapi_event_emit(evstate->event, evstate->qdict); - QDECREF(evstate->qdict); + qobject_unref(evstate->qdict); evstate->qdict = NULL; timer_mod_ns(evstate->timer, now + evconf->rate); } else { g_hash_table_remove(monitor_qapi_event_state, evstate); - QDECREF(evstate->data); + qobject_unref(evstate->data); timer_free(evstate->timer); g_free(evstate); } @@ -713,10 +800,6 @@ static gboolean qapi_event_throttle_equal(const void *a, const void *b) static void monitor_qapi_event_init(void) { - if (qtest_enabled()) { - event_clock_type = QEMU_CLOCK_VIRTUAL; - } - monitor_qapi_event_state = g_hash_table_new(qapi_event_throttle_hash, qapi_event_throttle_equal); qmp_event_set_func_emit(monitor_qapi_event_queue); @@ -725,16 +808,16 @@ static void monitor_qapi_event_init(void) static void handle_hmp_command(Monitor *mon, const char *cmdline); static void monitor_data_init(Monitor *mon, bool skip_flush, - bool use_io_thr) + bool use_io_thread) { memset(mon, 0, sizeof(Monitor)); - qemu_mutex_init(&mon->out_lock); + qemu_mutex_init(&mon->mon_lock); qemu_mutex_init(&mon->qmp.qmp_queue_lock); mon->outbuf = qstring_new(); /* Use *mon_cmds by default. */ mon->cmd_table = mon_cmds; mon->skip_flush = skip_flush; - mon->use_io_thr = use_io_thr; + mon->use_io_thread = use_io_thread; mon->qmp.qmp_requests = g_queue_new(); mon->qmp.qmp_responses = g_queue_new(); } @@ -747,8 +830,8 @@ static void monitor_data_destroy(Monitor *mon) json_message_parser_destroy(&mon->qmp.parser); } readline_free(mon->rs); - QDECREF(mon->outbuf); - qemu_mutex_destroy(&mon->out_lock); + qobject_unref(mon->outbuf); + qemu_mutex_destroy(&mon->mon_lock); qemu_mutex_destroy(&mon->qmp.qmp_queue_lock); monitor_qmp_cleanup_req_queue_locked(mon); monitor_qmp_cleanup_resp_queue_locked(mon); @@ -780,13 +863,13 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, handle_hmp_command(&hmp, command_line); cur_mon = old_mon; - qemu_mutex_lock(&hmp.out_lock); + qemu_mutex_lock(&hmp.mon_lock); if (qstring_get_length(hmp.outbuf) > 0) { output = g_strdup(qstring_get_str(hmp.outbuf)); } else { output = g_strdup(""); } - qemu_mutex_unlock(&hmp.out_lock); + qemu_mutex_unlock(&hmp.mon_lock); out: monitor_data_destroy(&hmp); @@ -801,9 +884,7 @@ static int compare_cmd(const char *name, const char *list) p = list; for(;;) { pstart = p; - p = strchr(p, '|'); - if (!p) - p = pstart + strlen(pstart); + p = qemu_strchrnul(p, '|'); if ((p - pstart) == len && !memcmp(pstart, name, len)) return 1; if (*p == '\0') @@ -940,6 +1021,18 @@ static int parse_cmdline(const char *cmdline, return -1; } +/* + * Can command @cmd be executed in preconfig state? + */ +static bool cmd_can_preconfig(const mon_cmd_t *cmd) +{ + if (!cmd->flags) { + return false; + } + + return strchr(cmd->flags, 'p'); +} + static void help_cmd_dump_one(Monitor *mon, const mon_cmd_t *cmd, char **prefix_args, @@ -947,6 +1040,10 @@ static void help_cmd_dump_one(Monitor *mon, { int i; + if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) { + return; + } + for (i = 0; i < prefix_args_nb; i++) { monitor_printf(mon, "%s ", prefix_args[i]); } @@ -969,7 +1066,9 @@ static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds, /* Find one entry to dump */ for (cmd = cmds; cmd->name != NULL; cmd++) { - if (compare_cmd(args[arg_index], cmd->name)) { + if (compare_cmd(args[arg_index], cmd->name) && + ((!runstate_check(RUN_STATE_PRECONFIG) || + cmd_can_preconfig(cmd)))) { if (cmd->sub_table) { /* continue with next arg */ help_cmd_dump(mon, cmd->sub_table, @@ -1137,9 +1236,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, */ static void qmp_unregister_commands_hack(void) { -#ifndef CONFIG_SPICE - qmp_unregister_command(&qmp_commands, "query-spice"); -#endif #ifndef CONFIG_REPLICATION qmp_unregister_command(&qmp_commands, "xen-set-replication"); qmp_unregister_command(&qmp_commands, "query-xen-replication-status"); @@ -1182,8 +1278,7 @@ static void monitor_init_qmp_commands(void) qmp_init_marshal(&qmp_commands); qmp_register_command(&qmp_commands, "query-qmp-schema", - qmp_query_qmp_schema, - QCO_NO_OPTIONS); + qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG); qmp_register_command(&qmp_commands, "device_add", qmp_device_add, QCO_NO_OPTIONS); qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, @@ -1193,99 +1288,59 @@ static void monitor_init_qmp_commands(void) QTAILQ_INIT(&qmp_cap_negotiation_commands); qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", - qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS); -} - -static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap) -{ - return mon->qmp.qmp_caps[cap]; + qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); } static bool qmp_oob_enabled(Monitor *mon) { - return qmp_cap_enabled(mon, QMP_CAPABILITY_OOB); + return mon->qmp.capab[QMP_CAPABILITY_OOB]; } -static void qmp_caps_check(Monitor *mon, QMPCapabilityList *list, - Error **errp) +static void monitor_qmp_caps_reset(Monitor *mon) { - for (; list; list = list->next) { - assert(list->value < QMP_CAPABILITY__MAX); - switch (list->value) { - case QMP_CAPABILITY_OOB: - if (!mon->use_io_thr) { - /* - * Out-Of-Band only works with monitors that are - * running on dedicated IOThread. - */ - error_setg(errp, "This monitor does not support " - "Out-Of-Band (OOB)"); - return; - } - break; - default: - break; - } - } -} - -/* This function should only be called after capabilities are checked. */ -static void qmp_caps_apply(Monitor *mon, QMPCapabilityList *list) -{ - for (; list; list = list->next) { - mon->qmp.qmp_caps[list->value] = true; - } + memset(mon->qmp.capab_offered, 0, sizeof(mon->qmp.capab_offered)); + memset(mon->qmp.capab, 0, sizeof(mon->qmp.capab)); + mon->qmp.capab_offered[QMP_CAPABILITY_OOB] = mon->use_io_thread; } /* - * Return true if check successful, or false otherwise. When false is - * returned, detailed error will be in errp if provided. + * Accept QMP capabilities in @list for @mon. + * On success, set mon->qmp.capab[], and return true. + * On error, set @errp, and return false. */ -static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp) +static bool qmp_caps_accept(Monitor *mon, QMPCapabilityList *list, + Error **errp) { - const char *command; - QmpCommand *cmd; + GString *unavailable = NULL; + bool capab[QMP_CAPABILITY__MAX]; - command = qdict_get_try_str(req, "execute"); - if (!command) { - error_setg(errp, "Command field 'execute' missing"); + memset(capab, 0, sizeof(capab)); + + for (; list; list = list->next) { + if (!mon->qmp.capab_offered[list->value]) { + if (!unavailable) { + unavailable = g_string_new(QMPCapability_str(list->value)); + } else { + g_string_append_printf(unavailable, ", %s", + QMPCapability_str(list->value)); + } + } + capab[list->value] = true; + } + + if (unavailable) { + error_setg(errp, "Capability %s not available", unavailable->str); + g_string_free(unavailable, true); return false; } - cmd = qmp_find_command(mon->qmp.commands, command); - if (!cmd) { - if (mon->qmp.commands == &qmp_cap_negotiation_commands) { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, - "Expecting capabilities negotiation " - "with 'qmp_capabilities'"); - } else { - error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, - "The command %s has not been found", command); - } - return false; - } - - if (qmp_is_oob(req)) { - if (!qmp_oob_enabled(mon)) { - error_setg(errp, "Please enable Out-Of-Band first " - "for the session during capabilities negotiation"); - return false; - } - if (!(cmd->options & QCO_ALLOW_OOB)) { - error_setg(errp, "The command %s does not support OOB", - command); - return false; - } - } - + memcpy(mon->qmp.capab, capab, sizeof(capab)); return true; } void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, Error **errp) { - Error *local_err = NULL; - if (cur_mon->qmp.commands == &qmp_commands) { error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, "Capabilities negotiation is already complete, command " @@ -1293,25 +1348,14 @@ void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable, return; } - /* Enable QMP capabilities provided by the client if applicable. */ - if (has_enable) { - qmp_caps_check(cur_mon, enable, &local_err); - if (local_err) { - /* - * Failed check on any of the capabilities will fail the - * entire command (and thus not apply any of the other - * capabilities that were also requested). - */ - error_propagate(errp, local_err); - return; - } - qmp_caps_apply(cur_mon, enable); + if (!qmp_caps_accept(cur_mon, enable, errp)) { + return; } cur_mon->qmp.commands = &qmp_commands; } -/* set the current CPU defined by the user */ +/* Set the current CPU defined by the user. Callers must hold BQL. */ int monitor_set_cpu(int cpu_index) { CPUState *cpu; @@ -1325,6 +1369,7 @@ int monitor_set_cpu(int cpu_index) return 0; } +/* Callers must hold BQL. */ static CPUState *mon_get_cpu_sync(bool synchronize) { CPUState *cpu; @@ -1969,8 +2014,10 @@ static void hmp_info_mtree(Monitor *mon, const QDict *qdict) { bool flatview = qdict_get_try_bool(qdict, "flatview", false); bool dispatch_tree = qdict_get_try_bool(qdict, "dispatch_tree", false); + bool owner = qdict_get_try_bool(qdict, "owner", false); - mtree_info((fprintf_function)monitor_printf, mon, flatview, dispatch_tree); + mtree_info((fprintf_function)monitor_printf, mon, flatview, dispatch_tree, + owner); } static void hmp_info_numa(Monitor *mon, const QDict *qdict) @@ -2187,7 +2234,7 @@ static void hmp_acl_remove(Monitor *mon, const QDict *qdict) void qmp_getfd(const char *fdname, Error **errp) { mon_fd_t *monfd; - int fd; + int fd, tmp_fd; fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); if (fd == -1) { @@ -2202,13 +2249,17 @@ void qmp_getfd(const char *fdname, Error **errp) return; } + qemu_mutex_lock(&cur_mon->mon_lock); QLIST_FOREACH(monfd, &cur_mon->fds, next) { if (strcmp(monfd->name, fdname) != 0) { continue; } - close(monfd->fd); + tmp_fd = monfd->fd; monfd->fd = fd; + qemu_mutex_unlock(&cur_mon->mon_lock); + /* Make sure close() is outside critical section */ + close(tmp_fd); return; } @@ -2217,24 +2268,31 @@ void qmp_getfd(const char *fdname, Error **errp) monfd->fd = fd; QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next); + qemu_mutex_unlock(&cur_mon->mon_lock); } void qmp_closefd(const char *fdname, Error **errp) { mon_fd_t *monfd; + int tmp_fd; + qemu_mutex_lock(&cur_mon->mon_lock); QLIST_FOREACH(monfd, &cur_mon->fds, next) { if (strcmp(monfd->name, fdname) != 0) { continue; } QLIST_REMOVE(monfd, next); - close(monfd->fd); + tmp_fd = monfd->fd; g_free(monfd->name); g_free(monfd); + qemu_mutex_unlock(&cur_mon->mon_lock); + /* Make sure close() is outside critical section */ + close(tmp_fd); return; } + qemu_mutex_unlock(&cur_mon->mon_lock); error_setg(errp, QERR_FD_NOT_FOUND, fdname); } @@ -2242,6 +2300,7 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) { mon_fd_t *monfd; + qemu_mutex_lock(&mon->mon_lock); QLIST_FOREACH(monfd, &mon->fds, next) { int fd; @@ -2255,10 +2314,12 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) QLIST_REMOVE(monfd, next); g_free(monfd->name); g_free(monfd); + qemu_mutex_unlock(&mon->mon_lock); return fd; } + qemu_mutex_unlock(&mon->mon_lock); error_setg(errp, "File descriptor named '%s' has not been found", fdname); return -1; } @@ -2290,9 +2351,11 @@ static void monitor_fdsets_cleanup(void) MonFdset *mon_fdset; MonFdset *mon_fdset_next; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) { monitor_fdset_cleanup(mon_fdset); } + qemu_mutex_unlock(&mon_fdsets_lock); } AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque, @@ -2327,6 +2390,7 @@ void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) MonFdsetFd *mon_fdset_fd; char fd_str[60]; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { if (mon_fdset->id != fdset_id) { continue; @@ -2346,10 +2410,12 @@ void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) goto error; } monitor_fdset_cleanup(mon_fdset); + qemu_mutex_unlock(&mon_fdsets_lock); return; } error: + qemu_mutex_unlock(&mon_fdsets_lock); if (has_fd) { snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64, fdset_id, fd); @@ -2365,6 +2431,7 @@ FdsetInfoList *qmp_query_fdsets(Error **errp) MonFdsetFd *mon_fdset_fd; FdsetInfoList *fdset_list = NULL; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { FdsetInfoList *fdset_info = g_malloc0(sizeof(*fdset_info)); FdsetFdInfoList *fdsetfd_list = NULL; @@ -2394,6 +2461,7 @@ FdsetInfoList *qmp_query_fdsets(Error **errp) fdset_info->next = fdset_list; fdset_list = fdset_info; } + qemu_mutex_unlock(&mon_fdsets_lock); return fdset_list; } @@ -2406,6 +2474,7 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, MonFdsetFd *mon_fdset_fd; AddfdInfo *fdinfo; + qemu_mutex_lock(&mon_fdsets_lock); if (has_fdset_id) { QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { /* Break if match found or match impossible due to ordering by ID */ @@ -2426,6 +2495,7 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, if (fdset_id < 0) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id", "a non-negative value"); + qemu_mutex_unlock(&mon_fdsets_lock); return NULL; } /* Use specified fdset ID */ @@ -2476,16 +2546,21 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, fdinfo->fdset_id = mon_fdset->id; fdinfo->fd = mon_fdset_fd->fd; + qemu_mutex_unlock(&mon_fdsets_lock); return fdinfo; } int monitor_fdset_get_fd(int64_t fdset_id, int flags) { -#ifndef _WIN32 +#ifdef _WIN32 + return -ENOENT; +#else MonFdset *mon_fdset; MonFdsetFd *mon_fdset_fd; int mon_fd_flags; + int ret; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { if (mon_fdset->id != fdset_id) { continue; @@ -2493,20 +2568,24 @@ int monitor_fdset_get_fd(int64_t fdset_id, int flags) QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL); if (mon_fd_flags == -1) { - return -1; + ret = -errno; + goto out; } if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) { - return mon_fdset_fd->fd; + ret = mon_fdset_fd->fd; + goto out; } } - errno = EACCES; - return -1; + ret = -EACCES; + goto out; } -#endif + ret = -ENOENT; - errno = ENOENT; - return -1; +out: + qemu_mutex_unlock(&mon_fdsets_lock); + return ret; +#endif } int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd) @@ -2514,20 +2593,25 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd) MonFdset *mon_fdset; MonFdsetFd *mon_fdset_fd_dup; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { if (mon_fdset->id != fdset_id) { continue; } QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { if (mon_fdset_fd_dup->fd == dup_fd) { - return -1; + goto err; } } mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup)); mon_fdset_fd_dup->fd = dup_fd; QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next); + qemu_mutex_unlock(&mon_fdsets_lock); return 0; } + +err: + qemu_mutex_unlock(&mon_fdsets_lock); return -1; } @@ -2536,6 +2620,7 @@ static int monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) MonFdset *mon_fdset; MonFdsetFd *mon_fdset_fd_dup; + qemu_mutex_lock(&mon_fdsets_lock); QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { if (mon_fdset_fd_dup->fd == dup_fd) { @@ -2544,13 +2629,17 @@ static int monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) if (QLIST_EMPTY(&mon_fdset->dup_fds)) { monitor_fdset_cleanup(mon_fdset); } - return -1; + goto err; } else { + qemu_mutex_unlock(&mon_fdsets_lock); return mon_fdset->id; } } } } + +err: + qemu_mutex_unlock(&mon_fdsets_lock); return -1; } @@ -2981,6 +3070,12 @@ static const mon_cmd_t *monitor_parse_command(Monitor *mon, (int)(p - cmdp_start), cmdp_start); return NULL; } + if (runstate_check(RUN_STATE_PRECONFIG) && !cmd_can_preconfig(cmd)) { + monitor_printf(mon, "Command '%.*s' not available with -preconfig " + "until after exit_preconfig.\n", + (int)(p - cmdp_start), cmdp_start); + return NULL; + } /* filter out following useless space */ while (qemu_isspace(*p)) { @@ -3200,7 +3295,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, monitor_printf(mon, "enter a positive value\n"); goto fail; } - val <<= 20; + val *= MiB; } qdict_put_int(qdict, key, val); } @@ -3362,7 +3457,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, return qdict; fail: - QDECREF(qdict); + qobject_unref(qdict); g_free(key); return NULL; } @@ -3371,6 +3466,7 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) { QDict *qdict; const mon_cmd_t *cmd; + const char *cmd_start = cmdline; trace_handle_hmp_command(mon, cmdline); @@ -3381,13 +3477,16 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) qdict = monitor_parse_arguments(mon, &cmdline, cmd); if (!qdict) { - monitor_printf(mon, "Try \"help %s\" for more information\n", - cmd->name); + while (cmdline > cmd_start && qemu_isspace(cmdline[-1])) { + cmdline--; + } + monitor_printf(mon, "Try \"help %.*s\" for more information\n", + (int)(cmdline - cmd_start), cmd_start); return; } cmd->cmd(mon, qdict); - QDECREF(qdict); + qobject_unref(qdict); } static void cmd_completion(Monitor *mon, const char *name, const char *list) @@ -3399,9 +3498,7 @@ static void cmd_completion(Monitor *mon, const char *name, const char *list) p = list; for(;;) { pstart = p; - p = strchr(p, '|'); - if (!p) - p = pstart + strlen(pstart); + p = qemu_strchrnul(p, '|'); len = p - pstart; if (len > sizeof(cmd) - 2) len = sizeof(cmd) - 2; @@ -3924,12 +4021,17 @@ static void monitor_find_completion_by_table(Monitor *mon, cmdname = args[0]; readline_set_completion_index(mon->rs, strlen(cmdname)); for (cmd = cmd_table; cmd->name != NULL; cmd++) { - cmd_completion(mon, cmdname, cmd->name); + if (!runstate_check(RUN_STATE_PRECONFIG) || + cmd_can_preconfig(cmd)) { + cmd_completion(mon, cmdname, cmd->name); + } } } else { /* find the command */ for (cmd = cmd_table; cmd->name != NULL; cmd++) { - if (compare_cmd(args[0], cmd->name)) { + if (compare_cmd(args[0], cmd->name) && + (!runstate_check(RUN_STATE_PRECONFIG) || + cmd_can_preconfig(cmd))) { break; } } @@ -4029,86 +4131,59 @@ static int monitor_can_read(void *opaque) } /* - * 1. This function takes ownership of rsp, err, and id. - * 2. rsp, err, and id may be NULL. - * 3. If err != NULL then rsp must be NULL. + * Emit QMP response @rsp with ID @id to @mon. + * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP. + * Nothing is emitted then. */ -static void monitor_qmp_respond(Monitor *mon, QObject *rsp, - Error *err, QObject *id) +static void monitor_qmp_respond(Monitor *mon, QDict *rsp, QObject *id) { - QDict *qdict = NULL; - - if (err) { - assert(!rsp); - qdict = qdict_new(); - qdict_put_obj(qdict, "error", qmp_build_error_object(err)); - error_free(err); - rsp = QOBJECT(qdict); - } - if (rsp) { if (id) { - /* This is for the qdict below. */ - qobject_incref(id); - qdict_put_obj(qobject_to(QDict, rsp), "id", id); + qdict_put_obj(rsp, "id", qobject_ref(id)); } - monitor_json_emitter(mon, rsp); + qmp_queue_response(mon, rsp); } - - qobject_decref(id); - qobject_decref(rsp); } -/* - * Dispatch one single QMP request. The function will free the req_obj - * and objects inside it before return. - */ -static void monitor_qmp_dispatch_one(QMPRequest *req_obj) +static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) { - Monitor *mon, *old_mon; - QObject *req, *rsp = NULL, *id; - bool need_resume; - - req = req_obj->req; - mon = req_obj->mon; - id = req_obj->id; - need_resume = req_obj->need_resume; - - g_free(req_obj); - - if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { - QString *req_json = qobject_to_json(req); - trace_handle_qmp_command(mon, qstring_get_str(req_json)); - QDECREF(req_json); - } + Monitor *old_mon; + QDict *rsp; + QDict *error; old_mon = cur_mon; cur_mon = mon; - rsp = qmp_dispatch(mon->qmp.commands, req); + rsp = qmp_dispatch(mon->qmp.commands, req, qmp_oob_enabled(mon)); cur_mon = old_mon; - /* Respond if necessary */ - monitor_qmp_respond(mon, rsp, NULL, id); - - /* This pairs with the monitor_suspend() in handle_qmp_command(). */ - if (need_resume) { - monitor_resume(mon); + if (mon->qmp.commands == &qmp_cap_negotiation_commands) { + error = qdict_get_qdict(rsp, "error"); + if (error + && !g_strcmp0(qdict_get_try_str(error, "class"), + QapiErrorClass_str(ERROR_CLASS_COMMAND_NOT_FOUND))) { + /* Provide a more useful error message */ + qdict_del(error, "desc"); + qdict_put_str(error, "desc", "Expecting capabilities negotiation" + " with 'qmp_capabilities'"); + } } - qobject_decref(req); + monitor_qmp_respond(mon, rsp, id); + qobject_unref(rsp); } /* - * Pop one QMP request from monitor queues, return NULL if not found. + * Pop a QMP request from a monitor request queue. + * Return the request, or NULL all request queues are empty. * We are using round-robin fashion to pop the request, to avoid * processing commands only on a very busy monitor. To achieve that, * when we process one request on a specific monitor, we put that * monitor to the end of mon_list queue. */ -static QMPRequest *monitor_qmp_requests_pop_one(void) +static QMPRequest *monitor_qmp_requests_pop_any(void) { QMPRequest *req_obj = NULL; Monitor *mon; @@ -4140,14 +4215,32 @@ static QMPRequest *monitor_qmp_requests_pop_one(void) static void monitor_qmp_bh_dispatcher(void *data) { - QMPRequest *req_obj = monitor_qmp_requests_pop_one(); + QMPRequest *req_obj = monitor_qmp_requests_pop_any(); + QDict *rsp; - if (req_obj) { - trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); - monitor_qmp_dispatch_one(req_obj); - /* Reschedule instead of looping so the main loop stays responsive */ - qemu_bh_schedule(mon_global.qmp_dispatcher_bh); + if (!req_obj) { + return; } + + if (req_obj->req) { + trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); + monitor_qmp_dispatch(req_obj->mon, req_obj->req, req_obj->id); + } else { + assert(req_obj->err); + rsp = qmp_error_response(req_obj->err); + req_obj->err = NULL; + monitor_qmp_respond(req_obj->mon, rsp, NULL); + qobject_unref(rsp); + } + + if (req_obj->need_resume) { + /* Pairs with the monitor_suspend() in handle_qmp_command() */ + monitor_resume(req_obj->mon); + } + qmp_request_free(req_obj); + + /* Reschedule instead of looping so the main loop stays responsive */ + qemu_bh_schedule(qmp_dispatcher_bh); } #define QMP_REQ_QUEUE_LEN_MAX (8) @@ -4155,7 +4248,7 @@ static void monitor_qmp_bh_dispatcher(void *data) static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) { QObject *req, *id = NULL; - QDict *qdict = NULL; + QDict *qdict; MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser); Monitor *mon = container_of(mon_qmp, Monitor, qmp); Error *err = NULL; @@ -4166,47 +4259,34 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) /* json_parser_parse_err() sucks: can fail without setting @err */ error_setg(&err, QERR_JSON_PARSING); } - if (err) { - goto err; + + qdict = qobject_to(QDict, req); + if (qdict) { + id = qobject_ref(qdict_get(qdict, "id")); + qdict_del(qdict, "id"); + } /* else will fail qmp_dispatch() */ + + if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { + QString *req_json = qobject_to_json(req); + trace_handle_qmp_command(mon, qstring_get_str(req_json)); + qobject_unref(req_json); } - /* Check against the request in general layout */ - qdict = qmp_dispatch_check_obj(req, &err); - if (!qdict) { - goto err; + if (qdict && qmp_is_oob(qdict)) { + /* OOB commands are executed immediately */ + trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) + ?: ""); + monitor_qmp_dispatch(mon, req, id); + return; } - /* Check against OOB specific */ - if (!qmp_cmd_oob_check(mon, qdict, &err)) { - goto err; - } - - id = qdict_get(qdict, "id"); - - /* When OOB is enabled, the "id" field is mandatory. */ - if (qmp_oob_enabled(mon) && !id) { - error_setg(&err, "Out-Of-Band capability requires that " - "every command contains an 'id' field"); - goto err; - } - - qobject_incref(id); - qdict_del(qdict, "id"); - req_obj = g_new0(QMPRequest, 1); req_obj->mon = mon; req_obj->id = id; req_obj->req = req; + req_obj->err = err; req_obj->need_resume = false; - if (qmp_is_oob(qdict)) { - /* Out-Of-Band (OOB) requests are executed directly in parser. */ - trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(req_obj->id) - ?: ""); - monitor_qmp_dispatch_one(req_obj); - return; - } - /* Protect qmp_requests and fetching its length. */ qemu_mutex_lock(&mon->qmp.qmp_queue_lock); @@ -4223,6 +4303,12 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) /* Drop the request if queue is full. */ if (mon->qmp.qmp_requests->length >= QMP_REQ_QUEUE_LEN_MAX) { qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); + /* + * FIXME @id's scope is just @mon, and broadcasting it is + * wrong. If another monitor's client has a command with + * the same ID in flight, the event will incorrectly claim + * that command was dropped. + */ qapi_event_send_command_dropped(id, COMMAND_DROP_REASON_QUEUE_FULL, &error_abort); @@ -4240,12 +4326,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); /* Kick the dispatcher routine */ - qemu_bh_schedule(mon_global.qmp_dispatcher_bh); - return; - -err: - monitor_qmp_respond(mon, NULL, err, NULL); - qobject_decref(req); + qemu_bh_schedule(qmp_dispatcher_bh); } static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) @@ -4295,10 +4376,10 @@ int monitor_suspend(Monitor *mon) if (monitor_is_qmp(mon)) { /* - * Kick iothread to make sure this takes effect. It'll be + * Kick I/O thread to make sure this takes effect. It'll be * evaluated again in prepare() of the watch object. */ - aio_notify(iothread_get_aio_context(mon_global.mon_iothread)); + aio_notify(iothread_get_aio_context(mon_iothread)); } trace_monitor_suspend(mon, 1); @@ -4314,11 +4395,11 @@ void monitor_resume(Monitor *mon) if (atomic_dec_fetch(&mon->suspend_cnt) == 0) { if (monitor_is_qmp(mon)) { /* - * For QMP monitors that are running in IOThread, let's - * kick the thread in case it's sleeping. + * For QMP monitors that are running in the I/O thread, + * let's kick the thread in case it's sleeping. */ - if (mon->use_io_thr) { - aio_notify(iothread_get_aio_context(mon_global.mon_iothread)); + if (mon->use_io_thread) { + aio_notify(iothread_get_aio_context(mon_iothread)); } } else { assert(mon->rs); @@ -4328,7 +4409,7 @@ void monitor_resume(Monitor *mon) trace_monitor_suspend(mon, -1); } -static QObject *get_qmp_greeting(Monitor *mon) +static QDict *qmp_greeting(Monitor *mon) { QList *cap_list = qlist_new(); QObject *ver = NULL; @@ -4337,37 +4418,38 @@ static QObject *get_qmp_greeting(Monitor *mon) qmp_marshal_query_version(NULL, &ver, NULL); for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) { - if (!mon->use_io_thr && cap == QMP_CAPABILITY_OOB) { - /* Monitors that are not using IOThread won't support OOB */ - continue; + if (mon->qmp.capab_offered[cap]) { + qlist_append_str(cap_list, QMPCapability_str(cap)); } - qlist_append_str(cap_list, QMPCapability_str(cap)); } - return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}", - ver, cap_list); -} - -static void monitor_qmp_caps_reset(Monitor *mon) -{ - memset(mon->qmp.qmp_caps, 0, sizeof(mon->qmp.qmp_caps)); + return qdict_from_jsonf_nofail( + "{'QMP': {'version': %p, 'capabilities': %p}}", + ver, cap_list); } static void monitor_qmp_event(void *opaque, int event) { - QObject *data; + QDict *data; Monitor *mon = opaque; switch (event) { case CHR_EVENT_OPENED: mon->qmp.commands = &qmp_cap_negotiation_commands; monitor_qmp_caps_reset(mon); - data = get_qmp_greeting(mon); - monitor_json_emitter(mon, data); - qobject_decref(data); + data = qmp_greeting(mon); + qmp_queue_response(mon, data); + qobject_unref(data); mon_refcount++; break; case CHR_EVENT_CLOSED: + /* + * Note: this is only useful when the output of the chardev + * backend is still open. For example, when the backend is + * stdio, it's possible that stdout is still open when stdin + * is closed. + */ + monitor_qmp_response_flush(mon); monitor_qmp_cleanup_queues(mon); json_message_parser_destroy(&mon->qmp.parser); json_message_parser_init(&mon->qmp.parser, handle_qmp_command); @@ -4383,9 +4465,9 @@ static void monitor_event(void *opaque, int event) switch (event) { case CHR_EVENT_MUX_IN: - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); mon->mux_out = 0; - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); if (mon->reset_seen) { readline_restart(mon->rs); monitor_resume(mon); @@ -4405,9 +4487,9 @@ static void monitor_event(void *opaque, int event) } else { atomic_inc(&mon->suspend_cnt); } - qemu_mutex_lock(&mon->out_lock); + qemu_mutex_lock(&mon->mon_lock); mon->mux_out = 1; - qemu_mutex_unlock(&mon->out_lock); + qemu_mutex_unlock(&mon->mon_lock); break; case CHR_EVENT_OPENED: @@ -4449,36 +4531,35 @@ static void sortcmdlist(void) static GMainContext *monitor_get_io_context(void) { - return iothread_get_g_main_context(mon_global.mon_iothread); + return iothread_get_g_main_context(mon_iothread); } static AioContext *monitor_get_aio_context(void) { - return iothread_get_aio_context(mon_global.mon_iothread); + return iothread_get_aio_context(mon_iothread); } static void monitor_iothread_init(void) { - mon_global.mon_iothread = iothread_create("mon_iothread", - &error_abort); + mon_iothread = iothread_create("mon_iothread", &error_abort); /* - * This MUST be on main loop thread since we have commands that - * have assumption to be run on main loop thread. It would be - * nice that one day we can remove this assumption in the future. + * The dispatcher BH must run in the main loop thread, since we + * have commands assuming that context. It would be nice to get + * rid of those assumptions. */ - mon_global.qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), - monitor_qmp_bh_dispatcher, - NULL); + qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), + monitor_qmp_bh_dispatcher, + NULL); /* - * Unlike the dispatcher BH, this must be run on the monitor IO - * thread, so that monitors that are using IO thread will make - * sure read/write operations are all done on the IO thread. + * The responder BH must be run in the monitor I/O thread, so that + * monitors that are using the I/O thread have their output + * written by the I/O thread. */ - mon_global.qmp_respond_bh = aio_bh_new(monitor_get_aio_context(), - monitor_qmp_bh_responder, - NULL); + qmp_respond_bh = aio_bh_new(monitor_get_aio_context(), + monitor_qmp_bh_responder, + NULL); } void monitor_init_globals(void) @@ -4487,6 +4568,7 @@ void monitor_init_globals(void) monitor_qapi_event_init(); sortcmdlist(); qemu_mutex_init(&monitor_lock); + qemu_mutex_init(&mon_fdsets_lock); monitor_iothread_init(); } @@ -4542,16 +4624,12 @@ static void monitor_qmp_setup_handlers_bh(void *opaque) Monitor *mon = opaque; GMainContext *context; - if (mon->use_io_thr) { - /* - * When use_io_thr is set, we use the global shared dedicated - * IO thread for this monitor to handle input/output. - */ + if (mon->use_io_thread) { + /* Use @mon_iothread context */ context = monitor_get_io_context(); - /* We should have inited globals before reaching here. */ assert(context); } else { - /* The default main loop, which is the main thread */ + /* Use default main loop context */ context = NULL; } @@ -4568,12 +4646,12 @@ void monitor_init(Chardev *chr, int flags) if (use_oob) { if (CHARDEV_IS_MUX(chr)) { - error_report("Monitor Out-Of-Band is not supported with " + error_report("Monitor out-of-band is not supported with " "MUX typed chardev backend"); exit(1); } if (use_readline) { - error_report("Monitor Out-Of-band is only supported by QMP"); + error_report("Monitor out-of-band is only supported by QMP"); exit(1); } } @@ -4593,7 +4671,7 @@ void monitor_init(Chardev *chr, int flags) if (monitor_is_qmp(mon)) { qemu_chr_fe_set_echo(&mon->chr, true); json_message_parser_init(&mon->qmp.parser, handle_qmp_command); - if (mon->use_io_thr) { + if (mon->use_io_thread) { /* * Make sure the old iowatch is gone. It's possible when * e.g. the chardev is in client mode, with wait=on. @@ -4601,15 +4679,12 @@ void monitor_init(Chardev *chr, int flags) remove_fd_in_watch(chr); /* * We can't call qemu_chr_fe_set_handlers() directly here - * since during the procedure the chardev will be active - * and running in monitor iothread, while we'll still do - * something before returning from it, which is a possible - * race too. To avoid that, we just create a BH to setup - * the handlers. + * since chardev might be running in the monitor I/O + * thread. Schedule a bottom half. */ aio_bh_schedule_oneshot(monitor_get_aio_context(), monitor_qmp_setup_handlers_bh, mon); - /* We'll add this to mon_list in the BH when setup done */ + /* The bottom half will add @mon to @mon_list */ return; } else { qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, @@ -4629,22 +4704,20 @@ void monitor_cleanup(void) Monitor *mon, *next; /* - * We need to explicitly stop the iothread (but not destroy it), - * cleanup the monitor resources, then destroy the iothread since + * We need to explicitly stop the I/O thread (but not destroy it), + * clean up the monitor resources, then destroy the I/O thread since * we need to unregister from chardev below in * monitor_data_destroy(), and chardev is not thread-safe yet */ - iothread_stop(mon_global.mon_iothread); + iothread_stop(mon_iothread); /* - * After we have IOThread to send responses, it's possible that - * when we stop the IOThread there are still replies queued in the - * responder queue. Flush all of them. Note that even after this - * flush it's still possible that out buffer is not flushed. - * It'll be done in below monitor_flush() as the last resort. + * Flush all response queues. Note that even after this flush, + * data may remain in output buffers. */ monitor_qmp_bh_responder(NULL); + /* Flush output buffers and destroy monitors */ qemu_mutex_lock(&monitor_lock); QTAILQ_FOREACH_SAFE(mon, &mon_list, entry, next) { QTAILQ_REMOVE(&mon_list, mon, entry); @@ -4654,14 +4727,14 @@ void monitor_cleanup(void) } qemu_mutex_unlock(&monitor_lock); - /* QEMUBHs needs to be deleted before destroying the IOThread. */ - qemu_bh_delete(mon_global.qmp_dispatcher_bh); - mon_global.qmp_dispatcher_bh = NULL; - qemu_bh_delete(mon_global.qmp_respond_bh); - mon_global.qmp_respond_bh = NULL; + /* QEMUBHs needs to be deleted before destroying the I/O thread */ + qemu_bh_delete(qmp_dispatcher_bh); + qmp_dispatcher_bh = NULL; + qemu_bh_delete(qmp_respond_bh); + qmp_respond_bh = NULL; - iothread_destroy(mon_global.mon_iothread); - mon_global.mon_iothread = NULL; + iothread_destroy(mon_iothread); + mon_iothread = NULL; } QemuOptsList qemu_mon_opts = { diff --git a/nbd/client.c b/nbd/client.c index b9e175d1c2..40b74d9761 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2017 Red Hat, Inc. + * Copyright (C) 2016-2018 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori * * Network Block Device Client Side @@ -435,8 +435,8 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, } be32_to_cpus(&info->min_block); if (!is_power_of_2(info->min_block)) { - error_setg(errp, "server minimum block size %" PRId32 - "is not a power of two", info->min_block); + error_setg(errp, "server minimum block size %" PRIu32 + " is not a power of two", info->min_block); nbd_send_opt_abort(ioc); return -1; } @@ -450,8 +450,8 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, be32_to_cpus(&info->opt_block); if (!is_power_of_2(info->opt_block) || info->opt_block < info->min_block) { - error_setg(errp, "server preferred block size %" PRId32 - "is not valid", info->opt_block); + error_setg(errp, "server preferred block size %" PRIu32 + " is not valid", info->opt_block); nbd_send_opt_abort(ioc); return -1; } @@ -462,6 +462,12 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, return -1; } be32_to_cpus(&info->max_block); + if (info->max_block < info->min_block) { + error_setg(errp, "server maximum block size %" PRIu32 + " is not valid", info->max_block); + nbd_send_opt_abort(ioc); + return -1; + } trace_nbd_opt_go_info_block_size(info->min_block, info->opt_block, info->max_block); break; @@ -613,8 +619,8 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, { int ret; NBDOptionReply reply; - uint32_t received_id; - bool received; + uint32_t received_id = 0; + bool received = false; uint32_t export_len = strlen(export); uint32_t context_len = strlen(context); uint32_t data_len = sizeof(export_len) + export_len + @@ -825,7 +831,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, if (info->structured_reply && base_allocation) { result = nbd_negotiate_simple_meta_context( - ioc, name, "base:allocation", + ioc, name, info->x_dirty_bitmap ?: "base:allocation", &info->meta_base_allocation_id, errp); if (result < 0) { goto fail; diff --git a/nbd/common.c b/nbd/common.c index 8c95c1d606..41f5ed8d9f 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -148,6 +148,8 @@ const char *nbd_cmd_lookup(uint16_t cmd) return "flush"; case NBD_CMD_TRIM: return "trim"; + case NBD_CMD_CACHE: + return "cache"; case NBD_CMD_WRITE_ZEROES: return "write zeroes"; case NBD_CMD_BLOCK_STATUS: diff --git a/nbd/server.c b/nbd/server.c index 9e1f227178..ea5fe0eb33 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -23,6 +23,13 @@ #include "nbd-internal.h" #define NBD_META_ID_BASE_ALLOCATION 0 +#define NBD_META_ID_DIRTY_BITMAP 1 + +/* NBD_MAX_BITMAP_EXTENTS: 1 mb of extents data. An empirical + * constant. If an increase is needed, note that the NBD protocol + * recommends no larger than 32 mb, so that the client won't consider + * the reply as a denial of service attack. */ +#define NBD_MAX_BITMAP_EXTENTS (0x100000 / 8) static int system_errno_to_nbd_errno(int err) { @@ -80,6 +87,9 @@ struct NBDExport { BlockBackend *eject_notifier_blk; Notifier eject_notifier; + + BdrvDirtyBitmap *export_bitmap; + char *export_bitmap_context; }; static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); @@ -88,10 +98,11 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); * as selected by NBD_OPT_SET_META_CONTEXT. Also used for * NBD_OPT_LIST_META_CONTEXT. */ typedef struct NBDExportMetaContexts { - char export_name[NBD_MAX_NAME_SIZE + 1]; + NBDExport *exp; bool valid; /* means that negotiation of the option finished without errors */ bool base_allocation; /* export base:allocation context (block status) */ + bool bitmap; /* export qemu:dirty-bitmap: */ } NBDExportMetaContexts; struct NBDClient { @@ -399,10 +410,9 @@ static int nbd_negotiate_handle_list(NBDClient *client, Error **errp) return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); } -static void nbd_check_meta_export_name(NBDClient *client) +static void nbd_check_meta_export(NBDClient *client) { - client->export_meta.valid &= !strcmp(client->exp->name, - client->export_meta.export_name); + client->export_meta.valid &= client->exp == client->export_meta.exp; } /* Send a reply to NBD_OPT_EXPORT_NAME. @@ -456,7 +466,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); nbd_export_get(client->exp); - nbd_check_meta_export_name(client); + nbd_check_meta_export(client); return 0; } @@ -650,7 +660,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, client->exp = exp; QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); nbd_export_get(client->exp); - nbd_check_meta_export_name(client); + nbd_check_meta_export(client); rc = 1; } return rc; @@ -734,45 +744,135 @@ static int nbd_negotiate_send_meta_context(NBDClient *client, return qio_channel_writev_all(client->ioc, iov, 2, errp) < 0 ? -EIO : 0; } -/* nbd_meta_base_query - * - * Handle query to 'base' namespace. For now, only base:allocation context is - * available in it. 'len' is the amount of text remaining to be read from - * the current name, after the 'base:' portion has been stripped. +/* Read strlen(@pattern) bytes, and set @match to true if they match @pattern. + * @match is never set to false. * * Return -errno on I/O error, 0 if option was completely handled by - * sending a reply about inconsistent lengths, or 1 on success. */ -static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, - uint32_t len, Error **errp) + * sending a reply about inconsistent lengths, or 1 on success. + * + * Note: return code = 1 doesn't mean that we've read exactly @pattern. + * It only means that there are no errors. + */ +static int nbd_meta_pattern(NBDClient *client, const char *pattern, bool *match, + Error **errp) { int ret; - char query[sizeof("allocation") - 1]; - size_t alen = strlen("allocation"); + char *query; + size_t len = strlen(pattern); - if (len == 0) { - if (client->opt == NBD_OPT_LIST_META_CONTEXT) { - meta->base_allocation = true; - } - trace_nbd_negotiate_meta_query_parse("base:"); - return 1; - } - - if (len != alen) { - trace_nbd_negotiate_meta_query_skip("not base:allocation"); - return nbd_opt_skip(client, len, errp); - } + assert(len); + query = g_malloc(len); ret = nbd_opt_read(client, query, len, errp); if (ret <= 0) { + g_free(query); return ret; } - if (strncmp(query, "allocation", alen) == 0) { - meta->base_allocation = true; + if (strncmp(query, pattern, len) == 0) { + trace_nbd_negotiate_meta_query_parse(pattern); + *match = true; + } else { + trace_nbd_negotiate_meta_query_skip("pattern not matched"); + } + g_free(query); + + return 1; +} + +/* + * Read @len bytes, and set @match to true if they match @pattern, or if @len + * is 0 and the client is performing _LIST_. @match is never set to false. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. + * + * Note: return code = 1 doesn't mean that we've read exactly @pattern. + * It only means that there are no errors. + */ +static int nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern, + uint32_t len, bool *match, Error **errp) +{ + if (len == 0) { + if (client->opt == NBD_OPT_LIST_META_CONTEXT) { + *match = true; + } + trace_nbd_negotiate_meta_query_parse("empty"); + return 1; } - trace_nbd_negotiate_meta_query_parse("base:allocation"); - return 1; + if (len != strlen(pattern)) { + trace_nbd_negotiate_meta_query_skip("different lengths"); + return nbd_opt_skip(client, len, errp); + } + + return nbd_meta_pattern(client, pattern, match, errp); +} + +/* nbd_meta_base_query + * + * Handle queries to 'base' namespace. For now, only the base:allocation + * context is available. 'len' is the amount of text remaining to be read from + * the current name, after the 'base:' portion has been stripped. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. + */ +static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, + uint32_t len, Error **errp) +{ + return nbd_meta_empty_or_pattern(client, "allocation", len, + &meta->base_allocation, errp); +} + +/* nbd_meta_bitmap_query + * + * Handle query to 'qemu:' namespace. + * @len is the amount of text remaining to be read from the current name, after + * the 'qemu:' portion has been stripped. + * + * Return -errno on I/O error, 0 if option was completely handled by + * sending a reply about inconsistent lengths, or 1 on success. */ +static int nbd_meta_qemu_query(NBDClient *client, NBDExportMetaContexts *meta, + uint32_t len, Error **errp) +{ + bool dirty_bitmap = false; + size_t dirty_bitmap_len = strlen("dirty-bitmap:"); + int ret; + + if (!meta->exp->export_bitmap) { + trace_nbd_negotiate_meta_query_skip("no dirty-bitmap exported"); + return nbd_opt_skip(client, len, errp); + } + + if (len == 0) { + if (client->opt == NBD_OPT_LIST_META_CONTEXT) { + meta->bitmap = true; + } + trace_nbd_negotiate_meta_query_parse("empty"); + return 1; + } + + if (len < dirty_bitmap_len) { + trace_nbd_negotiate_meta_query_skip("not dirty-bitmap:"); + return nbd_opt_skip(client, len, errp); + } + + len -= dirty_bitmap_len; + ret = nbd_meta_pattern(client, "dirty-bitmap:", &dirty_bitmap, errp); + if (ret <= 0) { + return ret; + } + if (!dirty_bitmap) { + trace_nbd_negotiate_meta_query_skip("not dirty-bitmap:"); + return nbd_opt_skip(client, len, errp); + } + + trace_nbd_negotiate_meta_query_parse("dirty-bitmap:"); + + return nbd_meta_empty_or_pattern( + client, meta->exp->export_bitmap_context + + strlen("qemu:dirty_bitmap:"), len, &meta->bitmap, errp); } /* nbd_negotiate_meta_query @@ -790,9 +890,14 @@ static int nbd_meta_base_query(NBDClient *client, NBDExportMetaContexts *meta, static int nbd_negotiate_meta_query(NBDClient *client, NBDExportMetaContexts *meta, Error **errp) { + /* + * Both 'qemu' and 'base' namespaces have length = 5 including a + * colon. If another length namespace is later introduced, this + * should certainly be refactored. + */ int ret; - char query[sizeof("base:") - 1]; - size_t baselen = strlen("base:"); + size_t ns_len = 5; + char ns[5]; uint32_t len; ret = nbd_opt_read(client, &len, sizeof(len), errp); @@ -801,24 +906,27 @@ static int nbd_negotiate_meta_query(NBDClient *client, } cpu_to_be32s(&len); - /* The only supported namespace for now is 'base'. So query should start - * with 'base:'. Otherwise, we can ignore it and skip the remainder. */ - if (len < baselen) { + if (len < ns_len) { trace_nbd_negotiate_meta_query_skip("length too short"); return nbd_opt_skip(client, len, errp); } - len -= baselen; - ret = nbd_opt_read(client, query, baselen, errp); + len -= ns_len; + ret = nbd_opt_read(client, ns, ns_len, errp); if (ret <= 0) { return ret; } - if (strncmp(query, "base:", baselen) != 0) { - trace_nbd_negotiate_meta_query_skip("not for base: namespace"); - return nbd_opt_skip(client, len, errp); + + if (!strncmp(ns, "base:", ns_len)) { + trace_nbd_negotiate_meta_query_parse("base:"); + return nbd_meta_base_query(client, meta, len, errp); + } else if (!strncmp(ns, "qemu:", ns_len)) { + trace_nbd_negotiate_meta_query_parse("qemu:"); + return nbd_meta_qemu_query(client, meta, len, errp); } - return nbd_meta_base_query(client, meta, len, errp); + trace_nbd_negotiate_meta_query_skip("unknown namespace"); + return nbd_opt_skip(client, len, errp); } /* nbd_negotiate_meta_queries @@ -829,7 +937,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, NBDExportMetaContexts *meta, Error **errp) { int ret; - NBDExport *exp; + char export_name[NBD_MAX_NAME_SIZE + 1]; NBDExportMetaContexts local_meta; uint32_t nb_queries; int i; @@ -848,15 +956,15 @@ static int nbd_negotiate_meta_queries(NBDClient *client, memset(meta, 0, sizeof(*meta)); - ret = nbd_opt_read_name(client, meta->export_name, NULL, errp); + ret = nbd_opt_read_name(client, export_name, NULL, errp); if (ret <= 0) { return ret; } - exp = nbd_export_find(meta->export_name); - if (exp == NULL) { + meta->exp = nbd_export_find(export_name); + if (meta->exp == NULL) { return nbd_opt_drop(client, NBD_REP_ERR_UNKNOWN, errp, - "export '%s' not present", meta->export_name); + "export '%s' not present", export_name); } ret = nbd_opt_read(client, &nb_queries, sizeof(nb_queries), errp); @@ -865,7 +973,7 @@ static int nbd_negotiate_meta_queries(NBDClient *client, } cpu_to_be32s(&nb_queries); trace_nbd_negotiate_meta_context(nbd_opt_lookup(client->opt), - meta->export_name, nb_queries); + export_name, nb_queries); if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) { /* enable all known contexts */ @@ -888,6 +996,16 @@ static int nbd_negotiate_meta_queries(NBDClient *client, } } + if (meta->bitmap) { + ret = nbd_negotiate_send_meta_context(client, + meta->exp->export_bitmap_context, + NBD_META_ID_DIRTY_BITMAP, + errp); + if (ret < 0) { + return ret; + } + } + ret = nbd_negotiate_send_rep(client, NBD_REP_ACK, errp); if (ret == 0) { meta->valid = true; @@ -1134,7 +1252,7 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) int ret; const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM | NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA | - NBD_FLAG_SEND_WRITE_ZEROES); + NBD_FLAG_SEND_WRITE_ZEROES | NBD_FLAG_SEND_CACHE); bool oldStyle; /* Old style negotiation header, no room for options @@ -1516,6 +1634,11 @@ void nbd_export_put(NBDExport *exp) exp->blk = NULL; } + if (exp->export_bitmap) { + bdrv_dirty_bitmap_set_qmp_locked(exp->export_bitmap, false); + g_free(exp->export_bitmap_context); + } + g_free(exp); } } @@ -1757,9 +1880,15 @@ static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset, } /* nbd_co_send_extents - * @extents should be in big-endian */ + * + * @length is only for tracing purposes (and may be smaller or larger + * than the client's original request). @last controls whether + * NBD_REPLY_FLAG_DONE is sent. @extents should already be in + * big-endian format. + */ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, - NBDExtent *extents, unsigned nb_extents, + NBDExtent *extents, unsigned int nb_extents, + uint64_t length, bool last, uint32_t context_id, Error **errp) { NBDStructuredMeta chunk; @@ -1769,7 +1898,9 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])} }; - set_be_chunk(&chunk.h, NBD_REPLY_FLAG_DONE, NBD_REPLY_TYPE_BLOCK_STATUS, + trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last); + set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0, + NBD_REPLY_TYPE_BLOCK_STATUS, handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len); stl_be_p(&chunk.context_id, context_id); @@ -1779,8 +1910,8 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, /* Get block status from the exported device and send it to the client */ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, BlockDriverState *bs, uint64_t offset, - uint64_t length, uint32_t context_id, - Error **errp) + uint32_t length, bool last, + uint32_t context_id, Error **errp) { int ret; NBDExtent extent; @@ -1791,7 +1922,86 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, client, handle, -ret, "can't get block status", errp); } - return nbd_co_send_extents(client, handle, &extent, 1, context_id, errp); + return nbd_co_send_extents(client, handle, &extent, 1, + be32_to_cpu(extent.length), last, + context_id, errp); +} + +/* + * Populate @extents from a dirty bitmap. Unless @dont_fragment, the + * final extent may exceed the original @length. Store in @length the + * byte length encoded (which may be smaller or larger than the + * original), and return the number of extents used. + */ +static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, + uint64_t *length, NBDExtent *extents, + unsigned int nb_extents, + bool dont_fragment) +{ + uint64_t begin = offset, end = offset; + uint64_t overall_end = offset + *length; + unsigned int i = 0; + BdrvDirtyBitmapIter *it; + bool dirty; + + bdrv_dirty_bitmap_lock(bitmap); + + it = bdrv_dirty_iter_new(bitmap); + dirty = bdrv_get_dirty_locked(NULL, bitmap, offset); + + assert(begin < overall_end && nb_extents); + while (begin < overall_end && i < nb_extents) { + if (dirty) { + end = bdrv_dirty_bitmap_next_zero(bitmap, begin); + } else { + bdrv_set_dirty_iter(it, begin); + end = bdrv_dirty_iter_next(it); + } + if (end == -1 || end - begin > UINT32_MAX) { + /* Cap to an aligned value < 4G beyond begin. */ + end = MIN(bdrv_dirty_bitmap_size(bitmap), + begin + UINT32_MAX + 1 - + bdrv_dirty_bitmap_granularity(bitmap)); + } + if (dont_fragment && end > overall_end) { + end = overall_end; + } + + extents[i].length = cpu_to_be32(end - begin); + extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0); + i++; + begin = end; + dirty = !dirty; + } + + bdrv_dirty_iter_free(it); + + bdrv_dirty_bitmap_unlock(bitmap); + + assert(offset < end); + *length = end - offset; + return i; +} + +static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle, + BdrvDirtyBitmap *bitmap, uint64_t offset, + uint32_t length, bool dont_fragment, bool last, + uint32_t context_id, Error **errp) +{ + int ret; + unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BITMAP_EXTENTS; + NBDExtent *extents = g_new(NBDExtent, nb_extents); + uint64_t final_length = length; + + nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents, + nb_extents, dont_fragment); + + ret = nbd_co_send_extents(client, handle, extents, nb_extents, + final_length, last, context_id, errp); + + g_free(extents); + + return ret; } /* nbd_co_receive_request @@ -1826,7 +2036,9 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, return -EIO; } - if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE) { + if (request->type == NBD_CMD_READ || request->type == NBD_CMD_WRITE || + request->type == NBD_CMD_CACHE) + { if (request->len > NBD_MAX_BUFFER_SIZE) { error_setg(errp, "len (%" PRIu32" ) is larger than max len (%u)", request->len, NBD_MAX_BUFFER_SIZE); @@ -1911,7 +2123,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, int ret; NBDExport *exp = client->exp; - assert(request->type == NBD_CMD_READ); + assert(request->type == NBD_CMD_READ || request->type == NBD_CMD_CACHE); /* XXX: NBD Protocol only documents use of FUA with WRITE */ if (request->flags & NBD_CMD_FLAG_FUA) { @@ -1930,7 +2142,7 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, ret = blk_pread(exp->blk, request->from + exp->dev_offset, data, request->len); - if (ret < 0) { + if (ret < 0 || request->type == NBD_CMD_CACHE) { return nbd_send_generic_reply(client, request->handle, ret, "reading from file failed", errp); } @@ -1963,6 +2175,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, switch (request->type) { case NBD_CMD_READ: + case NBD_CMD_CACHE: return nbd_do_cmd_read(client, request, data, errp); case NBD_CMD_WRITE: @@ -2007,11 +2220,38 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, "discard failed", errp); case NBD_CMD_BLOCK_STATUS: - if (client->export_meta.valid && client->export_meta.base_allocation) { - return nbd_co_send_block_status(client, request->handle, - blk_bs(exp->blk), request->from, - request->len, - NBD_META_ID_BASE_ALLOCATION, errp); + if (!request->len) { + return nbd_send_generic_reply(client, request->handle, -EINVAL, + "need non-zero length", errp); + } + if (client->export_meta.valid && + (client->export_meta.base_allocation || + client->export_meta.bitmap)) + { + if (client->export_meta.base_allocation) { + ret = nbd_co_send_block_status(client, request->handle, + blk_bs(exp->blk), request->from, + request->len, + !client->export_meta.bitmap, + NBD_META_ID_BASE_ALLOCATION, + errp); + if (ret < 0) { + return ret; + } + } + + if (client->export_meta.bitmap) { + ret = nbd_co_send_bitmap(client, request->handle, + client->exp->export_bitmap, + request->from, request->len, + request->flags & NBD_CMD_FLAG_REQ_ONE, + true, NBD_META_ID_DIRTY_BITMAP, errp); + if (ret < 0) { + return ret; + } + } + + return ret; } else { return nbd_send_generic_reply(client, request->handle, -EINVAL, "CMD_BLOCK_STATUS not negotiated", @@ -2163,3 +2403,44 @@ void nbd_client_new(NBDExport *exp, co = qemu_coroutine_create(nbd_co_client_start, client); qemu_coroutine_enter(co); } + +void nbd_export_bitmap(NBDExport *exp, const char *bitmap, + const char *bitmap_export_name, Error **errp) +{ + BdrvDirtyBitmap *bm = NULL; + BlockDriverState *bs = blk_bs(exp->blk); + + if (exp->export_bitmap) { + error_setg(errp, "Export bitmap is already set"); + return; + } + + while (true) { + bm = bdrv_find_dirty_bitmap(bs, bitmap); + if (bm != NULL || bs->backing == NULL) { + break; + } + + bs = bs->backing->bs; + } + + if (bm == NULL) { + error_setg(errp, "Bitmap '%s' is not found", bitmap); + return; + } + + if (bdrv_dirty_bitmap_enabled(bm)) { + error_setg(errp, "Bitmap '%s' is enabled", bitmap); + return; + } + + if (bdrv_dirty_bitmap_qmp_locked(bm)) { + error_setg(errp, "Bitmap '%s' is locked", bitmap); + return; + } + + bdrv_dirty_bitmap_set_qmp_locked(bm, true); + exp->export_bitmap = bm; + exp->export_bitmap_context = + g_strdup_printf("qemu:dirty-bitmap:%s", bitmap_export_name); +} diff --git a/nbd/trace-events b/nbd/trace-events index dee081e775..5e1d4afe8e 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -64,6 +64,7 @@ nbd_co_send_simple_reply(uint64_t handle, uint32_t error, const char *errname, i nbd_co_send_structured_done(uint64_t handle) "Send structured reply done: handle = %" PRIu64 nbd_co_send_structured_read(uint64_t handle, uint64_t offset, void *data, size_t size) "Send structured read data reply: handle = %" PRIu64 ", offset = %" PRIu64 ", data = %p, len = %zu" nbd_co_send_structured_read_hole(uint64_t handle, uint64_t offset, size_t size) "Send structured read hole reply: handle = %" PRIu64 ", offset = %" PRIu64 ", len = %zu" +nbd_co_send_extents(uint64_t handle, unsigned int extents, uint32_t id, uint64_t length, int last) "Send block status reply: handle = %" PRIu64 ", extents = %u, context = %d (extents cover %" PRIu64 " bytes, last chunk = %d)" nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, const char *msg) "Send structured error reply: handle = %" PRIu64 ", error = %d (%s), msg = '%s'" nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 diff --git a/net/can/can_host.c b/net/can/can_host.c index c3d26521cd..c79347abab 100644 --- a/net/can/can_host.c +++ b/net/can/can_host.c @@ -77,7 +77,7 @@ static void can_host_instance_init(Object *obj) object_property_add_link(obj, "canbus", TYPE_CAN_BUS, (Object **)&ch->bus, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); } diff --git a/net/colo-compare.c b/net/colo-compare.c index 23b2d2c4cc..dd745a491b 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -25,7 +25,7 @@ #include "net/queue.h" #include "chardev/char-fe.h" #include "qemu/sockets.h" -#include "net/colo.h" +#include "colo.h" #include "sysemu/iothread.h" #define TYPE_COLO_COMPARE "colo-compare" @@ -980,7 +980,7 @@ static void colo_compare_init(Object *obj) object_property_add_link(obj, "iothread", TYPE_IOTHREAD, (Object **)&s->iothread, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); + OBJ_PROP_LINK_STRONG, NULL); s->vnet_hdr = false; object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr, diff --git a/net/colo.c b/net/colo.c index 842626502e..6dda4ed66e 100644 --- a/net/colo.c +++ b/net/colo.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "trace.h" -#include "net/colo.h" +#include "colo.h" uint32_t connection_key_hash(const void *opaque) { diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index 62dad2d773..f584e4eba4 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "trace.h" -#include "net/colo.h" +#include "colo.h" #include "net/filter.h" #include "net/net.h" #include "qemu-common.h" diff --git a/net/hub.c b/net/hub.c index 5e84a9ad93..78b671ed95 100644 --- a/net/hub.c +++ b/net/hub.c @@ -23,8 +23,7 @@ /* * A hub broadcasts incoming packets to all its ports except the source port. - * Hubs can be used to provide independent network segments, also confusingly - * named the QEMU 'vlan' feature. + * Hubs can be used to provide independent emulated network segments. */ typedef struct NetHub NetHub; @@ -345,10 +344,10 @@ void net_hub_check_clients(void) } } if (has_host_dev && !has_nic) { - warn_report("vlan %d with no nics", hub->id); + warn_report("hub %d with no nics", hub->id); } if (has_nic && !has_host_dev) { - warn_report("vlan %d is not connected to host network", hub->id); + warn_report("hub %d is not connected to host network", hub->id); } } } diff --git a/net/net.c b/net/net.c index 29f83983e5..2a3133990c 100644 --- a/net/net.c +++ b/net/net.c @@ -965,7 +965,6 @@ static int net_client_init1(const void *object, bool is_netdev, Error **errp) const Netdev *netdev; const char *name; NetClientState *peer = NULL; - static bool vlan_warned; if (is_netdev) { netdev = object; @@ -1036,15 +1035,10 @@ static int net_client_init1(const void *object, bool is_netdev, Error **errp) return -1; } - /* Do not add to a vlan if it's a nic with a netdev= parameter. */ + /* Do not add to a hub if it's a nic with a netdev= parameter. */ if (netdev->type != NET_CLIENT_DRIVER_NIC || !opts->u.nic.has_netdev) { - peer = net_hub_add_port(net->has_vlan ? net->vlan : 0, NULL, NULL); - } - - if (net->has_vlan && !vlan_warned) { - error_report("'vlan' is deprecated. Please use 'netdev' instead."); - vlan_warned = true; + peer = net_hub_add_port(0, NULL, NULL); } } @@ -1099,7 +1093,9 @@ static int net_client_init(QemuOpts *opts, bool is_netdev, Error **errp) int ret = -1; Visitor *v = opts_visitor_new(opts); - if (is_netdev && is_help_option(qemu_opt_get(opts, "type"))) { + const char *type = qemu_opt_get(opts, "type"); + + if (is_netdev && type && is_help_option(type)) { show_netdevs(); exit(0); } else { @@ -1365,7 +1361,7 @@ void qmp_set_link(const char *name, bool up, Error **errp) * If the peer is a HUBPORT or a backend, we do not change the * link status. * - * This behavior is compatible with qemu vlans where there could be + * This behavior is compatible with qemu hubs where there could be * multiple clients that can still communicate with each other in * disconnected mode. For now maintain this compatibility. */ @@ -1502,11 +1498,12 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) g_free(mac); if (ret) { error_setg(errp, "invalid syntax for ethernet address"); - return -1; + goto out; } if (is_multicast_ether_addr(ni->macaddr.a)) { error_setg(errp, "NIC cannot have multicast MAC address"); - return -1; + ret = -1; + goto out; } } qemu_macaddr_default_if_unset(&ni->macaddr); @@ -1518,6 +1515,7 @@ static int net_param_nic(void *dummy, QemuOpts *opts, Error **errp) nb_nics++; } +out: g_free(nd_id); return ret; } diff --git a/net/slirp.c b/net/slirp.c index 8991816bbf..1e14318b4d 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -157,7 +157,8 @@ static int net_slirp_init(NetClientState *peer, const char *model, const char *bootfile, const char *vdhcp_start, const char *vnameserver, const char *vnameserver6, const char *smb_export, const char *vsmbserver, - const char **dnssearch, Error **errp) + const char **dnssearch, const char *vdomainname, + Error **errp) { /* default settings according to historic slirp */ struct in_addr net = { .s_addr = htonl(0x0a000200) }; /* 10.0.2.0 */ @@ -359,6 +360,11 @@ static int net_slirp_init(NetClientState *peer, const char *model, ip6_dns.s6_addr[15] |= 3; } + if (vdomainname && !*vdomainname) { + error_setg(errp, "'domainname' parameter cannot be empty"); + return -1; + } + nc = qemu_new_net_client(&net_slirp_info, peer, model, name); @@ -371,7 +377,7 @@ static int net_slirp_init(NetClientState *peer, const char *model, s->slirp = slirp_init(restricted, ipv4, net, mask, host, ipv6, ip6_prefix, vprefix6_len, ip6_host, vhostname, tftp_export, bootfile, dhcp, - dns, ip6_dns, dnssearch, s); + dns, ip6_dns, dnssearch, vdomainname, s); QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); for (config = slirp_configs; config; config = config->next) { @@ -415,7 +421,7 @@ static SlirpState *slirp_lookup(Monitor *mon, const char *hub_id, if (hub_id) { nc = net_hub_find_client_by_name(strtol(hub_id, NULL, 0), name); if (!nc) { - monitor_printf(mon, "unrecognized (vlan-id, stackname) pair\n"); + monitor_printf(mon, "unrecognized (hub-id, stackname) pair\n"); return NULL; } } else { @@ -486,7 +492,9 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) goto fail_syntax; } - host_port = atoi(p); + if (qemu_strtoi(p, NULL, 10, &host_port)) { + goto fail_syntax; + } err = slirp_remove_hostfwd(s->slirp, is_udp, host_addr, host_port); @@ -870,9 +878,9 @@ void hmp_info_usernet(Monitor *mon, const QDict *qdict) QTAILQ_FOREACH(s, &slirp_stacks, entry) { int id; - bool got_vlan_id = net_hub_id_for_client(&s->nc, &id) == 0; - monitor_printf(mon, "VLAN %d (%s):\n", - got_vlan_id ? id : -1, + bool got_hub_id = net_hub_id_for_client(&s->nc, &id) == 0; + monitor_printf(mon, "Hub %d (%s):\n", + got_hub_id ? id : -1, s->nc.name); slirp_connection_info(s->slirp, mon); } @@ -958,7 +966,7 @@ int net_init_slirp(const Netdev *netdev, const char *name, user->ipv6_host, user->hostname, user->tftp, user->bootfile, user->dhcpstart, user->dns, user->ipv6_dns, user->smb, - user->smbserver, dnssearch, errp); + user->smbserver, dnssearch, user->domainname, errp); while (slirp_configs) { config = slirp_configs; diff --git a/net/tap.c b/net/tap.c index 2b3a36f9b5..cc8525f154 100644 --- a/net/tap.c +++ b/net/tap.c @@ -40,6 +40,7 @@ #include "qemu-common.h" #include "qemu/cutils.h" #include "qemu/error-report.h" +#include "qemu/sockets.h" #include "net/tap.h" @@ -693,6 +694,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } return; } + qemu_set_nonblock(vhostfd); } else { vhostfd = open("/dev/vhost-net", O_RDWR); if (vhostfd < 0) { @@ -766,10 +768,10 @@ int net_init_tap(const Netdev *netdev, const char *name, queues = tap->has_queues ? tap->queues : 1; vhostfdname = tap->has_vhostfd ? tap->vhostfd : NULL; - /* QEMU vlans does not support multiqueue tap, in this case peer is set. + /* QEMU hubs do not support multiqueue tap, in this case peer is set. * For -netdev, peer is always NULL. */ if (peer && (tap->has_queues || tap->has_fds || tap->has_vhostfds)) { - error_setg(errp, "Multiqueue tap cannot be used with QEMU vlans"); + error_setg(errp, "Multiqueue tap cannot be used with hubs"); return -1; } @@ -803,7 +805,8 @@ int net_init_tap(const Netdev *netdev, const char *name, } else if (tap->has_fds) { char **fds; char **vhost_fds; - int nfds, nvhosts; + int nfds = 0, nvhosts = 0; + int ret = 0; if (tap->has_ifname || tap->has_script || tap->has_downscript || tap->has_vnet_hdr || tap->has_helper || tap->has_queues || @@ -823,6 +826,7 @@ int net_init_tap(const Netdev *netdev, const char *name, if (nfds != nvhosts) { error_setg(errp, "The number of fds passed does not match " "the number of vhostfds passed"); + ret = -1; goto free_fail; } } @@ -831,6 +835,7 @@ int net_init_tap(const Netdev *netdev, const char *name, fd = monitor_fd_param(cur_mon, fds[i], &err); if (fd == -1) { error_propagate(errp, err); + ret = -1; goto free_fail; } @@ -841,6 +846,7 @@ int net_init_tap(const Netdev *netdev, const char *name, } else if (vnet_hdr != tap_probe_vnet_hdr(fd)) { error_setg(errp, "vnet_hdr not consistent across given tap fds"); + ret = -1; goto free_fail; } @@ -850,21 +856,21 @@ int net_init_tap(const Netdev *netdev, const char *name, vnet_hdr, fd, &err); if (err) { error_propagate(errp, err); + ret = -1; goto free_fail; } } - g_free(fds); - g_free(vhost_fds); - return 0; free_fail: + for (i = 0; i < nvhosts; i++) { + g_free(vhost_fds[i]); + } for (i = 0; i < nfds; i++) { g_free(fds[i]); - g_free(vhost_fds[i]); } g_free(fds); g_free(vhost_fds); - return -1; + return ret; } else if (tap->has_helper) { if (tap->has_ifname || tap->has_script || tap->has_downscript || tap->has_vnet_hdr || tap->has_queues || tap->has_vhostfds) { diff --git a/net/vhost-user.c b/net/vhost-user.c index e0f16c895b..a39f9c9974 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -12,6 +12,7 @@ #include "clients.h" #include "net/vhost_net.h" #include "net/vhost-user.h" +#include "hw/virtio/vhost-user.h" #include "chardev/char-fe.h" #include "qapi/error.h" #include "qapi/qapi-commands-net.h" @@ -20,38 +21,39 @@ #include "qemu/option.h" #include "trace.h" -typedef struct VhostUserState { +typedef struct NetVhostUserState { NetClientState nc; CharBackend chr; /* only queue index 0 */ + VhostUserState *vhost_user; VHostNetState *vhost_net; guint watch; uint64_t acked_features; bool started; -} VhostUserState; +} NetVhostUserState; VHostNetState *vhost_user_get_vhost_net(NetClientState *nc) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); return s->vhost_net; } uint64_t vhost_user_get_acked_features(NetClientState *nc) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); return s->acked_features; } static void vhost_user_stop(int queues, NetClientState *ncs[]) { - VhostUserState *s; + NetVhostUserState *s; int i; for (i = 0; i < queues; i++) { assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER); - s = DO_UPCAST(VhostUserState, nc, ncs[i]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); if (s->vhost_net) { /* save acked features */ @@ -64,11 +66,12 @@ static void vhost_user_stop(int queues, NetClientState *ncs[]) } } -static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) +static int vhost_user_start(int queues, NetClientState *ncs[], + VhostUserState *be) { VhostNetOptions options; struct vhost_net *net = NULL; - VhostUserState *s; + NetVhostUserState *s; int max_queues; int i; @@ -77,7 +80,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], CharBackend *be) for (i = 0; i < queues; i++) { assert(ncs[i]->info->type == NET_CLIENT_DRIVER_VHOST_USER); - s = DO_UPCAST(VhostUserState, nc, ncs[i]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[i]); options.net_backend = ncs[i]; options.opaque = be; @@ -123,7 +126,7 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, without GUEST_ANNOUNCE capability. */ if (size == 60) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); int r; static int display_rarp_failure = 1; char mac_addr[6]; @@ -144,9 +147,9 @@ static ssize_t vhost_user_receive(NetClientState *nc, const uint8_t *buf, return size; } -static void vhost_user_cleanup(NetClientState *nc) +static void net_vhost_user_cleanup(NetClientState *nc) { - VhostUserState *s = DO_UPCAST(VhostUserState, nc, nc); + NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); if (s->vhost_net) { vhost_net_cleanup(s->vhost_net); @@ -159,6 +162,11 @@ static void vhost_user_cleanup(NetClientState *nc) s->watch = 0; } qemu_chr_fe_deinit(&s->chr, true); + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } } qemu_purge_queued_packets(nc); @@ -180,9 +188,9 @@ static bool vhost_user_has_ufo(NetClientState *nc) static NetClientInfo net_vhost_user_info = { .type = NET_CLIENT_DRIVER_VHOST_USER, - .size = sizeof(VhostUserState), + .size = sizeof(NetVhostUserState), .receive = vhost_user_receive, - .cleanup = vhost_user_cleanup, + .cleanup = net_vhost_user_cleanup, .has_vnet_hdr = vhost_user_has_vnet_hdr, .has_ufo = vhost_user_has_ufo, }; @@ -190,7 +198,7 @@ static NetClientInfo net_vhost_user_info = { static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond, void *opaque) { - VhostUserState *s = opaque; + NetVhostUserState *s = opaque; qemu_chr_fe_disconnect(&s->chr); @@ -203,7 +211,7 @@ static void chr_closed_bh(void *opaque) { const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; - VhostUserState *s; + NetVhostUserState *s; Error *err = NULL; int queues; @@ -212,7 +220,7 @@ static void chr_closed_bh(void *opaque) MAX_QUEUE_NUM); assert(queues < MAX_QUEUE_NUM); - s = DO_UPCAST(VhostUserState, nc, ncs[0]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); qmp_set_link(name, false, &err); vhost_user_stop(queues, ncs); @@ -229,7 +237,7 @@ static void net_vhost_user_event(void *opaque, int event) { const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; - VhostUserState *s; + NetVhostUserState *s; Chardev *chr; Error *err = NULL; int queues; @@ -239,12 +247,12 @@ static void net_vhost_user_event(void *opaque, int event) MAX_QUEUE_NUM); assert(queues < MAX_QUEUE_NUM); - s = DO_UPCAST(VhostUserState, nc, ncs[0]); + s = DO_UPCAST(NetVhostUserState, nc, ncs[0]); chr = qemu_chr_fe_get_driver(&s->chr); trace_vhost_user_event(chr->label, event); switch (event) { case CHR_EVENT_OPENED: - if (vhost_user_start(queues, ncs, &s->chr) < 0) { + if (vhost_user_start(queues, ncs, s->vhost_user) < 0) { qemu_chr_fe_disconnect(&s->chr); return; } @@ -283,12 +291,19 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, { Error *err = NULL; NetClientState *nc, *nc0 = NULL; - VhostUserState *s; + VhostUserState *user = NULL; + NetVhostUserState *s = NULL; int i; assert(name); assert(queues > 0); + user = vhost_user_init(); + if (!user) { + error_report("failed to init vhost_user"); + goto err; + } + for (i = 0; i < queues; i++) { nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s", @@ -296,20 +311,22 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, nc->queue_index = i; if (!nc0) { nc0 = nc; - s = DO_UPCAST(VhostUserState, nc, nc); + s = DO_UPCAST(NetVhostUserState, nc, nc); if (!qemu_chr_fe_init(&s->chr, chr, &err)) { error_report_err(err); - return -1; + goto err; } + user->chr = &s->chr; } - + s = DO_UPCAST(NetVhostUserState, nc, nc); + s->vhost_user = user; } - s = DO_UPCAST(VhostUserState, nc, nc0); + s = DO_UPCAST(NetVhostUserState, nc, nc0); do { if (qemu_chr_fe_wait_connected(&s->chr, &err) < 0) { error_report_err(err); - return -1; + goto err; } qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, NULL, nc0->name, NULL, @@ -319,6 +336,20 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, assert(s->vhost_net); return 0; + +err: + if (user) { + vhost_user_cleanup(user); + g_free(user); + if (s) { + s->vhost_user = NULL; + } + } + if (nc0) { + qemu_del_net_client(nc0); + } + + return -1; } static Chardev *net_vhost_claim_chardev( diff --git a/numa.c b/numa.c index 1116c90af9..5f6367b989 100644 --- a/numa.c +++ b/numa.c @@ -36,6 +36,7 @@ #include "hw/boards.h" #include "sysemu/hostmem.h" #include "hw/mem/pc-dimm.h" +#include "hw/mem/memory-device.h" #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/cutils.h" @@ -140,9 +141,8 @@ static void parse_numa_distance(NumaDistOptions *dist, Error **errp) uint8_t val = dist->val; if (src >= MAX_NODES || dst >= MAX_NODES) { - error_setg(errp, - "Invalid node %d, max possible could be %d", - MAX(src, dst), MAX_NODES); + error_setg(errp, "Parameter '%s' expects an integer between 0 and %d", + src >= MAX_NODES ? "src" : "dst", MAX_NODES - 1); return; } @@ -169,28 +169,11 @@ static void parse_numa_distance(NumaDistOptions *dist, Error **errp) have_numa_distance = true; } -static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) +static +void set_numa_options(MachineState *ms, NumaOptions *object, Error **errp) { - NumaOptions *object = NULL; - MachineState *ms = opaque; Error *err = NULL; - { - Visitor *v = opts_visitor_new(opts); - visit_type_NumaOptions(v, NULL, &object, &err); - visit_free(v); - } - - if (err) { - goto end; - } - - /* Fix up legacy suffix-less format */ - if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) { - const char *mem_str = qemu_opt_get(opts, "mem"); - qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); - } - switch (object->type) { case NUMA_OPTIONS_TYPE_NODE: parse_numa_node(ms, &object->u.node, &err); @@ -223,6 +206,31 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) abort(); } +end: + error_propagate(errp, err); +} + +int parse_numa(void *opaque, QemuOpts *opts, Error **errp) +{ + NumaOptions *object = NULL; + MachineState *ms = MACHINE(opaque); + Error *err = NULL; + Visitor *v = opts_visitor_new(opts); + + visit_type_NumaOptions(v, NULL, &object, &err); + visit_free(v); + if (err) { + goto end; + } + + /* Fix up legacy suffix-less format */ + if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) { + const char *mem_str = qemu_opt_get(opts, "mem"); + qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem); + } + + set_numa_options(ms, object, &err); + end: qapi_free_NumaOptions(object); if (err) { @@ -338,15 +346,11 @@ void numa_default_auto_assign_ram(MachineClass *mc, NodeInfo *nodes, nodes[i].node_mem = size - usedmem; } -void parse_numa_opts(MachineState *ms) +void numa_complete_configuration(MachineState *ms) { int i; MachineClass *mc = MACHINE_GET_CLASS(ms); - if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) { - exit(1); - } - /* * If memory hotplug is enabled (slots > 0) but without '-numa' * options explicitly on CLI, guestes will break. @@ -433,6 +437,24 @@ void parse_numa_opts(MachineState *ms) } } +void parse_numa_opts(MachineState *ms) +{ + if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) { + exit(1); + } +} + +void qmp_set_numa_node(NumaOptions *cmd, Error **errp) +{ + if (!runstate_check(RUN_STATE_PRECONFIG)) { + error_setg(errp, "The command is permitted only in '%s' state", + RunState_str(RUN_STATE_PRECONFIG)); + return; + } + + set_numa_options(MACHINE(qdev_get_machine()), cmd, errp); +} + void numa_cpu_pre_plug(const CPUArchId *slot, DeviceState *dev, Error **errp) { int node_id = object_property_get_int(OBJECT(dev), "node-id", &error_abort); @@ -469,6 +491,7 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner, /* Legacy behavior: if allocation failed, fall back to * regular RAM allocation. */ + mem_path = NULL; memory_region_init_ram_nomigrate(mr, owner, name, ram_size, &error_fatal); } #else @@ -500,8 +523,7 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, if (!backend) { continue; } - MemoryRegion *seg = host_memory_backend_get_memory(backend, - &error_fatal); + MemoryRegion *seg = host_memory_backend_get_memory(backend); if (memory_region_is_mapped(seg)) { char *path = object_get_canonical_path_component(OBJECT(backend)); @@ -520,7 +542,7 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, static void numa_stat_memory_devices(NumaNodeMem node_mem[]) { - MemoryDeviceInfoList *info_list = qmp_pc_dimm_device_list(); + MemoryDeviceInfoList *info_list = qmp_memory_device_list(); MemoryDeviceInfoList *info; PCDIMMDeviceInfo *pcdimm_info; @@ -544,10 +566,8 @@ static void numa_stat_memory_devices(NumaNodeMem node_mem[]) if (pcdimm_info) { node_mem[pcdimm_info->node].node_mem += pcdimm_info->size; - if (pcdimm_info->hotpluggable && pcdimm_info->hotplugged) { - node_mem[pcdimm_info->node].node_plugged_mem += - pcdimm_info->size; - } + node_mem[pcdimm_info->node].node_plugged_mem += + pcdimm_info->size; } } } @@ -578,7 +598,7 @@ static int query_memdev(Object *obj, void *opaque) m->value = g_malloc0(sizeof(*m->value)); - m->value->id = object_property_get_str(obj, "id", NULL); + m->value->id = object_get_canonical_path_component(obj); m->value->has_id = !!m->value->id; m->value->size = object_property_get_uint(obj, "size", diff --git a/os-posix.c b/os-posix.c index b9c2343b1e..9ce6f74513 100644 --- a/os-posix.c +++ b/os-posix.c @@ -41,7 +41,14 @@ #include #endif -static struct passwd *user_pwd; +/* + * Must set all three of these at once. + * Legal combinations are unset by name by uid + */ +static struct passwd *user_pwd; /* NULL non-NULL NULL */ +static uid_t user_uid = (uid_t)-1; /* -1 -1 >=0 */ +static gid_t user_gid = (gid_t)-1; /* -1 -1 >=0 */ + static const char *chroot_dir; static int daemonize; static int daemon_pipe; @@ -118,20 +125,47 @@ void os_set_proc_name(const char *s) /* Could rewrite argv[0] too, but that's a bit more complicated. This simple way is enough for `top'. */ if (prctl(PR_SET_NAME, name)) { - perror("unable to change process name"); + error_report("unable to change process name: %s", strerror(errno)); exit(1); } #else - fprintf(stderr, "Change of process name not supported by your OS\n"); + error_report("Change of process name not supported by your OS"); exit(1); #endif } + +static bool os_parse_runas_uid_gid(const char *optarg) +{ + unsigned long lv; + const char *ep; + uid_t got_uid; + gid_t got_gid; + int rc; + + rc = qemu_strtoul(optarg, &ep, 0, &lv); + got_uid = lv; /* overflow here is ID in C99 */ + if (rc || *ep != ':' || got_uid != lv || got_uid == (uid_t)-1) { + return false; + } + + rc = qemu_strtoul(ep + 1, 0, 0, &lv); + got_gid = lv; /* overflow here is ID in C99 */ + if (rc || got_gid != lv || got_gid == (gid_t)-1) { + return false; + } + + user_pwd = NULL; + user_uid = got_uid; + user_gid = got_gid; + return true; +} + /* * Parse OS specific command line options. * return 0 if option handled, -1 otherwise */ -void os_parse_cmd_args(int index, const char *optarg) +int os_parse_cmd_args(int index, const char *optarg) { switch (index) { #ifdef CONFIG_SLIRP @@ -144,8 +178,13 @@ void os_parse_cmd_args(int index, const char *optarg) #endif case QEMU_OPTION_runas: user_pwd = getpwnam(optarg); - if (!user_pwd) { - fprintf(stderr, "User \"%s\" doesn't exist\n", optarg); + if (user_pwd) { + user_uid = -1; + user_gid = -1; + } else if (!os_parse_runas_uid_gid(optarg)) { + error_report("User \"%s\" doesn't exist" + " (and is not :)", + optarg); exit(1); } break; @@ -160,27 +199,45 @@ void os_parse_cmd_args(int index, const char *optarg) fips_set_state(true); break; #endif + default: + return -1; } + + return 0; } static void change_process_uid(void) { - if (user_pwd) { - if (setgid(user_pwd->pw_gid) < 0) { - fprintf(stderr, "Failed to setgid(%d)\n", user_pwd->pw_gid); + assert((user_uid == (uid_t)-1) || user_pwd == NULL); + assert((user_uid == (uid_t)-1) == + (user_gid == (gid_t)-1)); + + if (user_pwd || user_uid != (uid_t)-1) { + gid_t intended_gid = user_pwd ? user_pwd->pw_gid : user_gid; + uid_t intended_uid = user_pwd ? user_pwd->pw_uid : user_uid; + if (setgid(intended_gid) < 0) { + error_report("Failed to setgid(%d)", intended_gid); exit(1); } - if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { - fprintf(stderr, "Failed to initgroups(\"%s\", %d)\n", - user_pwd->pw_name, user_pwd->pw_gid); - exit(1); + if (user_pwd) { + if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { + error_report("Failed to initgroups(\"%s\", %d)", + user_pwd->pw_name, user_pwd->pw_gid); + exit(1); + } + } else { + if (setgroups(1, &user_gid) < 0) { + error_report("Failed to setgroups(1, [%d])", + user_gid); + exit(1); + } } - if (setuid(user_pwd->pw_uid) < 0) { - fprintf(stderr, "Failed to setuid(%d)\n", user_pwd->pw_uid); + if (setuid(intended_uid) < 0) { + error_report("Failed to setuid(%d)", intended_uid); exit(1); } if (setuid(0) != -1) { - fprintf(stderr, "Dropping privileges failed\n"); + error_report("Dropping privileges failed"); exit(1); } } @@ -190,11 +247,11 @@ static void change_root(void) { if (chroot_dir) { if (chroot(chroot_dir) < 0) { - fprintf(stderr, "chroot failed\n"); + error_report("chroot failed"); exit(1); } if (chdir("/")) { - perror("not able to chdir to /"); + error_report("not able to chdir to /: %s", strerror(errno)); exit(1); } } @@ -256,7 +313,7 @@ void os_setup_post(void) if (daemonize) { if (chdir("/")) { - perror("not able to chdir to /"); + error_report("not able to chdir to /: %s", strerror(errno)); exit(1); } TFR(fd = qemu_open("/dev/null", O_RDWR)); @@ -330,7 +387,7 @@ int os_mlock(void) ret = mlockall(MCL_CURRENT | MCL_FUTURE); if (ret < 0) { - perror("mlockall"); + error_report("mlockall: %s", strerror(errno)); } return ret; diff --git a/os-win32.c b/os-win32.c index 586a7c7d49..0674f94b57 100644 --- a/os-win32.c +++ b/os-win32.c @@ -93,9 +93,9 @@ void os_set_line_buffering(void) * Parse OS specific command line options. * return 0 if option handled, -1 otherwise */ -void os_parse_cmd_args(int index, const char *optarg) +int os_parse_cmd_args(int index, const char *optarg) { - return; + return -1; } int qemu_create_pidfile(const char *filename) diff --git a/pc-bios/README b/pc-bios/README index a843e1e8b1..99e15a737b 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -17,7 +17,7 @@ - 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-20171214. + built from git tag qemu-slof-20180621. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index 0061dc9928..6ffa6ec524 100644 Binary files a/pc-bios/bios-256k.bin and b/pc-bios/bios-256k.bin differ diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index 69bb8635ae..afa450c4a0 100644 Binary files a/pc-bios/bios.bin and b/pc-bios/bios.bin differ diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index af70c365ee..a39cbe57ca 100644 Binary files a/pc-bios/openbios-ppc and b/pc-bios/openbios-ppc differ diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index 9e736e8838..7163ba8b3b 100644 Binary files a/pc-bios/openbios-sparc32 and b/pc-bios/openbios-sparc32 differ diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index 82ea0f8be6..0a9a338f78 100644 Binary files a/pc-bios/openbios-sparc64 and b/pc-bios/openbios-sparc64 differ diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index fdd6809c70..450a076dc0 100644 Binary files a/pc-bios/s390-ccw.img and b/pc-bios/s390-ccw.img differ diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 1712c2d95d..1eb316b02f 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -9,10 +9,13 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all -OBJECTS = start.o main.o bootmap.o sclp.o virtio.o virtio-scsi.o virtio-blkdev.o libc.o menu.o +OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o \ + virtio.o virtio-scsi.o virtio-blkdev.o libc.o + QEMU_CFLAGS := $(filter -W%, $(QEMU_CFLAGS)) QEMU_CFLAGS += -ffreestanding -fno-delete-null-pointer-checks -msoft-float QEMU_CFLAGS += -march=z900 -fPIE -fno-strict-aliasing +QEMU_CFLAGS += -fno-asynchronous-unwind-tables QEMU_CFLAGS += $(call cc-option, $(QEMU_CFLAGS), -fno-stack-protector) LDFLAGS += -Wl,-pie -nostdlib diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 9287b7a70f..7aef65ab67 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -29,14 +29,6 @@ /* Scratch space */ static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE))); -typedef struct ResetInfo { - uint32_t ipl_mask; - uint32_t ipl_addr; - uint32_t ipl_continue; -} ResetInfo; - -static ResetInfo save; - const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; @@ -57,53 +49,6 @@ static inline bool is_iso_vd_valid(IsoVolDesc *vd) vd->type <= VOL_DESC_TYPE_PARTITION; } -static void jump_to_IPL_2(void) -{ - ResetInfo *current = 0; - - void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue; - *current = save; - ipl(); /* should not return */ -} - -static void jump_to_IPL_code(uint64_t address) -{ - /* store the subsystem information _after_ the bootmap was loaded */ - write_subsystem_identification(); - - /* prevent unknown IPL types in the guest */ - if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { - iplb.pbt = S390_IPL_TYPE_CCW; - set_iplb(&iplb); - } - - /* - * The IPL PSW is at address 0. We also must not overwrite the - * content of non-BIOS memory after we loaded the guest, so we - * save the original content and restore it in jump_to_IPL_2. - */ - ResetInfo *current = 0; - - save = *current; - current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2; - current->ipl_continue = address & 0x7fffffff; - - debug_print_int("set IPL addr to", current->ipl_continue); - - /* Ensure the guest output starts fresh */ - sclp_print("\n"); - - /* - * HACK ALERT. - * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 - * can then use r15 as its stack pointer. - */ - asm volatile("lghi 1,1\n\t" - "diag 1,1,0x308\n\t" - : : : "1", "memory"); - panic("\n! IPL returns !\n"); -} - /*********************************************************************** * IPL an ECKD DASD (CDL or LDL/CMS format) */ @@ -297,7 +242,7 @@ static void run_eckd_boot_script(block_number_t bmt_block_nr, } debug_print_int("loadparm", loadparm); - IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" + IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" " maximum number of boot entries allowed"); memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -565,6 +510,8 @@ static void ipl_scsi(void) int program_table_entries = 0; BootMapTable *prog_table = (void *)sec; unsigned int loadparm = get_loadparm_index(); + bool valid_entries[MAX_BOOT_ENTRIES] = {false}; + size_t i; /* Grab the MBR */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -585,22 +532,22 @@ static void ipl_scsi(void) read_block(mbr->pt.blockno, sec, "Error reading Program Table"); IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic in PT"); - while (program_table_entries <= MAX_TABLE_ENTRIES) { - if (!prog_table->entry[program_table_entries].scsi.blockno) { - break; + for (i = 0; i < MAX_BOOT_ENTRIES; i++) { + if (prog_table->entry[i].scsi.blockno) { + valid_entries[i] = true; + program_table_entries++; } - program_table_entries++; } debug_print_int("program table entries", program_table_entries); IPL_assert(program_table_entries != 0, "Empty Program Table"); if (menu_is_enabled_enum()) { - loadparm = menu_get_enum_boot_index(program_table_entries); + loadparm = menu_get_enum_boot_index(valid_entries); } debug_print_int("loadparm", loadparm); - IPL_assert(loadparm <= MAX_TABLE_ENTRIES, "loadparm value greater than" + IPL_assert(loadparm < MAX_BOOT_ENTRIES, "loadparm value greater than" " maximum number of boot entries allowed"); zipl_run(&prog_table->entry[loadparm].scsi); /* no return */ @@ -727,13 +674,7 @@ static void load_iso_bc_entry(IsoBcSection *load) (void *)((uint64_t)bswap16(s.load_segment)), blks_to_load); - /* Trying to get PSW at zero address */ - if (*((uint64_t *)0) & IPL_PSW_MASK) { - jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); - } - - /* Try default linux start address */ - jump_to_IPL_code(KERN_IMAGE_START); + jump_to_low_kernel(); } static uint32_t find_iso_bc(void) diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 07eb600b00..a085212077 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -57,8 +57,6 @@ typedef union BootMapPointer { ExtEckdBlockPtr xeckd; } __attribute__ ((packed)) BootMapPointer; -#define MAX_TABLE_ENTRIES 30 - /* aka Program Table */ typedef struct BootMapTable { uint8_t magic[4]; @@ -355,10 +353,6 @@ static inline uint32_t iso_733_to_u32(uint64_t x) #define ISO_SECTOR_SIZE 2048 /* El Torito specifies boot image size in 512 byte blocks */ #define ET_SECTOR_SHIFT 2 -#define KERN_IMAGE_START 0x010000UL -#define PSW_MASK_64 0x0000000100000000ULL -#define PSW_MASK_32 0x0000000080000000ULL -#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) #define ISO_PRIMARY_VD_SECTOR 16 diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h index 55eaeee4b6..1a0795f645 100644 --- a/pc-bios/s390-ccw/cio.h +++ b/pc-bios/s390-ccw/cio.h @@ -125,7 +125,7 @@ struct tpi_info { __u32 reserved3 : 12; __u32 int_type : 3; __u32 reserved4 : 12; -} __attribute__ ((packed)); +} __attribute__ ((packed, aligned(4))); /* channel command word (type 1) */ struct ccw1 { diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h index 5357a36d51..772d5c57c9 100644 --- a/pc-bios/s390-ccw/iplb.h +++ b/pc-bios/s390-ccw/iplb.h @@ -12,6 +12,8 @@ #ifndef IPLB_H #define IPLB_H +#define LOADPARM_LEN 8 + struct IplBlockCcw { uint8_t reserved0[85]; uint8_t ssid; @@ -61,7 +63,7 @@ struct IplParameterBlock { uint8_t pbt; uint8_t flags; uint16_t reserved01; - uint8_t loadparm[8]; + uint8_t loadparm[LOADPARM_LEN]; union { IplBlockCcw ccw; IplBlockFcp fcp; @@ -101,10 +103,11 @@ static inline bool manage_iplb(IplParameterBlock *iplb, bool store) { register unsigned long addr asm("0") = (unsigned long) iplb; register unsigned long rc asm("1") = 0; + unsigned long subcode = store ? 6 : 5; asm volatile ("diag %0,%2,0x308\n" : "+d" (addr), "+d" (rc) - : "d" (store ? 6 : 5) + : "d" (subcode) : "memory", "cc"); return rc == 0x01; } diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c new file mode 100644 index 0000000000..266f1502b9 --- /dev/null +++ b/pc-bios/s390-ccw/jump2ipl.c @@ -0,0 +1,91 @@ +/* + * QEMU s390-ccw firmware - jump to IPL code + * + * 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. + */ + +#include "libc.h" +#include "s390-ccw.h" + +#define KERN_IMAGE_START 0x010000UL +#define PSW_MASK_64 0x0000000100000000ULL +#define PSW_MASK_32 0x0000000080000000ULL +#define IPL_PSW_MASK (PSW_MASK_32 | PSW_MASK_64) + +typedef struct ResetInfo { + uint32_t ipl_mask; + uint32_t ipl_addr; + uint32_t ipl_continue; +} ResetInfo; + +static ResetInfo save; + +static void jump_to_IPL_2(void) +{ + ResetInfo *current = 0; + + void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue; + *current = save; + ipl(); /* should not return */ +} + +void jump_to_IPL_code(uint64_t address) +{ + /* store the subsystem information _after_ the bootmap was loaded */ + write_subsystem_identification(); + + /* prevent unknown IPL types in the guest */ + if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) { + iplb.pbt = S390_IPL_TYPE_CCW; + set_iplb(&iplb); + } + + /* + * The IPL PSW is at address 0. We also must not overwrite the + * content of non-BIOS memory after we loaded the guest, so we + * save the original content and restore it in jump_to_IPL_2. + */ + ResetInfo *current = 0; + + save = *current; + current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2; + current->ipl_continue = address & 0x7fffffff; + + debug_print_int("set IPL addr to", current->ipl_continue); + + /* Ensure the guest output starts fresh */ + sclp_print("\n"); + + /* + * HACK ALERT. + * We use the load normal reset to keep r15 unchanged. jump_to_IPL_2 + * can then use r15 as its stack pointer. + */ + asm volatile("lghi 1,1\n\t" + "diag 1,1,0x308\n\t" + : : : "1", "memory"); + panic("\n! IPL returns !\n"); +} + +void jump_to_low_kernel(void) +{ + /* + * If it looks like a Linux binary, i.e. there is the "S390EP" magic from + * arch/s390/kernel/head.S here, then let's jump to the well-known Linux + * kernel start address (when jumping to the PSW-at-zero address instead, + * the kernel startup code fails when we booted from a network device). + */ + if (!memcmp((char *)0x10008, "S390EP", 6)) { + jump_to_IPL_code(KERN_IMAGE_START); + } + + /* Trying to get PSW at zero address */ + if (*((uint64_t *)0) & IPL_PSW_MASK) { + jump_to_IPL_code((*((uint64_t *)0)) & 0x7fffffff); + } + + /* No other option left, so use the Linux kernel start address */ + jump_to_IPL_code(KERN_IMAGE_START); +} diff --git a/pc-bios/s390-ccw/libc.c b/pc-bios/s390-ccw/libc.c index 38ea77d7aa..a786566c4c 100644 --- a/pc-bios/s390-ccw/libc.c +++ b/pc-bios/s390-ccw/libc.c @@ -63,7 +63,7 @@ uint64_t atoui(const char *str) */ char *uitoa(uint64_t num, char *str, size_t len) { - size_t num_idx = 1; /* account for NUL */ + long num_idx = 1; /* account for NUL */ uint64_t tmp = num; IPL_assert(str != NULL, "uitoa: no space allocated to store string"); diff --git a/pc-bios/s390-ccw/libc.h b/pc-bios/s390-ccw/libc.h index 63ece70c6b..818517ff5d 100644 --- a/pc-bios/s390-ccw/libc.h +++ b/pc-bios/s390-ccw/libc.h @@ -12,7 +12,7 @@ #ifndef S390_CCW_LIBC_H #define S390_CCW_LIBC_H -typedef long size_t; +typedef unsigned long size_t; typedef int bool; typedef unsigned char uint8_t; typedef unsigned short uint16_t; diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 9d9f8cf4d3..544851d672 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -15,11 +15,11 @@ char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE))); static SubChannelId blk_schid = { .one = 1 }; IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE))); -static char loadparm[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +static char loadparm_str[LOADPARM_LEN + 1] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; QemuIplParameters qipl; #define LOADPARM_PROMPT "PROMPT " -#define LOADPARM_EMPTY "........" +#define LOADPARM_EMPTY " " #define BOOT_MENU_FLAG_MASK (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL) /* @@ -45,7 +45,7 @@ void panic(const char *string) unsigned int get_loadparm_index(void) { - return atoui(loadparm); + return atoui(loadparm_str); } static bool find_dev(Schib *schib, int dev_no) @@ -80,13 +80,13 @@ static bool find_dev(Schib *schib, int dev_no) static void menu_setup(void) { - if (memcmp(loadparm, LOADPARM_PROMPT, 8) == 0) { + if (memcmp(loadparm_str, LOADPARM_PROMPT, LOADPARM_LEN) == 0) { menu_set_parms(QIPL_FLAG_BM_OPTS_CMD, 0); return; } /* If loadparm was set to any other value, then do not enable menu */ - if (memcmp(loadparm, LOADPARM_EMPTY, 8) != 0) { + if (memcmp(loadparm_str, LOADPARM_EMPTY, LOADPARM_LEN) != 0) { return; } @@ -116,8 +116,8 @@ static void virtio_setup(void) */ enable_mss_facility(); - sclp_get_loadparm_ascii(loadparm); - memcpy(ldp + 10, loadparm, 8); + sclp_get_loadparm_ascii(loadparm_str); + memcpy(ldp + 10, loadparm_str, LOADPARM_LEN); sclp_print(ldp); memcpy(&qipl, early_qipl, sizeof(QemuIplParameters)); diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index 96eec81e84..82a4ae6315 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -158,7 +158,7 @@ static void boot_menu_prompt(bool retry) } } -static int get_boot_index(int entries) +static int get_boot_index(bool *valid_entries) { int boot_index; bool retry = false; @@ -168,7 +168,8 @@ static int get_boot_index(int entries) boot_menu_prompt(retry); boot_index = get_index(); retry = true; - } while (boot_index < 0 || boot_index >= entries); + } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES || + !valid_entries[boot_index]); sclp_print("\nBooting entry #"); sclp_print(uitoa(boot_index, tmp, sizeof(tmp))); @@ -176,7 +177,8 @@ static int get_boot_index(int entries) return boot_index; } -static void zipl_println(const char *data, size_t len) +/* Returns the entry number that was printed */ +static int zipl_print_entry(const char *data, size_t len) { char buf[len + 2]; @@ -185,12 +187,15 @@ static void zipl_println(const char *data, size_t len) buf[len + 1] = '\0'; sclp_print(buf); + + return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf); } int menu_get_zipl_boot_index(const char *menu_data) { size_t len; - int entries; + int entry; + bool valid_entries[MAX_BOOT_ENTRIES] = {false}; uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET); uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET); @@ -202,34 +207,51 @@ int menu_get_zipl_boot_index(const char *menu_data) timeout = zipl_timeout * 1000; } - /* Print and count all menu items, including the banner */ - for (entries = 0; *menu_data; entries++) { + /* Print banner */ + sclp_print("s390-ccw zIPL Boot Menu\n\n"); + menu_data += strlen(menu_data) + 1; + + /* Print entries */ + while (*menu_data) { len = strlen(menu_data); - zipl_println(menu_data, len); + entry = zipl_print_entry(menu_data, len); menu_data += len + 1; - if (entries < 2) { + valid_entries[entry] = true; + + if (entry == 0) { sclp_print("\n"); } } sclp_print("\n"); - return get_boot_index(entries - 1); /* subtract 1 to exclude banner */ + return get_boot_index(valid_entries); } - -int menu_get_enum_boot_index(int entries) +int menu_get_enum_boot_index(bool *valid_entries) { - char tmp[4]; + char tmp[3]; + int i; - sclp_print("s390x Enumerated Boot Menu.\n\n"); + sclp_print("s390-ccw Enumerated Boot Menu.\n\n"); - sclp_print(uitoa(entries, tmp, sizeof(tmp))); - sclp_print(" entries detected. Select from boot index 0 to "); - sclp_print(uitoa(entries - 1, tmp, sizeof(tmp))); - sclp_print(".\n\n"); + for (i = 0; i < MAX_BOOT_ENTRIES; i++) { + if (valid_entries[i]) { + if (i < 10) { + sclp_print(" "); + } + sclp_print("["); + sclp_print(uitoa(i, tmp, sizeof(tmp))); + sclp_print("]"); + if (i == 0) { + sclp_print(" default\n"); + } + sclp_print("\n"); + } + } - return get_boot_index(entries); + sclp_print("\n"); + return get_boot_index(valid_entries); } void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) diff --git a/pc-bios/s390-ccw/netboot.mak b/pc-bios/s390-ccw/netboot.mak index a25d238144..14e96b2aa6 100644 --- a/pc-bios/s390-ccw/netboot.mak +++ b/pc-bios/s390-ccw/netboot.mak @@ -1,7 +1,8 @@ SLOF_DIR := $(SRC_PATH)/roms/SLOF -NETOBJS := start.o sclp.o virtio.o virtio-net.o netmain.o libnet.a libc.a +NETOBJS := start.o sclp.o virtio.o virtio-net.o jump2ipl.o netmain.o \ + libnet.a libc.a LIBC_INC := -nostdinc -I$(SLOF_DIR)/lib/libc/include LIBNET_INC := -I$(SLOF_DIR)/lib/libnet @@ -18,14 +19,15 @@ s390-netboot.img: s390-netboot.elf # libc files: -LIBC_CFLAGS := $(QEMU_CFLAGS) $(LIBC_INC) $(LIBNET_INC) +LIBC_CFLAGS := $(QEMU_CFLAGS) $(CFLAGS) $(LIBC_INC) $(LIBNET_INC) CTYPE_OBJS = isdigit.o isxdigit.o toupper.o %.o : $(SLOF_DIR)/lib/libc/ctype/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") -STRING_OBJS = strcat.o strchr.o strcmp.o strcpy.o strlen.o strncmp.o strncpy.o \ - strstr.o memset.o memcpy.o memmove.o memcmp.o +STRING_OBJS = strcat.o strchr.o strrchr.o strcpy.o strlen.o strncpy.o \ + strcmp.o strncmp.o strcasecmp.o strncasecmp.o strstr.o \ + memset.o memcpy.o memmove.o memcmp.o %.o : $(SLOF_DIR)/lib/libc/string/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") @@ -33,7 +35,7 @@ STDLIB_OBJS = atoi.o atol.o strtoul.o strtol.o rand.o malloc.o free.o %.o : $(SLOF_DIR)/lib/libc/stdlib/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") -STDIO_OBJS = sprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \ +STDIO_OBJS = sprintf.o snprintf.o vfprintf.o vsnprintf.o vsprintf.o fprintf.o \ printf.o putc.o puts.o putchar.o stdchnls.o fileno.o %.o : $(SLOF_DIR)/lib/libc/stdio/%.c $(call quiet-command,$(CC) $(LIBC_CFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") @@ -49,8 +51,8 @@ libc.a: $(LIBCOBJS) # libnet files: LIBNETOBJS := args.o dhcp.o dns.o icmpv6.o ipv6.o tcp.o udp.o bootp.o \ - dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o -LIBNETCFLAGS := $(QEMU_CFLAGS) -DDHCPARCH=0x1F $(LIBC_INC) $(LIBNET_INC) + dhcpv6.o ethernet.o ipv4.o ndp.o tftp.o pxelinux.o +LIBNETCFLAGS := $(QEMU_CFLAGS) $(CFLAGS) -DDHCPARCH=0x1F $(LIBC_INC) $(LIBNET_INC) %.o : $(SLOF_DIR)/lib/libnet/%.c $(call quiet-command,$(CC) $(LIBNETCFLAGS) -c -o $@ $<,"CC","$(TARGET_DIR)$@") diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index d86d46b03f..0392131c27 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "s390-ccw.h" #include "virtio.h" @@ -39,11 +40,19 @@ extern char _start[]; +#define KERNEL_ADDR ((void *)0L) +#define KERNEL_MAX_SIZE ((long)_start) +#define ARCH_COMMAND_LINE_SIZE 896 /* Taken from Linux kernel */ + +/* STSI 3.2.2 offset of first vmdb + offset of uuid inside vmdb */ +#define STSI322_VMDB_UUID_OFFSET ((8 + 12) * 4) + char stack[PAGE_SIZE * 8] __attribute__((aligned(PAGE_SIZE))); IplParameterBlock iplb __attribute__((aligned(PAGE_SIZE))); +static char cfgbuf[2048]; static SubChannelId net_schid = { .one = 1 }; -static int ip_version = 4; +static uint8_t mac[6]; static uint64_t dest_timer; static uint64_t get_timer_ms(void) @@ -96,10 +105,10 @@ static int dhcp(struct filename_ip *fn_ip, int retries) printf("\nGiving up after %d DHCP requests\n", retries); return -1; } - ip_version = 4; + fn_ip->ip_version = 4; rc = dhcpv4(NULL, fn_ip); if (rc == -1) { - ip_version = 6; + fn_ip->ip_version = 6; set_ipv6_address(fn_ip->fd, 0); rc = dhcpv6(NULL, fn_ip); if (rc == 0) { @@ -128,91 +137,44 @@ static void seed_rng(uint8_t mac[]) srand(seed); } -static int tftp_load(filename_ip_t *fnip, void *buffer, int len, - unsigned int retries, int ip_vers) +static int tftp_load(filename_ip_t *fnip, void *buffer, int len) { tftp_err_t tftp_err; int rc; - rc = tftp(fnip, buffer, len, retries, &tftp_err, 1, 1428, ip_vers); + rc = tftp(fnip, buffer, len, DEFAULT_TFTP_RETRIES, &tftp_err); - if (rc > 0) { - printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, - rc / 1024); - } else if (rc == -1) { - puts("unknown TFTP error"); - } else if (rc == -2) { - printf("TFTP buffer of %d bytes is too small for %s\n", - len, fnip->filename); - } else if (rc == -3) { - printf("file not found: %s\n", fnip->filename); - } else if (rc == -4) { - puts("TFTP access violation"); - } else if (rc == -5) { - puts("illegal TFTP operation"); - } else if (rc == -6) { - puts("unknown TFTP transfer ID"); - } else if (rc == -7) { - puts("no such TFTP user"); - } else if (rc == -8) { - puts("TFTP blocksize negotiation failed"); - } else if (rc == -9) { - puts("file exceeds maximum TFTP transfer size"); - } else if (rc <= -10 && rc >= -15) { - const char *icmp_err_str; - switch (rc) { - case -ICMP_NET_UNREACHABLE - 10: - icmp_err_str = "net unreachable"; - break; - case -ICMP_HOST_UNREACHABLE - 10: - icmp_err_str = "host unreachable"; - break; - case -ICMP_PROTOCOL_UNREACHABLE - 10: - icmp_err_str = "protocol unreachable"; - break; - case -ICMP_PORT_UNREACHABLE - 10: - icmp_err_str = "port unreachable"; - break; - case -ICMP_FRAGMENTATION_NEEDED - 10: - icmp_err_str = "fragmentation needed and DF set"; - break; - case -ICMP_SOURCE_ROUTE_FAILED - 10: - icmp_err_str = "source route failed"; - break; - default: - icmp_err_str = " UNKNOWN"; - break; - } - printf("ICMP ERROR \"%s\"\n", icmp_err_str); - } else if (rc == -40) { - printf("TFTP error occurred after %d bad packets received", - tftp_err.bad_tftp_packets); - } else if (rc == -41) { - printf("TFTP error occurred after missing %d responses", - tftp_err.no_packets); - } else if (rc == -42) { - printf("TFTP error missing block %d, expected block was %d", - tftp_err.blocks_missed, - tftp_err.blocks_received); + if (rc < 0) { + /* Make sure that error messages are put into a new line */ + printf("\n "); + } + + if (rc > 1024) { + printf(" TFTP: Received %s (%d KBytes)\n", fnip->filename, rc / 1024); + } else if (rc > 0) { + printf(" TFTP: Received %s (%d Bytes)\n", fnip->filename, rc); + } else { + const char *errstr = NULL; + int ecode; + tftp_get_error_info(fnip, &tftp_err, rc, &errstr, &ecode); + printf("TFTP error: %s\n", errstr ? errstr : "unknown error"); } return rc; } -static int net_load(char *buffer, int len) +static int net_init(filename_ip_t *fn_ip) { - filename_ip_t fn_ip; - uint8_t mac[6]; int rc; - memset(&fn_ip, 0, sizeof(filename_ip_t)); + memset(fn_ip, 0, sizeof(filename_ip_t)); rc = virtio_net_init(mac); if (rc < 0) { puts("Could not initialize network device"); return -101; } - fn_ip.fd = rc; + fn_ip->fd = rc; printf(" Using MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); @@ -220,30 +182,30 @@ static int net_load(char *buffer, int len) set_mac_address(mac); /* init ethernet layer */ seed_rng(mac); - rc = dhcp(&fn_ip, DEFAULT_BOOT_RETRIES); + rc = dhcp(fn_ip, DEFAULT_BOOT_RETRIES); if (rc >= 0) { - if (ip_version == 4) { - set_ipv4_address(fn_ip.own_ip); + if (fn_ip->ip_version == 4) { + set_ipv4_address(fn_ip->own_ip); } } else { puts("Could not get IP address"); return -101; } - if (ip_version == 4) { + if (fn_ip->ip_version == 4) { printf(" Using IPv4 address: %d.%d.%d.%d\n", - (fn_ip.own_ip >> 24) & 0xFF, (fn_ip.own_ip >> 16) & 0xFF, - (fn_ip.own_ip >> 8) & 0xFF, fn_ip.own_ip & 0xFF); - } else if (ip_version == 6) { + (fn_ip->own_ip >> 24) & 0xFF, (fn_ip->own_ip >> 16) & 0xFF, + (fn_ip->own_ip >> 8) & 0xFF, fn_ip->own_ip & 0xFF); + } else if (fn_ip->ip_version == 6) { char ip6_str[40]; - ipv6_to_str(fn_ip.own_ip6.addr, ip6_str); + ipv6_to_str(fn_ip->own_ip6.addr, ip6_str); printf(" Using IPv6 address: %s\n", ip6_str); } if (rc == -2) { printf("ARP request to TFTP server (%d.%d.%d.%d) failed\n", - (fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF, - (fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF); + (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, + (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); return -102; } if (rc == -4 || rc == -3) { @@ -251,28 +213,238 @@ static int net_load(char *buffer, int len) return -107; } - if (ip_version == 4) { - printf(" Requesting file \"%s\" via TFTP from %d.%d.%d.%d\n", - fn_ip.filename, - (fn_ip.server_ip >> 24) & 0xFF, (fn_ip.server_ip >> 16) & 0xFF, - (fn_ip.server_ip >> 8) & 0xFF, fn_ip.server_ip & 0xFF); - } else if (ip_version == 6) { + printf(" Using TFTP server: "); + if (fn_ip->ip_version == 4) { + printf("%d.%d.%d.%d\n", + (fn_ip->server_ip >> 24) & 0xFF, (fn_ip->server_ip >> 16) & 0xFF, + (fn_ip->server_ip >> 8) & 0xFF, fn_ip->server_ip & 0xFF); + } else if (fn_ip->ip_version == 6) { char ip6_str[40]; - printf(" Requesting file \"%s\" via TFTP from ", fn_ip.filename); - ipv6_to_str(fn_ip.server_ip6.addr, ip6_str); + ipv6_to_str(fn_ip->server_ip6.addr, ip6_str); printf("%s\n", ip6_str); } - /* Do the TFTP load and print error message if necessary */ - rc = tftp_load(&fn_ip, buffer, len, DEFAULT_TFTP_RETRIES, ip_version); - - if (ip_version == 4) { - dhcp_send_release(fn_ip.fd); + if (strlen(fn_ip->filename) > 0) { + printf(" Bootfile name: '%s'\n", fn_ip->filename); } return rc; } +static void net_release(filename_ip_t *fn_ip) +{ + if (fn_ip->ip_version == 4) { + dhcp_send_release(fn_ip->fd); + } +} + +/** + * Retrieve the Universally Unique Identifier of the VM. + * @return UUID string, or NULL in case of errors + */ +static const char *get_uuid(void) +{ + register int r0 asm("0"); + register int r1 asm("1"); + uint8_t *mem, *buf, uuid[16]; + int i, cc, chk = 0; + static char uuid_str[37]; + + mem = malloc(2 * PAGE_SIZE); + if (!mem) { + puts("Out of memory ... can not get UUID."); + return NULL; + } + buf = (uint8_t *)(((uint64_t)mem + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)); + memset(buf, 0, PAGE_SIZE); + + /* Get SYSIB 3.2.2 */ + r0 = (3 << 28) | 2; + r1 = 2; + asm volatile(" stsi 0(%[addr])\n" + " ipm %[cc]\n" + " srl %[cc],28\n" + : [cc] "=d" (cc) + : "d" (r0), "d" (r1), [addr] "a" (buf) + : "cc", "memory"); + if (cc) { + return NULL; + } + + for (i = 0; i < 16; i++) { + uuid[i] = buf[STSI322_VMDB_UUID_OFFSET + i]; + chk |= uuid[i]; + } + free(mem); + if (!chk) { + return NULL; + } + + sprintf(uuid_str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x", uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], + uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); + + return uuid_str; +} + +/** + * Load a kernel with initrd (i.e. with the information that we've got from + * a pxelinux.cfg config file) + */ +static int load_kernel_with_initrd(filename_ip_t *fn_ip, + struct pl_cfg_entry *entry) +{ + int rc; + + printf("Loading pxelinux.cfg entry '%s'\n", entry->label); + + if (!entry->kernel) { + printf("Kernel entry is missing!\n"); + return -1; + } + + strncpy(fn_ip->filename, entry->kernel, sizeof(fn_ip->filename)); + rc = tftp_load(fn_ip, KERNEL_ADDR, KERNEL_MAX_SIZE); + if (rc < 0) { + return rc; + } + + if (entry->initrd) { + uint64_t iaddr = (rc + 0xfff) & ~0xfffUL; + + strncpy(fn_ip->filename, entry->initrd, sizeof(fn_ip->filename)); + rc = tftp_load(fn_ip, (void *)iaddr, KERNEL_MAX_SIZE - iaddr); + if (rc < 0) { + return rc; + } + /* Patch location and size: */ + *(uint64_t *)0x10408 = iaddr; + *(uint64_t *)0x10410 = rc; + rc += iaddr; + } + + if (entry->append) { + strncpy((char *)0x10480, entry->append, ARCH_COMMAND_LINE_SIZE); + } + + return rc; +} + +#define MAX_PXELINUX_ENTRIES 16 + +static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) +{ + struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES]; + int num_ent, def_ent = 0; + + num_ent = pxelinux_load_parse_cfg(fn_ip, mac, get_uuid(), + DEFAULT_TFTP_RETRIES, + cfgbuf, sizeof(cfgbuf), + entries, MAX_PXELINUX_ENTRIES, &def_ent); + if (num_ent > 0) { + return load_kernel_with_initrd(fn_ip, &entries[def_ent]); + } + + return -1; +} + +/** + * Load via information from a .INS file (which can be found on CD-ROMs + * for example) + */ +static int handle_ins_cfg(filename_ip_t *fn_ip, char *cfg, int cfgsize) +{ + char *ptr; + int rc = -1, llen; + void *destaddr; + char *insbuf = cfg; + + ptr = strchr(insbuf, '\n'); + if (!ptr) { + puts("Does not seem to be a valid .INS file"); + return -1; + } + + *ptr = 0; + printf("\nParsing .INS file:\n %s\n", &insbuf[2]); + + insbuf = ptr + 1; + while (*insbuf && insbuf < cfg + cfgsize) { + ptr = strchr(insbuf, '\n'); + if (ptr) { + *ptr = 0; + } + llen = strlen(insbuf); + if (!llen) { + insbuf = ptr + 1; + continue; + } + ptr = strchr(insbuf, ' '); + if (!ptr) { + puts("Missing space separator in .INS file"); + return -1; + } + *ptr = 0; + strncpy(fn_ip->filename, insbuf, sizeof(fn_ip->filename)); + destaddr = (char *)atol(ptr + 1); + rc = tftp_load(fn_ip, destaddr, (long)_start - (long)destaddr); + if (rc <= 0) { + break; + } + insbuf += llen + 1; + } + + return rc; +} + +static int net_try_direct_tftp_load(filename_ip_t *fn_ip) +{ + int rc; + void *loadaddr = (void *)0x2000; /* Load right after the low-core */ + + rc = tftp_load(fn_ip, loadaddr, KERNEL_MAX_SIZE - (long)loadaddr); + if (rc < 0) { + return rc; + } else if (rc < 8) { + printf("'%s' is too small (%i bytes only).\n", fn_ip->filename, rc); + return -1; + } + + /* Check whether it is a configuration file instead of a kernel */ + if (rc < sizeof(cfgbuf) - 1) { + memcpy(cfgbuf, loadaddr, rc); + cfgbuf[rc] = 0; /* Make sure that it is NUL-terminated */ + if (!strncmp("* ", cfgbuf, 2)) { + return handle_ins_cfg(fn_ip, cfgbuf, rc); + } + /* + * pxelinux.cfg support via bootfile name is just here for developers' + * convenience (it eases testing with the built-in DHCP server of QEMU + * that does not support RFC 5071). The official way to configure a + * pxelinux.cfg file name is to use DHCP options 209 and 210 instead. + * So only use the pxelinux.cfg parser here for files that start with + * a magic comment string. + */ + if (!strncasecmp("# pxelinux", cfgbuf, 10)) { + struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES]; + int num_ent, def_ent = 0; + + num_ent = pxelinux_parse_cfg(cfgbuf, sizeof(cfgbuf), entries, + MAX_PXELINUX_ENTRIES, &def_ent); + if (num_ent <= 0) { + return -1; + } + return load_kernel_with_initrd(fn_ip, &entries[def_ent]); + } + } + + /* Move kernel to right location */ + memmove(KERNEL_ADDR, loadaddr, rc); + + return rc; +} + void panic(const char *string) { sclp_print(string); @@ -281,6 +453,15 @@ void panic(const char *string) } } +void write_subsystem_identification(void) +{ + SubChannelId *schid = (SubChannelId *) 184; + uint32_t *zeroes = (uint32_t *) 188; + + *schid = net_schid; + *zeroes = 0; +} + static bool find_net_dev(Schib *schib, int dev_no) { int i, r; @@ -344,17 +525,32 @@ static void virtio_setup(void) void main(void) { - int rc; + filename_ip_t fn_ip; + int rc, fnlen; sclp_setup(); sclp_print("Network boot starting...\n"); virtio_setup(); - rc = net_load(NULL, (long)_start); + rc = net_init(&fn_ip); + if (rc) { + panic("Network initialization failed. Halting.\n"); + } + + fnlen = strlen(fn_ip.filename); + if (fnlen > 0 && fn_ip.filename[fnlen - 1] != '/') { + rc = net_try_direct_tftp_load(&fn_ip); + } + if (rc <= 0) { + rc = net_try_pxelinux_cfg(&fn_ip); + } + + net_release(&fn_ip); + if (rc > 0) { sclp_print("Network loading done, starting kernel...\n"); - asm volatile (" lpsw 0(%0) " : : "r"(0) : "memory"); + jump_to_low_kernel(); } panic("Failed to load OS from network\n"); diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index fd18da22c6..9828aa233d 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -87,13 +87,19 @@ ulong get_second(void); /* bootmap.c */ void zipl_load(void); +/* jump2ipl.c */ +void jump_to_IPL_code(uint64_t address); +void jump_to_low_kernel(void); + /* menu.c */ void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout); int menu_get_zipl_boot_index(const char *menu_data); bool menu_is_enabled_zipl(void); -int menu_get_enum_boot_index(int entries); +int menu_get_enum_boot_index(bool *valid_entries); bool menu_is_enabled_enum(void); +#define MAX_BOOT_ENTRIES 31 + static inline void fill_hex(char *out, unsigned char val) { const char hex[] = "0123456789abcdef"; diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c index 3836cb4716..c0223fab0b 100644 --- a/pc-bios/s390-ccw/sclp.c +++ b/pc-bios/s390-ccw/sclp.c @@ -114,7 +114,7 @@ void sclp_get_loadparm_ascii(char *loadparm) memset((char *)_sccb, 0, sizeof(ReadInfo)); sccb->h.length = sizeof(ReadInfo); if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) { - ebcdic_to_ascii((char *) sccb->loadparm, loadparm, 8); + ebcdic_to_ascii((char *) sccb->loadparm, loadparm, LOADPARM_LEN); } } diff --git a/pc-bios/s390-ccw/sclp.h b/pc-bios/s390-ccw/sclp.h index 0dd987ff5d..8450161ba7 100644 --- a/pc-bios/s390-ccw/sclp.h +++ b/pc-bios/s390-ccw/sclp.h @@ -56,7 +56,7 @@ typedef struct ReadInfo { uint16_t rnmax; uint8_t rnsize; uint8_t reserved[13]; - uint8_t loadparm[8]; + uint8_t loadparm[LOADPARM_LEN]; } __attribute__((packed)) ReadInfo; typedef struct SCCB { diff --git a/pc-bios/s390-netboot.img b/pc-bios/s390-netboot.img index 31f3d141cd..2c6886efb8 100644 Binary files a/pc-bios/s390-netboot.img and b/pc-bios/s390-netboot.img differ diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index d46c83efb7..4e0e33f829 100644 Binary files a/pc-bios/slof.bin and b/pc-bios/slof.bin differ diff --git a/pc-bios/u-boot-sam460-20100605.bin b/pc-bios/u-boot-sam460-20100605.bin index 99408f8e95..e17de77c19 100755 Binary files a/pc-bios/u-boot-sam460-20100605.bin and b/pc-bios/u-boot-sam460-20100605.bin differ diff --git a/pc-bios/vgabios-bochs-display.bin b/pc-bios/vgabios-bochs-display.bin new file mode 100644 index 0000000000..6021d9b199 Binary files /dev/null and b/pc-bios/vgabios-bochs-display.bin differ diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin index cec498d59e..c1ec6b6298 100644 Binary files a/pc-bios/vgabios-cirrus.bin and b/pc-bios/vgabios-cirrus.bin differ diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin index 82c9970df7..2529ac954d 100644 Binary files a/pc-bios/vgabios-qxl.bin and b/pc-bios/vgabios-qxl.bin differ diff --git a/pc-bios/vgabios-ramfb.bin b/pc-bios/vgabios-ramfb.bin new file mode 100644 index 0000000000..30a124538f Binary files /dev/null and b/pc-bios/vgabios-ramfb.bin differ diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin index 8029c2ae12..2d868321c7 100644 Binary files a/pc-bios/vgabios-stdvga.bin and b/pc-bios/vgabios-stdvga.bin differ diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin index 79333575e0..8188eabb18 100644 Binary files a/pc-bios/vgabios-virtio.bin and b/pc-bios/vgabios-virtio.bin differ diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin index ba1718c784..58afa79d2f 100644 Binary files a/pc-bios/vgabios-vmware.bin and b/pc-bios/vgabios-vmware.bin differ diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin index b624014bb9..136c94520c 100644 Binary files a/pc-bios/vgabios.bin and b/pc-bios/vgabios.bin differ diff --git a/po/Makefile b/po/Makefile index cc630363de..e47e262ee6 100644 --- a/po/Makefile +++ b/po/Makefile @@ -43,7 +43,7 @@ install: $(OBJS) $(PO_PATH)/messages.po: $(SRC_PATH)/ui/gtk.c $(call quiet-command, ( cd $(SRC_PATH) && \ - xgettext -o - --from-code=UTF-8 --foreign-user \ + xgettext -o - --from-code=UTF-8 --foreign-user --no-location \ --package-name=QEMU --package-version=$(VERSION) \ --msgid-bugs-address=qemu-devel@nongnu.org -k_ -C ui/gtk.c | \ sed -e s/CHARSET/UTF-8/) >$@,"GEN","$@") diff --git a/po/bg.po b/po/bg.po index 279d1b864a..3d8c353372 100644 --- a/po/bg.po +++ b/po/bg.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 2.6.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2016-06-09 15:54+0300\n" "Last-Translator: Alexander Shopov \n" "Language-Team: Bulgarian \n" @@ -17,74 +17,59 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " — натиснете Ctrl+Alt+G, за да освободите фокуса" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [пауза]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Пауза" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Рестартиране" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Изключване" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "_Спиране на програмата" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "На _цял екран" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "_Копиране" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "_Увеличаване" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "_Намаляване" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "По_местване" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Напас_ване" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Прихващане при посо_чване" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "Прихващане на _фокуса" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Подпро_зорци" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "Към самостоятелен подпрозорец" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Машина" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Изглед" diff --git a/po/de_DE.po b/po/de_DE.po index de27fcf174..6f2c3cdc2f 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2012-02-28 16:00+0100\n" "Last-Translator: Kevin Wolf \n" "Language-Team: Deutsch \n" @@ -16,74 +16,59 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n!=1);\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Strg+Alt+G drücken, um Eingabegeräte freizugeben" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [Angehalten]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Angehalten" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Reset" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Herunterfahren" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "_Beenden" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "_Vollbild" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "_Kopieren" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "_Heranzoomen" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "_Wegzoomen" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "_Einpassen" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Auf _Fenstergröße skalieren" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Tastatur _automatisch einfangen" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "_Eingabegeräte einfangen" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Reiter anzeigen" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "Reiter abtrennen" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Maschine" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Ansicht" diff --git a/po/fr_FR.po b/po/fr_FR.po index 94f4a94f5c..25ad4c954a 100644 --- a/po/fr_FR.po +++ b/po/fr_FR.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2014-07-28 23:25+0200\n" "Last-Translator: Aurelien Jarno \n" "Language-Team: French \n" @@ -17,74 +17,59 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 1.4\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr "- Appuyer sur Ctrl+Alt+G pour arrêter la capture" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [En pause]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Pause" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Réinitialiser" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Éteindre" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "_Quitter" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "Mode _plein écran" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "_Copier" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "Zoom _avant" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "_Zoom arrière" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "Zoom _idéal" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Zoomer pour a_juster" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Capturer en _survolant" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "_Capturer les entrées" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Montrer les _onglets" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "_Détacher l'onglet" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Machine" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Vue" diff --git a/po/hu.po b/po/hu.po index 86f78e92b9..a82d9ec230 100644 --- a/po/hu.po +++ b/po/hu.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2013-05-06 20:42+0200\n" "Last-Translator: Ákos Kovács \n" "Language-Team: Hungarian \n" @@ -15,77 +15,62 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Nyomj Ctrl+Alt+G-t a bemeneti eszközök elengedéséhez" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [Megállítva]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Megállítás" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "Új_raindítás" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Leállítás" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "" -#: ui/gtk.c:2048 #, fuzzy msgid "Zoom _In" msgstr "Ablakmérethez _igazítás" -#: ui/gtk.c:2055 #, fuzzy msgid "Zoom _Out" msgstr "Ablakmérethez _igazítás" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Ablakmérethez _igazítás" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Automatikus _elfogás" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "_Bemeneti eszközök megragadása" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "_Fülek megjelenítése" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Gép" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Nézet" diff --git a/po/it.po b/po/it.po index bfae84e797..c6d9517207 100644 --- a/po/it.po +++ b/po/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2014-07-29 08:25+0200\n" "Last-Translator: Paolo Bonzini \n" "Language-Team: Italian \n" @@ -16,74 +16,59 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Premere Ctrl+Alt+G per rilasciare l'input" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [Pausa]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Pausa" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Reset" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Spegni" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "_Esci" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "A t_utto schermo" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "_Copia" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "_Aumenta zoom" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "_Riduci zoom" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "A_nnulla zoom" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Adatta alla _finestra" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Cattura _automatica input" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "_Cattura input" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Mostra _tab" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "_Sposta in una nuova finestra" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Macchina virtuale" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Visualizza" diff --git a/po/messages.po b/po/messages.po index e0a98c16a9..065bd459a0 100644 --- a/po/messages.po +++ b/po/messages.po @@ -5,9 +5,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: QEMU 2.7.93\n" +"Project-Id-Version: QEMU 2.12.91\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,74 +16,59 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr "" -#: ui/gtk.c:279 msgid " [Paused]" msgstr "" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "" -#: ui/gtk.c:2127 msgid "_View" msgstr "" diff --git a/po/tr.po b/po/tr.po index af34b52d52..632c7f3851 100644 --- a/po/tr.po +++ b/po/tr.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 1.4.50\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2013-04-22 18:35+0300\n" "Last-Translator: Ozan Çağlayan \n" "Language-Team: Türkçe <>\n" @@ -17,76 +17,61 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Gtranslator 2.91.6\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - Yakalamayı durdurmak için Ctrl+Alt+G tuşlarına basın" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [Duraklatıldı]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "_Duraklat" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "_Sıfırla" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "_Kapat" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "" -#: ui/gtk.c:2048 #, fuzzy msgid "Zoom _In" msgstr "Yakınlaş ve Sığ_dır" -#: ui/gtk.c:2055 #, fuzzy msgid "Zoom _Out" msgstr "Yakınlaş ve Sığ_dır" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "Yakınlaş ve Sığ_dır" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "Ü_zerindeyken Yakala" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "Girdiyi _Yakala" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "Se_kmeleri Göster" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "_Makine" -#: ui/gtk.c:2127 msgid "_View" msgstr "_Görüntüle" diff --git a/po/zh_CN.po b/po/zh_CN.po index d20b6c6981..b25e8e3c02 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: QEMU 2.2\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" -"POT-Creation-Date: 2016-12-13 21:46+0000\n" +"POT-Creation-Date: 2018-07-18 07:56+0200\n" "PO-Revision-Date: 2014-07-31 10:00+0800\n" "Last-Translator: Fam Zheng \n" "Language-Team: Chinese \n" @@ -17,74 +17,59 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Lokalize 1.4\n" -#: ui/gtk.c:275 msgid " - Press Ctrl+Alt+G to release grab" msgstr " - 按下 Ctrl+Alt+G 取消捕获" -#: ui/gtk.c:279 msgid " [Paused]" msgstr " [已暂停]" -#: ui/gtk.c:1922 msgid "_Pause" msgstr "暂停(_P)" -#: ui/gtk.c:1928 msgid "_Reset" msgstr "重置(_R)" -#: ui/gtk.c:1931 msgid "Power _Down" msgstr "关闭电源(_D)" -#: ui/gtk.c:1937 msgid "_Quit" msgstr "退出(_Q)" -#: ui/gtk.c:2029 msgid "_Fullscreen" msgstr "全屏(_F)" -#: ui/gtk.c:2032 msgid "_Copy" msgstr "复制(_C)" -#: ui/gtk.c:2048 msgid "Zoom _In" msgstr "放大(_I)" -#: ui/gtk.c:2055 msgid "Zoom _Out" msgstr "缩小(_O)" -#: ui/gtk.c:2062 msgid "Best _Fit" msgstr "最合适大小(_F)" -#: ui/gtk.c:2069 msgid "Zoom To _Fit" msgstr "缩放以适应大小(_F)" -#: ui/gtk.c:2075 msgid "Grab On _Hover" msgstr "鼠标经过时捕获(_H)" -#: ui/gtk.c:2078 msgid "_Grab Input" msgstr "捕获输入(_G)" -#: ui/gtk.c:2107 msgid "Show _Tabs" msgstr "显示标签页(_T)" -#: ui/gtk.c:2110 msgid "Detach Tab" msgstr "分离标签页" -#: ui/gtk.c:2122 +msgid "Show Menubar" +msgstr "" + msgid "_Machine" msgstr "虚拟机(_M)" -#: ui/gtk.c:2127 msgid "_View" msgstr "视图(_V)" diff --git a/qapi/block-core.json b/qapi/block-core.json index c50517bff3..5b9084a394 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -6,6 +6,7 @@ { 'include': 'common.json' } { 'include': 'crypto.json' } +{ 'include': 'job.json' } { 'include': 'sockets.json' } ## @@ -51,8 +52,7 @@ { 'union': 'ImageInfoSpecificQCow2Encryption', 'base': 'ImageInfoSpecificQCow2EncryptionBase', 'discriminator': 'format', - 'data': { 'aes': 'QCryptoBlockInfoQCow', - 'luks': 'QCryptoBlockInfoLUKS' } } + 'data': { 'luks': 'QCryptoBlockInfoLUKS' } } ## # @ImageInfoSpecificQCow2: @@ -866,6 +866,9 @@ # # @node-name: The node name of the device. (Since 2.3) # +# @qdev: The qdev ID, or if no ID is assigned, the QOM path of the block +# device. (since 3.0) +# # @stats: A @BlockDeviceStats for the device. # # @parent: This describes the file block device if it has one. @@ -879,7 +882,7 @@ # Since: 0.14.0 ## { 'struct': 'BlockStats', - 'data': {'*device': 'str', '*node-name': 'str', + 'data': {'*device': 'str', '*qdev': 'str', '*node-name': 'str', 'stats': 'BlockDeviceStats', '*parent': 'BlockStats', '*backing': 'BlockStats'} } @@ -941,7 +944,8 @@ # "idle_time_ns":2953431879, # "account_invalid":true, # "account_failed":false -# } +# }, +# "qdev": "/machine/unattached/device[23]" # }, # { # "device":"ide1-cd0", @@ -959,7 +963,8 @@ # "wr_merged":0, # "account_invalid":false, # "account_failed":false -# } +# }, +# "qdev": "/machine/unattached/device[24]" # }, # { # "device":"floppy0", @@ -977,7 +982,8 @@ # "wr_merged":0, # "account_invalid":false, # "account_failed":false -# } +# }, +# "qdev": "/machine/unattached/device[16]" # }, # { # "device":"sd0", @@ -1050,93 +1056,22 @@ 'data': ['top', 'full', 'none', 'incremental'] } ## -# @BlockJobType: +# @MirrorCopyMode: # -# Type of a block job. +# An enumeration whose values tell the mirror block job when to +# trigger writes to the target. # -# @commit: block commit job type, see "block-commit" +# @background: copy data in background only. # -# @stream: block stream job type, see "block-stream" +# @write-blocking: when data is written to the source, write it +# (synchronously) to the target as well. In +# addition, data is copied in background just like in +# @background mode. # -# @mirror: drive mirror job type, see "drive-mirror" -# -# @backup: drive backup job type, see "drive-backup" -# -# Since: 1.7 +# Since: 3.0 ## -{ 'enum': 'BlockJobType', - 'data': ['commit', 'stream', 'mirror', 'backup'] } - -## -# @BlockJobVerb: -# -# Represents command verbs that can be applied to a blockjob. -# -# @cancel: see @block-job-cancel -# -# @pause: see @block-job-pause -# -# @resume: see @block-job-resume -# -# @set-speed: see @block-job-set-speed -# -# @complete: see @block-job-complete -# -# @dismiss: see @block-job-dismiss -# -# @finalize: see @block-job-finalize -# -# Since: 2.12 -## -{ 'enum': 'BlockJobVerb', - 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', - 'finalize' ] } - -## -# @BlockJobStatus: -# -# Indicates the present state of a given blockjob in its lifetime. -# -# @undefined: Erroneous, default state. Should not ever be visible. -# -# @created: The job has been created, but not yet started. -# -# @running: The job is currently running. -# -# @paused: The job is running, but paused. The pause may be requested by -# either the QMP user or by internal processes. -# -# @ready: The job is running, but is ready for the user to signal completion. -# This is used for long-running jobs like mirror that are designed to -# run indefinitely. -# -# @standby: The job is ready, but paused. This is nearly identical to @paused. -# The job may return to @ready or otherwise be canceled. -# -# @waiting: The job is waiting for other jobs in the transaction to converge -# to the waiting state. This status will likely not be visible for -# the last job in a transaction. -# -# @pending: The job has finished its work, but has finalization steps that it -# needs to make prior to completing. These changes may require -# manual intervention by the management process if manual was set -# to true. These changes may still fail. -# -# @aborting: The job is in the process of being aborted, and will finish with -# an error. The job will afterwards report that it is @concluded. -# This status may not be visible to the management process. -# -# @concluded: The job has finished all work. If manual was set to true, the job -# will remain in the query list until it is dismissed. -# -# @null: The job is in the process of being dismantled. This state should not -# ever be visible externally. -# -# Since: 2.12 -## -{ 'enum': 'BlockJobStatus', - 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', - 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } +{ 'enum': 'MirrorCopyMode', + 'data': ['background', 'write-blocking'] } ## # @BlockJobInfo: @@ -1148,7 +1083,12 @@ # @device: The job identifier. Originally the device name but other # values are allowed since QEMU 2.7 # -# @len: the maximum progress value +# @len: Estimated @offset value at the completion of the job. This value can +# arbitrarily change while the job is running, in both directions. +# +# @offset: Progress made until now. The unit is arbitrary and the value can +# only meaningfully be used for the ratio of @offset to @len. The +# value is monotonically increasing. # # @busy: false if the job is known to be in a quiescent state, with # no pending I/O. Since 1.3. @@ -1156,8 +1096,6 @@ # @paused: whether the job is paused or, if @busy is true, will # pause itself as soon as possible. Since 1.3. # -# @offset: the current progress value -# # @speed: the rate limit, bytes per second # # @io-status: the status of the job (since 1.3) @@ -1172,14 +1110,18 @@ # @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the NULL # state and disappearing from the query list. (since 2.12) # +# @error: Error information if the job did not complete successfully. +# Not set if the job completed successfully. (since 2.12.1) +# # Since: 1.1 ## { 'struct': 'BlockJobInfo', 'data': {'type': 'str', 'device': 'str', 'len': 'int', 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool', - 'status': 'BlockJobStatus', - 'auto-finalize': 'bool', 'auto-dismiss': 'bool' } } + 'status': 'JobStatus', + 'auto-finalize': 'bool', 'auto-dismiss': 'bool', + '*error': 'str' } } ## # @query-block-jobs: @@ -1773,6 +1715,9 @@ # written. Both will result in identical contents. # Default is true. (Since 2.4) # +# @copy-mode: when to copy data to the destination; defaults to 'background' +# (Since: 3.0) +# # Since: 1.3 ## { 'struct': 'DriveMirror', @@ -1782,7 +1727,7 @@ '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', - '*unmap': 'bool' } } + '*unmap': 'bool', '*copy-mode': 'MirrorCopyMode' } } ## # @BlockDirtyBitmap: @@ -1815,11 +1760,29 @@ # Currently, all dirty tracking bitmaps are loaded from Qcow2 on # open. # +# @x-disabled: the bitmap is created in the disabled state, which means that +# it will not track drive changes. The bitmap may be enabled with +# x-block-dirty-bitmap-enable. Default is false. (Since: 3.0) +# # Since: 2.4 ## { 'struct': 'BlockDirtyBitmapAdd', 'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32', - '*persistent': 'bool', '*autoload': 'bool' } } + '*persistent': 'bool', '*autoload': 'bool', '*x-disabled': 'bool' } } + +## +# @BlockDirtyBitmapMerge: +# +# @node: name of device/node which the bitmap is tracking +# +# @dst_name: name of the destination dirty bitmap +# +# @src_name: name of the source dirty bitmap +# +# Since: 3.0 +## +{ 'struct': 'BlockDirtyBitmapMerge', + 'data': { 'node': 'str', 'dst_name': 'str', 'src_name': 'str' } } ## # @block-dirty-bitmap-add: @@ -1889,6 +1852,72 @@ { 'command': 'block-dirty-bitmap-clear', 'data': 'BlockDirtyBitmap' } +## +# @x-block-dirty-bitmap-enable: +# +# Enables a dirty bitmap so that it will begin tracking disk changes. +# +# Returns: nothing on success +# If @node is not a valid block device, DeviceNotFound +# If @name is not found, GenericError with an explanation +# +# Since: 3.0 +# +# Example: +# +# -> { "execute": "x-block-dirty-bitmap-enable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } +# +## + { 'command': 'x-block-dirty-bitmap-enable', + 'data': 'BlockDirtyBitmap' } + +## +# @x-block-dirty-bitmap-disable: +# +# Disables a dirty bitmap so that it will stop tracking disk changes. +# +# Returns: nothing on success +# If @node is not a valid block device, DeviceNotFound +# If @name is not found, GenericError with an explanation +# +# Since: 3.0 +# +# Example: +# +# -> { "execute": "x-block-dirty-bitmap-disable", +# "arguments": { "node": "drive0", "name": "bitmap0" } } +# <- { "return": {} } +# +## + { 'command': 'x-block-dirty-bitmap-disable', + 'data': 'BlockDirtyBitmap' } + +## +# @x-block-dirty-bitmap-merge: +# +# Merge @src_name dirty bitmap to @dst_name dirty bitmap. @src_name dirty +# bitmap is unchanged. On error, @dst_name is unchanged. +# +# Returns: nothing on success +# If @node is not a valid block device, DeviceNotFound +# If @dst_name or @src_name is not found, GenericError +# If bitmaps has different sizes or granularities, GenericError +# +# Since: 3.0 +# +# Example: +# +# -> { "execute": "x-block-dirty-bitmap-merge", +# "arguments": { "node": "drive0", "dst_name": "bitmap0", +# "src_name": "bitmap1" } } +# <- { "return": {} } +# +## + { 'command': 'x-block-dirty-bitmap-merge', + 'data': 'BlockDirtyBitmapMerge' } + ## # @BlockDirtyBitmapSha256: # @@ -1961,6 +1990,9 @@ # above @device. If this option is not given, a node name is # autogenerated. (Since: 2.9) # +# @copy-mode: when to copy data to the destination; defaults to 'background' +# (Since: 3.0) +# # Returns: nothing on success. # # Since: 2.6 @@ -1981,7 +2013,8 @@ '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', - '*filter-node-name': 'str' } } + '*filter-node-name': 'str', + '*copy-mode': 'MirrorCopyMode' } } ## # @block_set_io_throttle: @@ -2334,8 +2367,7 @@ # # This command returns immediately after marking the active background block # operation for pausing. It is an error to call this command if no -# operation is in progress. Pausing an already paused job has no cumulative -# effect; a single block-job-resume command will resume the job. +# operation is in progress or if the job is already paused. # # The operation will pause as soon as possible. No event is emitted when # the operation is actually paused. Cancelling a paused job automatically @@ -2359,7 +2391,7 @@ # # This command returns immediately after resuming a paused background block # operation. It is an error to call this command if no operation is in -# progress. Resuming an already running job is not an error. +# progress or if the job is not paused. # # This command also clears the error status of the job. # @@ -2410,7 +2442,7 @@ # QEMU 2.12+ job lifetime management semantics. # # This command will refuse to operate on any job that has not yet reached -# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of +# its terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of the # BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need # to be used as appropriate. # @@ -2506,16 +2538,18 @@ # @vxhs: Since 2.10 # @throttle: Since 2.11 # @nvme: Since 2.12 +# @copy-on-read: Since 3.0 +# @blklogwrites: Since 3.0 # # Since: 2.9 ## { 'enum': 'BlockdevDriver', - 'data': [ 'blkdebug', 'blkverify', 'bochs', 'cloop', - 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom', - 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', - 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', 'qcow2', 'qed', - 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh', - 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } + 'data': [ 'blkdebug', 'blklogwrites', 'blkverify', 'bochs', 'cloop', + 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster', + 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks', + 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', + 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', + 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } ## # @BlockdevOptionsFile: @@ -2530,6 +2564,10 @@ # @locking: whether to enable file locking. If set to 'auto', only enable # when Open File Descriptor (OFD) locking API is available # (default: auto, since 2.10) +# @x-check-cache-dropped: whether to check that page cache was dropped on live +# migration. May cause noticeable delays if the image +# file is large, do not use in production. +# (default: off) (since: 3.0) # # Since: 2.9 ## @@ -2537,7 +2575,8 @@ 'data': { 'filename': 'str', '*pr-manager': 'str', '*locking': 'OnOffAuto', - '*aio': 'BlockdevAioOptions' } } + '*aio': 'BlockdevAioOptions', + '*x-check-cache-dropped': 'bool' } } ## # @BlockdevOptionsNull: @@ -2663,18 +2702,21 @@ # @template: Specifies a template mode which can be adjusted using the other # flags, defaults to 'cached' # +# @bitmap-directory: since 3.0 +# # Since: 2.9 ## { 'struct': 'Qcow2OverlapCheckFlags', - 'data': { '*template': 'Qcow2OverlapCheckMode', - '*main-header': 'bool', - '*active-l1': 'bool', - '*active-l2': 'bool', - '*refcount-table': 'bool', - '*refcount-block': 'bool', - '*snapshot-table': 'bool', - '*inactive-l1': 'bool', - '*inactive-l2': 'bool' } } + 'data': { '*template': 'Qcow2OverlapCheckMode', + '*main-header': 'bool', + '*active-l1': 'bool', + '*active-l2': 'bool', + '*refcount-table': 'bool', + '*refcount-block': 'bool', + '*snapshot-table': 'bool', + '*inactive-l1': 'bool', + '*inactive-l2': 'bool', + '*bitmap-directory': 'bool' } } ## # @Qcow2OverlapChecks: @@ -2844,16 +2886,6 @@ 'data': { 'type': 'SshHostKeyCheckHashType', 'hash': 'str' }} -## -# @SshHostKeyDummy: -# -# For those union branches that don't need additional fields. -# -# Since: 2.12 -## -{ 'struct': 'SshHostKeyDummy', - 'data': {} } - ## # @SshHostKeyCheck: # @@ -2862,9 +2894,7 @@ { 'union': 'SshHostKeyCheck', 'base': { 'mode': 'SshHostKeyCheckMode' }, 'discriminator': 'mode', - 'data': { 'none': 'SshHostKeyDummy', - 'hash': 'SshHostKeyHash', - 'known_hosts': 'SshHostKeyDummy' } } + 'data': { 'hash': 'SshHostKeyHash' } } ## # @BlockdevOptionsSsh: @@ -3024,6 +3054,32 @@ '*inject-error': ['BlkdebugInjectErrorOptions'], '*set-state': ['BlkdebugSetStateOptions'] } } +## +# @BlockdevOptionsBlklogwrites: +# +# Driver specific block device options for blklogwrites. +# +# @file: block device +# +# @log: block device used to log writes to @file +# +# @log-sector-size: sector size used in logging writes to @file, determines +# granularity of offsets and sizes of writes (default: 512) +# +# @log-append: append to an existing log (default: false) +# +# @log-super-update-interval: interval of write requests after which the log +# super block is updated to disk (default: 4096) +# +# Since: 3.0 +## +{ 'struct': 'BlockdevOptionsBlklogwrites', + 'data': { 'file': 'BlockdevRef', + 'log': 'BlockdevRef', + '*log-sector-size': 'uint32', + '*log-append': 'bool', + '*log-super-update-interval': 'uint64' } } + ## # @BlockdevOptionsBlkverify: # @@ -3169,6 +3225,14 @@ '*timeout': 'int' } } +## +# @RbdAuthMode: +# +# Since: 3.0 +## +{ 'enum': 'RbdAuthMode', + 'data': [ 'cephx', 'none' ] } + ## # @BlockdevOptionsRbd: # @@ -3184,6 +3248,15 @@ # # @user: Ceph id name. # +# @auth-client-required: Acceptable authentication modes. +# This maps to Ceph configuration option +# "auth_client_required". (Since 3.0) +# +# @key-secret: ID of a QCryptoSecret object providing a key +# for cephx authentication. +# This maps to Ceph configuration option +# "key". (Since 3.0) +# # @server: Monitor host address and port. This maps # to the "mon_host" Ceph option. # @@ -3195,6 +3268,8 @@ '*conf': 'str', '*snapshot': 'str', '*user': 'str', + '*auth-client-required': ['RbdAuthMode'], + '*key-secret': 'str', '*server': ['InetSocketAddressBase'] } } ## @@ -3432,12 +3507,17 @@ # # @tls-creds: TLS credentials ID # +# @x-dirty-bitmap: A "qemu:dirty-bitmap:NAME" string to query in place of +# traditional "base:allocation" block status (see +# NBD_OPT_LIST_META_CONTEXT in the NBD protocol) (since 3.0) +# # Since: 2.9 ## { 'struct': 'BlockdevOptionsNbd', 'data': { 'server': 'SocketAddress', '*export': 'str', - '*tls-creds': 'str' } } + '*tls-creds': 'str', + '*x-dirty-bitmap': 'str' } } ## # @BlockdevOptionsRaw: @@ -3519,9 +3599,11 @@ 'discriminator': 'driver', 'data': { 'blkdebug': 'BlockdevOptionsBlkdebug', + 'blklogwrites':'BlockdevOptionsBlklogwrites', 'blkverify': 'BlockdevOptionsBlkverify', 'bochs': 'BlockdevOptionsGenericFormat', 'cloop': 'BlockdevOptionsGenericFormat', + 'copy-on-read':'BlockdevOptionsGenericFormat', 'dmg': 'BlockdevOptionsGenericFormat', 'file': 'BlockdevOptionsFile', 'ftp': 'BlockdevOptionsCurlFtp', @@ -4022,15 +4104,6 @@ '*subformat': 'BlockdevVpcSubformat', '*force-size': 'bool' } } -## -# @BlockdevCreateNotSupported: -# -# This is used for all drivers that don't support creating images. -# -# Since: 2.12 -## -{ 'struct': 'BlockdevCreateNotSupported', 'data': {}} - ## # @BlockdevCreateOptions: # @@ -4045,56 +4118,37 @@ 'driver': 'BlockdevDriver' }, 'discriminator': 'driver', 'data': { - 'blkdebug': 'BlockdevCreateNotSupported', - 'blkverify': 'BlockdevCreateNotSupported', - 'bochs': 'BlockdevCreateNotSupported', - 'cloop': 'BlockdevCreateNotSupported', - 'dmg': 'BlockdevCreateNotSupported', 'file': 'BlockdevCreateOptionsFile', - 'ftp': 'BlockdevCreateNotSupported', - 'ftps': 'BlockdevCreateNotSupported', 'gluster': 'BlockdevCreateOptionsGluster', - 'host_cdrom': 'BlockdevCreateNotSupported', - 'host_device': 'BlockdevCreateNotSupported', - 'http': 'BlockdevCreateNotSupported', - 'https': 'BlockdevCreateNotSupported', - 'iscsi': 'BlockdevCreateNotSupported', 'luks': 'BlockdevCreateOptionsLUKS', - 'nbd': 'BlockdevCreateNotSupported', 'nfs': 'BlockdevCreateOptionsNfs', - 'null-aio': 'BlockdevCreateNotSupported', - 'null-co': 'BlockdevCreateNotSupported', - 'nvme': 'BlockdevCreateNotSupported', 'parallels': 'BlockdevCreateOptionsParallels', 'qcow': 'BlockdevCreateOptionsQcow', 'qcow2': 'BlockdevCreateOptionsQcow2', 'qed': 'BlockdevCreateOptionsQed', - 'quorum': 'BlockdevCreateNotSupported', - 'raw': 'BlockdevCreateNotSupported', 'rbd': 'BlockdevCreateOptionsRbd', - 'replication': 'BlockdevCreateNotSupported', 'sheepdog': 'BlockdevCreateOptionsSheepdog', 'ssh': 'BlockdevCreateOptionsSsh', - 'throttle': 'BlockdevCreateNotSupported', 'vdi': 'BlockdevCreateOptionsVdi', 'vhdx': 'BlockdevCreateOptionsVhdx', - 'vmdk': 'BlockdevCreateNotSupported', - 'vpc': 'BlockdevCreateOptionsVpc', - 'vvfat': 'BlockdevCreateNotSupported', - 'vxhs': 'BlockdevCreateNotSupported' + 'vpc': 'BlockdevCreateOptionsVpc' } } ## -# @x-blockdev-create: +# @blockdev-create: # -# Create an image format on a given node. -# TODO Replace with something asynchronous (block job?) +# Starts a job to create an image format on a given node. The job is +# automatically finalized, but a manual job-dismiss is required. # -# Since: 2.12 +# @job-id: Identifier for the newly created job. +# +# @options: Options for the image creation. +# +# Since: 3.0 ## -{ 'command': 'x-blockdev-create', - 'data': 'BlockdevCreateOptions', - 'boxed': true } +{ 'command': 'blockdev-create', + 'data': { 'job-id': 'str', + 'options': 'BlockdevCreateOptions' } } ## # @blockdev-open-tray: @@ -4485,7 +4539,7 @@ # ## { 'event': 'BLOCK_JOB_COMPLETED', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'device': 'str', 'len' : 'int', 'offset': 'int', @@ -4521,7 +4575,7 @@ # ## { 'event': 'BLOCK_JOB_CANCELLED', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'device': 'str', 'len' : 'int', 'offset': 'int', @@ -4586,7 +4640,7 @@ # ## { 'event': 'BLOCK_JOB_READY', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'device': 'str', 'len' : 'int', 'offset': 'int', @@ -4613,7 +4667,7 @@ # ## { 'event': 'BLOCK_JOB_PENDING', - 'data': { 'type' : 'BlockJobType', + 'data': { 'type' : 'JobType', 'id' : 'str' } } ## diff --git a/qapi/block.json b/qapi/block.json index c694524002..11f01f28ef 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -77,6 +77,34 @@ { 'struct': 'BlockdevSnapshotInternal', 'data': { 'device': 'str', 'name': 'str' } } +## +# @PRManagerInfo: +# +# Information about a persistent reservation manager +# +# @id: the identifier of the persistent reservation manager +# +# @connected: true if the persistent reservation manager is connected to +# the underlying storage or helper +# +# Since: 3.0 +## +{ 'struct': 'PRManagerInfo', + 'data': {'id': 'str', 'connected': 'bool'} } + +## +# @query-pr-managers: +# +# Returns a list of information about each persistent reservation manager. +# +# Returns: a list of @PRManagerInfo for each persistent reservation manager +# +# Since: 3.0 +## +{ 'command': 'query-pr-managers', 'returns': ['PRManagerInfo'], + 'allow-preconfig': true } + + ## # @blockdev-snapshot-internal-sync: # @@ -268,6 +296,29 @@ { 'command': 'nbd-server-remove', 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} } +## +# @x-nbd-server-add-bitmap: +# +# Expose a dirty bitmap associated with the selected export. The bitmap search +# starts at the device attached to the export, and includes all backing files. +# The exported bitmap is then locked until the NBD export is removed. +# +# @name: Export name. +# +# @bitmap: Bitmap name to search for. +# +# @bitmap-export-name: How the bitmap will be seen by nbd clients +# (default @bitmap) +# +# Note: the client must use NBD_OPT_SET_META_CONTEXT with a query of +# "qemu:dirty-bitmap:NAME" (where NAME matches @bitmap-export-name) to access +# the exposed bitmap. +# +# Since: 3.0 +## + { 'command': 'x-nbd-server-add-bitmap', + 'data': {'name': 'str', 'bitmap': 'str', '*bitmap-export-name': 'str'} } + ## # @nbd-server-stop: # @@ -307,6 +358,30 @@ { 'event': 'DEVICE_TRAY_MOVED', 'data': { 'device': 'str', 'id': 'str', 'tray-open': 'bool' } } +## +# @PR_MANAGER_STATUS_CHANGED: +# +# Emitted whenever the connected status of a persistent reservation +# manager changes. +# +# @id: The id of the PR manager object +# +# @connected: true if the PR manager is connected to a backend +# +# Since: 3.0 +# +# Example: +# +# <- { "event": "PR_MANAGER_STATUS_CHANGED", +# "data": { "id": "pr-helper0", +# "connected": true +# }, +# "timestamp": { "seconds": 1519840375, "microseconds": 450486 } } +# +## +{ 'event': 'PR_MANAGER_STATUS_CHANGED', + 'data': { 'id': 'str', 'connected': 'bool' } } + ## # @QuorumOpType: # diff --git a/qapi/char.json b/qapi/char.json index ae19dcd1ed..b7b2a05766 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -62,7 +62,8 @@ # } # ## -{ 'command': 'query-chardev', 'returns': ['ChardevInfo'] } +{ 'command': 'query-chardev', 'returns': ['ChardevInfo'], + 'allow-preconfig': true } ## # @ChardevBackendInfo: @@ -319,6 +320,7 @@ ## { 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' }, 'base': 'ChardevCommon' } +# TODO: 'if': 'defined(CONFIG_SPICE)' ## # @ChardevSpicePort: @@ -331,6 +333,7 @@ ## { 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' }, 'base': 'ChardevCommon' } +# TODO: 'if': 'defined(CONFIG_SPICE)' ## # @ChardevVC: @@ -384,8 +387,10 @@ 'testdev': 'ChardevCommon', 'stdio' : 'ChardevStdio', 'console': 'ChardevCommon', - 'spicevmc' : 'ChardevSpiceChannel', - 'spiceport' : 'ChardevSpicePort', + 'spicevmc': 'ChardevSpiceChannel', +# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' }, + 'spiceport': 'ChardevSpicePort', +# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' }, 'vc' : 'ChardevVC', 'ringbuf': 'ChardevRingbuf', # next one is just for compatibility diff --git a/qapi/common.json b/qapi/common.json index d9b14dd429..c367adc4b6 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -126,3 +126,26 @@ ## { 'enum': 'OffAutoPCIBAR', 'data': [ 'off', 'auto', 'bar0', 'bar1', 'bar2', 'bar3', 'bar4', 'bar5' ] } + +## +# @SysEmuTarget: +# +# The comprehensive enumeration of QEMU system emulation ("softmmu") +# targets. Run "./configure --help" in the project root directory, and +# look for the *-softmmu targets near the "--target-list" option. The +# individual target constants are not documented here, for the time +# being. +# +# Notes: The resulting QMP strings can be appended to the "qemu-system-" +# prefix to produce the corresponding QEMU executable name. This +# is true even for "qemu-system-x86_64". +# +# Since: 3.0 +## +{ 'enum' : 'SysEmuTarget', + 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', + 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', + 'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc', + 'ppc64', 'ppcemb', 'riscv32', 'riscv64', 's390x', 'sh4', + 'sh4eb', 'sparc', 'sparc64', 'tricore', 'unicore32', + 'x86_64', 'xtensa', 'xtensaeb' ] } diff --git a/qapi/crypto.json b/qapi/crypto.json index 288bc056ef..a51b434412 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -297,16 +297,6 @@ 'uuid': 'str', 'slots': [ 'QCryptoBlockInfoLUKSSlot' ] }} -## -# @QCryptoBlockInfoQCow: -# -# Information about the QCow block encryption options -# -# Since: 2.7 -## -{ 'struct': 'QCryptoBlockInfoQCow', - 'data': { }} - ## # @QCryptoBlockInfo: @@ -318,5 +308,4 @@ { 'union': 'QCryptoBlockInfo', 'base': 'QCryptoBlockInfoBase', 'discriminator': 'format', - 'data': { 'qcow': 'QCryptoBlockInfoQCow', - 'luks': 'QCryptoBlockInfoLUKS' } } + 'data': { 'luks': 'QCryptoBlockInfoLUKS' } } diff --git a/qapi/introspect.json b/qapi/introspect.json index c7f67b7d78..137b39b992 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -259,8 +259,8 @@ # # @ret-type: the name of the command's result type. # -# @allow-oob: whether the command allows out-of-band execution. -# (Since: 2.12) +# @allow-oob: whether the command allows out-of-band execution, +# defaults to false (Since: 2.12) # # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # @@ -268,7 +268,7 @@ ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - 'allow-oob': 'bool' } } + '*allow-oob': 'bool' } } ## # @SchemaInfoEvent: diff --git a/qapi/job.json b/qapi/job.json new file mode 100644 index 0000000000..a121b615fb --- /dev/null +++ b/qapi/job.json @@ -0,0 +1,256 @@ +# -*- Mode: Python -*- + +## +# == Background jobs +## + +## +# @JobType: +# +# Type of a background job. +# +# @commit: block commit job type, see "block-commit" +# +# @stream: block stream job type, see "block-stream" +# +# @mirror: drive mirror job type, see "drive-mirror" +# +# @backup: drive backup job type, see "drive-backup" +# +# @create: image creation job type, see "blockdev-create" (since 3.0) +# +# Since: 1.7 +## +{ 'enum': 'JobType', + 'data': ['commit', 'stream', 'mirror', 'backup', 'create'] } + +## +# @JobStatus: +# +# Indicates the present state of a given job in its lifetime. +# +# @undefined: Erroneous, default state. Should not ever be visible. +# +# @created: The job has been created, but not yet started. +# +# @running: The job is currently running. +# +# @paused: The job is running, but paused. The pause may be requested by +# either the QMP user or by internal processes. +# +# @ready: The job is running, but is ready for the user to signal completion. +# This is used for long-running jobs like mirror that are designed to +# run indefinitely. +# +# @standby: The job is ready, but paused. This is nearly identical to @paused. +# The job may return to @ready or otherwise be canceled. +# +# @waiting: The job is waiting for other jobs in the transaction to converge +# to the waiting state. This status will likely not be visible for +# the last job in a transaction. +# +# @pending: The job has finished its work, but has finalization steps that it +# needs to make prior to completing. These changes will require +# manual intervention via @job-finalize if auto-finalize was set to +# false. These pending changes may still fail. +# +# @aborting: The job is in the process of being aborted, and will finish with +# an error. The job will afterwards report that it is @concluded. +# This status may not be visible to the management process. +# +# @concluded: The job has finished all work. If auto-dismiss was set to false, +# the job will remain in the query list until it is dismissed via +# @job-dismiss. +# +# @null: The job is in the process of being dismantled. This state should not +# ever be visible externally. +# +# Since: 2.12 +## +{ 'enum': 'JobStatus', + 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby', + 'waiting', 'pending', 'aborting', 'concluded', 'null' ] } + +## +# @JobVerb: +# +# Represents command verbs that can be applied to a job. +# +# @cancel: see @job-cancel +# +# @pause: see @job-pause +# +# @resume: see @job-resume +# +# @set-speed: see @block-job-set-speed +# +# @complete: see @job-complete +# +# @dismiss: see @job-dismiss +# +# @finalize: see @job-finalize +# +# Since: 2.12 +## +{ 'enum': 'JobVerb', + 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss', + 'finalize' ] } + +## +# @JOB_STATUS_CHANGE: +# +# Emitted when a job transitions to a different status. +# +# @id: The job identifier +# @status: The new job status +# +# Since: 3.0 +## +{ 'event': 'JOB_STATUS_CHANGE', + 'data': { 'id': 'str', + 'status': 'JobStatus' } } + +## +# @job-pause: +# +# Pause an active job. +# +# This command returns immediately after marking the active job for pausing. +# Pausing an already paused job is an error. +# +# The job will pause as soon as possible, which means transitioning into the +# PAUSED state if it was RUNNING, or into STANDBY if it was READY. The +# corresponding JOB_STATUS_CHANGE event will be emitted. +# +# Cancelling a paused job automatically resumes it. +# +# @id: The job identifier. +# +# Since: 3.0 +## +{ 'command': 'job-pause', 'data': { 'id': 'str' } } + +## +# @job-resume: +# +# Resume a paused job. +# +# This command returns immediately after resuming a paused job. Resuming an +# already running job is an error. +# +# @id : The job identifier. +# +# Since: 3.0 +## +{ 'command': 'job-resume', 'data': { 'id': 'str' } } + +## +# @job-cancel: +# +# Instruct an active background job to cancel at the next opportunity. +# This command returns immediately after marking the active job for +# cancellation. +# +# The job will cancel as soon as possible and then emit a JOB_STATUS_CHANGE +# event. Usually, the status will change to ABORTING, but it is possible that +# a job successfully completes (e.g. because it was almost done and there was +# no opportunity to cancel earlier than completing the job) and transitions to +# PENDING instead. +# +# @id: The job identifier. +# +# Since: 3.0 +## +{ 'command': 'job-cancel', 'data': { 'id': 'str' } } + + +## +# @job-complete: +# +# Manually trigger completion of an active job in the READY state. +# +# @id: The job identifier. +# +# Since: 3.0 +## +{ 'command': 'job-complete', 'data': { 'id': 'str' } } + +## +# @job-dismiss: +# +# Deletes a job that is in the CONCLUDED state. This command only needs to be +# run explicitly for jobs that don't have automatic dismiss enabled. +# +# This command will refuse to operate on any job that has not yet reached its +# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY +# event, job-cancel or job-complete will still need to be used as appropriate. +# +# @id: The job identifier. +# +# Since: 3.0 +## +{ 'command': 'job-dismiss', 'data': { 'id': 'str' } } + +## +# @job-finalize: +# +# Instructs all jobs in a transaction (or a single job if it is not part of any +# transaction) to finalize any graph changes and do any necessary cleanup. This +# command requires that all involved jobs are in the PENDING state. +# +# For jobs in a transaction, instructing one job to finalize will force +# ALL jobs in the transaction to finalize, so it is only necessary to instruct +# a single member job to finalize. +# +# @id: The identifier of any job in the transaction, or of a job that is not +# part of any transaction. +# +# Since: 3.0 +## +{ 'command': 'job-finalize', 'data': { 'id': 'str' } } + +## +# @JobInfo: +# +# Information about a job. +# +# @id: The job identifier +# +# @type: The kind of job that is being performed +# +# @status: Current job state/status +# +# @current-progress: Progress made until now. The unit is arbitrary and the +# value can only meaningfully be used for the ratio of +# @current-progress to @total-progress. The value is +# monotonically increasing. +# +# @total-progress: Estimated @current-progress value at the completion of +# the job. This value can arbitrarily change while the +# job is running, in both directions. +# +# @error: If this field is present, the job failed; if it is +# still missing in the CONCLUDED state, this indicates +# successful completion. +# +# The value is a human-readable error message to describe +# the reason for the job failure. It should not be parsed +# by applications. +# +# Since: 3.0 +## +{ 'struct': 'JobInfo', + 'data': { 'id': 'str', 'type': 'JobType', 'status': 'JobStatus', + 'current-progress': 'int', 'total-progress': 'int', + '*error': 'str' } } + +## +# @query-jobs: +# +# Return information about jobs. +# +# Returns: a list with a @JobInfo for each active job +# +# Since: 3.0 +## +{ 'command': 'query-jobs', 'returns': ['JobInfo'] } diff --git a/qapi/migration.json b/qapi/migration.json index 9d0bf82cf4..186e8a7303 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -39,6 +39,8 @@ # @page-size: The number of bytes per page for the various page-based # statistics (since 2.10) # +# @multifd-bytes: The number of bytes sent through multifd (since 3.0) +# # Since: 0.14.0 ## { 'struct': 'MigrationStats', @@ -46,7 +48,8 @@ 'duplicate': 'int', 'skipped': 'int', 'normal': 'int', 'normal-bytes': 'int', 'dirty-pages-rate' : 'int', 'mbps' : 'number', 'dirty-sync-count' : 'int', - 'postcopy-requests' : 'int', 'page-size' : 'int' } } + 'postcopy-requests' : 'int', 'page-size' : 'int', + 'multifd-bytes' : 'uint64' } } ## # @XBZRLECacheStats: @@ -89,6 +92,10 @@ # # @postcopy-active: like active, but now in postcopy mode. (since 2.5) # +# @postcopy-paused: during postcopy but paused. (since 3.0) +# +# @postcopy-recover: trying to recover from a paused postcopy. (since 3.0) +# # @completed: migration is finished. # # @failed: some error occurred during migration process. @@ -106,7 +113,8 @@ ## { 'enum': 'MigrationStatus', 'data': [ 'none', 'setup', 'cancelling', 'cancelled', - 'active', 'postcopy-active', 'completed', 'failed', 'colo', + 'active', 'postcopy-active', 'postcopy-paused', + 'postcopy-recover', 'completed', 'failed', 'colo', 'pre-switchover', 'device' ] } ## @@ -156,6 +164,15 @@ # @status is 'failed'. Clients should not attempt to parse the # error strings. (Since 2.7) # +# @postcopy-blocktime: total time when all vCPU were blocked during postcopy +# live migration. This is only present when the postcopy-blocktime +# migration capability is enabled. (Since 3.0) +# +# @postcopy-vcpu-blocktime: list of the postcopy blocktime per vCPU. This is +# only present when the postcopy-blocktime migration capability +# is enabled. (Since 3.0) +# +# # Since: 0.14.0 ## { 'struct': 'MigrationInfo', @@ -167,7 +184,9 @@ '*downtime': 'int', '*setup-time': 'int', '*cpu-throttle-percentage': 'int', - '*error-desc': 'str'} } + '*error-desc': 'str', + '*postcopy-blocktime' : 'uint32', + '*postcopy-vcpu-blocktime': ['uint32']} } ## # @query-migrate: @@ -357,13 +376,20 @@ # @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps. # (since 2.12) # +# @postcopy-blocktime: Calculate downtime for postcopy live migration +# (since 3.0) +# +# @late-block-activate: If enabled, the destination will not activate block +# devices (and thus take locks) immediately at the end of migration. +# (since 3.0) +# # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', 'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram', 'block', 'return-path', 'pause-before-switchover', 'x-multifd', - 'dirty-bitmaps' ] } + 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate' ] } ## # @MigrationCapabilityStatus: @@ -494,6 +520,9 @@ # and a power of 2 # (Since 2.11) # +# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. +# Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # Since: 2.4 ## { 'enum': 'MigrationParameter', @@ -502,7 +531,7 @@ 'tls-creds', 'tls-hostname', 'max-bandwidth', 'downtime-limit', 'x-checkpoint-delay', 'block-incremental', 'x-multifd-channels', 'x-multifd-page-count', - 'xbzrle-cache-size' ] } + 'xbzrle-cache-size', 'max-postcopy-bandwidth' ] } ## # @MigrateSetParameters: @@ -570,6 +599,10 @@ # needs to be a multiple of the target page size # and a power of 2 # (Since 2.11) +# +# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. +# Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # Since: 2.4 ## # TODO either fuse back into MigrationParameters, or make @@ -588,7 +621,8 @@ '*block-incremental': 'bool', '*x-multifd-channels': 'int', '*x-multifd-page-count': 'int', - '*xbzrle-cache-size': 'size' } } + '*xbzrle-cache-size': 'size', + '*max-postcopy-bandwidth': 'size' } } ## # @migrate-set-parameters: @@ -671,6 +705,10 @@ # needs to be a multiple of the target page size # and a power of 2 # (Since 2.11) +# +# @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. +# Defaults to 0 (unlimited). In bytes per second. +# (Since 3.0) # Since: 2.4 ## { 'struct': 'MigrationParameters', @@ -687,7 +725,8 @@ '*block-incremental': 'bool' , '*x-multifd-channels': 'uint8', '*x-multifd-page-count': 'uint32', - '*xbzrle-cache-size': 'size' } } + '*xbzrle-cache-size': 'size', + '*max-postcopy-bandwidth': 'size' } } ## # @query-migrate-parameters: @@ -1015,6 +1054,8 @@ # @detach: this argument exists only for compatibility reasons and # is ignored by QEMU # +# @resume: resume one paused migration, default "off". (since 3.0) +# # Returns: nothing on success # # Since: 0.14.0 @@ -1036,7 +1077,8 @@ # ## { 'command': 'migrate', - 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', '*detach': 'bool' } } + 'data': {'uri': 'str', '*blk': 'bool', '*inc': 'bool', + '*detach': 'bool', '*resume': 'bool' } } ## # @migrate-incoming: @@ -1170,3 +1212,39 @@ # Since: 2.9 ## { 'command': 'xen-colo-do-checkpoint' } + +## +# @migrate-recover: +# +# Provide a recovery migration stream URI. +# +# @uri: the URI to be used for the recovery of migration stream. +# +# Returns: nothing. +# +# Example: +# +# -> { "execute": "migrate-recover", +# "arguments": { "uri": "tcp:192.168.1.200:12345" } } +# <- { "return": {} } +# +# Since: 3.0 +## +{ 'command': 'migrate-recover', 'data': { 'uri': 'str' }, + 'allow-oob': true } + +## +# @migrate-pause: +# +# Pause a migration. Currently it only supports postcopy. +# +# Returns: nothing. +# +# Example: +# +# -> { "execute": "migrate-pause" } +# <- { "return": {} } +# +# Since: 3.0 +## +{ 'command': 'migrate-pause', 'allow-oob': true } diff --git a/qapi/misc.json b/qapi/misc.json index 5636f4a149..d450cfef21 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -5,6 +5,8 @@ # = Miscellanea ## +{ 'include': 'common.json' } + ## # @qmp_capabilities: # @@ -35,7 +37,8 @@ # ## { 'command': 'qmp_capabilities', - 'data': { '*enable': [ 'QMPCapability' ] } } + 'data': { '*enable': [ 'QMPCapability' ] }, + 'allow-preconfig': true } ## # @QMPCapability: @@ -43,7 +46,7 @@ # Enumeration of capabilities to be advertised during initial client # connection, used for agreeing on particular QMP extension behaviors. # -# @oob: QMP ability to support Out-Of-Band requests. +# @oob: QMP ability to support out-of-band requests. # (Please refer to qmp-spec.txt for more information on OOB) # # Since: 2.12 @@ -114,7 +117,8 @@ # } # ## -{ 'command': 'query-version', 'returns': 'VersionInfo' } +{ 'command': 'query-version', 'returns': 'VersionInfo', + 'allow-preconfig': true } ## # @CommandInfo: @@ -153,7 +157,8 @@ # Note: This example has been shortened as the real response is too long. # ## -{ 'command': 'query-commands', 'returns': ['CommandInfo'] } +{ 'command': 'query-commands', 'returns': ['CommandInfo'], + 'allow-preconfig': true } ## # @LostTickPolicy: @@ -237,7 +242,7 @@ # <- { "return": { "name": "qemu-name" } } # ## -{ 'command': 'query-name', 'returns': 'NameInfo' } +{ 'command': 'query-name', 'returns': 'NameInfo', 'allow-preconfig': true } ## # @KvmInfo: @@ -297,7 +302,7 @@ # <- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } # ## -{ 'command': 'query-uuid', 'returns': 'UuidInfo' } +{ 'command': 'query-uuid', 'returns': 'UuidInfo', 'allow-preconfig': true } ## # @EventInfo: @@ -391,8 +396,7 @@ 'mips': 'CpuInfoMIPS', 'tricore': 'CpuInfoTricore', 's390': 'CpuInfoS390', - 'riscv': 'CpuInfoRISCV', - 'other': 'CpuInfoOther' } } + 'riscv': 'CpuInfoRISCV' } } ## # @CpuInfoX86: @@ -462,16 +466,6 @@ ## { 'struct': 'CpuInfoRISCV', 'data': { 'pc': 'int' } } -## -# @CpuInfoOther: -# -# No additional information is available about the virtual CPU -# -# Since: 2.6 -# -## -{ 'struct': 'CpuInfoOther', 'data': { } } - ## # @CpuS390State: # @@ -556,25 +550,24 @@ # @props: properties describing to which node/socket/core/thread # virtual CPU belongs to, provided if supported by board # -# @arch: architecture of the cpu, which determines which additional fields -# will be listed +# @arch: base architecture of the cpu; deprecated since 3.0.0 in favor +# of @target +# +# @target: the QEMU system emulation target, which determines which +# additional fields will be listed (since 3.0) # # Since: 2.12 # ## -{ 'union': 'CpuInfoFast', - 'base': {'cpu-index': 'int', 'qom-path': 'str', - 'thread-id': 'int', '*props': 'CpuInstanceProperties', - 'arch': 'CpuInfoArch' }, - 'discriminator': 'arch', - 'data': { 'x86': 'CpuInfoOther', - 'sparc': 'CpuInfoOther', - 'ppc': 'CpuInfoOther', - 'mips': 'CpuInfoOther', - 'tricore': 'CpuInfoOther', - 's390': 'CpuInfoS390', - 'riscv': 'CpuInfoRISCV', - 'other': 'CpuInfoOther' } } +{ 'union' : 'CpuInfoFast', + 'base' : { 'cpu-index' : 'int', + 'qom-path' : 'str', + 'thread-id' : 'int', + '*props' : 'CpuInstanceProperties', + 'arch' : 'CpuInfoArch', + 'target' : 'SysEmuTarget' }, + 'discriminator' : 'target', + 'data' : { 's390x' : 'CpuInfoS390' } } ## # @query-cpus-fast: @@ -600,6 +593,7 @@ # }, # "qom-path": "/machine/unattached/device[0]", # "arch":"x86", +# "target":"x86_64", # "cpu-index": 0 # }, # { @@ -611,6 +605,7 @@ # }, # "qom-path": "/machine/unattached/device[2]", # "arch":"x86", +# "target":"x86_64", # "cpu-index": 1 # } # ] @@ -674,7 +669,8 @@ # } # ## -{ 'command': 'query-iothreads', 'returns': ['IOThreadInfo'] } +{ 'command': 'query-iothreads', 'returns': ['IOThreadInfo'], + 'allow-preconfig': true } ## # @BalloonInfo: @@ -1208,6 +1204,29 @@ ## { 'command': 'cont' } +## +# @x-exit-preconfig: +# +# Exit from "preconfig" state +# +# This command makes QEMU exit the preconfig state and proceed with +# VM initialization using configuration data provided on the command line +# and via the QMP monitor during the preconfig state. The command is only +# available during the preconfig state (i.e. when the --preconfig command +# line option was in use). +# +# Since 3.0 +# +# Returns: nothing +# +# Example: +# +# -> { "execute": "x-exit-preconfig" } +# <- { "return": {} } +# +## +{ 'command': 'x-exit-preconfig', 'allow-preconfig': true } + ## # @system_wakeup: # @@ -1349,7 +1368,8 @@ ## { 'command': 'qom-list', 'data': { 'path': 'str' }, - 'returns': [ 'ObjectPropertyInfo' ] } + 'returns': [ 'ObjectPropertyInfo' ], + 'allow-preconfig': true } ## # @qom-get: @@ -1385,7 +1405,8 @@ ## { 'command': 'qom-get', 'data': { 'path': 'str', 'property': 'str' }, - 'returns': 'any' } + 'returns': 'any', + 'allow-preconfig': true } ## # @qom-set: @@ -1402,7 +1423,8 @@ # Since: 1.2 ## { 'command': 'qom-set', - 'data': { 'path': 'str', 'property': 'str', 'value': 'any' } } + 'data': { 'path': 'str', 'property': 'str', 'value': 'any' }, + 'allow-preconfig': true } ## # @change: @@ -1484,7 +1506,8 @@ ## { 'command': 'qom-list-types', 'data': { '*implements': 'str', '*abstract': 'bool' }, - 'returns': [ 'ObjectTypeInfo' ] } + 'returns': [ 'ObjectTypeInfo' ], + 'allow-preconfig': true } ## # @device-list-properties: @@ -1495,6 +1518,10 @@ # # Returns: a list of ObjectPropertyInfo describing a devices properties # +# Note: objects can create properties at runtime, for example to describe +# links between different devices and/or objects. These properties +# are not included in the output of this command. +# # Since: 1.2 ## { 'command': 'device-list-properties', @@ -1508,13 +1535,18 @@ # # @typename: the type name of an object # +# Note: objects can create properties at runtime, for example to describe +# links between different devices and/or objects. These properties +# are not included in the output of this command. +# # Returns: a list of ObjectPropertyInfo describing object properties # # Since: 2.12 ## { 'command': 'qom-list-properties', 'data': { 'typename': 'str'}, - 'returns': [ 'ObjectPropertyInfo' ] } + 'returns': [ 'ObjectPropertyInfo' ], + 'allow-preconfig': true } ## # @xen-set-global-dirty-log: @@ -1645,10 +1677,13 @@ # # @kdump-snappy: kdump-compressed format with snappy-compressed # +# @win-dmp: Windows full crashdump format, +# can be used instead of ELF converting (since 2.13) +# # Since: 2.0 ## { 'enum': 'DumpGuestMemoryFormat', - 'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy' ] } + 'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', 'win-dmp' ] } ## # @dump-guest-memory: @@ -2449,12 +2484,12 @@ # # Information describing the QEMU target. # -# @arch: the target architecture (eg "x86_64", "i386", etc) +# @arch: the target architecture # # Since: 1.2.0 ## { 'struct': 'TargetInfo', - 'data': { 'arch': 'str' } } + 'data': { 'arch': 'SysEmuTarget' } } ## # @query-target: @@ -2614,7 +2649,8 @@ # ## {'command': 'query-command-line-options', 'data': { '*option': 'str' }, - 'returns': ['CommandLineOptionInfo'] } + 'returns': ['CommandLineOptionInfo'], + 'allow-preconfig': true } ## # @X86CPURegister32: @@ -2834,7 +2870,7 @@ # } # ## -{ 'command': 'query-memdev', 'returns': ['Memdev'] } +{ 'command': 'query-memdev', 'returns': ['Memdev'], 'allow-preconfig': true } ## # @PCDIMMDeviceInfo: @@ -3225,7 +3261,8 @@ # ]} # ## -{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'] } +{ 'command': 'query-hotpluggable-cpus', 'returns': ['HotpluggableCPU'], + 'allow-preconfig': true } ## # @GuidInfo: @@ -3417,6 +3454,9 @@ # only be dropped when the oob capability is enabled. # # @id: The dropped command's "id" field. +# FIXME Broken by design. Events are broadcast to all monitors. If +# another monitor's client has a command with the same ID in flight, +# the event will incorrectly claim that command was dropped. # # @reason: The reason why the command is dropped. # @@ -3433,19 +3473,15 @@ 'data': { 'id': 'any', 'reason': 'CommandDropReason' } } ## -# @x-oob-test: +# @set-numa-node: # -# Test OOB functionality. When sending this command with lock=true, -# it'll try to hang the dispatcher. When sending it with lock=false, -# it'll try to notify the locked thread to continue. Note: it should -# only be used by QMP test program rather than anything else. +# Runtime equivalent of '-numa' CLI option, available at +# preconfigure stage to configure numa mapping before initializing +# machine. # -# Since: 2.12 -# -# Example: -# -# { "execute": "x-oob-test", -# "arguments": { "lock": true } } +# Since 3.0 ## -{ 'command': 'x-oob-test', 'data' : { 'lock': 'bool' }, - 'allow-oob': true } +{ 'command': 'set-numa-node', 'boxed': true, + 'data': 'NumaOptions', + 'allow-preconfig': true +} diff --git a/qapi/net.json b/qapi/net.json index 9117c56972..c86f351161 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -88,16 +88,6 @@ ## { 'command': 'netdev_del', 'data': {'id': 'str'} } -## -# @NetdevNoneOptions: -# -# Use it alone to have zero network devices. -# -# Since: 1.2 -## -{ 'struct': 'NetdevNoneOptions', - 'data': { } } - ## # @NetLegacyNicOptions: # @@ -160,6 +150,9 @@ # @dnssearch: list of DNS suffixes to search, passed as DHCP option # to the guest # +# @domainname: guest-visible domain name of the virtual nameserver +# (since 3.0) +# # @ipv6-prefix: IPv6 network prefix (default is fec0::) (since # 2.6). The network prefix is given in the usual # hexadecimal IPv6 address notation. @@ -197,6 +190,7 @@ '*dhcpstart': 'str', '*dns': 'str', '*dnssearch': ['String'], + '*domainname': 'str', '*ipv6-prefix': 'str', '*ipv6-prefixlen': 'int', '*ipv6-host': 'str', @@ -209,7 +203,7 @@ ## # @NetdevTapOptions: # -# Connect the host TAP network interface name to the VLAN. +# Used to configure a host TAP network interface backend. # # @ifname: interface name # @@ -267,8 +261,8 @@ ## # @NetdevSocketOptions: # -# Connect the VLAN to a remote VLAN in another QEMU virtual machine using a TCP -# socket connection. +# Socket netdevs are used to establish a network connection to another +# QEMU virtual machine via a TCP socket. # # @fd: file descriptor of an already opened socket # @@ -296,7 +290,7 @@ ## # @NetdevL2TPv3Options: # -# Connect the VLAN to Ethernet over L2TPv3 Static tunnel +# Configure an Ethernet over L2TPv3 tunnel. # # @src: source address # @@ -352,7 +346,7 @@ ## # @NetdevVdeOptions: # -# Connect the VLAN to a vde switch running on the host. +# Connect to a vde switch running on the host. # # @sock: socket path # @@ -450,7 +444,7 @@ # # Since: 2.7 # -# 'dump' - removed with 2.12 +# 'dump': dropped in 2.12 ## { 'enum': 'NetClientDriver', 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'vde', @@ -473,7 +467,6 @@ 'base': { 'id': 'str', 'type': 'NetClientDriver' }, 'discriminator': 'type', 'data': { - 'none': 'NetdevNoneOptions', 'nic': 'NetLegacyNicOptions', 'user': 'NetdevUserOptions', 'tap': 'NetdevTapOptions', @@ -490,8 +483,6 @@ # # Captures the configuration of a network device; legacy. # -# @vlan: vlan number -# # @id: identifier for monitor commands # # @name: identifier for monitor commands, ignored if @id is present @@ -499,10 +490,11 @@ # @opts: device type specific properties (legacy) # # Since: 1.2 +# +# 'vlan': dropped in 3.0 ## { 'struct': 'NetLegacy', 'data': { - '*vlan': 'int32', '*id': 'str', '*name': 'str', 'opts': 'NetLegacyOptions' } } @@ -527,7 +519,6 @@ 'base': { 'type': 'NetLegacyOptionsType' }, 'discriminator': 'type', 'data': { - 'none': 'NetdevNoneOptions', 'nic': 'NetLegacyNicOptions', 'user': 'NetdevUserOptions', 'tap': 'NetdevTapOptions', diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c index fd23803166..6b24afd367 100644 --- a/qapi/qapi-dealloc-visitor.c +++ b/qapi/qapi-dealloc-visitor.c @@ -99,7 +99,7 @@ static void qapi_dealloc_type_anything(Visitor *v, const char *name, QObject **obj, Error **errp) { if (obj) { - qobject_decref(*obj); + qobject_unref(*obj); } } @@ -107,7 +107,7 @@ static void qapi_dealloc_type_null(Visitor *v, const char *name, QNull **obj, Error **errp) { if (obj) { - QDECREF(*obj); + qobject_unref(*obj); } } diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 25bce78352..65b6dc2f6f 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -84,6 +84,7 @@ { 'include': 'crypto.json' } { 'include': 'block.json' } { 'include': 'char.json' } +{ 'include': 'job.json' } { 'include': 'net.json' } { 'include': 'rocker.json' } { 'include': 'tpm.json' } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index dd05907265..6f2d466596 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -18,14 +18,16 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qbool.h" +#include "sysemu/sysemu.h" -QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) +static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, + Error **errp) { + const char *exec_key = NULL; const QDictEntry *ent; const char *arg_name; const QObject *arg_obj; - bool has_exec_key = false; - QDict *dict = NULL; + QDict *dict; dict = qobject_to(QDict, request); if (!dict) { @@ -38,27 +40,25 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) arg_name = qdict_entry_key(ent); arg_obj = qdict_entry_value(ent); - if (!strcmp(arg_name, "execute")) { + if (!strcmp(arg_name, "execute") + || (!strcmp(arg_name, "exec-oob") && allow_oob)) { if (qobject_type(arg_obj) != QTYPE_QSTRING) { - error_setg(errp, - "QMP input member 'execute' must be a string"); + error_setg(errp, "QMP input member '%s' must be a string", + arg_name); return NULL; } - has_exec_key = true; + if (exec_key) { + error_setg(errp, "QMP input member '%s' clashes with '%s'", + arg_name, exec_key); + return NULL; + } + exec_key = arg_name; } else if (!strcmp(arg_name, "arguments")) { if (qobject_type(arg_obj) != QTYPE_QDICT) { error_setg(errp, "QMP input member 'arguments' must be an object"); return NULL; } - } else if (!strcmp(arg_name, "id")) { - continue; - } else if (!strcmp(arg_name, "control")) { - if (qobject_type(arg_obj) != QTYPE_QDICT) { - error_setg(errp, - "QMP input member 'control' must be a dict"); - return NULL; - } } else { error_setg(errp, "QMP input member '%s' is unexpected", arg_name); @@ -66,7 +66,7 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) } } - if (!has_exec_key) { + if (!exec_key) { error_setg(errp, "QMP input lacks member 'execute'"); return NULL; } @@ -75,20 +75,27 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) } static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, - Error **errp) + bool allow_oob, Error **errp) { Error *local_err = NULL; + bool oob; const char *command; QDict *args, *dict; QmpCommand *cmd; QObject *ret = NULL; - dict = qmp_dispatch_check_obj(request, errp); + dict = qmp_dispatch_check_obj(request, allow_oob, errp); if (!dict) { return NULL; } - command = qdict_get_str(dict, "execute"); + command = qdict_get_try_str(dict, "execute"); + oob = false; + if (!command) { + assert(allow_oob); + command = qdict_get_str(dict, "exec-oob"); + oob = true; + } cmd = qmp_find_command(cmds, command); if (cmd == NULL) { error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, @@ -100,12 +107,24 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, command); return NULL; } + if (oob && !(cmd->options & QCO_ALLOW_OOB)) { + error_setg(errp, "The command %s does not support OOB", + command); + return false; + } + + if (runstate_check(RUN_STATE_PRECONFIG) && + !(cmd->options & QCO_ALLOW_PRECONFIG)) { + error_setg(errp, "The command '%s' isn't permitted in '%s' state", + cmd->name, RunState_str(RUN_STATE_PRECONFIG)); + return NULL; + } if (!qdict_haskey(dict, "arguments")) { args = qdict_new(); } else { args = qdict_get_qdict(dict, "arguments"); - QINCREF(args); + qobject_ref(args); } cmd->fn(args, &ret, &local_err); @@ -114,61 +133,53 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, } else if (cmd->options & QCO_NO_SUCCESS_RESP) { g_assert(!ret); } else if (!ret) { + /* TODO turn into assertion */ ret = QOBJECT(qdict_new()); } - QDECREF(args); + qobject_unref(args); return ret; } -QObject *qmp_build_error_object(Error *err) +QDict *qmp_error_response(Error *err) { - return qobject_from_jsonf("{ 'class': %s, 'desc': %s }", - QapiErrorClass_str(error_get_class(err)), - error_get_pretty(err)); + QDict *rsp; + + rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }", + QapiErrorClass_str(error_get_class(err)), + error_get_pretty(err)); + error_free(err); + return rsp; } /* - * Detect whether a request should be run out-of-band, by quickly - * peeking at whether we have: { "control": { "run-oob": true } }. By - * default commands are run in-band. + * Does @qdict look like a command to be run out-of-band? */ bool qmp_is_oob(QDict *dict) { - QBool *bool_obj; - - dict = qdict_get_qdict(dict, "control"); - if (!dict) { - return false; - } - - bool_obj = qobject_to(QBool, qdict_get(dict, "run-oob")); - if (!bool_obj) { - return false; - } - - return qbool_get_bool(bool_obj); + return qdict_haskey(dict, "exec-oob") + && !qdict_haskey(dict, "execute"); } -QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request) +QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, + bool allow_oob) { Error *err = NULL; QObject *ret; QDict *rsp; - ret = do_qmp_dispatch(cmds, request, &err); + ret = do_qmp_dispatch(cmds, request, allow_oob, &err); - rsp = qdict_new(); if (err) { - qdict_put_obj(rsp, "error", qmp_build_error_object(err)); - error_free(err); + rsp = qmp_error_response(err); } else if (ret) { + rsp = qdict_new(); qdict_put_obj(rsp, "return", ret); } else { - QDECREF(rsp); - return NULL; + /* Can only happen for commands with QCO_NO_SUCCESS_RESP */ + rsp = NULL; } - return QOBJECT(rsp); + return rsp; } diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c index 9d7e88e84a..5b8854043e 100644 --- a/qapi/qmp-event.c +++ b/qapi/qmp-event.c @@ -34,15 +34,15 @@ QMPEventFuncEmit qmp_event_get_func_emit(void) static void timestamp_put(QDict *qdict) { int err; - QObject *obj; + QDict *ts; qemu_timeval tv; err = qemu_gettimeofday(&tv); /* Put -1 to indicate failure of getting host time */ - obj = qobject_from_jsonf("{ 'seconds': %lld, 'microseconds': %lld }", - err < 0 ? -1LL : (long long)tv.tv_sec, - err < 0 ? -1LL : (long long)tv.tv_usec); - qdict_put_obj(qdict, "timestamp", obj); + ts = qdict_from_jsonf_nofail("{ 'seconds': %lld, 'microseconds': %lld }", + err < 0 ? -1LL : (long long)tv.tv_sec, + err < 0 ? -1LL : (long long)tv.tv_usec); + qdict_put(qdict, "timestamp", ts); } /* diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index a7569d5dce..da57f4cc24 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -588,8 +588,7 @@ static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, return; } - qobject_incref(qobj); - *obj = qobj; + *obj = qobject_ref(qobj); } static void qobject_input_type_null(Visitor *v, const char *name, @@ -652,7 +651,7 @@ static void qobject_input_free(Visitor *v) qobject_input_stack_object_free(tos); } - qobject_decref(qiv->root); + qobject_unref(qiv->root); if (qiv->errname) { g_string_free(qiv->errname, TRUE); } @@ -677,8 +676,7 @@ static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj) v->visitor.optional = qobject_input_optional; v->visitor.free = qobject_input_free; - v->root = obj; - qobject_incref(obj); + v->root = qobject_ref(obj); return v; } @@ -744,7 +742,7 @@ Visitor *qobject_input_visitor_new_str(const char *str, } v = qobject_input_visitor_new_keyval(QOBJECT(args)); } - QDECREF(args); + qobject_unref(args); return v; } diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c index 877e37eeb8..89ffd8a7bf 100644 --- a/qapi/qobject-output-visitor.c +++ b/qapi/qobject-output-visitor.c @@ -188,8 +188,8 @@ static void qobject_output_type_any(Visitor *v, const char *name, QObject **obj, Error **errp) { QObjectOutputVisitor *qov = to_qov(v); - qobject_incref(*obj); - qobject_output_add_obj(qov, name, *obj); + + qobject_output_add_obj(qov, name, qobject_ref(*obj)); } static void qobject_output_type_null(Visitor *v, const char *name, @@ -201,7 +201,7 @@ static void qobject_output_type_null(Visitor *v, const char *name, /* Finish building, and return the root object. * The root object is never null. The caller becomes the object's - * owner, and should use qobject_decref() when done with it. */ + * owner, and should use qobject_unref() when done with it. */ static void qobject_output_complete(Visitor *v, void *opaque) { QObjectOutputVisitor *qov = to_qov(v); @@ -210,8 +210,7 @@ static void qobject_output_complete(Visitor *v, void *opaque) assert(qov->root && QSLIST_EMPTY(&qov->stack)); assert(opaque == qov->result); - qobject_incref(qov->root); - *qov->result = qov->root; + *qov->result = qobject_ref(qov->root); qov->result = NULL; } @@ -226,7 +225,7 @@ static void qobject_output_free(Visitor *v) g_free(e); } - qobject_decref(qov->root); + qobject_unref(qov->root); g_free(qov); } diff --git a/qapi/run-state.json b/qapi/run-state.json index 1c9fff3aef..332e44897b 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -49,12 +49,15 @@ # @colo: guest is paused to save/restore VM state under colo checkpoint, # VM can not get into this state unless colo capability is enabled # for migration. (since 2.8) +# @preconfig: QEMU is paused before board specific init callback is executed. +# The state is reachable only if the --preconfig CLI option is used. +# (Since 3.0) ## { 'enum': 'RunState', 'data': [ 'debug', 'inmigrate', 'internal-error', 'io-error', 'paused', 'postmigrate', 'prelaunch', 'finish-migrate', 'restore-vm', 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog', - 'guest-panicked', 'colo' ] } + 'guest-panicked', 'colo', 'preconfig' ] } ## # @StatusInfo: @@ -91,7 +94,8 @@ # "status": "running" } } # ## -{ 'command': 'query-status', 'returns': 'StatusInfo' } +{ 'command': 'query-status', 'returns': 'StatusInfo', + 'allow-preconfig': true } ## # @SHUTDOWN: diff --git a/qapi/trace-events b/qapi/trace-events index 9e9008a1dc..70e049ea80 100644 --- a/qapi/trace-events +++ b/qapi/trace-events @@ -29,6 +29,6 @@ visit_type_int64(void *v, const char *name, int64_t *obj) "v=%p name=%s obj=%p" visit_type_size(void *v, const char *name, uint64_t *obj) "v=%p name=%s obj=%p" visit_type_bool(void *v, const char *name, bool *obj) "v=%p name=%s obj=%p" visit_type_str(void *v, const char *name, char **obj) "v=%p name=%s obj=%p" -visit_type_number(void *v, const char *name, double *obj) "v=%p name=%s obj=%p" +visit_type_number(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" visit_type_any(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" visit_type_null(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" diff --git a/qapi/transaction.json b/qapi/transaction.json index bd312792da..d7e4274550 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -46,6 +46,8 @@ # - @abort: since 1.6 # - @block-dirty-bitmap-add: since 2.5 # - @block-dirty-bitmap-clear: since 2.5 +# - @x-block-dirty-bitmap-enable: since 3.0 +# - @x-block-dirty-bitmap-disable: since 3.0 # - @blockdev-backup: since 2.3 # - @blockdev-snapshot: since 2.5 # - @blockdev-snapshot-internal-sync: since 1.7 @@ -59,6 +61,8 @@ 'abort': 'Abort', 'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd', 'block-dirty-bitmap-clear': 'BlockDirtyBitmap', + 'x-block-dirty-bitmap-enable': 'BlockDirtyBitmap', + 'x-block-dirty-bitmap-disable': 'BlockDirtyBitmap', 'blockdev-backup': 'BlockdevBackup', 'blockdev-snapshot': 'BlockdevSnapshot', 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal', diff --git a/qapi/ui.json b/qapi/ui.json index 5d01ad4304..4ca91bb45a 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -118,7 +118,8 @@ { 'struct': 'SpiceBasicInfo', 'data': { 'host': 'str', 'port': 'str', - 'family': 'NetworkAddressFamily' } } + 'family': 'NetworkAddressFamily' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SpiceServerInfo: @@ -131,7 +132,8 @@ ## { 'struct': 'SpiceServerInfo', 'base': 'SpiceBasicInfo', - 'data': { '*auth': 'str' } } + 'data': { '*auth': 'str' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SpiceChannel: @@ -156,7 +158,8 @@ { 'struct': 'SpiceChannel', 'base': 'SpiceBasicInfo', 'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int', - 'tls': 'bool'} } + 'tls': 'bool'}, + 'if': 'defined(CONFIG_SPICE)' } ## # @SpiceQueryMouseMode: @@ -175,7 +178,8 @@ # Since: 1.1 ## { 'enum': 'SpiceQueryMouseMode', - 'data': [ 'client', 'server', 'unknown' ] } + 'data': [ 'client', 'server', 'unknown' ], + 'if': 'defined(CONFIG_SPICE)' } ## # @SpiceInfo: @@ -212,7 +216,8 @@ { 'struct': 'SpiceInfo', 'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int', '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str', - 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} } + 'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']}, + 'if': 'defined(CONFIG_SPICE)' } ## # @query-spice: @@ -257,7 +262,8 @@ # } # ## -{ 'command': 'query-spice', 'returns': 'SpiceInfo' } +{ 'command': 'query-spice', 'returns': 'SpiceInfo', + 'if': 'defined(CONFIG_SPICE)' } ## # @SPICE_CONNECTED: @@ -282,7 +288,8 @@ ## { 'event': 'SPICE_CONNECTED', 'data': { 'server': 'SpiceBasicInfo', - 'client': 'SpiceBasicInfo' } } + 'client': 'SpiceBasicInfo' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SPICE_INITIALIZED: @@ -310,7 +317,8 @@ ## { 'event': 'SPICE_INITIALIZED', 'data': { 'server': 'SpiceServerInfo', - 'client': 'SpiceChannel' } } + 'client': 'SpiceChannel' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SPICE_DISCONNECTED: @@ -335,7 +343,8 @@ ## { 'event': 'SPICE_DISCONNECTED', 'data': { 'server': 'SpiceBasicInfo', - 'client': 'SpiceBasicInfo' } } + 'client': 'SpiceBasicInfo' }, + 'if': 'defined(CONFIG_SPICE)' } ## # @SPICE_MIGRATE_COMPLETED: @@ -350,7 +359,8 @@ # "event": "SPICE_MIGRATE_COMPLETED" } # ## -{ 'event': 'SPICE_MIGRATE_COMPLETED' } +{ 'event': 'SPICE_MIGRATE_COMPLETED', + 'if': 'defined(CONFIG_SPICE)' } ## # == VNC @@ -377,7 +387,8 @@ 'data': { 'host': 'str', 'service': 'str', 'family': 'NetworkAddressFamily', - 'websocket': 'bool' } } + 'websocket': 'bool' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VncServerInfo: @@ -391,7 +402,8 @@ ## { 'struct': 'VncServerInfo', 'base': 'VncBasicInfo', - 'data': { '*auth': 'str' } } + 'data': { '*auth': 'str' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VncClientInfo: @@ -408,7 +420,8 @@ ## { 'struct': 'VncClientInfo', 'base': 'VncBasicInfo', - 'data': { '*x509_dname': 'str', '*sasl_username': 'str' } } + 'data': { '*x509_dname': 'str', '*sasl_username': 'str' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VncInfo: @@ -449,7 +462,8 @@ { 'struct': 'VncInfo', 'data': {'enabled': 'bool', '*host': 'str', '*family': 'NetworkAddressFamily', - '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} } + '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']}, + 'if': 'defined(CONFIG_VNC)' } ## # @VncPrimaryAuth: @@ -460,7 +474,8 @@ ## { 'enum': 'VncPrimaryAuth', 'data': [ 'none', 'vnc', 'ra2', 'ra2ne', 'tight', 'ultra', - 'tls', 'vencrypt', 'sasl' ] } + 'tls', 'vencrypt', 'sasl' ], + 'if': 'defined(CONFIG_VNC)' } ## # @VncVencryptSubAuth: @@ -474,8 +489,8 @@ 'tls-none', 'x509-none', 'tls-vnc', 'x509-vnc', 'tls-plain', 'x509-plain', - 'tls-sasl', 'x509-sasl' ] } - + 'tls-sasl', 'x509-sasl' ], + 'if': 'defined(CONFIG_VNC)' } ## # @VncServerInfo2: @@ -492,8 +507,8 @@ { 'struct': 'VncServerInfo2', 'base': 'VncBasicInfo', 'data': { 'auth' : 'VncPrimaryAuth', - '*vencrypt' : 'VncVencryptSubAuth' } } - + '*vencrypt' : 'VncVencryptSubAuth' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VncInfo2: @@ -525,7 +540,8 @@ 'clients' : ['VncClientInfo'], 'auth' : 'VncPrimaryAuth', '*vencrypt' : 'VncVencryptSubAuth', - '*display' : 'str' } } + '*display' : 'str' }, + 'if': 'defined(CONFIG_VNC)' } ## # @query-vnc: @@ -556,8 +572,8 @@ # } # ## -{ 'command': 'query-vnc', 'returns': 'VncInfo' } - +{ 'command': 'query-vnc', 'returns': 'VncInfo', + 'if': 'defined(CONFIG_VNC)' } ## # @query-vnc-servers: # @@ -567,7 +583,8 @@ # # Since: 2.3 ## -{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'] } +{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'], + 'if': 'defined(CONFIG_VNC)' } ## # @change-vnc-password: @@ -581,7 +598,8 @@ # Notes: An empty password in this command will set the password to the empty # string. Existing clients are unaffected by executing this command. ## -{ 'command': 'change-vnc-password', 'data': {'password': 'str'} } +{ 'command': 'change-vnc-password', 'data': {'password': 'str'}, + 'if': 'defined(CONFIG_VNC)' } ## # @VNC_CONNECTED: @@ -610,7 +628,8 @@ ## { 'event': 'VNC_CONNECTED', 'data': { 'server': 'VncServerInfo', - 'client': 'VncBasicInfo' } } + 'client': 'VncBasicInfo' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VNC_INITIALIZED: @@ -637,7 +656,8 @@ ## { 'event': 'VNC_INITIALIZED', 'data': { 'server': 'VncServerInfo', - 'client': 'VncClientInfo' } } + 'client': 'VncClientInfo' }, + 'if': 'defined(CONFIG_VNC)' } ## # @VNC_DISCONNECTED: @@ -663,7 +683,8 @@ ## { 'event': 'VNC_DISCONNECTED', 'data': { 'server': 'VncServerInfo', - 'client': 'VncClientInfo' } } + 'client': 'VncClientInfo' }, + 'if': 'defined(CONFIG_VNC)' } ## # = Input @@ -995,17 +1016,6 @@ 'events' : [ 'InputEvent' ] } } -## -# @DisplayNoOpts: -# -# Empty struct for displays without config options. -# -# Since: 2.12 -# -## -{ 'struct' : 'DisplayNoOpts', - 'data' : { } } - ## # @DisplayGTK: # @@ -1019,6 +1029,24 @@ { 'struct' : 'DisplayGTK', 'data' : { '*grab-on-hover' : 'bool' } } + ## + # @DisplayGLMode: + # + # Display OpenGL mode. + # + # @off: Disable OpenGL (default). + # @on: Use OpenGL, pick context type automatically. + # Would better be named 'auto' but is called 'on' for backward + # compatibility with bool type. + # @core: Use OpenGL with Core (desktop) Context. + # @es: Use OpenGL with ES (embedded systems) Context. + # + # Since: 3.0 + # + ## + { 'enum' : 'DisplayGLMode', + 'data' : [ 'off', 'on', 'core', 'es' ] } + ## # @DisplayType: # @@ -1048,12 +1076,6 @@ 'base' : { 'type' : 'DisplayType', '*full-screen' : 'bool', '*window-close' : 'bool', - '*gl' : 'bool' }, + '*gl' : 'DisplayGLMode' }, 'discriminator' : 'type', - 'data' : { 'default' : 'DisplayNoOpts', - 'none' : 'DisplayNoOpts', - 'gtk' : 'DisplayGTK', - 'sdl' : 'DisplayNoOpts', - 'egl-headless' : 'DisplayNoOpts', - 'curses' : 'DisplayNoOpts', - 'cocoa' : 'DisplayNoOpts' } } + 'data' : { 'gtk' : 'DisplayGTK' } } diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi new file mode 100644 index 0000000000..9920a85adc --- /dev/null +++ b/qemu-deprecated.texi @@ -0,0 +1,234 @@ +@node Deprecated features +@appendix Deprecated features + +In general features are intended to be supported indefinitely once +introduced into QEMU. In the event that a feature needs to be removed, +it will be listed in this appendix. The feature will remain functional +for 2 releases prior to actual removal. Deprecated features may also +generate warnings on the console when QEMU starts up, or if activated +via a monitor command, however, this is not a mandatory requirement. + +Prior to the 2.10.0 release there was no official policy on how +long features would be deprecated prior to their removal, nor +any documented list of which features were deprecated. Thus +any features deprecated prior to 2.10.0 will be treated as if +they were first deprecated in the 2.10.0 release. + +What follows is a list of all features currently marked as +deprecated. + +@section Build options + +@subsection GTK 2.x + +Previously QEMU has supported building against both GTK 2.x +and 3.x series APIs. Support for the GTK 2.x builds will be +discontinued, so maintainers should switch to using GTK 3.x, +which is the default. + +@subsection SDL 1.2 + +Previously QEMU has supported building against both SDL 1.2 +and 2.0 series APIs. Support for the SDL 1.2 builds will be +discontinued, so maintainers should switch to using SDL 2.0, +which is the default. + +@section System emulator command line arguments + +@subsection -no-kvm (since 1.3.0) + +The ``-no-kvm'' argument is now a synonym for setting +``-machine accel=tcg''. + +@subsection -vnc tls (since 2.5.0) + +The ``-vnc tls'' argument is now a synonym for setting +``-object tls-creds-anon,id=tls0'' combined with +``-vnc tls-creds=tls0' + +@subsection -vnc x509 (since 2.5.0) + +The ``-vnc x509=/path/to/certs'' argument is now a +synonym for setting +``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=no'' +combined with ``-vnc tls-creds=tls0' + +@subsection -vnc x509verify (since 2.5.0) + +The ``-vnc x509verify=/path/to/certs'' argument is now a +synonym for setting +``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=yes'' +combined with ``-vnc tls-creds=tls0' + +@subsection -tftp (since 2.6.0) + +The ``-tftp /some/dir'' argument is replaced by either +``-netdev user,id=x,tftp=/some/dir '' (for pluggable NICs, accompanied +with ``-device ...,netdev=x''), or ``-nic user,tftp=/some/dir'' +(for embedded NICs). The new syntax allows different settings to be +provided per NIC. + +@subsection -bootp (since 2.6.0) + +The ``-bootp /some/file'' argument is replaced by either +``-netdev user,id=x,bootp=/some/file '' (for pluggable NICs, accompanied +with ``-device ...,netdev=x''), or ``-nic user,bootp=/some/file'' +(for embedded NICs). The new syntax allows different settings to be +provided per NIC. + +@subsection -redir (since 2.6.0) + +The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport'' argument is +replaced by either +``-netdev user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' +(for pluggable NICs, accompanied with ``-device ...,netdev=x'') or +``-nic user,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' +(for embedded NICs). The new syntax allows different settings to be +provided per NIC. + +@subsection -smb (since 2.6.0) + +The ``-smb /some/dir'' argument is replaced by either +``-netdev user,id=x,smb=/some/dir '' (for pluggable NICs, accompanied +with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' +(for embedded NICs). The new syntax allows different settings to be +provided per NIC. + +@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) + +The drive geometry arguments are replaced by the the geometry arguments +that can be specified with the ``-device'' parameter. + +@subsection -drive serial=... (since 2.10.0) + +The drive serial argument is replaced by the the serial argument +that can be specified with the ``-device'' parameter. + +@subsection -drive addr=... (since 2.10.0) + +The drive addr argument is replaced by the the addr argument +that can be specified with the ``-device'' parameter. + +@subsection -usbdevice (since 2.10.0) + +The ``-usbdevice DEV'' argument is now a synonym for setting +the ``-device usb-DEV'' argument instead. The deprecated syntax +would automatically enable USB support on the machine type. +If using the new syntax, USB support must be explicitly +enabled via the ``-machine usb=on'' argument. + +@subsection -nodefconfig (since 2.11.0) + +The ``-nodefconfig`` argument is a synonym for ``-no-user-config``. + +@subsection -balloon (since 2.12.0) + +The @option{--balloon virtio} argument has been superseded by +@option{--device virtio-balloon}. + +@subsection -machine s390-squash-mcss=on|off (since 2.12.0) + +The ``s390-squash-mcss=on`` property has been obsoleted by allowing the +cssid to be chosen freely. Instead of squashing subchannels into the +default channel subsystem image for guests that do not support multiple +channel subsystems, all devices can be put into the default channel +subsystem image. + +@subsection -fsdev handle (since 2.12.0) + +The ``handle'' fsdev backend does not support symlinks and causes the 9p +filesystem in the guest to fail a fair amount of tests from the PJD POSIX +filesystem test suite. Also it requires the CAP_DAC_READ_SEARCH capability, +which is not the recommended way to run QEMU. This backend should not be +used and it will be removed with no replacement. + +@subsection -no-frame (since 2.12.0) + +The @code{--no-frame} argument works with SDL 1.2 only. The other user +interfaces never implemented this in the first place. So this will be +removed together with SDL 1.2 support. + +@subsection -rtc-td-hack (since 2.12.0) + +The @code{-rtc-td-hack} option has been replaced by +@code{-rtc driftfix=slew}. + +@subsection -localtime (since 2.12.0) + +The @code{-localtime} option has been replaced by @code{-rtc base=localtime}. + +@subsection -startdate (since 2.12.0) + +The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. + +@subsection -virtioconsole (since 3.0.0) + +Option @option{-virtioconsole} has been replaced by +@option{-device virtconsole}. + +@subsection -clock (since 3.0.0) + +The @code{-clock} option is ignored since QEMU version 1.7.0. There is no +replacement since it is not needed anymore. + +@subsection -enable-hax (since 3.0.0) + +The @option{-enable-hax} option has been replaced by @option{-accel hax}. +Both options have been introduced in QEMU version 2.9.0. + +@subsection -drive file=json:@{...@{'driver':'file'@}@} (since 3.0) + +The 'file' driver for drives is no longer appropriate for character or host +devices and will only accept regular files (S_IFREG). The correct driver +for these file types is 'host_cdrom' or 'host_device' as appropriate. + +@section QEMU Machine Protocol (QMP) commands + +@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) + +"autoload" parameter is now ignored. All bitmaps are automatically loaded +from qcow2 images. + +@subsection query-cpus (since 2.12.0) + +The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. + +@subsection query-cpus-fast "arch" output member (since 3.0.0) + +The ``arch'' output member of the ``query-cpus-fast'' command is +replaced by the ``target'' output member. + +@section System emulator devices + +@subsection ivshmem (since 2.6.0) + +The ``ivshmem'' device type is replaced by either the ``ivshmem-plain'' +or ``ivshmem-doorbell`` device types. + +@subsection Page size support < 4k for embedded PowerPC CPUs (since 2.12.0) + +qemu-system-ppcemb will be removed. qemu-system-ppc (or qemu-system-ppc64) +should be used instead. That means that embedded 4xx PowerPC CPUs will not +support page sizes < 4096 any longer. + +@section System emulator machines + +@subsection pc-0.10 and pc-0.11 (since 3.0) + +These machine types are very old and likely can not be used for live migration +from old QEMU versions anymore. A newer machine type should be used instead. + +@section Device options + +@subsection Block device options + +@subsubsection "backing": "" (since 2.12.0) + +In order to prevent QEMU from automatically opening an image's backing +chain, use ``"backing": null'' instead. + +@subsection vio-spapr-device device options + +@subsubsection "irq": "" (since 3.0.0) + +The ``irq'' property is obsoleted. diff --git a/qemu-doc.texi b/qemu-doc.texi index 5813d27615..abfd2db546 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -39,6 +39,7 @@ * QEMU User space emulator:: * Implementation notes:: * Deprecated features:: +* Supported build platforms:: * License:: * Index:: @end menu @@ -714,20 +715,12 @@ state is not saved or restored properly (in particular USB). @node pcsys_network @section Network emulation -QEMU can simulate several network cards (PCI or ISA cards on the PC -target) and can connect them to an arbitrary number of Virtual Local -Area Networks (VLANs). Host TAP devices can be connected to any QEMU -VLAN. VLAN can be connected between separate instances of QEMU to -simulate large networks. For simpler usage, a non privileged user mode -network stack can replace the TAP device to have a basic network -connection. - -@subsection VLANs - -QEMU simulates several VLANs. A VLAN can be symbolised as a virtual -connection between several network devices. These devices can be for -example QEMU virtual Ethernet cards or virtual Host ethernet devices -(TAP devices). +QEMU can simulate several network cards (e.g. PCI or ISA cards on the PC +target) and can connect them to a network backend on the host or an emulated +hub. The various host network backends can either be used to connect the NIC of +the guest to a real network (e.g. by using a TAP devices or the non-privileged +user mode network stack), or to other guest instances running in another QEMU +process (e.g. by using the socket host network backend). @subsection Using TAP network interfaces @@ -763,7 +756,7 @@ network). The virtual network configuration is the following: @example - QEMU VLAN <------> Firewall/DHCP server <-----> Internet + guest (10.0.2.15) <------> Firewall/DHCP server <-----> Internet | (10.0.2.2) | ----> DNS server (10.0.2.3) @@ -798,11 +791,23 @@ When using the @option{'-netdev user,hostfwd=...'} option, TCP or UDP connections can be redirected from the host to the guest. It allows for example to redirect X11, telnet or SSH connections. -@subsection Connecting VLANs between QEMU instances +@subsection Hubs -Using the @option{-net socket} option, it is possible to make VLANs -that span several QEMU instances. See @ref{sec_invocation} to have a -basic example. +QEMU can simulate several hubs. A hub can be thought of as a virtual connection +between several network devices. These devices can be for example QEMU virtual +ethernet cards or virtual Host ethernet devices (TAP devices). You can connect +guest NICs or host network backends to such a hub using the @option{-netdev +hubport} or @option{-nic hubport} options. The legacy @option{-net} option +also connects the given device to the emulated hub with ID 0 (i.e. the default +hub) unless you specify a netdev with @option{-net nic,netdev=xxx} here. + +@subsection Connecting emulated networks between QEMU instances + +Using the @option{-netdev socket} (or @option{-nic socket} or +@option{-net socket}) option, it is possible to create emulated +networks that span several QEMU instances. +See the description of the @option{-netdev socket} option in the +@ref{sec_invocation,,Invocation chapter} to have a basic example. @node pcsys_other_devs @section Other Devices @@ -1257,6 +1262,7 @@ The recommendation is for the server to keep its certificates in either * tls_generate_server:: * tls_generate_client:: * tls_creds_setup:: +* tls_psk:: @end menu @node tls_generate_ca @subsection Setup the Certificate Authority @@ -1505,6 +1511,42 @@ example with VNC: $QEMU -vnc 0.0.0.0:0,tls-creds=tls0 @end example +@node tls_psk +@subsection TLS Pre-Shared Keys (PSK) + +Instead of using certificates, you may also use TLS Pre-Shared Keys +(TLS-PSK). This can be simpler to set up than certificates but is +less scalable. + +Use the GnuTLS @code{psktool} program to generate a @code{keys.psk} +file containing one or more usernames and random keys: + +@example +mkdir -m 0700 /tmp/keys +psktool -u rich -p /tmp/keys/keys.psk +@end example + +TLS-enabled servers such as qemu-nbd can use this directory like so: + +@example +qemu-nbd \ + -t -x / \ + --object tls-creds-psk,id=tls0,endpoint=server,dir=/tmp/keys \ + --tls-creds tls0 \ + image.qcow2 +@end example + +When connecting from a qemu-based client you must specify the +directory containing @code{keys.psk} and an optional @var{username} +(defaults to ``qemu''): + +@example +qemu-img info \ + --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=rich,endpoint=client \ + --image-opts \ + file.driver=nbd,file.host=localhost,file.port=10809,file.tls-creds=tls0,file.export=/ +@end example + @node gdb_usage @section GDB usage @@ -2749,232 +2791,74 @@ Run the emulation in single step mode. @include qemu-tech.texi -@node Deprecated features -@appendix Deprecated features +@include qemu-deprecated.texi -In general features are intended to be supported indefinitely once -introduced into QEMU. In the event that a feature needs to be removed, -it will be listed in this appendix. The feature will remain functional -for 2 releases prior to actual removal. Deprecated features may also -generate warnings on the console when QEMU starts up, or if activated -via a monitor command, however, this is not a mandatory requirement. +@node Supported build platforms +@appendix Supported build platforms -Prior to the 2.10.0 release there was no official policy on how -long features would be deprecated prior to their removal, nor -any documented list of which features were deprecated. Thus -any features deprecated prior to 2.10.0 will be treated as if -they were first deprecated in the 2.10.0 release. +QEMU aims to support building and executing on multiple host OS platforms. +This appendix outlines which platforms are the major build targets. These +platforms are used as the basis for deciding upon the minimum required +versions of 3rd party software QEMU depends on. The supported platforms +are the targets for automated testing performed by the project when patches +are submitted for review, and tested before and after merge. -What follows is a list of all features currently marked as -deprecated. +If a platform is not listed here, it does not imply that QEMU won't work. +If an unlisted platform has comparable software versions to a listed platform, +there is every expectation that it will work. Bug reports are welcome for +problems encountered on unlisted platforms unless they are clearly older +vintage than what is described here. -@section Build options +Note that when considering software versions shipped in distros as support +targets, QEMU considers only the version number, and assumes the features in +that distro match the upstream release with the same version. In other words, +if a distro backports extra features to the software in their distro, QEMU +upstream code will not add explicit support for those backports, unless the +feature is auto-detectable in a manner that works for the upstream releases +too. -@subsection GTK 2.x +The Repology site @url{https://repology.org} is a useful resource to identify +currently shipped versions of software in various operating systems, though +it does not cover all distros listed below. -Previously QEMU has supported building against both GTK 2.x -and 3.x series APIs. Support for the GTK 2.x builds will be -discontinued, so maintainers should switch to using GTK 3.x, -which is the default. +@section Linux OS -@subsection SDL 1.2 +For distributions with frequent, short-lifetime releases, the project will +aim to support all versions that are not end of life by their respective +vendors. For the purposes of identifying supported software versions, the +project will look at Fedora, Ubuntu, and openSUSE distros. Other short- +lifetime distros will be assumed to ship similar software versions. -Previously QEMU has supported building against both SDL 1.2 -and 2.0 series APIs. Support for the SDL 1.2 builds will be -discontinued, so maintainers should switch to using SDL 2.0, -which is the default. +For distributions with long-lifetime releases, the project will aim to support +the most recent major version at all times. Support for the previous major +version will be dropped 2 years after the new major version is released. For +the purposes of identifying supported software versions, the project will look +at RHEL, Debian, Ubuntu LTS, and SLES distros. Other long-lifetime distros will +be assumed to ship similar software versions. -@section System emulator command line arguments +@section Windows -@subsection -no-kvm-pit-reinjection (since 1.3.0) +The project supports building with current versions of the MinGW toolchain, +hosted on Linux. -The ``-no-kvm-pit-reinjection'' argument is now a -synonym for setting ``-global kvm-pit.lost_tick_policy=discard''. +@section macOS -@subsection -no-kvm-irqchip (since 1.3.0) +The project supports building with the two most recent versions of macOS, with +the current homebrew package set available. -The ``-no-kvm-irqchip'' argument is now a synonym for -setting ``-machine kernel_irqchip=off''. +@section FreeBSD -@subsection -no-kvm (since 1.3.0) +The project aims to support the all the versions which are not end of life. -The ``-no-kvm'' argument is now a synonym for setting -``-machine accel=tcg''. +@section NetBSD -@subsection -vnc tls (since 2.5.0) +The project aims to support the most recent major version at all times. Support +for the previous major version will be dropped 2 years after the new major +version is released. -The ``-vnc tls'' argument is now a synonym for setting -``-object tls-creds-anon,id=tls0'' combined with -``-vnc tls-creds=tls0' +@section OpenBSD -@subsection -vnc x509 (since 2.5.0) - -The ``-vnc x509=/path/to/certs'' argument is now a -synonym for setting -``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=no'' -combined with ``-vnc tls-creds=tls0' - -@subsection -vnc x509verify (since 2.5.0) - -The ``-vnc x509verify=/path/to/certs'' argument is now a -synonym for setting -``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=yes'' -combined with ``-vnc tls-creds=tls0' - -@subsection -tftp (since 2.6.0) - -The ``-tftp /some/dir'' argument is replaced by either -``-netdev user,id=x,tftp=/some/dir '' (for pluggable NICs, accompanied -with ``-device ...,netdev=x''), or ``-nic user,tftp=/some/dir'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -bootp (since 2.6.0) - -The ``-bootp /some/file'' argument is replaced by either -``-netdev user,id=x,bootp=/some/file '' (for pluggable NICs, accompanied -with ``-device ...,netdev=x''), or ``-nic user,bootp=/some/file'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -redir (since 2.6.0) - -The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport'' argument is -replaced by either -``-netdev user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' -(for pluggable NICs, accompanied with ``-device ...,netdev=x'') or -``-nic user,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -smb (since 2.6.0) - -The ``-smb /some/dir'' argument is replaced by either -``-netdev user,id=x,smb=/some/dir '' (for pluggable NICs, accompanied -with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -net vlan (since 2.9.0) - -The ``-net vlan=NN'' argument was mostly used to attach separate -network backends to different virtual NICs. This is the default -behavior for ``-netdev'' and ``-nic''. You can connect multiple -``-netdev'' and ``-nic'' devices to the same network using the -"hubport" network backend, created with ``-netdev hubport,hubid=NN,...'' -and ``-nic hubport,hubid=NN''. - -@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) - -The drive geometry arguments are replaced by the the geometry arguments -that can be specified with the ``-device'' parameter. - -@subsection -drive serial=... (since 2.10.0) - -The drive serial argument is replaced by the the serial argument -that can be specified with the ``-device'' parameter. - -@subsection -drive addr=... (since 2.10.0) - -The drive addr argument is replaced by the the addr argument -that can be specified with the ``-device'' parameter. - -@subsection -usbdevice (since 2.10.0) - -The ``-usbdevice DEV'' argument is now a synonym for setting -the ``-device usb-DEV'' argument instead. The deprecated syntax -would automatically enable USB support on the machine type. -If using the new syntax, USB support must be explicitly -enabled via the ``-machine usb=on'' argument. - -@subsection -nodefconfig (since 2.11.0) - -The ``-nodefconfig`` argument is a synonym for ``-no-user-config``. - -@subsection -balloon (since 2.12.0) - -The @option{--balloon virtio} argument has been superseded by -@option{--device virtio-balloon}. - -@subsection -machine s390-squash-mcss=on|off (since 2.12.0) - -The ``s390-squash-mcss=on`` property has been obsoleted by allowing the -cssid to be chosen freely. Instead of squashing subchannels into the -default channel subsystem image for guests that do not support multiple -channel subsystems, all devices can be put into the default channel -subsystem image. - -@subsection -fsdev handle (since 2.12.0) - -The ``handle'' fsdev backend does not support symlinks and causes the 9p -filesystem in the guest to fail a fair amount of tests from the PJD POSIX -filesystem test suite. Also it requires the CAP_DAC_READ_SEARCH capability, -which is not the recommended way to run QEMU. This backend should not be -used and it will be removed with no replacement. - -@subsection -no-frame (since 2.12.0) - -The @code{--no-frame} argument works with SDL 1.2 only. The other user -interfaces never implemented this in the first place. So this will be -removed together with SDL 1.2 support. - -@subsection -rtc-td-hack (since 2.12.0) - -The @code{-rtc-td-hack} option has been replaced by -@code{-rtc driftfix=slew}. - -@subsection -localtime (since 2.12.0) - -The @code{-localtime} option has been replaced by @code{-rtc base=localtime}. - -@subsection -startdate (since 2.12.0) - -The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. - -@section qemu-img command line arguments - -@subsection convert -s (since 2.0.0) - -The ``convert -s snapshot_id_or_name'' argument is obsoleted -by the ``convert -l snapshot_param'' argument instead. - -@section QEMU Machine Protocol (QMP) commands - -@subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) - -"autoload" parameter is now ignored. All bitmaps are automatically loaded -from qcow2 images. - -@subsection query-cpus (since 2.12.0) - -The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. - -@section System emulator devices - -@subsection ivshmem (since 2.6.0) - -The ``ivshmem'' device type is replaced by either the ``ivshmem-plain'' -or ``ivshmem-doorbell`` device types. - -@subsection Page size support < 4k for embedded PowerPC CPUs (since 2.12.0) - -qemu-system-ppcemb will be removed. qemu-system-ppc (or qemu-system-ppc64) -should be used instead. That means that embedded 4xx PowerPC CPUs will not -support page sizes < 4096 any longer. - -@section System emulator machines - -@subsection Xilinx EP108 (since 2.11.0) - -The ``xlnx-ep108'' machine has been replaced by the ``xlnx-zcu102'' machine. -The ``xlnx-zcu102'' machine has the same features and capabilites in QEMU. - -@section Block device options - -@subsection "backing": "" (since 2.12.0) - -In order to prevent QEMU from automatically opening an image's backing -chain, use ``"backing": null'' instead. +The project aims to support the all the versions which are not end of life. @node License @appendix License diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 2fe31893cf..1526f327a5 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -6,6 +6,9 @@ HXCOMM DEF(command, callback, arg_string) is used to construct HXCOMM command structures and help message. HXCOMM HXCOMM can be used for comments, discarded from both texi and C +HXCOMM When amending the TEXI sections, please remember to copy the usage +HXCOMM over to the per-command sections in qemu-img.texi. + STEXI @table @option ETEXI @@ -23,13 +26,13 @@ STEXI ETEXI DEF("check", img_check, - "check [-q] [--object objectdef] [--image-opts] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename") + "check [--object objectdef] [--image-opts] [-q] [-f fmt] [--output=ofmt] [-r [leaks | all]] [-T src_cache] [-U] filename") STEXI @item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename} ETEXI DEF("commit", img_commit, - "commit [-q] [--object objectdef] [--image-opts] [-f fmt] [-t cache] [-b base] [-d] [-p] filename") + "commit [--object objectdef] [--image-opts] [-q] [-f fmt] [-t cache] [-b base] [-d] [-p] filename") STEXI @item commit [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} ETEXI @@ -41,13 +44,13 @@ STEXI ETEXI DEF("convert", img_convert, - "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-s snapshot_id_or_name] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") + "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") STEXI -@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("create", img_create, - "create [-q] [--object objectdef] [-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]") STEXI @item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] ETEXI @@ -89,9 +92,9 @@ STEXI ETEXI DEF("resize", img_resize, - "resize [--object objectdef] [--image-opts] [-q] [--shrink] filename [+ | -]size") + "resize [--object objectdef] [--image-opts] [-f fmt] [--preallocation=prealloc] [-q] [--shrink] filename [+ | -]size") STEXI -@item resize [--object @var{objectdef}] [--image-opts] [-q] [--shrink] @var{filename} [+ | -]@var{size} +@item resize [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--preallocation=@var{prealloc}] [-q] [--shrink] @var{filename} [+ | -]@var{size} ETEXI STEXI diff --git a/qemu-img.c b/qemu-img.c index 855fa52514..1acddf693c 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -123,7 +123,6 @@ static void QEMU_NORETURN help(void) " " arg_string "\n" #include "qemu-img-cmds.h" #undef DEF -#undef GEN_DOCS "\n" "Command parameters:\n" " 'filename' is a disk image filename\n" @@ -149,8 +148,6 @@ static void QEMU_NORETURN help(void) " 'snapshot_param' is param used for internal snapshot, format\n" " is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" " '[ID_OR_NAME]'\n" - " 'snapshot_id_or_name' is deprecated, use 'snapshot_param'\n" - " instead\n" " '-c' indicates that target image must be compressed (qcow format only)\n" " '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n" " new backing file match exactly. The image doesn't need a working\n" @@ -250,6 +247,11 @@ static int print_block_option_help(const char *filename, const char *fmt) return 1; } + if (!drv->create_opts) { + error_report("Format driver '%s' does not support image creation", fmt); + return 1; + } + create_opts = qemu_opts_append(create_opts, drv->create_opts); if (filename) { proto_drv = bdrv_find_protocol(filename, true, &local_err); @@ -258,9 +260,15 @@ static int print_block_option_help(const char *filename, const char *fmt) qemu_opts_free(create_opts); return 1; } + if (!proto_drv->create_opts) { + error_report("Protocal driver '%s' does not support image creation", + proto_drv->format_name); + return 1; + } create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); } + printf("Supported options:\n"); qemu_opts_print_help(create_opts); qemu_opts_free(create_opts); return 0; @@ -277,12 +285,12 @@ static BlockBackend *img_open_opts(const char *optstr, options = qemu_opts_to_qdict(opts, NULL); if (force_share) { if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE) - && !qdict_get_bool(options, BDRV_OPT_FORCE_SHARE)) { + && strcmp(qdict_get_str(options, BDRV_OPT_FORCE_SHARE), "on")) { error_report("--force-share/-U conflicts with image options"); - QDECREF(options); + qobject_unref(options); return NULL; } - qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); + qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on"); } blk = blk_new_open(NULL, NULL, options, flags, &local_err); if (!blk) { @@ -561,9 +569,9 @@ static void dump_json_image_check(ImageCheck *check, bool quiet) str = qobject_to_json_pretty(obj); assert(str != NULL); qprintf(quiet, "%s\n", qstring_get_str(str)); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); - QDECREF(str); + qobject_unref(str); } static void dump_human_image_check(ImageCheck *check, bool quiet) @@ -862,19 +870,23 @@ static void run_block_job(BlockJob *job, Error **errp) int ret = 0; aio_context_acquire(aio_context); - block_job_ref(job); + job_ref(&job->job); do { + float progress = 0.0f; aio_poll(aio_context, true); - qemu_progress_print(job->len ? - ((float)job->offset / job->len * 100.f) : 0.0f, 0); - } while (!job->ready && !job->completed); + if (job->job.progress_total) { + progress = (float)job->job.progress_current / + job->job.progress_total * 100.f; + } + qemu_progress_print(progress, 0); + } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); - if (!job->completed) { - ret = block_job_complete_sync(job, errp); + if (!job_is_completed(&job->job)) { + ret = job_complete_sync(&job->job, errp); } else { - ret = job->ret; + ret = job->job.ret; } - block_job_unref(job); + job_unref(&job->job); aio_context_release(aio_context); /* publish completion progress only when success */ @@ -1015,7 +1027,7 @@ static int img_commit(int argc, char **argv) aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0, + commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0, BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, &cbi, false, &local_err); aio_context_release(aio_context); @@ -1093,11 +1105,15 @@ static int64_t find_nonzero(const uint8_t *buf, int64_t n) * * 'pnum' is set to the number of sectors (including and immediately following * the first one) that are known to be in the same allocated/unallocated state. + * The function will try to align the end offset to alignment boundaries so + * that the request will at least end aligned and consequtive requests will + * also start at an aligned offset. */ -static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum) +static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum, + int64_t sector_num, int alignment) { bool is_zero; - int i; + int i, tail; if (n <= 0) { *pnum = 0; @@ -1110,6 +1126,23 @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum) break; } } + + 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. */ + is_zero = false; + } + if (!is_zero) { + /* align up end offset of allocated areas. */ + i += alignment - tail; + i = MIN(i, n); + } else { + /* align down end offset of zero areas. */ + i -= tail; + } + } *pnum = i; return !is_zero; } @@ -1120,7 +1153,7 @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum) * breaking up write requests for only small sparse areas. */ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, - int min) + int min, int64_t sector_num, int alignment) { int ret; int num_checked, num_used; @@ -1129,7 +1162,7 @@ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, min = n; } - ret = is_allocated_sectors(buf, n, pnum); + ret = is_allocated_sectors(buf, n, pnum, sector_num, alignment); if (!ret) { return ret; } @@ -1137,13 +1170,15 @@ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, num_used = *pnum; buf += BDRV_SECTOR_SIZE * *pnum; n -= *pnum; + sector_num += *pnum; num_checked = num_used; while (n > 0) { - ret = is_allocated_sectors(buf, n, pnum); + ret = is_allocated_sectors(buf, n, pnum, sector_num, alignment); buf += BDRV_SECTOR_SIZE * *pnum; n -= *pnum; + sector_num += *pnum; num_checked += *pnum; if (ret) { num_used = num_checked; @@ -1542,9 +1577,13 @@ typedef struct ImgConvertState { BlockBackend *target; bool has_zero_init; bool compressed; + bool unallocated_blocks_are_zero; bool target_has_backing; + int64_t target_backing_sectors; /* negative if unknown */ bool wr_in_order; + bool copy_range; int min_sparse; + int alignment; size_t cluster_sectors; size_t buf_sectors; long num_coroutines; @@ -1571,12 +1610,23 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) { int64_t src_cur_offset; int ret, n, src_cur; + bool post_backing_zero = false; convert_select_part(s, sector_num, &src_cur, &src_cur_offset); assert(s->total_sectors > sector_num); n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); + if (s->target_backing_sectors >= 0) { + if (sector_num >= s->target_backing_sectors) { + post_backing_zero = s->unallocated_blocks_are_zero; + } else if (sector_num + n > s->target_backing_sectors) { + /* Split requests around target_backing_sectors (because + * starting from there, zeros are handled differently) */ + n = s->target_backing_sectors - sector_num; + } + } + if (s->sector_next_status <= sector_num) { int64_t count = n * BDRV_SECTOR_SIZE; @@ -1598,7 +1648,7 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); if (ret & BDRV_BLOCK_ZERO) { - s->status = BLK_ZERO; + s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO; } else if (ret & BDRV_BLOCK_DATA) { s->status = BLK_DATA; } else { @@ -1698,7 +1748,8 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, * zeroed. */ if (!s->min_sparse || (!s->compressed && - is_allocated_sectors_min(buf, n, &n, s->min_sparse)) || + is_allocated_sectors_min(buf, n, &n, s->min_sparse, + sector_num, s->alignment)) || (s->compressed && !buffer_is_zero(buf, n * BDRV_SECTOR_SIZE))) { @@ -1737,6 +1788,37 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, return 0; } +static int coroutine_fn convert_co_copy_range(ImgConvertState *s, int64_t sector_num, + int nb_sectors) +{ + int n, ret; + + while (nb_sectors > 0) { + BlockBackend *blk; + int src_cur; + int64_t bs_sectors, src_cur_offset; + int64_t offset; + + convert_select_part(s, sector_num, &src_cur, &src_cur_offset); + offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS; + blk = s->src[src_cur]; + bs_sectors = s->src_sectors[src_cur]; + + n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); + + ret = blk_co_copy_range(blk, offset, s->target, + sector_num << BDRV_SECTOR_BITS, + n << BDRV_SECTOR_BITS, 0, 0); + if (ret < 0) { + return ret; + } + + sector_num += n; + nb_sectors -= n; + } + return 0; +} + static void coroutine_fn convert_co_do_copy(void *opaque) { ImgConvertState *s = opaque; @@ -1759,6 +1841,7 @@ static void coroutine_fn convert_co_do_copy(void *opaque) int n; int64_t sector_num; enum ImgConvertBlockStatus status; + bool copy_range; qemu_co_mutex_lock(&s->lock); if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) { @@ -1788,7 +1871,9 @@ static void coroutine_fn convert_co_do_copy(void *opaque) s->allocated_sectors, 0); } - if (status == BLK_DATA) { +retry: + copy_range = s->copy_range && s->status == BLK_DATA; + if (status == BLK_DATA && !copy_range) { ret = convert_co_read(s, sector_num, n, buf); if (ret < 0) { error_report("error while reading sector %" PRId64 @@ -1810,7 +1895,15 @@ static void coroutine_fn convert_co_do_copy(void *opaque) } if (s->ret == -EINPROGRESS) { - ret = convert_co_write(s, sector_num, n, buf, status); + if (copy_range) { + ret = convert_co_copy_range(s, sector_num, n); + if (ret) { + s->copy_range = false; + goto retry; + } + } else { + ret = convert_co_write(s, sector_num, n, buf, status); + } if (ret < 0) { error_report("error while writing sector %" PRId64 ": %s", sector_num, strerror(-ret)); @@ -1912,6 +2005,8 @@ static int convert_do_copy(ImgConvertState *s) return s->ret; } +#define MAX_BUF_SECTORS 32768 + static int img_convert(int argc, char **argv) { int c, bs_i, flags, src_flags = 0; @@ -1929,10 +2024,12 @@ static int img_convert(int argc, char **argv) skip_create = false, progress = false, tgt_image_opts = false; int64_t ret = -EINVAL; bool force_share = false; + bool explict_min_sparse = false; ImgConvertState s = (ImgConvertState) { /* Need at least 4k of zeros for sparse detection */ .min_sparse = 8, + .copy_range = false, .buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE, .wr_in_order = true, .num_coroutines = 8, @@ -1947,7 +2044,7 @@ static int img_convert(int argc, char **argv) {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:O:B:co:s:l:S:pt:T:qnm:WU", + c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", long_options, NULL); if (c == -1) { break; @@ -1971,6 +2068,9 @@ static int img_convert(int argc, char **argv) case 'B': out_baseimg = optarg; break; + case 'C': + s.copy_range = true; + break; case 'c': s.compressed = true; break; @@ -1987,9 +2087,6 @@ static int img_convert(int argc, char **argv) g_free(old_options); } break; - case 's': - snapshot_name = optarg; - break; case 'l': if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { sn_opts = qemu_opts_parse_noisily(&internal_snapshot_opts, @@ -2008,12 +2105,17 @@ static int img_convert(int argc, char **argv) int64_t sval; sval = cvtnum(optarg); - if (sval < 0) { - error_report("Invalid minimum zero buffer size for sparse output specified"); + if (sval < 0 || sval & (BDRV_SECTOR_SIZE - 1) || + sval / BDRV_SECTOR_SIZE > MAX_BUF_SECTORS) { + error_report("Invalid buffer size for sparse output specified. " + "Valid sizes are multiples of %llu up to %llu. Select " + "0 to disable sparse detection (fully allocates output).", + BDRV_SECTOR_SIZE, MAX_BUF_SECTORS * BDRV_SECTOR_SIZE); goto fail_getopt; } s.min_sparse = sval / BDRV_SECTOR_SIZE; + explict_min_sparse = true; break; } case 'p': @@ -2073,8 +2175,13 @@ static int img_convert(int argc, char **argv) goto fail_getopt; } - if (!s.wr_in_order && s.compressed) { - error_report("Out of order write and compress are mutually exclusive"); + if (s.compressed && s.copy_range) { + error_report("Cannot enable copy offloading when -c is used"); + goto fail_getopt; + } + + if (explict_min_sparse && s.copy_range) { + error_report("Cannot enable copy offloading when -S is used"); goto fail_getopt; } @@ -2297,14 +2404,21 @@ static int img_convert(int argc, char **argv) } /* increase bufsectors from the default 4096 (2M) if opt_transfer - * or discard_alignment of the out_bs is greater. Limit to 32768 (16MB) - * as maximum. */ - s.buf_sectors = MIN(32768, + * or discard_alignment of the out_bs is greater. Limit to + * MAX_BUF_SECTORS as maximum which is currently 32768 (16MB). */ + s.buf_sectors = MIN(MAX_BUF_SECTORS, MAX(s.buf_sectors, MAX(out_bs->bl.opt_transfer >> BDRV_SECTOR_BITS, out_bs->bl.pdiscard_alignment >> BDRV_SECTOR_BITS))); + /* try to align the write requests to the destination to avoid unnecessary + * RMW cycles. */ + s.alignment = MAX(pow2floor(s.min_sparse), + DIV_ROUND_UP(out_bs->bl.request_alignment, + BDRV_SECTOR_SIZE)); + assert(is_power_of_2(s.alignment)); + if (skip_create) { int64_t output_sectors = blk_nb_sectors(s.target); if (output_sectors < 0) { @@ -2319,6 +2433,16 @@ static int img_convert(int argc, char **argv) } } + if (s.target_has_backing) { + /* Errors are treated as "backing length unknown" (which means + * s.target_backing_sectors has to be negative, which it will + * be automatically). The backing file length is used only + * for optimizations, so such a case is not fatal. */ + s.target_backing_sectors = bdrv_nb_sectors(out_bs->backing->bs); + } else { + s.target_backing_sectors = -1; + } + ret = bdrv_get_info(out_bs, &bdi); if (ret < 0) { if (s.compressed) { @@ -2328,6 +2452,7 @@ static int img_convert(int argc, char **argv) } else { s.compressed = s.compressed || bdi.needs_compressed_writes; s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; + s.unallocated_blocks_are_zero = bdi.unallocated_blocks_are_zero; } ret = convert_do_copy(&s); @@ -2384,9 +2509,9 @@ static void dump_json_image_info_list(ImageInfoList *list) str = qobject_to_json_pretty(obj); assert(str != NULL); printf("%s\n", qstring_get_str(str)); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); - QDECREF(str); + qobject_unref(str); } static void dump_json_image_info(ImageInfo *info) @@ -2400,9 +2525,9 @@ static void dump_json_image_info(ImageInfo *info) str = qobject_to_json_pretty(obj); assert(str != NULL); printf("%s\n", qstring_get_str(str)); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); - QDECREF(str); + qobject_unref(str); } static void dump_human_image_info_list(ImageInfoList *list) @@ -2827,7 +2952,7 @@ static int img_map(int argc, char **argv) int64_t n; /* Probe up to 1 GiB at a time. */ - n = QEMU_ALIGN_DOWN(MIN(1 << 30, length - offset), BDRV_SECTOR_SIZE); + n = MIN(1 << 30, length - offset); ret = get_block_status(bs, offset, n, &next); if (ret < 0) { @@ -3191,6 +3316,9 @@ static int img_rebase(int argc, char **argv) } if (out_baseimg[0]) { + const char *overlay_filename; + char *out_real_path; + options = qdict_new(); if (out_basefmt) { qdict_put_str(options, "driver", out_basefmt); @@ -3199,8 +3327,26 @@ static int img_rebase(int argc, char **argv) qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); } - blk_new_backing = blk_new_open(out_baseimg, NULL, + overlay_filename = bs->exact_filename[0] ? bs->exact_filename + : bs->filename; + out_real_path = g_malloc(PATH_MAX); + + bdrv_get_full_backing_filename_from_filename(overlay_filename, + out_baseimg, + out_real_path, + PATH_MAX, + &local_err); + if (local_err) { + error_reportf_err(local_err, + "Could not resolve backing filename: "); + ret = -1; + g_free(out_real_path); + goto out; + } + + blk_new_backing = blk_new_open(out_real_path, NULL, options, src_flags, &local_err); + g_free(out_real_path); if (!blk_new_backing) { error_reportf_err(local_err, "Could not open new backing file '%s': ", @@ -3381,7 +3527,7 @@ static int img_resize(int argc, char **argv) Error *err = NULL; int c, ret, relative; const char *filename, *fmt, *size; - int64_t n, total_size, current_size; + int64_t n, total_size, current_size, new_size; bool quiet = false; BlockBackend *blk = NULL; PreallocMode prealloc = PREALLOC_MODE_OFF; @@ -3557,11 +3703,42 @@ static int img_resize(int argc, char **argv) } ret = blk_truncate(blk, total_size, prealloc, &err); - if (!ret) { - qprintf(quiet, "Image resized.\n"); - } else { + if (ret < 0) { error_report_err(err); + goto out; } + + new_size = blk_getlength(blk); + if (new_size < 0) { + error_report("Failed to verify truncated image length: %s", + strerror(-new_size)); + ret = -1; + goto out; + } + + /* Some block drivers implement a truncation method, but only so + * the user can cause qemu to refresh the image's size from disk. + * The idea is that the user resizes the image outside of qemu and + * then invokes block_resize to inform qemu about it. + * (This includes iscsi and file-posix for device files.) + * Of course, that is not the behavior someone invoking + * qemu-img resize would find useful, so we catch that behavior + * here and tell the user. */ + if (new_size != total_size && new_size == current_size) { + error_report("Image was not resized; resizing may not be supported " + "for this image"); + ret = -1; + goto out; + } + + if (new_size != total_size) { + warn_report("Image should have been resized to %" PRIi64 + " bytes, but was resized to %" PRIi64 " bytes", + total_size, new_size); + } + + qprintf(quiet, "Image resized.\n"); + out: blk_unref(blk); if (ret) { @@ -3577,6 +3754,32 @@ static void amend_status_cb(BlockDriverState *bs, qemu_progress_print(100.f * offset / total_work_size, 0); } +static int print_amend_option_help(const char *format) +{ + BlockDriver *drv; + + /* Find driver and parse its options */ + drv = bdrv_find_format(format); + if (!drv) { + error_report("Unknown file format '%s'", format); + return 1; + } + + if (!drv->bdrv_amend_options) { + error_report("Format driver '%s' does not support option amendment", + format); + return 1; + } + + /* Every driver supporting amendment must have create_opts */ + assert(drv->create_opts); + + printf("Creation options for '%s':\n", format); + qemu_opts_print_help(drv->create_opts); + printf("\nNote that not all of these options may be amendable.\n"); + return 0; +} + static int img_amend(int argc, char **argv) { Error *err = NULL; @@ -3676,7 +3879,7 @@ static int img_amend(int argc, char **argv) if (fmt && has_help_option(options)) { /* If a format is explicitly specified (and possibly no filename is * given), print option help here */ - ret = print_block_option_help(filename, fmt); + ret = print_amend_option_help(fmt); goto out; } @@ -3705,17 +3908,20 @@ static int img_amend(int argc, char **argv) if (has_help_option(options)) { /* If the format was auto-detected, print option help here */ - ret = print_block_option_help(filename, fmt); + ret = print_amend_option_help(fmt); goto out; } - if (!bs->drv->create_opts) { - error_report("Format driver '%s' does not support any options to amend", + if (!bs->drv->bdrv_amend_options) { + error_report("Format driver '%s' does not support option amendment", fmt); ret = -1; goto out; } + /* Every driver supporting amendment must have create_opts */ + assert(bs->drv->create_opts); + create_opts = qemu_opts_append(create_opts, bs->drv->create_opts); opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); qemu_opts_do_parse(opts, options, NULL, &err); @@ -3727,10 +3933,10 @@ static int img_amend(int argc, char **argv) /* In case the driver does not call amend_status_cb() */ qemu_progress_print(0.f, 0); - ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL); + ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL, &err); qemu_progress_print(100.f, 0); if (ret < 0) { - error_report("Error while amending options: %s", strerror(-ret)); + error_report_err(err); goto out; } @@ -4457,9 +4663,9 @@ static void dump_json_block_measure_info(BlockMeasureInfo *info) str = qobject_to_json_pretty(obj); assert(str != NULL); printf("%s\n", qstring_get_str(str)); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); - QDECREF(str); + qobject_unref(str); } static int img_measure(int argc, char **argv) @@ -4685,7 +4891,6 @@ static const img_cmd_t img_cmds[] = { { option, callback }, #include "qemu-img-cmds.h" #undef DEF -#undef GEN_DOCS { NULL, NULL, }, }; diff --git a/qemu-img.texi b/qemu-img.texi index 8a26400adb..3b6710a580 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -61,9 +61,6 @@ by the used format or see the format descriptions below for details. is param used for internal snapshot, format is 'snapshot.id=[ID],snapshot.name=[NAME]' or '[ID_OR_NAME]' -@item snapshot_id_or_name -is deprecated, use snapshot_param instead - @end table @table @option @@ -172,6 +169,12 @@ Number of parallel coroutines for the convert process Allow out-of-order writes to the destination. This option improves performance, but is only recommended for preallocated devices like host devices or other raw block devices. +@item -C +Try to use copy offloading to move data from source image to target. This may +improve performance if the data is remote, such as with NFS or iSCSI backends, +but will not automatically sparsify zero sectors, and may result in a fully +allocated target image depending on the host support for getting allocation +information. @end table Parameters to dd subcommand: @@ -193,7 +196,13 @@ sets the number of input blocks to skip Command description: @table @option -@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] @var{filename} + +@item amend [--object @var{objectdef}] [--image-opts] [-p] [-p] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} + +Amends the image format specific @var{options} for the image file +@var{filename}. Not all file formats support this operation. + +@item bench [-c @var{count}] [-d @var{depth}] [-f @var{fmt}] [--flush-interval=@var{flush_interval}] [-n] [--no-drain] [-o @var{offset}] [--pattern=@var{pattern}] [-q] [-s @var{buffer_size}] [-S @var{step_size}] [-t @var{cache}] [-w] [-U] @var{filename} Run a simple sequential I/O benchmark on the specified image. If @code{-w} is specified, a write test is performed, otherwise a read test is performed. @@ -217,7 +226,7 @@ specified as well. For write tests, by default a buffer filled with zeros is written. This can be overridden with a pattern byte specified by @var{pattern}. -@item check [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] @var{filename} +@item check [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [--output=@var{ofmt}] [-r [leaks | all]] [-T @var{src_cache}] [-U] @var{filename} Perform a consistency check on the disk image @var{filename}. The command can output in the format @var{ofmt} which is either @code{human} or @code{json}. @@ -253,31 +262,7 @@ If @code{-r} is specified, exit codes representing the image state refer to the state after (the attempt at) repairing it. That is, a successful @code{-r all} will yield the exit code 0, independently of the image state before. -@item create [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] - -Create the new disk image @var{filename} of size @var{size} and format -@var{fmt}. Depending on the file format, you can add one or more @var{options} -that enable additional features of this format. - -If the option @var{backing_file} is specified, then the image will record -only the differences from @var{backing_file}. No size needs to be specified in -this case. @var{backing_file} will never be modified unless you use the -@code{commit} monitor command (or qemu-img commit). - -If a relative path name is given, the backing file is looked up relative to -the directory containing @var{filename}. - -Note that a given backing file will be opened to check that it is valid. Use -the @code{-u} option to enable unsafe backing file mode, which means that the -image will be created even if the associated backing file cannot be opened. A -matching backing file must be created or additional options be used to make the -backing file specification valid when you want to use an image created this -way. - -The size can also be specified using the @var{size} option with @code{-o}, -it doesn't need to be specified separately in this case. - -@item commit [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} +@item commit [--object @var{objectdef}] [--image-opts] [-q] [-f @var{fmt}] [-t @var{cache}] [-b @var{base}] [-d] [-p] @var{filename} Commit the changes recorded in @var{filename} in its base image or backing file. If the backing file is smaller than the snapshot, then the backing file will be @@ -299,7 +284,7 @@ all images between @var{base} and the top image will be invalid and may return garbage data when read. For this reason, @code{-b} implies @code{-d} (so that the top image stays valid). -@item compare [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-s] [-q] @var{filename1} @var{filename2} +@item compare [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [-F @var{fmt}] [-T @var{src_cache}] [-p] [-q] [-s] [-U] @var{filename1} @var{filename2} Check if two images have the same content. You can compare images with different format or settings. @@ -340,9 +325,9 @@ Error on reading data @end table -@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-s @var{snapshot_id_or_name}] [-l @var{snapshot_param}] [-m @var{num_coroutines}] [-W] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} -Convert the disk image @var{filename} or a snapshot @var{snapshot_param}(@var{snapshot_id_or_name} is deprecated) +Convert the disk image @var{filename} or a snapshot @var{snapshot_param} to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} option) or use any format specific options like encryption (@code{-o} option). @@ -381,7 +366,31 @@ creating compressed images. @var{num_coroutines} specifies how many coroutines work in parallel during the convert process (defaults to 8). -@item dd [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output} +@item create [--object @var{objectdef}] [-q] [-f @var{fmt}] [-b @var{backing_file}] [-F @var{backing_fmt}] [-u] [-o @var{options}] @var{filename} [@var{size}] + +Create the new disk image @var{filename} of size @var{size} and format +@var{fmt}. Depending on the file format, you can add one or more @var{options} +that enable additional features of this format. + +If the option @var{backing_file} is specified, then the image will record +only the differences from @var{backing_file}. No size needs to be specified in +this case. @var{backing_file} will never be modified unless you use the +@code{commit} monitor command (or qemu-img commit). + +If a relative path name is given, the backing file is looked up relative to +the directory containing @var{filename}. + +Note that a given backing file will be opened to check that it is valid. Use +the @code{-u} option to enable unsafe backing file mode, which means that the +image will be created even if the associated backing file cannot be opened. A +matching backing file must be created or additional options be used to make the +backing file specification valid when you want to use an image created this +way. + +The size can also be specified using the @var{size} option with @code{-o}, +it doesn't need to be specified separately in this case. + +@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output} Dd copies from @var{input} file to @var{output} file converting it from @var{fmt} format to @var{output_fmt} format. @@ -392,7 +401,7 @@ dd will stop reading input after reading @var{blocks} input blocks. The size syntax is similar to dd(1)'s size syntax. -@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename} +@item info [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] [-U] @var{filename} Give information about the disk image @var{filename}. Use it in particular to know the size reserved on disk which can be different @@ -500,11 +509,11 @@ been written to all sectors. This is the maximum size that the image file can occupy with the exception of internal snapshots, dirty bitmaps, vmstate data, and other advanced image format features. -@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename} +@item snapshot [--object @var{objectdef}] [--image-opts] [-U] [-q] [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} List, apply, create or delete snapshots in image @var{filename}. -@item rebase [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} +@item rebase [--object @var{objectdef}] [--image-opts] [-U] [-q] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-p] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} Changes the backing file of an image. Only the formats @code{qcow2} and @code{qed} support changing the backing file. @@ -564,7 +573,7 @@ qemu-img rebase -b base.img diff.qcow2 At this point, @code{modified.img} can be discarded, since @code{base.img + diff.qcow2} contains the same information. -@item resize [--shrink] [--preallocation=@var{prealloc}] @var{filename} [+ | -]@var{size} +@item resize [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--preallocation=@var{prealloc}] [-q] [--shrink] @var{filename} [+ | -]@var{size} Change the disk image as if it had been created with @var{size}. @@ -585,10 +594,6 @@ how the additional image area should be allocated on the host. See the format description in the @code{NOTES} section which values are allowed. Using this option may result in slightly more data being allocated than necessary. -@item amend [-p] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename} - -Amends the image format specific @var{options} for the image file -@var{filename}. Not all file formats support this operation. @end table @c man end diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 9b3cd00af6..5bf5f28178 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -48,10 +48,9 @@ void qemuio_add_command(const cmdinfo_t *ci) qsort(cmdtab, ncmds, sizeof(*cmdtab), compare_cmdname); } -int qemuio_command_usage(const cmdinfo_t *ci) +void qemuio_command_usage(const cmdinfo_t *ci) { printf("%s %s -- %s\n", ci->name, ci->args, ci->oneline); - return 0; } static int init_check_command(BlockBackend *blk, const cmdinfo_t *ct) @@ -72,7 +71,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, char *cmd = argv[0]; if (!init_check_command(blk, ct)) { - return 0; + return -EINVAL; } if (argc - 1 < ct->argmin || (ct->argmax != -1 && argc - 1 > ct->argmax)) { @@ -89,7 +88,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, "bad argument count %d to %s, expected between %d and %d arguments\n", argc-1, cmd, ct->argmin, ct->argmax); } - return 0; + return -EINVAL; } /* Request additional permissions if necessary for this command. The caller @@ -109,7 +108,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, ret = blk_set_perm(blk, new_perm, orig_shared_perm, &local_err); if (ret < 0) { error_report_err(local_err); - return 0; + return ret; } } } @@ -652,7 +651,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) struct timeval t1, t2; bool Cflag = false, qflag = false, vflag = false; bool Pflag = false, sflag = false, lflag = false, bflag = false; - int c, cnt; + int c, cnt, ret; char *buf; int64_t offset; int64_t count; @@ -674,7 +673,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) pattern_count = cvtnum(optarg); if (pattern_count < 0) { print_cvtnum_err(pattern_count, optarg); - return 0; + return pattern_count; } break; case 'p': @@ -684,7 +683,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) Pflag = true; pattern = parse_pattern(optarg); if (pattern < 0) { - return 0; + return -EINVAL; } break; case 'q': @@ -695,40 +694,43 @@ static int read_f(BlockBackend *blk, int argc, char **argv) pattern_offset = cvtnum(optarg); if (pattern_offset < 0) { print_cvtnum_err(pattern_offset, optarg); - return 0; + return pattern_offset; } break; case 'v': vflag = true; break; default: - return qemuio_command_usage(&read_cmd); + qemuio_command_usage(&read_cmd); + return -EINVAL; } } if (optind != argc - 2) { - return qemuio_command_usage(&read_cmd); + qemuio_command_usage(&read_cmd); + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return offset; } optind++; count = cvtnum(argv[optind]); if (count < 0) { print_cvtnum_err(count, argv[optind]); - return 0; + return count; } else if (count > BDRV_REQUEST_MAX_BYTES) { printf("length cannot exceed %" PRIu64 ", given %s\n", (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); - return 0; + return -EINVAL; } if (!Pflag && (lflag || sflag)) { - return qemuio_command_usage(&read_cmd); + qemuio_command_usage(&read_cmd); + return -EINVAL; } if (!lflag) { @@ -737,19 +739,19 @@ static int read_f(BlockBackend *blk, int argc, char **argv) if ((pattern_count < 0) || (pattern_count + pattern_offset > count)) { printf("pattern verification range exceeds end of read data\n"); - return 0; + return -EINVAL; } if (bflag) { if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", offset); - return 0; + return -EINVAL; } if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { printf("%"PRId64" is not a sector-aligned value for 'count'\n", count); - return 0; + return -EINVAL; } } @@ -757,16 +759,19 @@ static int read_f(BlockBackend *blk, int argc, char **argv) gettimeofday(&t1, NULL); if (bflag) { - cnt = do_load_vmstate(blk, buf, offset, count, &total); + ret = do_load_vmstate(blk, buf, offset, count, &total); } else { - cnt = do_pread(blk, buf, offset, count, &total); + ret = do_pread(blk, buf, offset, count, &total); } gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("read failed: %s\n", strerror(-cnt)); + if (ret < 0) { + printf("read failed: %s\n", strerror(-ret)); goto out; } + cnt = ret; + + ret = 0; if (Pflag) { void *cmp_buf = g_malloc(pattern_count); @@ -775,6 +780,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) printf("Pattern verification failed at offset %" PRId64 ", %"PRId64" bytes\n", offset + pattern_offset, pattern_count); + ret = -EINVAL; } g_free(cmp_buf); } @@ -793,8 +799,7 @@ static int read_f(BlockBackend *blk, int argc, char **argv) out: qemu_io_free(buf); - - return 0; + return ret; } static void readv_help(void) @@ -832,7 +837,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) { struct timeval t1, t2; bool Cflag = false, qflag = false, vflag = false; - int c, cnt; + int c, cnt, ret; char *buf; int64_t offset; /* Some compilers get confused and warn if this is not initialized. */ @@ -851,7 +856,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) Pflag = true; pattern = parse_pattern(optarg); if (pattern < 0) { - return 0; + return -EINVAL; } break; case 'q': @@ -861,36 +866,41 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) vflag = true; break; default: - return qemuio_command_usage(&readv_cmd); + qemuio_command_usage(&readv_cmd); + return -EINVAL; } } if (optind > argc - 2) { - return qemuio_command_usage(&readv_cmd); + qemuio_command_usage(&readv_cmd); + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return offset; } optind++; nr_iov = argc - optind; buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, 0xab); if (buf == NULL) { - return 0; + return -EINVAL; } gettimeofday(&t1, NULL); - cnt = do_aio_readv(blk, &qiov, offset, &total); + ret = do_aio_readv(blk, &qiov, offset, &total); gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("readv failed: %s\n", strerror(-cnt)); + if (ret < 0) { + printf("readv failed: %s\n", strerror(-ret)); goto out; } + cnt = ret; + + ret = 0; if (Pflag) { void *cmp_buf = g_malloc(qiov.size); @@ -898,6 +908,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) if (memcmp(buf, cmp_buf, qiov.size)) { printf("Pattern verification failed at offset %" PRId64 ", %zd bytes\n", offset, qiov.size); + ret = -EINVAL; } g_free(cmp_buf); } @@ -917,7 +928,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) out: qemu_iovec_destroy(&qiov); qemu_io_free(buf); - return 0; + return ret; } static void write_help(void) @@ -963,7 +974,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) bool Cflag = false, qflag = false, bflag = false; bool Pflag = false, zflag = false, cflag = false; int flags = 0; - int c, cnt; + int c, cnt, ret; char *buf = NULL; int64_t offset; int64_t count; @@ -992,7 +1003,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) Pflag = true; pattern = parse_pattern(optarg); if (pattern < 0) { - return 0; + return -EINVAL; } break; case 'q': @@ -1005,62 +1016,64 @@ static int write_f(BlockBackend *blk, int argc, char **argv) zflag = true; break; default: - return qemuio_command_usage(&write_cmd); + qemuio_command_usage(&write_cmd); + return -EINVAL; } } if (optind != argc - 2) { - return qemuio_command_usage(&write_cmd); + qemuio_command_usage(&write_cmd); + return -EINVAL; } if (bflag && zflag) { printf("-b and -z cannot be specified at the same time\n"); - return 0; + return -EINVAL; } if ((flags & BDRV_REQ_FUA) && (bflag || cflag)) { printf("-f and -b or -c cannot be specified at the same time\n"); - return 0; + return -EINVAL; } if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) { printf("-u requires -z to be specified\n"); - return 0; + return -EINVAL; } if (zflag && Pflag) { printf("-z and -P cannot be specified at the same time\n"); - return 0; + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return offset; } optind++; count = cvtnum(argv[optind]); if (count < 0) { print_cvtnum_err(count, argv[optind]); - return 0; + return count; } else if (count > BDRV_REQUEST_MAX_BYTES) { printf("length cannot exceed %" PRIu64 ", given %s\n", (uint64_t)BDRV_REQUEST_MAX_BYTES, argv[optind]); - return 0; + return -EINVAL; } if (bflag || cflag) { if (!QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE)) { printf("%" PRId64 " is not a sector-aligned value for 'offset'\n", offset); - return 0; + return -EINVAL; } if (!QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE)) { printf("%"PRId64" is not a sector-aligned value for 'count'\n", count); - return 0; + return -EINVAL; } } @@ -1070,20 +1083,23 @@ static int write_f(BlockBackend *blk, int argc, char **argv) gettimeofday(&t1, NULL); if (bflag) { - cnt = do_save_vmstate(blk, buf, offset, count, &total); + ret = do_save_vmstate(blk, buf, offset, count, &total); } else if (zflag) { - cnt = do_co_pwrite_zeroes(blk, offset, count, flags, &total); + ret = do_co_pwrite_zeroes(blk, offset, count, flags, &total); } else if (cflag) { - cnt = do_write_compressed(blk, buf, offset, count, &total); + ret = do_write_compressed(blk, buf, offset, count, &total); } else { - cnt = do_pwrite(blk, buf, offset, count, flags, &total); + ret = do_pwrite(blk, buf, offset, count, flags, &total); } gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("write failed: %s\n", strerror(-cnt)); + if (ret < 0) { + printf("write failed: %s\n", strerror(-ret)); goto out; } + cnt = ret; + + ret = 0; if (qflag) { goto out; @@ -1097,8 +1113,7 @@ out: if (!zflag) { qemu_io_free(buf); } - - return 0; + return ret; } static void @@ -1138,7 +1153,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) struct timeval t1, t2; bool Cflag = false, qflag = false; int flags = 0; - int c, cnt; + int c, cnt, ret; char *buf; int64_t offset; /* Some compilers get confused and warn if this is not initialized. */ @@ -1161,39 +1176,44 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) case 'P': pattern = parse_pattern(optarg); if (pattern < 0) { - return 0; + return -EINVAL; } break; default: - return qemuio_command_usage(&writev_cmd); + qemuio_command_usage(&writev_cmd); + return -EINVAL; } } if (optind > argc - 2) { - return qemuio_command_usage(&writev_cmd); + qemuio_command_usage(&writev_cmd); + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return offset; } optind++; nr_iov = argc - optind; buf = create_iovec(blk, &qiov, &argv[optind], nr_iov, pattern); if (buf == NULL) { - return 0; + return -EINVAL; } gettimeofday(&t1, NULL); - cnt = do_aio_writev(blk, &qiov, offset, flags, &total); + ret = do_aio_writev(blk, &qiov, offset, flags, &total); gettimeofday(&t2, NULL); - if (cnt < 0) { - printf("writev failed: %s\n", strerror(-cnt)); + if (ret < 0) { + printf("writev failed: %s\n", strerror(-ret)); goto out; } + cnt = ret; + + ret = 0; if (qflag) { goto out; @@ -1205,7 +1225,7 @@ static int writev_f(BlockBackend *blk, int argc, char **argv) out: qemu_iovec_destroy(&qiov); qemu_io_free(buf); - return 0; + return ret; } struct aio_ctx { @@ -1312,6 +1332,9 @@ static void aio_read_help(void) " standard output stream (with -v option) for subsequent inspection.\n" " The read is performed asynchronously and the aio_flush command must be\n" " used to ensure all outstanding aio requests have been completed.\n" +" Note that due to its asynchronous nature, this command will be\n" +" considered successful once the request is submitted, independently\n" +" of potential I/O errors or pattern mismatches.\n" " -C, -- report statistics in a machine parsable format\n" " -P, -- use a pattern to verify read data\n" " -i, -- treat request as invalid, for exercising stats\n" @@ -1348,7 +1371,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) ctx->pattern = parse_pattern(optarg); if (ctx->pattern < 0) { g_free(ctx); - return 0; + return -EINVAL; } break; case 'i': @@ -1364,20 +1387,23 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) break; default: g_free(ctx); - return qemuio_command_usage(&aio_read_cmd); + qemuio_command_usage(&aio_read_cmd); + return -EINVAL; } } if (optind > argc - 2) { g_free(ctx); - return qemuio_command_usage(&aio_read_cmd); + qemuio_command_usage(&aio_read_cmd); + return -EINVAL; } ctx->offset = cvtnum(argv[optind]); if (ctx->offset < 0) { - print_cvtnum_err(ctx->offset, argv[optind]); + int ret = ctx->offset; + print_cvtnum_err(ret, argv[optind]); g_free(ctx); - return 0; + return ret; } optind++; @@ -1386,7 +1412,7 @@ static int aio_read_f(BlockBackend *blk, int argc, char **argv) if (ctx->buf == NULL) { block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_READ); g_free(ctx); - return 0; + return -EINVAL; } gettimeofday(&ctx->t1, NULL); @@ -1410,6 +1436,9 @@ static void aio_write_help(void) " filled with a set pattern (0xcdcdcdcd).\n" " The write is performed asynchronously and the aio_flush command must be\n" " used to ensure all outstanding aio requests have been completed.\n" +" Note that due to its asynchronous nature, this command will be\n" +" considered successful once the request is submitted, independently\n" +" of potential I/O errors or pattern mismatches.\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" " -f, -- use Force Unit Access semantics\n" @@ -1459,7 +1488,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) pattern = parse_pattern(optarg); if (pattern < 0) { g_free(ctx); - return 0; + return -EINVAL; } break; case 'i': @@ -1472,38 +1501,41 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) break; default: g_free(ctx); - return qemuio_command_usage(&aio_write_cmd); + qemuio_command_usage(&aio_write_cmd); + return -EINVAL; } } if (optind > argc - 2) { g_free(ctx); - return qemuio_command_usage(&aio_write_cmd); + qemuio_command_usage(&aio_write_cmd); + return -EINVAL; } if (ctx->zflag && optind != argc - 2) { printf("-z supports only a single length parameter\n"); g_free(ctx); - return 0; + return -EINVAL; } if ((flags & BDRV_REQ_MAY_UNMAP) && !ctx->zflag) { printf("-u requires -z to be specified\n"); g_free(ctx); - return 0; + return -EINVAL; } if (ctx->zflag && ctx->Pflag) { printf("-z and -P cannot be specified at the same time\n"); g_free(ctx); - return 0; + return -EINVAL; } ctx->offset = cvtnum(argv[optind]); if (ctx->offset < 0) { - print_cvtnum_err(ctx->offset, argv[optind]); + int ret = ctx->offset; + print_cvtnum_err(ret, argv[optind]); g_free(ctx); - return 0; + return ret; } optind++; @@ -1512,7 +1544,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) if (count < 0) { print_cvtnum_err(count, argv[optind]); g_free(ctx); - return 0; + return count; } ctx->qiov.size = count; @@ -1525,7 +1557,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) if (ctx->buf == NULL) { block_acct_invalid(blk_get_stats(blk), BLOCK_ACCT_WRITE); g_free(ctx); - return 0; + return -EINVAL; } gettimeofday(&ctx->t1, NULL); @@ -1535,6 +1567,7 @@ static int aio_write_f(BlockBackend *blk, int argc, char **argv) blk_aio_pwritev(blk, ctx->offset, &ctx->qiov, flags, aio_write_done, ctx); } + return 0; } @@ -1555,8 +1588,7 @@ static const cmdinfo_t aio_flush_cmd = { static int flush_f(BlockBackend *blk, int argc, char **argv) { - blk_flush(blk); - return 0; + return blk_flush(blk); } static const cmdinfo_t flush_cmd = { @@ -1575,13 +1607,13 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv) offset = cvtnum(argv[1]); if (offset < 0) { print_cvtnum_err(offset, argv[1]); - return 0; + return offset; } ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err); if (ret < 0) { error_report_err(local_err); - return 0; + return ret; } return 0; @@ -1606,7 +1638,7 @@ static int length_f(BlockBackend *blk, int argc, char **argv) size = blk_getlength(blk); if (size < 0) { printf("getlength: %s\n", strerror(-size)); - return 0; + return size; } cvtstr(size, s1, sizeof(s1)); @@ -1640,7 +1672,7 @@ static int info_f(BlockBackend *blk, int argc, char **argv) ret = bdrv_get_info(bs, &bdi); if (ret) { - return 0; + return ret; } cvtstr(bdi.cluster_size, s1, sizeof(s1)); @@ -1713,30 +1745,32 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) qflag = true; break; default: - return qemuio_command_usage(&discard_cmd); + qemuio_command_usage(&discard_cmd); + return -EINVAL; } } if (optind != argc - 2) { - return qemuio_command_usage(&discard_cmd); + qemuio_command_usage(&discard_cmd); + return -EINVAL; } offset = cvtnum(argv[optind]); if (offset < 0) { print_cvtnum_err(offset, argv[optind]); - return 0; + return offset; } optind++; bytes = cvtnum(argv[optind]); if (bytes < 0) { print_cvtnum_err(bytes, argv[optind]); - return 0; + return bytes; } else if (bytes >> BDRV_SECTOR_BITS > BDRV_REQUEST_MAX_SECTORS) { printf("length cannot exceed %"PRIu64", given %s\n", (uint64_t)BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS, argv[optind]); - return 0; + return -EINVAL; } gettimeofday(&t1, NULL); @@ -1745,7 +1779,7 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) if (ret < 0) { printf("discard failed: %s\n", strerror(-ret)); - goto out; + return ret; } /* Finally, report back -- -C gives a parsable format */ @@ -1754,7 +1788,6 @@ static int discard_f(BlockBackend *blk, int argc, char **argv) print_report("discard", &t2, offset, bytes, bytes, 1, Cflag); } -out: return 0; } @@ -1769,14 +1802,14 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) start = offset = cvtnum(argv[1]); if (offset < 0) { print_cvtnum_err(offset, argv[1]); - return 0; + return offset; } if (argc == 3) { count = cvtnum(argv[2]); if (count < 0) { print_cvtnum_err(count, argv[2]); - return 0; + return count; } } else { count = BDRV_SECTOR_SIZE; @@ -1788,7 +1821,7 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv) ret = bdrv_is_allocated(bs, offset, remaining, &num); if (ret < 0) { printf("is_allocated failed: %s\n", strerror(-ret)); - return 0; + return ret; } offset += num; remaining -= num; @@ -1863,17 +1896,17 @@ static int map_f(BlockBackend *blk, int argc, char **argv) bytes = blk_getlength(blk); if (bytes < 0) { error_report("Failed to query image length: %s", strerror(-bytes)); - return 0; + return bytes; } while (bytes) { ret = map_is_allocated(blk_bs(blk), offset, bytes, &num); if (ret < 0) { error_report("Failed to get allocation status: %s", strerror(-ret)); - return 0; + return ret; } else if (!num) { error_report("Unexpected end of image"); - return 0; + return -EIO; } retstr = ret ? " allocated" : "not allocated"; @@ -1954,19 +1987,19 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) case 'c': if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { error_report("Invalid cache option: %s", optarg); - return 0; + return -EINVAL; } break; case 'o': if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) { qemu_opts_reset(&reopen_opts); - return 0; + return -EINVAL; } break; case 'r': if (has_rw_option) { error_report("Only one -r/-w option may be given"); - return 0; + return -EINVAL; } flags &= ~BDRV_O_RDWR; has_rw_option = true; @@ -1974,20 +2007,22 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) case 'w': if (has_rw_option) { error_report("Only one -r/-w option may be given"); - return 0; + return -EINVAL; } flags |= BDRV_O_RDWR; has_rw_option = true; break; default: qemu_opts_reset(&reopen_opts); - return qemuio_command_usage(&reopen_cmd); + qemuio_command_usage(&reopen_cmd); + return -EINVAL; } } if (optind != argc) { qemu_opts_reset(&reopen_opts); - return qemuio_command_usage(&reopen_cmd); + qemuio_command_usage(&reopen_cmd); + return -EINVAL; } if (writethrough != blk_enable_write_cache(blk) && @@ -1995,7 +2030,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) { error_report("Cannot change cache.writeback: Device attached"); qemu_opts_reset(&reopen_opts); - return 0; + return -EBUSY; } if (!(flags & BDRV_O_RDWR)) { @@ -2021,10 +2056,10 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) if (local_err) { error_report_err(local_err); - } else { - blk_set_enable_write_cache(blk, !writethrough); + return -EINVAL; } + blk_set_enable_write_cache(blk, !writethrough); return 0; } @@ -2035,6 +2070,7 @@ static int break_f(BlockBackend *blk, int argc, char **argv) ret = bdrv_debug_breakpoint(blk_bs(blk), argv[1], argv[2]); if (ret < 0) { printf("Could not set breakpoint: %s\n", strerror(-ret)); + return ret; } return 0; @@ -2047,6 +2083,7 @@ static int remove_break_f(BlockBackend *blk, int argc, char **argv) ret = bdrv_debug_remove_breakpoint(blk_bs(blk), argv[1]); if (ret < 0) { printf("Could not remove breakpoint %s: %s\n", argv[1], strerror(-ret)); + return ret; } return 0; @@ -2078,6 +2115,7 @@ static int resume_f(BlockBackend *blk, int argc, char **argv) ret = bdrv_debug_resume(blk_bs(blk), argv[1]); if (ret < 0) { printf("Could not resume request: %s\n", strerror(-ret)); + return ret; } return 0; @@ -2097,7 +2135,6 @@ static int wait_break_f(BlockBackend *blk, int argc, char **argv) while (!bdrv_debug_is_suspended(blk_bs(blk), argv[1])) { aio_poll(blk_get_aio_context(blk), true); } - return 0; } @@ -2154,11 +2191,11 @@ static int sigraise_f(BlockBackend *blk, int argc, char **argv) int64_t sig = cvtnum(argv[1]); if (sig < 0) { print_cvtnum_err(sig, argv[1]); - return 0; + return sig; } else if (sig > NSIG) { printf("signal argument '%s' is too large to be a valid signal\n", argv[1]); - return 0; + return -EINVAL; } /* Using raise() to kill this process does not necessarily flush all open @@ -2168,6 +2205,7 @@ static int sigraise_f(BlockBackend *blk, int argc, char **argv) fflush(stderr); raise(sig); + return 0; } @@ -2187,7 +2225,7 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv) ms = strtol(argv[1], &endptr, 0); if (ms < 0 || *endptr != '\0') { printf("%s is not a valid number\n", argv[1]); - return 0; + return -EINVAL; } timer = timer_new_ns(QEMU_CLOCK_HOST, sleep_cb, &expired); @@ -2198,7 +2236,6 @@ static int sleep_f(BlockBackend *blk, int argc, char **argv) } timer_free(timer); - return 0; } @@ -2258,7 +2295,7 @@ static int help_f(BlockBackend *blk, int argc, char **argv) ct = find_command(argv[1]); if (ct == NULL) { printf("command %s not found\n", argv[1]); - return 0; + return -EINVAL; } help_onecmd(argv[1], ct); @@ -2276,14 +2313,14 @@ static const cmdinfo_t help_cmd = { .oneline = "help for one or all commands", }; -bool qemuio_command(BlockBackend *blk, const char *cmd) +int qemuio_command(BlockBackend *blk, const char *cmd) { AioContext *ctx; char *input; const cmdinfo_t *ct; char **v; int c; - bool done = false; + int ret = 0; input = g_strdup(cmd); v = breakline(input, &c); @@ -2292,16 +2329,17 @@ bool qemuio_command(BlockBackend *blk, const char *cmd) if (ct) { ctx = blk ? blk_get_aio_context(blk) : qemu_get_aio_context(); aio_context_acquire(ctx); - done = command(blk, ct, c, v); + ret = command(blk, ct, c, v); aio_context_release(ctx); } else { fprintf(stderr, "command \"%s\" not found\n", v[0]); + ret = -EINVAL; } } g_free(input); g_free(v); - return done; + return ret; } static void __attribute((constructor)) init_qemuio_commands(void) diff --git a/qemu-io.c b/qemu-io.c index e692c555e0..13829f5e21 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -37,6 +37,7 @@ static char *progname; static BlockBackend *qemuio_blk; +static bool quit_qemu_io; /* qemu-io commands passed using -c */ static int ncmdline; @@ -86,7 +87,7 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share, if (qemuio_blk) { error_report("file open already, try 'help close'"); - QDECREF(opts); + qobject_unref(opts); return 1; } @@ -95,12 +96,12 @@ static int openfile(char *name, int flags, bool writethrough, bool force_share, opts = qdict_new(); } if (qdict_haskey(opts, BDRV_OPT_FORCE_SHARE) - && !qdict_get_bool(opts, BDRV_OPT_FORCE_SHARE)) { + && strcmp(qdict_get_str(opts, BDRV_OPT_FORCE_SHARE), "on")) { error_report("-U conflicts with image options"); - QDECREF(opts); + qobject_unref(opts); return 1; } - qdict_put_bool(opts, BDRV_OPT_FORCE_SHARE, true); + qdict_put_str(opts, BDRV_OPT_FORCE_SHARE, "on"); } qemuio_blk = blk_new_open(name, NULL, opts, flags, &local_err); if (!qemuio_blk) { @@ -166,6 +167,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv) int readonly = 0; bool writethrough = true; int c; + int ret; QemuOpts *qopts; QDict *opts; bool force_share = false; @@ -192,25 +194,25 @@ static int open_f(BlockBackend *blk, int argc, char **argv) if (bdrv_parse_cache_mode(optarg, &flags, &writethrough) < 0) { error_report("Invalid cache option: %s", optarg); qemu_opts_reset(&empty_opts); - return 0; + return -EINVAL; } break; case 'd': if (bdrv_parse_discard_flags(optarg, &flags) < 0) { error_report("Invalid discard option: %s", optarg); qemu_opts_reset(&empty_opts); - return 0; + return -EINVAL; } break; case 'o': if (imageOpts) { printf("--image-opts and 'open -o' are mutually exclusive\n"); qemu_opts_reset(&empty_opts); - return 0; + return -EINVAL; } if (!qemu_opts_parse_noisily(&empty_opts, optarg, false)) { qemu_opts_reset(&empty_opts); - return 0; + return -EINVAL; } break; case 'U': @@ -218,7 +220,8 @@ static int open_f(BlockBackend *blk, int argc, char **argv) break; default: qemu_opts_reset(&empty_opts); - return qemuio_command_usage(&open_cmd); + qemuio_command_usage(&open_cmd); + return -EINVAL; } } @@ -229,7 +232,7 @@ static int open_f(BlockBackend *blk, int argc, char **argv) if (imageOpts && (optind == argc - 1)) { if (!qemu_opts_parse_noisily(&empty_opts, argv[optind], false)) { qemu_opts_reset(&empty_opts); - return 0; + return -EINVAL; } optind++; } @@ -239,19 +242,26 @@ static int open_f(BlockBackend *blk, int argc, char **argv) qemu_opts_reset(&empty_opts); if (optind == argc - 1) { - openfile(argv[optind], flags, writethrough, force_share, opts); + ret = openfile(argv[optind], flags, writethrough, force_share, opts); } else if (optind == argc) { - openfile(NULL, flags, writethrough, force_share, opts); + ret = openfile(NULL, flags, writethrough, force_share, opts); } else { - QDECREF(opts); + qobject_unref(opts); qemuio_command_usage(&open_cmd); + return -EINVAL; } + + if (ret) { + return -EINVAL; + } + return 0; } static int quit_f(BlockBackend *blk, int argc, char **argv) { - return 1; + quit_qemu_io = true; + return 0; } static const cmdinfo_t quit_cmd = { @@ -390,20 +400,24 @@ static void prep_fetchline(void *opaque) *fetchable= 1; } -static void command_loop(void) +static int command_loop(void) { - int i, done = 0, fetchable = 0, prompted = 0; + int i, fetchable = 0, prompted = 0; + int ret, last_error = 0; char *input; - for (i = 0; !done && i < ncmdline; i++) { - done = qemuio_command(qemuio_blk, cmdline[i]); + for (i = 0; !quit_qemu_io && i < ncmdline; i++) { + ret = qemuio_command(qemuio_blk, cmdline[i]); + if (ret < 0) { + last_error = ret; + } } if (cmdline) { g_free(cmdline); - return; + return last_error; } - while (!done) { + while (!quit_qemu_io) { if (!prompted) { printf("%s", get_prompt()); fflush(stdout); @@ -421,13 +435,19 @@ static void command_loop(void) if (input == NULL) { break; } - done = qemuio_command(qemuio_blk, input); + ret = qemuio_command(qemuio_blk, input); g_free(input); + if (ret < 0) { + last_error = ret; + } + prompted = 0; fetchable = 0; } qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL); + + return last_error; } static void add_user_command(char *optarg) @@ -492,6 +512,7 @@ int main(int argc, char **argv) int c; int opt_index = 0; int flags = BDRV_O_UNMAP; + int ret; bool writethrough = true; Error *local_error = NULL; QDict *opts = NULL; @@ -653,7 +674,7 @@ int main(int argc, char **argv) } } } - command_loop(); + ret = command_loop(); /* * Make sure all outstanding requests complete before the program exits. @@ -662,5 +683,10 @@ int main(int argc, char **argv) blk_unref(qemuio_blk); g_free(readline_state); - return 0; + + if (ret < 0) { + return 1; + } else { + return 0; + } } diff --git a/qemu-nbd.c b/qemu-nbd.c index 0af0560ad1..51b9d38c72 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -482,6 +482,12 @@ static const char *socket_activation_validate_opts(const char *device, return NULL; } +static void qemu_nbd_shutdown(void) +{ + job_cancel_sync_all(); + bdrv_close_all(); +} + int main(int argc, char **argv) { BlockBackend *blk; @@ -928,7 +934,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } bdrv_init(); - atexit(bdrv_close_all); + atexit(qemu_nbd_shutdown); srcpath = argv[optind]; if (imageOpts) { diff --git a/qemu-option-trace.texi b/qemu-option-trace.texi index 4166d5cdc2..7d1b7f05c5 100644 --- a/qemu-option-trace.texi +++ b/qemu-option-trace.texi @@ -2,9 +2,8 @@ Specify tracing options. @table @option @item [enable=]@var{pattern} -Immediately enable events matching @var{pattern}. -The file must contain one event name (as listed in the @file{trace-events-all} -file) per line; globbing patterns are accepted too. This option is only +Immediately enable events matching @var{pattern} +(either event name or a globbing pattern). This option is only available if QEMU has been compiled with the @var{simple}, @var{log} or @var{ftrace} tracing backend. To specify multiple events or patterns, specify the @option{-trace} option multiple times. diff --git a/qemu-options-wrapper.h b/qemu-options-wrapper.h index 13bfea0294..6f548e3922 100644 --- a/qemu-options-wrapper.h +++ b/qemu-options-wrapper.h @@ -34,7 +34,6 @@ #undef DEF #undef DEFHEADING #undef ARCHHEADING -#undef GEN_DOCS #undef QEMU_OPTIONS_GENERATE_ENUM #undef QEMU_OPTIONS_GENERATE_HELP diff --git a/qemu-options.hx b/qemu-options.hx index ca4e412f2f..b1bf0f485f 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -130,7 +130,7 @@ ETEXI DEF("accel", HAS_ARG, QEMU_OPTION_accel, "-accel [accel=]accelerator[,thread=single|multi]\n" " select accelerator (kvm, xen, hax, hvf, whpx or tcg; use 'help' for a list)\n" - " thread=single|multi (enable multi-threaded TCG)", QEMU_ARCH_ALL) + " thread=single|multi (enable multi-threaded TCG)\n", QEMU_ARCH_ALL) STEXI @item -accel @var{name}[,prop=@var{value}[,...]] @findex -accel @@ -471,7 +471,7 @@ STEXI @item -balloon virtio[,addr=@var{addr}] @findex -balloon Enable virtio balloon device, optionally with PCI address @var{addr}. This -option is deprecated, use @option{--device virtio-balloon} instead. +option is deprecated, use @option{-device virtio-balloon} instead. ETEXI DEF("device", HAS_ARG, QEMU_OPTION_device, @@ -1240,7 +1240,7 @@ ETEXI DEF("display", HAS_ARG, QEMU_OPTION_display, "-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]\n" - " [,window_close=on|off][,gl=on|off]\n" + " [,window_close=on|off][,gl=on|core|es|off]\n" "-display gtk[,grab_on_hover=on|off][,gl=on|off]|\n" "-display vnc=[,]\n" "-display curses\n" @@ -1906,8 +1906,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, "-netdev user,id=str[,ipv4[=on|off]][,net=addr[/mask]][,host=addr]\n" " [,ipv6[=on|off]][,ipv6-net=addr[/int]][,ipv6-host=addr]\n" " [,restrict=on|off][,hostname=host][,dhcpstart=addr]\n" - " [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,tftp=dir]\n" - " [,bootfile=f][,hostfwd=rule][,guestfwd=rule]" + " [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,domainname=domain]\n" + " [,tftp=dir][,bootfile=f][,hostfwd=rule][,guestfwd=rule]" #ifndef _WIN32 "[,smb=dir[,smbserver=addr]]\n" #endif @@ -2003,9 +2003,9 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " configure a vhost-user network, backed by a chardev 'dev'\n" #endif "-netdev hubport,id=str,hubid=n[,netdev=nd]\n" - " configure a hub port on QEMU VLAN 'n'\n", QEMU_ARCH_ALL) + " configure a hub port on the hub with ID 'n'\n", QEMU_ARCH_ALL) DEF("nic", HAS_ARG, QEMU_OPTION_nic, - "--nic [tap|bridge|" + "-nic [tap|bridge|" #ifdef CONFIG_SLIRP "user|" #endif @@ -2024,14 +2024,13 @@ DEF("nic", HAS_ARG, QEMU_OPTION_nic, "socket][,option][,...][mac=macaddr]\n" " initialize an on-board / default host NIC (using MAC address\n" " macaddr) and connect it to the given host network backend\n" - "--nic none use it alone to have zero network devices (the default is to\n" + "-nic none use it alone to have zero network devices (the default is to\n" " provided a 'user' network connection)\n", QEMU_ARCH_ALL) DEF("net", HAS_ARG, QEMU_OPTION_net, - "-net nic[,vlan=n][,netdev=nd][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n" + "-net nic[,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v]\n" " configure or create an on-board (or machine default) NIC and\n" - " connect it either to VLAN 'n' or the netdev 'nd' (for pluggable\n" - " NICs please use '-device devtype,netdev=nd' instead)\n" + " connect it to hub 0 (please use -nic unless you need a hub)\n" "-net [" #ifdef CONFIG_SLIRP "user|" @@ -2044,7 +2043,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, #ifdef CONFIG_NETMAP "netmap|" #endif - "socket][,vlan=n][,option][,option][,...]\n" + "socket][,option][,option][,...]\n" " old way to initialize a host network interface\n" " (use the -netdev option if possible instead)\n", QEMU_ARCH_ALL) STEXI @@ -2136,6 +2135,9 @@ Example: qemu-system-i386 -nic user,dnssearch=mgmt.example.org,dnssearch=example.org @end example +@item domainname=@var{domain} +Specifies the client domain name reported by the built-in DHCP server. + @item tftp=@var{dir} When using the user mode network stack, activate a built-in TFTP server. The files in @var{dir} will be exposed as the root of a TFTP server. @@ -2462,17 +2464,14 @@ qemu -m 512 -object memory-backend-file,id=mem,size=512M,mem-path=/hugetlbfs,sha Create a hub port on the emulated hub with ID @var{hubid}. The hubport netdev lets you connect a NIC to a QEMU emulated hub instead of a -single netdev. @code{-net} and @code{-device} with the parameter @option{vlan} -(deprecated), or @code{-nic hubport} can also be used to connect a -network device or a NIC to a hub. Alternatively, you can also connect the -hubport to another netdev with ID @var{nd} by using the @option{netdev=@var{nd}} -option. +single netdev. Alternatively, you can also connect the hubport to another +netdev with ID @var{nd} by using the @option{netdev=@var{nd}} option. -@item -net nic[,vlan=@var{n}][,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] +@item -net nic[,netdev=@var{nd}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] @findex -net Legacy option to configure or create an on-board (or machine default) Network -Interface Card(NIC) and connect it either to the emulated hub port ("vlan") -with number @var{n} (@var{n} = 0 is the default), or to the netdev @var{nd}. +Interface Card(NIC) and connect it either to the emulated hub with ID 0 (i.e. +the default hub), or to the netdev @var{nd}. The NIC is an e1000 by default on the PC target. Optionally, the MAC address can be changed to @var{mac}, the device address set to @var{addr} (PCI cards only), and a @var{name} can be assigned for use in monitor commands. @@ -2482,11 +2481,10 @@ that the card should have; this option currently only affects virtio cards; set NIC is created. QEMU can emulate several different models of network card. Use @code{-net nic,model=help} for a list of available devices for your target. -@item -net user|tap|bridge|socket|l2tpv3|vde[,...][,vlan=@var{n}][,name=@var{name}] +@item -net user|tap|bridge|socket|l2tpv3|vde[,...][,name=@var{name}] Configure a host network backend (with the options corresponding to the same -@option{-netdev} option) and connect it to the emulated hub ("vlan") with the -number @var{n} (default is number 0). Use @var{name} to specify the name of the -hub port. +@option{-netdev} option) and connect it to the emulated hub 0 (the default +hub). Use @var{name} to specify the name of the hub port. ETEXI STEXI @@ -3304,6 +3302,20 @@ STEXI Run the emulation in single step mode. ETEXI +DEF("preconfig", 0, QEMU_OPTION_preconfig, \ + "--preconfig pause QEMU before machine is initialized (experimental)\n", + QEMU_ARCH_ALL) +STEXI +@item --preconfig +@findex --preconfig +Pause QEMU for interactive configuration before the machine is created, +which allows querying and configuring properties that will affect +machine initialization. Use QMP command 'x-exit-preconfig' to exit +the preconfig state and move to the next state (i.e. run guest if -S +isn't used or pause the second time if -S is used). This option is +experimental. +ETEXI + DEF("S", 0, QEMU_OPTION_S, \ "-S freeze CPU at startup (use 'c' to start execution)\n", QEMU_ARCH_ALL) @@ -3326,6 +3338,30 @@ mlocking qemu and guest memory can be enabled via @option{mlock=on} (enabled by default). ETEXI +DEF("overcommit", HAS_ARG, QEMU_OPTION_overcommit, + "-overcommit [mem-lock=on|off][cpu-pm=on|off]\n" + " run qemu with overcommit hints\n" + " mem-lock=on|off controls memory lock support (default: off)\n" + " cpu-pm=on|off controls cpu power management (default: off)\n", + QEMU_ARCH_ALL) +STEXI +@item -overcommit mem-lock=on|off +@item -overcommit cpu-pm=on|off +@findex -overcommit +Run qemu with hints about host resource overcommit. The default is +to assume that host overcommits all resources. + +Locking qemu and guest memory can be enabled via @option{mem-lock=on} (disabled +by default). This works when host memory is not overcommitted and reduces the +worst-case latency for guest. This is equivalent to @option{realtime}. + +Guest ability to manage power state of host cpus (increasing latency for other +processes on the same host cpu, but decreasing latency for guest) can be +enabled via @option{cpu-pm=on} (disabled by default). This works best when +host CPU is not overcommitted. When used, host estimates of CPU cycle and power +utilization will be incorrect, not taking into account guest idle time. +ETEXI + DEF("gdb", HAS_ARG, QEMU_OPTION_gdb, \ "-gdb dev wait for gdb connection on 'dev'\n", QEMU_ARCH_ALL) STEXI @@ -3422,7 +3458,7 @@ STEXI Enable HAX (Hardware-based Acceleration eXecution) support. This option is only available if HAX support is enabled when compiling. HAX is only applicable to MAC and Windows platform, and thus does not conflict with -KVM. +KVM. This option is deprecated, use @option{-accel hax} instead. ETEXI DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, @@ -3675,10 +3711,7 @@ STEXI @item -virtioconsole @var{c} @findex -virtioconsole Set virtio console. - -This option is maintained for backward compatibility. - -Please use @code{-device virtconsole} for the new way of invocation. +This option is deprecated, please use @option{-device virtconsole} instead. ETEXI DEF("show-cursor", 0, QEMU_OPTION_show_cursor, \ @@ -3765,7 +3798,8 @@ ETEXI #ifndef _WIN32 DEF("runas", HAS_ARG, QEMU_OPTION_runas, \ - "-runas user change to user id user just before starting the VM\n", + "-runas user change to user id user just before starting the VM\n" \ + " user can be numeric uid:gid instead\n", QEMU_ARCH_ALL) #endif STEXI @@ -3916,16 +3950,6 @@ ETEXI HXCOMM Deprecated by -machine accel=tcg property DEF("no-kvm", 0, QEMU_OPTION_no_kvm, "", QEMU_ARCH_I386) -HXCOMM Deprecated by kvm-pit driver properties -DEF("no-kvm-pit-reinjection", 0, QEMU_OPTION_no_kvm_pit_reinjection, - "", QEMU_ARCH_I386) - -HXCOMM Deprecated by -machine kernel_irqchip=on|off property -DEF("no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip, "", QEMU_ARCH_I386) - -HXCOMM Deprecated (ignored) -DEF("tdf", 0, QEMU_OPTION_tdf,"", QEMU_ARCH_ALL) - DEF("msg", HAS_ARG, QEMU_OPTION_msg, "-msg timestamp[=on|off]\n" " change the format of messages\n" @@ -4112,6 +4136,30 @@ expensive operation that consumes random pool entropy, so it is recommended that a persistent set of parameters be generated upfront and saved. +@item -object tls-creds-psk,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/keys/dir}[,username=@var{username}] + +Creates a TLS Pre-Shared Keys (PSK) credentials object, which can be used to provide +TLS support on network backends. The @option{id} parameter is a unique +ID which network backends will use to access the credentials. The +@option{endpoint} is either @option{server} or @option{client} depending +on whether the QEMU network backend that uses the credentials will be +acting as a client or as a server. For clients only, @option{username} +is the username which will be sent to the server. If omitted +it defaults to ``qemu''. + +The @var{dir} parameter tells QEMU where to find the keys file. +It is called ``@var{dir}/keys.psk'' and contains ``username:key'' +pairs. This file can most easily be created using the GnuTLS +@code{psktool} program. + +For server endpoints, @var{dir} may also contain a file +@var{dh-params.pem} providing diffie-hellman parameters to use +for the TLS server. If the file is missing, QEMU will generate +a set of DH parameters at startup. This is a computationally +expensive operation that consumes random pool entropy, so it is +recommended that a persistent set of parameters be generated +up front and saved. + @item -object tls-creds-x509,id=@var{id},endpoint=@var{endpoint},dir=@var{/path/to/cred/dir},priority=@var{priority},verify-peer=@var{on|off},passwordid=@var{id} Creates a TLS anonymous credentials object, which can be used to provide diff --git a/qemu-seccomp.c b/qemu-seccomp.c index b770a77d33..9cd8eb9499 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -13,6 +13,11 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu/osdep.h" +#include "qemu/config-file.h" +#include "qemu/option.h" +#include "qemu/module.h" +#include "qemu/error-report.h" +#include #include #include "sysemu/seccomp.h" @@ -29,6 +34,12 @@ struct QemuSeccompSyscall { int32_t num; uint8_t set; + uint8_t narg; + const struct scmp_arg_cmp *arg_cmp; +}; + +const struct scmp_arg_cmp sched_setscheduler_arg[] = { + SCMP_A1(SCMP_CMP_NE, SCHED_IDLE) }; static const struct QemuSeccompSyscall blacklist[] = { @@ -87,7 +98,8 @@ static const struct QemuSeccompSyscall blacklist[] = { { SCMP_SYS(setpriority), QEMU_SECCOMP_SET_RESOURCECTL }, { SCMP_SYS(sched_setparam), QEMU_SECCOMP_SET_RESOURCECTL }, { SCMP_SYS(sched_getparam), QEMU_SECCOMP_SET_RESOURCECTL }, - { SCMP_SYS(sched_setscheduler), QEMU_SECCOMP_SET_RESOURCECTL }, + { SCMP_SYS(sched_setscheduler), QEMU_SECCOMP_SET_RESOURCECTL, + ARRAY_SIZE(sched_setscheduler_arg), sched_setscheduler_arg }, { SCMP_SYS(sched_getscheduler), QEMU_SECCOMP_SET_RESOURCECTL }, { SCMP_SYS(sched_setaffinity), QEMU_SECCOMP_SET_RESOURCECTL }, { SCMP_SYS(sched_getaffinity), QEMU_SECCOMP_SET_RESOURCECTL }, @@ -96,7 +108,7 @@ static const struct QemuSeccompSyscall blacklist[] = { }; -int seccomp_start(uint32_t seccomp_opts) +static int seccomp_start(uint32_t seccomp_opts) { int rc = 0; unsigned int i = 0; @@ -113,7 +125,8 @@ int seccomp_start(uint32_t seccomp_opts) continue; } - rc = seccomp_rule_add(ctx, SCMP_ACT_KILL, blacklist[i].num, 0); + rc = seccomp_rule_add_array(ctx, SCMP_ACT_KILL, blacklist[i].num, + blacklist[i].narg, blacklist[i].arg_cmp); if (rc < 0) { goto seccomp_return; } @@ -125,3 +138,117 @@ int seccomp_start(uint32_t seccomp_opts) seccomp_release(ctx); return rc; } + +#ifdef CONFIG_SECCOMP +int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) +{ + if (qemu_opt_get_bool(opts, "enable", false)) { + uint32_t seccomp_opts = QEMU_SECCOMP_SET_DEFAULT + | QEMU_SECCOMP_SET_OBSOLETE; + const char *value = NULL; + + value = qemu_opt_get(opts, "obsolete"); + if (value) { + if (g_str_equal(value, "allow")) { + seccomp_opts &= ~QEMU_SECCOMP_SET_OBSOLETE; + } else if (g_str_equal(value, "deny")) { + /* this is the default option, this if is here + * to provide a little bit of consistency for + * the command line */ + } else { + error_report("invalid argument for obsolete"); + return -1; + } + } + + value = qemu_opt_get(opts, "elevateprivileges"); + if (value) { + if (g_str_equal(value, "deny")) { + seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; + } else if (g_str_equal(value, "children")) { + seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; + + /* calling prctl directly because we're + * not sure if host has CAP_SYS_ADMIN set*/ + if (prctl(PR_SET_NO_NEW_PRIVS, 1)) { + error_report("failed to set no_new_privs " + "aborting"); + return -1; + } + } else if (g_str_equal(value, "allow")) { + /* default value */ + } else { + error_report("invalid argument for elevateprivileges"); + return -1; + } + } + + value = qemu_opt_get(opts, "spawn"); + if (value) { + if (g_str_equal(value, "deny")) { + seccomp_opts |= QEMU_SECCOMP_SET_SPAWN; + } else if (g_str_equal(value, "allow")) { + /* default value */ + } else { + error_report("invalid argument for spawn"); + return -1; + } + } + + value = qemu_opt_get(opts, "resourcecontrol"); + if (value) { + if (g_str_equal(value, "deny")) { + seccomp_opts |= QEMU_SECCOMP_SET_RESOURCECTL; + } else if (g_str_equal(value, "allow")) { + /* default value */ + } else { + error_report("invalid argument for resourcecontrol"); + return -1; + } + } + + if (seccomp_start(seccomp_opts) < 0) { + error_report("failed to install seccomp syscall filter " + "in the kernel"); + return -1; + } + } + + return 0; +} + +static QemuOptsList qemu_sandbox_opts = { + .name = "sandbox", + .implied_opt_name = "enable", + .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head), + .desc = { + { + .name = "enable", + .type = QEMU_OPT_BOOL, + }, + { + .name = "obsolete", + .type = QEMU_OPT_STRING, + }, + { + .name = "elevateprivileges", + .type = QEMU_OPT_STRING, + }, + { + .name = "spawn", + .type = QEMU_OPT_STRING, + }, + { + .name = "resourcecontrol", + .type = QEMU_OPT_STRING, + }, + { /* end of list */ } + }, +}; + +static void seccomp_register(void) +{ + qemu_add_opts(&qemu_sandbox_opts); +} +opts_init(seccomp_register); +#endif diff --git a/qemu-tech.texi b/qemu-tech.texi index 52a56ae25e..7c3d1f05e1 100644 --- a/qemu-tech.texi +++ b/qemu-tech.texi @@ -5,6 +5,7 @@ * CPU emulation:: * Translator Internals:: * QEMU compared to other emulators:: +* Managed start up options:: * Bibliography:: @end menu @@ -314,6 +315,42 @@ VirtualBox [9], Xen [10] and KVM [11] are based on QEMU. QEMU-SystemC [12] uses QEMU to simulate a system where some hardware devices are developed in SystemC. +@node Managed start up options +@section Managed start up options + +In system mode emulation, it's possible to create a VM in a paused state using +the -S command line option. In this state the machine is completely initialized +according to command line options and ready to execute VM code but VCPU threads +are not executing any code. The VM state in this paused state depends on the way +QEMU was started. It could be in: +@table @asis +@item initial state (after reset/power on state) +@item with direct kernel loading, the initial state could be amended to execute +code loaded by QEMU in the VM's RAM and with incoming migration +@item with incoming migration, initial state will by amended with the migrated +machine state after migration completes. +@end table + +This paused state is typically used by users to query machine state and/or +additionally configure the machine (by hotplugging devices) in runtime before +allowing VM code to run. + +However, at the -S pause point, it's impossible to configure options that affect +initial VM creation (like: -smp/-m/-numa ...) or cold plug devices. The +experimental --preconfig command line option allows pausing QEMU +before the initial VM creation, in a ``preconfig'' state, where additional +queries and configuration can be performed via QMP before moving on to +the resulting configuration startup. In the preconfig state, QEMU only allows +a limited set of commands over the QMP monitor, where the commands do not +depend on an initialized machine, including but not limited to: +@table @asis +@item qmp_capabilities +@item query-qmp-schema +@item query-commands +@item query-status +@item x-exit-preconfig +@end table + @node Bibliography @section Bibliography diff --git a/qga/channel-posix.c b/qga/channel-posix.c index b812bf4d51..5a925a9818 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -2,7 +2,7 @@ #include #include "qapi/error.h" #include "qemu/sockets.h" -#include "qga/channel.h" +#include "channel.h" #ifdef CONFIG_SOLARIS #include diff --git a/qga/channel-win32.c b/qga/channel-win32.c index 7e6dc4d26f..b3597a8a0f 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" #include #include -#include "qga/guest-agent-core.h" -#include "qga/channel.h" +#include "guest-agent-core.h" +#include "channel.h" typedef struct GAChannelReadState { guint thread_id; diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0dc219dbcf..37e8a2d791 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -16,7 +16,7 @@ #include #include #include -#include "qga/guest-agent-core.h" +#include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" @@ -46,6 +46,7 @@ extern char **environ; #include #include #include +#include #ifdef FIFREEZE #define CONFIG_FSFREEZE @@ -458,7 +459,7 @@ struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, if (!has_count) { count = QGA_READ_COUNT_DEFAULT; - } else if (count < 0) { + } else if (count < 0 || count >= UINT32_MAX) { error_setg(errp, "value '%" PRId64 "' is invalid for argument count", count); return NULL; @@ -875,13 +876,29 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, p = strstr(syspath, "/devices/pci"); if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) { - g_debug("only pci device is supported: sysfs path \"%s\"", syspath); + g_debug("only pci device is supported: sysfs path '%s'", syspath); return; } - driver = get_pci_driver(syspath, (p + 12 + pcilen) - syspath, errp); - if (!driver) { - goto cleanup; + p += 12 + pcilen; + while (true) { + driver = get_pci_driver(syspath, p - syspath, errp); + if (driver && (g_str_equal(driver, "ata_piix") || + g_str_equal(driver, "sym53c8xx") || + g_str_equal(driver, "virtio-pci") || + g_str_equal(driver, "ahci"))) { + break; + } + + g_free(driver); + if (sscanf(p, "/%x:%x:%x.%x%n", + pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) { + p += pcilen; + continue; + } + + g_debug("unsupported driver or sysfs path '%s'", syspath); + return; } p = strstr(syspath, "/target"); @@ -1072,6 +1089,8 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, Error **errp) { GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs)); + struct statvfs buf; + unsigned long used, nonroot_total, fr_size; char *devpath = g_strdup_printf("/sys/dev/block/%u:%u", mount->devmajor, mount->devminor); @@ -1079,7 +1098,19 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount, fs->type = g_strdup(mount->devtype); build_guest_fsinfo_for_device(devpath, fs, errp); + if (statvfs(fs->mountpoint, &buf) == 0) { + fr_size = buf.f_frsize; + used = buf.f_blocks - buf.f_bfree; + nonroot_total = used + buf.f_bavail; + fs->used_bytes = used * fr_size; + fs->total_bytes = nonroot_total * fr_size; + + fs->has_total_bytes = true; + fs->has_used_bytes = true; + } + g_free(devpath); + return fs; } @@ -1274,6 +1305,12 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, } free_fs_mount_list(&mounts); + /* We may not issue any FIFREEZE here. + * Just unset ga_state here and ready for the next call. + */ + if (i == 0) { + ga_unset_frozen(ga_state); + } return i; error: @@ -1439,102 +1476,209 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) #define SUSPEND_SUPPORTED 0 #define SUSPEND_NOT_SUPPORTED 1 -static void bios_supports_mode(const char *pmutils_bin, const char *pmutils_arg, - const char *sysfile_str, Error **errp) +typedef enum { + SUSPEND_MODE_DISK = 0, + SUSPEND_MODE_RAM = 1, + SUSPEND_MODE_HYBRID = 2, +} SuspendMode; + +/* + * Executes a command in a child process using g_spawn_sync, + * returning an int >= 0 representing the exit status of the + * process. + * + * If the program wasn't found in path, returns -1. + * + * If a problem happened when creating the child process, + * returns -1 and errp is set. + */ +static int run_process_child(const char *command[], Error **errp) +{ + int exit_status, spawn_flag; + GError *g_err = NULL; + bool success; + + spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL | + G_SPAWN_STDERR_TO_DEV_NULL; + + success = g_spawn_sync(NULL, (char **)command, environ, spawn_flag, + NULL, NULL, NULL, NULL, + &exit_status, &g_err); + + if (success) { + return WEXITSTATUS(exit_status); + } + + if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) { + error_setg(errp, "failed to create child process, error '%s'", + g_err->message); + } + + g_error_free(g_err); + return -1; +} + +static bool systemd_supports_mode(SuspendMode mode, Error **errp) { Error *local_err = NULL; - char *pmutils_path; + const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend", + "systemd-hybrid-sleep"}; + const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + /* + * systemctl status uses LSB return codes so we can expect + * status > 0 and be ok. To assert if the guest has support + * for the selected suspend mode, status should be < 4. 4 is + * the code for unknown service status, the return value when + * the service does not exist. A common value is status = 3 + * (program is not running). + */ + if (status > 0 && status < 4) { + return true; + } + + if (local_err) { + error_propagate(errp, local_err); + } + + return false; +} + +static void systemd_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"}; + const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == 0) { + return; + } + + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program 'systemctl %s' was not found", + systemctl_args[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, "the helper program 'systemctl %s' returned an " + "unexpected exit status code (%d)", + systemctl_args[mode], status); + } +} + +static bool pmutils_supports_mode(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_args[3] = {"--hibernate", "--suspend", + "--suspend-hybrid"}; + const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == SUSPEND_SUPPORTED) { + return true; + } + + if ((status == -1) && !local_err) { + return false; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", "pm-is-supported", status); + } + + return false; +} + +static void pmutils_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend", + "pm-suspend-hybrid"}; + const char *cmd[2] = {pmutils_binaries[mode], NULL}; + int status; + + status = run_process_child(cmd, &local_err); + + if (status == 0) { + return; + } + + if ((status == -1) && !local_err) { + error_setg(errp, "the helper program '%s' was not found", + pmutils_binaries[mode]); + return; + } + + if (local_err) { + error_propagate(errp, local_err); + } else { + error_setg(errp, + "the helper program '%s' returned an unexpected exit" + " status code (%d)", pmutils_binaries[mode], status); + } +} + +static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) +{ + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; + char buf[32]; /* hopefully big enough */ + int fd; + ssize_t ret; + + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return false; + } + + fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); + if (fd < 0) { + return false; + } + + ret = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (ret <= 0) { + return false; + } + buf[ret] = '\0'; + + if (strstr(buf, sysfile_str)) { + return true; + } + return false; +} + +static void linux_sys_state_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + const char *sysfile_strs[3] = {"disk", "mem", NULL}; + const char *sysfile_str = sysfile_strs[mode]; pid_t pid; int status; - pmutils_path = g_find_program_in_path(pmutils_bin); + if (!sysfile_str) { + error_setg(errp, "unknown guest suspend mode"); + return; + } pid = fork(); if (!pid) { - char buf[32]; /* hopefully big enough */ - ssize_t ret; - int fd; - - setsid(); - reopen_fd_to_null(0); - reopen_fd_to_null(1); - reopen_fd_to_null(2); - - if (pmutils_path) { - execle(pmutils_path, pmutils_bin, pmutils_arg, NULL, environ); - } - - /* - * If we get here either pm-utils is not installed or execle() has - * failed. Let's try the manual method if the caller wants it. - */ - - if (!sysfile_str) { - _exit(SUSPEND_NOT_SUPPORTED); - } - - fd = open(LINUX_SYS_STATE_FILE, O_RDONLY); - if (fd < 0) { - _exit(SUSPEND_NOT_SUPPORTED); - } - - ret = read(fd, buf, sizeof(buf)-1); - if (ret <= 0) { - _exit(SUSPEND_NOT_SUPPORTED); - } - buf[ret] = '\0'; - - if (strstr(buf, sysfile_str)) { - _exit(SUSPEND_SUPPORTED); - } - - _exit(SUSPEND_NOT_SUPPORTED); - } else if (pid < 0) { - error_setg_errno(errp, errno, "failed to create child process"); - goto out; - } - - ga_wait_child(pid, &status, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto out; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; - } - - switch (WEXITSTATUS(status)) { - case SUSPEND_SUPPORTED: - goto out; - case SUSPEND_NOT_SUPPORTED: - error_setg(errp, - "the requested suspend mode is not supported by the guest"); - goto out; - default: - error_setg(errp, - "the helper program '%s' returned an unexpected exit status" - " code (%d)", pmutils_path, WEXITSTATUS(status)); - goto out; - } - -out: - g_free(pmutils_path); -} - -static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, - Error **errp) -{ - Error *local_err = NULL; - char *pmutils_path; - pid_t pid; - int status; - - pmutils_path = g_find_program_in_path(pmutils_bin); - - pid = fork(); - if (pid == 0) { /* child */ int fd; @@ -1543,19 +1687,6 @@ static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, reopen_fd_to_null(1); reopen_fd_to_null(2); - if (pmutils_path) { - execle(pmutils_path, pmutils_bin, NULL, environ); - } - - /* - * If we get here either pm-utils is not installed or execle() has - * failed. Let's try the manual method if the caller wants it. - */ - - if (!sysfile_str) { - _exit(EXIT_FAILURE); - } - fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); if (fd < 0) { _exit(EXIT_FAILURE); @@ -1568,67 +1699,74 @@ static void guest_suspend(const char *pmutils_bin, const char *sysfile_str, _exit(EXIT_SUCCESS); } else if (pid < 0) { error_setg_errno(errp, errno, "failed to create child process"); - goto out; + return; } ga_wait_child(pid, &status, &local_err); if (local_err) { error_propagate(errp, local_err); - goto out; - } - - if (!WIFEXITED(status)) { - error_setg(errp, "child process has terminated abnormally"); - goto out; + return; } if (WEXITSTATUS(status)) { error_setg(errp, "child process has failed to suspend"); - goto out; } -out: - g_free(pmutils_path); +} + +static void guest_suspend(SuspendMode mode, Error **errp) +{ + Error *local_err = NULL; + bool mode_supported = false; + + if (systemd_supports_mode(mode, &local_err)) { + mode_supported = true; + systemd_suspend(mode, &local_err); + } + + if (!local_err) { + return; + } + + error_free(local_err); + + if (pmutils_supports_mode(mode, &local_err)) { + mode_supported = true; + pmutils_suspend(mode, &local_err); + } + + if (!local_err) { + return; + } + + error_free(local_err); + + if (linux_sys_state_supports_mode(mode, &local_err)) { + mode_supported = true; + linux_sys_state_suspend(mode, &local_err); + } + + if (!mode_supported) { + error_setg(errp, + "the requested suspend mode is not supported by the guest"); + } else if (local_err) { + error_propagate(errp, local_err); + } } void qmp_guest_suspend_disk(Error **errp) { - Error *local_err = NULL; - - bios_supports_mode("pm-is-supported", "--hibernate", "disk", &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - guest_suspend("pm-hibernate", "disk", errp); + guest_suspend(SUSPEND_MODE_DISK, errp); } void qmp_guest_suspend_ram(Error **errp) { - Error *local_err = NULL; - - bios_supports_mode("pm-is-supported", "--suspend", "mem", &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - guest_suspend("pm-suspend", "mem", errp); + guest_suspend(SUSPEND_MODE_RAM, errp); } void qmp_guest_suspend_hybrid(Error **errp) { - Error *local_err = NULL; - - bios_supports_mode("pm-is-supported", "--suspend-hybrid", NULL, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - guest_suspend("pm-suspend-hybrid", NULL, errp); + guest_suspend(SUSPEND_MODE_HYBRID, errp); } static GuestNetworkInterfaceList * diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 2d48394748..98d9735389 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -32,8 +32,8 @@ #include #include -#include "qga/guest-agent-core.h" -#include "qga/vss-win32.h" +#include "guest-agent-core.h" +#include "vss-win32.h" #include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" @@ -318,7 +318,7 @@ GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, } if (!has_count) { count = QGA_READ_COUNT_DEFAULT; - } else if (count < 0) { + } else if (count < 0 || count >= UINT32_MAX) { error_setg(errp, "value '%" PRId64 "' is invalid for argument count", count); return NULL; @@ -670,6 +670,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) char fs_name[32]; char vol_info[MAX_PATH+1]; size_t len; + uint64_t i64FreeBytesToCaller, i64TotalBytes, i64FreeBytes; GuestFilesystemInfo *fs = NULL; GetVolumePathNamesForVolumeName(guid, (LPCH)&mnt, 0, &info_size); @@ -699,10 +700,21 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) fs_name[sizeof(fs_name) - 1] = 0; fs = g_malloc(sizeof(*fs)); fs->name = g_strdup(guid); + fs->has_total_bytes = false; + fs->has_used_bytes = false; if (len == 0) { fs->mountpoint = g_strdup("System Reserved"); } else { fs->mountpoint = g_strndup(mnt_point, len); + if (GetDiskFreeSpaceEx(fs->mountpoint, + (PULARGE_INTEGER) & i64FreeBytesToCaller, + (PULARGE_INTEGER) & i64TotalBytes, + (PULARGE_INTEGER) & i64FreeBytes)) { + fs->used_bytes = i64TotalBytes - i64FreeBytes; + fs->total_bytes = i64TotalBytes; + fs->has_total_bytes = true; + fs->has_used_bytes = true; + } } fs->type = g_strdup(fs_name); fs->disk = build_guest_disk_info(guid, errp); @@ -853,6 +865,19 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) GuestFilesystemTrimResponse *resp; HANDLE handle; WCHAR guid[MAX_PATH] = L""; + OSVERSIONINFO osvi; + BOOL win8_or_later; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&osvi); + win8_or_later = (osvi.dwMajorVersion > 6 || + ((osvi.dwMajorVersion == 6) && + (osvi.dwMinorVersion >= 2))); + if (!win8_or_later) { + error_setg(errp, "fstrim is only supported for Win8+"); + return NULL; + } handle = FindFirstVolumeW(guid, ARRAYSIZE(guid)); if (handle == INVALID_HANDLE_VALUE) { diff --git a/qga/commands.c b/qga/commands.c index a64b34ccab..0c7d1385c2 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "qga/guest-agent-core.h" +#include "guest-agent-core.h" #include "qga-qapi-commands.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" @@ -414,10 +414,8 @@ GuestExec *qmp_guest_exec(const char *path, argv = guest_exec_get_args(&arglist, true); envp = has_env ? guest_exec_get_args(env, false) : NULL; - flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD; -#if GLIB_CHECK_VERSION(2, 33, 2) - flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP; -#endif + flags = G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD | + G_SPAWN_SEARCH_PATH_FROM_ENVP; if (!has_output) { flags |= G_SPAWN_STDOUT_TO_DEV_NULL | G_SPAWN_STDERR_TO_DEV_NULL; } @@ -514,7 +512,6 @@ GuestHostName *qmp_guest_get_host_name(Error **err) GuestTimezone *qmp_guest_get_timezone(Error **errp) { -#if GLIB_CHECK_VERSION(2, 28, 0) GuestTimezone *info = NULL; GTimeZone *tz = NULL; gint64 now = 0; @@ -544,8 +541,4 @@ GuestTimezone *qmp_guest_get_timezone(Error **errp) error: g_free(info); return NULL; -#else - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -#endif } diff --git a/qga/guest-agent-command-state.c b/qga/guest-agent-command-state.c index e609d320f0..18bcb5941d 100644 --- a/qga/guest-agent-command-state.c +++ b/qga/guest-agent-command-state.c @@ -10,7 +10,7 @@ * See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" -#include "qga/guest-agent-core.h" +#include "guest-agent-core.h" struct GACommandState { GSList *groups; diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index 5af11627f8..f751a7e9f7 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -41,7 +41,7 @@ channel); - payload_qstr = qobject_to_json(payload); + payload_qstr = qobject_to_json(QOBJECT(payload)); if (!payload_qstr) { return -EINVAL; } @@ -563,7 +563,7 @@ static int send_response(GAState *s, QObject *payload) response_qstr = qstring_new(); qstring_append_chr(response_qstr, QGA_SENTINEL_BYTE); qstring_append(response_qstr, qstring_get_str(payload_qstr)); - QDECREF(payload_qstr); + qobject_unref(payload_qstr); } else { response_qstr = payload_qstr; } @@ -571,7 +571,7 @@ static int send_response(GAState *s, QObject *payload) qstring_append_chr(response_qstr, '\n'); buf = qstring_get_str(response_qstr); status = ga_channel_write_all(s->channel, buf, strlen(buf)); - QDECREF(response_qstr); + qobject_unref(response_qstr); if (status != G_IO_STATUS_NORMAL) { return -EIO; } @@ -581,18 +581,18 @@ static int send_response(GAState *s, QObject *payload) static void process_command(GAState *s, QDict *req) { - QObject *rsp = NULL; + QDict *rsp; int ret; g_assert(req); g_debug("processing command"); - rsp = qmp_dispatch(&ga_commands, QOBJECT(req)); + rsp = qmp_dispatch(&ga_commands, QOBJECT(req), false); if (rsp) { ret = send_response(s, rsp); if (ret < 0) { g_warning("error sending response: %s", strerror(-ret)); } - qobject_decref(rsp); + qobject_unref(rsp); } } @@ -600,46 +600,42 @@ static void process_command(GAState *s, QDict *req) static void process_event(JSONMessageParser *parser, GQueue *tokens) { GAState *s = container_of(parser, GAState, parser); - QDict *qdict; + QObject *obj; + QDict *req, *rsp; Error *err = NULL; int ret; g_assert(s && parser); g_debug("process_event: called"); - qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err)); - if (err || !qdict) { - QDECREF(qdict); - qdict = qdict_new(); - if (!err) { - g_warning("failed to parse event: unknown error"); - error_setg(&err, QERR_JSON_PARSING); - } else { - g_warning("failed to parse event: %s", error_get_pretty(err)); - } - qdict_put_obj(qdict, "error", qmp_build_error_object(err)); - error_free(err); + obj = json_parser_parse_err(tokens, NULL, &err); + if (err) { + goto err; + } + req = qobject_to(QDict, obj); + if (!req) { + error_setg(&err, QERR_JSON_PARSING); + goto err; + } + if (!qdict_haskey(req, "execute")) { + g_warning("unrecognized payload format"); + error_setg(&err, QERR_UNSUPPORTED); + goto err; } - /* handle host->guest commands */ - if (qdict_haskey(qdict, "execute")) { - process_command(s, qdict); - } else { - if (!qdict_haskey(qdict, "error")) { - QDECREF(qdict); - qdict = qdict_new(); - g_warning("unrecognized payload format"); - error_setg(&err, QERR_UNSUPPORTED); - qdict_put_obj(qdict, "error", qmp_build_error_object(err)); - error_free(err); - } - ret = send_response(s, QOBJECT(qdict)); - if (ret < 0) { - g_warning("error sending error response: %s", strerror(-ret)); - } - } + process_command(s, req); + qobject_unref(obj); + return; - QDECREF(qdict); +err: + g_warning("failed to parse event: %s", error_get_pretty(err)); + rsp = qmp_error_response(err); + ret = send_response(s, rsp); + if (ret < 0) { + g_warning("error sending error response: %s", strerror(-ret)); + } + qobject_unref(rsp); + qobject_unref(obj); } /* false return signals GAChannel to close the current client connection */ diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 17884c7c70..dfbc4a5e32 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -435,7 +435,9 @@ # for up to 10 seconds by VSS. # # Returns: Number of file systems currently frozen. On error, all filesystems -# will be thawed. +# will be thawed. If no filesystems are frozen as a result of this call, +# then @guest-fsfreeze-status will remain "thawed" and calling +# @guest-fsfreeze-thaw is not necessary. # # Since: 0.15.0 ## @@ -846,6 +848,8 @@ # @name: disk name # @mountpoint: mount point path # @type: file system type string +# @used-bytes: file system used bytes (since 3.0) +# @total-bytes: non-root file system total bytes (since 3.0) # @disk: an array of disk hardware information that the volume lies on, # which may be empty if the disk type is not supported # @@ -853,6 +857,7 @@ ## { 'struct': 'GuestFilesystemInfo', 'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str', + '*used-bytes': 'uint64', '*total-bytes': 'uint64', 'disk': ['GuestDiskAddress']} } ## @@ -1168,10 +1173,10 @@ # # @kernel-release: # * POSIX: release field returned by uname(2) -# * Windows: version number of the OS +# * Windows: build number of the OS # @kernel-version: # * POSIX: version field returned by uname(2) -# * Windows: build number of the OS +# * Windows: version number of the OS # @machine: # * POSIX: machine field returned by uname(2) # * Windows: one of x86, x86_64, arm, ia64 diff --git a/qga/vss-win32.c b/qga/vss-win32.c index 0199c2a792..a541f3ae01 100644 --- a/qga/vss-win32.c +++ b/qga/vss-win32.c @@ -14,9 +14,9 @@ #include #include "qapi/error.h" #include "qemu/error-report.h" -#include "qga/guest-agent-core.h" -#include "qga/vss-win32.h" -#include "qga/vss-win32/requester.h" +#include "guest-agent-core.h" +#include "vss-win32.h" +#include "vss-win32/requester.h" #define QGA_VSS_DLL "qga-vss.dll" diff --git a/qmp.c b/qmp.c index f72261667f..e7c0a2fd60 100644 --- a/qmp.c +++ b/qmp.c @@ -39,7 +39,7 @@ #include "qapi/qobject-input-visitor.h" #include "hw/boards.h" #include "qom/object_interfaces.h" -#include "hw/mem/pc-dimm.h" +#include "hw/mem/memory-device.h" #include "hw/acpi/acpi_dev_interface.h" NameInfo *qmp_query_name(Error **errp) @@ -129,37 +129,15 @@ void qmp_cpu_add(int64_t id, Error **errp) } } -#ifndef CONFIG_VNC -/* If VNC support is enabled, the "true" query-vnc command is - defined in the VNC subsystem */ -VncInfo *qmp_query_vnc(Error **errp) +void qmp_x_exit_preconfig(Error **errp) { - error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); - return NULL; -}; - -VncInfo2List *qmp_query_vnc_servers(Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); - return NULL; -}; -#endif - -#ifndef CONFIG_SPICE -/* - * qmp_unregister_commands_hack() ensures that QMP command query-spice - * exists only #ifdef CONFIG_SPICE. Necessary for an accurate - * query-commands result. However, the QAPI schema is blissfully - * unaware of that, and the QAPI code generator happily generates a - * dead qmp_marshal_query_spice() that calls qmp_query_spice(). - * Provide it one, or else linking fails. FIXME Educate the QAPI - * schema on CONFIG_SPICE. - */ -SpiceInfo *qmp_query_spice(Error **errp) -{ - abort(); -}; -#endif + if (!runstate_check(RUN_STATE_PRECONFIG)) { + error_setg(errp, "The command is permitted only in '%s' state", + RunState_str(RUN_STATE_PRECONFIG)); + return; + } + qemu_exit_preconfig_request(); +} void qmp_cont(Error **errp) { @@ -402,23 +380,17 @@ static void qmp_change_vnc(const char *target, bool has_arg, const char *arg, qmp_change_vnc_listen(target, errp); } } -#else -void qmp_change_vnc_password(const char *password, Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); -} -static void qmp_change_vnc(const char *target, bool has_arg, const char *arg, - Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); -} #endif /* !CONFIG_VNC */ void qmp_change(const char *device, const char *target, bool has_arg, const char *arg, Error **errp) { if (strcmp(device, "vnc") == 0) { +#ifdef CONFIG_VNC qmp_change_vnc(target, has_arg, arg, errp); +#else + error_setg(errp, QERR_FEATURE_DISABLED, "vnc"); +#endif } else { qmp_blockdev_change_medium(true, device, false, NULL, target, has_arg, arg, false, 0, errp); @@ -710,7 +682,7 @@ void qmp_object_add(const char *type, const char *id, error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); return; } - QINCREF(pdict); + qobject_ref(pdict); } else { pdict = qdict_new(); } @@ -721,7 +693,7 @@ void qmp_object_add(const char *type, const char *id, if (obj) { object_unref(obj); } - QDECREF(pdict); + qobject_unref(pdict); } void qmp_object_del(const char *id, Error **errp) @@ -731,7 +703,7 @@ void qmp_object_del(const char *id, Error **errp) MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp) { - return qmp_pc_dimm_device_list(); + return qmp_memory_device_list(); } ACPIOSTInfoList *qmp_query_acpi_ospm_status(Error **errp) @@ -765,19 +737,3 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp) return mem_info; } - -static QemuSemaphore x_oob_test_sem; - -static void __attribute__((constructor)) x_oob_test_init(void) -{ - qemu_sem_init(&x_oob_test_sem, 0); -} - -void qmp_x_oob_test(bool lock, Error **errp) -{ - if (lock) { - qemu_sem_wait(&x_oob_test_sem); - } else { - qemu_sem_post(&x_oob_test_sem); - } -} diff --git a/qobject/Makefile.objs b/qobject/Makefile.objs index 002d25873a..7b12c9cacf 100644 --- a/qobject/Makefile.objs +++ b/qobject/Makefile.objs @@ -1,2 +1,3 @@ util-obj-y = qnull.o qnum.o qstring.o qdict.o qlist.o qbool.o qlit.o util-obj-y += qjson.o qobject.o json-lexer.o json-streamer.o json-parser.o +util-obj-y += block-qdict.o diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c new file mode 100644 index 0000000000..80c653013f --- /dev/null +++ b/qobject/block-qdict.c @@ -0,0 +1,733 @@ +/* + * Special QDict functions used by the block layer + * + * Copyright (c) 2013-2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "block/qdict.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qobject-input-visitor.h" +#include "qemu/cutils.h" +#include "qapi/error.h" + +/** + * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the + * value of 'key' in 'src' is copied there (and the refcount increased + * accordingly). + */ +void qdict_copy_default(QDict *dst, QDict *src, const char *key) +{ + QObject *val; + + if (qdict_haskey(dst, key)) { + return; + } + + val = qdict_get(src, key); + if (val) { + qdict_put_obj(dst, key, qobject_ref(val)); + } +} + +/** + * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a + * new QString initialised by 'val' is put there. + */ +void qdict_set_default_str(QDict *dst, const char *key, const char *val) +{ + if (qdict_haskey(dst, key)) { + return; + } + + qdict_put_str(dst, key, val); +} + +static void qdict_flatten_qdict(QDict *qdict, QDict *target, + const char *prefix); + +static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) +{ + QObject *value; + const QListEntry *entry; + QDict *dict_val; + QList *list_val; + char *new_key; + int i; + + /* This function is never called with prefix == NULL, i.e., it is always + * called from within qdict_flatten_q(list|dict)(). Therefore, it does not + * need to remove list entries during the iteration (the whole list will be + * deleted eventually anyway from qdict_flatten_qdict()). */ + assert(prefix); + + entry = qlist_first(qlist); + + for (i = 0; entry; entry = qlist_next(entry), i++) { + value = qlist_entry_obj(entry); + dict_val = qobject_to(QDict, value); + list_val = qobject_to(QList, value); + new_key = g_strdup_printf("%s.%i", prefix, i); + + /* + * Flatten non-empty QDict and QList recursively into @target, + * copy other objects to @target + */ + if (dict_val && qdict_size(dict_val)) { + qdict_flatten_qdict(dict_val, target, new_key); + } else if (list_val && !qlist_empty(list_val)) { + qdict_flatten_qlist(list_val, target, new_key); + } else { + qdict_put_obj(target, new_key, qobject_ref(value)); + } + + g_free(new_key); + } +} + +static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) +{ + QObject *value; + const QDictEntry *entry, *next; + QDict *dict_val; + QList *list_val; + char *key, *new_key; + + entry = qdict_first(qdict); + + while (entry != NULL) { + next = qdict_next(qdict, entry); + value = qdict_entry_value(entry); + dict_val = qobject_to(QDict, value); + list_val = qobject_to(QList, value); + + if (prefix) { + key = new_key = g_strdup_printf("%s.%s", prefix, entry->key); + } else { + key = entry->key; + new_key = NULL; + } + + /* + * Flatten non-empty QDict and QList recursively into @target, + * copy other objects to @target. + * On the root level (if @qdict == @target), remove flattened + * nested QDicts and QLists from @qdict. + * + * (Note that we do not need to remove entries from nested + * dicts or lists. Their reference count is decremented on + * the root level, so there are no leaks. In fact, if they + * have a reference count greater than one, we are probably + * well advised not to modify them altogether.) + */ + if (dict_val && qdict_size(dict_val)) { + qdict_flatten_qdict(dict_val, target, key); + if (target == qdict) { + qdict_del(qdict, entry->key); + } + } else if (list_val && !qlist_empty(list_val)) { + qdict_flatten_qlist(list_val, target, key); + if (target == qdict) { + qdict_del(qdict, entry->key); + } + } else if (target != qdict) { + qdict_put_obj(target, key, qobject_ref(value)); + } + + g_free(new_key); + entry = next; + } +} + +/** + * qdict_flatten(): For each nested non-empty QDict with key x, all + * fields with key y are moved to this QDict and their key is renamed + * to "x.y". For each nested non-empty QList with key x, the field at + * index y is moved to this QDict with the key "x.y" (i.e., the + * reverse of what qdict_array_split() does). + * This operation is applied recursively for nested QDicts and QLists. + */ +void qdict_flatten(QDict *qdict) +{ + qdict_flatten_qdict(qdict, qdict, NULL); +} + +/* extract all the src QDict entries starting by start into dst */ +void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) + +{ + const QDictEntry *entry, *next; + const char *p; + + *dst = qdict_new(); + entry = qdict_first(src); + + while (entry != NULL) { + next = qdict_next(src, entry); + if (strstart(entry->key, start, &p)) { + qdict_put_obj(*dst, p, qobject_ref(entry->value)); + qdict_del(src, entry->key); + } + entry = next; + } +} + +static int qdict_count_prefixed_entries(const QDict *src, const char *start) +{ + const QDictEntry *entry; + int count = 0; + + for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { + if (strstart(entry->key, start, NULL)) { + if (count == INT_MAX) { + return -ERANGE; + } + count++; + } + } + + return count; +} + +/** + * qdict_array_split(): This function moves array-like elements of a QDict into + * a new QList. Every entry in the original QDict with a key "%u" or one + * prefixed "%u.", where %u designates an unsigned integer starting at 0 and + * incrementally counting up, will be moved to a new QDict at index %u in the + * output QList with the key prefix removed, if that prefix is "%u.". If the + * whole key is just "%u", the whole QObject will be moved unchanged without + * creating a new QDict. The function terminates when there is no entry in the + * QDict with a prefix directly (incrementally) following the last one; it also + * returns if there are both entries with "%u" and "%u." for the same index %u. + * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} + * (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) + * => [{"a": 42, "b": 23}, {"x": 0}, 66] + * and {"4.y": 1, "o.o": 7} (remainder of the old QDict) + */ +void qdict_array_split(QDict *src, QList **dst) +{ + unsigned i; + + *dst = qlist_new(); + + for (i = 0; i < UINT_MAX; i++) { + QObject *subqobj; + bool is_subqdict; + QDict *subqdict; + char indexstr[32], prefix[32]; + size_t snprintf_ret; + + snprintf_ret = snprintf(indexstr, 32, "%u", i); + assert(snprintf_ret < 32); + + subqobj = qdict_get(src, indexstr); + + snprintf_ret = snprintf(prefix, 32, "%u.", i); + assert(snprintf_ret < 32); + + /* Overflow is the same as positive non-zero results */ + is_subqdict = qdict_count_prefixed_entries(src, prefix); + + /* + * There may be either a single subordinate object (named + * "%u") or multiple objects (each with a key prefixed "%u."), + * but not both. + */ + if (!subqobj == !is_subqdict) { + break; + } + + if (is_subqdict) { + qdict_extract_subqdict(src, &subqdict, prefix); + assert(qdict_size(subqdict) > 0); + } else { + qobject_ref(subqobj); + qdict_del(src, indexstr); + } + + qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); + } +} + +/** + * qdict_split_flat_key: + * @key: the key string to split + * @prefix: non-NULL pointer to hold extracted prefix + * @suffix: non-NULL pointer to remaining suffix + * + * Given a flattened key such as 'foo.0.bar', split it into two parts + * at the first '.' separator. Allows double dot ('..') to escape the + * normal separator. + * + * e.g. + * 'foo.0.bar' -> prefix='foo' and suffix='0.bar' + * 'foo..0.bar' -> prefix='foo.0' and suffix='bar' + * + * The '..' sequence will be unescaped in the returned 'prefix' + * string. The 'suffix' string will be left in escaped format, so it + * can be fed back into the qdict_split_flat_key() key as the input + * later. + * + * The caller is responsible for freeing the string returned in @prefix + * using g_free(). + */ +static void qdict_split_flat_key(const char *key, char **prefix, + const char **suffix) +{ + const char *separator; + size_t i, j; + + /* Find first '.' separator, but if there is a pair '..' + * that acts as an escape, so skip over '..' */ + separator = NULL; + do { + if (separator) { + separator += 2; + } else { + separator = key; + } + separator = strchr(separator, '.'); + } while (separator && separator[1] == '.'); + + if (separator) { + *prefix = g_strndup(key, separator - key); + *suffix = separator + 1; + } else { + *prefix = g_strdup(key); + *suffix = NULL; + } + + /* Unescape the '..' sequence into '.' */ + for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) { + if ((*prefix)[i] == '.') { + assert((*prefix)[i + 1] == '.'); + i++; + } + (*prefix)[j] = (*prefix)[i]; + } + (*prefix)[j] = '\0'; +} + +/** + * qdict_is_list: + * @maybe_list: dict to check if keys represent list elements. + * + * Determine whether all keys in @maybe_list are valid list elements. + * If @maybe_list is non-zero in length and all the keys look like + * valid list indexes, this will return 1. If @maybe_list is zero + * length or all keys are non-numeric then it will return 0 to indicate + * it is a normal qdict. If there is a mix of numeric and non-numeric + * keys, or the list indexes are non-contiguous, an error is reported. + * + * Returns: 1 if a valid list, 0 if a dict, -1 on error + */ +static int qdict_is_list(QDict *maybe_list, Error **errp) +{ + const QDictEntry *ent; + ssize_t len = 0; + ssize_t max = -1; + int is_list = -1; + int64_t val; + + for (ent = qdict_first(maybe_list); ent != NULL; + ent = qdict_next(maybe_list, ent)) { + int is_index = !qemu_strtoi64(ent->key, NULL, 10, &val); + + if (is_list == -1) { + is_list = is_index; + } + + if (is_index != is_list) { + error_setg(errp, "Cannot mix list and non-list keys"); + return -1; + } + + if (is_index) { + len++; + if (val > max) { + max = val; + } + } + } + + if (is_list == -1) { + assert(!qdict_size(maybe_list)); + is_list = 0; + } + + /* NB this isn't a perfect check - e.g. it won't catch + * a list containing '1', '+1', '01', '3', but that + * does not matter - we've still proved that the + * input is a list. It is up the caller to do a + * stricter check if desired */ + if (len != (max + 1)) { + error_setg(errp, "List indices are not contiguous, " + "saw %zd elements but %zd largest index", + len, max); + return -1; + } + + return is_list; +} + +/** + * qdict_crumple: + * @src: the original flat dictionary (only scalar values) to crumple + * + * Takes a flat dictionary whose keys use '.' separator to indicate + * nesting, and values are scalars, empty dictionaries or empty lists, + * and crumples it into a nested structure. + * + * To include a literal '.' in a key name, it must be escaped as '..' + * + * For example, an input of: + * + * { 'foo.0.bar': 'one', 'foo.0.wizz': '1', + * 'foo.1.bar': 'two', 'foo.1.wizz': '2' } + * + * will result in an output of: + * + * { + * 'foo': [ + * { 'bar': 'one', 'wizz': '1' }, + * { 'bar': 'two', 'wizz': '2' } + * ], + * } + * + * The following scenarios in the input dict will result in an + * error being returned: + * + * - Any values in @src are non-scalar types + * - If keys in @src imply that a particular level is both a + * list and a dict. e.g., "foo.0.bar" and "foo.eek.bar". + * - If keys in @src imply that a particular level is a list, + * but the indices are non-contiguous. e.g. "foo.0.bar" and + * "foo.2.bar" without any "foo.1.bar" present. + * - If keys in @src represent list indexes, but are not in + * the "%zu" format. e.g. "foo.+0.bar" + * + * Returns: either a QDict or QList for the nested data structure, or NULL + * on error + */ +QObject *qdict_crumple(const QDict *src, Error **errp) +{ + const QDictEntry *ent; + QDict *two_level, *multi_level = NULL, *child_dict; + QDict *dict_val; + QList *list_val; + QObject *dst = NULL, *child; + size_t i; + char *prefix = NULL; + const char *suffix = NULL; + int is_list; + + two_level = qdict_new(); + + /* Step 1: split our totally flat dict into a two level dict */ + for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { + dict_val = qobject_to(QDict, ent->value); + list_val = qobject_to(QList, ent->value); + if ((dict_val && qdict_size(dict_val)) + || (list_val && !qlist_empty(list_val))) { + error_setg(errp, "Value %s is not flat", ent->key); + goto error; + } + + qdict_split_flat_key(ent->key, &prefix, &suffix); + child = qdict_get(two_level, prefix); + child_dict = qobject_to(QDict, child); + + if (child) { + /* + * If @child_dict, then all previous keys with this prefix + * had a suffix. If @suffix, this one has one as well, + * and we're good, else there's a clash. + */ + if (!child_dict || !suffix) { + error_setg(errp, "Cannot mix scalar and non-scalar keys"); + goto error; + } + } + + if (suffix) { + if (!child_dict) { + child_dict = qdict_new(); + qdict_put(two_level, prefix, child_dict); + } + qdict_put_obj(child_dict, suffix, qobject_ref(ent->value)); + } else { + qdict_put_obj(two_level, prefix, qobject_ref(ent->value)); + } + + g_free(prefix); + prefix = NULL; + } + + /* Step 2: optionally process the two level dict recursively + * into a multi-level dict */ + multi_level = qdict_new(); + for (ent = qdict_first(two_level); ent != NULL; + ent = qdict_next(two_level, ent)) { + dict_val = qobject_to(QDict, ent->value); + if (dict_val && qdict_size(dict_val)) { + child = qdict_crumple(dict_val, errp); + if (!child) { + goto error; + } + + qdict_put_obj(multi_level, ent->key, child); + } else { + qdict_put_obj(multi_level, ent->key, qobject_ref(ent->value)); + } + } + qobject_unref(two_level); + two_level = NULL; + + /* Step 3: detect if we need to turn our dict into list */ + is_list = qdict_is_list(multi_level, errp); + if (is_list < 0) { + goto error; + } + + if (is_list) { + dst = QOBJECT(qlist_new()); + + for (i = 0; i < qdict_size(multi_level); i++) { + char *key = g_strdup_printf("%zu", i); + + child = qdict_get(multi_level, key); + g_free(key); + + if (!child) { + error_setg(errp, "Missing list index %zu", i); + goto error; + } + + qlist_append_obj(qobject_to(QList, dst), qobject_ref(child)); + } + qobject_unref(multi_level); + multi_level = NULL; + } else { + dst = QOBJECT(multi_level); + } + + return dst; + + error: + g_free(prefix); + qobject_unref(multi_level); + qobject_unref(two_level); + qobject_unref(dst); + return NULL; +} + +/** + * qdict_crumple_for_keyval_qiv: + * @src: the flat dictionary (only scalar values) to crumple + * @errp: location to store error + * + * Like qdict_crumple(), but additionally transforms scalar values so + * the result can be passed to qobject_input_visitor_new_keyval(). + * + * The block subsystem uses this function to prepare its flat QDict + * with possibly confused scalar types for a visit. It should not be + * used for anything else, and it should go away once the block + * subsystem has been cleaned up. + */ +static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) +{ + QDict *tmp = NULL; + char *buf; + const char *s; + const QDictEntry *ent; + QObject *dst; + + for (ent = qdict_first(src); ent; ent = qdict_next(src, ent)) { + buf = NULL; + switch (qobject_type(ent->value)) { + case QTYPE_QNULL: + case QTYPE_QSTRING: + continue; + case QTYPE_QNUM: + s = buf = qnum_to_string(qobject_to(QNum, ent->value)); + break; + case QTYPE_QDICT: + case QTYPE_QLIST: + /* @src isn't flat; qdict_crumple() will fail */ + continue; + case QTYPE_QBOOL: + s = qbool_get_bool(qobject_to(QBool, ent->value)) + ? "on" : "off"; + break; + default: + abort(); + } + + if (!tmp) { + tmp = qdict_clone_shallow(src); + } + qdict_put(tmp, ent->key, qstring_from_str(s)); + g_free(buf); + } + + dst = qdict_crumple(tmp ?: src, errp); + qobject_unref(tmp); + return dst; +} + +/** + * qdict_array_entries(): Returns the number of direct array entries if the + * sub-QDict of src specified by the prefix in subqdict (or src itself for + * prefix == "") is valid as an array, i.e. the length of the created list if + * the sub-QDict would become empty after calling qdict_array_split() on it. If + * the array is not valid, -EINVAL is returned. + */ +int qdict_array_entries(QDict *src, const char *subqdict) +{ + const QDictEntry *entry; + unsigned i; + unsigned entries = 0; + size_t subqdict_len = strlen(subqdict); + + assert(!subqdict_len || subqdict[subqdict_len - 1] == '.'); + + /* qdict_array_split() loops until UINT_MAX, but as we want to return + * negative errors, we only have a signed return value here. Any additional + * entries will lead to -EINVAL. */ + for (i = 0; i < INT_MAX; i++) { + QObject *subqobj; + int subqdict_entries; + char *prefix = g_strdup_printf("%s%u.", subqdict, i); + + subqdict_entries = qdict_count_prefixed_entries(src, prefix); + + /* Remove ending "." */ + prefix[strlen(prefix) - 1] = 0; + subqobj = qdict_get(src, prefix); + + g_free(prefix); + + if (subqdict_entries < 0) { + return subqdict_entries; + } + + /* There may be either a single subordinate object (named "%u") or + * multiple objects (each with a key prefixed "%u."), but not both. */ + if (subqobj && subqdict_entries) { + return -EINVAL; + } else if (!subqobj && !subqdict_entries) { + break; + } + + entries += subqdict_entries ? subqdict_entries : 1; + } + + /* Consider everything handled that isn't part of the given sub-QDict */ + for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { + if (!strstart(qdict_entry_key(entry), subqdict, NULL)) { + entries++; + } + } + + /* Anything left in the sub-QDict that wasn't handled? */ + if (qdict_size(src) != entries) { + return -EINVAL; + } + + return i; +} + +/** + * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all + * elements from src to dest. + * + * If an element from src has a key already present in dest, it will not be + * moved unless overwrite is true. + * + * If overwrite is true, the conflicting values in dest will be discarded and + * replaced by the corresponding values from src. + * + * Therefore, with overwrite being true, the src QDict will always be empty when + * this function returns. If overwrite is false, the src QDict will be empty + * iff there were no conflicts. + */ +void qdict_join(QDict *dest, QDict *src, bool overwrite) +{ + const QDictEntry *entry, *next; + + entry = qdict_first(src); + while (entry) { + next = qdict_next(src, entry); + + if (overwrite || !qdict_haskey(dest, entry->key)) { + qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); + qdict_del(src, entry->key); + } + + entry = next; + } +} + +/** + * qdict_rename_keys(): Rename keys in qdict according to the replacements + * specified in the array renames. The array must be terminated by an entry + * with from = NULL. + * + * The renames are performed individually in the order of the array, so entries + * may be renamed multiple times and may or may not conflict depending on the + * order of the renames array. + * + * Returns true for success, false in error cases. + */ +bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) +{ + QObject *qobj; + + while (renames->from) { + if (qdict_haskey(qdict, renames->from)) { + if (qdict_haskey(qdict, renames->to)) { + error_setg(errp, "'%s' and its alias '%s' can't be used at the " + "same time", renames->to, renames->from); + return false; + } + + qobj = qdict_get(qdict, renames->from); + qdict_put_obj(qdict, renames->to, qobject_ref(qobj)); + qdict_del(qdict, renames->from); + } + + renames++; + } + return true; +} + +/* + * Create a QObject input visitor for flat @qdict with possibly + * confused scalar types. + * + * The block subsystem uses this function to visit its flat QDict with + * possibly confused scalar types. It should not be used for anything + * else, and it should go away once the block subsystem has been + * cleaned up. + */ +Visitor *qobject_input_visitor_new_flat_confused(QDict *qdict, + Error **errp) +{ + QObject *crumpled; + Visitor *v; + + crumpled = qdict_crumple_for_keyval_qiv(qdict, errp); + if (!crumpled) { + return NULL; + } + + v = qobject_input_visitor_new_keyval(crumpled); + qobject_unref(crumpled); + return v; +} diff --git a/qobject/json-parser.c b/qobject/json-parser.c index 769b960c9f..a5aa790d62 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -222,7 +222,7 @@ static QString *qstring_from_escaped_str(JSONParserContext *ctxt, return str; out: - QDECREF(str); + qobject_unref(str); return NULL; } @@ -311,12 +311,12 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) qdict_put_obj(dict, qstring_get_str(key), value); - QDECREF(key); + qobject_unref(key); return 0; out: - QDECREF(key); + qobject_unref(key); return -1; } @@ -371,7 +371,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) return QOBJECT(dict); out: - QDECREF(dict); + qobject_unref(dict); return NULL; } @@ -435,7 +435,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) return QOBJECT(list); out: - QDECREF(list); + qobject_unref(list); return NULL; } diff --git a/qobject/qdict.c b/qobject/qdict.c index d1997a0d8a..3d8c2f7bbc 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -14,13 +14,8 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qbool.h" -#include "qapi/qmp/qlist.h" #include "qapi/qmp/qnull.h" #include "qapi/qmp/qstring.h" -#include "qapi/error.h" -#include "qemu/queue.h" -#include "qemu-common.h" -#include "qemu/cutils.h" /** * qdict_new(): Create a new QDict @@ -123,7 +118,7 @@ void qdict_put_obj(QDict *qdict, const char *key, QObject *value) entry = qdict_find(qdict, key, bucket); if (entry) { /* replace key's value */ - qobject_decref(entry->value); + qobject_unref(entry->value); entry->value = value; } else { /* allocate a new entry */ @@ -373,8 +368,7 @@ QDict *qdict_clone_shallow(const QDict *src) for (i = 0; i < QDICT_BUCKET_MAX; i++) { QLIST_FOREACH(entry, &src->table[i], next) { - qobject_incref(entry->value); - qdict_put_obj(dest, entry->key, entry->value); + qdict_put_obj(dest, entry->key, qobject_ref(entry->value)); } } @@ -390,7 +384,7 @@ static void qentry_destroy(QDictEntry *e) assert(e->key != NULL); assert(e->value != NULL); - qobject_decref(e->value); + qobject_unref(e->value); g_free(e->key); g_free(e); } @@ -464,636 +458,3 @@ void qdict_destroy_obj(QObject *obj) g_free(qdict); } - -/** - * qdict_copy_default(): If no entry mapped by 'key' exists in 'dst' yet, the - * value of 'key' in 'src' is copied there (and the refcount increased - * accordingly). - */ -void qdict_copy_default(QDict *dst, QDict *src, const char *key) -{ - QObject *val; - - if (qdict_haskey(dst, key)) { - return; - } - - val = qdict_get(src, key); - if (val) { - qobject_incref(val); - qdict_put_obj(dst, key, val); - } -} - -/** - * qdict_set_default_str(): If no entry mapped by 'key' exists in 'dst' yet, a - * new QString initialised by 'val' is put there. - */ -void qdict_set_default_str(QDict *dst, const char *key, const char *val) -{ - if (qdict_haskey(dst, key)) { - return; - } - - qdict_put_str(dst, key, val); -} - -static void qdict_flatten_qdict(QDict *qdict, QDict *target, - const char *prefix); - -static void qdict_flatten_qlist(QList *qlist, QDict *target, const char *prefix) -{ - QObject *value; - const QListEntry *entry; - char *new_key; - int i; - - /* This function is never called with prefix == NULL, i.e., it is always - * called from within qdict_flatten_q(list|dict)(). Therefore, it does not - * need to remove list entries during the iteration (the whole list will be - * deleted eventually anyway from qdict_flatten_qdict()). */ - assert(prefix); - - entry = qlist_first(qlist); - - for (i = 0; entry; entry = qlist_next(entry), i++) { - value = qlist_entry_obj(entry); - new_key = g_strdup_printf("%s.%i", prefix, i); - - if (qobject_type(value) == QTYPE_QDICT) { - qdict_flatten_qdict(qobject_to(QDict, value), target, new_key); - } else if (qobject_type(value) == QTYPE_QLIST) { - qdict_flatten_qlist(qobject_to(QList, value), target, new_key); - } else { - /* All other types are moved to the target unchanged. */ - qobject_incref(value); - qdict_put_obj(target, new_key, value); - } - - g_free(new_key); - } -} - -static void qdict_flatten_qdict(QDict *qdict, QDict *target, const char *prefix) -{ - QObject *value; - const QDictEntry *entry, *next; - char *new_key; - bool delete; - - entry = qdict_first(qdict); - - while (entry != NULL) { - - next = qdict_next(qdict, entry); - value = qdict_entry_value(entry); - new_key = NULL; - delete = false; - - if (prefix) { - new_key = g_strdup_printf("%s.%s", prefix, entry->key); - } - - if (qobject_type(value) == QTYPE_QDICT) { - /* Entries of QDicts are processed recursively, the QDict object - * itself disappears. */ - qdict_flatten_qdict(qobject_to(QDict, value), target, - new_key ? new_key : entry->key); - delete = true; - } else if (qobject_type(value) == QTYPE_QLIST) { - qdict_flatten_qlist(qobject_to(QList, value), target, - new_key ? new_key : entry->key); - delete = true; - } else if (prefix) { - /* All other objects are moved to the target unchanged. */ - qobject_incref(value); - qdict_put_obj(target, new_key, value); - delete = true; - } - - g_free(new_key); - - if (delete) { - qdict_del(qdict, entry->key); - - /* Restart loop after modifying the iterated QDict */ - entry = qdict_first(qdict); - continue; - } - - entry = next; - } -} - -/** - * qdict_flatten(): For each nested QDict with key x, all fields with key y - * are moved to this QDict and their key is renamed to "x.y". For each nested - * QList with key x, the field at index y is moved to this QDict with the key - * "x.y" (i.e., the reverse of what qdict_array_split() does). - * This operation is applied recursively for nested QDicts and QLists. - */ -void qdict_flatten(QDict *qdict) -{ - qdict_flatten_qdict(qdict, qdict, NULL); -} - -/* extract all the src QDict entries starting by start into dst */ -void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) - -{ - const QDictEntry *entry, *next; - const char *p; - - *dst = qdict_new(); - entry = qdict_first(src); - - while (entry != NULL) { - next = qdict_next(src, entry); - if (strstart(entry->key, start, &p)) { - qobject_incref(entry->value); - qdict_put_obj(*dst, p, entry->value); - qdict_del(src, entry->key); - } - entry = next; - } -} - -static int qdict_count_prefixed_entries(const QDict *src, const char *start) -{ - const QDictEntry *entry; - int count = 0; - - for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { - if (strstart(entry->key, start, NULL)) { - if (count == INT_MAX) { - return -ERANGE; - } - count++; - } - } - - return count; -} - -/** - * qdict_array_split(): This function moves array-like elements of a QDict into - * a new QList. Every entry in the original QDict with a key "%u" or one - * prefixed "%u.", where %u designates an unsigned integer starting at 0 and - * incrementally counting up, will be moved to a new QDict at index %u in the - * output QList with the key prefix removed, if that prefix is "%u.". If the - * whole key is just "%u", the whole QObject will be moved unchanged without - * creating a new QDict. The function terminates when there is no entry in the - * QDict with a prefix directly (incrementally) following the last one; it also - * returns if there are both entries with "%u" and "%u." for the same index %u. - * Example: {"0.a": 42, "0.b": 23, "1.x": 0, "4.y": 1, "o.o": 7, "2": 66} - * (or {"1.x": 0, "4.y": 1, "0.a": 42, "o.o": 7, "0.b": 23, "2": 66}) - * => [{"a": 42, "b": 23}, {"x": 0}, 66] - * and {"4.y": 1, "o.o": 7} (remainder of the old QDict) - */ -void qdict_array_split(QDict *src, QList **dst) -{ - unsigned i; - - *dst = qlist_new(); - - for (i = 0; i < UINT_MAX; i++) { - QObject *subqobj; - bool is_subqdict; - QDict *subqdict; - char indexstr[32], prefix[32]; - size_t snprintf_ret; - - snprintf_ret = snprintf(indexstr, 32, "%u", i); - assert(snprintf_ret < 32); - - subqobj = qdict_get(src, indexstr); - - snprintf_ret = snprintf(prefix, 32, "%u.", i); - assert(snprintf_ret < 32); - - /* Overflow is the same as positive non-zero results */ - is_subqdict = qdict_count_prefixed_entries(src, prefix); - - // There may be either a single subordinate object (named "%u") or - // multiple objects (each with a key prefixed "%u."), but not both. - if (!subqobj == !is_subqdict) { - break; - } - - if (is_subqdict) { - qdict_extract_subqdict(src, &subqdict, prefix); - assert(qdict_size(subqdict) > 0); - } else { - qobject_incref(subqobj); - qdict_del(src, indexstr); - } - - qlist_append_obj(*dst, subqobj ?: QOBJECT(subqdict)); - } -} - -/** - * qdict_split_flat_key: - * @key: the key string to split - * @prefix: non-NULL pointer to hold extracted prefix - * @suffix: non-NULL pointer to remaining suffix - * - * Given a flattened key such as 'foo.0.bar', split it into two parts - * at the first '.' separator. Allows double dot ('..') to escape the - * normal separator. - * - * e.g. - * 'foo.0.bar' -> prefix='foo' and suffix='0.bar' - * 'foo..0.bar' -> prefix='foo.0' and suffix='bar' - * - * The '..' sequence will be unescaped in the returned 'prefix' - * string. The 'suffix' string will be left in escaped format, so it - * can be fed back into the qdict_split_flat_key() key as the input - * later. - * - * The caller is responsible for freeing the string returned in @prefix - * using g_free(). - */ -static void qdict_split_flat_key(const char *key, char **prefix, - const char **suffix) -{ - const char *separator; - size_t i, j; - - /* Find first '.' separator, but if there is a pair '..' - * that acts as an escape, so skip over '..' */ - separator = NULL; - do { - if (separator) { - separator += 2; - } else { - separator = key; - } - separator = strchr(separator, '.'); - } while (separator && separator[1] == '.'); - - if (separator) { - *prefix = g_strndup(key, separator - key); - *suffix = separator + 1; - } else { - *prefix = g_strdup(key); - *suffix = NULL; - } - - /* Unescape the '..' sequence into '.' */ - for (i = 0, j = 0; (*prefix)[i] != '\0'; i++, j++) { - if ((*prefix)[i] == '.') { - assert((*prefix)[i + 1] == '.'); - i++; - } - (*prefix)[j] = (*prefix)[i]; - } - (*prefix)[j] = '\0'; -} - -/** - * qdict_is_list: - * @maybe_list: dict to check if keys represent list elements. - * - * Determine whether all keys in @maybe_list are valid list elements. - * If @maybe_list is non-zero in length and all the keys look like - * valid list indexes, this will return 1. If @maybe_list is zero - * length or all keys are non-numeric then it will return 0 to indicate - * it is a normal qdict. If there is a mix of numeric and non-numeric - * keys, or the list indexes are non-contiguous, an error is reported. - * - * Returns: 1 if a valid list, 0 if a dict, -1 on error - */ -static int qdict_is_list(QDict *maybe_list, Error **errp) -{ - const QDictEntry *ent; - ssize_t len = 0; - ssize_t max = -1; - int is_list = -1; - int64_t val; - - for (ent = qdict_first(maybe_list); ent != NULL; - ent = qdict_next(maybe_list, ent)) { - - if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { - if (is_list == -1) { - is_list = 1; - } else if (!is_list) { - error_setg(errp, - "Cannot mix list and non-list keys"); - return -1; - } - len++; - if (val > max) { - max = val; - } - } else { - if (is_list == -1) { - is_list = 0; - } else if (is_list) { - error_setg(errp, - "Cannot mix list and non-list keys"); - return -1; - } - } - } - - if (is_list == -1) { - assert(!qdict_size(maybe_list)); - is_list = 0; - } - - /* NB this isn't a perfect check - e.g. it won't catch - * a list containing '1', '+1', '01', '3', but that - * does not matter - we've still proved that the - * input is a list. It is up the caller to do a - * stricter check if desired */ - if (len != (max + 1)) { - error_setg(errp, "List indices are not contiguous, " - "saw %zd elements but %zd largest index", - len, max); - return -1; - } - - return is_list; -} - -/** - * qdict_crumple: - * @src: the original flat dictionary (only scalar values) to crumple - * - * Takes a flat dictionary whose keys use '.' separator to indicate - * nesting, and values are scalars, and crumples it into a nested - * structure. - * - * To include a literal '.' in a key name, it must be escaped as '..' - * - * For example, an input of: - * - * { 'foo.0.bar': 'one', 'foo.0.wizz': '1', - * 'foo.1.bar': 'two', 'foo.1.wizz': '2' } - * - * will result in an output of: - * - * { - * 'foo': [ - * { 'bar': 'one', 'wizz': '1' }, - * { 'bar': 'two', 'wizz': '2' } - * ], - * } - * - * The following scenarios in the input dict will result in an - * error being returned: - * - * - Any values in @src are non-scalar types - * - If keys in @src imply that a particular level is both a - * list and a dict. e.g., "foo.0.bar" and "foo.eek.bar". - * - If keys in @src imply that a particular level is a list, - * but the indices are non-contiguous. e.g. "foo.0.bar" and - * "foo.2.bar" without any "foo.1.bar" present. - * - If keys in @src represent list indexes, but are not in - * the "%zu" format. e.g. "foo.+0.bar" - * - * Returns: either a QDict or QList for the nested data structure, or NULL - * on error - */ -QObject *qdict_crumple(const QDict *src, Error **errp) -{ - const QDictEntry *ent; - QDict *two_level, *multi_level = NULL; - QObject *dst = NULL, *child; - size_t i; - char *prefix = NULL; - const char *suffix = NULL; - int is_list; - - two_level = qdict_new(); - - /* Step 1: split our totally flat dict into a two level dict */ - for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) { - if (qobject_type(ent->value) == QTYPE_QDICT || - qobject_type(ent->value) == QTYPE_QLIST) { - error_setg(errp, "Value %s is not a scalar", - ent->key); - goto error; - } - - qdict_split_flat_key(ent->key, &prefix, &suffix); - - child = qdict_get(two_level, prefix); - if (suffix) { - QDict *child_dict = qobject_to(QDict, child); - if (!child_dict) { - if (child) { - error_setg(errp, "Key %s prefix is already set as a scalar", - prefix); - goto error; - } - - child_dict = qdict_new(); - qdict_put_obj(two_level, prefix, QOBJECT(child_dict)); - } - - qobject_incref(ent->value); - qdict_put_obj(child_dict, suffix, ent->value); - } else { - if (child) { - error_setg(errp, "Key %s prefix is already set as a dict", - prefix); - goto error; - } - qobject_incref(ent->value); - qdict_put_obj(two_level, prefix, ent->value); - } - - g_free(prefix); - prefix = NULL; - } - - /* Step 2: optionally process the two level dict recursively - * into a multi-level dict */ - multi_level = qdict_new(); - for (ent = qdict_first(two_level); ent != NULL; - ent = qdict_next(two_level, ent)) { - QDict *dict = qobject_to(QDict, ent->value); - if (dict) { - child = qdict_crumple(dict, errp); - if (!child) { - goto error; - } - - qdict_put_obj(multi_level, ent->key, child); - } else { - qobject_incref(ent->value); - qdict_put_obj(multi_level, ent->key, ent->value); - } - } - QDECREF(two_level); - two_level = NULL; - - /* Step 3: detect if we need to turn our dict into list */ - is_list = qdict_is_list(multi_level, errp); - if (is_list < 0) { - goto error; - } - - if (is_list) { - dst = QOBJECT(qlist_new()); - - for (i = 0; i < qdict_size(multi_level); i++) { - char *key = g_strdup_printf("%zu", i); - - child = qdict_get(multi_level, key); - g_free(key); - - if (!child) { - error_setg(errp, "Missing list index %zu", i); - goto error; - } - - qobject_incref(child); - qlist_append_obj(qobject_to(QList, dst), child); - } - QDECREF(multi_level); - multi_level = NULL; - } else { - dst = QOBJECT(multi_level); - } - - return dst; - - error: - g_free(prefix); - QDECREF(multi_level); - QDECREF(two_level); - qobject_decref(dst); - return NULL; -} - -/** - * qdict_array_entries(): Returns the number of direct array entries if the - * sub-QDict of src specified by the prefix in subqdict (or src itself for - * prefix == "") is valid as an array, i.e. the length of the created list if - * the sub-QDict would become empty after calling qdict_array_split() on it. If - * the array is not valid, -EINVAL is returned. - */ -int qdict_array_entries(QDict *src, const char *subqdict) -{ - const QDictEntry *entry; - unsigned i; - unsigned entries = 0; - size_t subqdict_len = strlen(subqdict); - - assert(!subqdict_len || subqdict[subqdict_len - 1] == '.'); - - /* qdict_array_split() loops until UINT_MAX, but as we want to return - * negative errors, we only have a signed return value here. Any additional - * entries will lead to -EINVAL. */ - for (i = 0; i < INT_MAX; i++) { - QObject *subqobj; - int subqdict_entries; - char *prefix = g_strdup_printf("%s%u.", subqdict, i); - - subqdict_entries = qdict_count_prefixed_entries(src, prefix); - - /* Remove ending "." */ - prefix[strlen(prefix) - 1] = 0; - subqobj = qdict_get(src, prefix); - - g_free(prefix); - - if (subqdict_entries < 0) { - return subqdict_entries; - } - - /* There may be either a single subordinate object (named "%u") or - * multiple objects (each with a key prefixed "%u."), but not both. */ - if (subqobj && subqdict_entries) { - return -EINVAL; - } else if (!subqobj && !subqdict_entries) { - break; - } - - entries += subqdict_entries ? subqdict_entries : 1; - } - - /* Consider everything handled that isn't part of the given sub-QDict */ - for (entry = qdict_first(src); entry; entry = qdict_next(src, entry)) { - if (!strstart(qdict_entry_key(entry), subqdict, NULL)) { - entries++; - } - } - - /* Anything left in the sub-QDict that wasn't handled? */ - if (qdict_size(src) != entries) { - return -EINVAL; - } - - return i; -} - -/** - * qdict_join(): Absorb the src QDict into the dest QDict, that is, move all - * elements from src to dest. - * - * If an element from src has a key already present in dest, it will not be - * moved unless overwrite is true. - * - * If overwrite is true, the conflicting values in dest will be discarded and - * replaced by the corresponding values from src. - * - * Therefore, with overwrite being true, the src QDict will always be empty when - * this function returns. If overwrite is false, the src QDict will be empty - * iff there were no conflicts. - */ -void qdict_join(QDict *dest, QDict *src, bool overwrite) -{ - const QDictEntry *entry, *next; - - entry = qdict_first(src); - while (entry) { - next = qdict_next(src, entry); - - if (overwrite || !qdict_haskey(dest, entry->key)) { - qobject_incref(entry->value); - qdict_put_obj(dest, entry->key, entry->value); - qdict_del(src, entry->key); - } - - entry = next; - } -} - -/** - * qdict_rename_keys(): Rename keys in qdict according to the replacements - * specified in the array renames. The array must be terminated by an entry - * with from = NULL. - * - * The renames are performed individually in the order of the array, so entries - * may be renamed multiple times and may or may not conflict depending on the - * order of the renames array. - * - * Returns true for success, false in error cases. - */ -bool qdict_rename_keys(QDict *qdict, const QDictRenames *renames, Error **errp) -{ - QObject *qobj; - - while (renames->from) { - if (qdict_haskey(qdict, renames->from)) { - if (qdict_haskey(qdict, renames->to)) { - error_setg(errp, "'%s' and its alias '%s' can't be used at the " - "same time", renames->to, renames->from); - return false; - } - - qobj = qdict_get(qdict, renames->from); - qobject_incref(qobj); - qdict_put_obj(qdict, renames->to, qobj); - qdict_del(qdict, renames->from); - } - - renames++; - } - return true; -} diff --git a/qobject/qjson.c b/qobject/qjson.c index 655d38adf1..2f6a590e44 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -59,10 +59,6 @@ QObject *qobject_from_json(const char *string, Error **errp) return qobject_from_jsonv(string, NULL, errp); } -/* - * IMPORTANT: This function aborts on error, thus it must not - * be used with untrusted arguments. - */ QObject *qobject_from_jsonf(const char *string, ...) { QObject *obj; @@ -72,7 +68,24 @@ QObject *qobject_from_jsonf(const char *string, ...) obj = qobject_from_jsonv(string, &ap, &error_abort); va_end(ap); - assert(obj != NULL); + return obj; +} + +/* + * Parse @string as JSON object with %-escapes interpolated. + * Abort on error. Do not use with untrusted @string. + * Return the resulting QDict. It is never null. + */ +QDict *qdict_from_jsonf_nofail(const char *string, ...) +{ + QDict *obj; + va_list ap; + + va_start(ap, string); + obj = qobject_to(QDict, qobject_from_jsonv(string, &ap, &error_abort)); + va_end(ap); + + assert(obj); return obj; } @@ -104,7 +117,7 @@ static void to_json_dict_iter(const char *key, QObject *obj, void *opaque) qkey = qstring_from_str(key); to_json(QOBJECT(qkey), s->str, s->pretty, s->indent); - QDECREF(qkey); + qobject_unref(qkey); qstring_append(s->str, ": "); to_json(obj, s->str, s->pretty, s->indent); diff --git a/qobject/qlist.c b/qobject/qlist.c index 954fe98375..37c1c167f1 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -39,7 +39,7 @@ static void qlist_copy_elem(QObject *obj, void *opaque) { QList *dst = opaque; - qobject_incref(obj); + qobject_ref(obj); qlist_append_obj(dst, obj); } @@ -196,7 +196,7 @@ void qlist_destroy_obj(QObject *obj) QTAILQ_FOREACH_SAFE(entry, &qlist->head, next, next_entry) { QTAILQ_REMOVE(&qlist->head, entry, next); - qobject_decref(entry->value); + qobject_unref(entry->value); g_free(entry); } diff --git a/qobject/qobject.c b/qobject/qobject.c index 23600aa1c1..cf4b7e229e 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -16,6 +16,15 @@ #include "qapi/qmp/qlist.h" #include "qapi/qmp/qstring.h" +QEMU_BUILD_BUG_MSG( + offsetof(QNull, base) != 0 || + offsetof(QNum, base) != 0 || + offsetof(QString, base) != 0 || + offsetof(QDict, base) != 0 || + offsetof(QList, base) != 0 || + offsetof(QBool, base) != 0, + "base qobject must be at offset 0"); + static void (*qdestroy[QTYPE__MAX])(QObject *) = { [QTYPE_NONE] = NULL, /* No such object exists */ [QTYPE_QNULL] = NULL, /* qnull_ is indestructible */ @@ -28,9 +37,9 @@ static void (*qdestroy[QTYPE__MAX])(QObject *) = { void qobject_destroy(QObject *obj) { - assert(!obj->refcnt); - assert(QTYPE_QNULL < obj->type && obj->type < QTYPE__MAX); - qdestroy[obj->type](obj); + assert(!obj->base.refcnt); + assert(QTYPE_QNULL < obj->base.type && obj->base.type < QTYPE__MAX); + qdestroy[obj->base.type](obj); } @@ -53,11 +62,11 @@ bool qobject_is_equal(const QObject *x, const QObject *y) return true; } - if (!x || !y || x->type != y->type) { + if (!x || !y || x->base.type != y->base.type) { return false; } - assert(QTYPE_NONE < x->type && x->type < QTYPE__MAX); + assert(QTYPE_NONE < x->base.type && x->base.type < QTYPE__MAX); - return qis_equal[x->type](x, y); + return qis_equal[x->base.type](x, y); } diff --git a/qobject/qstring.c b/qobject/qstring.c index afca54b47a..0f1510e792 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -37,21 +37,23 @@ size_t qstring_get_length(const QString *qstring) * * Return string reference */ -QString *qstring_from_substr(const char *str, int start, int end) +QString *qstring_from_substr(const char *str, size_t start, size_t end) { QString *qstring; + assert(start <= end); + qstring = g_malloc(sizeof(*qstring)); qobject_init(QOBJECT(qstring), QTYPE_QSTRING); - qstring->length = end - start + 1; + qstring->length = end - start; qstring->capacity = qstring->length; + assert(qstring->capacity < SIZE_MAX); qstring->string = g_malloc(qstring->capacity + 1); memcpy(qstring->string, str + start, qstring->length); qstring->string[qstring->length] = 0; - return qstring; } @@ -62,13 +64,15 @@ QString *qstring_from_substr(const char *str, int start, int end) */ QString *qstring_from_str(const char *str) { - return qstring_from_substr(str, 0, strlen(str) - 1); + return qstring_from_substr(str, 0, strlen(str)); } static void capacity_increase(QString *qstring, size_t len) { if (qstring->capacity < (qstring->length + len)) { + assert(len <= SIZE_MAX - qstring->capacity); qstring->capacity += len; + assert(qstring->capacity <= SIZE_MAX / 2); qstring->capacity *= 2; /* use exponential growth */ qstring->string = g_realloc(qstring->string, qstring->capacity + 1); diff --git a/qom/object.c b/qom/object.c index 467795189c..75d1d48944 100644 --- a/qom/object.c +++ b/qom/object.c @@ -295,7 +295,7 @@ static void type_initialize(TypeImpl *ti) GSList *e; int i; - g_assert_cmpint(parent->class_size, <=, ti->class_size); + g_assert(parent->class_size <= ti->class_size); memcpy(ti->class, parent->class, parent->class_size); ti->class->interfaces = NULL; ti->class->properties = g_hash_table_new_full( @@ -372,9 +372,9 @@ static void object_initialize_with_type(void *data, size_t size, TypeImpl *type) g_assert(type != NULL); type_initialize(type); - g_assert_cmpint(type->instance_size, >=, sizeof(Object)); + g_assert(type->instance_size >= sizeof(Object)); g_assert(type->abstract == false); - g_assert_cmpint(size, >=, type->instance_size); + g_assert(size >= type->instance_size); memset(obj, 0, type->instance_size); obj->class = type->class; @@ -392,6 +392,60 @@ void object_initialize(void *data, size_t size, const char *typename) object_initialize_with_type(data, size, type); } +void object_initialize_child(Object *parentobj, const char *propname, + void *childobj, size_t size, const char *type, + Error **errp, ...) +{ + va_list vargs; + + va_start(vargs, errp); + object_initialize_childv(parentobj, propname, childobj, size, type, errp, + vargs); + va_end(vargs); +} + +void object_initialize_childv(Object *parentobj, const char *propname, + void *childobj, size_t size, const char *type, + Error **errp, va_list vargs) +{ + Error *local_err = NULL; + Object *obj; + + object_initialize(childobj, size, type); + obj = OBJECT(childobj); + + object_set_propv(obj, &local_err, vargs); + if (local_err) { + goto out; + } + + object_property_add_child(parentobj, propname, obj, &local_err); + if (local_err) { + goto out; + } + + if (object_dynamic_cast(obj, TYPE_USER_CREATABLE)) { + user_creatable_complete(obj, &local_err); + if (local_err) { + object_unparent(obj); + goto out; + } + } + + /* + * Since object_property_add_child added a reference to the child object, + * we can drop the reference added by object_initialize(), so the child + * property will own the only reference to the object. + */ + object_unref(obj); + +out: + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + } +} + static inline bool object_property_is_child(ObjectProperty *prop) { return strstart(prop->type, "child<", NULL); @@ -475,7 +529,7 @@ static void object_finalize(void *data) object_property_del_all(obj); object_deinit(obj, ti); - g_assert_cmpint(obj->ref, ==, 0); + g_assert(obj->ref == 0); if (obj->free) { obj->free(obj); } @@ -917,7 +971,7 @@ void object_unref(Object *obj) if (!obj) { return; } - g_assert_cmpint(obj->ref, >, 0); + g_assert(obj->ref > 0); /* parent always holds a reference to its children */ if (atomic_fetch_dec(&obj->ref) == 1) { @@ -1129,7 +1183,7 @@ void object_property_set_str(Object *obj, const char *value, QString *qstr = qstring_from_str(value); object_property_set_qobject(obj, QOBJECT(qstr), name, errp); - QDECREF(qstr); + qobject_unref(qstr); } char *object_property_get_str(Object *obj, const char *name, @@ -1147,7 +1201,7 @@ char *object_property_get_str(Object *obj, const char *name, error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name, "string"); } - qobject_decref(ret); + qobject_unref(ret); return retval; } @@ -1187,7 +1241,7 @@ void object_property_set_bool(Object *obj, bool value, QBool *qbool = qbool_from_bool(value); object_property_set_qobject(obj, QOBJECT(qbool), name, errp); - QDECREF(qbool); + qobject_unref(qbool); } bool object_property_get_bool(Object *obj, const char *name, @@ -1208,7 +1262,7 @@ bool object_property_get_bool(Object *obj, const char *name, retval = qbool_get_bool(qbool); } - qobject_decref(ret); + qobject_unref(ret); return retval; } @@ -1218,7 +1272,7 @@ void object_property_set_int(Object *obj, int64_t value, QNum *qnum = qnum_from_int(value); object_property_set_qobject(obj, QOBJECT(qnum), name, errp); - QDECREF(qnum); + qobject_unref(qnum); } int64_t object_property_get_int(Object *obj, const char *name, @@ -1238,7 +1292,7 @@ int64_t object_property_get_int(Object *obj, const char *name, retval = -1; } - qobject_decref(ret); + qobject_unref(ret); return retval; } @@ -1248,7 +1302,7 @@ void object_property_set_uint(Object *obj, uint64_t value, QNum *qnum = qnum_from_uint(value); object_property_set_qobject(obj, QOBJECT(qnum), name, errp); - QDECREF(qnum); + qobject_unref(qnum); } uint64_t object_property_get_uint(Object *obj, const char *name, @@ -1267,7 +1321,7 @@ uint64_t object_property_get_uint(Object *obj, const char *name, retval = 0; } - qobject_decref(ret); + qobject_unref(ret); return retval; } @@ -1564,9 +1618,11 @@ static void object_set_link_property(Object *obj, Visitor *v, return; } - object_ref(new_target); *child = new_target; - object_unref(old_target); + if (prop->flags == OBJ_PROP_LINK_STRONG) { + object_ref(new_target); + object_unref(old_target); + } } static Object *object_resolve_link_property(Object *parent, void *opaque, const gchar *part) @@ -1581,7 +1637,7 @@ static void object_release_link_property(Object *obj, const char *name, { LinkProperty *prop = opaque; - if ((prop->flags & OBJ_PROP_LINK_UNREF_ON_RELEASE) && *prop->child) { + if ((prop->flags & OBJ_PROP_LINK_STRONG) && *prop->child) { object_unref(*prop->child); } g_free(prop); @@ -1644,8 +1700,9 @@ gchar *object_get_canonical_path_component(Object *obj) ObjectProperty *prop = NULL; GHashTableIter iter; - g_assert(obj); - g_assert(obj->parent != NULL); + if (obj->parent == NULL) { + return NULL; + } g_hash_table_iter_init(&iter, obj->parent->properties); while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&prop)) { @@ -1668,25 +1725,29 @@ gchar *object_get_canonical_path(Object *obj) Object *root = object_get_root(); char *newpath, *path = NULL; - while (obj != root) { - char *component = object_get_canonical_path_component(obj); - - if (path) { - newpath = g_strdup_printf("%s/%s", component, path); - g_free(component); - g_free(path); - path = newpath; - } else { - path = component; - } - - obj = obj->parent; + if (obj == root) { + return g_strdup("/"); } - newpath = g_strdup_printf("/%s", path ? path : ""); - g_free(path); + do { + char *component = object_get_canonical_path_component(obj); - return newpath; + if (!component) { + /* A canonical path must be complete, so discard what was + * collected so far. + */ + g_free(path); + return NULL; + } + + newpath = g_strdup_printf("/%s%s", component, path ? path : ""); + g_free(path); + g_free(component); + path = newpath; + obj = obj->parent; + } while (obj != root); + + return path; } Object *object_resolve_path_component(Object *parent, const gchar *part) diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 2f76e1f36d..72b97a8bed 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -65,12 +65,6 @@ Object *user_creatable_add_type(const char *type, const char *id, assert(qdict); obj = object_new(type); - if (object_property_find(obj, "id", NULL)) { - object_property_set_str(obj, id, "id", &local_err); - if (local_err) { - goto out; - } - } visit_start_struct(v, NULL, NULL, 0, &local_err); if (local_err) { goto out; @@ -140,7 +134,7 @@ Object *user_creatable_add_opts(QemuOpts *opts, Error **errp) qemu_opts_set_id(opts, (char *) id); qemu_opt_set(opts, "qom-type", type, &error_abort); g_free(type); - QDECREF(pdict); + qobject_unref(pdict); return obj; } diff --git a/qtest.c b/qtest.c index cbbfb71114..69b9e9962b 100644 --- a/qtest.c +++ b/qtest.c @@ -387,19 +387,23 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (words[0][5] == 'b') { uint8_t data = value; - cpu_physical_memory_write(addr, &data, 1); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 1, true); } else if (words[0][5] == 'w') { uint16_t data = value; tswap16s(&data); - cpu_physical_memory_write(addr, &data, 2); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 2, true); } else if (words[0][5] == 'l') { uint32_t data = value; tswap32s(&data); - cpu_physical_memory_write(addr, &data, 4); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 4, true); } else if (words[0][5] == 'q') { uint64_t data = value; tswap64s(&data); - cpu_physical_memory_write(addr, &data, 8); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 8, true); } qtest_send_prefix(chr); qtest_send(chr, "OK\n"); @@ -417,18 +421,22 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (words[0][4] == 'b') { uint8_t data; - cpu_physical_memory_read(addr, &data, 1); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + &data, 1, false); value = data; } else if (words[0][4] == 'w') { uint16_t data; - cpu_physical_memory_read(addr, &data, 2); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 2, false); value = tswap16(data); } else if (words[0][4] == 'l') { uint32_t data; - cpu_physical_memory_read(addr, &data, 4); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &data, 4, false); value = tswap32(data); } else if (words[0][4] == 'q') { - cpu_physical_memory_read(addr, &value, 8); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *) &value, 8, false); tswap64s(&value); } qtest_send_prefix(chr); @@ -448,7 +456,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_assert(len); data = g_malloc(len); - cpu_physical_memory_read(addr, data, len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, false); enc = g_malloc(2 * len + 1); for (i = 0; i < len; i++) { @@ -473,7 +482,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) g_assert(ret == 0); data = g_malloc(len); - cpu_physical_memory_read(addr, data, len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, false); b64_data = g_base64_encode(data, len); qtest_send_prefix(chr); qtest_sendf(chr, "OK %s\n", b64_data); @@ -507,7 +517,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) data[i] = 0; } } - cpu_physical_memory_write(addr, data, len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, true); g_free(data); qtest_send_prefix(chr); @@ -529,7 +540,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (len) { data = g_malloc(len); memset(data, pattern, len); - cpu_physical_memory_write(addr, data, len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, true); g_free(data); } @@ -562,7 +574,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) out_len = MIN(out_len, len); } - cpu_physical_memory_write(addr, data, out_len); + address_space_rw(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, + data, len, true); qtest_send_prefix(chr); qtest_send(chr, "OK\n"); diff --git a/roms/Makefile b/roms/Makefile index 02b69fbac8..f1ac85ae9b 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -1,5 +1,5 @@ -vgabios_variants := stdvga cirrus vmware qxl isavga virtio +vgabios_variants := stdvga cirrus vmware qxl isavga virtio bochs-display ramfb vgabios_targets := $(subst -isavga,,$(patsubst %,vgabios-%.bin,$(vgabios_variants))) pxerom_variants := e1000 e1000e eepro100 ne2k_pci pcnet rtl8139 virtio vmxnet3 pxerom_targets := 8086100e 808610d3 80861209 10500940 10222000 10ec8139 1af41000 15ad07b0 @@ -56,8 +56,7 @@ default: @echo "nothing is build by default" @echo "available build targets:" @echo " bios -- update bios.bin (seabios)" - @echo " seavgabios -- update vgabios binaries (seabios)" - @echo " lgplvgabios -- update vgabios binaries (lgpl)" + @echo " vgabios -- update vgabios binaries (seabios)" @echo " sgabios -- update sgabios binaries" @echo " pxerom -- update nic roms (bios only)" @echo " efirom -- update nic roms (bios+efi, this needs" @@ -71,7 +70,7 @@ bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k cp seabios/builds/seabios-128k/bios.bin ../pc-bios/bios.bin cp seabios/builds/seabios-256k/bios.bin ../pc-bios/bios-256k.bin -seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants)) +vgabios seavgabios: $(patsubst %,seavgabios-%,$(vgabios_variants)) seavgabios-isavga: build-seabios-config-vga-isavga cp seabios/builds/vga-isavga/vgabios.bin ../pc-bios/vgabios.bin @@ -94,17 +93,6 @@ build-seabios-config-%: config.% OUT=$(CURDIR)/seabios/builds/$*/ all -lgplvgabios: $(patsubst %,lgplvgabios-%,$(vgabios_variants)) - -lgplvgabios-isavga: build-lgplvgabios - cp vgabios/VGABIOS-lgpl-latest.bin ../pc-bios/vgabios.bin -lgplvgabios-%: build-lgplvgabios - cp vgabios/VGABIOS-lgpl-latest.$*.bin ../pc-bios/vgabios-$*.bin - -build-lgplvgabios: - $(MAKE) -C vgabios $(vgabios_targets) - - .PHONY: sgabios skiboot sgabios: $(MAKE) -C sgabios @@ -159,8 +147,6 @@ skiboot: clean: rm -rf seabios/.config seabios/out seabios/builds - $(MAKE) -C vgabios clean - rm -f vgabios/VGABIOS-lgpl-latest* $(MAKE) -C sgabios clean rm -f sgabios/.depend $(MAKE) -C ipxe/src veryclean diff --git a/roms/SLOF b/roms/SLOF index fa981320a1..7d37babcfa 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit fa981320a1e0968d6fc1b8de319723ff8212b337 +Subproject commit 7d37babcfa48a6eb08e726a8d13b745cb2eebe1c diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k index 486ef0e132..35b5a07d8f 100644 --- a/roms/config.seabios-128k +++ b/roms/config.seabios-128k @@ -2,6 +2,7 @@ # need to turn off features (xhci,uas) to make it fit into 128k CONFIG_QEMU=y CONFIG_ROM_SIZE=128 +CONFIG_ATA_DMA=y CONFIG_BOOTSPLASH=n CONFIG_XEN=n CONFIG_USB_OHCI=n diff --git a/roms/config.seabios-256k b/roms/config.seabios-256k index 65e5015c2f..b14b614fcc 100644 --- a/roms/config.seabios-256k +++ b/roms/config.seabios-256k @@ -1,3 +1,4 @@ # for qemu machine types 2.0 + newer CONFIG_QEMU=y CONFIG_ROM_SIZE=256 +CONFIG_ATA_DMA=y diff --git a/roms/config.vga-bochs-display b/roms/config.vga-bochs-display new file mode 100644 index 0000000000..d2adaaef66 --- /dev/null +++ b/roms/config.vga-bochs-display @@ -0,0 +1,3 @@ +CONFIG_BUILD_VGABIOS=y +CONFIG_DISPLAY_BOCHS=y +CONFIG_VGA_PCI=y diff --git a/roms/config.vga-ramfb b/roms/config.vga-ramfb new file mode 100644 index 0000000000..c809c799b9 --- /dev/null +++ b/roms/config.vga-ramfb @@ -0,0 +1,3 @@ +CONFIG_BUILD_VGABIOS=y +CONFIG_VGA_RAMFB=y +CONFIG_VGA_PCI=n diff --git a/roms/openbios b/roms/openbios index 54d959d97f..8fe6f5f96f 160000 --- a/roms/openbios +++ b/roms/openbios @@ -1 +1 @@ -Subproject commit 54d959d97fb331708767b2fd4a878efd2bbc41bb +Subproject commit 8fe6f5f96f6ca39f1f62200be7fa130e929f13f2 diff --git a/roms/seabios b/roms/seabios index 0551a4be2c..f9626ccb91 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit 0551a4be2ce599fb60e478b4c15e06ab6587822c +Subproject commit f9626ccb91e771f990fbb2da92e427a399d7d918 diff --git a/roms/u-boot-sam460ex b/roms/u-boot-sam460ex index 8ee007c421..60b3916f33 160000 --- a/roms/u-boot-sam460ex +++ b/roms/u-boot-sam460ex @@ -1 +1 @@ -Subproject commit 8ee007c4216fd6a0d760589e8405ce4494497aa0 +Subproject commit 60b3916f33e617a815973c5a6df77055b2e3a588 diff --git a/roms/vgabios b/roms/vgabios deleted file mode 160000 index 19ea12c230..0000000000 --- a/roms/vgabios +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 19ea12c230ded95928ecaef0db47a82231c2e485 diff --git a/rules.mak b/rules.mak index 93a07027b0..bbb2667928 100644 --- a/rules.mak +++ b/rules.mak @@ -1,4 +1,7 @@ +# These are used when we want to do substitutions without confusing Make +NULL := +SPACE := $(NULL) # COMMA := , # Don't use implicit rules or variables @@ -73,7 +76,7 @@ expand-objs = $(strip $(sort $(filter %.o,$1)) \ # must link with the C++ compiler, not the plain C compiler. LINKPROG = $(or $(CXX),$(CC)) -LINK = $(call quiet-command, $(LINKPROG) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ +LINK = $(call quiet-command, $(LINKPROG) $(QEMU_LDFLAGS) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ \ $(call process-archive-undefs, $1) \ $(version-obj-y) $(call extract-libs,$1) $(LIBS),"LINK","$(TARGET_DIR)$@") diff --git a/scripts/analyse-9p-simpletrace.py b/scripts/analyse-9p-simpletrace.py index 3c3dee4337..710e01adba 100755 --- a/scripts/analyse-9p-simpletrace.py +++ b/scripts/analyse-9p-simpletrace.py @@ -3,6 +3,7 @@ # Usage: ./analyse-9p-simpletrace # # Author: Harsh Prateek Bora +from __future__ import print_function import os import simpletrace @@ -79,135 +80,135 @@ symbol_9p = { class VirtFSRequestTracker(simpletrace.Analyzer): def begin(self): - print "Pretty printing 9p simpletrace log ..." + print("Pretty printing 9p simpletrace log ...") def v9fs_rerror(self, tag, id, err): - print "RERROR (tag =", tag, ", id =", symbol_9p[id], ", err = \"", os.strerror(err), "\")" + print("RERROR (tag =", tag, ", id =", symbol_9p[id], ", err = \"", os.strerror(err), "\")") def v9fs_version(self, tag, id, msize, version): - print "TVERSION (tag =", tag, ", msize =", msize, ", version =", version, ")" + print("TVERSION (tag =", tag, ", msize =", msize, ", version =", version, ")") def v9fs_version_return(self, tag, id, msize, version): - print "RVERSION (tag =", tag, ", msize =", msize, ", version =", version, ")" + print("RVERSION (tag =", tag, ", msize =", msize, ", version =", version, ")") def v9fs_attach(self, tag, id, fid, afid, uname, aname): - print "TATTACH (tag =", tag, ", fid =", fid, ", afid =", afid, ", uname =", uname, ", aname =", aname, ")" + print("TATTACH (tag =", tag, ", fid =", fid, ", afid =", afid, ", uname =", uname, ", aname =", aname, ")") def v9fs_attach_return(self, tag, id, type, version, path): - print "RATTACH (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})" + print("RATTACH (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})") def v9fs_stat(self, tag, id, fid): - print "TSTAT (tag =", tag, ", fid =", fid, ")" + print("TSTAT (tag =", tag, ", fid =", fid, ")") def v9fs_stat_return(self, tag, id, mode, atime, mtime, length): - print "RSTAT (tag =", tag, ", mode =", mode, ", atime =", atime, ", mtime =", mtime, ", length =", length, ")" + print("RSTAT (tag =", tag, ", mode =", mode, ", atime =", atime, ", mtime =", mtime, ", length =", length, ")") def v9fs_getattr(self, tag, id, fid, request_mask): - print "TGETATTR (tag =", tag, ", fid =", fid, ", request_mask =", hex(request_mask), ")" + print("TGETATTR (tag =", tag, ", fid =", fid, ", request_mask =", hex(request_mask), ")") def v9fs_getattr_return(self, tag, id, result_mask, mode, uid, gid): - print "RGETATTR (tag =", tag, ", result_mask =", hex(result_mask), ", mode =", oct(mode), ", uid =", uid, ", gid =", gid, ")" + print("RGETATTR (tag =", tag, ", result_mask =", hex(result_mask), ", mode =", oct(mode), ", uid =", uid, ", gid =", gid, ")") def v9fs_walk(self, tag, id, fid, newfid, nwnames): - print "TWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", nwnames =", nwnames, ")" + print("TWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", nwnames =", nwnames, ")") def v9fs_walk_return(self, tag, id, nwnames, qids): - print "RWALK (tag =", tag, ", nwnames =", nwnames, ", qids =", hex(qids), ")" + print("RWALK (tag =", tag, ", nwnames =", nwnames, ", qids =", hex(qids), ")") def v9fs_open(self, tag, id, fid, mode): - print "TOPEN (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ")" + print("TOPEN (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ")") def v9fs_open_return(self, tag, id, type, version, path, iounit): - print "ROPEN (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")" + print("ROPEN (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")") def v9fs_lcreate(self, tag, id, dfid, flags, mode, gid): - print "TLCREATE (tag =", tag, ", dfid =", dfid, ", flags =", oct(flags), ", mode =", oct(mode), ", gid =", gid, ")" + print("TLCREATE (tag =", tag, ", dfid =", dfid, ", flags =", oct(flags), ", mode =", oct(mode), ", gid =", gid, ")") def v9fs_lcreate_return(self, tag, id, type, version, path, iounit): - print "RLCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")" + print("RLCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")") def v9fs_fsync(self, tag, id, fid, datasync): - print "TFSYNC (tag =", tag, ", fid =", fid, ", datasync =", datasync, ")" + print("TFSYNC (tag =", tag, ", fid =", fid, ", datasync =", datasync, ")") def v9fs_clunk(self, tag, id, fid): - print "TCLUNK (tag =", tag, ", fid =", fid, ")" + print("TCLUNK (tag =", tag, ", fid =", fid, ")") def v9fs_read(self, tag, id, fid, off, max_count): - print "TREAD (tag =", tag, ", fid =", fid, ", off =", off, ", max_count =", max_count, ")" + print("TREAD (tag =", tag, ", fid =", fid, ", off =", off, ", max_count =", max_count, ")") def v9fs_read_return(self, tag, id, count, err): - print "RREAD (tag =", tag, ", count =", count, ", err =", err, ")" + print("RREAD (tag =", tag, ", count =", count, ", err =", err, ")") def v9fs_readdir(self, tag, id, fid, offset, max_count): - print "TREADDIR (tag =", tag, ", fid =", fid, ", offset =", offset, ", max_count =", max_count, ")" + print("TREADDIR (tag =", tag, ", fid =", fid, ", offset =", offset, ", max_count =", max_count, ")") def v9fs_readdir_return(self, tag, id, count, retval): - print "RREADDIR (tag =", tag, ", count =", count, ", retval =", retval, ")" + print("RREADDIR (tag =", tag, ", count =", count, ", retval =", retval, ")") def v9fs_write(self, tag, id, fid, off, count, cnt): - print "TWRITE (tag =", tag, ", fid =", fid, ", off =", off, ", count =", count, ", cnt =", cnt, ")" + print("TWRITE (tag =", tag, ", fid =", fid, ", off =", off, ", count =", count, ", cnt =", cnt, ")") def v9fs_write_return(self, tag, id, total, err): - print "RWRITE (tag =", tag, ", total =", total, ", err =", err, ")" + print("RWRITE (tag =", tag, ", total =", total, ", err =", err, ")") def v9fs_create(self, tag, id, fid, name, perm, mode): - print "TCREATE (tag =", tag, ", fid =", fid, ", perm =", oct(perm), ", name =", name, ", mode =", oct(mode), ")" + print("TCREATE (tag =", tag, ", fid =", fid, ", perm =", oct(perm), ", name =", name, ", mode =", oct(mode), ")") def v9fs_create_return(self, tag, id, type, version, path, iounit): - print "RCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")" + print("RCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")") def v9fs_symlink(self, tag, id, fid, name, symname, gid): - print "TSYMLINK (tag =", tag, ", fid =", fid, ", name =", name, ", symname =", symname, ", gid =", gid, ")" + print("TSYMLINK (tag =", tag, ", fid =", fid, ", name =", name, ", symname =", symname, ", gid =", gid, ")") def v9fs_symlink_return(self, tag, id, type, version, path): - print "RSYMLINK (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})" + print("RSYMLINK (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})") def v9fs_flush(self, tag, id, flush_tag): - print "TFLUSH (tag =", tag, ", flush_tag =", flush_tag, ")" + print("TFLUSH (tag =", tag, ", flush_tag =", flush_tag, ")") def v9fs_link(self, tag, id, dfid, oldfid, name): - print "TLINK (tag =", tag, ", dfid =", dfid, ", oldfid =", oldfid, ", name =", name, ")" + print("TLINK (tag =", tag, ", dfid =", dfid, ", oldfid =", oldfid, ", name =", name, ")") def v9fs_remove(self, tag, id, fid): - print "TREMOVE (tag =", tag, ", fid =", fid, ")" + print("TREMOVE (tag =", tag, ", fid =", fid, ")") def v9fs_wstat(self, tag, id, fid, mode, atime, mtime): - print "TWSTAT (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", atime =", atime, "mtime =", mtime, ")" + print("TWSTAT (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", atime =", atime, "mtime =", mtime, ")") def v9fs_mknod(self, tag, id, fid, mode, major, minor): - print "TMKNOD (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", major =", major, ", minor =", minor, ")" + print("TMKNOD (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", major =", major, ", minor =", minor, ")") def v9fs_lock(self, tag, id, fid, type, start, length): - print "TLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")" + print("TLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")") def v9fs_lock_return(self, tag, id, status): - print "RLOCK (tag =", tag, ", status =", status, ")" + print("RLOCK (tag =", tag, ", status =", status, ")") def v9fs_getlock(self, tag, id, fid, type, start, length): - print "TGETLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")" + print("TGETLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")") def v9fs_getlock_return(self, tag, id, type, start, length, proc_id): - print "RGETLOCK (tag =", tag, "type =", type, ", start =", start, ", length =", length, ", proc_id =", proc_id, ")" + print("RGETLOCK (tag =", tag, "type =", type, ", start =", start, ", length =", length, ", proc_id =", proc_id, ")") def v9fs_mkdir(self, tag, id, fid, name, mode, gid): - print "TMKDIR (tag =", tag, ", fid =", fid, ", name =", name, ", mode =", mode, ", gid =", gid, ")" + print("TMKDIR (tag =", tag, ", fid =", fid, ", name =", name, ", mode =", mode, ", gid =", gid, ")") def v9fs_mkdir_return(self, tag, id, type, version, path, err): - print "RMKDIR (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, err =", err, ")" + print("RMKDIR (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, err =", err, ")") def v9fs_xattrwalk(self, tag, id, fid, newfid, name): - print "TXATTRWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", xattr name =", name, ")" + print("TXATTRWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", xattr name =", name, ")") def v9fs_xattrwalk_return(self, tag, id, size): - print "RXATTRWALK (tag =", tag, ", xattrsize =", size, ")" + print("RXATTRWALK (tag =", tag, ", xattrsize =", size, ")") def v9fs_xattrcreate(self, tag, id, fid, name, size, flags): - print "TXATTRCREATE (tag =", tag, ", fid =", fid, ", name =", name, ", xattrsize =", size, ", flags =", flags, ")" + print("TXATTRCREATE (tag =", tag, ", fid =", fid, ", name =", name, ", xattrsize =", size, ", flags =", flags, ")") def v9fs_readlink(self, tag, id, fid): - print "TREADLINK (tag =", tag, ", fid =", fid, ")" + print("TREADLINK (tag =", tag, ", fid =", fid, ")") def v9fs_readlink_return(self, tag, id, target): - print "RREADLINK (tag =", tag, ", target =", target, ")" + print("RREADLINK (tag =", tag, ", target =", target, ")") simpletrace.run(VirtFSRequestTracker()) diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py index 101e84dea5..30090bdfff 100755 --- a/scripts/analyse-locks-simpletrace.py +++ b/scripts/analyse-locks-simpletrace.py @@ -6,6 +6,7 @@ # Author: Alex Bennée # +from __future__ import print_function import os import simpletrace import argparse @@ -77,7 +78,7 @@ if __name__ == '__main__': # Now dump the individual lock stats for key, val in sorted(analyser.mutex_records.iteritems(), - key=lambda (k,v): v["locks"]): + key=lambda k_v: k_v[1]["locks"]): print ("Lock: %#x locks: %d, locked: %d, unlocked: %d" % (key, val["locks"], val["locked"], val["unlocked"])) diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index 88ff4adb30..5c2010c917 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see . +from __future__ import print_function import numpy as np import json import os @@ -162,7 +163,7 @@ class RamSection(object): len = self.file.read64() self.sizeinfo[self.name] = '0x%016x' % len if self.write_memory: - print self.name + print(self.name) mkdir_p('./' + os.path.dirname(self.name)) f = open('./' + self.name, "wb") f.truncate(0) @@ -588,7 +589,7 @@ if args.extract: dump = MigrationDump(args.file) dump.read(desc_only = True) - print "desc.json" + print("desc.json") f = open("desc.json", "wb") f.truncate() f.write(jsonenc.encode(dump.vmsd_desc)) @@ -596,7 +597,7 @@ if args.extract: dump.read(write_memory = True) dict = dump.getDict() - print "state.json" + print("state.json") f = open("state.json", "wb") f.truncate() f.write(jsonenc.encode(dict)) @@ -605,10 +606,10 @@ elif args.dump == "state": dump = MigrationDump(args.file) dump.read(dump_memory = args.memory) dict = dump.getDict() - print jsonenc.encode(dict) + print(jsonenc.encode(dict)) elif args.dump == "desc": dump = MigrationDump(args.file) dump.read(desc_only = True) - print jsonenc.encode(dump.vmsd_desc) + print(jsonenc.encode(dump.vmsd_desc)) else: raise Exception("Please specify either -x, -d state or -d dump") diff --git a/scripts/argparse.py b/scripts/argparse.py deleted file mode 100644 index 27d1f28935..0000000000 --- a/scripts/argparse.py +++ /dev/null @@ -1,2406 +0,0 @@ -# This is a local copy of the standard library argparse module taken from PyPI. -# It is licensed under the Python Software Foundation License. This is a -# fallback for Python 2.6 which does not include this module. Python 2.7+ and -# 3+ will never load this module because built-in modules are loaded before -# anything in sys.path. -# -# If your script is not located in the same directory as this file, import it -# like this: -# -# import os -# import sys -# sys.path.append(os.path.join(os.path.dirname(__file__), ..., 'scripts')) -# import argparse - -# Author: Steven J. Bethard . -# Maintainer: Thomas Waldmann - -"""Command-line parsing library - -This module is an optparse-inspired command-line parsing library that: - - - handles both optional and positional arguments - - produces highly informative usage messages - - supports parsers that dispatch to sub-parsers - -The following is a simple usage example that sums integers from the -command-line and writes the result to a file:: - - parser = argparse.ArgumentParser( - description='sum the integers at the command line') - parser.add_argument( - 'integers', metavar='int', nargs='+', type=int, - help='an integer to be summed') - parser.add_argument( - '--log', default=sys.stdout, type=argparse.FileType('w'), - help='the file where the sum should be written') - args = parser.parse_args() - args.log.write('%s' % sum(args.integers)) - args.log.close() - -The module contains the following public classes: - - - ArgumentParser -- The main entry point for command-line parsing. As the - example above shows, the add_argument() method is used to populate - the parser with actions for optional and positional arguments. Then - the parse_args() method is invoked to convert the args at the - command-line into an object with attributes. - - - ArgumentError -- The exception raised by ArgumentParser objects when - there are errors with the parser's actions. Errors raised while - parsing the command-line are caught by ArgumentParser and emitted - as command-line messages. - - - FileType -- A factory for defining types of files to be created. As the - example above shows, instances of FileType are typically passed as - the type= argument of add_argument() calls. - - - Action -- The base class for parser actions. Typically actions are - selected by passing strings like 'store_true' or 'append_const' to - the action= argument of add_argument(). However, for greater - customization of ArgumentParser actions, subclasses of Action may - be defined and passed as the action= argument. - - - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, - ArgumentDefaultsHelpFormatter -- Formatter classes which - may be passed as the formatter_class= argument to the - ArgumentParser constructor. HelpFormatter is the default, - RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser - not to change the formatting for help text, and - ArgumentDefaultsHelpFormatter adds information about argument defaults - to the help. - -All other classes in this module are considered implementation details. -(Also note that HelpFormatter and RawDescriptionHelpFormatter are only -considered public as object names -- the API of the formatter objects is -still considered an implementation detail.) -""" - -__version__ = '1.4.0' # we use our own version number independent of the - # one in stdlib and we release this on pypi. - -__external_lib__ = True # to make sure the tests really test THIS lib, - # not the builtin one in Python stdlib - -__all__ = [ - 'ArgumentParser', - 'ArgumentError', - 'ArgumentTypeError', - 'FileType', - 'HelpFormatter', - 'ArgumentDefaultsHelpFormatter', - 'RawDescriptionHelpFormatter', - 'RawTextHelpFormatter', - 'Namespace', - 'Action', - 'ONE_OR_MORE', - 'OPTIONAL', - 'PARSER', - 'REMAINDER', - 'SUPPRESS', - 'ZERO_OR_MORE', -] - - -import copy as _copy -import os as _os -import re as _re -import sys as _sys -import textwrap as _textwrap - -from gettext import gettext as _ - -try: - set -except NameError: - # for python < 2.4 compatibility (sets module is there since 2.3): - from sets import Set as set - -try: - basestring -except NameError: - basestring = str - -try: - sorted -except NameError: - # for python < 2.4 compatibility: - def sorted(iterable, reverse=False): - result = list(iterable) - result.sort() - if reverse: - result.reverse() - return result - - -def _callable(obj): - return hasattr(obj, '__call__') or hasattr(obj, '__bases__') - - -SUPPRESS = '==SUPPRESS==' - -OPTIONAL = '?' -ZERO_OR_MORE = '*' -ONE_OR_MORE = '+' -PARSER = 'A...' -REMAINDER = '...' -_UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args' - -# ============================= -# Utility functions and classes -# ============================= - -class _AttributeHolder(object): - """Abstract base class that provides __repr__. - - The __repr__ method returns a string in the format:: - ClassName(attr=name, attr=name, ...) - The attributes are determined either by a class-level attribute, - '_kwarg_names', or by inspecting the instance __dict__. - """ - - def __repr__(self): - type_name = type(self).__name__ - arg_strings = [] - for arg in self._get_args(): - arg_strings.append(repr(arg)) - for name, value in self._get_kwargs(): - arg_strings.append('%s=%r' % (name, value)) - return '%s(%s)' % (type_name, ', '.join(arg_strings)) - - def _get_kwargs(self): - return sorted(self.__dict__.items()) - - def _get_args(self): - return [] - - -def _ensure_value(namespace, name, value): - if getattr(namespace, name, None) is None: - setattr(namespace, name, value) - return getattr(namespace, name) - - -# =============== -# Formatting Help -# =============== - -class HelpFormatter(object): - """Formatter for generating usage messages and argument help strings. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def __init__(self, - prog, - indent_increment=2, - max_help_position=24, - width=None): - - # default setting for width - if width is None: - try: - width = int(_os.environ['COLUMNS']) - except (KeyError, ValueError): - width = 80 - width -= 2 - - self._prog = prog - self._indent_increment = indent_increment - self._max_help_position = max_help_position - self._width = width - - self._current_indent = 0 - self._level = 0 - self._action_max_length = 0 - - self._root_section = self._Section(self, None) - self._current_section = self._root_section - - self._whitespace_matcher = _re.compile(r'\s+') - self._long_break_matcher = _re.compile(r'\n\n\n+') - - # =============================== - # Section and indentation methods - # =============================== - def _indent(self): - self._current_indent += self._indent_increment - self._level += 1 - - def _dedent(self): - self._current_indent -= self._indent_increment - assert self._current_indent >= 0, 'Indent decreased below 0.' - self._level -= 1 - - class _Section(object): - - def __init__(self, formatter, parent, heading=None): - self.formatter = formatter - self.parent = parent - self.heading = heading - self.items = [] - - def format_help(self): - # format the indented section - if self.parent is not None: - self.formatter._indent() - join = self.formatter._join_parts - for func, args in self.items: - func(*args) - item_help = join([func(*args) for func, args in self.items]) - if self.parent is not None: - self.formatter._dedent() - - # return nothing if the section was empty - if not item_help: - return '' - - # add the heading if the section was non-empty - if self.heading is not SUPPRESS and self.heading is not None: - current_indent = self.formatter._current_indent - heading = '%*s%s:\n' % (current_indent, '', self.heading) - else: - heading = '' - - # join the section-initial newline, the heading and the help - return join(['\n', heading, item_help, '\n']) - - def _add_item(self, func, args): - self._current_section.items.append((func, args)) - - # ======================== - # Message building methods - # ======================== - def start_section(self, heading): - self._indent() - section = self._Section(self, self._current_section, heading) - self._add_item(section.format_help, []) - self._current_section = section - - def end_section(self): - self._current_section = self._current_section.parent - self._dedent() - - def add_text(self, text): - if text is not SUPPRESS and text is not None: - self._add_item(self._format_text, [text]) - - def add_usage(self, usage, actions, groups, prefix=None): - if usage is not SUPPRESS: - args = usage, actions, groups, prefix - self._add_item(self._format_usage, args) - - def add_argument(self, action): - if action.help is not SUPPRESS: - - # find all invocations - get_invocation = self._format_action_invocation - invocations = [get_invocation(action)] - for subaction in self._iter_indented_subactions(action): - invocations.append(get_invocation(subaction)) - - # update the maximum item length - invocation_length = max([len(s) for s in invocations]) - action_length = invocation_length + self._current_indent - self._action_max_length = max(self._action_max_length, - action_length) - - # add the item to the list - self._add_item(self._format_action, [action]) - - def add_arguments(self, actions): - for action in actions: - self.add_argument(action) - - # ======================= - # Help-formatting methods - # ======================= - def format_help(self): - help = self._root_section.format_help() - if help: - help = self._long_break_matcher.sub('\n\n', help) - help = help.strip('\n') + '\n' - return help - - def _join_parts(self, part_strings): - return ''.join([part - for part in part_strings - if part and part is not SUPPRESS]) - - def _format_usage(self, usage, actions, groups, prefix): - if prefix is None: - prefix = _('usage: ') - - # if usage is specified, use that - if usage is not None: - usage = usage % dict(prog=self._prog) - - # if no optionals or positionals are available, usage is just prog - elif usage is None and not actions: - usage = '%(prog)s' % dict(prog=self._prog) - - # if optionals and positionals are available, calculate usage - elif usage is None: - prog = '%(prog)s' % dict(prog=self._prog) - - # split optionals from positionals - optionals = [] - positionals = [] - for action in actions: - if action.option_strings: - optionals.append(action) - else: - positionals.append(action) - - # build full usage string - format = self._format_actions_usage - action_usage = format(optionals + positionals, groups) - usage = ' '.join([s for s in [prog, action_usage] if s]) - - # wrap the usage parts if it's too long - text_width = self._width - self._current_indent - if len(prefix) + len(usage) > text_width: - - # break usage into wrappable parts - part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' - opt_usage = format(optionals, groups) - pos_usage = format(positionals, groups) - opt_parts = _re.findall(part_regexp, opt_usage) - pos_parts = _re.findall(part_regexp, pos_usage) - assert ' '.join(opt_parts) == opt_usage - assert ' '.join(pos_parts) == pos_usage - - # helper for wrapping lines - def get_lines(parts, indent, prefix=None): - lines = [] - line = [] - if prefix is not None: - line_len = len(prefix) - 1 - else: - line_len = len(indent) - 1 - for part in parts: - if line_len + 1 + len(part) > text_width: - lines.append(indent + ' '.join(line)) - line = [] - line_len = len(indent) - 1 - line.append(part) - line_len += len(part) + 1 - if line: - lines.append(indent + ' '.join(line)) - if prefix is not None: - lines[0] = lines[0][len(indent):] - return lines - - # if prog is short, follow it with optionals or positionals - if len(prefix) + len(prog) <= 0.75 * text_width: - indent = ' ' * (len(prefix) + len(prog) + 1) - if opt_parts: - lines = get_lines([prog] + opt_parts, indent, prefix) - lines.extend(get_lines(pos_parts, indent)) - elif pos_parts: - lines = get_lines([prog] + pos_parts, indent, prefix) - else: - lines = [prog] - - # if prog is long, put it on its own line - else: - indent = ' ' * len(prefix) - parts = opt_parts + pos_parts - lines = get_lines(parts, indent) - if len(lines) > 1: - lines = [] - lines.extend(get_lines(opt_parts, indent)) - lines.extend(get_lines(pos_parts, indent)) - lines = [prog] + lines - - # join lines into usage - usage = '\n'.join(lines) - - # prefix with 'usage:' - return '%s%s\n\n' % (prefix, usage) - - def _format_actions_usage(self, actions, groups): - # find group indices and identify actions in groups - group_actions = set() - inserts = {} - for group in groups: - try: - start = actions.index(group._group_actions[0]) - except ValueError: - continue - else: - end = start + len(group._group_actions) - if actions[start:end] == group._group_actions: - for action in group._group_actions: - group_actions.add(action) - if not group.required: - if start in inserts: - inserts[start] += ' [' - else: - inserts[start] = '[' - inserts[end] = ']' - else: - if start in inserts: - inserts[start] += ' (' - else: - inserts[start] = '(' - inserts[end] = ')' - for i in range(start + 1, end): - inserts[i] = '|' - - # collect all actions format strings - parts = [] - for i, action in enumerate(actions): - - # suppressed arguments are marked with None - # remove | separators for suppressed arguments - if action.help is SUPPRESS: - parts.append(None) - if inserts.get(i) == '|': - inserts.pop(i) - elif inserts.get(i + 1) == '|': - inserts.pop(i + 1) - - # produce all arg strings - elif not action.option_strings: - part = self._format_args(action, action.dest) - - # if it's in a group, strip the outer [] - if action in group_actions: - if part[0] == '[' and part[-1] == ']': - part = part[1:-1] - - # add the action string to the list - parts.append(part) - - # produce the first way to invoke the option in brackets - else: - option_string = action.option_strings[0] - - # if the Optional doesn't take a value, format is: - # -s or --long - if action.nargs == 0: - part = '%s' % option_string - - # if the Optional takes a value, format is: - # -s ARGS or --long ARGS - else: - default = action.dest.upper() - args_string = self._format_args(action, default) - part = '%s %s' % (option_string, args_string) - - # make it look optional if it's not required or in a group - if not action.required and action not in group_actions: - part = '[%s]' % part - - # add the action string to the list - parts.append(part) - - # insert things at the necessary indices - for i in sorted(inserts, reverse=True): - parts[i:i] = [inserts[i]] - - # join all the action items with spaces - text = ' '.join([item for item in parts if item is not None]) - - # clean up separators for mutually exclusive groups - open = r'[\[(]' - close = r'[\])]' - text = _re.sub(r'(%s) ' % open, r'\1', text) - text = _re.sub(r' (%s)' % close, r'\1', text) - text = _re.sub(r'%s *%s' % (open, close), r'', text) - text = _re.sub(r'\(([^|]*)\)', r'\1', text) - text = text.strip() - - # return the text - return text - - def _format_text(self, text): - if '%(prog)' in text: - text = text % dict(prog=self._prog) - text_width = self._width - self._current_indent - indent = ' ' * self._current_indent - return self._fill_text(text, text_width, indent) + '\n\n' - - def _format_action(self, action): - # determine the required width and the entry label - help_position = min(self._action_max_length + 2, - self._max_help_position) - help_width = self._width - help_position - action_width = help_position - self._current_indent - 2 - action_header = self._format_action_invocation(action) - - # ho nelp; start on same line and add a final newline - if not action.help: - tup = self._current_indent, '', action_header - action_header = '%*s%s\n' % tup - - # short action name; start on the same line and pad two spaces - elif len(action_header) <= action_width: - tup = self._current_indent, '', action_width, action_header - action_header = '%*s%-*s ' % tup - indent_first = 0 - - # long action name; start on the next line - else: - tup = self._current_indent, '', action_header - action_header = '%*s%s\n' % tup - indent_first = help_position - - # collect the pieces of the action help - parts = [action_header] - - # if there was help for the action, add lines of help text - if action.help: - help_text = self._expand_help(action) - help_lines = self._split_lines(help_text, help_width) - parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) - for line in help_lines[1:]: - parts.append('%*s%s\n' % (help_position, '', line)) - - # or add a newline if the description doesn't end with one - elif not action_header.endswith('\n'): - parts.append('\n') - - # if there are any sub-actions, add their help as well - for subaction in self._iter_indented_subactions(action): - parts.append(self._format_action(subaction)) - - # return a single string - return self._join_parts(parts) - - def _format_action_invocation(self, action): - if not action.option_strings: - metavar, = self._metavar_formatter(action, action.dest)(1) - return metavar - - else: - parts = [] - - # if the Optional doesn't take a value, format is: - # -s, --long - if action.nargs == 0: - parts.extend(action.option_strings) - - # if the Optional takes a value, format is: - # -s ARGS, --long ARGS - else: - default = action.dest.upper() - args_string = self._format_args(action, default) - for option_string in action.option_strings: - parts.append('%s %s' % (option_string, args_string)) - - return ', '.join(parts) - - def _metavar_formatter(self, action, default_metavar): - if action.metavar is not None: - result = action.metavar - elif action.choices is not None: - choice_strs = [str(choice) for choice in action.choices] - result = '{%s}' % ','.join(choice_strs) - else: - result = default_metavar - - def format(tuple_size): - if isinstance(result, tuple): - return result - else: - return (result, ) * tuple_size - return format - - def _format_args(self, action, default_metavar): - get_metavar = self._metavar_formatter(action, default_metavar) - if action.nargs is None: - result = '%s' % get_metavar(1) - elif action.nargs == OPTIONAL: - result = '[%s]' % get_metavar(1) - elif action.nargs == ZERO_OR_MORE: - result = '[%s [%s ...]]' % get_metavar(2) - elif action.nargs == ONE_OR_MORE: - result = '%s [%s ...]' % get_metavar(2) - elif action.nargs == REMAINDER: - result = '...' - elif action.nargs == PARSER: - result = '%s ...' % get_metavar(1) - else: - formats = ['%s' for _ in range(action.nargs)] - result = ' '.join(formats) % get_metavar(action.nargs) - return result - - def _expand_help(self, action): - params = dict(vars(action), prog=self._prog) - for name in list(params): - if params[name] is SUPPRESS: - del params[name] - for name in list(params): - if hasattr(params[name], '__name__'): - params[name] = params[name].__name__ - if params.get('choices') is not None: - choices_str = ', '.join([str(c) for c in params['choices']]) - params['choices'] = choices_str - return self._get_help_string(action) % params - - def _iter_indented_subactions(self, action): - try: - get_subactions = action._get_subactions - except AttributeError: - pass - else: - self._indent() - for subaction in get_subactions(): - yield subaction - self._dedent() - - def _split_lines(self, text, width): - text = self._whitespace_matcher.sub(' ', text).strip() - return _textwrap.wrap(text, width) - - def _fill_text(self, text, width, indent): - text = self._whitespace_matcher.sub(' ', text).strip() - return _textwrap.fill(text, width, initial_indent=indent, - subsequent_indent=indent) - - def _get_help_string(self, action): - return action.help - - -class RawDescriptionHelpFormatter(HelpFormatter): - """Help message formatter which retains any formatting in descriptions. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _fill_text(self, text, width, indent): - return ''.join([indent + line for line in text.splitlines(True)]) - - -class RawTextHelpFormatter(RawDescriptionHelpFormatter): - """Help message formatter which retains formatting of all help text. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _split_lines(self, text, width): - return text.splitlines() - - -class ArgumentDefaultsHelpFormatter(HelpFormatter): - """Help message formatter which adds default values to argument help. - - Only the name of this class is considered a public API. All the methods - provided by the class are considered an implementation detail. - """ - - def _get_help_string(self, action): - help = action.help - if '%(default)' not in action.help: - if action.default is not SUPPRESS: - defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] - if action.option_strings or action.nargs in defaulting_nargs: - help += ' (default: %(default)s)' - return help - - -# ===================== -# Options and Arguments -# ===================== - -def _get_action_name(argument): - if argument is None: - return None - elif argument.option_strings: - return '/'.join(argument.option_strings) - elif argument.metavar not in (None, SUPPRESS): - return argument.metavar - elif argument.dest not in (None, SUPPRESS): - return argument.dest - else: - return None - - -class ArgumentError(Exception): - """An error from creating or using an argument (optional or positional). - - The string value of this exception is the message, augmented with - information about the argument that caused it. - """ - - def __init__(self, argument, message): - self.argument_name = _get_action_name(argument) - self.message = message - - def __str__(self): - if self.argument_name is None: - format = '%(message)s' - else: - format = 'argument %(argument_name)s: %(message)s' - return format % dict(message=self.message, - argument_name=self.argument_name) - - -class ArgumentTypeError(Exception): - """An error from trying to convert a command line string to a type.""" - pass - - -# ============== -# Action classes -# ============== - -class Action(_AttributeHolder): - """Information about how to convert command line strings to Python objects. - - Action objects are used by an ArgumentParser to represent the information - needed to parse a single argument from one or more strings from the - command line. The keyword arguments to the Action constructor are also - all attributes of Action instances. - - Keyword Arguments: - - - option_strings -- A list of command-line option strings which - should be associated with this action. - - - dest -- The name of the attribute to hold the created object(s) - - - nargs -- The number of command-line arguments that should be - consumed. By default, one argument will be consumed and a single - value will be produced. Other values include: - - N (an integer) consumes N arguments (and produces a list) - - '?' consumes zero or one arguments - - '*' consumes zero or more arguments (and produces a list) - - '+' consumes one or more arguments (and produces a list) - Note that the difference between the default and nargs=1 is that - with the default, a single value will be produced, while with - nargs=1, a list containing a single value will be produced. - - - const -- The value to be produced if the option is specified and the - option uses an action that takes no values. - - - default -- The value to be produced if the option is not specified. - - - type -- The type which the command-line arguments should be converted - to, should be one of 'string', 'int', 'float', 'complex' or a - callable object that accepts a single string argument. If None, - 'string' is assumed. - - - choices -- A container of values that should be allowed. If not None, - after a command-line argument has been converted to the appropriate - type, an exception will be raised if it is not a member of this - collection. - - - required -- True if the action must always be specified at the - command line. This is only meaningful for optional command-line - arguments. - - - help -- The help string describing the argument. - - - metavar -- The name to be used for the option's argument with the - help string. If None, the 'dest' value will be used as the name. - """ - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - self.option_strings = option_strings - self.dest = dest - self.nargs = nargs - self.const = const - self.default = default - self.type = type - self.choices = choices - self.required = required - self.help = help - self.metavar = metavar - - def _get_kwargs(self): - names = [ - 'option_strings', - 'dest', - 'nargs', - 'const', - 'default', - 'type', - 'choices', - 'help', - 'metavar', - ] - return [(name, getattr(self, name)) for name in names] - - def __call__(self, parser, namespace, values, option_string=None): - raise NotImplementedError(_('.__call__() not defined')) - - -class _StoreAction(Action): - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - if nargs == 0: - raise ValueError('nargs for store actions must be > 0; if you ' - 'have nothing to store, actions such as store ' - 'true or store const may be more appropriate') - if const is not None and nargs != OPTIONAL: - raise ValueError('nargs must be %r to supply const' % OPTIONAL) - super(_StoreAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=nargs, - const=const, - default=default, - type=type, - choices=choices, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, values) - - -class _StoreConstAction(Action): - - def __init__(self, - option_strings, - dest, - const, - default=None, - required=False, - help=None, - metavar=None): - super(_StoreConstAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - const=const, - default=default, - required=required, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - setattr(namespace, self.dest, self.const) - - -class _StoreTrueAction(_StoreConstAction): - - def __init__(self, - option_strings, - dest, - default=False, - required=False, - help=None): - super(_StoreTrueAction, self).__init__( - option_strings=option_strings, - dest=dest, - const=True, - default=default, - required=required, - help=help) - - -class _StoreFalseAction(_StoreConstAction): - - def __init__(self, - option_strings, - dest, - default=True, - required=False, - help=None): - super(_StoreFalseAction, self).__init__( - option_strings=option_strings, - dest=dest, - const=False, - default=default, - required=required, - help=help) - - -class _AppendAction(Action): - - def __init__(self, - option_strings, - dest, - nargs=None, - const=None, - default=None, - type=None, - choices=None, - required=False, - help=None, - metavar=None): - if nargs == 0: - raise ValueError('nargs for append actions must be > 0; if arg ' - 'strings are not supplying the value to append, ' - 'the append const action may be more appropriate') - if const is not None and nargs != OPTIONAL: - raise ValueError('nargs must be %r to supply const' % OPTIONAL) - super(_AppendAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=nargs, - const=const, - default=default, - type=type, - choices=choices, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - items = _copy.copy(_ensure_value(namespace, self.dest, [])) - items.append(values) - setattr(namespace, self.dest, items) - - -class _AppendConstAction(Action): - - def __init__(self, - option_strings, - dest, - const, - default=None, - required=False, - help=None, - metavar=None): - super(_AppendConstAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - const=const, - default=default, - required=required, - help=help, - metavar=metavar) - - def __call__(self, parser, namespace, values, option_string=None): - items = _copy.copy(_ensure_value(namespace, self.dest, [])) - items.append(self.const) - setattr(namespace, self.dest, items) - - -class _CountAction(Action): - - def __init__(self, - option_strings, - dest, - default=None, - required=False, - help=None): - super(_CountAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=0, - default=default, - required=required, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - new_count = _ensure_value(namespace, self.dest, 0) + 1 - setattr(namespace, self.dest, new_count) - - -class _HelpAction(Action): - - def __init__(self, - option_strings, - dest=SUPPRESS, - default=SUPPRESS, - help=None): - super(_HelpAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - nargs=0, - help=help) - - def __call__(self, parser, namespace, values, option_string=None): - parser.print_help() - parser.exit() - - -class _VersionAction(Action): - - def __init__(self, - option_strings, - version=None, - dest=SUPPRESS, - default=SUPPRESS, - help="show program's version number and exit"): - super(_VersionAction, self).__init__( - option_strings=option_strings, - dest=dest, - default=default, - nargs=0, - help=help) - self.version = version - - def __call__(self, parser, namespace, values, option_string=None): - version = self.version - if version is None: - version = parser.version - formatter = parser._get_formatter() - formatter.add_text(version) - parser.exit(message=formatter.format_help()) - - -class _SubParsersAction(Action): - - class _ChoicesPseudoAction(Action): - - def __init__(self, name, aliases, help): - metavar = dest = name - if aliases: - metavar += ' (%s)' % ', '.join(aliases) - sup = super(_SubParsersAction._ChoicesPseudoAction, self) - sup.__init__(option_strings=[], dest=dest, help=help, - metavar=metavar) - - def __init__(self, - option_strings, - prog, - parser_class, - dest=SUPPRESS, - help=None, - metavar=None): - - self._prog_prefix = prog - self._parser_class = parser_class - self._name_parser_map = {} - self._choices_actions = [] - - super(_SubParsersAction, self).__init__( - option_strings=option_strings, - dest=dest, - nargs=PARSER, - choices=self._name_parser_map, - help=help, - metavar=metavar) - - def add_parser(self, name, **kwargs): - # set prog from the existing prefix - if kwargs.get('prog') is None: - kwargs['prog'] = '%s %s' % (self._prog_prefix, name) - - aliases = kwargs.pop('aliases', ()) - - # create a pseudo-action to hold the choice help - if 'help' in kwargs: - help = kwargs.pop('help') - choice_action = self._ChoicesPseudoAction(name, aliases, help) - self._choices_actions.append(choice_action) - - # create the parser and add it to the map - parser = self._parser_class(**kwargs) - self._name_parser_map[name] = parser - - # make parser available under aliases also - for alias in aliases: - self._name_parser_map[alias] = parser - - return parser - - def _get_subactions(self): - return self._choices_actions - - def __call__(self, parser, namespace, values, option_string=None): - parser_name = values[0] - arg_strings = values[1:] - - # set the parser name if requested - if self.dest is not SUPPRESS: - setattr(namespace, self.dest, parser_name) - - # select the parser - try: - parser = self._name_parser_map[parser_name] - except KeyError: - tup = parser_name, ', '.join(self._name_parser_map) - msg = _('unknown parser %r (choices: %s)' % tup) - raise ArgumentError(self, msg) - - # parse all the remaining options into the namespace - # store any unrecognized options on the object, so that the top - # level parser can decide what to do with them - namespace, arg_strings = parser.parse_known_args(arg_strings, namespace) - if arg_strings: - vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, []) - getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings) - - -# ============== -# Type classes -# ============== - -class FileType(object): - """Factory for creating file object types - - Instances of FileType are typically passed as type= arguments to the - ArgumentParser add_argument() method. - - Keyword Arguments: - - mode -- A string indicating how the file is to be opened. Accepts the - same values as the builtin open() function. - - bufsize -- The file's desired buffer size. Accepts the same values as - the builtin open() function. - """ - - def __init__(self, mode='r', bufsize=None): - self._mode = mode - self._bufsize = bufsize - - def __call__(self, string): - # the special argument "-" means sys.std{in,out} - if string == '-': - if 'r' in self._mode: - return _sys.stdin - elif 'w' in self._mode: - return _sys.stdout - else: - msg = _('argument "-" with mode %r' % self._mode) - raise ValueError(msg) - - try: - # all other arguments are used as file names - if self._bufsize: - return open(string, self._mode, self._bufsize) - else: - return open(string, self._mode) - except IOError: - err = _sys.exc_info()[1] - message = _("can't open '%s': %s") - raise ArgumentTypeError(message % (string, err)) - - def __repr__(self): - args = [self._mode, self._bufsize] - args_str = ', '.join([repr(arg) for arg in args if arg is not None]) - return '%s(%s)' % (type(self).__name__, args_str) - -# =========================== -# Optional and Positional Parsing -# =========================== - -class Namespace(_AttributeHolder): - """Simple object for storing attributes. - - Implements equality by attribute names and values, and provides a simple - string representation. - """ - - def __init__(self, **kwargs): - for name in kwargs: - setattr(self, name, kwargs[name]) - - __hash__ = None - - def __eq__(self, other): - return vars(self) == vars(other) - - def __ne__(self, other): - return not (self == other) - - def __contains__(self, key): - return key in self.__dict__ - - -class _ActionsContainer(object): - - def __init__(self, - description, - prefix_chars, - argument_default, - conflict_handler): - super(_ActionsContainer, self).__init__() - - self.description = description - self.argument_default = argument_default - self.prefix_chars = prefix_chars - self.conflict_handler = conflict_handler - - # set up registries - self._registries = {} - - # register actions - self.register('action', None, _StoreAction) - self.register('action', 'store', _StoreAction) - self.register('action', 'store_const', _StoreConstAction) - self.register('action', 'store_true', _StoreTrueAction) - self.register('action', 'store_false', _StoreFalseAction) - self.register('action', 'append', _AppendAction) - self.register('action', 'append_const', _AppendConstAction) - self.register('action', 'count', _CountAction) - self.register('action', 'help', _HelpAction) - self.register('action', 'version', _VersionAction) - self.register('action', 'parsers', _SubParsersAction) - - # raise an exception if the conflict handler is invalid - self._get_handler() - - # action storage - self._actions = [] - self._option_string_actions = {} - - # groups - self._action_groups = [] - self._mutually_exclusive_groups = [] - - # defaults storage - self._defaults = {} - - # determines whether an "option" looks like a negative number - self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$') - - # whether or not there are any optionals that look like negative - # numbers -- uses a list so it can be shared and edited - self._has_negative_number_optionals = [] - - # ==================== - # Registration methods - # ==================== - def register(self, registry_name, value, object): - registry = self._registries.setdefault(registry_name, {}) - registry[value] = object - - def _registry_get(self, registry_name, value, default=None): - return self._registries[registry_name].get(value, default) - - # ================================== - # Namespace default accessor methods - # ================================== - def set_defaults(self, **kwargs): - self._defaults.update(kwargs) - - # if these defaults match any existing arguments, replace - # the previous default on the object with the new one - for action in self._actions: - if action.dest in kwargs: - action.default = kwargs[action.dest] - - def get_default(self, dest): - for action in self._actions: - if action.dest == dest and action.default is not None: - return action.default - return self._defaults.get(dest, None) - - - # ======================= - # Adding argument actions - # ======================= - def add_argument(self, *args, **kwargs): - """ - add_argument(dest, ..., name=value, ...) - add_argument(option_string, option_string, ..., name=value, ...) - """ - - # if no positional args are supplied or only one is supplied and - # it doesn't look like an option string, parse a positional - # argument - chars = self.prefix_chars - if not args or len(args) == 1 and args[0][0] not in chars: - if args and 'dest' in kwargs: - raise ValueError('dest supplied twice for positional argument') - kwargs = self._get_positional_kwargs(*args, **kwargs) - - # otherwise, we're adding an optional argument - else: - kwargs = self._get_optional_kwargs(*args, **kwargs) - - # if no default was supplied, use the parser-level default - if 'default' not in kwargs: - dest = kwargs['dest'] - if dest in self._defaults: - kwargs['default'] = self._defaults[dest] - elif self.argument_default is not None: - kwargs['default'] = self.argument_default - - # create the action object, and add it to the parser - action_class = self._pop_action_class(kwargs) - if not _callable(action_class): - raise ValueError('unknown action "%s"' % action_class) - action = action_class(**kwargs) - - # raise an error if the action type is not callable - type_func = self._registry_get('type', action.type, action.type) - if not _callable(type_func): - raise ValueError('%r is not callable' % type_func) - - return self._add_action(action) - - def add_argument_group(self, *args, **kwargs): - group = _ArgumentGroup(self, *args, **kwargs) - self._action_groups.append(group) - return group - - def add_mutually_exclusive_group(self, **kwargs): - group = _MutuallyExclusiveGroup(self, **kwargs) - self._mutually_exclusive_groups.append(group) - return group - - def _add_action(self, action): - # resolve any conflicts - self._check_conflict(action) - - # add to actions list - self._actions.append(action) - action.container = self - - # index the action by any option strings it has - for option_string in action.option_strings: - self._option_string_actions[option_string] = action - - # set the flag if any option strings look like negative numbers - for option_string in action.option_strings: - if self._negative_number_matcher.match(option_string): - if not self._has_negative_number_optionals: - self._has_negative_number_optionals.append(True) - - # return the created action - return action - - def _remove_action(self, action): - self._actions.remove(action) - - def _add_container_actions(self, container): - # collect groups by titles - title_group_map = {} - for group in self._action_groups: - if group.title in title_group_map: - msg = _('cannot merge actions - two groups are named %r') - raise ValueError(msg % (group.title)) - title_group_map[group.title] = group - - # map each action to its group - group_map = {} - for group in container._action_groups: - - # if a group with the title exists, use that, otherwise - # create a new group matching the container's group - if group.title not in title_group_map: - title_group_map[group.title] = self.add_argument_group( - title=group.title, - description=group.description, - conflict_handler=group.conflict_handler) - - # map the actions to their new group - for action in group._group_actions: - group_map[action] = title_group_map[group.title] - - # add container's mutually exclusive groups - # NOTE: if add_mutually_exclusive_group ever gains title= and - # description= then this code will need to be expanded as above - for group in container._mutually_exclusive_groups: - mutex_group = self.add_mutually_exclusive_group( - required=group.required) - - # map the actions to their new mutex group - for action in group._group_actions: - group_map[action] = mutex_group - - # add all actions to this container or their group - for action in container._actions: - group_map.get(action, self)._add_action(action) - - def _get_positional_kwargs(self, dest, **kwargs): - # make sure required is not specified - if 'required' in kwargs: - msg = _("'required' is an invalid argument for positionals") - raise TypeError(msg) - - # mark positional arguments as required if at least one is - # always required - if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: - kwargs['required'] = True - if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: - kwargs['required'] = True - - # return the keyword arguments with no option strings - return dict(kwargs, dest=dest, option_strings=[]) - - def _get_optional_kwargs(self, *args, **kwargs): - # determine short and long option strings - option_strings = [] - long_option_strings = [] - for option_string in args: - # error on strings that don't start with an appropriate prefix - if not option_string[0] in self.prefix_chars: - msg = _('invalid option string %r: ' - 'must start with a character %r') - tup = option_string, self.prefix_chars - raise ValueError(msg % tup) - - # strings starting with two prefix characters are long options - option_strings.append(option_string) - if option_string[0] in self.prefix_chars: - if len(option_string) > 1: - if option_string[1] in self.prefix_chars: - long_option_strings.append(option_string) - - # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' - dest = kwargs.pop('dest', None) - if dest is None: - if long_option_strings: - dest_option_string = long_option_strings[0] - else: - dest_option_string = option_strings[0] - dest = dest_option_string.lstrip(self.prefix_chars) - if not dest: - msg = _('dest= is required for options like %r') - raise ValueError(msg % option_string) - dest = dest.replace('-', '_') - - # return the updated keyword arguments - return dict(kwargs, dest=dest, option_strings=option_strings) - - def _pop_action_class(self, kwargs, default=None): - action = kwargs.pop('action', default) - return self._registry_get('action', action, action) - - def _get_handler(self): - # determine function from conflict handler string - handler_func_name = '_handle_conflict_%s' % self.conflict_handler - try: - return getattr(self, handler_func_name) - except AttributeError: - msg = _('invalid conflict_resolution value: %r') - raise ValueError(msg % self.conflict_handler) - - def _check_conflict(self, action): - - # find all options that conflict with this option - confl_optionals = [] - for option_string in action.option_strings: - if option_string in self._option_string_actions: - confl_optional = self._option_string_actions[option_string] - confl_optionals.append((option_string, confl_optional)) - - # resolve any conflicts - if confl_optionals: - conflict_handler = self._get_handler() - conflict_handler(action, confl_optionals) - - def _handle_conflict_error(self, action, conflicting_actions): - message = _('conflicting option string(s): %s') - conflict_string = ', '.join([option_string - for option_string, action - in conflicting_actions]) - raise ArgumentError(action, message % conflict_string) - - def _handle_conflict_resolve(self, action, conflicting_actions): - - # remove all conflicting options - for option_string, action in conflicting_actions: - - # remove the conflicting option - action.option_strings.remove(option_string) - self._option_string_actions.pop(option_string, None) - - # if the option now has no option string, remove it from the - # container holding it - if not action.option_strings: - action.container._remove_action(action) - - -class _ArgumentGroup(_ActionsContainer): - - def __init__(self, container, title=None, description=None, **kwargs): - # add any missing keyword arguments by checking the container - update = kwargs.setdefault - update('conflict_handler', container.conflict_handler) - update('prefix_chars', container.prefix_chars) - update('argument_default', container.argument_default) - super_init = super(_ArgumentGroup, self).__init__ - super_init(description=description, **kwargs) - - # group attributes - self.title = title - self._group_actions = [] - - # share most attributes with the container - self._registries = container._registries - self._actions = container._actions - self._option_string_actions = container._option_string_actions - self._defaults = container._defaults - self._has_negative_number_optionals = \ - container._has_negative_number_optionals - - def _add_action(self, action): - action = super(_ArgumentGroup, self)._add_action(action) - self._group_actions.append(action) - return action - - def _remove_action(self, action): - super(_ArgumentGroup, self)._remove_action(action) - self._group_actions.remove(action) - - -class _MutuallyExclusiveGroup(_ArgumentGroup): - - def __init__(self, container, required=False): - super(_MutuallyExclusiveGroup, self).__init__(container) - self.required = required - self._container = container - - def _add_action(self, action): - if action.required: - msg = _('mutually exclusive arguments must be optional') - raise ValueError(msg) - action = self._container._add_action(action) - self._group_actions.append(action) - return action - - def _remove_action(self, action): - self._container._remove_action(action) - self._group_actions.remove(action) - - -class ArgumentParser(_AttributeHolder, _ActionsContainer): - """Object for parsing command line strings into Python objects. - - Keyword Arguments: - - prog -- The name of the program (default: sys.argv[0]) - - usage -- A usage message (default: auto-generated from arguments) - - description -- A description of what the program does - - epilog -- Text following the argument descriptions - - parents -- Parsers whose arguments should be copied into this one - - formatter_class -- HelpFormatter class for printing help messages - - prefix_chars -- Characters that prefix optional arguments - - fromfile_prefix_chars -- Characters that prefix files containing - additional arguments - - argument_default -- The default value for all arguments - - conflict_handler -- String indicating how to handle conflicts - - add_help -- Add a -h/-help option - """ - - def __init__(self, - prog=None, - usage=None, - description=None, - epilog=None, - version=None, - parents=[], - formatter_class=HelpFormatter, - prefix_chars='-', - fromfile_prefix_chars=None, - argument_default=None, - conflict_handler='error', - add_help=True): - - if version is not None: - import warnings - warnings.warn( - """The "version" argument to ArgumentParser is deprecated. """ - """Please use """ - """"add_argument(..., action='version', version="N", ...)" """ - """instead""", DeprecationWarning) - - superinit = super(ArgumentParser, self).__init__ - superinit(description=description, - prefix_chars=prefix_chars, - argument_default=argument_default, - conflict_handler=conflict_handler) - - # default setting for prog - if prog is None: - prog = _os.path.basename(_sys.argv[0]) - - self.prog = prog - self.usage = usage - self.epilog = epilog - self.version = version - self.formatter_class = formatter_class - self.fromfile_prefix_chars = fromfile_prefix_chars - self.add_help = add_help - - add_group = self.add_argument_group - self._positionals = add_group(_('positional arguments')) - self._optionals = add_group(_('optional arguments')) - self._subparsers = None - - # register types - def identity(string): - return string - self.register('type', None, identity) - - # add help and version arguments if necessary - # (using explicit default to override global argument_default) - if '-' in prefix_chars: - default_prefix = '-' - else: - default_prefix = prefix_chars[0] - if self.add_help: - self.add_argument( - default_prefix+'h', default_prefix*2+'help', - action='help', default=SUPPRESS, - help=_('show this help message and exit')) - if self.version: - self.add_argument( - default_prefix+'v', default_prefix*2+'version', - action='version', default=SUPPRESS, - version=self.version, - help=_("show program's version number and exit")) - - # add parent arguments and defaults - for parent in parents: - self._add_container_actions(parent) - try: - defaults = parent._defaults - except AttributeError: - pass - else: - self._defaults.update(defaults) - - # ======================= - # Pretty __repr__ methods - # ======================= - def _get_kwargs(self): - names = [ - 'prog', - 'usage', - 'description', - 'version', - 'formatter_class', - 'conflict_handler', - 'add_help', - ] - return [(name, getattr(self, name)) for name in names] - - # ================================== - # Optional/Positional adding methods - # ================================== - def add_subparsers(self, **kwargs): - if self._subparsers is not None: - self.error(_('cannot have multiple subparser arguments')) - - # add the parser class to the arguments if it's not present - kwargs.setdefault('parser_class', type(self)) - - if 'title' in kwargs or 'description' in kwargs: - title = _(kwargs.pop('title', 'subcommands')) - description = _(kwargs.pop('description', None)) - self._subparsers = self.add_argument_group(title, description) - else: - self._subparsers = self._positionals - - # prog defaults to the usage message of this parser, skipping - # optional arguments and with no "usage:" prefix - if kwargs.get('prog') is None: - formatter = self._get_formatter() - positionals = self._get_positional_actions() - groups = self._mutually_exclusive_groups - formatter.add_usage(self.usage, positionals, groups, '') - kwargs['prog'] = formatter.format_help().strip() - - # create the parsers action and add it to the positionals list - parsers_class = self._pop_action_class(kwargs, 'parsers') - action = parsers_class(option_strings=[], **kwargs) - self._subparsers._add_action(action) - - # return the created parsers action - return action - - def _add_action(self, action): - if action.option_strings: - self._optionals._add_action(action) - else: - self._positionals._add_action(action) - return action - - def _get_optional_actions(self): - return [action - for action in self._actions - if action.option_strings] - - def _get_positional_actions(self): - return [action - for action in self._actions - if not action.option_strings] - - # ===================================== - # Command line argument parsing methods - # ===================================== - def parse_args(self, args=None, namespace=None): - args, argv = self.parse_known_args(args, namespace) - if argv: - msg = _('unrecognized arguments: %s') - self.error(msg % ' '.join(argv)) - return args - - def parse_known_args(self, args=None, namespace=None): - # args default to the system args - if args is None: - args = _sys.argv[1:] - - # default Namespace built from parser defaults - if namespace is None: - namespace = Namespace() - - # add any action defaults that aren't present - for action in self._actions: - if action.dest is not SUPPRESS: - if not hasattr(namespace, action.dest): - if action.default is not SUPPRESS: - setattr(namespace, action.dest, action.default) - - # add any parser defaults that aren't present - for dest in self._defaults: - if not hasattr(namespace, dest): - setattr(namespace, dest, self._defaults[dest]) - - # parse the arguments and exit if there are any errors - try: - namespace, args = self._parse_known_args(args, namespace) - if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): - args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) - delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) - return namespace, args - except ArgumentError: - err = _sys.exc_info()[1] - self.error(str(err)) - - def _parse_known_args(self, arg_strings, namespace): - # replace arg strings that are file references - if self.fromfile_prefix_chars is not None: - arg_strings = self._read_args_from_files(arg_strings) - - # map all mutually exclusive arguments to the other arguments - # they can't occur with - action_conflicts = {} - for mutex_group in self._mutually_exclusive_groups: - group_actions = mutex_group._group_actions - for i, mutex_action in enumerate(mutex_group._group_actions): - conflicts = action_conflicts.setdefault(mutex_action, []) - conflicts.extend(group_actions[:i]) - conflicts.extend(group_actions[i + 1:]) - - # find all option indices, and determine the arg_string_pattern - # which has an 'O' if there is an option at an index, - # an 'A' if there is an argument, or a '-' if there is a '--' - option_string_indices = {} - arg_string_pattern_parts = [] - arg_strings_iter = iter(arg_strings) - for i, arg_string in enumerate(arg_strings_iter): - - # all args after -- are non-options - if arg_string == '--': - arg_string_pattern_parts.append('-') - for arg_string in arg_strings_iter: - arg_string_pattern_parts.append('A') - - # otherwise, add the arg to the arg strings - # and note the index if it was an option - else: - option_tuple = self._parse_optional(arg_string) - if option_tuple is None: - pattern = 'A' - else: - option_string_indices[i] = option_tuple - pattern = 'O' - arg_string_pattern_parts.append(pattern) - - # join the pieces together to form the pattern - arg_strings_pattern = ''.join(arg_string_pattern_parts) - - # converts arg strings to the appropriate and then takes the action - seen_actions = set() - seen_non_default_actions = set() - - def take_action(action, argument_strings, option_string=None): - seen_actions.add(action) - argument_values = self._get_values(action, argument_strings) - - # error if this argument is not allowed with other previously - # seen arguments, assuming that actions that use the default - # value don't really count as "present" - if argument_values is not action.default: - seen_non_default_actions.add(action) - for conflict_action in action_conflicts.get(action, []): - if conflict_action in seen_non_default_actions: - msg = _('not allowed with argument %s') - action_name = _get_action_name(conflict_action) - raise ArgumentError(action, msg % action_name) - - # take the action if we didn't receive a SUPPRESS value - # (e.g. from a default) - if argument_values is not SUPPRESS: - action(self, namespace, argument_values, option_string) - - # function to convert arg_strings into an optional action - def consume_optional(start_index): - - # get the optional identified at this index - option_tuple = option_string_indices[start_index] - action, option_string, explicit_arg = option_tuple - - # identify additional optionals in the same arg string - # (e.g. -xyz is the same as -x -y -z if no args are required) - match_argument = self._match_argument - action_tuples = [] - while True: - - # if we found no optional action, skip it - if action is None: - extras.append(arg_strings[start_index]) - return start_index + 1 - - # if there is an explicit argument, try to match the - # optional's string arguments to only this - if explicit_arg is not None: - arg_count = match_argument(action, 'A') - - # if the action is a single-dash option and takes no - # arguments, try to parse more single-dash options out - # of the tail of the option string - chars = self.prefix_chars - if arg_count == 0 and option_string[1] not in chars: - action_tuples.append((action, [], option_string)) - char = option_string[0] - option_string = char + explicit_arg[0] - new_explicit_arg = explicit_arg[1:] or None - optionals_map = self._option_string_actions - if option_string in optionals_map: - action = optionals_map[option_string] - explicit_arg = new_explicit_arg - else: - msg = _('ignored explicit argument %r') - raise ArgumentError(action, msg % explicit_arg) - - # if the action expect exactly one argument, we've - # successfully matched the option; exit the loop - elif arg_count == 1: - stop = start_index + 1 - args = [explicit_arg] - action_tuples.append((action, args, option_string)) - break - - # error if a double-dash option did not use the - # explicit argument - else: - msg = _('ignored explicit argument %r') - raise ArgumentError(action, msg % explicit_arg) - - # if there is no explicit argument, try to match the - # optional's string arguments with the following strings - # if successful, exit the loop - else: - start = start_index + 1 - selected_patterns = arg_strings_pattern[start:] - arg_count = match_argument(action, selected_patterns) - stop = start + arg_count - args = arg_strings[start:stop] - action_tuples.append((action, args, option_string)) - break - - # add the Optional to the list and return the index at which - # the Optional's string args stopped - assert action_tuples - for action, args, option_string in action_tuples: - take_action(action, args, option_string) - return stop - - # the list of Positionals left to be parsed; this is modified - # by consume_positionals() - positionals = self._get_positional_actions() - - # function to convert arg_strings into positional actions - def consume_positionals(start_index): - # match as many Positionals as possible - match_partial = self._match_arguments_partial - selected_pattern = arg_strings_pattern[start_index:] - arg_counts = match_partial(positionals, selected_pattern) - - # slice off the appropriate arg strings for each Positional - # and add the Positional and its args to the list - for action, arg_count in zip(positionals, arg_counts): - args = arg_strings[start_index: start_index + arg_count] - start_index += arg_count - take_action(action, args) - - # slice off the Positionals that we just parsed and return the - # index at which the Positionals' string args stopped - positionals[:] = positionals[len(arg_counts):] - return start_index - - # consume Positionals and Optionals alternately, until we have - # passed the last option string - extras = [] - start_index = 0 - if option_string_indices: - max_option_string_index = max(option_string_indices) - else: - max_option_string_index = -1 - while start_index <= max_option_string_index: - - # consume any Positionals preceding the next option - next_option_string_index = min([ - index - for index in option_string_indices - if index >= start_index]) - if start_index != next_option_string_index: - positionals_end_index = consume_positionals(start_index) - - # only try to parse the next optional if we didn't consume - # the option string during the positionals parsing - if positionals_end_index > start_index: - start_index = positionals_end_index - continue - else: - start_index = positionals_end_index - - # if we consumed all the positionals we could and we're not - # at the index of an option string, there were extra arguments - if start_index not in option_string_indices: - strings = arg_strings[start_index:next_option_string_index] - extras.extend(strings) - start_index = next_option_string_index - - # consume the next optional and any arguments for it - start_index = consume_optional(start_index) - - # consume any positionals following the last Optional - stop_index = consume_positionals(start_index) - - # if we didn't consume all the argument strings, there were extras - extras.extend(arg_strings[stop_index:]) - - # if we didn't use all the Positional objects, there were too few - # arg strings supplied. - if positionals: - self.error(_('too few arguments')) - - # make sure all required actions were present, and convert defaults. - for action in self._actions: - if action not in seen_actions: - if action.required: - name = _get_action_name(action) - self.error(_('argument %s is required') % name) - else: - # Convert action default now instead of doing it before - # parsing arguments to avoid calling convert functions - # twice (which may fail) if the argument was given, but - # only if it was defined already in the namespace - if (action.default is not None and - isinstance(action.default, basestring) and - hasattr(namespace, action.dest) and - action.default is getattr(namespace, action.dest)): - setattr(namespace, action.dest, - self._get_value(action, action.default)) - - # make sure all required groups had one option present - for group in self._mutually_exclusive_groups: - if group.required: - for action in group._group_actions: - if action in seen_non_default_actions: - break - - # if no actions were used, report the error - else: - names = [_get_action_name(action) - for action in group._group_actions - if action.help is not SUPPRESS] - msg = _('one of the arguments %s is required') - self.error(msg % ' '.join(names)) - - # return the updated namespace and the extra arguments - return namespace, extras - - def _read_args_from_files(self, arg_strings): - # expand arguments referencing files - new_arg_strings = [] - for arg_string in arg_strings: - - # for regular arguments, just add them back into the list - if arg_string[0] not in self.fromfile_prefix_chars: - new_arg_strings.append(arg_string) - - # replace arguments referencing files with the file content - else: - try: - args_file = open(arg_string[1:]) - try: - arg_strings = [] - for arg_line in args_file.read().splitlines(): - for arg in self.convert_arg_line_to_args(arg_line): - arg_strings.append(arg) - arg_strings = self._read_args_from_files(arg_strings) - new_arg_strings.extend(arg_strings) - finally: - args_file.close() - except IOError: - err = _sys.exc_info()[1] - self.error(str(err)) - - # return the modified argument list - return new_arg_strings - - def convert_arg_line_to_args(self, arg_line): - return [arg_line] - - def _match_argument(self, action, arg_strings_pattern): - # match the pattern for this action to the arg strings - nargs_pattern = self._get_nargs_pattern(action) - match = _re.match(nargs_pattern, arg_strings_pattern) - - # raise an exception if we weren't able to find a match - if match is None: - nargs_errors = { - None: _('expected one argument'), - OPTIONAL: _('expected at most one argument'), - ONE_OR_MORE: _('expected at least one argument'), - } - default = _('expected %s argument(s)') % action.nargs - msg = nargs_errors.get(action.nargs, default) - raise ArgumentError(action, msg) - - # return the number of arguments matched - return len(match.group(1)) - - def _match_arguments_partial(self, actions, arg_strings_pattern): - # progressively shorten the actions list by slicing off the - # final actions until we find a match - result = [] - for i in range(len(actions), 0, -1): - actions_slice = actions[:i] - pattern = ''.join([self._get_nargs_pattern(action) - for action in actions_slice]) - match = _re.match(pattern, arg_strings_pattern) - if match is not None: - result.extend([len(string) for string in match.groups()]) - break - - # return the list of arg string counts - return result - - def _parse_optional(self, arg_string): - # if it's an empty string, it was meant to be a positional - if not arg_string: - return None - - # if it doesn't start with a prefix, it was meant to be positional - if not arg_string[0] in self.prefix_chars: - return None - - # if the option string is present in the parser, return the action - if arg_string in self._option_string_actions: - action = self._option_string_actions[arg_string] - return action, arg_string, None - - # if it's just a single character, it was meant to be positional - if len(arg_string) == 1: - return None - - # if the option string before the "=" is present, return the action - if '=' in arg_string: - option_string, explicit_arg = arg_string.split('=', 1) - if option_string in self._option_string_actions: - action = self._option_string_actions[option_string] - return action, option_string, explicit_arg - - # search through all possible prefixes of the option string - # and all actions in the parser for possible interpretations - option_tuples = self._get_option_tuples(arg_string) - - # if multiple actions match, the option string was ambiguous - if len(option_tuples) > 1: - options = ', '.join([option_string - for action, option_string, explicit_arg in option_tuples]) - tup = arg_string, options - self.error(_('ambiguous option: %s could match %s') % tup) - - # if exactly one action matched, this segmentation is good, - # so return the parsed action - elif len(option_tuples) == 1: - option_tuple, = option_tuples - return option_tuple - - # if it was not found as an option, but it looks like a negative - # number, it was meant to be positional - # unless there are negative-number-like options - if self._negative_number_matcher.match(arg_string): - if not self._has_negative_number_optionals: - return None - - # if it contains a space, it was meant to be a positional - if ' ' in arg_string: - return None - - # it was meant to be an optional but there is no such option - # in this parser (though it might be a valid option in a subparser) - return None, arg_string, None - - def _get_option_tuples(self, option_string): - result = [] - - # option strings starting with two prefix characters are only - # split at the '=' - chars = self.prefix_chars - if option_string[0] in chars and option_string[1] in chars: - if '=' in option_string: - option_prefix, explicit_arg = option_string.split('=', 1) - else: - option_prefix = option_string - explicit_arg = None - for option_string in self._option_string_actions: - if option_string.startswith(option_prefix): - action = self._option_string_actions[option_string] - tup = action, option_string, explicit_arg - result.append(tup) - - # single character options can be concatenated with their arguments - # but multiple character options always have to have their argument - # separate - elif option_string[0] in chars and option_string[1] not in chars: - option_prefix = option_string - explicit_arg = None - short_option_prefix = option_string[:2] - short_explicit_arg = option_string[2:] - - for option_string in self._option_string_actions: - if option_string == short_option_prefix: - action = self._option_string_actions[option_string] - tup = action, option_string, short_explicit_arg - result.append(tup) - elif option_string.startswith(option_prefix): - action = self._option_string_actions[option_string] - tup = action, option_string, explicit_arg - result.append(tup) - - # shouldn't ever get here - else: - self.error(_('unexpected option string: %s') % option_string) - - # return the collected option tuples - return result - - def _get_nargs_pattern(self, action): - # in all examples below, we have to allow for '--' args - # which are represented as '-' in the pattern - nargs = action.nargs - - # the default (None) is assumed to be a single argument - if nargs is None: - nargs_pattern = '(-*A-*)' - - # allow zero or one arguments - elif nargs == OPTIONAL: - nargs_pattern = '(-*A?-*)' - - # allow zero or more arguments - elif nargs == ZERO_OR_MORE: - nargs_pattern = '(-*[A-]*)' - - # allow one or more arguments - elif nargs == ONE_OR_MORE: - nargs_pattern = '(-*A[A-]*)' - - # allow any number of options or arguments - elif nargs == REMAINDER: - nargs_pattern = '([-AO]*)' - - # allow one argument followed by any number of options or arguments - elif nargs == PARSER: - nargs_pattern = '(-*A[-AO]*)' - - # all others should be integers - else: - nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) - - # if this is an optional action, -- is not allowed - if action.option_strings: - nargs_pattern = nargs_pattern.replace('-*', '') - nargs_pattern = nargs_pattern.replace('-', '') - - # return the pattern - return nargs_pattern - - # ======================== - # Value conversion methods - # ======================== - def _get_values(self, action, arg_strings): - # for everything but PARSER args, strip out '--' - if action.nargs not in [PARSER, REMAINDER]: - arg_strings = [s for s in arg_strings if s != '--'] - - # optional argument produces a default when not present - if not arg_strings and action.nargs == OPTIONAL: - if action.option_strings: - value = action.const - else: - value = action.default - if isinstance(value, basestring): - value = self._get_value(action, value) - self._check_value(action, value) - - # when nargs='*' on a positional, if there were no command-line - # args, use the default if it is anything other than None - elif (not arg_strings and action.nargs == ZERO_OR_MORE and - not action.option_strings): - if action.default is not None: - value = action.default - else: - value = arg_strings - self._check_value(action, value) - - # single argument or optional argument produces a single value - elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: - arg_string, = arg_strings - value = self._get_value(action, arg_string) - self._check_value(action, value) - - # REMAINDER arguments convert all values, checking none - elif action.nargs == REMAINDER: - value = [self._get_value(action, v) for v in arg_strings] - - # PARSER arguments convert all values, but check only the first - elif action.nargs == PARSER: - value = [self._get_value(action, v) for v in arg_strings] - self._check_value(action, value[0]) - - # all other types of nargs produce a list - else: - value = [self._get_value(action, v) for v in arg_strings] - for v in value: - self._check_value(action, v) - - # return the converted value - return value - - def _get_value(self, action, arg_string): - type_func = self._registry_get('type', action.type, action.type) - if not _callable(type_func): - msg = _('%r is not callable') - raise ArgumentError(action, msg % type_func) - - # convert the value to the appropriate type - try: - result = type_func(arg_string) - - # ArgumentTypeErrors indicate errors - except ArgumentTypeError: - name = getattr(action.type, '__name__', repr(action.type)) - msg = str(_sys.exc_info()[1]) - raise ArgumentError(action, msg) - - # TypeErrors or ValueErrors also indicate errors - except (TypeError, ValueError): - name = getattr(action.type, '__name__', repr(action.type)) - msg = _('invalid %s value: %r') - raise ArgumentError(action, msg % (name, arg_string)) - - # return the converted value - return result - - def _check_value(self, action, value): - # converted value must be one of the choices (if specified) - if action.choices is not None and value not in action.choices: - tup = value, ', '.join(map(repr, action.choices)) - msg = _('invalid choice: %r (choose from %s)') % tup - raise ArgumentError(action, msg) - - # ======================= - # Help-formatting methods - # ======================= - def format_usage(self): - formatter = self._get_formatter() - formatter.add_usage(self.usage, self._actions, - self._mutually_exclusive_groups) - return formatter.format_help() - - def format_help(self): - formatter = self._get_formatter() - - # usage - formatter.add_usage(self.usage, self._actions, - self._mutually_exclusive_groups) - - # description - formatter.add_text(self.description) - - # positionals, optionals and user-defined groups - for action_group in self._action_groups: - formatter.start_section(action_group.title) - formatter.add_text(action_group.description) - formatter.add_arguments(action_group._group_actions) - formatter.end_section() - - # epilog - formatter.add_text(self.epilog) - - # determine help from format above - return formatter.format_help() - - def format_version(self): - import warnings - warnings.warn( - 'The format_version method is deprecated -- the "version" ' - 'argument to ArgumentParser is no longer supported.', - DeprecationWarning) - formatter = self._get_formatter() - formatter.add_text(self.version) - return formatter.format_help() - - def _get_formatter(self): - return self.formatter_class(prog=self.prog) - - # ===================== - # Help-printing methods - # ===================== - def print_usage(self, file=None): - if file is None: - file = _sys.stdout - self._print_message(self.format_usage(), file) - - def print_help(self, file=None): - if file is None: - file = _sys.stdout - self._print_message(self.format_help(), file) - - def print_version(self, file=None): - import warnings - warnings.warn( - 'The print_version method is deprecated -- the "version" ' - 'argument to ArgumentParser is no longer supported.', - DeprecationWarning) - self._print_message(self.format_version(), file) - - def _print_message(self, message, file=None): - if message: - if file is None: - file = _sys.stderr - file.write(message) - - # =============== - # Exiting methods - # =============== - def exit(self, status=0, message=None): - if message: - self._print_message(message, _sys.stderr) - _sys.exit(status) - - def error(self, message): - """error(message: string) - - Prints a usage message incorporating the message to stderr and - exits. - - If you override this in a subclass, it should not return -- it - should either exit or raise an exception. - """ - self.print_usage(_sys.stderr) - self.exit(2, _('%s: error: %s\n') % (self.prog, message)) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index d52207a3cc..42e1c50dd8 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -224,9 +224,8 @@ our $NonptrType; our $Type; our $Declare; -our $UTF8 = qr { - [\x09\x0A\x0D\x20-\x7E] # ASCII - | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte +our $NON_ASCII_UTF8 = qr{ + [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates @@ -235,9 +234,15 @@ our $UTF8 = qr { | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 }x; +our $UTF8 = qr{ + [\x09\x0A\x0D\x20-\x7E] # ASCII + | $NON_ASCII_UTF8 +}x; + # There are still some false positives, but this catches most # common cases. our $typeTypedefs = qr{(?x: + (?![KMGTPE]iB) # IEC binary prefix (do not match) [A-Z][A-Z\d_]*[a-z][A-Za-z\d_]* # camelcase | [A-Z][A-Z\d_]*AIOCB # all uppercase | [A-Z][A-Z\d_]*CPU # all uppercase @@ -265,7 +270,36 @@ our @typeList = ( qr{${Ident}_handler_fn}, qr{target_(?:u)?long}, qr{hwaddr}, + # external libraries qr{xml${Ident}}, + qr{xen\w+_handle}, + # Glib definitions + qr{gchar}, + qr{gshort}, + qr{glong}, + qr{gint}, + qr{gboolean}, + qr{guchar}, + qr{gushort}, + qr{gulong}, + qr{guint}, + qr{gfloat}, + qr{gdouble}, + qr{gpointer}, + qr{gconstpointer}, + qr{gint8}, + qr{guint8}, + qr{gint16}, + qr{guint16}, + qr{gint32}, + qr{guint32}, + qr{gint64}, + qr{guint64}, + qr{gsize}, + qr{gssize}, + qr{goffset}, + qr{gintptr}, + qr{guintptr}, ); # This can be modified by sub possible. Since it can be empty, be careful @@ -1098,11 +1132,10 @@ sub possible { case| else| asm|__asm__| - do| - \#| - \#\# + do )(?:\s|$)| - ^(?:typedef|struct|enum)\b + ^(?:typedef|struct|enum)\b| + ^\# )}x; warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); if ($possible !~ $notPermitted) { @@ -1112,7 +1145,7 @@ sub possible { if ($possible =~ /^\s*$/) { } elsif ($possible =~ /\s/) { - $possible =~ s/\s*$Type\s*//g; + $possible =~ s/\s*(?:$Type|\#\#)\s*//g; for my $modifier (split(' ', $possible)) { if ($modifier !~ $notPermitted) { warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); @@ -1178,6 +1211,11 @@ sub process { my $signoff = 0; my $is_patch = 0; + my $in_header_lines = $file ? 0 : 1; + my $in_commit_log = 0; #Scanning lines before patch + my $reported_maintainer_file = 0; + my $non_utf8_charset = 0; + our @report = (); our $cnt_lines = 0; our $cnt_error = 0; @@ -1330,7 +1368,6 @@ sub process { if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@; - } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@; @@ -1369,6 +1406,8 @@ sub process { if ($line =~ /^\s*signed-off-by:/i) { # This is a signoff, if ugly, so do not double report. $signoff++; + $in_commit_log = 0; + if (!($line =~ /^\s*Signed-off-by:/)) { ERROR("The correct form is \"Signed-off-by\"\n" . $herecurr); @@ -1379,6 +1418,22 @@ sub process { } } +# Check if MAINTAINERS is being updated. If so, there's probably no need to +# emit the "does MAINTAINERS need updating?" message on file add/move/delete + if ($line =~ /^\s*MAINTAINERS\s*\|/) { + $reported_maintainer_file = 1; + } + +# Check for added, moved or deleted files + if (!$reported_maintainer_file && !$in_commit_log && + ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || + $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || + ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && + (defined($1) || defined($2))))) { + $reported_maintainer_file = 1; + WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); + } + # Check for wrappage within a valid hunk of the file if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { ERROR("patch seems to be corrupt (line wrapped?)\n" . @@ -1397,6 +1452,28 @@ sub process { ERROR("Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); } +# Check if it's the start of a commit log +# (not a header line and we haven't seen the patch filename) + if ($in_header_lines && $realfile =~ /^$/ && + !($rawline =~ /^\s+\S/ || + $rawline =~ /^(commit\b|from\b|[\w-]+:).*$/i)) { + $in_header_lines = 0; + $in_commit_log = 1; + } + +# Check if there is UTF-8 in a commit log when a mail header has explicitly +# declined it, i.e defined some charset where it is missing. + if ($in_header_lines && + $rawline =~ /^Content-Type:.+charset="(.+)".*$/ && + $1 !~ /utf-8/i) { + $non_utf8_charset = 1; + } + + if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ && + $rawline =~ /$NON_ASCII_UTF8/) { + WARN("8-bit UTF-8 used in possible commit log\n" . $herecurr); + } + # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); diff --git a/scripts/coccinelle/qobject.cocci b/scripts/coccinelle/qobject.cocci index 47bcafe9a9..9fee9c0d9a 100644 --- a/scripts/coccinelle/qobject.cocci +++ b/scripts/coccinelle/qobject.cocci @@ -3,11 +3,11 @@ expression Obj, Key, E; @@ ( -- qobject_incref(QOBJECT(E)); -+ QINCREF(E); +- qobject_ref(QOBJECT(E)); ++ qobject_ref(E); | -- qobject_decref(QOBJECT(E)); -+ QDECREF(E); +- qobject_unref(QOBJECT(E)); ++ qobject_unref(E); | - qdict_put_obj(Obj, Key, QOBJECT(E)); + qdict_put(Obj, Key, E); diff --git a/scripts/coverity-model.c b/scripts/coverity-model.c index c702804f41..2c0346ff25 100644 --- a/scripts/coverity-model.c +++ b/scripts/coverity-model.c @@ -103,6 +103,14 @@ static int get_keysym(const name2keysym_t *table, } } +/* Replay data is considered trusted. */ +uint8_t replay_get_byte(void) +{ + uint8_t byte; + return byte; +} + + /* * GLib memory allocation functions. * diff --git a/scripts/device-crash-test b/scripts/device-crash-test index 5d17dc68dd..e6c233e9bf 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -23,6 +23,7 @@ Run QEMU with all combinations of -machine and -device types, check for crashes and unexpected errors. """ +from __future__ import print_function import sys import os @@ -218,9 +219,6 @@ ERROR_WHITELIST = [ {'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR}, {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR}, {'exitcode':-11, 'device':'isa-serial', 'loglevel':logging.ERROR, 'expected':True}, - {'exitcode':-11, 'device':'mioe3680_pci', 'loglevel':logging.ERROR, 'expected':True}, - {'exitcode':-11, 'device':'pcm3680_pci', 'loglevel':logging.ERROR, 'expected':True}, - {'exitcode':-11, 'device':'kvaser_pci', 'loglevel':logging.ERROR, 'expected':True}, # everything else (including SIGABRT and SIGSEGV) will be a fatal error: {'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL}, @@ -557,7 +555,7 @@ def main(): tc[k] = v if len(binariesToTest(args, tc)) == 0: - print >>sys.stderr, "No QEMU binary found" + print("No QEMU binary found", file=sys.stderr) parser.print_usage(sys.stderr) return 1 diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index 276eebf0c2..5a857cebcf 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -12,6 +12,7 @@ Authors: 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 __future__ import print_function import ctypes import struct diff --git a/scripts/kvm/kvm_flightrecorder b/scripts/kvm/kvm_flightrecorder index 7fb1c2d1a7..54a56745e4 100755 --- a/scripts/kvm/kvm_flightrecorder +++ b/scripts/kvm/kvm_flightrecorder @@ -32,6 +32,7 @@ # consuming CPU cycles. No disk I/O is performed since the ring buffer holds a # fixed-size in-memory trace. +from __future__ import print_function import sys import os @@ -77,8 +78,8 @@ def tail_trace(): pass def usage(): - print 'Usage: %s start [buffer_size_kb] | stop | dump | tail' % sys.argv[0] - print 'Control the KVM flight recorder tracing.' + print('Usage: %s start [buffer_size_kb] | stop | dump | tail' % sys.argv[0]) + print('Control the KVM flight recorder tracing.') sys.exit(0) def main(): @@ -87,15 +88,15 @@ def main(): cmd = sys.argv[1] if cmd == '--version': - print 'kvm_flightrecorder version 1.0' + print('kvm_flightrecorder version 1.0') sys.exit(0) if not os.path.isdir(tracing_dir): - print 'Unable to tracing debugfs directory, try:' - print 'mount -t debugfs none /sys/kernel/debug' + print('Unable to tracing debugfs directory, try:') + print('mount -t debugfs none /sys/kernel/debug') sys.exit(1) if not os.access(tracing_dir, os.W_OK): - print 'Unable to write to tracing debugfs directory, please run as root' + print('Unable to write to tracing debugfs directory, please run as root') sys.exit(1) if cmd == 'start': @@ -105,16 +106,16 @@ def main(): try: buffer_size_kb = int(sys.argv[2]) except ValueError: - print 'Invalid per-cpu trace buffer size in KB' + print('Invalid per-cpu trace buffer size in KB') sys.exit(1) write_file(trace_path('buffer_size_kb'), str(buffer_size_kb)) - print 'Per-CPU ring buffer size set to %d KB' % buffer_size_kb + print('Per-CPU ring buffer size set to %d KB' % buffer_size_kb) start_tracing() - print 'KVM flight recorder enabled' + print('KVM flight recorder enabled') elif cmd == 'stop': stop_tracing() - print 'KVM flight recorder disabled' + print('KVM flight recorder disabled') elif cmd == 'dump': dump_trace() elif cmd == 'tail': diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index d9a6db0bb7..99a8146aaa 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -10,6 +10,7 @@ # This work is licensed under the terms of the GNU GPL, version 2. See # the COPYING file in the top-level directory. +from __future__ import print_function MSR_IA32_VMX_BASIC = 0x480 MSR_IA32_VMX_PINBASED_CTLS = 0x481 MSR_IA32_VMX_PROCBASED_CTLS = 0x482 diff --git a/scripts/ordereddict.py b/scripts/ordereddict.py deleted file mode 100644 index 2d1d81370b..0000000000 --- a/scripts/ordereddict.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) 2009 Raymond Hettinger -# -# 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. - -from UserDict import DictMixin - - -class OrderedDict(dict, DictMixin): - - def __init__(self, *args, **kwds): - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__end - except AttributeError: - self.clear() - self.update(*args, **kwds) - - def clear(self): - self.__end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.__map = {} # key --> [key, prev, next] - dict.clear(self) - - def __setitem__(self, key, value): - if key not in self: - end = self.__end - curr = end[1] - curr[2] = end[1] = self.__map[key] = [key, curr, end] - dict.__setitem__(self, key, value) - - def __delitem__(self, key): - dict.__delitem__(self, key) - key, prev, next = self.__map.pop(key) - prev[2] = next - next[1] = prev - - def __iter__(self): - end = self.__end - curr = end[2] - while curr is not end: - yield curr[0] - curr = curr[2] - - def __reversed__(self): - end = self.__end - curr = end[1] - while curr is not end: - yield curr[0] - curr = curr[1] - - def popitem(self, last=True): - if not self: - raise KeyError('dictionary is empty') - if last: - key = reversed(self).next() - else: - key = iter(self).next() - value = self.pop(key) - return key, value - - def __reduce__(self): - items = [[k, self[k]] for k in self] - tmp = self.__map, self.__end - del self.__map, self.__end - inst_dict = vars(self).copy() - self.__map, self.__end = tmp - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def keys(self): - return list(self) - - setdefault = DictMixin.setdefault - update = DictMixin.update - pop = DictMixin.pop - values = DictMixin.values - items = DictMixin.items - iterkeys = DictMixin.iterkeys - itervalues = DictMixin.itervalues - iteritems = DictMixin.iteritems - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - - def copy(self): - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - if isinstance(other, OrderedDict): - if len(self) != len(other): - return False - for p, q in zip(self.items(), other.items()): - if p != q: - return False - return True - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 0c5da3a54d..0f3c991918 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -193,13 +193,15 @@ out: return ret -def gen_register_command(name, success_response, allow_oob): +def gen_register_command(name, success_response, allow_oob, allow_preconfig): options = [] if not success_response: options += ['QCO_NO_SUCCESS_RESP'] if allow_oob: options += ['QCO_ALLOW_OOB'] + if allow_preconfig: + options += ['QCO_ALLOW_PRECONFIG'] if not options: options = ['QCO_NO_OPTIONS'] @@ -237,7 +239,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-commands', ' * Schema-defined QAPI/QMP commands', __doc__) - self._regy = '' + self._regy = QAPIGenCCode() self._visited_ret_types = {} def _begin_module(self, name): @@ -273,19 +275,28 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); ''', c_prefix=c_name(self._prefix, protect=False))) - genc.add(gen_registry(self._regy, self._prefix)) + genc.add(gen_registry(self._regy.get_content(), self._prefix)) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): if not gen: return - self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) + # FIXME: If T is a user-defined type, the user is responsible + # for making this work, i.e. to make T's condition the + # conjunction of the T-returning commands' conditions. If T + # is a built-in type, this isn't possible: the + # qmp_marshal_output_T() will be generated unconditionally. if ret_type and ret_type not in self._visited_ret_types[self._genc]: self._visited_ret_types[self._genc].add(ret_type) - self._genc.add(gen_marshal_output(ret_type)) - self._genh.add(gen_marshal_decl(name)) - self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) - self._regy += gen_register_command(name, success_response, allow_oob) + with ifcontext(ret_type.ifcond, + self._genh, self._genc, self._regy): + self._genc.add(gen_marshal_output(ret_type)) + with ifcontext(ifcond, self._genh, self._genc, self._regy): + 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._regy.add(gen_register_command(name, success_response, + allow_oob, allow_preconfig)) def gen_commands(schema, output_dir, prefix): diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 3e14bc41f2..9230a2a3e8 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -12,14 +12,13 @@ # See the COPYING file in the top-level directory. from __future__ import print_function +from contextlib import contextmanager import errno import os import re import string -try: - from collections import OrderedDict -except: - from ordereddict import OrderedDict +import sys +from collections import OrderedDict builtin_types = { 'null': 'QTYPE_QNULL', @@ -343,7 +342,10 @@ class QAPISchemaParser(object): return None try: - fobj = open(incl_fname, 'r') + if sys.version_info[0] >= 3: + fobj = open(incl_fname, 'r', encoding='utf-8') + else: + fobj = open(incl_fname, 'r') except IOError as e: raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname)) return QAPISchemaParser(fobj, previously_included, info) @@ -637,6 +639,27 @@ def add_name(name, info, meta, implicit=False): all_names[name] = meta +def check_if(expr, info): + + def check_if_str(ifcond, info): + if not isinstance(ifcond, str): + raise QAPISemError( + info, "'if' condition must be a string or a list of strings") + if ifcond == '': + raise QAPISemError(info, "'if' condition '' makes no sense") + + ifcond = expr.get('if') + if ifcond is None: + return + if isinstance(ifcond, list): + if ifcond == []: + raise QAPISemError(info, "'if' condition [] is useless") + for elt in ifcond: + check_if_str(elt, info) + else: + check_if_str(ifcond, info) + + def check_type(info, source, value, allow_array=False, allow_dict=False, allow_optional=False, allow_metas=[]): @@ -782,13 +805,6 @@ def check_union(expr, info): "enum '%s'" % (key, enum_define['enum'])) - # If discriminator is user-defined, ensure all values are covered - if enum_define: - for value in enum_define['data']: - if value not in members.keys(): - raise QAPISemError(info, "Union '%s' data missing '%s' branch" - % (name, value)) - def check_alternate(expr, info): name = expr['alternate'] @@ -872,10 +888,13 @@ def check_keys(expr_elem, meta, required, optional=[]): raise QAPISemError(info, "'%s' of %s '%s' should only use false value" % (key, meta, name)) - if (key == 'boxed' or key == 'allow-oob') and value is not True: + if (key == 'boxed' or key == 'allow-oob' or + key == 'allow-preconfig') and value is not True: raise QAPISemError(info, "'%s' of %s '%s' should only use true value" % (key, meta, name)) + if key == 'if': + check_if(expr, info) for key in required: if key not in expr: raise QAPISemError(info, "Key '%s' is missing from %s '%s'" @@ -904,28 +923,28 @@ def check_exprs(exprs): if 'enum' in expr: meta = 'enum' - check_keys(expr_elem, 'enum', ['data'], ['prefix']) + check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix']) enum_types[expr[meta]] = expr elif 'union' in expr: meta = 'union' check_keys(expr_elem, 'union', ['data'], - ['base', 'discriminator']) + ['base', 'discriminator', 'if']) union_types[expr[meta]] = expr elif 'alternate' in expr: meta = 'alternate' - check_keys(expr_elem, 'alternate', ['data']) + check_keys(expr_elem, 'alternate', ['data'], ['if']) elif 'struct' in expr: meta = 'struct' - check_keys(expr_elem, 'struct', ['data'], ['base']) + check_keys(expr_elem, 'struct', ['data'], ['base', 'if']) struct_types[expr[meta]] = expr elif 'command' in expr: meta = 'command' check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', - 'boxed', 'allow-oob']) + 'boxed', 'allow-oob', 'allow-preconfig', 'if']) elif 'event' in expr: meta = 'event' - check_keys(expr_elem, 'event', [], ['data', 'boxed']) + check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if']) else: raise QAPISemError(expr_elem['info'], "Expression is missing metatype") @@ -983,8 +1002,16 @@ def check_exprs(exprs): # Schema compiler frontend # +def listify_cond(ifcond): + if not ifcond: + return [] + if not isinstance(ifcond, list): + return [ifcond] + return ifcond + + class QAPISchemaEntity(object): - def __init__(self, name, info, doc): + def __init__(self, name, info, doc, ifcond=None): assert name is None or isinstance(name, str) self.name = name self.module = None @@ -995,12 +1022,19 @@ class QAPISchemaEntity(object): # such place). self.info = info self.doc = doc + self._ifcond = ifcond # self.ifcond is set only after .check() def c_name(self): return c_name(self.name) def check(self, schema): - pass + if isinstance(self._ifcond, QAPISchemaType): + # inherit the condition from a type + typ = self._ifcond + typ.check(schema) + self.ifcond = typ.ifcond + else: + self.ifcond = listify_cond(self._ifcond) def is_implicit(self): return not self.info @@ -1029,26 +1063,26 @@ class QAPISchemaVisitor(object): def visit_builtin_type(self, name, info, json_type): pass - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): pass - def visit_array_type(self, name, info, element_type): + def visit_array_type(self, name, info, ifcond, element_type): pass - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): pass - def visit_object_type_flat(self, name, info, members, variants): + def visit_object_type_flat(self, name, info, ifcond, members, variants): pass - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): pass - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): pass - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): pass @@ -1127,8 +1161,8 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): - def __init__(self, name, info, doc, values, prefix): - QAPISchemaType.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, values, prefix): + QAPISchemaType.__init__(self, name, info, doc, ifcond) for v in values: assert isinstance(v, QAPISchemaMember) v.set_owner(name) @@ -1137,6 +1171,7 @@ class QAPISchemaEnumType(QAPISchemaType): self.prefix = prefix def check(self, schema): + QAPISchemaType.check(self, schema) seen = {} for v in self.values: v.check_clash(self.info, seen) @@ -1157,20 +1192,23 @@ class QAPISchemaEnumType(QAPISchemaType): return 'string' def visit(self, visitor): - visitor.visit_enum_type(self.name, self.info, + visitor.visit_enum_type(self.name, self.info, self.ifcond, self.member_names(), self.prefix) class QAPISchemaArrayType(QAPISchemaType): def __init__(self, name, info, element_type): - QAPISchemaType.__init__(self, name, info, None) + QAPISchemaType.__init__(self, name, info, None, None) assert isinstance(element_type, str) self._element_type_name = element_type self.element_type = None def check(self, schema): + QAPISchemaType.check(self, schema) self.element_type = schema.lookup_type(self._element_type_name) assert self.element_type + self.element_type.check(schema) + self.ifcond = self.element_type.ifcond def is_implicit(self): return True @@ -1188,15 +1226,17 @@ class QAPISchemaArrayType(QAPISchemaType): return 'array of ' + elt_doc_type def visit(self, visitor): - visitor.visit_array_type(self.name, self.info, self.element_type) + visitor.visit_array_type(self.name, self.info, self.ifcond, + self.element_type) class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, base, local_members, variants): + def __init__(self, name, info, doc, ifcond, + base, local_members, variants): # struct has local_members, optional base, and no variants # flat union has base, variants, and no local_members # simple union has local_members, variants, and no base - QAPISchemaType.__init__(self, name, info, doc) + QAPISchemaType.__init__(self, name, info, doc, ifcond) assert base is None or isinstance(base, str) for m in local_members: assert isinstance(m, QAPISchemaObjectTypeMember) @@ -1211,6 +1251,7 @@ class QAPISchemaObjectType(QAPISchemaType): self.members = None def check(self, schema): + QAPISchemaType.check(self, schema) if self.members is False: # check for cycles raise QAPISemError(self.info, "Object %s contains itself" % self.name) @@ -1268,9 +1309,9 @@ class QAPISchemaObjectType(QAPISchemaType): return 'object' def visit(self, visitor): - visitor.visit_object_type(self.name, self.info, + visitor.visit_object_type(self.name, self.info, self.ifcond, self.base, self.local_members, self.variants) - visitor.visit_object_type_flat(self.name, self.info, + visitor.visit_object_type_flat(self.name, self.info, self.ifcond, self.members, self.variants) @@ -1359,6 +1400,14 @@ class QAPISchemaObjectTypeVariants(object): self.tag_member = seen[c_name(self._tag_name)] assert self._tag_name == self.tag_member.name assert isinstance(self.tag_member.type, QAPISchemaEnumType) + if self._tag_name: # flat union + # branches that are not explicitly covered get an empty type + cases = set([v.name for v in self.variants]) + for val in self.tag_member.type.values: + if val.name not in cases: + v = QAPISchemaObjectTypeVariant(val.name, 'q_empty') + v.set_owner(self.tag_member.owner) + self.variants.append(v) for v in self.variants: v.check(schema) # Union names must match enum values; alternate names are @@ -1384,8 +1433,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): class QAPISchemaAlternateType(QAPISchemaType): - def __init__(self, name, info, doc, variants): - QAPISchemaType.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, variants): + QAPISchemaType.__init__(self, name, info, doc, ifcond) assert isinstance(variants, QAPISchemaObjectTypeVariants) assert variants.tag_member variants.set_owner(name) @@ -1393,6 +1442,7 @@ class QAPISchemaAlternateType(QAPISchemaType): self.variants = variants def check(self, schema): + QAPISchemaType.check(self, schema) self.variants.tag_member.check(schema) # Not calling self.variants.check_clash(), because there's nothing # to clash with @@ -1414,16 +1464,17 @@ class QAPISchemaAlternateType(QAPISchemaType): return 'value' def visit(self, visitor): - visitor.visit_alternate_type(self.name, self.info, self.variants) + visitor.visit_alternate_type(self.name, self.info, self.ifcond, + self.variants) def is_empty(self): return False class QAPISchemaCommand(QAPISchemaEntity): - def __init__(self, name, info, doc, arg_type, ret_type, - gen, success_response, boxed, allow_oob): - QAPISchemaEntity.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, arg_type, ret_type, + gen, success_response, boxed, allow_oob, allow_preconfig): + QAPISchemaEntity.__init__(self, name, info, doc, ifcond) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type @@ -1434,8 +1485,10 @@ class QAPISchemaCommand(QAPISchemaEntity): self.success_response = success_response self.boxed = boxed self.allow_oob = allow_oob + self.allow_preconfig = allow_preconfig def check(self, schema): + QAPISchemaEntity.check(self, schema) if self._arg_type_name: self.arg_type = schema.lookup_type(self._arg_type_name) assert (isinstance(self.arg_type, QAPISchemaObjectType) or @@ -1455,21 +1508,23 @@ class QAPISchemaCommand(QAPISchemaEntity): assert isinstance(self.ret_type, QAPISchemaType) def visit(self, visitor): - visitor.visit_command(self.name, self.info, + visitor.visit_command(self.name, self.info, self.ifcond, self.arg_type, self.ret_type, self.gen, self.success_response, - self.boxed, self.allow_oob) + self.boxed, self.allow_oob, + self.allow_preconfig) class QAPISchemaEvent(QAPISchemaEntity): - def __init__(self, name, info, doc, arg_type, boxed): - QAPISchemaEntity.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, arg_type, boxed): + QAPISchemaEntity.__init__(self, name, info, doc, ifcond) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type = None self.boxed = boxed def check(self, schema): + QAPISchemaEntity.check(self, schema) if self._arg_type_name: self.arg_type = schema.lookup_type(self._arg_type_name) assert (isinstance(self.arg_type, QAPISchemaObjectType) or @@ -1486,13 +1541,18 @@ class QAPISchemaEvent(QAPISchemaEntity): raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") def visit(self, visitor): - visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) + visitor.visit_event(self.name, self.info, self.ifcond, + self.arg_type, self.boxed) class QAPISchema(object): def __init__(self, fname): self._fname = fname - parser = QAPISchemaParser(open(fname, 'r')) + if sys.version_info[0] >= 3: + f = open(fname, 'r', encoding='utf-8') + else: + f = open(fname, 'r') + parser = QAPISchemaParser(f) exprs = check_exprs(parser.exprs) self.docs = parser.docs self._entity_list = [] @@ -1558,22 +1618,22 @@ class QAPISchema(object): ('null', 'null', 'QNull' + pointer_suffix)]: self._def_builtin_type(*t) self.the_empty_object_type = QAPISchemaObjectType( - 'q_empty', None, None, None, [], None) + 'q_empty', None, None, None, None, [], None) self._def_entity(self.the_empty_object_type) qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']) - self._def_entity(QAPISchemaEnumType('QType', None, None, + self._def_entity(QAPISchemaEnumType('QType', None, None, None, qtype_values, 'QTYPE')) def _make_enum_members(self, values): return [QAPISchemaMember(v) for v in values] - def _make_implicit_enum_type(self, name, info, values): + def _make_implicit_enum_type(self, name, info, ifcond, values): # See also QAPISchemaObjectTypeMember._pretty_owner() name = name + 'Kind' # Use namespace reserved by add_name() self._def_entity(QAPISchemaEnumType( - name, info, None, self._make_enum_members(values), None)) + name, info, None, ifcond, self._make_enum_members(values), None)) return name def _make_array_type(self, element_type, info): @@ -1582,22 +1642,37 @@ class QAPISchema(object): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name - def _make_implicit_object_type(self, name, info, doc, role, members): + def _make_implicit_object_type(self, name, info, doc, ifcond, + role, members): if not members: return None # See also QAPISchemaObjectTypeMember._pretty_owner() name = 'q_obj_%s-%s' % (name, role) - if not self.lookup_entity(name, QAPISchemaObjectType): - self._def_entity(QAPISchemaObjectType(name, info, doc, None, - members, None)) + typ = self.lookup_entity(name, QAPISchemaObjectType) + if typ: + # The implicit object type has multiple users. This can + # happen only for simple unions' implicit wrapper types. + # Its ifcond should be the disjunction of its user's + # ifconds. Not implemented. Instead, we always pass the + # wrapped type's ifcond, which is trivially the same for all + # users. It's also necessary for the wrapper to compile. + # But it's not tight: the disjunction need not imply it. We + # may end up compiling useless wrapper types. + # TODO kill simple unions or implement the disjunction + assert ifcond == typ._ifcond # pylint: disable=protected-access + else: + self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, + None, members, None)) return name def _def_enum_type(self, expr, info, doc): name = expr['enum'] data = expr['data'] prefix = expr.get('prefix') + ifcond = expr.get('if') self._def_entity(QAPISchemaEnumType( - name, info, doc, self._make_enum_members(data), prefix)) + name, info, doc, ifcond, + self._make_enum_members(data), prefix)) def _make_member(self, name, typ, info): optional = False @@ -1617,7 +1692,8 @@ class QAPISchema(object): name = expr['struct'] base = expr.get('base') data = expr['data'] - self._def_entity(QAPISchemaObjectType(name, info, doc, base, + ifcond = expr.get('if') + self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base, self._make_members(data, info), None)) @@ -1629,18 +1705,21 @@ class QAPISchema(object): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( - typ, info, None, 'wrapper', [self._make_member('data', typ, info)]) + typ, info, None, self.lookup_type(typ), + 'wrapper', [self._make_member('data', typ, info)]) return QAPISchemaObjectTypeVariant(case, typ) def _def_union_type(self, expr, info, doc): name = expr['union'] data = expr['data'] base = expr.get('base') + ifcond = expr.get('if') tag_name = expr.get('discriminator') tag_member = None if isinstance(base, dict): - base = (self._make_implicit_object_type( - name, info, doc, 'base', self._make_members(base, info))) + base = self._make_implicit_object_type( + name, info, doc, ifcond, + 'base', self._make_members(base, info)) if tag_name: variants = [self._make_variant(key, value) for (key, value) in data.items()] @@ -1648,12 +1727,12 @@ class QAPISchema(object): else: variants = [self._make_simple_variant(key, value, info) for (key, value) in data.items()] - typ = self._make_implicit_enum_type(name, info, + typ = self._make_implicit_enum_type(name, info, ifcond, [v.name for v in variants]) tag_member = QAPISchemaObjectTypeMember('type', typ, False) members = [tag_member] self._def_entity( - QAPISchemaObjectType(name, info, doc, base, members, + QAPISchemaObjectType(name, info, doc, ifcond, base, members, QAPISchemaObjectTypeVariants(tag_name, tag_member, variants))) @@ -1661,11 +1740,12 @@ class QAPISchema(object): def _def_alternate_type(self, expr, info, doc): name = expr['alternate'] data = expr['data'] + ifcond = expr.get('if') variants = [self._make_variant(key, value) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) self._def_entity( - QAPISchemaAlternateType(name, info, doc, + QAPISchemaAlternateType(name, info, doc, ifcond, QAPISchemaObjectTypeVariants(None, tag_member, variants))) @@ -1678,24 +1758,27 @@ class QAPISchema(object): success_response = expr.get('success-response', True) boxed = expr.get('boxed', False) allow_oob = expr.get('allow-oob', False) + allow_preconfig = expr.get('allow-preconfig', False) + ifcond = expr.get('if') if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, 'arg', self._make_members(data, info)) + name, info, doc, ifcond, 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, + self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, gen, success_response, - boxed, allow_oob)) + boxed, allow_oob, allow_preconfig)) def _def_event(self, expr, info, doc): name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) + ifcond = expr.get('if') if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) + name, info, doc, ifcond, 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) def _def_exprs(self, exprs): for expr_elem in exprs: @@ -1822,7 +1905,7 @@ def c_name(name, protect=True): 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) # namespace pollution: - polluted_words = set(['unix', 'errno', 'mips', 'sparc']) + polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) name = name.translate(c_name_trans) if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words): @@ -1859,8 +1942,8 @@ def cgen(code, **kwds): if indent_level: indent = genindent(indent_level) # re.subn() lacks flags support before Python 2.7, use re.compile() - raw = re.subn(re.compile(r'^.', re.MULTILINE), - indent + r'\g<0>', raw) + raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE), + indent, raw) raw = raw[0] return re.sub(re.escape(eatspace) + r' *', '', raw) @@ -1892,6 +1975,40 @@ def guardend(name): name=guardname(name)) +def gen_if(ifcond): + ret = '' + for ifc in ifcond: + ret += mcgen(''' +#if %(cond)s +''', cond=ifc) + return ret + + +def gen_endif(ifcond): + ret = '' + for ifc in reversed(ifcond): + ret += mcgen(''' +#endif /* %(cond)s */ +''', cond=ifc) + return ret + + +def _wrap_ifcond(ifcond, before, after): + if before == after: + return after # suppress empty #if ... #endif + + assert after.startswith(before) + out = before + added = after[len(before):] + if added[0] == '\n': + out += '\n' + added = added[1:] + out += gen_if(ifcond) + out += added + out += gen_endif(ifcond) + return out + + def gen_enum_lookup(name, values, prefix=None): ret = mcgen(''' @@ -1989,6 +2106,10 @@ class QAPIGen(object): def add(self, text): self._body += text + def get_content(self, fname=None): + return (self._top(fname) + self._preamble + self._body + + self._bottom(fname)) + def _top(self, fname): return '' @@ -2005,9 +2126,11 @@ class QAPIGen(object): if e.errno != errno.EEXIST: raise fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) - f = os.fdopen(fd, 'r+') - text = (self._top(fname) + self._preamble + self._body - + self._bottom(fname)) + if sys.version_info[0] >= 3: + f = open(fd, 'r+', encoding='utf-8') + else: + f = os.fdopen(fd, 'r+') + text = self.get_content(fname) oldtext = f.read(len(text) + 1) if text != oldtext: f.seek(0) @@ -2016,10 +2139,62 @@ class QAPIGen(object): f.close() -class QAPIGenC(QAPIGen): +@contextmanager +def ifcontext(ifcond, *args): + """A 'with' statement context manager to wrap with start_if()/end_if() + + *args: any number of QAPIGenCCode + + Example:: + + with ifcontext(ifcond, self._genh, self._genc): + modify self._genh and self._genc ... + + Is equivalent to calling:: + + self._genh.start_if(ifcond) + self._genc.start_if(ifcond) + modify self._genh and self._genc ... + self._genh.end_if() + self._genc.end_if() + """ + for arg in args: + arg.start_if(ifcond) + yield + for arg in args: + arg.end_if() + + +class QAPIGenCCode(QAPIGen): + + def __init__(self): + QAPIGen.__init__(self) + self._start_if = None + + def start_if(self, ifcond): + assert self._start_if is None + self._start_if = (ifcond, self._body, self._preamble) + + def end_if(self): + assert self._start_if + self._wrap_ifcond() + self._start_if = None + + def _wrap_ifcond(self): + self._body = _wrap_ifcond(self._start_if[0], + self._start_if[1], self._body) + self._preamble = _wrap_ifcond(self._start_if[0], + self._start_if[2], self._preamble) + + def get_content(self, fname=None): + assert self._start_if is None + return QAPIGen.get_content(self, fname) + + +class QAPIGenC(QAPIGenCCode): def __init__(self, blurb, pydoc): - QAPIGen.__init__(self) + QAPIGenCCode.__init__(self) self._blurb = blurb self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, re.MULTILINE)) diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py old mode 100644 new mode 100755 index 9b312b2c51..987fd3c943 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -174,7 +174,7 @@ def texi_members(doc, what, base, variants, member_func): return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) -def texi_sections(doc): +def texi_sections(doc, ifcond): """Format additional sections following arguments""" body = '' for section in doc.sections: @@ -185,14 +185,16 @@ def texi_sections(doc): body += texi_example(section.text) else: body += texi_format(section.text) + if ifcond: + body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond) return body -def texi_entity(doc, what, base=None, variants=None, +def texi_entity(doc, what, ifcond, base=None, variants=None, member_func=texi_member): return (texi_body(doc) + texi_members(doc, what, base, variants, member_func) - + texi_sections(doc)) + + texi_sections(doc, ifcond)) class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): @@ -204,47 +206,47 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): def write(self, output_dir): self._gen.write(output_dir, self._prefix + 'qapi-doc.texi') - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): doc = self.cur_doc self._gen.add(TYPE_FMT(type='Enum', name=doc.symbol, - body=texi_entity(doc, 'Values', + body=texi_entity(doc, 'Values', ifcond, member_func=texi_enum_value))) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): doc = self.cur_doc if base and base.is_implicit(): base = None self._gen.add(TYPE_FMT(type='Object', name=doc.symbol, - body=texi_entity(doc, 'Members', + body=texi_entity(doc, 'Members', ifcond, base, variants))) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): doc = self.cur_doc self._gen.add(TYPE_FMT(type='Alternate', name=doc.symbol, - body=texi_entity(doc, 'Members'))) + body=texi_entity(doc, 'Members', ifcond))) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): doc = self.cur_doc if boxed: body = texi_body(doc) body += ('\n@b{Arguments:} the members of @code{%s}\n' % arg_type.name) - body += texi_sections(doc) + body += texi_sections(doc, ifcond) else: - body = texi_entity(doc, 'Arguments') + body = texi_entity(doc, 'Arguments', ifcond) self._gen.add(MSG_FMT(type='Command', name=doc.symbol, body=body)) - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): doc = self.cur_doc self._gen.add(MSG_FMT(type='Event', name=doc.symbol, - body=texi_entity(doc, 'Arguments'))) + body=texi_entity(doc, 'Arguments', ifcond))) def symbol(self, doc, entity): if self._gen._body: @@ -257,7 +259,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): assert not doc.args if self._gen._body: self._gen.add('\n') - self._gen.add(texi_body(doc) + texi_sections(doc)) + self._gen.add(texi_body(doc) + texi_sections(doc, None)) def gen_doc(schema, output_dir, prefix): diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 3dc523cf39..764ef177ab 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -142,7 +142,7 @@ out: ''') ret += mcgen(''' error_propagate(errp, err); - QDECREF(qmp); + qobject_unref(qmp); } ''') return ret @@ -180,12 +180,15 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): types=types)) def visit_end(self): - self._genh.add(gen_enum(self._enum_name, self._event_names)) - self._genc.add(gen_enum_lookup(self._enum_name, self._event_names)) + (genc, genh) = self._module[self._main_module] + genh.add(gen_enum(self._enum_name, self._event_names)) + genc.add(gen_enum_lookup(self._enum_name, self._event_names)) - def visit_event(self, name, info, arg_type, boxed): - self._genh.add(gen_event_send_decl(name, arg_type, boxed)) - self._genc.add(gen_event_send(name, arg_type, boxed, self._enum_name)) + def visit_event(self, name, info, ifcond, arg_type, boxed): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_event_send_decl(name, arg_type, boxed)) + self._genc.add(gen_event_send(name, arg_type, boxed, + self._enum_name)) self._event_names.append(name) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index f9e67e8227..189a4edaba 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -18,6 +18,15 @@ def to_qlit(obj, level=0, suppress_first_indent=False): def indent(level): return level * 4 * ' ' + if isinstance(obj, tuple): + ifobj, ifcond = obj + ret = gen_if(ifcond) + ret += to_qlit(ifobj, level) + endif = gen_endif(ifcond) + if endif: + ret += '\n' + endif + return ret + ret = '' if not suppress_first_indent: ret += indent(level) @@ -26,11 +35,11 @@ def to_qlit(obj, level=0, suppress_first_indent=False): elif isinstance(obj, str): ret += 'QLIT_QSTR(' + to_c_string(obj) + ')' elif isinstance(obj, list): - elts = [to_qlit(elt, level + 1) + elts = [to_qlit(elt, level + 1).strip('\n') for elt in obj] elts.append(indent(level + 1) + "{}") ret += 'QLIT_QLIST(((QLitObject[]) {\n' - ret += ',\n'.join(elts) + '\n' + ret += '\n'.join(elts) + '\n' ret += indent(level) + '}))' elif isinstance(obj, dict): elts = [] @@ -45,6 +54,8 @@ def to_qlit(obj, level=0, suppress_first_indent=False): ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false') else: assert False # not implemented + if level > 0: + ret += ',' return ret @@ -75,13 +86,10 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): def visit_end(self): # visit the types that are actually used - qlits = self._qlits - self._qlits = [] for typ in self._used_types: typ.visit(self) # generate C # TODO can generate awfully long lines - qlits.extend(self._qlits) name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit' self._genh.add(mcgen(''' #include "qapi/qmp/qlit.h" @@ -93,7 +101,7 @@ extern const QLitObject %(c_name)s; const QLitObject %(c_name)s = %(c_string)s; ''', c_name=c_name(name), - c_string=to_qlit(qlits))) + c_string=to_qlit(self._qlits))) self._schema = None self._qlits = [] self._used_types = [] @@ -129,12 +137,12 @@ const QLitObject %(c_name)s = %(c_string)s; return '[' + self._use_type(typ.element_type) + ']' return self._name(typ.name) - def _gen_qlit(self, name, mtype, obj): + def _gen_qlit(self, name, mtype, obj, ifcond): if mtype not in ('command', 'event', 'builtin', 'array'): name = self._name(name) obj['name'] = name obj['meta-type'] = mtype - self._qlits.append(obj) + self._qlits.append((obj, ifcond)) def _gen_member(self, member): ret = {'name': member.name, 'type': self._use_type(member.type)} @@ -150,39 +158,42 @@ const QLitObject %(c_name)s = %(c_string)s; return {'case': variant.name, 'type': self._use_type(variant.type)} def visit_builtin_type(self, name, info, json_type): - self._gen_qlit(name, 'builtin', {'json-type': json_type}) + self._gen_qlit(name, 'builtin', {'json-type': json_type}, []) - def visit_enum_type(self, name, info, values, prefix): - self._gen_qlit(name, 'enum', {'values': values}) + def visit_enum_type(self, name, info, ifcond, values, prefix): + self._gen_qlit(name, 'enum', {'values': values}, ifcond) - def visit_array_type(self, name, info, element_type): + def visit_array_type(self, name, info, ifcond, element_type): element = self._use_type(element_type) - self._gen_qlit('[' + element + ']', 'array', {'element-type': element}) + self._gen_qlit('[' + element + ']', 'array', {'element-type': element}, + ifcond) - def visit_object_type_flat(self, name, info, members, variants): + def visit_object_type_flat(self, name, info, ifcond, members, variants): obj = {'members': [self._gen_member(m) for m in members]} if variants: obj.update(self._gen_variants(variants.tag_member.name, variants.variants)) - self._gen_qlit(name, 'object', obj) + self._gen_qlit(name, 'object', obj, ifcond) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): self._gen_qlit(name, 'alternate', {'members': [{'type': self._use_type(m.type)} - for m in variants.variants]}) + for m in variants.variants]}, ifcond) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type - self._gen_qlit(name, 'command', - {'arg-type': self._use_type(arg_type), - 'ret-type': self._use_type(ret_type), - 'allow-oob': allow_oob}) + obj = {'arg-type': self._use_type(arg_type), + 'ret-type': self._use_type(ret_type) } + if allow_oob: + obj['allow-oob'] = allow_oob + self._gen_qlit(name, 'command', obj, ifcond) - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type - self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}) + self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}, + ifcond) def gen_introspect(schema, output_dir, prefix, opt_unmask): diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index 64d9c0fb37..fd7808103c 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -55,7 +55,7 @@ def gen_struct_members(members): return ret -def gen_object(name, base, members, variants): +def gen_object(name, ifcond, base, members, variants): if name in objects_seen: return '' objects_seen.add(name) @@ -64,11 +64,14 @@ def gen_object(name, base, members, variants): if variants: for v in variants.variants: if isinstance(v.type, QAPISchemaObjectType): - ret += gen_object(v.type.name, v.type.base, + ret += gen_object(v.type.name, v.type.ifcond, v.type.base, v.type.local_members, v.type.variants) ret += mcgen(''' +''') + ret += gen_if(ifcond) + ret += mcgen(''' struct %(c_name)s { ''', c_name=c_name(name)) @@ -101,6 +104,7 @@ struct %(c_name)s { ret += mcgen(''' }; ''') + ret += gen_endif(ifcond) return ret @@ -125,6 +129,8 @@ def gen_variants(variants): c_name=c_name(variants.tag_member.name)) for var in variants.variants: + if var.type.name == 'q_empty': + continue ret += mcgen(''' %(c_type)s %(c_name)s; ''', @@ -206,34 +212,40 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_type_cleanup_decl(name)) self._genc.add(gen_type_cleanup(name)) - def visit_enum_type(self, name, info, values, prefix): - self._genh.preamble_add(gen_enum(name, values, prefix)) - self._genc.add(gen_enum_lookup(name, values, prefix)) + def visit_enum_type(self, name, info, ifcond, values, prefix): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.preamble_add(gen_enum(name, values, prefix)) + self._genc.add(gen_enum_lookup(name, values, prefix)) - def visit_array_type(self, name, info, element_type): - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_array(name, element_type)) - self._gen_type_cleanup(name) + def visit_array_type(self, name, info, ifcond, element_type): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_array(name, element_type)) + self._gen_type_cleanup(name) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_object(name, base, members, variants)) - if base and not base.is_implicit(): - self._genh.add(gen_upcast(name, base)) - # TODO Worth changing the visitor signature, so we could - # directly use rather than repeat type.is_implicit()? - if not name.startswith('q_'): - # implicit types won't be directly allocated/freed - self._gen_type_cleanup(name) + with ifcontext(ifcond, self._genh): + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, ifcond, base, members, variants)) + with ifcontext(ifcond, self._genh, self._genc): + if base and not base.is_implicit(): + self._genh.add(gen_upcast(name, base)) + # TODO Worth changing the visitor signature, so we could + # directly use rather than repeat type.is_implicit()? + if not name.startswith('q_'): + # implicit types won't be directly allocated/freed + self._gen_type_cleanup(name) - def visit_alternate_type(self, name, info, variants): - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_object(name, None, + def visit_alternate_type(self, name, info, ifcond, variants): + with ifcontext(ifcond, self._genh): + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, ifcond, None, [variants.tag_member], variants)) - self._gen_type_cleanup(name) + with ifcontext(ifcond, self._genh, self._genc): + self._gen_type_cleanup(name) def gen_types(schema, output_dir, prefix, opt_builtins): diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 5d72d8936c..dd5034a66a 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -81,15 +81,24 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) c_name=c_name(variants.tag_member.name)) for var in variants.variants: - ret += mcgen(''' + case_str = c_enum_const(variants.tag_member.type.name, + var.name, + variants.tag_member.type.prefix) + if var.type.name == 'q_empty': + # valid variant and nothing to do + ret += mcgen(''' + case %(case)s: + break; +''', + case=case_str) + else: + ret += mcgen(''' case %(case)s: visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err); break; ''', - case=c_enum_const(variants.tag_member.type.name, - var.name, - variants.tag_member.type.prefix), - c_type=var.type.c_name(), c_name=c_name(var.name)) + case=case_str, + c_type=var.type.c_name(), c_name=c_name(var.name)) ret += mcgen(''' default: @@ -293,7 +302,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): #include "qapi/qmp/qerror.h" #include "%(visit)s.h" ''', - visit=visit, prefix=self._prefix)) + visit=visit)) self._genh.preamble_add(mcgen(''' #include "qapi/qapi-builtin-visit.h" #include "%(types)s.h" @@ -301,30 +310,35 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', types=types)) - def visit_enum_type(self, name, info, values, prefix): - self._genh.add(gen_visit_decl(name, scalar=True)) - self._genc.add(gen_visit_enum(name)) + def visit_enum_type(self, name, info, ifcond, values, prefix): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_decl(name, scalar=True)) + self._genc.add(gen_visit_enum(name)) - def visit_array_type(self, name, info, element_type): - self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_list(name, element_type)) + def visit_array_type(self, name, info, ifcond, element_type): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_list(name, element_type)) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self._genh.add(gen_visit_members_decl(name)) - self._genc.add(gen_visit_object_members(name, base, members, variants)) - # TODO Worth changing the visitor signature, so we could - # directly use rather than repeat type.is_implicit()? - if not name.startswith('q_'): - # only explicit types need an allocating visit - self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_object(name, base, members, variants)) + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_members_decl(name)) + self._genc.add(gen_visit_object_members(name, base, + members, variants)) + # TODO Worth changing the visitor signature, so we could + # directly use rather than repeat type.is_implicit()? + if not name.startswith('q_'): + # only explicit types need an allocating visit + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_object(name, base, members, variants)) - def visit_alternate_type(self, name, info, variants): - self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_alternate(name, variants)) + def visit_alternate_type(self, name, info, ifcond, variants): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_alternate(name, variants)) def gen_visit(schema, output_dir, prefix, opt_builtins): diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index 7ab7435fbd..b0dc8a714a 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -1,21 +1,21 @@ #!/bin/sh -# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC/s390/HPPA/Xtensa/microblaze -# program execution by the kernel +# Enable automatic program execution by the kernel. qemu_target_list="i386 i486 alpha arm armeb sparc32plus ppc ppc64 ppc64le m68k \ mips mipsel mipsn32 mipsn32el mips64 mips64el \ -sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb microblaze microblazeel" +sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb \ +microblaze microblazeel or1k" i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' -i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' i386_family=i386 i486_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00' -i486_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +i486_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' i486_family=i386 alpha_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90' -alpha_mask='\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +alpha_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' alpha_family=alpha arm_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00' @@ -27,11 +27,11 @@ armeb_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff armeb_family=armeb sparc_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02' -sparc_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +sparc_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' sparc_family=sparc sparc32plus_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x12' -sparc32plus_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +sparc32plus_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' sparc32plus_family=sparc ppc_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14' @@ -47,7 +47,7 @@ ppc64le_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\x ppc64le_family=ppcle m68k_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04' -m68k_mask='\xff\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +m68k_mask='\xff\xff\xff\xff\xff\xff\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' m68k_family=m68k # FIXME: We could use the other endianness on a MIPS host. @@ -77,15 +77,15 @@ mips64el_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\ mips64el_family=mips sh4_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00' -sh4_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +sh4_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' sh4_family=sh4 sh4eb_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a' -sh4eb_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +sh4eb_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' sh4eb_family=sh4 s390x_magic='\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x16' -s390x_mask='\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +s390x_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' s390x_family=s390x aarch64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00' @@ -124,6 +124,10 @@ microblazeel_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\ microblazeel_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' microblazeel_family=microblazeel +or1k_magic='\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x5c' +or1k_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff' +or1k_family=or1k + qemu_get_family() { cpu=${HOST_ARCH:-$(uname -m)} case "$cpu" in @@ -161,21 +165,26 @@ usage() { cat <> "$EXPORTDIR/qemu-$cpu" - fi } qemu_set_binfmts() { @@ -287,6 +302,7 @@ qemu_set_binfmts() { qemu="$QEMU_PATH/qemu-i386" fi + qemu="$qemu$QEMU_SUFFIX" if [ "$host_family" != "$family" ] ; then $BINFMT_SET fi @@ -300,9 +316,11 @@ SYSTEMDDIR="/etc/binfmt.d" DEBIANDIR="/usr/share/binfmts" QEMU_PATH=/usr/local/bin -FLAGS="" +CREDENTIAL=no +PERSISTENT=no +QEMU_SUFFIX="" -options=$(getopt -o ds:Q:e:hc: -l debian,systemd:,qemu-path:,exportdir:,help,credential: -- "$@") +options=$(getopt -o ds:Q:S:e:hc:p: -l debian,systemd:,qemu-path:,qemu-suffix:,exportdir:,help,credential:,persistent: -- "$@") eval set -- "$options" while true ; do @@ -338,6 +356,10 @@ while true ; do shift QEMU_PATH="$1" ;; + -F|--qemu-suffix) + shift + QEMU_SUFFIX="$1" + ;; -e|--exportdir) shift EXPORTDIR="$1" @@ -348,11 +370,11 @@ while true ; do ;; -c|--credential) shift - if [ "$1" = "yes" ] ; then - FLAGS="OC" - else - FLAGS="" - fi + CREDENTIAL="$1" + ;; + -p|--persistent) + shift + PERSISTENT="$1" ;; *) break diff --git a/scripts/qemu.py b/scripts/qemu.py index 08a3e9af5a..f099ce7278 100644 --- a/scripts/qemu.py +++ b/scripts/qemu.py @@ -17,19 +17,41 @@ import logging import os import subprocess import qmp.qmp +import re import shutil +import socket import tempfile LOG = logging.getLogger(__name__) +#: Maps machine types to the preferred console device types +CONSOLE_DEV_TYPES = { + r'^clipper$': 'isa-serial', + r'^malta': 'isa-serial', + r'^(pc.*|q35.*|isapc)$': 'isa-serial', + r'^(40p|powernv|prep)$': 'isa-serial', + r'^pseries.*': 'spapr-vty', + r'^s390-ccw-virtio.*': 'sclpconsole', + } + + class QEMUMachineError(Exception): """ Exception called when an error in QEMUMachine happens. """ +class QEMUMachineAddDeviceError(QEMUMachineError): + """ + Exception raised when a request to add a device can not be fulfilled + + The failures are caused by limitations, lack of information or conflicting + requests on the QEMUMachine methods. This exception does not represent + failures reported by the QEMU binary itself. + """ + class MonitorResponseError(qmp.qmp.QMPError): ''' Represents erroneous QMP monitor reply @@ -91,6 +113,10 @@ class QEMUMachine(object): self._test_dir = test_dir self._temp_dir = None self._launched = False + self._machine = None + self._console_device_type = None + self._console_address = None + self._console_socket = None # just in case logging wasn't configured by the main script: logging.basicConfig() @@ -175,9 +201,19 @@ class QEMUMachine(object): self._monitor_address[1]) else: moncdev = 'socket,id=mon,path=%s' % self._vm_monitor - return ['-chardev', moncdev, + args = ['-chardev', moncdev, '-mon', 'chardev=mon,mode=control', '-display', 'none', '-vga', 'none'] + if self._machine is not None: + args.extend(['-machine', self._machine]) + if self._console_device_type is not None: + self._console_address = os.path.join(self._temp_dir, + self._name + "-console.sock") + chardev = ('socket,id=console,path=%s,server,nowait' % + self._console_address) + device = '%s,chardev=console' % self._console_device_type + args.extend(['-chardev', chardev, '-device', device]) + return args def _pre_launch(self): self._temp_dir = tempfile.mkdtemp(dir=self._test_dir) @@ -202,6 +238,10 @@ class QEMUMachine(object): self._qemu_log_path = None + if self._console_socket is not None: + self._console_socket.close() + self._console_socket = None + if self._temp_dir is not None: shutil.rmtree(self._temp_dir) self._temp_dir = None @@ -359,3 +399,64 @@ class QEMUMachine(object): of the qemu process. ''' return self._iolog + + def add_args(self, *args): + ''' + Adds to the list of extra arguments to be given to the QEMU binary + ''' + self._args.extend(args) + + def set_machine(self, machine_type): + ''' + Sets the machine type + + If set, the machine type will be added to the base arguments + of the resulting QEMU command line. + ''' + self._machine = machine_type + + def set_console(self, device_type=None): + ''' + Sets the device type for a console device + + If set, the console device and a backing character device will + be added to the base arguments of the resulting QEMU command + line. + + This is a convenience method that will either use the provided + device type, of if not given, it will used the device type set + on CONSOLE_DEV_TYPES. + + The actual setting of command line arguments will be be done at + machine launch time, as it depends on the temporary directory + to be created. + + @param device_type: the device type, such as "isa-serial" + @raises: QEMUMachineAddDeviceError if the device type is not given + and can not be determined. + ''' + if device_type is None: + if self._machine is None: + raise QEMUMachineAddDeviceError("Can not add a console device:" + " QEMU instance without a " + "defined machine type") + for regex, device in CONSOLE_DEV_TYPES.items(): + if re.match(regex, self._machine): + device_type = device + break + if device_type is None: + raise QEMUMachineAddDeviceError("Can not add a console device:" + " no matching console device " + "type definition") + self._console_device_type = device_type + + @property + def console_socket(self): + """ + Returns a socket connected to the console + """ + if self._console_socket is None: + self._console_socket = socket.socket(socket.AF_UNIX, + socket.SOCK_STREAM) + self._console_socket.connect(self._console_address) + return self._console_socket diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client index 7d2a472094..e8cb7646a0 100755 --- a/scripts/qmp/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -36,6 +36,7 @@ # See also: https://wiki.qemu.org/Features/QAPI/GuestAgent # +from __future__ import print_function import base64 import random @@ -135,7 +136,7 @@ class QemuGuestAgentClient: def fsfreeze(self, cmd): if cmd not in ['status', 'freeze', 'thaw']: - raise StandardError('Invalid command: ' + cmd) + raise Exception('Invalid command: ' + cmd) return getattr(self.qga, 'fsfreeze' + '_' + cmd)() @@ -144,7 +145,7 @@ class QemuGuestAgentClient: def suspend(self, mode): if mode not in ['disk', 'ram', 'hybrid']: - raise StandardError('Invalid mode: ' + mode) + raise Exception('Invalid mode: ' + mode) try: getattr(self.qga, 'suspend' + '_' + mode)() @@ -155,7 +156,7 @@ class QemuGuestAgentClient: def shutdown(self, mode='powerdown'): if mode not in ['powerdown', 'halt', 'reboot']: - raise StandardError('Invalid mode: ' + mode) + raise Exception('Invalid mode: ' + mode) try: self.qga.shutdown(mode=mode) diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp index 514b539a6b..6cb46fdae2 100755 --- a/scripts/qmp/qmp +++ b/scripts/qmp/qmp @@ -10,6 +10,7 @@ # This work is licensed under the terms of the GNU GPLv2 or later. # See the COPYING file in the top-level directory. +from __future__ import print_function import sys, os from qmp import QEMUMonitorProtocol @@ -26,15 +27,15 @@ def print_response(rsp, prefix=[]): print_response(rsp[key], prefix + [key]) else: if len(prefix): - print '%s: %s' % ('.'.join(prefix), rsp) + print('%s: %s' % ('.'.join(prefix), rsp)) else: - print '%s' % (rsp) + print('%s' % (rsp)) def main(args): path = None # Use QMP_PATH if it's set - if os.environ.has_key('QMP_PATH'): + if 'QMP_PATH' in os.environ: path = os.environ['QMP_PATH'] while len(args): @@ -53,21 +54,21 @@ def main(args): elif arg in ['help']: os.execlp('man', 'man', 'qmp') else: - print 'Unknown argument "%s"' % arg + print('Unknown argument "%s"' % arg) args = args[1:] else: break if not path: - print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH" + print("QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH") return 1 if len(args): command, args = args[0], args[1:] else: - print 'No command found' - print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"' + print('No command found') + print('Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"') return 1 if command in ['help']: @@ -78,7 +79,7 @@ def main(args): def do_command(srv, cmd, **kwds): rsp = srv.cmd(cmd, kwds) - if rsp.has_key('error'): + if 'error' in rsp: raise Exception(rsp['error']['desc']) return rsp['return'] @@ -93,7 +94,7 @@ def main(args): os.execvp(fullcmd, [fullcmd] + args) except OSError as exc: if exc.errno == 2: - print 'Command "%s" not found.' % (fullcmd) + print('Command "%s" not found.' % (fullcmd)) return 1 raise return 0 @@ -104,7 +105,7 @@ def main(args): arguments = {} for arg in args: if not arg.startswith('--'): - print 'Unknown argument "%s"' % arg + print('Unknown argument "%s"' % arg) return 1 arg = arg[2:] diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index be449de621..a42306dd89 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -65,6 +65,7 @@ # which will echo back the properly formatted JSON-compliant QMP that is being # sent to QEMU, which is useful for debugging and documentation generation. +from __future__ import print_function import qmp import json import ast @@ -132,7 +133,7 @@ class QMPShell(qmp.QEMUMonitorProtocol): def _fill_completion(self): cmds = self.cmd('query-commands') - if cmds.has_key('error'): + if 'error' in cmds: return for cmd in cmds['return']: self._completer.append(cmd['name']) @@ -153,14 +154,14 @@ class QMPShell(qmp.QEMUMonitorProtocol): # File not found. No problem. pass else: - print "Failed to read history '%s'; %s" % (self._histfile, e) + print("Failed to read history '%s'; %s" % (self._histfile, e)) atexit.register(self.__save_history) def __save_history(self): try: readline.write_history_file(self._histfile) except Exception as e: - print "Failed to save history file '%s'; %s" % (self._histfile, e) + print("Failed to save history file '%s'; %s" % (self._histfile, e)) def __parse_value(self, val): try: @@ -258,15 +259,15 @@ class QMPShell(qmp.QEMUMonitorProtocol): if self._pretty: indent = 4 jsobj = json.dumps(qmp, indent=indent) - print str(jsobj) + print(str(jsobj)) def _execute_cmd(self, cmdline): try: qmpcmd = self.__build_cmd(cmdline) except Exception as e: - print 'Error while parsing command line: %s' % e - print 'command format: ', - print '[arg-name1=arg1] ... [arg-nameN=argN]' + print('Error while parsing command line: %s' % e) + print('command format: ', end=' ') + print('[arg-name1=arg1] ... [arg-nameN=argN]') return True # For transaction mode, we may have just cached the action: if qmpcmd is None: @@ -275,7 +276,7 @@ class QMPShell(qmp.QEMUMonitorProtocol): self._print(qmpcmd) resp = self.cmd_obj(qmpcmd) if resp is None: - print 'Disconnected' + print('Disconnected') return False self._print(resp) return True @@ -285,12 +286,12 @@ class QMPShell(qmp.QEMUMonitorProtocol): self.__completer_setup() def show_banner(self, msg='Welcome to the QMP low-level shell!'): - print msg + print(msg) if not self._greeting: - print 'Connected' + print('Connected') return version = self._greeting['QMP']['version']['qemu'] - print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']) + print('Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])) def get_prompt(self): if self._transmode: @@ -306,11 +307,11 @@ class QMPShell(qmp.QEMUMonitorProtocol): try: cmdline = raw_input(prompt) except EOFError: - print + print() return False if cmdline == '': for ev in self.get_events(): - print ev + print(ev) self.clear_events() return True else: @@ -366,24 +367,24 @@ class HMPShell(QMPShell): try: idx = int(cmdline.split()[1]) if not 'return' in self.__cmd_passthrough('info version', idx): - print 'bad CPU index' + print('bad CPU index') return True self.__cpu_index = idx except ValueError: - print 'cpu command takes an integer argument' + print('cpu command takes an integer argument') return True resp = self.__cmd_passthrough(cmdline, self.__cpu_index) if resp is None: - print 'Disconnected' + 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'], + print(resp['return'], end=' ') else: # Error - print '%s: %s' % (resp['error']['class'], resp['error']['desc']) + print('%s: %s' % (resp['error']['class'], resp['error']['desc'])) return True def show_banner(self): diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse index 5c6754aa63..4d85970a78 100755 --- a/scripts/qmp/qom-fuse +++ b/scripts/qmp/qom-fuse @@ -28,7 +28,7 @@ class QOMFS(Fuse): self.ino_count = 1 def get_ino(self, path): - if self.ino_map.has_key(path): + if path in self.ino_map: return self.ino_map[path] self.ino_map[path] = self.ino_count self.ino_count += 1 @@ -89,7 +89,7 @@ class QOMFS(Fuse): def getattr(self, path): if self.is_link(path): - value = posix.stat_result((0755 | stat.S_IFLNK, + value = posix.stat_result((0o755 | stat.S_IFLNK, self.get_ino(path), 0, 2, @@ -100,7 +100,7 @@ class QOMFS(Fuse): 0, 0)) elif self.is_object(path): - value = posix.stat_result((0755 | stat.S_IFDIR, + value = posix.stat_result((0o755 | stat.S_IFDIR, self.get_ino(path), 0, 2, @@ -111,7 +111,7 @@ class QOMFS(Fuse): 0, 0)) elif self.is_property(path): - value = posix.stat_result((0644 | stat.S_IFREG, + value = posix.stat_result((0o644 | stat.S_IFREG, self.get_ino(path), 0, 1, diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get index 0172c69441..ec5275d53a 100755 --- a/scripts/qmp/qom-get +++ b/scripts/qmp/qom-get @@ -11,6 +11,7 @@ # the COPYING file in the top-level directory. ## +from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol @@ -33,7 +34,7 @@ def usage_error(error_msg = "unspecified error"): if len(args) > 0: if args[0] == "-h": - print usage() + print(usage()) exit(0); elif args[0] == "-s": try: @@ -43,7 +44,7 @@ if len(args) > 0: args = args[2:] if not socket_path: - if os.environ.has_key('QMP_SOCKET'): + if 'QMP_SOCKET' in os.environ: socket_path = os.environ['QMP_SOCKET'] else: usage_error("no QMP socket path or address given"); @@ -62,6 +63,6 @@ srv.connect() rsp = srv.command('qom-get', path=path, property=prop) if type(rsp) == dict: for i in rsp.keys(): - print '%s: %s' % (i, rsp[i]) + print('%s: %s' % (i, rsp[i])) else: - print rsp + print(rsp) diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list index 1e7cc6cb2d..0f97440973 100755 --- a/scripts/qmp/qom-list +++ b/scripts/qmp/qom-list @@ -11,6 +11,7 @@ # the COPYING file in the top-level directory. ## +from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol @@ -33,7 +34,7 @@ def usage_error(error_msg = "unspecified error"): if len(args) > 0: if args[0] == "-h": - print usage() + print(usage()) exit(0); elif args[0] == "-s": try: @@ -43,7 +44,7 @@ if len(args) > 0: args = args[2:] if not socket_path: - if os.environ.has_key('QMP_SOCKET'): + if 'QMP_SOCKET' in os.environ: socket_path = os.environ['QMP_SOCKET'] else: usage_error("no QMP socket path or address given"); @@ -52,13 +53,13 @@ srv = QEMUMonitorProtocol(socket_path) srv.connect() if len(args) == 0: - print '/' + print('/') sys.exit(0) for item in srv.command('qom-list', path=args[0]): if item['type'].startswith('child<'): - print '%s/' % item['name'] + print('%s/' % item['name']) elif item['type'].startswith('link<'): - print '@%s/' % item['name'] + print('@%s/' % item['name']) else: - print '%s' % item['name'] + print('%s' % item['name']) diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set index 94e2778922..26ed9e3263 100755 --- a/scripts/qmp/qom-set +++ b/scripts/qmp/qom-set @@ -11,6 +11,7 @@ # the COPYING file in the top-level directory. ## +from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol @@ -34,7 +35,7 @@ def usage_error(error_msg = "unspecified error"): if len(args) > 0: if args[0] == "-h": - print usage() + print(usage()) exit(0); elif args[0] == "-s": try: @@ -44,7 +45,7 @@ if len(args) > 0: args = args[2:] if not socket_path: - if os.environ.has_key('QMP_SOCKET'): + if 'QMP_SOCKET' in os.environ: socket_path = os.environ['QMP_SOCKET'] else: usage_error("no QMP socket path or address given"); @@ -61,4 +62,4 @@ else: srv = QEMUMonitorProtocol(socket_path) srv.connect() -print srv.command('qom-set', path=path, property=prop, value=value) +print(srv.command('qom-set', path=path, property=prop, value=value)) diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree index 906fcd2640..31603c681f 100755 --- a/scripts/qmp/qom-tree +++ b/scripts/qmp/qom-tree @@ -13,6 +13,7 @@ # the COPYING file in the top-level directory. ## +from __future__ import print_function import sys import os from qmp import QEMUMonitorProtocol @@ -35,7 +36,7 @@ def usage_error(error_msg = "unspecified error"): if len(args) > 0: if args[0] == "-h": - print usage() + print(usage()) exit(0); elif args[0] == "-s": try: @@ -45,7 +46,7 @@ if len(args) > 0: args = args[2:] if not socket_path: - if os.environ.has_key('QMP_SOCKET'): + if 'QMP_SOCKET' in os.environ: socket_path = os.environ['QMP_SOCKET'] else: usage_error("no QMP socket path or address given"); @@ -54,15 +55,15 @@ srv = QEMUMonitorProtocol(socket_path) srv.connect() def list_node(path): - print '%s' % path + print('%s' % path) items = srv.command('qom-list', path=path) for item in items: if not item['type'].startswith('child<'): try: - print ' %s: %s (%s)' % (item['name'], srv.command('qom-get', path=path, property=item['name']), item['type']) + print(' %s: %s (%s)' % (item['name'], srv.command('qom-get', path=path, property=item['name']), item['type'])) except: - print ' %s: (%s)' % (item['name'], item['type']) - print '' + print(' %s: (%s)' % (item['name'], item['type'])) + print('') for item in items: if item['type'].startswith('child<'): list_node((path if (path != '/') else '') + '/' + item['name']) diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index e274086277..5ae77c8a92 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -18,6 +18,7 @@ # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see . +from __future__ import print_function import argparse import struct from collections import namedtuple @@ -89,9 +90,9 @@ def call_decode(table, index, dumpfile): "Search decode table for next step" decoder = next((d for d in table if d.eid == index), None) if not decoder: - print "Could not decode index: %d" % (index) - print "Entry is: %s" % (decoder) - print "Decode Table is:\n%s" % (table) + print("Could not decode index: %d" % (index)) + print("Entry is: %s" % (decoder)) + print("Decode Table is:\n%s" % (table)) return False else: return decoder.fn(decoder.eid, decoder.name, dumpfile) @@ -103,23 +104,23 @@ def print_event(eid, name, string=None, event_count=None): event_count = replay_state.event_count if string: - print "%d:%s(%d) %s" % (event_count, name, eid, string) + print("%d:%s(%d) %s" % (event_count, name, eid, string)) else: - print "%d:%s(%d)" % (event_count, name, eid) + print("%d:%s(%d)" % (event_count, name, eid)) # Decoders for each event type def decode_unimp(eid, name, _unused_dumpfile): "Unimplimented decoder, will trigger exit" - print "%s not handled - will now stop" % (name) + print("%s not handled - will now stop" % (name)) return False # Checkpoint decoder def swallow_async_qword(eid, name, dumpfile): "Swallow a qword of data without looking at it" step_id = read_qword(dumpfile) - print " %s(%d) @ %d" % (name, eid, step_id) + print(" %s(%d) @ %d" % (name, eid, step_id)) return True async_decode_table = [ Decoder(0, "REPLAY_ASYNC_EVENT_BH", swallow_async_qword), @@ -139,8 +140,8 @@ def decode_async(eid, name, dumpfile): async_event_checkpoint = read_byte(dumpfile) if async_event_checkpoint != replay_state.current_checkpoint: - print " mismatch between checkpoint %d and async data %d" % ( - replay_state.current_checkpoint, async_event_checkpoint) + print(" mismatch between checkpoint %d and async data %d" % ( + replay_state.current_checkpoint, async_event_checkpoint)) return True return call_decode(async_decode_table, async_event_kind, dumpfile) @@ -283,7 +284,7 @@ def decode_file(filename): version = read_dword(dumpfile) junk = read_qword(dumpfile) - print "HEADER: version 0x%x" % (version) + print("HEADER: version 0x%x" % (version)) if version == 0xe02007: event_decode_table = v7_event_table diff --git a/scripts/signrom.py b/scripts/signrom.py index 0497a1c32e..313ee28a17 100644 --- a/scripts/signrom.py +++ b/scripts/signrom.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # Option ROM signing utility # diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index 9d45c6ba4e..4ad34f90cd 100755 --- a/scripts/simpletrace.py +++ b/scripts/simpletrace.py @@ -9,6 +9,7 @@ # # For help see docs/devel/tracing.txt +from __future__ import print_function import struct import re import inspect @@ -44,7 +45,7 @@ def get_record(edict, idtoname, rechdr, fobj): rec = (name, rechdr[1], rechdr[3]) try: event = edict[name] - except KeyError, e: + except KeyError as e: import sys sys.stderr.write('%s event is logged but is not declared ' \ 'in the trace events file, try using ' \ @@ -69,7 +70,7 @@ def get_record(edict, idtoname, rechdr, fobj): def get_mapping(fobj): (event_id, ) = struct.unpack('=Q', fobj.read(8)) (len, ) = struct.unpack('=L', fobj.read(4)) - name = fobj.read(len) + name = fobj.read(len).decode() return (event_id, name) @@ -257,6 +258,6 @@ if __name__ == '__main__': else: fields.append('%s=0x%x' % (name, rec[i])) i += 1 - print ' '.join(fields) + print(' '.join(fields)) run(Formatter()) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index b20fac34a3..0e3c9e146c 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -53,8 +53,6 @@ ALLOWED_TYPES = [ "bool", "unsigned", "signed", - "float", - "double", "int8_t", "uint8_t", "int16_t", diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 78933d03ad..6751f41bc5 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -38,7 +38,7 @@ def generate_h(event, group): out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', ' struct timeval _now;', ' gettimeofday(&_now, NULL);', - ' qemu_log("%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",', + ' qemu_log("%%d@%%zu.%%06zu:%(name)s " %(fmt)s "\\n",', ' getpid(),', ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', ' %(argnames)s);', diff --git a/scripts/travis/coverage-summary.sh b/scripts/travis/coverage-summary.sh new file mode 100755 index 0000000000..d7086cf9ca --- /dev/null +++ b/scripts/travis/coverage-summary.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Author: Alex Bennée +# +# Summerise the state of code coverage with gcovr and tweak the output +# to be more sane on Travis hosts. As we expect to be executed on a +# throw away CI instance we do spam temp files all over the shop. You +# most likely don't want to execute this script but just call gcovr +# directly. See also "make coverage-report" +# +# This code is licensed under the GPL version 2 or later. See +# the COPYING file in the top-level directory. + +# first generate the coverage report +gcovr -p -o raw-report.txt + +# strip the full-path and line markers +sed s@$PWD\/@@ raw-report.txt | sed s/[0-9]\*[,-]//g > simplified.txt + +# reflow lines that got split +awk '/.[ch]$/ { printf("%s", $0); next } 1' simplified.txt > rejoined.txt + +# columnify +column -t rejoined.txt > final.txt + +# and dump, stripping out 0% coverage +grep -v "0%" final.txt diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 5b1d8dcdf4..feb75390aa 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -43,6 +43,7 @@ cp_portable() { -e 'limits' \ -e 'linux/kernel' \ -e 'linux/sysinfo' \ + -e 'asm-generic/kvm_para' \ > /dev/null then echo "Unexpected #include in input file $f". @@ -50,7 +51,8 @@ cp_portable() { fi header=$(basename "$f"); - sed -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ + sed -e 's/__aligned_u64/__u64 __attribute__((aligned(8)))/g' \ + -e 's/__u\([0-9][0-9]*\)/uint\1_t/g' \ -e 's/u\([0-9][0-9]*\)/uint\1_t/g' \ -e 's/__s\([0-9][0-9]*\)/int\1_t/g' \ -e 's/__le\([0-9][0-9]*\)/uint\1_t/g' \ @@ -83,11 +85,6 @@ for arch in $ARCHLIST; do continue fi - # Blacklist architectures which have KVM headers but are actually dead - if [ "$arch" = "ia64" -o "$arch" = "mips" ]; then - continue - fi - if [ "$arch" = x86 ]; then arch_var=SRCARCH else @@ -98,11 +95,12 @@ for arch in $ARCHLIST; do rm -rf "$output/linux-headers/asm-$arch" mkdir -p "$output/linux-headers/asm-$arch" - for header in kvm.h kvm_para.h unistd.h; do + for header in kvm.h unistd.h bitsperlong.h; do cp "$tmpdir/include/asm/$header" "$output/linux-headers/asm-$arch" done - if [ $arch = powerpc ]; then - cp "$tmpdir/include/asm/epapr_hcalls.h" "$output/linux-headers/asm-powerpc/" + + if [ $arch = mips ]; then + cp "$tmpdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/" fi rm -rf "$output/include/standard-headers/asm-$arch" @@ -118,35 +116,46 @@ for arch in $ARCHLIST; do cp "$tmpdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/" fi if [ $arch = x86 ]; then - cat <<-EOF >"$output/include/standard-headers/asm-x86/hyperv.h" - /* this is a temporary placeholder until kvm_para.h stops including it */ -EOF cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-x86/" cp "$tmpdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" + cp_portable "$tmpdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch" fi done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" -for header in kvm.h kvm_para.h vfio.h vfio_ccw.h vhost.h \ +for header in kvm.h vfio.h vfio_ccw.h vhost.h \ psci.h psp-sev.h userfaultfd.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done + rm -rf "$output/linux-headers/asm-generic" mkdir -p "$output/linux-headers/asm-generic" -for header in kvm_para.h; do +for header in unistd.h bitsperlong.h; do cp "$tmpdir/include/asm-generic/$header" "$output/linux-headers/asm-generic" done + if [ -L "$linux/source" ]; then cp "$linux/source/COPYING" "$output/linux-headers" else cp "$linux/COPYING" "$output/linux-headers" fi -cat <$output/linux-headers/asm-x86/hyperv.h -#include "standard-headers/asm-x86/hyperv.h" -EOF +# Recent kernel sources split the copyright/license info into multiple +# files, which we need to copy. This set of licenses is the set that +# are referred to by SPDX lines in the headers we currently copy. +# We don't copy the Documentation/process/license-rules.rst which +# is also referred to by COPYING, since it's explanatory rather than license. +if [ -d "$linux/LICENSES" ]; then + mkdir -p "$output/linux-headers/LICENSES/preferred" \ + "$output/linux-headers/LICENSES/exceptions" + for l in preferred/GPL-2.0 preferred/BSD-2-Clause preferred/BSD-3-Clause \ + exceptions/Linux-syscall-note; do + cp "$linux/LICENSES/$l" "$output/linux-headers/LICENSES/$l" + done +fi + cat <$output/linux-headers/linux/virtio_config.h #include "standard-headers/linux/virtio_config.h" EOF diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index bcef7ee28e..d3467288dc 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -19,6 +19,7 @@ # You should have received a copy of the GNU General Public License along # with this program; if not, see . +from __future__ import print_function import argparse import json import sys @@ -157,7 +158,7 @@ def check_fields(src_fields, dest_fields, desc, sec): while True: if advance_src: try: - s_item = s_iter.next() + s_item = next(s_iter) except StopIteration: if s_iter_list == []: break @@ -172,14 +173,14 @@ def check_fields(src_fields, dest_fields, desc, sec): if advance_dest: try: - d_item = d_iter.next() + d_item = next(d_iter) except StopIteration: if d_iter_list == []: # We were not in a substruct - print "Section \"" + sec + "\",", - print "Description " + "\"" + desc + "\":", - print "expected field \"" + s_item["field"] + "\",", - print "while dest has no further fields" + print("Section \"" + sec + "\",", end=' ') + print("Description " + "\"" + desc + "\":", end=' ') + print("expected field \"" + s_item["field"] + "\",", end=' ') + print("while dest has no further fields") bump_taint() break @@ -197,10 +198,10 @@ def check_fields(src_fields, dest_fields, desc, sec): advance_dest = True continue if unused_count < 0: - print "Section \"" + sec + "\",", - print "Description \"" + desc + "\":", - print "unused size mismatch near \"", - print s_item["field"] + "\"" + print("Section \"" + sec + "\",", end=' ') + print("Description \"" + desc + "\":", end=' ') + print("unused size mismatch near \"", end=' ') + print(s_item["field"] + "\"") bump_taint() break continue @@ -211,10 +212,10 @@ def check_fields(src_fields, dest_fields, desc, sec): advance_src = True continue if unused_count < 0: - print "Section \"" + sec + "\",", - print "Description \"" + desc + "\":", - print "unused size mismatch near \"", - print d_item["field"] + "\"" + print("Section \"" + sec + "\",", end=' ') + print("Description \"" + desc + "\":", end=' ') + print("unused size mismatch near \"", end=' ') + print(d_item["field"] + "\"") bump_taint() break continue @@ -262,10 +263,10 @@ def check_fields(src_fields, dest_fields, desc, sec): unused_count = s_item["size"] - d_item["size"] continue - print "Section \"" + sec + "\",", - print "Description \"" + desc + "\":", - print "expected field \"" + s_item["field"] + "\",", - print "got \"" + d_item["field"] + "\"; skipping rest" + print("Section \"" + sec + "\",", end=' ') + print("Description \"" + desc + "\":", end=' ') + print("expected field \"" + s_item["field"] + "\",", end=' ') + print("got \"" + d_item["field"] + "\"; skipping rest") bump_taint() break @@ -289,8 +290,8 @@ def check_subsections(src_sub, dest_sub, desc, sec): check_descriptions(s_item, d_item, sec) if not found: - print "Section \"" + sec + "\", Description \"" + desc + "\":", - print "Subsection \"" + s_item["name"] + "\" not found" + print("Section \"" + sec + "\", Description \"" + desc + "\":", end=' ') + print("Subsection \"" + s_item["name"] + "\" not found") bump_taint() @@ -299,8 +300,8 @@ def check_description_in_list(s_item, d_item, sec, desc): return if not "Description" in d_item: - print "Section \"" + sec + "\", Description \"" + desc + "\",", - print "Field \"" + s_item["field"] + "\": missing description" + print("Section \"" + sec + "\", Description \"" + desc + "\",", end=' ') + print("Field \"" + s_item["field"] + "\": missing description") bump_taint() return @@ -311,17 +312,17 @@ def check_descriptions(src_desc, dest_desc, sec): check_version(src_desc, dest_desc, sec, src_desc["name"]) if not check_fields_match(sec, src_desc["name"], dest_desc["name"]): - print "Section \"" + sec + "\":", - print "Description \"" + src_desc["name"] + "\"", - print "missing, got \"" + dest_desc["name"] + "\" instead; skipping" + print("Section \"" + sec + "\":", end=' ') + print("Description \"" + src_desc["name"] + "\"", end=' ') + print("missing, got \"" + dest_desc["name"] + "\" instead; skipping") bump_taint() return for f in src_desc: if not f in dest_desc: - print "Section \"" + sec + "\"", - print "Description \"" + src_desc["name"] + "\":", - print "Entry \"" + f + "\" missing" + print("Section \"" + sec + "\"", end=' ') + print("Description \"" + src_desc["name"] + "\":", end=' ') + print("Entry \"" + f + "\" missing") bump_taint() continue @@ -334,39 +335,39 @@ def check_descriptions(src_desc, dest_desc, sec): def check_version(s, d, sec, desc=None): if s["version_id"] > d["version_id"]: - print "Section \"" + sec + "\"", + print("Section \"" + sec + "\"", end=' ') if desc: - print "Description \"" + desc + "\":", - print "version error:", s["version_id"], ">", d["version_id"] + print("Description \"" + desc + "\":", end=' ') + print("version error:", s["version_id"], ">", d["version_id"]) bump_taint() if not "minimum_version_id" in d: return if s["version_id"] < d["minimum_version_id"]: - print "Section \"" + sec + "\"", + print("Section \"" + sec + "\"", end=' ') if desc: - print "Description \"" + desc + "\":", - print "minimum version error:", s["version_id"], "<", - print d["minimum_version_id"] + print("Description \"" + desc + "\":", end=' ') + print("minimum version error:", s["version_id"], "<", end=' ') + print(d["minimum_version_id"]) bump_taint() def check_size(s, d, sec, desc=None, field=None): if s["size"] != d["size"]: - print "Section \"" + sec + "\"", + print("Section \"" + sec + "\"", end=' ') if desc: - print "Description \"" + desc + "\"", + print("Description \"" + desc + "\"", end=' ') if field: - print "Field \"" + field + "\"", - print "size mismatch:", s["size"], ",", d["size"] + print("Field \"" + field + "\"", end=' ') + print("size mismatch:", s["size"], ",", d["size"]) bump_taint() def check_machine_type(s, d): if s["Name"] != d["Name"]: - print "Warning: checking incompatible machine types:", - print "\"" + s["Name"] + "\", \"" + d["Name"] + "\"" + print("Warning: checking incompatible machine types:", end=' ') + print("\"" + s["Name"] + "\", \"" + d["Name"] + "\"") return @@ -400,7 +401,7 @@ def main(): # doesn't exist in dest. dest_sec = get_changed_sec_name(sec) if not dest_sec in dest_data: - print "Section \"" + sec + "\" does not exist in dest" + print("Section \"" + sec + "\" does not exist in dest") bump_taint() continue @@ -415,8 +416,8 @@ def main(): for entry in s: if not entry in d: - print "Section \"" + sec + "\": Entry \"" + entry + "\"", - print "missing" + print("Section \"" + sec + "\": Entry \"" + entry + "\"", end=' ') + print("missing") bump_taint() continue diff --git a/scsi/Makefile.objs b/scsi/Makefile.objs index 4d25e476cf..bb8789cd8b 100644 --- a/scsi/Makefile.objs +++ b/scsi/Makefile.objs @@ -1,3 +1,4 @@ block-obj-y += utils.o block-obj-$(CONFIG_LINUX) += pr-manager.o pr-manager-helper.o +block-obj-$(call lnot,$(CONFIG_LINUX)) += pr-manager-stub.o diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index 82ff6b6123..3027dde60d 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -17,6 +17,7 @@ #include "io/channel.h" #include "io/channel-socket.h" #include "pr-helper.h" +#include "qapi/qapi-events-block.h" #include @@ -38,6 +39,17 @@ typedef struct PRManagerHelper { QIOChannel *ioc; } PRManagerHelper; +static void pr_manager_send_status_changed_event(PRManagerHelper *pr_mgr) +{ + char *id = object_get_canonical_path_component(OBJECT(pr_mgr)); + + if (id) { + qapi_event_send_pr_manager_status_changed(id, !!pr_mgr->ioc, + &error_abort); + g_free(id); + } +} + /* Called with lock held. */ static int pr_manager_helper_read(PRManagerHelper *pr_mgr, void *buf, int sz, Error **errp) @@ -47,6 +59,7 @@ static int pr_manager_helper_read(PRManagerHelper *pr_mgr, if (r < 0) { object_unref(OBJECT(pr_mgr->ioc)); pr_mgr->ioc = NULL; + pr_manager_send_status_changed_event(pr_mgr); return -EINVAL; } @@ -71,6 +84,8 @@ static int pr_manager_helper_write(PRManagerHelper *pr_mgr, if (n_written <= 0) { assert(n_written != QIO_CHANNEL_ERR_BLOCK); object_unref(OBJECT(pr_mgr->ioc)); + pr_mgr->ioc = NULL; + pr_manager_send_status_changed_event(pr_mgr); return n_written < 0 ? -EINVAL : 0; } @@ -126,6 +141,7 @@ static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, goto out_close; } + pr_manager_send_status_changed_event(pr_mgr); return 0; out_close: @@ -234,6 +250,18 @@ out: return ret; } +static bool pr_manager_helper_is_connected(PRManager *p) +{ + PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(p); + bool result; + + qemu_mutex_lock(&pr_mgr->lock); + result = (pr_mgr->ioc != NULL); + qemu_mutex_unlock(&pr_mgr->lock); + + return result; +} + static void pr_manager_helper_complete(UserCreatable *uc, Error **errp) { PRManagerHelper *pr_mgr = PR_MANAGER_HELPER(uc); @@ -283,6 +311,7 @@ static void pr_manager_helper_class_init(ObjectClass *klass, &error_abort); uc_klass->complete = pr_manager_helper_complete; prmgr_klass->run = pr_manager_helper_run; + prmgr_klass->is_connected = pr_manager_helper_is_connected; } static const TypeInfo pr_manager_helper_info = { diff --git a/scsi/pr-manager-stub.c b/scsi/pr-manager-stub.c new file mode 100644 index 0000000000..738b6d7425 --- /dev/null +++ b/scsi/pr-manager-stub.c @@ -0,0 +1,30 @@ +/* + * Persistent reservation manager - stub for non-Linux platforms + * + * Copyright (c) 2018 Red Hat, Inc. + * + * Author: Paolo Bonzini + * + * This code is licensed under the LGPL. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "scsi/pr-manager.h" +#include "trace.h" +#include "qapi/qapi-types-block.h" +#include "qapi/qapi-commands-block.h" + +PRManager *pr_manager_lookup(const char *id, Error **errp) +{ + /* The classes do not exist at all! */ + error_setg(errp, "No persistent reservation manager with id '%s'", id); + return NULL; +} + + +PRManagerInfoList *qmp_query_pr_managers(Error **errp) +{ + return NULL; +} diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c index 87c45db5d4..2a8f300dde 100644 --- a/scsi/pr-manager.c +++ b/scsi/pr-manager.c @@ -17,6 +17,10 @@ #include "block/thread-pool.h" #include "scsi/pr-manager.h" #include "trace.h" +#include "qapi/qapi-types-block.h" +#include "qapi/qapi-commands-block.h" + +#define PR_MANAGER_PATH "/objects" typedef struct PRManagerData { PRManager *pr_mgr; @@ -64,6 +68,14 @@ BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, data, complete, opaque); } +bool pr_manager_is_connected(PRManager *pr_mgr) +{ + PRManagerClass *pr_mgr_class = + PR_MANAGER_GET_CLASS(pr_mgr); + + return !pr_mgr_class->is_connected || pr_mgr_class->is_connected(pr_mgr); +} + static const TypeInfo pr_manager_info = { .parent = TYPE_OBJECT, .name = TYPE_PR_MANAGER, @@ -105,5 +117,38 @@ pr_manager_register_types(void) type_register_static(&pr_manager_info); } +static int query_one_pr_manager(Object *object, void *opaque) +{ + PRManagerInfoList ***prev = opaque; + PRManagerInfoList *elem; + PRManagerInfo *info; + PRManager *pr_mgr; + + pr_mgr = (PRManager *)object_dynamic_cast(object, TYPE_PR_MANAGER); + if (!pr_mgr) { + return 0; + } + + elem = g_new0(PRManagerInfoList, 1); + info = g_new0(PRManagerInfo, 1); + info->id = object_get_canonical_path_component(object); + info->connected = pr_manager_is_connected(pr_mgr); + elem->value = info; + elem->next = NULL; + + **prev = elem; + *prev = &elem->next; + return 0; +} + +PRManagerInfoList *qmp_query_pr_managers(Error **errp) +{ + PRManagerInfoList *head = NULL; + PRManagerInfoList **prev = &head; + Object *container = container_get(object_get_root(), PR_MANAGER_PATH); + + object_child_foreach(container, query_one_pr_manager, &prev); + return head; +} type_init(pr_manager_register_types); diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index d0f83176e1..1528a712a0 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -74,6 +74,12 @@ static int uid = -1; static int gid = -1; #endif +static void compute_default_paths(void) +{ + socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); + pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); +} + static void usage(const char *name) { (printf) ( @@ -447,6 +453,14 @@ static int multipath_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, char transportids[PR_HELPER_DATA_SIZE]; int r; + if (sz < PR_OUT_FIXED_PARAM_SIZE) { + /* Illegal request, Parameter list length error. This isn't fatal; + * we have read the data, send an error without closing the socket. + */ + scsi_build_sense(sense, SENSE_CODE(INVALID_PARAM_LEN)); + return CHECK_CONDITION; + } + switch (rq_servact) { case MPATH_PROUT_REG_SA: case MPATH_PROUT_RES_SA: @@ -550,7 +564,11 @@ static int do_pr_in(int fd, const uint8_t *cdb, uint8_t *sense, #ifdef CONFIG_MPATH if (is_mpath(fd)) { /* multipath_pr_in fills the whole input buffer. */ - return multipath_pr_in(fd, cdb, sense, data, *resp_sz); + int r = multipath_pr_in(fd, cdb, sense, data, *resp_sz); + if (r != GOOD) { + *resp_sz = 0; + } + return r; } #endif @@ -562,6 +580,12 @@ static int do_pr_out(int fd, const uint8_t *cdb, uint8_t *sense, const uint8_t *param, int sz) { int resp_sz; + + if ((fcntl(fd, F_GETFL) & O_ACCMODE) == O_RDONLY) { + scsi_build_sense(sense, SENSE_CODE(INVALID_OPCODE)); + return CHECK_CONDITION; + } + #ifdef CONFIG_MPATH if (is_mpath(fd)) { return multipath_pr_out(fd, cdb, sense, param, sz); @@ -678,21 +702,6 @@ static int coroutine_fn prh_read_request(PRHelperClient *client, errp) < 0) { goto out_close; } - if ((fcntl(client->fd, F_GETFL) & O_ACCMODE) == O_RDONLY) { - scsi_build_sense(resp->sense, SENSE_CODE(INVALID_OPCODE)); - sz = 0; - } else if (sz < PR_OUT_FIXED_PARAM_SIZE) { - /* Illegal request, Parameter list length error. This isn't fatal; - * we have read the data, send an error without closing the socket. - */ - scsi_build_sense(resp->sense, SENSE_CODE(INVALID_PARAM_LEN)); - sz = 0; - } - if (sz == 0) { - resp->result = CHECK_CONDITION; - close(client->fd); - client->fd = -1; - } } req->fd = client->fd; @@ -773,25 +782,23 @@ static void coroutine_fn prh_co_entry(void *opaque) break; } - if (sz > 0) { - num_active_sockets++; - if (req.cdb[0] == PERSISTENT_RESERVE_OUT) { - r = do_pr_out(req.fd, req.cdb, resp.sense, - client->data, sz); - resp.sz = 0; - } else { - resp.sz = sizeof(client->data); - r = do_pr_in(req.fd, req.cdb, resp.sense, - client->data, &resp.sz); - resp.sz = MIN(resp.sz, sz); - } - num_active_sockets--; - close(req.fd); - if (r == -1) { - break; - } - resp.result = r; + num_active_sockets++; + if (req.cdb[0] == PERSISTENT_RESERVE_OUT) { + r = do_pr_out(req.fd, req.cdb, resp.sense, + client->data, sz); + resp.sz = 0; + } else { + resp.sz = sizeof(client->data); + r = do_pr_in(req.fd, req.cdb, resp.sense, + client->data, &resp.sz); + resp.sz = MIN(resp.sz, sz); } + num_active_sockets--; + close(req.fd); + if (r == -1) { + break; + } + resp.result = r; if (prh_write_response(client, &req, &resp, &local_err) < 0) { break; @@ -832,26 +839,6 @@ static gboolean accept_client(QIOChannel *ioc, GIOCondition cond, gpointer opaqu return TRUE; } - -/* - * Check socket parameters compatibility when socket activation is used. - */ -static const char *socket_activation_validate_opts(void) -{ - if (socket_path != NULL) { - return "Unix socket can't be set when using socket activation"; - } - - return NULL; -} - -static void compute_default_paths(void) -{ - if (!socket_path) { - socket_path = qemu_get_local_state_pathname("run/qemu-pr-helper.sock"); - } -} - static void termsig_handler(int signum) { atomic_cmpxchg(&state, RUNNING, TERMINATE); @@ -925,6 +912,7 @@ int main(int argc, char **argv) char *trace_file = NULL; bool daemonize = false; bool pidfile_specified = false; + bool socket_path_specified = false; unsigned socket_activation; struct sigaction sa_sigterm; @@ -941,12 +929,14 @@ int main(int argc, char **argv) qemu_add_opts(&qemu_trace_opts); qemu_init_exec_dir(argv[0]); - pidfile = qemu_get_local_state_pathname("run/qemu-pr-helper.pid"); + compute_default_paths(); while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { case 'k': - socket_path = optarg; + g_free(socket_path); + socket_path = g_strdup(optarg); + socket_path_specified = true; if (socket_path[0] != '/') { error_report("socket path must be absolute"); exit(EXIT_FAILURE); @@ -1037,10 +1027,9 @@ int main(int argc, char **argv) socket_activation = check_socket_activation(); if (socket_activation == 0) { SocketAddress saddr; - compute_default_paths(); saddr = (SocketAddress){ .type = SOCKET_ADDRESS_TYPE_UNIX, - .u.q_unix.path = g_strdup(socket_path) + .u.q_unix.path = socket_path, }; server_ioc = qio_channel_socket_new(); if (qio_channel_socket_listen_sync(server_ioc, &saddr, &local_err) < 0) { @@ -1048,12 +1037,10 @@ int main(int argc, char **argv) error_report_err(local_err); return 1; } - g_free(saddr.u.q_unix.path); } else { /* Using socket activation - check user didn't use -p etc. */ - const char *err_msg = socket_activation_validate_opts(); - if (err_msg != NULL) { - error_report("%s", err_msg); + if (socket_path_specified) { + error_report("Unix socket can't be set when using socket activation"); exit(EXIT_FAILURE); } @@ -1070,7 +1057,6 @@ int main(int argc, char **argv) error_get_pretty(local_err)); exit(EXIT_FAILURE); } - socket_path = NULL; } if (qemu_init_main_loop(&local_err)) { diff --git a/slirp/arp_table.c b/slirp/arp_table.c index 3547043555..f81963bb88 100644 --- a/slirp/arp_table.c +++ b/slirp/arp_table.c @@ -33,7 +33,7 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]) int i; DEBUG_CALL("arp_table_add"); - DEBUG_ARG("ip = 0x%x", ip_addr); + DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){.s_addr = ip_addr})); DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", ethaddr[0], ethaddr[1], ethaddr[2], ethaddr[3], ethaddr[4], ethaddr[5])); @@ -67,7 +67,7 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr, int i; DEBUG_CALL("arp_table_search"); - DEBUG_ARG("ip = 0x%x", ip_addr); + DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){.s_addr = ip_addr})); /* If broadcast address */ if (ip_addr == 0xffffffff || ip_addr == broadcast_addr) { diff --git a/slirp/bootp.c b/slirp/bootp.c index 5dd1a415b5..9e7b53ba94 100644 --- a/slirp/bootp.c +++ b/slirp/bootp.c @@ -298,6 +298,14 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) q += val; } + if (slirp->vdomainname) { + val = strlen(slirp->vdomainname); + *q++ = RFC1533_DOMAINNAME; + *q++ = val; + memcpy(q, slirp->vdomainname, val); + q += val; + } + if (slirp->vdnssearch) { size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend); val = slirp->vdnssearch_len; diff --git a/slirp/libslirp.h b/slirp/libslirp.h index 540b3e5903..740408a96e 100644 --- a/slirp/libslirp.h +++ b/slirp/libslirp.h @@ -16,7 +16,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, struct in_addr vnameserver, struct in6_addr vnameserver6, const char **vdnssearch, - void *opaque); + const char *vdomainname, void *opaque); void slirp_cleanup(Slirp *slirp); void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout); diff --git a/slirp/mbuf.c b/slirp/mbuf.c index 5ff24559fd..1b7868355a 100644 --- a/slirp/mbuf.c +++ b/slirp/mbuf.c @@ -138,7 +138,7 @@ m_cat(struct mbuf *m, struct mbuf *n) * If there's no room, realloc */ if (M_FREEROOM(m) < n->m_len) - m_inc(m,m->m_size+MINCSIZE); + m_inc(m, m->m_len + n->m_len); memcpy(m->m_data+m->m_len, n->m_data, n->m_len); m->m_len += n->m_len; @@ -147,32 +147,29 @@ m_cat(struct mbuf *m, struct mbuf *n) } -/* make m size bytes large */ +/* make m 'size' bytes large from m_data */ void m_inc(struct mbuf *m, int size) { - int datasize; + int datasize; - /* some compiles throw up on gotos. This one we can fake. */ - if(m->m_size>size) return; + /* some compilers throw up on gotos. This one we can fake. */ + if (M_ROOM(m) > size) { + return; + } - if (m->m_flags & M_EXT) { - datasize = m->m_data - m->m_ext; - m->m_ext = g_realloc(m->m_ext, size); - m->m_data = m->m_ext + datasize; - } else { - char *dat; - datasize = m->m_data - m->m_dat; - dat = g_malloc(size); - memcpy(dat, m->m_dat, m->m_size); - - m->m_ext = dat; - m->m_data = m->m_ext + datasize; - m->m_flags |= M_EXT; - } - - m->m_size = size; + if (m->m_flags & M_EXT) { + datasize = m->m_data - m->m_ext; + m->m_ext = g_realloc(m->m_ext, size + datasize); + } else { + datasize = m->m_data - m->m_dat; + m->m_ext = g_malloc(size + datasize); + memcpy(m->m_ext, m->m_dat, m->m_size); + m->m_flags |= M_EXT; + } + m->m_data = m->m_ext + datasize; + m->m_size = size + datasize; } diff --git a/slirp/mbuf.h b/slirp/mbuf.h index 893601ff9d..33b84485d6 100644 --- a/slirp/mbuf.h +++ b/slirp/mbuf.h @@ -33,8 +33,6 @@ #ifndef MBUF_H #define MBUF_H -#define MINCSIZE 4096 /* Amount to increase mbuf if too small */ - /* * Macros for type conversion * mtod(m,t) - convert mbuf pointer to data pointer of correct type @@ -72,11 +70,11 @@ struct mbuf { struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */ int m_flags; /* Misc flags */ - int m_size; /* Size of data */ + int m_size; /* Size of mbuf, from m_dat or m_ext */ struct socket *m_so; - caddr_t m_data; /* Location of data */ - int m_len; /* Amount of data in this mbuf */ + caddr_t m_data; /* Current location of data */ + int m_len; /* Amount of data in this mbuf, from m_data */ Slirp *slirp; bool resolution_requested; diff --git a/slirp/ncsi.c b/slirp/ncsi.c index d12ba3e494..7116034afc 100644 --- a/slirp/ncsi.c +++ b/slirp/ncsi.c @@ -1,7 +1,7 @@ /* * NC-SI (Network Controller Sideband Interface) "echo" model * - * Copyright (C) 2016 IBM Corp. + * Copyright (C) 2016-2018 IBM Corp. * * This code is licensed under the GPL version 2 or later. See the * COPYING file in the top-level directory. @@ -11,6 +11,23 @@ #include "ncsi-pkt.h" +static uint32_t ncsi_calculate_checksum(uint16_t *data, int len) +{ + uint32_t checksum = 0; + int i; + + /* + * 32-bit unsigned sum of the NC-SI packet header and NC-SI packet + * payload interpreted as a series of 16-bit unsigned integer values. + */ + for (i = 0; i < len; i++) { + checksum += htons(data[i]); + } + + checksum = (~checksum + 1); + return checksum; +} + /* Get Capabilities */ static int ncsi_rsp_handler_gc(struct ncsi_rsp_pkt_hdr *rnh) { @@ -35,6 +52,20 @@ static int ncsi_rsp_handler_gls(struct ncsi_rsp_pkt_hdr *rnh) return 0; } +/* Get Parameters */ +static int ncsi_rsp_handler_gp(struct ncsi_rsp_pkt_hdr *rnh) +{ + struct ncsi_rsp_gp_pkt *rsp = (struct ncsi_rsp_gp_pkt *) rnh; + + /* no MAC address filters or VLAN filters on the channel */ + rsp->mac_cnt = 0; + rsp->mac_enable = 0; + rsp->vlan_cnt = 0; + rsp->vlan_enable = 0; + + return 0; +} + static const struct ncsi_rsp_handler { unsigned char type; int payload; @@ -60,9 +91,9 @@ static const struct ncsi_rsp_handler { { NCSI_PKT_RSP_EGMF, 4, NULL }, { NCSI_PKT_RSP_DGMF, 4, NULL }, { NCSI_PKT_RSP_SNFC, 4, NULL }, - { NCSI_PKT_RSP_GVI, 36, NULL }, + { NCSI_PKT_RSP_GVI, 40, NULL }, { NCSI_PKT_RSP_GC, 32, ncsi_rsp_handler_gc }, - { NCSI_PKT_RSP_GP, -1, NULL }, + { NCSI_PKT_RSP_GP, 40, ncsi_rsp_handler_gp }, { NCSI_PKT_RSP_GCPS, 172, NULL }, { NCSI_PKT_RSP_GNS, 172, NULL }, { NCSI_PKT_RSP_GNPTS, 172, NULL }, @@ -87,6 +118,9 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) (ncsi_reply + ETH_HLEN); const struct ncsi_rsp_handler *handler = NULL; int i; + int ncsi_rsp_len = sizeof(*nh); + uint32_t checksum; + uint32_t *pchecksum; memset(ncsi_reply, 0, sizeof(ncsi_reply)); @@ -116,15 +150,18 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) /* TODO: handle errors */ handler->handler(rnh); } + ncsi_rsp_len += handler->payload; } else { rnh->common.length = 0; rnh->code = htons(NCSI_PKT_RSP_C_UNAVAILABLE); rnh->reason = htons(NCSI_PKT_RSP_R_UNKNOWN); } - /* TODO: add a checksum at the end of the frame but the specs - * allows it to be zero */ + /* Add the optional checksum at the end of the frame. */ + checksum = ncsi_calculate_checksum((uint16_t *) rnh, ncsi_rsp_len); + pchecksum = (uint32_t *)((void *) rnh + ncsi_rsp_len); + *pchecksum = htonl(checksum); + ncsi_rsp_len += 4; - slirp_output(slirp->opaque, ncsi_reply, ETH_HLEN + sizeof(*nh) + - (handler ? handler->payload : 0) + 4); + slirp_output(slirp->opaque, ncsi_reply, ETH_HLEN + ncsi_rsp_len); } diff --git a/slirp/slirp.c b/slirp/slirp.c index 1cb6b07004..5c3bd6163f 100644 --- a/slirp/slirp.c +++ b/slirp/slirp.c @@ -286,7 +286,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, const char *tftp_path, const char *bootfile, struct in_addr vdhcp_start, struct in_addr vnameserver, struct in6_addr vnameserver6, const char **vdnssearch, - void *opaque) + const char *vdomainname, void *opaque) { Slirp *slirp = g_malloc0(sizeof(Slirp)); @@ -317,6 +317,7 @@ Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, } slirp->tftp_prefix = g_strdup(tftp_path); slirp->bootp_filename = g_strdup(bootfile); + slirp->vdomainname = g_strdup(vdomainname); slirp->vdhcp_startaddr = vdhcp_start; slirp->vnameserver_addr = vnameserver; slirp->vnameserver_addr6 = vnameserver6; @@ -349,6 +350,7 @@ void slirp_cleanup(Slirp *slirp) g_free(slirp->vdnssearch); g_free(slirp->tftp_prefix); g_free(slirp->bootp_filename); + g_free(slirp->vdomainname); g_free(slirp); } @@ -676,13 +678,13 @@ void slirp_pollfds_poll(GArray *pollfds, int select_error) /* continue; */ } else { ret = sowrite(so); + if (ret > 0) { + /* Call tcp_output in case we need to send a window + * update to the guest, otherwise it will be stuck + * until it sends a window probe. */ + tcp_output(sototcpcb(so)); + } } - /* - * XXXXX If we wrote something (a lot), there - * could be a need for a window update. - * In the worst case, the remote will send - * a window probe to get things going again - */ } /* diff --git a/slirp/slirp.h b/slirp/slirp.h index 06febfc78b..10b410898a 100644 --- a/slirp/slirp.h +++ b/slirp/slirp.h @@ -193,6 +193,7 @@ struct Slirp { char *bootp_filename; size_t vdnssearch_len; uint8_t *vdnssearch; + char *vdomainname; /* tcp states */ struct socket tcb; diff --git a/slirp/socket.c b/slirp/socket.c index cb7b5b608d..08fe98907d 100644 --- a/slirp/socket.c +++ b/slirp/socket.c @@ -340,7 +340,7 @@ sosendoob(struct socket *so) struct sbuf *sb = &so->so_rcv; char buff[2048]; /* XXX Shouldn't be sending more oob data than this */ - int n, len; + int n; DEBUG_CALL("sosendoob"); DEBUG_ARG("so = %p", so); @@ -359,7 +359,7 @@ sosendoob(struct socket *so) * send it all */ uint32_t urgc = so->so_urgc; - len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; + int len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr; if (len > urgc) { len = urgc; } @@ -374,13 +374,13 @@ sosendoob(struct socket *so) len += n; } n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */ +#ifdef DEBUG + if (n != len) { + DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n")); + } +#endif } -#ifdef DEBUG - if (n != len) { - DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n")); - } -#endif if (n < 0) { return n; } @@ -701,10 +701,10 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, memset(&addr, 0, addrlen); DEBUG_CALL("tcp_listen"); - DEBUG_ARG("haddr = %x", haddr); - DEBUG_ARG("hport = %d", hport); - DEBUG_ARG("laddr = %x", laddr); - DEBUG_ARG("lport = %d", lport); + DEBUG_ARG("haddr = %s", inet_ntoa((struct in_addr){.s_addr = haddr})); + DEBUG_ARG("hport = %d", ntohs(hport)); + DEBUG_ARG("laddr = %s", inet_ntoa((struct in_addr){.s_addr = laddr})); + DEBUG_ARG("lport = %d", ntohs(lport)); DEBUG_ARG("flags = %x", flags); so = socreate(slirp); @@ -754,6 +754,8 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, return NULL; } qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + opt = 1; + qemu_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)); getsockname(s,(struct sockaddr *)&addr,&addrlen); so->so_ffamily = AF_INET; diff --git a/slirp/tcp_subr.c b/slirp/tcp_subr.c index da0d53743f..8d0f94b75f 100644 --- a/slirp/tcp_subr.c +++ b/slirp/tcp_subr.c @@ -416,6 +416,8 @@ int tcp_fconnect(struct socket *so, unsigned short af) socket_set_fast_reuse(s); opt = 1; qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); + opt = 1; + qemu_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); addr = so->fhost.ss; DEBUG_CALL(" connect()ing") diff --git a/slirp/udp.c b/slirp/udp.c index 227d779022..e5bf065bf2 100644 --- a/slirp/udp.c +++ b/slirp/udp.c @@ -241,8 +241,8 @@ int udp_output(struct socket *so, struct mbuf *m, DEBUG_CALL("udp_output"); DEBUG_ARG("so = %p", so); DEBUG_ARG("m = %p", m); - DEBUG_ARG("saddr = %lx", (long)saddr->sin_addr.s_addr); - DEBUG_ARG("daddr = %lx", (long)daddr->sin_addr.s_addr); + DEBUG_ARG("saddr = %s", inet_ntoa(saddr->sin_addr)); + DEBUG_ARG("daddr = %s", inet_ntoa(daddr->sin_addr)); /* * Adjust for header diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 2d59d84091..53d3f32cb2 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -34,7 +34,7 @@ stub-obj-y += uuid.o stub-obj-y += vm-stop.o stub-obj-y += vmstate.o stub-obj-$(CONFIG_WIN32) += fd-register.o -stub-obj-y += qmp_pc_dimm.o +stub-obj-y += qmp_memory_device.o stub-obj-y += target-monitor-defs.o stub-obj-y += target-get-monitor-def.o stub-obj-y += pc_madt_cpu_entry.o diff --git a/stubs/error-printf.c b/stubs/error-printf.c index ac6b92aa69..99c6406668 100644 --- a/stubs/error-printf.c +++ b/stubs/error-printf.c @@ -4,7 +4,8 @@ void error_vprintf(const char *fmt, va_list ap) { - if (g_test_initialized() && !g_test_subprocess()) { + if (g_test_initialized() && !g_test_subprocess() && + getenv("QTEST_SILENT_ERRORS")) { char *msg = g_strdup_vprintf(fmt, ap); g_test_message("%s", msg); g_free(msg); diff --git a/stubs/fdset.c b/stubs/fdset.c index 6020cf28c8..4f3edf2ea4 100644 --- a/stubs/fdset.c +++ b/stubs/fdset.c @@ -14,7 +14,7 @@ int monitor_fdset_dup_fd_find(int dup_fd) int monitor_fdset_get_fd(int64_t fdset_id, int flags) { - return -1; + return -ENOENT; } void monitor_fdset_dup_fd_remove(int dupfd) diff --git a/stubs/linux-aio.c b/stubs/linux-aio.c index ed47bd443c..84d1f784ae 100644 --- a/stubs/linux-aio.c +++ b/stubs/linux-aio.c @@ -21,7 +21,7 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context) abort(); } -LinuxAioState *laio_init(void) +LinuxAioState *laio_init(Error **errp) { abort(); } diff --git a/stubs/monitor.c b/stubs/monitor.c index e018c8f594..3890771bb5 100644 --- a/stubs/monitor.c +++ b/stubs/monitor.c @@ -3,7 +3,7 @@ #include "qemu-common.h" #include "monitor/monitor.h" -Monitor *cur_mon = NULL; +__thread Monitor *cur_mon; int monitor_get_fd(Monitor *mon, const char *name, Error **errp) { diff --git a/stubs/qmp_pc_dimm.c b/stubs/qmp_memory_device.c similarity index 61% rename from stubs/qmp_pc_dimm.c rename to stubs/qmp_memory_device.c index b6b2cca89e..85ff8f2d7e 100644 --- a/stubs/qmp_pc_dimm.c +++ b/stubs/qmp_memory_device.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" #include "qom/object.h" -#include "hw/mem/pc-dimm.h" +#include "hw/mem/memory-device.h" -MemoryDeviceInfoList *qmp_pc_dimm_device_list(void) +MemoryDeviceInfoList *qmp_memory_device_list(void) { return NULL; } diff --git a/target/alpha/helper.c b/target/alpha/helper.c index 8a6a948572..57e2c212b3 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -442,20 +442,19 @@ void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n", env->pc, extract32(env->flags, ENV_FLAG_PS_SHIFT, 8)); for (i = 0; i < 31; i++) { - cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i, - linux_reg_names[i], cpu_alpha_load_gr(env, i)); - if ((i % 3) == 2) - cpu_fprintf(f, "\n"); + cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx "%c", i, + linux_reg_names[i], cpu_alpha_load_gr(env, i), + (i % 3) == 2 ? '\n' : ' '); } cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n", env->lock_addr, env->lock_value); - for (i = 0; i < 31; i++) { - cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i, - *((uint64_t *)(&env->fir[i]))); - if ((i % 3) == 2) - cpu_fprintf(f, "\n"); + if (flags & CPU_DUMP_FPU) { + for (i = 0; i < 31; i++) { + cpu_fprintf(f, "FIR%02d %016" PRIx64 "%c", i, env->fir[i], + (i % 3) == 2 ? '\n' : ' '); + } } cpu_fprintf(f, "\n"); } diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 73a1b5e63e..e5d62850c5 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -488,7 +488,7 @@ static DisasJumpType gen_bdirect(DisasContext *ctx, int ra, int32_t disp) } else if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb); + tcg_gen_exit_tb(ctx->base.tb, 0); return DISAS_NORETURN; } else { tcg_gen_movi_i64(cpu_pc, dest); @@ -507,12 +507,12 @@ static DisasJumpType gen_bcond_internal(DisasContext *ctx, TCGCond cond, tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb); + tcg_gen_exit_tb(ctx->base.tb, 0); gen_set_label(lab_true); tcg_gen_goto_tb(1); tcg_gen_movi_i64(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + 1); + tcg_gen_exit_tb(ctx->base.tb, 1); return DISAS_NORETURN; } else { @@ -1273,7 +1273,7 @@ static DisasJumpType gen_call_pal(DisasContext *ctx, int palcode) if (!use_exit_tb(ctx)) { tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, entry); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb); + tcg_gen_exit_tb(ctx->base.tb, 0); return DISAS_NORETURN; } else { tcg_gen_movi_i64(cpu_pc, entry); @@ -2919,8 +2919,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) return ret; } -static int alpha_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cpu, int max_insns) +static void alpha_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUAlphaState *env = cpu->env_ptr; @@ -2959,8 +2958,7 @@ static int alpha_tr_init_disas_context(DisasContextBase *dcbase, mask = TARGET_PAGE_MASK; } bound = -(ctx->base.pc_first | mask) / 4; - - return MIN(max_insns, bound); + ctx->base.max_insns = MIN(ctx->base.max_insns, bound); } static void alpha_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -3011,7 +3009,7 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) if (use_goto_tb(ctx, ctx->base.pc_next)) { tcg_gen_goto_tb(0); tcg_gen_movi_i64(cpu_pc, ctx->base.pc_next); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb); + tcg_gen_exit_tb(ctx->base.tb, 0); } /* FALLTHRU */ case DISAS_PC_STALE: @@ -3027,7 +3025,7 @@ static void alpha_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) if (ctx->base.singlestep_enabled) { gen_excp_1(EXCP_DEBUG, 0); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } break; default: diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index 1297bead5f..11c7baf8a3 100644 --- a/target/arm/Makefile.objs +++ b/target/arm/Makefile.objs @@ -10,3 +10,13 @@ obj-y += gdbstub.o obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o obj-y += crypto_helper.o obj-$(CONFIG_SOFTMMU) += arm-powerctl.o + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +target/arm/decode-sve.inc.c: $(SRC_PATH)/target/arm/sve.decode $(DECODETREE) + $(call quiet-command,\ + $(PYTHON) $(DECODETREE) --decode disas_sve -o $@ $<,\ + "GEN", $(TARGET_DIR)$@) + +target/arm/translate-sve.o: target/arm/decode-sve.inc.c +obj-$(TARGET_AARCH64) += translate-sve.o sve_helper.o diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index 25207cb850..ce55eeb682 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -15,7 +15,6 @@ #include "arm-powerctl.h" #include "qemu/log.h" #include "qemu/main-loop.h" -#include "exec/exec-all.h" #ifndef DEBUG_ARM_POWERCTL #define DEBUG_ARM_POWERCTL 0 diff --git a/target/arm/arm_ldst.h b/target/arm/arm_ldst.h index 01587b3ebb..5e0ac8bef0 100644 --- a/target/arm/arm_ldst.h +++ b/target/arm/arm_ldst.h @@ -20,7 +20,6 @@ #ifndef ARM_LDST_H #define ARM_LDST_H -#include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "qemu/bswap.h" diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 022d8c5787..64a8005a4b 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -55,13 +55,26 @@ static bool arm_cpu_has_work(CPUState *cs) | CPU_INTERRUPT_EXITTB); } -void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHook *hook, +void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void *opaque) { - /* We currently only support registering a single hook function */ - assert(!cpu->el_change_hook); - cpu->el_change_hook = hook; - cpu->el_change_hook_opaque = opaque; + ARMELChangeHook *entry = g_new0(ARMELChangeHook, 1); + + entry->hook = hook; + entry->opaque = opaque; + + QLIST_INSERT_HEAD(&cpu->pre_el_change_hooks, entry, node); +} + +void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, + void *opaque) +{ + ARMELChangeHook *entry = g_new0(ARMELChangeHook, 1); + + entry->hook = hook; + entry->opaque = opaque; + + QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node); } static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque) @@ -151,6 +164,13 @@ static void arm_cpu_reset(CPUState *s) env->cp15.sctlr_el[1] |= SCTLR_UCT | SCTLR_UCI | SCTLR_DZE; /* and to the FP/Neon instructions */ env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 20, 2, 3); + /* and to the SVE instructions */ + env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 16, 2, 3); + env->cp15.cptr_el[3] |= CPTR_EZ; + /* with maximum vector length */ + env->vfp.zcr_el[1] = ARM_MAX_VQ - 1; + env->vfp.zcr_el[2] = ARM_MAX_VQ - 1; + env->vfp.zcr_el[3] = ARM_MAX_VQ - 1; #else /* Reset into the highest available EL */ if (arm_feature(env, ARM_FEATURE_EL3)) { @@ -219,7 +239,7 @@ static void arm_cpu_reset(CPUState *s) /* Load the initial SP and PC from offset 0 and 4 in the vector table */ vecbase = env->v7m.vecbase[env->v7m.secure]; - rom = rom_ptr(vecbase); + rom = rom_ptr(vecbase, 8); if (rom) { /* Address zero is covered by ROM which hasn't yet been * copied into physical memory. @@ -311,6 +331,8 @@ static void arm_cpu_reset(CPUState *s) &env->vfp.fp_status); set_float_detect_tininess(float_tininess_before_rounding, &env->vfp.standard_fp_status); + set_float_detect_tininess(float_tininess_before_rounding, + &env->vfp.fp_status_f16); #ifndef CONFIG_USER_ONLY if (kvm_enabled()) { kvm_arm_reset_vcpu(cpu); @@ -552,6 +574,9 @@ static void arm_cpu_initfn(Object *obj) cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); + QLIST_INIT(&cpu->pre_el_change_hooks); + QLIST_INIT(&cpu->el_change_hooks); + #ifndef CONFIG_USER_ONLY /* Our inbound IRQ and FIQ lines */ if (kvm_enabled()) { @@ -672,7 +697,7 @@ static void arm_cpu_post_init(Object *obj) TYPE_MEMORY_REGION, (Object **)&cpu->secure_memory, qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); #endif } @@ -700,7 +725,7 @@ static void arm_cpu_post_init(Object *obj) if (arm_feature(&cpu->env, ARM_FEATURE_M_SECURITY)) { object_property_add_link(obj, "idau", TYPE_IDAU_INTERFACE, &cpu->idau, qdev_prop_allow_set_link_before_realize, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); qdev_property_add_static(DEVICE(obj), &arm_cpu_initsvtor_property, &error_abort); @@ -713,7 +738,18 @@ static void arm_cpu_post_init(Object *obj) static void arm_cpu_finalizefn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMELChangeHook *hook, *next; + g_hash_table_destroy(cpu->cp_regs); + + QLIST_FOREACH_SAFE(hook, &cpu->pre_el_change_hooks, node, next) { + QLIST_REMOVE(hook, node); + g_free(hook); + } + QLIST_FOREACH_SAFE(hook, &cpu->el_change_hooks, node, next) { + QLIST_REMOVE(hook, node); + g_free(hook); + } } static void arm_cpu_realizefn(DeviceState *dev, Error **errp) @@ -738,6 +774,24 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) return; } +#ifndef CONFIG_USER_ONLY + /* The NVIC and M-profile CPU are two halves of a single piece of + * hardware; trying to use one without the other is a command line + * error and will result in segfaults if not caught here. + */ + if (arm_feature(env, ARM_FEATURE_M)) { + if (!env->nvic) { + error_setg(errp, "This board cannot be used with Cortex-M CPUs"); + return; + } + } else { + if (env->nvic) { + error_setg(errp, "This board can only be used with Cortex-M CPUs"); + return; + } + } +#endif + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -746,9 +800,20 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) /* Some features automatically imply others: */ if (arm_feature(env, ARM_FEATURE_V8)) { - set_feature(env, ARM_FEATURE_V7); + set_feature(env, ARM_FEATURE_V7VE); + } + if (arm_feature(env, ARM_FEATURE_V7VE)) { + /* v7 Virtualization Extensions. In real hardware this implies + * EL2 and also the presence of the Security Extensions. + * For QEMU, for backwards-compatibility we implement some + * CPUs or CPU configs which have no actual EL2 or EL3 but do + * include the various other features that V7VE implies. + * Presence of EL2 itself is ARM_FEATURE_EL2, and of the + * Security Extensions is ARM_FEATURE_EL3. + */ set_feature(env, ARM_FEATURE_ARM_DIV); set_feature(env, ARM_FEATURE_LPAE); + set_feature(env, ARM_FEATURE_V7); } if (arm_feature(env, ARM_FEATURE_V7)) { set_feature(env, ARM_FEATURE_VAPA); @@ -1191,6 +1256,7 @@ static void cortex_m3_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); cpu->midr = 0x410fc231; cpu->pmsav7_dregion = 8; cpu->id_pfr0 = 0x00000030; @@ -1207,6 +1273,7 @@ static void cortex_m3_initfn(Object *obj) cpu->id_isar3 = 0x01111110; cpu->id_isar4 = 0x01310102; cpu->id_isar5 = 0x00000000; + cpu->id_isar6 = 0x00000000; } static void cortex_m4_initfn(Object *obj) @@ -1215,6 +1282,7 @@ static void cortex_m4_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); cpu->midr = 0x410fc240; /* r0p0 */ cpu->pmsav7_dregion = 8; @@ -1232,6 +1300,7 @@ static void cortex_m4_initfn(Object *obj) cpu->id_isar3 = 0x01111110; cpu->id_isar4 = 0x01310102; cpu->id_isar5 = 0x00000000; + cpu->id_isar6 = 0x00000000; } static void cortex_m33_initfn(Object *obj) @@ -1240,6 +1309,7 @@ static void cortex_m33_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_M); + set_feature(&cpu->env, ARM_FEATURE_M_MAIN); set_feature(&cpu->env, ARM_FEATURE_M_SECURITY); set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP); cpu->midr = 0x410fd213; /* r0p3 */ @@ -1259,6 +1329,7 @@ static void cortex_m33_initfn(Object *obj) cpu->id_isar3 = 0x01111131; cpu->id_isar4 = 0x01310132; cpu->id_isar5 = 0x00000000; + cpu->id_isar6 = 0x00000000; cpu->clidr = 0x00000000; cpu->ctr = 0x8000c000; } @@ -1309,11 +1380,20 @@ static void cortex_r5_initfn(Object *obj) cpu->id_isar3 = 0x01112131; cpu->id_isar4 = 0x0010142; cpu->id_isar5 = 0x0; + cpu->id_isar6 = 0x0; cpu->mp_is_up = true; cpu->pmsav7_dregion = 16; define_arm_cp_regs(cpu, cortexr5_cp_reginfo); } +static void cortex_r5f_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cortex_r5_initfn(obj); + set_feature(&cpu->env, ARM_FEATURE_VFP3); +} + static const ARMCPRegInfo cortexa8_cp_reginfo[] = { { .name = "L2LOCKDOWN", .cp = 15, .crn = 9, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -1459,15 +1539,13 @@ static void cortex_a7_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cpu->dtb_compatible = "arm,cortex-a7"; - set_feature(&cpu->env, ARM_FEATURE_V7); + set_feature(&cpu->env, ARM_FEATURE_V7VE); set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_LPAE); set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A7; cpu->midr = 0x410fc075; @@ -1504,15 +1582,13 @@ static void cortex_a15_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); cpu->dtb_compatible = "arm,cortex-a15"; - set_feature(&cpu->env, ARM_FEATURE_V7); + set_feature(&cpu->env, ARM_FEATURE_V7VE); set_feature(&cpu->env, ARM_FEATURE_VFP4); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); - set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_LPAE); set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15; cpu->midr = 0x412fc0f1; @@ -1731,15 +1807,13 @@ static void arm_max_initfn(Object *obj) * since we don't correctly set the ID registers to advertise them, */ set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_VFP4); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_V8_AES); set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); set_feature(&cpu->env, ARM_FEATURE_CRC); set_feature(&cpu->env, ARM_FEATURE_V8_RDM); + set_feature(&cpu->env, ARM_FEATURE_V8_DOTPROD); set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); #endif } @@ -1774,6 +1848,7 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "cortex-m33", .initfn = cortex_m33_initfn, .class_init = arm_v7m_class_init }, { .name = "cortex-r5", .initfn = cortex_r5_initfn }, + { .name = "cortex-r5f", .initfn = cortex_r5f_initfn }, { .name = "cortex-a7", .initfn = cortex_a7_initfn }, { .name = "cortex-a8", .initfn = cortex_a8_initfn }, { .name = "cortex-a9", .initfn = cortex_a9_initfn }, @@ -1879,6 +1954,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_num_core_regs = 26; cc->gdb_core_xml_file = "arm-core.xml"; cc->gdb_arch_name = arm_gdb_arch_name; + cc->gdb_get_dynamic_xml = arm_gdb_get_dynamic_xml; cc->gdb_stop_before_watchpoint = true; cc->debug_excp_handler = arm_debug_excp_handler; cc->debug_check_watchpoint = arm_debug_check_watchpoint; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 19a0c03f9b..e310ffc29d 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -133,6 +133,19 @@ enum { s<2n+1> maps to the most significant half of d */ +/** + * DynamicGDBXMLInfo: + * @desc: Contains the XML descriptions. + * @num_cpregs: Number of the Coprocessor registers seen by GDB. + * @cpregs_keys: Array that contains the corresponding Key of + * a given cpreg with the same order of the cpreg in the XML description. + */ +typedef struct DynamicGDBXMLInfo { + char *desc; + int num_cpregs; + uint32_t *cpregs_keys; +} DynamicGDBXMLInfo; + /* CPU state for each instance of a generic timer (in cp15 c14) */ typedef struct ARMGenericTimer { uint64_t cval; /* Timer CompareValue register */ @@ -367,8 +380,8 @@ typedef struct CPUARMState { uint32_t c9_data; uint64_t c9_pmcr; /* performance monitor control register */ uint64_t c9_pmcnten; /* perf monitor counter enables */ - uint32_t c9_pmovsr; /* perf monitor overflow status */ - uint32_t c9_pmuserenr; /* perf monitor user enable */ + uint64_t c9_pmovsr; /* perf monitor overflow status */ + uint64_t c9_pmuserenr; /* perf monitor user enable */ uint64_t c9_pmselr; /* perf monitor counter selection register */ uint64_t c9_pminten; /* perf monitor interrupt enables */ union { /* Memory attribute redirection */ @@ -527,7 +540,10 @@ typedef struct CPUARMState { #ifdef TARGET_AARCH64 /* Store FFR as pregs[16] to make it easier to treat as any other. */ +#define FFR_PRED_NUM 16 ARMPredicateReg pregs[17]; + /* Scratch space for aa64 sve predicate temporary. */ + ARMPredicateReg preg_tmp; #endif uint32_t xregs[16]; @@ -535,7 +551,7 @@ typedef struct CPUARMState { int vec_len; int vec_stride; - /* scratch space when Tn are not sufficient. */ + /* Scratch space for aa32 neon expansion. */ uint32_t scratch[8]; /* There are a number of distinct float control structures: @@ -632,12 +648,17 @@ typedef struct CPUARMState { } CPUARMState; /** - * ARMELChangeHook: + * ARMELChangeHookFn: * type of a function which can be registered via arm_register_el_change_hook() * to get callbacks when the CPU changes its exception level or mode. */ -typedef void ARMELChangeHook(ARMCPU *cpu, void *opaque); - +typedef void ARMELChangeHookFn(ARMCPU *cpu, void *opaque); +typedef struct ARMELChangeHook ARMELChangeHook; +struct ARMELChangeHook { + ARMELChangeHookFn *hook; + void *opaque; + QLIST_ENTRY(ARMELChangeHook) node; +}; /* These values map onto the return values for * QEMU_PSCI_0_2_FN_AFFINITY_INFO */ @@ -682,6 +703,8 @@ struct ARMCPU { uint64_t *cpreg_vmstate_values; int32_t cpreg_vmstate_array_len; + DynamicGDBXMLInfo dyn_xml; + /* Timers used by the generic (architected) timer */ QEMUTimer *gt_timer[NUM_GTIMERS]; /* GPIO outputs for generic timer */ @@ -790,6 +813,7 @@ struct ARMCPU { uint32_t id_isar3; uint32_t id_isar4; uint32_t id_isar5; + uint32_t id_isar6; uint64_t id_aa64pfr0; uint64_t id_aa64pfr1; uint64_t id_aa64dfr0; @@ -826,8 +850,8 @@ struct ARMCPU { */ bool cfgend; - ARMELChangeHook *el_change_hook; - void *el_change_hook_opaque; + QLIST_HEAD(, ARMELChangeHook) pre_el_change_hooks; + QLIST_HEAD(, ARMELChangeHook) el_change_hooks; int32_t node_id; /* NUMA node this CPU belongs to */ @@ -863,6 +887,17 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +/* Dynamically generates for gdb stub an XML description of the sysregs from + * the cp_regs hashtable. Returns the registered sysregs number. + */ +int arm_gen_dynamic_xml(CPUState *cpu); + +/* Returns the dynamically generated XML for the gdb stub. + * Returns a pointer to the XML contents for the specified XML file or NULL + * if the XML name doesn't match the predefined one. + */ +const char *arm_gdb_get_dynamic_xml(CPUState *cpu, const char *xmlname); + int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, @@ -1408,6 +1443,7 @@ enum arm_features { ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */ ARM_FEATURE_THUMB2EE, ARM_FEATURE_V7MP, /* v7 Multiprocessing Extensions */ + ARM_FEATURE_V7VE, /* v7 Virtualization Extensions (non-EL2 parts) */ ARM_FEATURE_V4T, ARM_FEATURE_V5, ARM_FEATURE_STRONGARM, @@ -1444,9 +1480,12 @@ enum arm_features { ARM_FEATURE_V8_SHA3, /* implements SHA3 part of v8 Crypto Extensions */ ARM_FEATURE_V8_SM3, /* implements SM3 part of v8 Crypto Extensions */ ARM_FEATURE_V8_SM4, /* implements SM4 part of v8 Crypto Extensions */ + ARM_FEATURE_V8_ATOMICS, /* ARMv8.1-Atomics feature */ ARM_FEATURE_V8_RDM, /* implements v8.1 simd round multiply */ + ARM_FEATURE_V8_DOTPROD, /* implements v8.2 simd dot product */ ARM_FEATURE_V8_FP16, /* implements v8.2 half-precision float */ ARM_FEATURE_V8_FCMA, /* has complex number part of v8.3 extensions. */ + ARM_FEATURE_M_MAIN, /* M profile Main Extension */ }; static inline int arm_feature(CPUARMState *env, int feature) @@ -1815,10 +1854,11 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid) #define ARM_LAST_SPECIAL ARM_CP_DC_ZVA #define ARM_CP_FPU 0x1000 #define ARM_CP_SVE 0x2000 +#define ARM_CP_NO_GDB 0x4000 /* Used only as a terminator for ARMCPRegInfo lists */ #define ARM_CP_SENTINEL 0xffff /* Mask of only the flag bits in a type field */ -#define ARM_CP_FLAG_MASK 0x30ff +#define ARM_CP_FLAG_MASK 0x70ff /* Valid values for ARMCPRegInfo state field, indicating which of * the AArch32 and AArch64 execution states this register is visible in. @@ -2889,28 +2929,29 @@ static inline AddressSpace *arm_addressspace(CPUState *cs, MemTxAttrs attrs) #endif /** - * arm_register_el_change_hook: - * Register a hook function which will be called back whenever this + * arm_register_pre_el_change_hook: + * Register a hook function which will be called immediately before this * CPU changes exception level or mode. The hook function will be * passed a pointer to the ARMCPU and the opaque data pointer passed * to this function when the hook was registered. * - * Note that we currently only support registering a single hook function, - * and will assert if this function is called twice. - * This facility is intended for the use of the GICv3 emulation. + * Note that if a pre-change hook is called, any registered post-change hooks + * are guaranteed to subsequently be called. */ -void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHook *hook, +void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void *opaque); - /** - * arm_get_el_change_hook_opaque: - * Return the opaque data that will be used by the el_change_hook - * for this CPU. + * arm_register_el_change_hook: + * Register a hook function which will be called immediately after this + * CPU changes exception level or mode. The hook function will be + * passed a pointer to the ARMCPU and the opaque data pointer passed + * to this function when the hook was registered. + * + * Note that any registered hooks registered here are guaranteed to be called + * if pre-change hooks have been. */ -static inline void *arm_get_el_change_hook_opaque(ARMCPU *cpu) -{ - return cpu->el_change_hook_opaque; -} +void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void + *opaque); /** * aa32_vfp_dreg: @@ -2939,4 +2980,7 @@ static inline uint64_t *aa64_vfp_qreg(CPUARMState *env, unsigned regno) return &env->vfp.zregs[regno].d[0]; } +/* Shared between translate-sve.c and sve_helper.c. */ +extern const uint64_t pred_esz_masks[4]; + #endif diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 991d764674..d0581d59d8 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -139,6 +139,7 @@ static void aarch64_a57_initfn(Object *obj) cpu->id_isar3 = 0x01112131; cpu->id_isar4 = 0x00011142; cpu->id_isar5 = 0x00011121; + cpu->id_isar6 = 0; cpu->id_aa64pfr0 = 0x00002222; cpu->id_aa64dfr0 = 0x10305106; cpu->pmceid0 = 0x00000000; @@ -199,6 +200,7 @@ static void aarch64_a53_initfn(Object *obj) cpu->id_isar3 = 0x01112131; cpu->id_isar4 = 0x00011142; cpu->id_isar5 = 0x00011121; + cpu->id_isar6 = 0; cpu->id_aa64pfr0 = 0x00002222; cpu->id_aa64dfr0 = 0x10305106; cpu->id_aa64isar0 = 0x00011120; @@ -235,22 +237,16 @@ static void aarch64_max_initfn(Object *obj) * whereas the architecture requires them to be present in both if * present in either. */ - set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_VFP4); - set_feature(&cpu->env, ARM_FEATURE_NEON); - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - set_feature(&cpu->env, ARM_FEATURE_V8_AES); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA256); set_feature(&cpu->env, ARM_FEATURE_V8_SHA512); set_feature(&cpu->env, ARM_FEATURE_V8_SHA3); set_feature(&cpu->env, ARM_FEATURE_V8_SM3); set_feature(&cpu->env, ARM_FEATURE_V8_SM4); - set_feature(&cpu->env, ARM_FEATURE_V8_PMULL); - set_feature(&cpu->env, ARM_FEATURE_CRC); + set_feature(&cpu->env, ARM_FEATURE_V8_ATOMICS); set_feature(&cpu->env, ARM_FEATURE_V8_RDM); + set_feature(&cpu->env, ARM_FEATURE_V8_DOTPROD); set_feature(&cpu->env, ARM_FEATURE_V8_FP16); set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); + set_feature(&cpu->env, ARM_FEATURE_SVE); /* For usermode -cpu max we can use a larger and more efficient DCZ * blocksize since we don't have to follow what the hardware does. */ diff --git a/target/arm/crypto_helper.c b/target/arm/crypto_helper.c index cc339ea7e0..f800266727 100644 --- a/target/arm/crypto_helper.c +++ b/target/arm/crypto_helper.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "crypto/aes.h" diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 04c1208d03..0c64c0292e 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -22,6 +22,11 @@ #include "cpu.h" #include "exec/gdbstub.h" +typedef struct RegisterSysregXmlParam { + CPUState *cs; + GString *s; +} RegisterSysregXmlParam; + /* Old gdb always expect FPA registers. Newer (xml-aware) gdb only expect whatever the target description contains. Due to a historical mishap the FPA registers appear in between core integer regs and the CPSR. @@ -101,3 +106,73 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) /* Unknown register. */ return 0; } + +static void arm_gen_one_xml_reg_tag(GString *s, DynamicGDBXMLInfo *dyn_xml, + ARMCPRegInfo *ri, uint32_t ri_key, + int bitsize) +{ + g_string_append_printf(s, "name); + g_string_append_printf(s, " bitsize=\"%d\"", bitsize); + g_string_append_printf(s, " group=\"cp_regs\"/>"); + dyn_xml->num_cpregs++; + dyn_xml->cpregs_keys[dyn_xml->num_cpregs - 1] = ri_key; +} + +static void arm_register_sysreg_for_xml(gpointer key, gpointer value, + gpointer p) +{ + uint32_t ri_key = *(uint32_t *)key; + ARMCPRegInfo *ri = value; + RegisterSysregXmlParam *param = (RegisterSysregXmlParam *)p; + GString *s = param->s; + ARMCPU *cpu = ARM_CPU(param->cs); + CPUARMState *env = &cpu->env; + DynamicGDBXMLInfo *dyn_xml = &cpu->dyn_xml; + + if (!(ri->type & (ARM_CP_NO_RAW | ARM_CP_NO_GDB))) { + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + if (ri->state == ARM_CP_STATE_AA64) { + arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + } + } else { + if (ri->state == ARM_CP_STATE_AA32) { + if (!arm_feature(env, ARM_FEATURE_EL3) && + (ri->secure & ARM_CP_SECSTATE_S)) { + return; + } + if (ri->type & ARM_CP_64BIT) { + arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 64); + } else { + arm_gen_one_xml_reg_tag(s , dyn_xml, ri, ri_key, 32); + } + } + } + } +} + +int arm_gen_dynamic_xml(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + GString *s = g_string_new(NULL); + RegisterSysregXmlParam param = {cs, s}; + + cpu->dyn_xml.num_cpregs = 0; + cpu->dyn_xml.cpregs_keys = g_new(uint32_t, g_hash_table_size(cpu->cp_regs)); + g_string_printf(s, ""); + g_string_append_printf(s, ""); + g_string_append_printf(s, ""); + g_hash_table_foreach(cpu->cp_regs, arm_register_sysreg_for_xml, ¶m); + g_string_append_printf(s, ""); + cpu->dyn_xml.desc = g_string_free(s, false); + return cpu->dyn_xml.num_cpregs; +} + +const char *arm_gdb_get_dynamic_xml(CPUState *cs, const char *xmlname) +{ + ARMCPU *cpu = ARM_CPU(cs); + + if (strcmp(xmlname, "system-registers.xml") == 0) { + return cpu->dyn_xml.desc; + } + return NULL; +} diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index afb25ad20c..7f6ad3000b 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -85,6 +85,16 @@ static inline uint32_t float_rel_to_flags(int res) return flags; } +uint64_t HELPER(vfp_cmph_a64)(uint32_t x, uint32_t y, void *fp_status) +{ + return float_rel_to_flags(float16_compare_quiet(x, y, fp_status)); +} + +uint64_t HELPER(vfp_cmpeh_a64)(uint32_t x, uint32_t y, void *fp_status) +{ + return float_rel_to_flags(float16_compare(x, y, fp_status)); +} + uint64_t HELPER(vfp_cmps_a64)(float32 x, float32 y, void *fp_status) { return float_rel_to_flags(float32_compare_quiet(x, y, fp_status)); @@ -204,7 +214,7 @@ uint64_t HELPER(neon_cgt_f64)(float64 a, float64 b, void *fpstp) #define float64_three make_float64(0x4008000000000000ULL) #define float64_one_point_five make_float64(0x3FF8000000000000ULL) -float16 HELPER(recpsf_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(recpsf_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; @@ -249,7 +259,7 @@ float64 HELPER(recpsf_f64)(float64 a, float64 b, void *fpstp) return float64_muladd(a, b, float64_two, 0, fpst); } -float16 HELPER(rsqrtsf_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(rsqrtsf_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; @@ -356,7 +366,7 @@ uint64_t HELPER(neon_addlp_u16)(uint64_t a) } /* Floating-point reciprocal exponent - see FPRecpX in ARM ARM */ -float16 HELPER(frecpx_f16)(float16 a, void *fpstp) +uint32_t HELPER(frecpx_f16)(uint32_t a, void *fpstp) { float_status *fpst = fpstp; uint16_t val16, sbit; @@ -366,7 +376,7 @@ float16 HELPER(frecpx_f16)(float16 a, void *fpstp) float16 nan = a; if (float16_is_signaling_nan(a, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float16_maybe_silence_nan(a, fpst); + nan = float16_silence_nan(a, fpst); } if (fpst->default_nan_mode) { nan = float16_default_nan(fpst); @@ -374,6 +384,8 @@ float16 HELPER(frecpx_f16)(float16 a, void *fpstp) return nan; } + a = float16_squash_input_denormal(a, fpst); + val16 = float16_val(a); sbit = 0x8000 & val16; exp = extract32(val16, 10, 5); @@ -395,7 +407,7 @@ float32 HELPER(frecpx_f32)(float32 a, void *fpstp) float32 nan = a; if (float32_is_signaling_nan(a, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float32_maybe_silence_nan(a, fpst); + nan = float32_silence_nan(a, fpst); } if (fpst->default_nan_mode) { nan = float32_default_nan(fpst); @@ -403,6 +415,8 @@ float32 HELPER(frecpx_f32)(float32 a, void *fpstp) return nan; } + a = float32_squash_input_denormal(a, fpst); + val32 = float32_val(a); sbit = 0x80000000ULL & val32; exp = extract32(val32, 23, 8); @@ -424,7 +438,7 @@ float64 HELPER(frecpx_f64)(float64 a, void *fpstp) float64 nan = a; if (float64_is_signaling_nan(a, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float64_maybe_silence_nan(a, fpst); + nan = float64_silence_nan(a, fpst); } if (fpst->default_nan_mode) { nan = float64_default_nan(fpst); @@ -432,6 +446,8 @@ float64 HELPER(frecpx_f64)(float64 a, void *fpstp) return nan; } + a = float64_squash_input_denormal(a, fpst); + val64 = float64_val(a); sbit = 0x8000000000000000ULL & val64; exp = extract64(float64_val(a), 52, 11); @@ -456,7 +472,6 @@ float32 HELPER(fcvtx_f64_to_f32)(float64 a, CPUARMState *env) set_float_rounding_mode(float_round_to_zero, &tstat); set_float_exception_flags(0, &tstat); r = float64_to_float32(a, &tstat); - r = float32_maybe_silence_nan(r, &tstat); exflags = get_float_exception_flags(&tstat); if (exflags & float_flag_inexact) { r = make_float32(float32_val(r) | 1); @@ -636,6 +651,49 @@ uint64_t HELPER(paired_cmpxchg64_be_parallel)(CPUARMState *env, uint64_t addr, return do_paired_cmpxchg64_be(env, addr, new_lo, new_hi, true, GETPC()); } +/* Writes back the old data into Rs. */ +void HELPER(casp_le_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, + uint64_t new_lo, uint64_t new_hi) +{ + uintptr_t ra = GETPC(); +#ifndef CONFIG_ATOMIC128 + cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); +#else + Int128 oldv, cmpv, newv; + + cmpv = int128_make128(env->xregs[rs], env->xregs[rs + 1]); + newv = int128_make128(new_lo, new_hi); + + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra); + + env->xregs[rs] = int128_getlo(oldv); + env->xregs[rs + 1] = int128_gethi(oldv); +#endif +} + +void HELPER(casp_be_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, + uint64_t new_hi, uint64_t new_lo) +{ + uintptr_t ra = GETPC(); +#ifndef CONFIG_ATOMIC128 + cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); +#else + Int128 oldv, cmpv, newv; + + cmpv = int128_make128(env->xregs[rs + 1], env->xregs[rs]); + newv = int128_make128(new_lo, new_hi); + + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); + + env->xregs[rs + 1] = int128_getlo(oldv); + env->xregs[rs] = int128_gethi(oldv); +#endif +} + /* * AdvSIMD half-precision */ @@ -643,7 +701,7 @@ uint64_t HELPER(paired_cmpxchg64_be_parallel)(CPUARMState *env, uint64_t addr, #define ADVSIMD_HELPER(name, suffix) HELPER(glue(glue(advsimd_, name), suffix)) #define ADVSIMD_HALFOP(name) \ -float16 ADVSIMD_HELPER(name, h)(float16 a, float16 b, void *fpstp) \ +uint32_t ADVSIMD_HELPER(name, h)(uint32_t a, uint32_t b, void *fpstp) \ { \ float_status *fpst = fpstp; \ return float16_ ## name(a, b, fpst); \ @@ -703,7 +761,8 @@ ADVSIMD_HALFOP(mulx) ADVSIMD_TWOHALFOP(mulx) /* fused multiply-accumulate */ -float16 HELPER(advsimd_muladdh)(float16 a, float16 b, float16 c, void *fpstp) +uint32_t HELPER(advsimd_muladdh)(uint32_t a, uint32_t b, uint32_t c, + void *fpstp) { float_status *fpst = fpstp; return float16_muladd(a, b, c, 0, fpst); @@ -734,14 +793,14 @@ uint32_t HELPER(advsimd_muladd2h)(uint32_t two_a, uint32_t two_b, #define ADVSIMD_CMPRES(test) (test) ? 0xffff : 0 -uint32_t HELPER(advsimd_ceq_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_ceq_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; int compare = float16_compare_quiet(a, b, fpst); return ADVSIMD_CMPRES(compare == float_relation_equal); } -uint32_t HELPER(advsimd_cge_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_cge_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; int compare = float16_compare(a, b, fpst); @@ -749,14 +808,14 @@ uint32_t HELPER(advsimd_cge_f16)(float16 a, float16 b, void *fpstp) compare == float_relation_equal); } -uint32_t HELPER(advsimd_cgt_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_cgt_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; int compare = float16_compare(a, b, fpst); return ADVSIMD_CMPRES(compare == float_relation_greater); } -uint32_t HELPER(advsimd_acge_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_acge_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; float16 f0 = float16_abs(a); @@ -766,7 +825,7 @@ uint32_t HELPER(advsimd_acge_f16)(float16 a, float16 b, void *fpstp) compare == float_relation_equal); } -uint32_t HELPER(advsimd_acgt_f16)(float16 a, float16 b, void *fpstp) +uint32_t HELPER(advsimd_acgt_f16)(uint32_t a, uint32_t b, void *fpstp) { float_status *fpst = fpstp; float16 f0 = float16_abs(a); @@ -776,12 +835,12 @@ uint32_t HELPER(advsimd_acgt_f16)(float16 a, float16 b, void *fpstp) } /* round to integral */ -float16 HELPER(advsimd_rinth_exact)(float16 x, void *fp_status) +uint32_t HELPER(advsimd_rinth_exact)(uint32_t x, void *fp_status) { return float16_round_to_int(x, fp_status); } -float16 HELPER(advsimd_rinth)(float16 x, void *fp_status) +uint32_t HELPER(advsimd_rinth)(uint32_t x, void *fp_status) { int old_flags = get_float_exception_flags(fp_status), new_flags; float16 ret; @@ -805,7 +864,7 @@ float16 HELPER(advsimd_rinth)(float16 x, void *fp_status) * setting the mode appropriately before calling the helper. */ -uint32_t HELPER(advsimd_f16tosinth)(float16 a, void *fpstp) +uint32_t HELPER(advsimd_f16tosinth)(uint32_t a, void *fpstp) { float_status *fpst = fpstp; @@ -817,7 +876,7 @@ uint32_t HELPER(advsimd_f16tosinth)(float16 a, void *fpstp) return float16_to_int16(a, fpst); } -uint32_t HELPER(advsimd_f16touinth)(float16 a, void *fpstp) +uint32_t HELPER(advsimd_f16touinth)(uint32_t a, void *fpstp) { float_status *fpst = fpstp; @@ -833,7 +892,7 @@ uint32_t HELPER(advsimd_f16touinth)(float16 a, void *fpstp) * Square Root and Reciprocal square root */ -float16 HELPER(sqrt_f16)(float16 a, void *fpstp) +uint32_t HELPER(sqrt_f16)(uint32_t a, void *fpstp) { float_status *s = fpstp; diff --git a/target/arm/helper-a64.h b/target/arm/helper-a64.h index ef4ddfe9d8..9d3a907049 100644 --- a/target/arm/helper-a64.h +++ b/target/arm/helper-a64.h @@ -19,6 +19,8 @@ DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64) DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, ptr) +DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr) DEF_HELPER_3(vfp_cmpes_a64, i64, f32, f32, ptr) DEF_HELPER_3(vfp_cmpd_a64, i64, f64, f64, ptr) @@ -51,6 +53,8 @@ DEF_HELPER_FLAGS_4(paired_cmpxchg64_le_parallel, TCG_CALL_NO_WG, DEF_HELPER_FLAGS_4(paired_cmpxchg64_be, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_4(paired_cmpxchg64_be_parallel, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_5(casp_le_parallel, void, env, i32, i64, i64, i64) +DEF_HELPER_5(casp_be_parallel, void, env, i32, i64, i64, i64) DEF_HELPER_FLAGS_3(advsimd_maxh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(advsimd_minh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) DEF_HELPER_FLAGS_3(advsimd_maxnumh, TCG_CALL_NO_RWG, f16, f16, f16, ptr) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h new file mode 100644 index 0000000000..023952a9a4 --- /dev/null +++ b/target/arm/helper-sve.h @@ -0,0 +1,1403 @@ +/* + * AArch64 SVE specific helper definitions + * + * Copyright (c) 2018 Linaro, Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +DEF_HELPER_FLAGS_2(sve_predtest1, TCG_CALL_NO_WG, i32, i64, i64) +DEF_HELPER_FLAGS_3(sve_predtest, TCG_CALL_NO_WG, i32, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_pfirst, TCG_CALL_NO_WG, i32, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_pnext, TCG_CALL_NO_WG, i32, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_and_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_and_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_and_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_and_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_eor_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_eor_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_eor_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_eor_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_orr_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orr_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orr_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orr_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_bic_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_bic_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_bic_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_bic_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_add_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_add_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_add_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_add_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_sub_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sub_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sub_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sub_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_smax_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smax_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smax_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smax_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_umax_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umax_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umax_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umax_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_smin_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smin_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smin_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smin_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_umin_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umin_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umin_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umin_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_sabd_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sabd_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sabd_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sabd_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_uabd_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_uabd_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_uabd_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_uabd_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_mul_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_mul_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_mul_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_mul_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_smulh_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smulh_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smulh_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_smulh_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_umulh_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umulh_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umulh_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_umulh_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_sdiv_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sdiv_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_udiv_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_udiv_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_asr_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_lsr_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_lsl_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_sel_zpzz_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_zpzz_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_zpzz_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_zpzz_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_asr_zpzw_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzw_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_asr_zpzw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_lsr_zpzw_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzw_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsr_zpzw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_lsl_zpzw_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzw_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_lsl_zpzw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_orv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_orv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_orv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_orv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_eorv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_eorv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_eorv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_eorv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_andv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_andv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_andv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_andv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_saddv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_saddv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_saddv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_uaddv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uaddv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uaddv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uaddv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_smaxv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_smaxv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_smaxv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_smaxv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_umaxv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_umaxv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_umaxv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_umaxv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_sminv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sminv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sminv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sminv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_uminv_b, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uminv_h, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uminv_s, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uminv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_clr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_clr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_clr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_clr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_movz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_movz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_movz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_movz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_asr_zpzi_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zpzi_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zpzi_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zpzi_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_lsr_zpzi_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zpzi_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zpzi_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zpzi_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_lsl_zpzi_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zpzi_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zpzi_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zpzi_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_asrd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asrd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asrd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asrd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cls_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cls_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cls_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_clz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_clz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_clz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_clz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cnt_zpz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnt_zpz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnt_zpz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnt_zpz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cnot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnot_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cnot_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fabs_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fabs_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fabs_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fneg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fneg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fneg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_not_zpz_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_not_zpz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_not_zpz_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_not_zpz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_sxtb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_sxtb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_sxtb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_uxtb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uxtb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uxtb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_sxth_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_sxth_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_uxth_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uxth_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_sxtw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uxtw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_abs_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_abs_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_abs_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_abs_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_neg_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_neg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_neg_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_neg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_mla_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mla_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mla_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mla_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_mls_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mls_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mls_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_mls_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_index_b, TCG_CALL_NO_RWG, void, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_4(sve_index_h, TCG_CALL_NO_RWG, void, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_4(sve_index_s, TCG_CALL_NO_RWG, void, ptr, i32, i32, i32) +DEF_HELPER_FLAGS_4(sve_index_d, TCG_CALL_NO_RWG, void, ptr, i64, i64, i32) + +DEF_HELPER_FLAGS_4(sve_asr_zzw_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zzw_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_asr_zzw_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_lsr_zzw_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zzw_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsr_zzw_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_lsl_zzw_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zzw_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_lsl_zzw_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_adr_p32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_adr_p64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_adr_s32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_adr_u32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_fexpa_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fexpa_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fexpa_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_ftssel_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ftssel_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_ftssel_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_sqaddi_b, TCG_CALL_NO_RWG, void, ptr, ptr, s32, i32) +DEF_HELPER_FLAGS_4(sve_sqaddi_h, TCG_CALL_NO_RWG, void, ptr, ptr, s32, i32) +DEF_HELPER_FLAGS_4(sve_sqaddi_s, TCG_CALL_NO_RWG, void, ptr, ptr, s64, i32) +DEF_HELPER_FLAGS_4(sve_sqaddi_d, TCG_CALL_NO_RWG, void, ptr, ptr, s64, i32) + +DEF_HELPER_FLAGS_4(sve_uqaddi_b, TCG_CALL_NO_RWG, void, ptr, ptr, s32, i32) +DEF_HELPER_FLAGS_4(sve_uqaddi_h, TCG_CALL_NO_RWG, void, ptr, ptr, s32, i32) +DEF_HELPER_FLAGS_4(sve_uqaddi_s, TCG_CALL_NO_RWG, void, ptr, ptr, s64, i32) +DEF_HELPER_FLAGS_4(sve_uqaddi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_uqsubi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_5(sve_cpy_m_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_5(sve_cpy_m_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_5(sve_cpy_m_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_5(sve_cpy_m_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_cpy_z_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_cpy_z_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_cpy_z_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_cpy_z_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_ext, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_insr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_insr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_insr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_insr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_3(sve_rev_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_rev_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_rev_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_rev_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_tbl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_tbl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_tbl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_tbl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_sunpk_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sunpk_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_sunpk_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_uunpk_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uunpk_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_uunpk_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_zip_p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uzp_p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_trn_p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_rev_p, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sve_punpk_p, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_zip_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_zip_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_zip_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_zip_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_uzp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uzp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uzp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_uzp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_trn_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_trn_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_trn_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_trn_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_compact_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_compact_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_2(sve_last_active_element, TCG_CALL_NO_RWG, s32, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_revb_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_revb_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_revb_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_revh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_revh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_revw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_rbit_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_rbit_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_rbit_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_rbit_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_splice, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzz_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzz_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzz_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzz_d, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmple_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplt_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplo_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpls_ppzw_b, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmple_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplt_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplo_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpls_ppzw_h, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_cmpeq_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpne_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpge_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpgt_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphi_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmphs_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmple_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplt_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmplo_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_cmpls_ppzw_s, TCG_CALL_NO_RWG, + i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cmpeq_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpne_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpgt_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpge_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplt_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmple_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphs_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphi_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplo_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpls_ppzi_b, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cmpeq_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpne_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpgt_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpge_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplt_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmple_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphs_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphi_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplo_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpls_ppzi_h, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cmpeq_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpne_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpgt_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpge_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplt_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmple_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphs_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphi_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplo_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpls_ppzi_s, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_cmpeq_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpne_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpgt_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpge_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplt_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmple_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphs_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmphi_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmplo_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_cmpls_ppzi_d, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_and_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_bic_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_eor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_sel_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orr_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_orn_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_nor_pppp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_nand_pppp, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_brkpa, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_brkpb, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_brkpas, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_brkpbs, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_brka_z, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkb_z, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brka_m, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkb_m, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_brkas_z, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkbs_z, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkas_m, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkbs_m, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_brkn, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_brkns, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_cntp, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_while, TCG_CALL_NO_RWG, i32, ptr, i32, i32) + +DEF_HELPER_FLAGS_4(sve_subri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_subri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_subri_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_subri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_smaxi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smaxi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smaxi_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smaxi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_smini_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smini_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smini_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_smini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_umaxi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umaxi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umaxi_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umaxi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(sve_umini_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umini_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umini_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(sve_umini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_5(gvec_recps_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_recps_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_recps_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_rsqrts_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_rsqrts_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_rsqrts_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_faddv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_faddv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_faddv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fmaxnmv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fmaxnmv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fmaxnmv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fminnmv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fminnmv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fminnmv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fmaxv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fmaxv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fmaxv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_fminv_h, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fminv_s, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve_fminv_d, TCG_CALL_NO_RWG, + i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fadda_h, TCG_CALL_NO_RWG, + i64, i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fadda_s, TCG_CALL_NO_RWG, + i64, i64, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fadda_d, TCG_CALL_NO_RWG, + i64, i64, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmge0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmge0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmge0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmgt0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmgt0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmgt0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmlt0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmlt0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmlt0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmle0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmle0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmle0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmeq0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmeq0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmeq0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcmne0_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmne0_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcmne0_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fsub_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsub_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsub_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmul_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmul_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmul_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fdiv_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fdiv_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fdiv_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmin_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmin_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmin_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmax_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmax_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmax_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fminnum_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fminnum_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fminnum_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmaxnum_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnum_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnum_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fabd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fabd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fabd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fscalbn_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fscalbn_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fscalbn_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmulx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmulx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmulx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fadds_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadds_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fadds_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fsubs_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsubs_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsubs_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmuls_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmuls_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmuls_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fsubrs_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsubrs_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fsubrs_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmaxnms_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnms_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnms_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fminnms_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fminnms_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fminnms_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmaxs_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxs_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmaxs_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fmins_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmins_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fmins_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i64, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcvt_sh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_dh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_hs, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_hd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvt_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcvtzs_hh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_hs, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_ss, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_hd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzs_dd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fcvtzu_hh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_hs, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_ss, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_hd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fcvtzu_dd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_frint_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frint_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frint_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_frintx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frintx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frintx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_frecpx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frecpx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_frecpx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_fsqrt_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fsqrt_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_fsqrt_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_scvt_hh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_sh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_dh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_ss, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_scvt_dd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_ucvt_hh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_sh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_dh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_ss, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_sd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_ds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ucvt_dd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcmge_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmge_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmge_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcmgt_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmgt_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmgt_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcmeq_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmeq_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmeq_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcmne_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmne_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmne_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcmuo_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmuo_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcmuo_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_facge_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_facge_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_facge_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_facgt_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_facgt_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_facgt_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve_fcadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sve_fcadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fmla_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_fmls_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fmls_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fmls_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_fnmla_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fnmla_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fnmla_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fnmls_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_3(sve_fcmla_zpzzz_h, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fcmla_zpzzz_s, TCG_CALL_NO_RWG, void, env, ptr, i32) +DEF_HELPER_FLAGS_3(sve_fcmla_zpzzz_d, TCG_CALL_NO_RWG, void, env, ptr, i32) + +DEF_HELPER_FLAGS_5(sve_ftmad_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve_ftmad_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve_ld1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1bh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1bs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1bd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1hs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1hd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1sd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldbsu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhsu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldssu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldbsu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhsu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldssu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldbdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldddu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldbdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldddu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldbdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldddu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldbds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldffbsu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhsu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffssu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldffbsu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhsu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffssu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldffbdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsdu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffddu_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldffbdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsdu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffddu_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_ldffbdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsdu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffddu_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffbds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbs_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sths_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stss_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbs_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sths_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stss_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbd_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sthd_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stsd_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbd_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sthd_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stsd_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) + +DEF_HELPER_FLAGS_6(sve_stbd_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_sthd_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stsd_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) diff --git a/target/arm/helper.c b/target/arm/helper.c index b14fdab140..66afb08ee0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -17,6 +17,7 @@ #include "exec/semihost.h" #include "sysemu/kvm.h" #include "fpu/softfloat.h" +#include "qemu/range.h" #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ @@ -41,6 +42,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, /* Security attributes for an address, as returned by v8m_security_lookup. */ typedef struct V8M_SAttributes { + bool subpage; /* true if these attrs don't cover the whole TARGET_PAGE */ bool ns; bool nsc; uint8_t sregion; @@ -52,11 +54,6 @@ typedef struct V8M_SAttributes { static void v8m_security_lookup(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, V8M_SAttributes *sattrs); - -/* Definitions for the PMCCNTR and PMCR registers */ -#define PMCRD 0x8 -#define PMCRC 0x4 -#define PMCRE 0x1 #endif static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) @@ -220,6 +217,29 @@ static void write_raw_cp_reg(CPUARMState *env, const ARMCPRegInfo *ri, } } +static int arm_gdb_get_sysreg(CPUARMState *env, uint8_t *buf, int reg) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + const ARMCPRegInfo *ri; + uint32_t key; + + key = cpu->dyn_xml.cpregs_keys[reg]; + ri = get_arm_cp_reginfo(cpu->cp_regs, key); + if (ri) { + if (cpreg_field_is_64bit(ri)) { + return gdb_get_reg64(buf, (uint64_t)read_raw_cp_reg(env, ri)); + } else { + return gdb_get_reg32(buf, (uint32_t)read_raw_cp_reg(env, ri)); + } + } + return 0; +} + +static int arm_gdb_set_sysreg(CPUARMState *env, uint8_t *buf, int reg) +{ + return 0; +} + static bool raw_accessors_invalid(const ARMCPRegInfo *ri) { /* Return true if the regdef would cause an assertion if you called @@ -695,12 +715,12 @@ static const ARMCPRegInfo cp_reginfo[] = { * the secure register to be properly reset and migrated. There is also no * v8 EL1 version of the register so the non-secure instance stands alone. */ - { .name = "FCSEIDR(NS)", + { .name = "FCSEIDR", .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 0, .access = PL1_RW, .secure = ARM_CP_SECSTATE_NS, .fieldoffset = offsetof(CPUARMState, cp15.fcseidr_ns), .resetvalue = 0, .writefn = fcse_write, .raw_writefn = raw_write, }, - { .name = "FCSEIDR(S)", + { .name = "FCSEIDR_S", .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 0, .access = PL1_RW, .secure = ARM_CP_SECSTATE_S, .fieldoffset = offsetof(CPUARMState, cp15.fcseidr_s), @@ -716,7 +736,7 @@ static const ARMCPRegInfo cp_reginfo[] = { .access = PL1_RW, .secure = ARM_CP_SECSTATE_NS, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_el[1]), .resetvalue = 0, .writefn = contextidr_write, .raw_writefn = raw_write, }, - { .name = "CONTEXTIDR(S)", .state = ARM_CP_STATE_AA32, + { .name = "CONTEXTIDR_S", .state = ARM_CP_STATE_AA32, .cp = 15, .opc1 = 0, .crn = 13, .crm = 0, .opc2 = 1, .access = PL1_RW, .secure = ARM_CP_SECSTATE_S, .fieldoffset = offsetof(CPUARMState, cp15.contextidr_s), @@ -845,6 +865,14 @@ static void cpacr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.cpacr_el1 = value; } +static void cpacr_reset(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Call cpacr_write() so that we reset with the correct RAO bits set + * for our CPU features. + */ + cpacr_write(env, ri, 0); +} + static CPAccessResult cpacr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -902,10 +930,28 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { { .name = "CPACR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 2, .accessfn = cpacr_access, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.cpacr_el1), - .resetvalue = 0, .writefn = cpacr_write }, + .resetfn = cpacr_reset, .writefn = cpacr_write }, REGINFO_SENTINEL }; +/* Definitions for the PMU registers */ +#define PMCRN_MASK 0xf800 +#define PMCRN_SHIFT 11 +#define PMCRD 0x8 +#define PMCRC 0x4 +#define PMCRE 0x1 + +static inline uint32_t pmu_num_counters(CPUARMState *env) +{ + return (env->cp15.c9_pmcr & PMCRN_MASK) >> PMCRN_SHIFT; +} + +/* Bits allowed to be set/cleared for PMCNTEN* and PMINTEN* */ +static inline uint64_t pmu_counter_mask(CPUARMState *env) +{ + return (1 << 31) | ((1 << pmu_num_counters(env)) - 1); +} + static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -994,7 +1040,7 @@ static inline bool arm_ccnt_enabled(CPUARMState *env) { /* This does not support checking PMCCFILTR_EL0 register */ - if (!(env->cp15.c9_pmcr & PMCRE)) { + if (!(env->cp15.c9_pmcr & PMCRE) || !(env->cp15.c9_pmcnten & (1 << 31))) { return false; } @@ -1106,21 +1152,21 @@ static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { pmccntr_sync(env); - env->cp15.pmccfiltr_el0 = value & 0x7E000000; + env->cp15.pmccfiltr_el0 = value & 0xfc000000; pmccntr_sync(env); } static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - value &= (1 << 31); + value &= pmu_counter_mask(env); env->cp15.c9_pmcnten |= value; } static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - value &= (1 << 31); + value &= pmu_counter_mask(env); env->cp15.c9_pmcnten &= ~value; } @@ -1168,14 +1214,14 @@ static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { /* We have no event counters so only the C bit can be changed */ - value &= (1 << 31); + value &= pmu_counter_mask(env); env->cp15.c9_pminten |= value; } static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - value &= (1 << 31); + value &= pmu_counter_mask(env); env->cp15.c9_pminten &= ~value; } @@ -1292,7 +1338,8 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenclr_write }, { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, - .access = PL0_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), + .access = PL0_RW, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .accessfn = pmreg_access, .writefn = pmovsr_write, .raw_writefn = raw_write }, @@ -1318,7 +1365,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), .writefn = pmselr_write, .raw_writefn = raw_write, }, { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, - .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_IO, + .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, .readfn = pmccntr_read, .writefn = pmccntr_write32, .accessfn = pmreg_access_ccntr }, { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, @@ -1347,7 +1394,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .accessfn = pmreg_access_xevcntr }, { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, .access = PL0_R | PL1_RW, .accessfn = access_tpm, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmuserenr), .resetvalue = 0, .writefn = pmuserenr_write, .raw_writefn = raw_write }, { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64, @@ -1358,7 +1405,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .writefn = pmuserenr_write, .raw_writefn = raw_write }, { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .accessfn = access_tpm, - .type = ARM_CP_ALIAS, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pminten), .resetvalue = 0, .writefn = pmintenset_write, .raw_writefn = raw_write }, @@ -1967,7 +2014,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { cp15.c14_timer[GTIMER_PHYS].ctl), .writefn = gt_phys_ctl_write, .raw_writefn = raw_write, }, - { .name = "CNTP_CTL(S)", + { .name = "CNTP_CTL_S", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1, .secure = ARM_CP_SECSTATE_S, .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL1_RW | PL0_R, @@ -2006,7 +2053,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_ptimer_access, .readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write, }, - { .name = "CNTP_TVAL(S)", + { .name = "CNTP_TVAL_S", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0, .secure = ARM_CP_SECSTATE_S, .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL1_RW | PL0_R, @@ -2060,7 +2107,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { .accessfn = gt_ptimer_access, .writefn = gt_phys_cval_write, .raw_writefn = raw_write, }, - { .name = "CNTP_CVAL(S)", .cp = 15, .crm = 14, .opc1 = 2, + { .name = "CNTP_CVAL_S", .cp = 15, .crm = 14, .opc1 = 2, .secure = ARM_CP_SECSTATE_S, .access = PL1_RW | PL0_R, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, @@ -2121,11 +2168,32 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }; #else -/* In user-mode none of the generic timer registers are accessible, - * and their implementation depends on QEMU_CLOCK_VIRTUAL and qdev gpio outputs, - * so instead just don't register any of them. + +/* In user-mode most of the generic timer registers are inaccessible + * however modern kernels (4.12+) allow access to cntvct_el0 */ + +static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* Currently we have no support for QEMUTimer in linux-user so we + * can't call gt_get_countervalue(env), instead we directly + * call the lower level functions. + */ + return cpu_get_clock() / GTIMER_SCALE; +} + static const ARMCPRegInfo generic_timer_cp_reginfo[] = { + { .name = "CNTFRQ_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0, + .type = ARM_CP_CONST, .access = PL0_R /* no PL1_RW in linux-user */, + .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq), + .resetvalue = NANOSECONDS_PER_SECOND / GTIMER_SCALE, + }, + { .name = "CNTVCT_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 2, + .access = PL0_R, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .readfn = gt_virt_cnt_read, + }, REGINFO_SENTINEL }; @@ -4347,7 +4415,7 @@ static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri, static const ARMCPRegInfo zcr_el1_reginfo = { .name = "ZCR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 1, .crm = 2, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_SVE | ARM_CP_FPU, + .access = PL1_RW, .type = ARM_CP_SVE, .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[1]), .writefn = zcr_write, .raw_writefn = raw_write }; @@ -4355,7 +4423,7 @@ static const ARMCPRegInfo zcr_el1_reginfo = { static const ARMCPRegInfo zcr_el2_reginfo = { .name = "ZCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 0, - .access = PL2_RW, .type = ARM_CP_SVE | ARM_CP_FPU, + .access = PL2_RW, .type = ARM_CP_SVE, .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[2]), .writefn = zcr_write, .raw_writefn = raw_write }; @@ -4363,14 +4431,14 @@ static const ARMCPRegInfo zcr_el2_reginfo = { static const ARMCPRegInfo zcr_no_el2_reginfo = { .name = "ZCR_EL2", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 2, .opc2 = 0, - .access = PL2_RW, .type = ARM_CP_SVE | ARM_CP_FPU, + .access = PL2_RW, .type = ARM_CP_SVE, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }; static const ARMCPRegInfo zcr_el3_reginfo = { .name = "ZCR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 1, .crm = 2, .opc2 = 0, - .access = PL3_RW, .type = ARM_CP_SVE | ARM_CP_FPU, + .access = PL3_RW, .type = ARM_CP_SVE, .fieldoffset = offsetof(CPUARMState, vfp.zcr_el[3]), .writefn = zcr_write, .raw_writefn = raw_write }; @@ -4525,7 +4593,7 @@ void hw_breakpoint_update(ARMCPU *cpu, int n) case 4: /* unlinked address mismatch (reserved if AArch64) */ case 5: /* linked address mismatch (reserved if AArch64) */ qemu_log_mask(LOG_UNIMP, - "arm: address mismatch breakpoint types not implemented"); + "arm: address mismatch breakpoint types not implemented\n"); return; case 0: /* unlinked address match */ case 1: /* linked address match */ @@ -4559,7 +4627,7 @@ void hw_breakpoint_update(ARMCPU *cpu, int n) case 8: /* unlinked VMID match (reserved if no EL2) */ case 10: /* unlinked context ID and VMID match (reserved if no EL2) */ qemu_log_mask(LOG_UNIMP, - "arm: unlinked context breakpoint types not implemented"); + "arm: unlinked context breakpoint types not implemented\n"); return; case 9: /* linked VMID match (reserved if no EL2) */ case 11: /* linked context ID and VMID match (reserved if no EL2) */ @@ -4805,11 +4873,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->id_mmfr4 }, - /* 7 is as yet unallocated and must RAZ */ - { .name = "ID_ISAR7_RESERVED", .state = ARM_CP_STATE_BOTH, + { .name = "ID_ISAR6", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = 0 }, + .resetvalue = cpu->id_isar6 }, REGINFO_SENTINEL }; define_arm_cp_regs(cpu, v6_idregs); @@ -5333,7 +5400,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) for (r = id_cp_reginfo; r->type != ARM_CP_SENTINEL; r++) { r->access = PL1_RW; } - id_tlbtr_reginfo.access = PL1_RW; + id_mpuir_reginfo.access = PL1_RW; id_tlbtr_reginfo.access = PL1_RW; } if (arm_feature(env, ARM_FEATURE_V8)) { @@ -5474,6 +5541,9 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) gdb_register_coprocessor(cs, vfp_gdb_get_reg, vfp_gdb_set_reg, 19, "arm-vfp.xml", 0); } + gdb_register_coprocessor(cs, arm_gdb_get_sysreg, arm_gdb_set_sysreg, + arm_gen_dynamic_xml(cs), + "system-registers.xml", 0); } /* Sort alphabetically by type name, except for "any". */ @@ -5521,12 +5591,6 @@ void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf) (*cpu_fprintf)(f, "Available CPUs:\n"); g_slist_foreach(list, arm_cpu_list_entry, &s); g_slist_free(list); -#ifdef CONFIG_KVM - /* The 'host' CPU type is dynamically registered only if KVM is - * enabled, so we have to special-case it here: - */ - (*cpu_fprintf)(f, " host (only available in KVM mode)\n"); -#endif } static void arm_cpu_add_definition(gpointer data, gpointer user_data) @@ -5563,7 +5627,8 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, void *opaque, int state, int secstate, - int crm, int opc1, int opc2) + int crm, int opc1, int opc2, + const char *name) { /* Private utility function for define_one_arm_cp_reg_with_opaque(): * add a single reginfo struct to the hash table. @@ -5573,6 +5638,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; int ns = (secstate & ARM_CP_SECSTATE_NS) ? 1 : 0; + r2->name = g_strdup(name); /* Reset the secure state to the specific incoming state. This is * necessary as the register may have been defined with both states. */ @@ -5664,7 +5730,7 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, if (((r->crm == CP_ANY) && crm != 0) || ((r->opc1 == CP_ANY) && opc1 != 0) || ((r->opc2 == CP_ANY) && opc2 != 0)) { - r2->type |= ARM_CP_ALIAS; + r2->type |= ARM_CP_ALIAS | ARM_CP_NO_GDB; } /* Check that raw accesses are either forbidden or handled. Note that @@ -5804,19 +5870,24 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, /* Under AArch32 CP registers can be common * (same for secure and non-secure world) or banked. */ + char *name; + switch (r->secure) { case ARM_CP_SECSTATE_S: case ARM_CP_SECSTATE_NS: add_cpreg_to_hashtable(cpu, r, opaque, state, - r->secure, crm, opc1, opc2); + r->secure, crm, opc1, opc2, + r->name); break; default: + name = g_strdup_printf("%s_S", r->name); add_cpreg_to_hashtable(cpu, r, opaque, state, ARM_CP_SECSTATE_S, - crm, opc1, opc2); + crm, opc1, opc2, name); + g_free(name); add_cpreg_to_hashtable(cpu, r, opaque, state, ARM_CP_SECSTATE_NS, - crm, opc1, opc2); + crm, opc1, opc2, r->name); break; } } else { @@ -5824,7 +5895,7 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, * of AArch32 */ add_cpreg_to_hashtable(cpu, r, opaque, state, ARM_CP_SECSTATE_NS, - crm, opc1, opc2); + crm, opc1, opc2, r->name); } } } @@ -6913,7 +6984,6 @@ static bool v7m_push_stack(ARMCPU *cpu) static void do_v7m_exception_exit(ARMCPU *cpu) { CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); uint32_t excret; uint32_t xpsr; bool ufault = false; @@ -7096,9 +7166,11 @@ static void do_v7m_exception_exit(ARMCPU *cpu) uint32_t frameptr = *frame_sp_p; bool pop_ok = true; ARMMMUIdx mmu_idx; + bool return_to_priv = return_to_handler || + !(env->v7m.control[return_to_secure] & R_V7M_CONTROL_NPRIV_MASK); mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, return_to_secure, - !return_to_handler); + return_to_priv); if (!QEMU_IS_ALIGNED(frameptr, 8) && arm_feature(env, ARM_FEATURE_V8)) { @@ -7112,9 +7184,11 @@ static void do_v7m_exception_exit(ARMCPU *cpu) ((excret & R_V7M_EXCRET_ES_MASK) == 0 || (excret & R_V7M_EXCRET_DCRS_MASK) == 0)) { uint32_t expected_sig = 0xfefa125b; - uint32_t actual_sig = ldl_phys(cs->as, frameptr); + uint32_t actual_sig; - if (expected_sig != actual_sig) { + pop_ok = v7m_stack_read(cpu, &actual_sig, frameptr, mmu_idx); + + if (pop_ok && expected_sig != actual_sig) { /* Take a SecureFault on the current stack */ env->v7m.sfsr |= R_V7M_SFSR_INVIS_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); @@ -7125,7 +7199,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) return; } - pop_ok = + pop_ok = pop_ok && v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) && v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) && v7m_stack_read(cpu, &env->regs[5], frameptr + 0xc, mmu_idx) && @@ -8235,6 +8309,14 @@ void arm_cpu_do_interrupt(CPUState *cs) return; } + /* Hooks may change global state so BQL should be held, also the + * BQL needs to be held for any modification of + * cs->interrupt_request. + */ + g_assert(qemu_mutex_iothread_locked()); + + arm_call_pre_el_change_hook(cpu); + assert(!excp_is_internal(cs->exception_index)); if (arm_el_is_aa64(env, new_el)) { arm_cpu_do_interrupt_aarch64(cs); @@ -8242,12 +8324,6 @@ void arm_cpu_do_interrupt(CPUState *cs) arm_cpu_do_interrupt_aarch32(cs); } - /* Hooks may change global state so BQL should be held, also the - * BQL needs to be held for any modification of - * cs->interrupt_request. - */ - g_assert(qemu_mutex_iothread_locked()); - arm_call_el_change_hook(cpu); if (!kvm_enabled()) { @@ -8680,13 +8756,7 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, return addr; } -/* All loads done in the course of a page table walk go through here. - * TODO: rather than ignoring errors from physical memory reads (which - * are external aborts in ARM terminology) we should propagate this - * error out so that we can turn it into a Data Abort if this walk - * was being done for a CPU load/store or an address translation instruction - * (but not if it was for a debug access). - */ +/* All loads done in the course of a page table walk go through here. */ static uint32_t arm_ldl_ptw(CPUState *cs, hwaddr addr, bool is_secure, ARMMMUIdx mmu_idx, ARMMMUFaultInfo *fi) { @@ -9544,6 +9614,7 @@ static inline bool m_is_system_region(CPUARMState *env, uint32_t address) static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, int *prot, + target_ulong *page_size, ARMMMUFaultInfo *fi) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -9551,6 +9622,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, bool is_user = regime_is_user(env, mmu_idx); *phys_ptr = address; + *page_size = TARGET_PAGE_SIZE; *prot = 0; if (regime_translation_disabled(env, mmu_idx) || @@ -9592,6 +9664,20 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, } if (address < base || address > base + rmask) { + /* + * Address not in this region. We must check whether the + * region covers addresses in the same page as our address. + * In that case we must not report a size that covers the + * whole page for a subsequent hit against a different MPU + * region or the background region, because it would result in + * incorrect TLB hits for subsequent accesses to addresses that + * are in this MPU region. + */ + if (ranges_overlap(base, rmask, + address & TARGET_PAGE_MASK, + TARGET_PAGE_SIZE)) { + *page_size = 1; + } continue; } @@ -9623,16 +9709,12 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, rsize++; } } - if (rsize < TARGET_PAGE_BITS) { - qemu_log_mask(LOG_UNIMP, - "DRSR[%d]: No support for MPU (sub)region size of" - " %" PRIu32 " bytes. Minimum is %d.\n", - n, (1 << rsize), TARGET_PAGE_SIZE); - continue; - } if (srdis) { continue; } + if (rsize < TARGET_PAGE_BITS) { + *page_size = 1 << rsize; + } break; } @@ -9713,6 +9795,17 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, fi->type = ARMFault_Permission; fi->level = 1; + /* + * Core QEMU code can't handle execution from small pages yet, so + * don't try it. This way we'll get an MPU exception, rather than + * eventually causing QEMU to exit in get_page_addr_code(). + */ + if (*page_size < TARGET_PAGE_SIZE && (*prot & PAGE_EXEC)) { + qemu_log_mask(LOG_UNIMP, + "MPU: No support for execution from regions " + "smaller than 1K\n"); + *prot &= ~PAGE_EXEC; + } return !(*prot & (1 << access_type)); } @@ -9743,6 +9836,8 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, int r; bool idau_exempt = false, idau_ns = true, idau_nsc = true; int idau_region = IREGION_NOTVALID; + uint32_t addr_page_base = address & TARGET_PAGE_MASK; + uint32_t addr_page_limit = addr_page_base + (TARGET_PAGE_SIZE - 1); if (cpu->idau) { IDAUInterfaceClass *iic = IDAU_INTERFACE_GET_CLASS(cpu->idau); @@ -9780,6 +9875,9 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, uint32_t limit = env->sau.rlar[r] | 0x1f; if (base <= address && limit >= address) { + if (base > addr_page_base || limit < addr_page_limit) { + sattrs->subpage = true; + } if (sattrs->srvalid) { /* If we hit in more than one region then we must report * as Secure, not NS-Callable, with no valid region @@ -9799,6 +9897,22 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, sattrs->srvalid = true; sattrs->sregion = r; } + } else { + /* + * Address not in this region. We must check whether the + * region covers addresses in the same page as our address. + * In that case we must not report a size that covers the + * whole page for a subsequent hit against a different MPU + * region or the background region, because it would result + * in incorrect TLB hits for subsequent accesses to + * addresses that are in this MPU region. + */ + if (limit >= base && + ranges_overlap(base, limit - base + 1, + addr_page_base, + TARGET_PAGE_SIZE)) { + sattrs->subpage = true; + } } } } @@ -9819,13 +9933,16 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, MemTxAttrs *txattrs, - int *prot, ARMMMUFaultInfo *fi, uint32_t *mregion) + int *prot, bool *is_subpage, + ARMMMUFaultInfo *fi, uint32_t *mregion) { /* Perform a PMSAv8 MPU lookup (without also doing the SAU check * that a full phys-to-virt translation does). * mregion is (if not NULL) set to the region number which matched, * or -1 if no region number is returned (MPU off, address did not * hit a region, address hit in multiple regions). + * We set is_subpage to true if the region hit doesn't cover the + * entire TARGET_PAGE the address is within. */ ARMCPU *cpu = arm_env_get_cpu(env); bool is_user = regime_is_user(env, mmu_idx); @@ -9833,7 +9950,10 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, int n; int matchregion = -1; bool hit = false; + uint32_t addr_page_base = address & TARGET_PAGE_MASK; + uint32_t addr_page_limit = addr_page_base + (TARGET_PAGE_SIZE - 1); + *is_subpage = false; *phys_ptr = address; *prot = 0; if (mregion) { @@ -9868,9 +9988,28 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, } if (address < base || address > limit) { + /* + * Address not in this region. We must check whether the + * region covers addresses in the same page as our address. + * In that case we must not report a size that covers the + * whole page for a subsequent hit against a different MPU + * region or the background region, because it would result in + * incorrect TLB hits for subsequent accesses to addresses that + * are in this MPU region. + */ + if (limit >= base && + ranges_overlap(base, limit - base + 1, + addr_page_base, + TARGET_PAGE_SIZE)) { + *is_subpage = true; + } continue; } + if (base > addr_page_base || limit < addr_page_limit) { + *is_subpage = true; + } + if (hit) { /* Multiple regions match -- always a failure (unlike * PMSAv7 where highest-numbered-region wins) @@ -9882,23 +10021,6 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, matchregion = n; hit = true; - - if (base & ~TARGET_PAGE_MASK) { - qemu_log_mask(LOG_UNIMP, - "MPU_RBAR[%d]: No support for MPU region base" - "address of 0x%" PRIx32 ". Minimum alignment is " - "%d\n", - n, base, TARGET_PAGE_BITS); - continue; - } - if ((limit + 1) & ~TARGET_PAGE_MASK) { - qemu_log_mask(LOG_UNIMP, - "MPU_RBAR[%d]: No support for MPU region limit" - "address of 0x%" PRIx32 ". Minimum alignment is " - "%d\n", - n, limit, TARGET_PAGE_BITS); - continue; - } } } @@ -9934,6 +10056,18 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, fi->type = ARMFault_Permission; fi->level = 1; + /* + * Core QEMU code can't handle execution from small pages yet, so + * don't try it. This means any attempted execution will generate + * an MPU exception, rather than eventually causing QEMU to exit in + * get_page_addr_code(). + */ + if (*is_subpage && (*prot & PAGE_EXEC)) { + qemu_log_mask(LOG_UNIMP, + "MPU: No support for execution from regions " + "smaller than 1K\n"); + *prot &= ~PAGE_EXEC; + } return !(*prot & (1 << access_type)); } @@ -9941,10 +10075,13 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, MMUAccessType access_type, ARMMMUIdx mmu_idx, hwaddr *phys_ptr, MemTxAttrs *txattrs, - int *prot, ARMMMUFaultInfo *fi) + int *prot, target_ulong *page_size, + ARMMMUFaultInfo *fi) { uint32_t secure = regime_is_secure(env, mmu_idx); V8M_SAttributes sattrs = {}; + bool ret; + bool mpu_is_subpage; if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { v8m_security_lookup(env, address, access_type, mmu_idx, &sattrs); @@ -9972,6 +10109,7 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, } else { fi->type = ARMFault_QEMU_SFault; } + *page_size = sattrs.subpage ? 1 : TARGET_PAGE_SIZE; *phys_ptr = address; *prot = 0; return true; @@ -9994,6 +10132,7 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, * for M_FAKE_FSR_SFAULT in arm_v7m_cpu_do_interrupt(). */ fi->type = ARMFault_QEMU_SFault; + *page_size = sattrs.subpage ? 1 : TARGET_PAGE_SIZE; *phys_ptr = address; *prot = 0; return true; @@ -10001,8 +10140,22 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, } } - return pmsav8_mpu_lookup(env, address, access_type, mmu_idx, phys_ptr, - txattrs, prot, fi, NULL); + ret = pmsav8_mpu_lookup(env, address, access_type, mmu_idx, phys_ptr, + txattrs, prot, &mpu_is_subpage, fi, NULL); + /* + * TODO: this is a temporary hack to ignore the fact that the SAU region + * is smaller than a page if this is an executable region. We never + * supported small MPU regions, but we did (accidentally) allow small + * SAU regions, and if we now made small SAU regions not be executable + * then this would break previously working guest code. We can't + * remove this until/unless we implement support for execution from + * small regions. + */ + if (*prot & PAGE_EXEC) { + sattrs.subpage = false; + } + *page_size = sattrs.subpage || mpu_is_subpage ? 1 : TARGET_PAGE_SIZE; + return ret; } static bool get_phys_addr_pmsav5(CPUARMState *env, uint32_t address, @@ -10278,11 +10431,11 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address, if (arm_feature(env, ARM_FEATURE_V8)) { /* PMSAv8 */ ret = get_phys_addr_pmsav8(env, address, access_type, mmu_idx, - phys_ptr, attrs, prot, fi); + phys_ptr, attrs, prot, page_size, fi); } else if (arm_feature(env, ARM_FEATURE_V7)) { /* PMSAv7 */ ret = get_phys_addr_pmsav7(env, address, access_type, mmu_idx, - phys_ptr, prot, fi); + phys_ptr, prot, page_size, fi); } else { /* Pre-v7 MPU */ ret = get_phys_addr_pmsav5(env, address, access_type, mmu_idx, @@ -10344,9 +10497,15 @@ bool arm_tlb_fill(CPUState *cs, vaddr address, core_to_arm_mmu_idx(env, mmu_idx), &phys_addr, &attrs, &prot, &page_size, fi, NULL); if (!ret) { - /* Map a single [sub]page. */ - phys_addr &= TARGET_PAGE_MASK; - address &= TARGET_PAGE_MASK; + /* + * Map a single [sub]page. Regions smaller than our declared + * target page size are handled specially, so for those we + * pass in the exact addresses. + */ + if (page_size >= TARGET_PAGE_SIZE) { + phys_addr &= TARGET_PAGE_MASK; + address &= TARGET_PAGE_MASK; + } tlb_set_page_with_attrs(cs, address, phys_addr, attrs, prot, mmu_idx, page_size); return 0; @@ -10690,6 +10849,7 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) uint32_t mregion; bool targetpriv; bool targetsec = env->v7m.secure; + bool is_subpage; /* Work out what the security state and privilege level we're * interested in is... @@ -10719,7 +10879,8 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) if (arm_current_el(env) != 0 || alt) { /* We can ignore the return value as prot is always set */ pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, - &phys_addr, &attrs, &prot, &fi, &mregion); + &phys_addr, &attrs, &prot, &is_subpage, + &fi, &mregion); if (mregion == -1) { mrvalid = false; mregion = 0; @@ -11300,35 +11461,35 @@ DO_VFP_cmp(d, float64) /* Integer to float and float to integer conversions */ -#define CONV_ITOF(name, fsz, sign) \ - float##fsz HELPER(name)(uint32_t x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ +#define CONV_ITOF(name, ftype, fsz, sign) \ +ftype HELPER(name)(uint32_t x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ } -#define CONV_FTOI(name, fsz, sign, round) \ -uint32_t HELPER(name)(float##fsz x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - if (float##fsz##_is_any_nan(x)) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - return float##fsz##_to_##sign##int32##round(x, fpst); \ +#define CONV_FTOI(name, ftype, fsz, sign, round) \ +sign##int32_t HELPER(name)(ftype x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + if (float##fsz##_is_any_nan(x)) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##sign##int32##round(x, fpst); \ } -#define FLOAT_CONVS(name, p, fsz, sign) \ -CONV_ITOF(vfp_##name##to##p, fsz, sign) \ -CONV_FTOI(vfp_to##name##p, fsz, sign, ) \ -CONV_FTOI(vfp_to##name##z##p, fsz, sign, _round_to_zero) +#define FLOAT_CONVS(name, p, ftype, fsz, sign) \ + CONV_ITOF(vfp_##name##to##p, ftype, fsz, sign) \ + CONV_FTOI(vfp_to##name##p, ftype, fsz, sign, ) \ + CONV_FTOI(vfp_to##name##z##p, ftype, fsz, sign, _round_to_zero) -FLOAT_CONVS(si, h, 16, ) -FLOAT_CONVS(si, s, 32, ) -FLOAT_CONVS(si, d, 64, ) -FLOAT_CONVS(ui, h, 16, u) -FLOAT_CONVS(ui, s, 32, u) -FLOAT_CONVS(ui, d, 64, u) +FLOAT_CONVS(si, h, uint32_t, 16, ) +FLOAT_CONVS(si, s, float32, 32, ) +FLOAT_CONVS(si, d, float64, 64, ) +FLOAT_CONVS(ui, h, uint32_t, 16, u) +FLOAT_CONVS(ui, s, float32, 32, u) +FLOAT_CONVS(ui, d, float64, 64, u) #undef CONV_ITOF #undef CONV_FTOI @@ -11337,20 +11498,12 @@ FLOAT_CONVS(ui, d, 64, u) /* floating point conversion */ float64 VFP_HELPER(fcvtd, s)(float32 x, CPUARMState *env) { - float64 r = float32_to_float64(x, &env->vfp.fp_status); - /* ARM requires that S<->D conversion of any kind of NaN generates - * a quiet NaN by forcing the most significant frac bit to 1. - */ - return float64_maybe_silence_nan(r, &env->vfp.fp_status); + return float32_to_float64(x, &env->vfp.fp_status); } float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env) { - float32 r = float64_to_float32(x, &env->vfp.fp_status); - /* ARM requires that S<->D conversion of any kind of NaN generates - * a quiet NaN by forcing the most significant frac bit to 1. - */ - return float32_maybe_silence_nan(r, &env->vfp.fp_status); + return float64_to_float32(x, &env->vfp.fp_status); } /* VFP3 fixed point conversion. */ @@ -11409,11 +11562,94 @@ VFP_CONV_FIX_A64(sq, s, 32, 64, int64) VFP_CONV_FIX(uh, s, 32, 32, uint16) VFP_CONV_FIX(ul, s, 32, 32, uint32) VFP_CONV_FIX_A64(uq, s, 32, 64, uint64) -VFP_CONV_FIX_A64(sl, h, 16, 32, int32) -VFP_CONV_FIX_A64(ul, h, 16, 32, uint32) + #undef VFP_CONV_FIX #undef VFP_CONV_FIX_FLOAT #undef VFP_CONV_FLOAT_FIX_ROUND +#undef VFP_CONV_FIX_A64 + +/* Conversion to/from f16 can overflow to infinity before/after scaling. + * Therefore we convert to f64, scale, and then convert f64 to f16; or + * vice versa for conversion to integer. + * + * For 16- and 32-bit integers, the conversion to f64 never rounds. + * For 64-bit integers, any integer that would cause rounding will also + * overflow to f16 infinity, so there is no double rounding problem. + */ + +static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst) +{ + return float64_to_float16(float64_scalbn(f, -shift, fpst), true, fpst); +} + +uint32_t HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) +{ + return do_postscale_fp16(int32_to_float64(x, fpst), shift, fpst); +} + +uint32_t HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) +{ + return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst); +} + +uint32_t HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) +{ + return do_postscale_fp16(int64_to_float64(x, fpst), shift, fpst); +} + +uint32_t HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) +{ + return do_postscale_fp16(uint64_to_float64(x, fpst), shift, fpst); +} + +static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst) +{ + if (unlikely(float16_is_any_nan(f))) { + float_raise(float_flag_invalid, fpst); + return 0; + } else { + int old_exc_flags = get_float_exception_flags(fpst); + float64 ret; + + ret = float16_to_float64(f, true, fpst); + ret = float64_scalbn(ret, shift, fpst); + old_exc_flags |= get_float_exception_flags(fpst) + & float_flag_input_denormal; + set_float_exception_flags(old_exc_flags, fpst); + + return ret; + } +} + +uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst) +{ + return float64_to_int16(do_prescale_fp16(x, shift, fpst), fpst); +} + +uint32_t HELPER(vfp_touhh)(uint32_t x, uint32_t shift, void *fpst) +{ + return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst); +} + +uint32_t HELPER(vfp_toslh)(uint32_t x, uint32_t shift, void *fpst) +{ + return float64_to_int32(do_prescale_fp16(x, shift, fpst), fpst); +} + +uint32_t HELPER(vfp_toulh)(uint32_t x, uint32_t shift, void *fpst) +{ + return float64_to_uint32(do_prescale_fp16(x, shift, fpst), fpst); +} + +uint64_t HELPER(vfp_tosqh)(uint32_t x, uint32_t shift, void *fpst) +{ + return float64_to_int64(do_prescale_fp16(x, shift, fpst), fpst); +} + +uint64_t HELPER(vfp_touqh)(uint32_t x, uint32_t shift, void *fpst) +{ + return float64_to_uint64(do_prescale_fp16(x, shift, fpst), fpst); +} /* Set the current fp rounding mode and return the old one. * The argument is a softfloat float_round_ value. @@ -11446,64 +11682,56 @@ uint32_t HELPER(set_neon_rmode)(uint32_t rmode, CPUARMState *env) } /* Half precision conversions. */ -static float32 do_fcvt_f16_to_f32(uint32_t a, CPUARMState *env, float_status *s) +float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, void *fpstp, uint32_t ahp_mode) { - int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - float32 r = float16_to_float32(make_float16(a), ieee, s); - if (ieee) { - return float32_maybe_silence_nan(r, s); - } + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float32 r = float16_to_float32(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); return r; } -static uint32_t do_fcvt_f32_to_f16(float32 a, CPUARMState *env, float_status *s) +uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) { - int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - float16 r = float32_to_float16(a, ieee, s); - if (ieee) { - r = float16_maybe_silence_nan(r, s); - } - return float16_val(r); -} - -float32 HELPER(neon_fcvt_f16_to_f32)(uint32_t a, CPUARMState *env) -{ - return do_fcvt_f16_to_f32(a, env, &env->vfp.standard_fp_status); -} - -uint32_t HELPER(neon_fcvt_f32_to_f16)(float32 a, CPUARMState *env) -{ - return do_fcvt_f32_to_f16(a, env, &env->vfp.standard_fp_status); -} - -float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, CPUARMState *env) -{ - return do_fcvt_f16_to_f32(a, env, &env->vfp.fp_status); -} - -uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, CPUARMState *env) -{ - return do_fcvt_f32_to_f16(a, env, &env->vfp.fp_status); -} - -float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, CPUARMState *env) -{ - int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - float64 r = float16_to_float64(make_float16(a), ieee, &env->vfp.fp_status); - if (ieee) { - return float64_maybe_silence_nan(r, &env->vfp.fp_status); - } + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float32_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); return r; } -uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, CPUARMState *env) +float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, void *fpstp, uint32_t ahp_mode) { - int ieee = (env->vfp.xregs[ARM_VFP_FPSCR] & (1 << 26)) == 0; - float16 r = float64_to_float16(a, ieee, &env->vfp.fp_status); - if (ieee) { - r = float16_maybe_silence_nan(r, &env->vfp.fp_status); - } - return float16_val(r); + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float64 r = float16_to_float64(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); + return r; +} + +uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float64_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); + return r; } #define float32_two make_float32(0x40000000) @@ -11631,7 +11859,7 @@ static bool round_to_inf(float_status *fpst, bool sign_bit) g_assert_not_reached(); } -float16 HELPER(recpe_f16)(float16 input, void *fpstp) +uint32_t HELPER(recpe_f16)(uint32_t input, void *fpstp) { float_status *fpst = fpstp; float16 f16 = float16_squash_input_denormal(input, fpst); @@ -11645,7 +11873,7 @@ float16 HELPER(recpe_f16)(float16 input, void *fpstp) float16 nan = f16; if (float16_is_signaling_nan(f16, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float16_maybe_silence_nan(f16, fpst); + nan = float16_silence_nan(f16, fpst); } if (fpst->default_nan_mode) { nan = float16_default_nan(fpst); @@ -11693,7 +11921,7 @@ float32 HELPER(recpe_f32)(float32 input, void *fpstp) float32 nan = f32; if (float32_is_signaling_nan(f32, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float32_maybe_silence_nan(f32, fpst); + nan = float32_silence_nan(f32, fpst); } if (fpst->default_nan_mode) { nan = float32_default_nan(fpst); @@ -11741,7 +11969,7 @@ float64 HELPER(recpe_f64)(float64 input, void *fpstp) float64 nan = f64; if (float64_is_signaling_nan(f64, fpst)) { float_raise(float_flag_invalid, fpst); - nan = float64_maybe_silence_nan(f64, fpst); + nan = float64_silence_nan(f64, fpst); } if (fpst->default_nan_mode) { nan = float64_default_nan(fpst); @@ -11826,7 +12054,7 @@ static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) return extract64(estimate, 0, 8) << 44; } -float16 HELPER(rsqrte_f16)(float16 input, void *fpstp) +uint32_t HELPER(rsqrte_f16)(uint32_t input, void *fpstp) { float_status *s = fpstp; float16 f16 = float16_squash_input_denormal(input, s); @@ -11840,7 +12068,7 @@ float16 HELPER(rsqrte_f16)(float16 input, void *fpstp) float16 nan = f16; if (float16_is_signaling_nan(f16, s)) { float_raise(float_flag_invalid, s); - nan = float16_maybe_silence_nan(f16, s); + nan = float16_silence_nan(f16, s); } if (s->default_nan_mode) { nan = float16_default_nan(s); @@ -11884,7 +12112,7 @@ float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) float32 nan = f32; if (float32_is_signaling_nan(f32, s)) { float_raise(float_flag_invalid, s); - nan = float32_maybe_silence_nan(f32, s); + nan = float32_silence_nan(f32, s); } if (s->default_nan_mode) { nan = float32_default_nan(s); @@ -11927,7 +12155,7 @@ float64 HELPER(rsqrte_f64)(float64 input, void *fpstp) float64 nan = f64; if (float64_is_signaling_nan(f64, s)) { float_raise(float_flag_invalid, s); - nan = float64_maybe_silence_nan(f64, s); + nan = float64_silence_nan(f64, s); } if (s->default_nan_mode) { nan = float64_default_nan(s); diff --git a/target/arm/helper.h b/target/arm/helper.h index 34e8cc8904..59e8c3bd1b 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -134,12 +134,12 @@ DEF_HELPER_2(vfp_touid, i32, f64, ptr) DEF_HELPER_2(vfp_touizh, i32, f16, ptr) DEF_HELPER_2(vfp_touizs, i32, f32, ptr) DEF_HELPER_2(vfp_touizd, i32, f64, ptr) -DEF_HELPER_2(vfp_tosih, i32, f16, ptr) -DEF_HELPER_2(vfp_tosis, i32, f32, ptr) -DEF_HELPER_2(vfp_tosid, i32, f64, ptr) -DEF_HELPER_2(vfp_tosizh, i32, f16, ptr) -DEF_HELPER_2(vfp_tosizs, i32, f32, ptr) -DEF_HELPER_2(vfp_tosizd, i32, f64, ptr) +DEF_HELPER_2(vfp_tosih, s32, f16, ptr) +DEF_HELPER_2(vfp_tosis, s32, f32, ptr) +DEF_HELPER_2(vfp_tosid, s32, f64, ptr) +DEF_HELPER_2(vfp_tosizh, s32, f16, ptr) +DEF_HELPER_2(vfp_tosizs, s32, f32, ptr) +DEF_HELPER_2(vfp_tosizd, s32, f64, ptr) DEF_HELPER_3(vfp_toshs_round_to_zero, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosls_round_to_zero, i32, f32, i32, ptr) @@ -149,8 +149,12 @@ DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, ptr) DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, ptr) +DEF_HELPER_3(vfp_touhh, i32, f16, i32, ptr) +DEF_HELPER_3(vfp_toshh, i32, f16, i32, ptr) DEF_HELPER_3(vfp_toulh, i32, f16, i32, ptr) DEF_HELPER_3(vfp_toslh, i32, f16, i32, ptr) +DEF_HELPER_3(vfp_touqh, i64, f16, i32, ptr) +DEF_HELPER_3(vfp_tosqh, i64, f16, i32, ptr) DEF_HELPER_3(vfp_toshs, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosls, i32, f32, i32, ptr) DEF_HELPER_3(vfp_tosqs, i64, f32, i32, ptr) @@ -177,16 +181,16 @@ DEF_HELPER_3(vfp_ultod, f64, i64, i32, ptr) DEF_HELPER_3(vfp_uqtod, f64, i64, i32, ptr) DEF_HELPER_3(vfp_sltoh, f16, i32, i32, ptr) DEF_HELPER_3(vfp_ultoh, f16, i32, i32, ptr) +DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, ptr) +DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, ptr) DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, ptr) DEF_HELPER_FLAGS_2(set_neon_rmode, TCG_CALL_NO_RWG, i32, i32, env) -DEF_HELPER_2(vfp_fcvt_f16_to_f32, f32, i32, env) -DEF_HELPER_2(vfp_fcvt_f32_to_f16, i32, f32, env) -DEF_HELPER_2(neon_fcvt_f16_to_f32, f32, i32, env) -DEF_HELPER_2(neon_fcvt_f32_to_f16, i32, f32, env) -DEF_HELPER_FLAGS_2(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, i32, env) -DEF_HELPER_FLAGS_2(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, i32, f64, env) +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f32, TCG_CALL_NO_RWG, f32, f16, ptr, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f32_to_f16, TCG_CALL_NO_RWG, f16, f32, ptr, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, f16, ptr, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, f16, f64, ptr, i32) DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, ptr) DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, ptr) @@ -579,6 +583,16 @@ DEF_HELPER_FLAGS_5(gvec_qrdmlah_s32, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_udot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sdot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_udot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_sdot_idx_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_udot_idx_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sdot_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_udot_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, @@ -597,6 +611,48 @@ DEF_HELPER_FLAGS_5(gvec_fcmlas_idx, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_fcmlad, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_frsqrte_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmul_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(gvec_fmla_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) + #ifdef TARGET_AARCH64 #include "helper-a64.h" +#include "helper-sve.h" #endif diff --git a/target/arm/internals.h b/target/arm/internals.h index 8ce944b7a0..dc9357766c 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -727,11 +727,19 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr); -/* Call the EL change hook if one has been registered */ +/* Call any registered EL change hooks */ +static inline void arm_call_pre_el_change_hook(ARMCPU *cpu) +{ + ARMELChangeHook *hook, *next; + QLIST_FOREACH_SAFE(hook, &cpu->pre_el_change_hooks, node, next) { + hook->hook(cpu, hook->opaque); + } +} static inline void arm_call_el_change_hook(ARMCPU *cpu) { - if (cpu->el_change_hook) { - cpu->el_change_hook(cpu, cpu->el_change_hook_opaque); + ARMELChangeHook *hook, *next; + QLIST_FOREACH_SAFE(hook, &cpu->el_change_hooks, node, next) { + hook->hook(cpu, hook->opaque); } } diff --git a/target/arm/iwmmxt_helper.c b/target/arm/iwmmxt_helper.c index 7d87e1a0a8..f6a4fc5b7f 100644 --- a/target/arm/iwmmxt_helper.c +++ b/target/arm/iwmmxt_helper.c @@ -22,7 +22,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" /* iwMMXt macros extracted from GNU gdb. */ diff --git a/target/arm/kvm.c b/target/arm/kvm.c index ecc39ac295..65f867d569 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -20,8 +20,10 @@ #include "sysemu/kvm.h" #include "kvm_arm.h" #include "cpu.h" +#include "trace.h" #include "internals.h" #include "hw/arm/arm.h" +#include "hw/pci/pci.h" #include "exec/memattrs.h" #include "exec/address-spaces.h" #include "hw/boards.h" @@ -182,10 +184,15 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu) * We use a MemoryListener to track mapping and unmapping of * the regions during board creation, so the board models don't * need to do anything special for the KVM case. + * + * Sometimes the address must be OR'ed with some other fields + * (for example for KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION). + * @kda_addr_ormask aims at storing the value of those fields. */ typedef struct KVMDevice { struct kvm_arm_device_addr kda; struct kvm_device_attr kdattr; + uint64_t kda_addr_ormask; MemoryRegion *mr; QSLIST_ENTRY(KVMDevice) entries; int dev_fd; @@ -232,6 +239,8 @@ static void kvm_arm_set_device_addr(KVMDevice *kd) */ if (kd->dev_fd >= 0) { uint64_t addr = kd->kda.addr; + + addr |= kd->kda_addr_ormask; attr->addr = (uintptr_t)&addr; ret = kvm_device_ioctl(kd->dev_fd, KVM_SET_DEVICE_ATTR, attr); } else { @@ -254,6 +263,7 @@ static void kvm_arm_machine_init_done(Notifier *notifier, void *data) kvm_arm_set_device_addr(kd); } memory_region_unref(kd->mr); + QSLIST_REMOVE_HEAD(&kvm_devices_head, entries); g_free(kd); } memory_listener_unregister(&devlistener); @@ -264,7 +274,7 @@ static Notifier notify = { }; void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, - uint64_t attr, int dev_fd) + uint64_t attr, int dev_fd, uint64_t addr_ormask) { KVMDevice *kd; @@ -284,6 +294,7 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, kd->kdattr.group = group; kd->kdattr.attr = attr; kd->dev_fd = dev_fd; + kd->kda_addr_ormask = addr_ormask; QSLIST_INSERT_HEAD(&kvm_devices_head, kd, entries); memory_region_ref(kd->mr); } @@ -649,7 +660,42 @@ int kvm_arm_vgic_probe(void) int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, uint64_t address, uint32_t data, PCIDevice *dev) { - return 0; + AddressSpace *as = pci_device_iommu_address_space(dev); + hwaddr xlat, len, doorbell_gpa; + MemoryRegionSection mrs; + MemoryRegion *mr; + int ret = 1; + + if (as == &address_space_memory) { + return 0; + } + + /* MSI doorbell address is translated by an IOMMU */ + + rcu_read_lock(); + mr = address_space_translate(as, address, &xlat, &len, true, + MEMTXATTRS_UNSPECIFIED); + if (!mr) { + goto unlock; + } + mrs = memory_region_find(mr, xlat, 1); + if (!mrs.mr) { + goto unlock; + } + + doorbell_gpa = mrs.offset_within_address_space; + memory_region_unref(mrs.mr); + + route->u.msi.address_lo = doorbell_gpa; + route->u.msi.address_hi = doorbell_gpa >> 32; + + trace_kvm_arm_fixup_msi_route(address, doorbell_gpa); + + ret = 0; + +unlock: + rcu_read_unlock(); + return ret; } int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route, diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index 1740cda47d..4e91c11796 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -36,7 +36,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * and then query that CPU for the relevant ID registers. */ int i, ret, fdarray[3]; - uint32_t midr, id_pfr0, id_isar0, mvfr1; + uint32_t midr, id_pfr0, mvfr1; uint64_t features = 0; /* Old kernels may not know about the PREFERRED_TARGET ioctl: however * we know these will only support creating one kind of guest CPU, @@ -58,11 +58,6 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) | ENCODE_CP_REG(15, 0, 0, 0, 1, 0, 0), .addr = (uintptr_t)&id_pfr0, }, - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 0, 2, 0, 0), - .addr = (uintptr_t)&id_isar0, - }, { .id = KVM_REG_ARM | KVM_REG_SIZE_U32 | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1, @@ -98,26 +93,14 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) /* Now we've retrieved all the register information we can * set the feature bits based on the ID register fields. * We can assume any KVM supporting CPU is at least a v7 - * with VFPv3, LPAE and the generic timers; this in turn implies - * most of the other feature bits, but a few must be tested. + * with VFPv3, virtualization extensions, and the generic + * timers; this in turn implies most of the other feature + * bits, but a few must be tested. */ - set_feature(&features, ARM_FEATURE_V7); + set_feature(&features, ARM_FEATURE_V7VE); set_feature(&features, ARM_FEATURE_VFP3); - set_feature(&features, ARM_FEATURE_LPAE); set_feature(&features, ARM_FEATURE_GENERIC_TIMER); - switch (extract32(id_isar0, 24, 4)) { - case 1: - set_feature(&features, ARM_FEATURE_THUMB_DIV); - break; - case 2: - set_feature(&features, ARM_FEATURE_ARM_DIV); - set_feature(&features, ARM_FEATURE_THUMB_DIV); - break; - default: - break; - } - if (extract32(id_pfr0, 12, 4) == 1) { set_feature(&features, ARM_FEATURE_THUMB2EE); } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 1e2364007d..863f205822 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -34,6 +34,7 @@ int kvm_arm_vcpu_init(CPUState *cs); * @group: device control API group for setting addresses * @attr: device control API address type * @dev_fd: device control device file descriptor (or -1 if not supported) + * @addr_ormask: value to be OR'ed with resolved address * * Remember the memory region @mr, and when it is mapped by the * machine model, tell the kernel that base address using the @@ -45,7 +46,7 @@ int kvm_arm_vcpu_init(CPUState *cs); * address at the point where machine init is complete. */ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, - uint64_t attr, int dev_fd); + uint64_t attr, int dev_fd, uint64_t addr_ormask); /** * kvm_arm_init_cpreg_list: diff --git a/target/arm/machine.c b/target/arm/machine.c index 2e28d086bd..ff4ec22bf7 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -184,6 +184,7 @@ static const VMStateDescription vmstate_m_faultmask_primask = { .name = "cpu/m/faultmask-primask", .version_id = 1, .minimum_version_id = 1, + .needed = m_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.faultmask[M_REG_NS], ARMCPU), VMSTATE_UINT32(env.v7m.primask[M_REG_NS], ARMCPU), @@ -230,6 +231,7 @@ static const VMStateDescription vmstate_m_scr = { .name = "cpu/m/scr", .version_id = 1, .minimum_version_id = 1, + .needed = m_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.scr[M_REG_NS], ARMCPU), VMSTATE_END_OF_LIST() @@ -240,6 +242,7 @@ static const VMStateDescription vmstate_m_other_sp = { .name = "cpu/m/other-sp", .version_id = 1, .minimum_version_id = 1, + .needed = m_needed, .fields = (VMStateField[]) { VMSTATE_UINT32(env.v7m.other_sp, ARMCPU), VMSTATE_END_OF_LIST() diff --git a/target/arm/neon_helper.c b/target/arm/neon_helper.c index a1ec6537eb..c2c6491a83 100644 --- a/target/arm/neon_helper.c +++ b/target/arm/neon_helper.c @@ -9,7 +9,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index 84f08bf815..f728f25e4b 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -511,6 +511,10 @@ void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask) /* Write the CPSR for a 32-bit exception return */ void HELPER(cpsr_write_eret)(CPUARMState *env, uint32_t val) { + qemu_mutex_lock_iothread(); + arm_call_pre_el_change_hook(arm_env_get_cpu(env)); + qemu_mutex_unlock_iothread(); + cpsr_write(env, val, CPSR_ERET_MASK, CPSRWriteExceptionReturn); /* Generated code has already stored the new PC value, but @@ -1028,6 +1032,10 @@ void HELPER(exception_return)(CPUARMState *env) goto illegal_return; } + qemu_mutex_lock_iothread(); + arm_call_pre_el_change_hook(arm_env_get_cpu(env)); + qemu_mutex_unlock_iothread(); + if (!return_to_aa64) { env->aarch64 = 0; /* We do a raw CPSR write because aarch64_sync_64_to_32() diff --git a/target/arm/psci.c b/target/arm/psci.c index eb7b88e926..a74d78802a 100644 --- a/target/arm/psci.c +++ b/target/arm/psci.c @@ -22,7 +22,6 @@ #include "sysemu/sysemu.h" #include "internals.h" #include "arm-powerctl.h" -#include "exec/exec-all.h" bool arm_is_psci_call(ARMCPU *cpu, int excp_type) { diff --git a/target/arm/sve.decode b/target/arm/sve.decode new file mode 100644 index 0000000000..e10b689454 --- /dev/null +++ b/target/arm/sve.decode @@ -0,0 +1,1094 @@ +# AArch64 SVE instruction descriptions +# +# Copyright (c) 2017 Linaro, Ltd +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . + +# +# This file is processed by scripts/decodetree.py +# + +########################################################################### +# Named fields. These are primarily for disjoint fields. + +%imm4_16_p1 16:4 !function=plus1 +%imm6_22_5 22:1 5:5 +%imm7_22_16 22:2 16:5 +%imm8_16_10 16:5 10:3 +%imm9_16_10 16:s6 10:3 +%size_23 23:2 +%dtype_23_13 23:2 13:2 +%index3_22_19 22:1 19:2 + +# A combination of tsz:imm3 -- extract esize. +%tszimm_esz 22:2 5:5 !function=tszimm_esz +# A combination of tsz:imm3 -- extract (2 * esize) - (tsz:imm3) +%tszimm_shr 22:2 5:5 !function=tszimm_shr +# A combination of tsz:imm3 -- extract (tsz:imm3) - esize +%tszimm_shl 22:2 5:5 !function=tszimm_shl + +# Similarly for the tszh/tszl pair at 22/16 for zzi +%tszimm16_esz 22:2 16:5 !function=tszimm_esz +%tszimm16_shr 22:2 16:5 !function=tszimm_shr +%tszimm16_shl 22:2 16:5 !function=tszimm_shl + +# Signed 8-bit immediate, optionally shifted left by 8. +%sh8_i8s 5:9 !function=expand_imm_sh8s +# Unsigned 8-bit immediate, optionally shifted left by 8. +%sh8_i8u 5:9 !function=expand_imm_sh8u + +# Unsigned load of msz into esz=2, represented as a dtype. +%msz_dtype 23:2 !function=msz_dtype + +# Either a copy of rd (at bit 0), or a different source +# as propagated via the MOVPRFX instruction. +%reg_movprfx 0:5 + +########################################################################### +# Named attribute sets. These are used to make nice(er) names +# when creating helpers common to those for the individual +# instruction patterns. + +&rr_esz rd rn esz +&rri rd rn imm +&rr_dbm rd rn dbm +&rrri rd rn rm imm +&rri_esz rd rn imm esz +&rrr_esz rd rn rm esz +&rpr_esz rd pg rn esz +&rpr_s rd pg rn s +&rprr_s rd pg rn rm s +&rprr_esz rd pg rn rm esz +&rprrr_esz rd pg rn rm ra esz +&rpri_esz rd pg rn imm esz +&ptrue rd esz pat s +&incdec_cnt rd pat esz imm d u +&incdec2_cnt rd rn pat esz imm d u +&incdec_pred rd pg esz d u +&incdec2_pred rd rn pg esz d u +&rprr_load rd pg rn rm dtype nreg +&rpri_load rd pg rn imm dtype nreg +&rprr_store rd pg rn rm msz esz nreg +&rpri_store rd pg rn imm msz esz nreg +&rprr_gather_load rd pg rn rm esz msz u ff xs scale +&rpri_gather_load rd pg rn imm esz msz u ff +&rprr_scatter_store rd pg rn rm esz msz xs scale +&rpri_scatter_store rd pg rn imm esz msz + +########################################################################### +# Named instruction formats. These are generally used to +# reduce the amount of duplication between instruction patterns. + +# Two operand with unused vector element size +@pd_pn_e0 ........ ........ ....... rn:4 . rd:4 &rr_esz esz=0 + +# Two operand +@pd_pn ........ esz:2 .. .... ....... rn:4 . rd:4 &rr_esz +@rd_rn ........ esz:2 ...... ...... rn:5 rd:5 &rr_esz + +# Two operand with governing predicate, flags setting +@pd_pg_pn_s ........ . s:1 ...... .. pg:4 . rn:4 . rd:4 &rpr_s + +# Three operand with unused vector element size +@rd_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 &rrr_esz esz=0 + +# Three predicate operand, with governing predicate, flag setting +@pd_pg_pn_pm_s ........ . s:1 .. rm:4 .. pg:4 . rn:4 . rd:4 &rprr_s + +# Three operand, vector element size +@rd_rn_rm ........ esz:2 . rm:5 ... ... rn:5 rd:5 &rrr_esz +@pd_pn_pm ........ esz:2 .. rm:4 ....... rn:4 . rd:4 &rrr_esz +@rdn_rm ........ esz:2 ...... ...... rm:5 rd:5 \ + &rrr_esz rn=%reg_movprfx +@rdn_sh_i8u ........ esz:2 ...... ...... ..... rd:5 \ + &rri_esz rn=%reg_movprfx imm=%sh8_i8u +@rdn_i8u ........ esz:2 ...... ... imm:8 rd:5 \ + &rri_esz rn=%reg_movprfx +@rdn_i8s ........ esz:2 ...... ... imm:s8 rd:5 \ + &rri_esz rn=%reg_movprfx + +# Three operand with "memory" size, aka immediate left shift +@rd_rn_msz_rm ........ ... rm:5 .... imm:2 rn:5 rd:5 &rrri + +# Two register operand, with governing predicate, vector element size +@rdn_pg_rm ........ esz:2 ... ... ... pg:3 rm:5 rd:5 \ + &rprr_esz rn=%reg_movprfx +@rdm_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 \ + &rprr_esz rm=%reg_movprfx +@rd_pg4_rn_rm ........ esz:2 . rm:5 .. pg:4 rn:5 rd:5 &rprr_esz +@pd_pg_rn_rm ........ esz:2 . rm:5 ... pg:3 rn:5 . rd:4 &rprr_esz + +# Three register operand, with governing predicate, vector element size +@rda_pg_rn_rm ........ esz:2 . rm:5 ... pg:3 rn:5 rd:5 \ + &rprrr_esz ra=%reg_movprfx +@rdn_pg_ra_rm ........ esz:2 . rm:5 ... pg:3 ra:5 rd:5 \ + &rprrr_esz rn=%reg_movprfx +@rdn_pg_rm_ra ........ esz:2 . ra:5 ... pg:3 rm:5 rd:5 \ + &rprrr_esz rn=%reg_movprfx + +# One register operand, with governing predicate, vector element size +@rd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 rd:5 &rpr_esz +@rd_pg4_pn ........ esz:2 ... ... .. pg:4 . rn:4 rd:5 &rpr_esz +@pd_pg_rn ........ esz:2 ... ... ... pg:3 rn:5 . rd:4 &rpr_esz + +# One register operand, with governing predicate, no vector element size +@rd_pg_rn_e0 ........ .. ... ... ... pg:3 rn:5 rd:5 &rpr_esz esz=0 + +# Two register operands with a 6-bit signed immediate. +@rd_rn_i6 ........ ... rn:5 ..... imm:s6 rd:5 &rri + +# Two register operand, one immediate operand, with predicate, +# element size encoded as TSZHL. User must fill in imm. +@rdn_pg_tszimm ........ .. ... ... ... pg:3 ..... rd:5 \ + &rpri_esz rn=%reg_movprfx esz=%tszimm_esz + +# Similarly without predicate. +@rd_rn_tszimm ........ .. ... ... ...... rn:5 rd:5 \ + &rri_esz esz=%tszimm16_esz + +# Two register operand, one immediate operand, with 4-bit predicate. +# User must fill in imm. +@rdn_pg4 ........ esz:2 .. pg:4 ... ........ rd:5 \ + &rpri_esz rn=%reg_movprfx + +# Two register operand, one one-bit floating-point operand. +@rdn_i1 ........ esz:2 ......... pg:3 .... imm:1 rd:5 \ + &rpri_esz rn=%reg_movprfx + +# Two register operand, one encoded bitmask. +@rdn_dbm ........ .. .... dbm:13 rd:5 \ + &rr_dbm rn=%reg_movprfx + +# Predicate output, vector and immediate input, +# controlling predicate, element size. +@pd_pg_rn_i7 ........ esz:2 . imm:7 . pg:3 rn:5 . rd:4 &rpri_esz +@pd_pg_rn_i5 ........ esz:2 . imm:s5 ... pg:3 rn:5 . rd:4 &rpri_esz + +# Basic Load/Store with 9-bit immediate offset +@pd_rn_i9 ........ ........ ...... rn:5 . rd:4 \ + &rri imm=%imm9_16_10 +@rd_rn_i9 ........ ........ ...... rn:5 rd:5 \ + &rri imm=%imm9_16_10 + +# One register, pattern, and uint4+1. +# User must fill in U and D. +@incdec_cnt ........ esz:2 .. .... ...... pat:5 rd:5 \ + &incdec_cnt imm=%imm4_16_p1 +@incdec2_cnt ........ esz:2 .. .... ...... pat:5 rd:5 \ + &incdec2_cnt imm=%imm4_16_p1 rn=%reg_movprfx + +# One register, predicate. +# User must fill in U and D. +@incdec_pred ........ esz:2 .... .. ..... .. pg:4 rd:5 &incdec_pred +@incdec2_pred ........ esz:2 .... .. ..... .. pg:4 rd:5 \ + &incdec2_pred rn=%reg_movprfx + +# Loads; user must fill in NREG. +@rprr_load_dt ....... dtype:4 rm:5 ... pg:3 rn:5 rd:5 &rprr_load +@rpri_load_dt ....... dtype:4 . imm:s4 ... pg:3 rn:5 rd:5 &rpri_load + +@rprr_load_msz ....... .... rm:5 ... pg:3 rn:5 rd:5 \ + &rprr_load dtype=%msz_dtype +@rpri_load_msz ....... .... . imm:s4 ... pg:3 rn:5 rd:5 \ + &rpri_load dtype=%msz_dtype + +# Gather Loads. +@rprr_g_load_u ....... .. . . rm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load xs=2 +@rprr_g_load_xs_u ....... .. xs:1 . rm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load +@rprr_g_load_xs_u_sc ....... .. xs:1 scale:1 rm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load +@rprr_g_load_xs_sc ....... .. xs:1 scale:1 rm:5 . . ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load +@rprr_g_load_u_sc ....... .. . scale:1 rm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load xs=2 +@rprr_g_load_sc ....... .. . scale:1 rm:5 . . ff:1 pg:3 rn:5 rd:5 \ + &rprr_gather_load xs=2 +@rpri_g_load ....... msz:2 .. imm:5 . u:1 ff:1 pg:3 rn:5 rd:5 \ + &rpri_gather_load + +# Stores; user must fill in ESZ, MSZ, NREG as needed. +@rprr_store ....... .. .. rm:5 ... pg:3 rn:5 rd:5 &rprr_store +@rpri_store_msz ....... msz:2 .. . imm:s4 ... pg:3 rn:5 rd:5 &rpri_store +@rprr_store_esz_n0 ....... .. esz:2 rm:5 ... pg:3 rn:5 rd:5 \ + &rprr_store nreg=0 +@rprr_scatter_store ....... msz:2 .. rm:5 ... pg:3 rn:5 rd:5 \ + &rprr_scatter_store +@rpri_scatter_store ....... msz:2 .. imm:5 ... pg:3 rn:5 rd:5 \ + &rpri_scatter_store + +########################################################################### +# Instruction patterns. Grouped according to the SVE encodingindex.xhtml. + +### SVE Integer Arithmetic - Binary Predicated Group + +# SVE bitwise logical vector operations (predicated) +ORR_zpzz 00000100 .. 011 000 000 ... ..... ..... @rdn_pg_rm +EOR_zpzz 00000100 .. 011 001 000 ... ..... ..... @rdn_pg_rm +AND_zpzz 00000100 .. 011 010 000 ... ..... ..... @rdn_pg_rm +BIC_zpzz 00000100 .. 011 011 000 ... ..... ..... @rdn_pg_rm + +# SVE integer add/subtract vectors (predicated) +ADD_zpzz 00000100 .. 000 000 000 ... ..... ..... @rdn_pg_rm +SUB_zpzz 00000100 .. 000 001 000 ... ..... ..... @rdn_pg_rm +SUB_zpzz 00000100 .. 000 011 000 ... ..... ..... @rdm_pg_rn # SUBR + +# SVE integer min/max/difference (predicated) +SMAX_zpzz 00000100 .. 001 000 000 ... ..... ..... @rdn_pg_rm +UMAX_zpzz 00000100 .. 001 001 000 ... ..... ..... @rdn_pg_rm +SMIN_zpzz 00000100 .. 001 010 000 ... ..... ..... @rdn_pg_rm +UMIN_zpzz 00000100 .. 001 011 000 ... ..... ..... @rdn_pg_rm +SABD_zpzz 00000100 .. 001 100 000 ... ..... ..... @rdn_pg_rm +UABD_zpzz 00000100 .. 001 101 000 ... ..... ..... @rdn_pg_rm + +# SVE integer multiply/divide (predicated) +MUL_zpzz 00000100 .. 010 000 000 ... ..... ..... @rdn_pg_rm +SMULH_zpzz 00000100 .. 010 010 000 ... ..... ..... @rdn_pg_rm +UMULH_zpzz 00000100 .. 010 011 000 ... ..... ..... @rdn_pg_rm +# Note that divide requires size >= 2; below 2 is unallocated. +SDIV_zpzz 00000100 .. 010 100 000 ... ..... ..... @rdn_pg_rm +UDIV_zpzz 00000100 .. 010 101 000 ... ..... ..... @rdn_pg_rm +SDIV_zpzz 00000100 .. 010 110 000 ... ..... ..... @rdm_pg_rn # SDIVR +UDIV_zpzz 00000100 .. 010 111 000 ... ..... ..... @rdm_pg_rn # UDIVR + +### SVE Integer Reduction Group + +# SVE bitwise logical reduction (predicated) +ORV 00000100 .. 011 000 001 ... ..... ..... @rd_pg_rn +EORV 00000100 .. 011 001 001 ... ..... ..... @rd_pg_rn +ANDV 00000100 .. 011 010 001 ... ..... ..... @rd_pg_rn + +# SVE constructive prefix (predicated) +MOVPRFX_z 00000100 .. 010 000 001 ... ..... ..... @rd_pg_rn +MOVPRFX_m 00000100 .. 010 001 001 ... ..... ..... @rd_pg_rn + +# SVE integer add reduction (predicated) +# Note that saddv requires size != 3. +UADDV 00000100 .. 000 001 001 ... ..... ..... @rd_pg_rn +SADDV 00000100 .. 000 000 001 ... ..... ..... @rd_pg_rn + +# SVE integer min/max reduction (predicated) +SMAXV 00000100 .. 001 000 001 ... ..... ..... @rd_pg_rn +UMAXV 00000100 .. 001 001 001 ... ..... ..... @rd_pg_rn +SMINV 00000100 .. 001 010 001 ... ..... ..... @rd_pg_rn +UMINV 00000100 .. 001 011 001 ... ..... ..... @rd_pg_rn + +### SVE Shift by Immediate - Predicated Group + +# SVE bitwise shift by immediate (predicated) +ASR_zpzi 00000100 .. 000 000 100 ... .. ... ..... \ + @rdn_pg_tszimm imm=%tszimm_shr +LSR_zpzi 00000100 .. 000 001 100 ... .. ... ..... \ + @rdn_pg_tszimm imm=%tszimm_shr +LSL_zpzi 00000100 .. 000 011 100 ... .. ... ..... \ + @rdn_pg_tszimm imm=%tszimm_shl +ASRD 00000100 .. 000 100 100 ... .. ... ..... \ + @rdn_pg_tszimm imm=%tszimm_shr + +# SVE bitwise shift by vector (predicated) +ASR_zpzz 00000100 .. 010 000 100 ... ..... ..... @rdn_pg_rm +LSR_zpzz 00000100 .. 010 001 100 ... ..... ..... @rdn_pg_rm +LSL_zpzz 00000100 .. 010 011 100 ... ..... ..... @rdn_pg_rm +ASR_zpzz 00000100 .. 010 100 100 ... ..... ..... @rdm_pg_rn # ASRR +LSR_zpzz 00000100 .. 010 101 100 ... ..... ..... @rdm_pg_rn # LSRR +LSL_zpzz 00000100 .. 010 111 100 ... ..... ..... @rdm_pg_rn # LSLR + +# SVE bitwise shift by wide elements (predicated) +# Note these require size != 3. +ASR_zpzw 00000100 .. 011 000 100 ... ..... ..... @rdn_pg_rm +LSR_zpzw 00000100 .. 011 001 100 ... ..... ..... @rdn_pg_rm +LSL_zpzw 00000100 .. 011 011 100 ... ..... ..... @rdn_pg_rm + +### SVE Integer Arithmetic - Unary Predicated Group + +# SVE unary bit operations (predicated) +# Note esz != 0 for FABS and FNEG. +CLS 00000100 .. 011 000 101 ... ..... ..... @rd_pg_rn +CLZ 00000100 .. 011 001 101 ... ..... ..... @rd_pg_rn +CNT_zpz 00000100 .. 011 010 101 ... ..... ..... @rd_pg_rn +CNOT 00000100 .. 011 011 101 ... ..... ..... @rd_pg_rn +NOT_zpz 00000100 .. 011 110 101 ... ..... ..... @rd_pg_rn +FABS 00000100 .. 011 100 101 ... ..... ..... @rd_pg_rn +FNEG 00000100 .. 011 101 101 ... ..... ..... @rd_pg_rn + +# SVE integer unary operations (predicated) +# Note esz > original size for extensions. +ABS 00000100 .. 010 110 101 ... ..... ..... @rd_pg_rn +NEG 00000100 .. 010 111 101 ... ..... ..... @rd_pg_rn +SXTB 00000100 .. 010 000 101 ... ..... ..... @rd_pg_rn +UXTB 00000100 .. 010 001 101 ... ..... ..... @rd_pg_rn +SXTH 00000100 .. 010 010 101 ... ..... ..... @rd_pg_rn +UXTH 00000100 .. 010 011 101 ... ..... ..... @rd_pg_rn +SXTW 00000100 .. 010 100 101 ... ..... ..... @rd_pg_rn +UXTW 00000100 .. 010 101 101 ... ..... ..... @rd_pg_rn + +### SVE Floating Point Compare - Vectors Group + +# SVE floating-point compare vectors +FCMGE_ppzz 01100101 .. 0 ..... 010 ... ..... 0 .... @pd_pg_rn_rm +FCMGT_ppzz 01100101 .. 0 ..... 010 ... ..... 1 .... @pd_pg_rn_rm +FCMEQ_ppzz 01100101 .. 0 ..... 011 ... ..... 0 .... @pd_pg_rn_rm +FCMNE_ppzz 01100101 .. 0 ..... 011 ... ..... 1 .... @pd_pg_rn_rm +FCMUO_ppzz 01100101 .. 0 ..... 110 ... ..... 0 .... @pd_pg_rn_rm +FACGE_ppzz 01100101 .. 0 ..... 110 ... ..... 1 .... @pd_pg_rn_rm +FACGT_ppzz 01100101 .. 0 ..... 111 ... ..... 1 .... @pd_pg_rn_rm + +### SVE Integer Multiply-Add Group + +# SVE integer multiply-add writing addend (predicated) +MLA 00000100 .. 0 ..... 010 ... ..... ..... @rda_pg_rn_rm +MLS 00000100 .. 0 ..... 011 ... ..... ..... @rda_pg_rn_rm + +# SVE integer multiply-add writing multiplicand (predicated) +MLA 00000100 .. 0 ..... 110 ... ..... ..... @rdn_pg_ra_rm # MAD +MLS 00000100 .. 0 ..... 111 ... ..... ..... @rdn_pg_ra_rm # MSB + +### SVE Integer Arithmetic - Unpredicated Group + +# SVE integer add/subtract vectors (unpredicated) +ADD_zzz 00000100 .. 1 ..... 000 000 ..... ..... @rd_rn_rm +SUB_zzz 00000100 .. 1 ..... 000 001 ..... ..... @rd_rn_rm +SQADD_zzz 00000100 .. 1 ..... 000 100 ..... ..... @rd_rn_rm +UQADD_zzz 00000100 .. 1 ..... 000 101 ..... ..... @rd_rn_rm +SQSUB_zzz 00000100 .. 1 ..... 000 110 ..... ..... @rd_rn_rm +UQSUB_zzz 00000100 .. 1 ..... 000 111 ..... ..... @rd_rn_rm + +### SVE Logical - Unpredicated Group + +# SVE bitwise logical operations (unpredicated) +AND_zzz 00000100 00 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 +ORR_zzz 00000100 01 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 +EOR_zzz 00000100 10 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 +BIC_zzz 00000100 11 1 ..... 001 100 ..... ..... @rd_rn_rm_e0 + +### SVE Index Generation Group + +# SVE index generation (immediate start, immediate increment) +INDEX_ii 00000100 esz:2 1 imm2:s5 010000 imm1:s5 rd:5 + +# SVE index generation (immediate start, register increment) +INDEX_ir 00000100 esz:2 1 rm:5 010010 imm:s5 rd:5 + +# SVE index generation (register start, immediate increment) +INDEX_ri 00000100 esz:2 1 imm:s5 010001 rn:5 rd:5 + +# SVE index generation (register start, register increment) +INDEX_rr 00000100 .. 1 ..... 010011 ..... ..... @rd_rn_rm + +### SVE Stack Allocation Group + +# SVE stack frame adjustment +ADDVL 00000100 001 ..... 01010 ...... ..... @rd_rn_i6 +ADDPL 00000100 011 ..... 01010 ...... ..... @rd_rn_i6 + +# SVE stack frame size +RDVL 00000100 101 11111 01010 imm:s6 rd:5 + +### SVE Bitwise Shift - Unpredicated Group + +# SVE bitwise shift by immediate (unpredicated) +ASR_zzi 00000100 .. 1 ..... 1001 00 ..... ..... \ + @rd_rn_tszimm imm=%tszimm16_shr +LSR_zzi 00000100 .. 1 ..... 1001 01 ..... ..... \ + @rd_rn_tszimm imm=%tszimm16_shr +LSL_zzi 00000100 .. 1 ..... 1001 11 ..... ..... \ + @rd_rn_tszimm imm=%tszimm16_shl + +# SVE bitwise shift by wide elements (unpredicated) +# Note esz != 3 +ASR_zzw 00000100 .. 1 ..... 1000 00 ..... ..... @rd_rn_rm +LSR_zzw 00000100 .. 1 ..... 1000 01 ..... ..... @rd_rn_rm +LSL_zzw 00000100 .. 1 ..... 1000 11 ..... ..... @rd_rn_rm + +### SVE Compute Vector Address Group + +# SVE vector address generation +ADR_s32 00000100 00 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm +ADR_u32 00000100 01 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm +ADR_p32 00000100 10 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm +ADR_p64 00000100 11 1 ..... 1010 .. ..... ..... @rd_rn_msz_rm + +### SVE Integer Misc - Unpredicated Group + +# SVE constructive prefix (unpredicated) +MOVPRFX 00000100 00 1 00000 101111 rn:5 rd:5 + +# SVE floating-point exponential accelerator +# Note esz != 0 +FEXPA 00000100 .. 1 00000 101110 ..... ..... @rd_rn + +# SVE floating-point trig select coefficient +# Note esz != 0 +FTSSEL 00000100 .. 1 ..... 101100 ..... ..... @rd_rn_rm + +### SVE Element Count Group + +# SVE element count +CNT_r 00000100 .. 10 .... 1110 0 0 ..... ..... @incdec_cnt d=0 u=1 + +# SVE inc/dec register by element count +INCDEC_r 00000100 .. 11 .... 1110 0 d:1 ..... ..... @incdec_cnt u=1 + +# SVE saturating inc/dec register by element count +SINCDEC_r_32 00000100 .. 10 .... 1111 d:1 u:1 ..... ..... @incdec_cnt +SINCDEC_r_64 00000100 .. 11 .... 1111 d:1 u:1 ..... ..... @incdec_cnt + +# SVE inc/dec vector by element count +# Note this requires esz != 0. +INCDEC_v 00000100 .. 1 1 .... 1100 0 d:1 ..... ..... @incdec2_cnt u=1 + +# SVE saturating inc/dec vector by element count +# Note these require esz != 0. +SINCDEC_v 00000100 .. 1 0 .... 1100 d:1 u:1 ..... ..... @incdec2_cnt + +### SVE Bitwise Immediate Group + +# SVE bitwise logical with immediate (unpredicated) +ORR_zzi 00000101 00 0000 ............. ..... @rdn_dbm +EOR_zzi 00000101 01 0000 ............. ..... @rdn_dbm +AND_zzi 00000101 10 0000 ............. ..... @rdn_dbm + +# SVE broadcast bitmask immediate +DUPM 00000101 11 0000 dbm:13 rd:5 + +### SVE Integer Wide Immediate - Predicated Group + +# SVE copy floating-point immediate (predicated) +FCPY 00000101 .. 01 .... 110 imm:8 ..... @rdn_pg4 + +# SVE copy integer immediate (predicated) +CPY_m_i 00000101 .. 01 .... 01 . ........ ..... @rdn_pg4 imm=%sh8_i8s +CPY_z_i 00000101 .. 01 .... 00 . ........ ..... @rdn_pg4 imm=%sh8_i8s + +### SVE Permute - Extract Group + +# SVE extract vector (immediate offset) +EXT 00000101 001 ..... 000 ... rm:5 rd:5 \ + &rrri rn=%reg_movprfx imm=%imm8_16_10 + +### SVE Permute - Unpredicated Group + +# SVE broadcast general register +DUP_s 00000101 .. 1 00000 001110 ..... ..... @rd_rn + +# SVE broadcast indexed element +DUP_x 00000101 .. 1 ..... 001000 rn:5 rd:5 \ + &rri imm=%imm7_22_16 + +# SVE insert SIMD&FP scalar register +INSR_f 00000101 .. 1 10100 001110 ..... ..... @rdn_rm + +# SVE insert general register +INSR_r 00000101 .. 1 00100 001110 ..... ..... @rdn_rm + +# SVE reverse vector elements +REV_v 00000101 .. 1 11000 001110 ..... ..... @rd_rn + +# SVE vector table lookup +TBL 00000101 .. 1 ..... 001100 ..... ..... @rd_rn_rm + +# SVE unpack vector elements +UNPK 00000101 esz:2 1100 u:1 h:1 001110 rn:5 rd:5 + +### SVE Permute - Predicates Group + +# SVE permute predicate elements +ZIP1_p 00000101 .. 10 .... 010 000 0 .... 0 .... @pd_pn_pm +ZIP2_p 00000101 .. 10 .... 010 001 0 .... 0 .... @pd_pn_pm +UZP1_p 00000101 .. 10 .... 010 010 0 .... 0 .... @pd_pn_pm +UZP2_p 00000101 .. 10 .... 010 011 0 .... 0 .... @pd_pn_pm +TRN1_p 00000101 .. 10 .... 010 100 0 .... 0 .... @pd_pn_pm +TRN2_p 00000101 .. 10 .... 010 101 0 .... 0 .... @pd_pn_pm + +# SVE reverse predicate elements +REV_p 00000101 .. 11 0100 010 000 0 .... 0 .... @pd_pn + +# SVE unpack predicate elements +PUNPKLO 00000101 00 11 0000 010 000 0 .... 0 .... @pd_pn_e0 +PUNPKHI 00000101 00 11 0001 010 000 0 .... 0 .... @pd_pn_e0 + +### SVE Permute - Interleaving Group + +# SVE permute vector elements +ZIP1_z 00000101 .. 1 ..... 011 000 ..... ..... @rd_rn_rm +ZIP2_z 00000101 .. 1 ..... 011 001 ..... ..... @rd_rn_rm +UZP1_z 00000101 .. 1 ..... 011 010 ..... ..... @rd_rn_rm +UZP2_z 00000101 .. 1 ..... 011 011 ..... ..... @rd_rn_rm +TRN1_z 00000101 .. 1 ..... 011 100 ..... ..... @rd_rn_rm +TRN2_z 00000101 .. 1 ..... 011 101 ..... ..... @rd_rn_rm + +### SVE Permute - Predicated Group + +# SVE compress active elements +# Note esz >= 2 +COMPACT 00000101 .. 100001 100 ... ..... ..... @rd_pg_rn + +# SVE conditionally broadcast element to vector +CLASTA_z 00000101 .. 10100 0 100 ... ..... ..... @rdn_pg_rm +CLASTB_z 00000101 .. 10100 1 100 ... ..... ..... @rdn_pg_rm + +# SVE conditionally copy element to SIMD&FP scalar +CLASTA_v 00000101 .. 10101 0 100 ... ..... ..... @rd_pg_rn +CLASTB_v 00000101 .. 10101 1 100 ... ..... ..... @rd_pg_rn + +# SVE conditionally copy element to general register +CLASTA_r 00000101 .. 11000 0 101 ... ..... ..... @rd_pg_rn +CLASTB_r 00000101 .. 11000 1 101 ... ..... ..... @rd_pg_rn + +# SVE copy element to SIMD&FP scalar register +LASTA_v 00000101 .. 10001 0 100 ... ..... ..... @rd_pg_rn +LASTB_v 00000101 .. 10001 1 100 ... ..... ..... @rd_pg_rn + +# SVE copy element to general register +LASTA_r 00000101 .. 10000 0 101 ... ..... ..... @rd_pg_rn +LASTB_r 00000101 .. 10000 1 101 ... ..... ..... @rd_pg_rn + +# SVE copy element from SIMD&FP scalar register +CPY_m_v 00000101 .. 100000 100 ... ..... ..... @rd_pg_rn + +# SVE copy element from general register to vector (predicated) +CPY_m_r 00000101 .. 101000 101 ... ..... ..... @rd_pg_rn + +# SVE reverse within elements +# Note esz >= operation size +REVB 00000101 .. 1001 00 100 ... ..... ..... @rd_pg_rn +REVH 00000101 .. 1001 01 100 ... ..... ..... @rd_pg_rn +REVW 00000101 .. 1001 10 100 ... ..... ..... @rd_pg_rn +RBIT 00000101 .. 1001 11 100 ... ..... ..... @rd_pg_rn + +# SVE vector splice (predicated) +SPLICE 00000101 .. 101 100 100 ... ..... ..... @rdn_pg_rm + +### SVE Select Vectors Group + +# SVE select vector elements (predicated) +SEL_zpzz 00000101 .. 1 ..... 11 .... ..... ..... @rd_pg4_rn_rm + +### SVE Integer Compare - Vectors Group + +# SVE integer compare_vectors +CMPHS_ppzz 00100100 .. 0 ..... 000 ... ..... 0 .... @pd_pg_rn_rm +CMPHI_ppzz 00100100 .. 0 ..... 000 ... ..... 1 .... @pd_pg_rn_rm +CMPGE_ppzz 00100100 .. 0 ..... 100 ... ..... 0 .... @pd_pg_rn_rm +CMPGT_ppzz 00100100 .. 0 ..... 100 ... ..... 1 .... @pd_pg_rn_rm +CMPEQ_ppzz 00100100 .. 0 ..... 101 ... ..... 0 .... @pd_pg_rn_rm +CMPNE_ppzz 00100100 .. 0 ..... 101 ... ..... 1 .... @pd_pg_rn_rm + +# SVE integer compare with wide elements +# Note these require esz != 3. +CMPEQ_ppzw 00100100 .. 0 ..... 001 ... ..... 0 .... @pd_pg_rn_rm +CMPNE_ppzw 00100100 .. 0 ..... 001 ... ..... 1 .... @pd_pg_rn_rm +CMPGE_ppzw 00100100 .. 0 ..... 010 ... ..... 0 .... @pd_pg_rn_rm +CMPGT_ppzw 00100100 .. 0 ..... 010 ... ..... 1 .... @pd_pg_rn_rm +CMPLT_ppzw 00100100 .. 0 ..... 011 ... ..... 0 .... @pd_pg_rn_rm +CMPLE_ppzw 00100100 .. 0 ..... 011 ... ..... 1 .... @pd_pg_rn_rm +CMPHS_ppzw 00100100 .. 0 ..... 110 ... ..... 0 .... @pd_pg_rn_rm +CMPHI_ppzw 00100100 .. 0 ..... 110 ... ..... 1 .... @pd_pg_rn_rm +CMPLO_ppzw 00100100 .. 0 ..... 111 ... ..... 0 .... @pd_pg_rn_rm +CMPLS_ppzw 00100100 .. 0 ..... 111 ... ..... 1 .... @pd_pg_rn_rm + +### SVE Integer Compare - Unsigned Immediate Group + +# SVE integer compare with unsigned immediate +CMPHS_ppzi 00100100 .. 1 ....... 0 ... ..... 0 .... @pd_pg_rn_i7 +CMPHI_ppzi 00100100 .. 1 ....... 0 ... ..... 1 .... @pd_pg_rn_i7 +CMPLO_ppzi 00100100 .. 1 ....... 1 ... ..... 0 .... @pd_pg_rn_i7 +CMPLS_ppzi 00100100 .. 1 ....... 1 ... ..... 1 .... @pd_pg_rn_i7 + +### SVE Integer Compare - Signed Immediate Group + +# SVE integer compare with signed immediate +CMPGE_ppzi 00100101 .. 0 ..... 000 ... ..... 0 .... @pd_pg_rn_i5 +CMPGT_ppzi 00100101 .. 0 ..... 000 ... ..... 1 .... @pd_pg_rn_i5 +CMPLT_ppzi 00100101 .. 0 ..... 001 ... ..... 0 .... @pd_pg_rn_i5 +CMPLE_ppzi 00100101 .. 0 ..... 001 ... ..... 1 .... @pd_pg_rn_i5 +CMPEQ_ppzi 00100101 .. 0 ..... 100 ... ..... 0 .... @pd_pg_rn_i5 +CMPNE_ppzi 00100101 .. 0 ..... 100 ... ..... 1 .... @pd_pg_rn_i5 + +### SVE Predicate Logical Operations Group + +# SVE predicate logical operations +AND_pppp 00100101 0. 00 .... 01 .... 0 .... 0 .... @pd_pg_pn_pm_s +BIC_pppp 00100101 0. 00 .... 01 .... 0 .... 1 .... @pd_pg_pn_pm_s +EOR_pppp 00100101 0. 00 .... 01 .... 1 .... 0 .... @pd_pg_pn_pm_s +SEL_pppp 00100101 0. 00 .... 01 .... 1 .... 1 .... @pd_pg_pn_pm_s +ORR_pppp 00100101 1. 00 .... 01 .... 0 .... 0 .... @pd_pg_pn_pm_s +ORN_pppp 00100101 1. 00 .... 01 .... 0 .... 1 .... @pd_pg_pn_pm_s +NOR_pppp 00100101 1. 00 .... 01 .... 1 .... 0 .... @pd_pg_pn_pm_s +NAND_pppp 00100101 1. 00 .... 01 .... 1 .... 1 .... @pd_pg_pn_pm_s + +### SVE Predicate Misc Group + +# SVE predicate test +PTEST 00100101 01 010000 11 pg:4 0 rn:4 0 0000 + +# SVE predicate initialize +PTRUE 00100101 esz:2 01100 s:1 111000 pat:5 0 rd:4 + +# SVE initialize FFR +SETFFR 00100101 0010 1100 1001 0000 0000 0000 + +# SVE zero predicate register +PFALSE 00100101 0001 1000 1110 0100 0000 rd:4 + +# SVE predicate read from FFR (predicated) +RDFFR_p 00100101 0 s:1 0110001111000 pg:4 0 rd:4 + +# SVE predicate read from FFR (unpredicated) +RDFFR 00100101 0001 1001 1111 0000 0000 rd:4 + +# SVE FFR write from predicate (WRFFR) +WRFFR 00100101 0010 1000 1001 000 rn:4 00000 + +# SVE predicate first active +PFIRST 00100101 01 011 000 11000 00 .... 0 .... @pd_pn_e0 + +# SVE predicate next active +PNEXT 00100101 .. 011 001 11000 10 .... 0 .... @pd_pn + +### SVE Partition Break Group + +# SVE propagate break from previous partition +BRKPA 00100101 0. 00 .... 11 .... 0 .... 0 .... @pd_pg_pn_pm_s +BRKPB 00100101 0. 00 .... 11 .... 0 .... 1 .... @pd_pg_pn_pm_s + +# SVE partition break condition +BRKA_z 00100101 0. 01000001 .... 0 .... 0 .... @pd_pg_pn_s +BRKB_z 00100101 1. 01000001 .... 0 .... 0 .... @pd_pg_pn_s +BRKA_m 00100101 0. 01000001 .... 0 .... 1 .... @pd_pg_pn_s +BRKB_m 00100101 1. 01000001 .... 0 .... 1 .... @pd_pg_pn_s + +# SVE propagate break to next partition +BRKN 00100101 0. 01100001 .... 0 .... 0 .... @pd_pg_pn_s + +### SVE Predicate Count Group + +# SVE predicate count +CNTP 00100101 .. 100 000 10 .... 0 .... ..... @rd_pg4_pn + +# SVE inc/dec register by predicate count +INCDECP_r 00100101 .. 10110 d:1 10001 00 .... ..... @incdec_pred u=1 + +# SVE inc/dec vector by predicate count +INCDECP_z 00100101 .. 10110 d:1 10000 00 .... ..... @incdec2_pred u=1 + +# SVE saturating inc/dec register by predicate count +SINCDECP_r_32 00100101 .. 1010 d:1 u:1 10001 00 .... ..... @incdec_pred +SINCDECP_r_64 00100101 .. 1010 d:1 u:1 10001 10 .... ..... @incdec_pred + +# SVE saturating inc/dec vector by predicate count +SINCDECP_z 00100101 .. 1010 d:1 u:1 10000 00 .... ..... @incdec2_pred + +### SVE Integer Compare - Scalars Group + +# SVE conditionally terminate scalars +CTERM 00100101 1 sf:1 1 rm:5 001000 rn:5 ne:1 0000 + +# SVE integer compare scalar count and limit +WHILE 00100101 esz:2 1 rm:5 000 sf:1 u:1 1 rn:5 eq:1 rd:4 + +### SVE Integer Wide Immediate - Unpredicated Group + +# SVE broadcast floating-point immediate (unpredicated) +FDUP 00100101 esz:2 111 00 1110 imm:8 rd:5 + +# SVE broadcast integer immediate (unpredicated) +DUP_i 00100101 esz:2 111 00 011 . ........ rd:5 imm=%sh8_i8s + +# SVE integer add/subtract immediate (unpredicated) +ADD_zzi 00100101 .. 100 000 11 . ........ ..... @rdn_sh_i8u +SUB_zzi 00100101 .. 100 001 11 . ........ ..... @rdn_sh_i8u +SUBR_zzi 00100101 .. 100 011 11 . ........ ..... @rdn_sh_i8u +SQADD_zzi 00100101 .. 100 100 11 . ........ ..... @rdn_sh_i8u +UQADD_zzi 00100101 .. 100 101 11 . ........ ..... @rdn_sh_i8u +SQSUB_zzi 00100101 .. 100 110 11 . ........ ..... @rdn_sh_i8u +UQSUB_zzi 00100101 .. 100 111 11 . ........ ..... @rdn_sh_i8u + +# SVE integer min/max immediate (unpredicated) +SMAX_zzi 00100101 .. 101 000 110 ........ ..... @rdn_i8s +UMAX_zzi 00100101 .. 101 001 110 ........ ..... @rdn_i8u +SMIN_zzi 00100101 .. 101 010 110 ........ ..... @rdn_i8s +UMIN_zzi 00100101 .. 101 011 110 ........ ..... @rdn_i8u + +# SVE integer multiply immediate (unpredicated) +MUL_zzi 00100101 .. 110 000 110 ........ ..... @rdn_i8s + +# SVE integer dot product (unpredicated) +DOT_zzz 01000100 1 sz:1 0 rm:5 00000 u:1 rn:5 rd:5 ra=%reg_movprfx + +# SVE integer dot product (indexed) +DOT_zzx 01000100 101 index:2 rm:3 00000 u:1 rn:5 rd:5 \ + sz=0 ra=%reg_movprfx +DOT_zzx 01000100 111 index:1 rm:4 00000 u:1 rn:5 rd:5 \ + sz=1 ra=%reg_movprfx + +# SVE floating-point complex add (predicated) +FCADD 01100100 esz:2 00000 rot:1 100 pg:3 rm:5 rd:5 \ + rn=%reg_movprfx + +# SVE floating-point complex multiply-add (predicated) +FCMLA_zpzzz 01100100 esz:2 0 rm:5 0 rot:2 pg:3 rn:5 rd:5 \ + ra=%reg_movprfx + +# SVE floating-point complex multiply-add (indexed) +FCMLA_zzxz 01100100 10 1 index:2 rm:3 0001 rot:2 rn:5 rd:5 \ + ra=%reg_movprfx esz=1 +FCMLA_zzxz 01100100 11 1 index:1 rm:4 0001 rot:2 rn:5 rd:5 \ + ra=%reg_movprfx esz=2 + +### SVE FP Multiply-Add Indexed Group + +# SVE floating-point multiply-add (indexed) +FMLA_zzxz 01100100 0.1 .. rm:3 00000 sub:1 rn:5 rd:5 \ + ra=%reg_movprfx index=%index3_22_19 esz=1 +FMLA_zzxz 01100100 101 index:2 rm:3 00000 sub:1 rn:5 rd:5 \ + ra=%reg_movprfx esz=2 +FMLA_zzxz 01100100 111 index:1 rm:4 00000 sub:1 rn:5 rd:5 \ + ra=%reg_movprfx esz=3 + +### SVE FP Multiply Indexed Group + +# SVE floating-point multiply (indexed) +FMUL_zzx 01100100 0.1 .. rm:3 001000 rn:5 rd:5 \ + index=%index3_22_19 esz=1 +FMUL_zzx 01100100 101 index:2 rm:3 001000 rn:5 rd:5 esz=2 +FMUL_zzx 01100100 111 index:1 rm:4 001000 rn:5 rd:5 esz=3 + +### SVE FP Fast Reduction Group + +FADDV 01100101 .. 000 000 001 ... ..... ..... @rd_pg_rn +FMAXNMV 01100101 .. 000 100 001 ... ..... ..... @rd_pg_rn +FMINNMV 01100101 .. 000 101 001 ... ..... ..... @rd_pg_rn +FMAXV 01100101 .. 000 110 001 ... ..... ..... @rd_pg_rn +FMINV 01100101 .. 000 111 001 ... ..... ..... @rd_pg_rn + +## SVE Floating Point Unary Operations - Unpredicated Group + +FRECPE 01100101 .. 001 110 001100 ..... ..... @rd_rn +FRSQRTE 01100101 .. 001 111 001100 ..... ..... @rd_rn + +### SVE FP Compare with Zero Group + +FCMGE_ppz0 01100101 .. 0100 00 001 ... ..... 0 .... @pd_pg_rn +FCMGT_ppz0 01100101 .. 0100 00 001 ... ..... 1 .... @pd_pg_rn +FCMLT_ppz0 01100101 .. 0100 01 001 ... ..... 0 .... @pd_pg_rn +FCMLE_ppz0 01100101 .. 0100 01 001 ... ..... 1 .... @pd_pg_rn +FCMEQ_ppz0 01100101 .. 0100 10 001 ... ..... 0 .... @pd_pg_rn +FCMNE_ppz0 01100101 .. 0100 11 001 ... ..... 0 .... @pd_pg_rn + +### SVE FP Accumulating Reduction Group + +# SVE floating-point serial reduction (predicated) +FADDA 01100101 .. 011 000 001 ... ..... ..... @rdn_pg_rm + +### SVE Floating Point Arithmetic - Unpredicated Group + +# SVE floating-point arithmetic (unpredicated) +FADD_zzz 01100101 .. 0 ..... 000 000 ..... ..... @rd_rn_rm +FSUB_zzz 01100101 .. 0 ..... 000 001 ..... ..... @rd_rn_rm +FMUL_zzz 01100101 .. 0 ..... 000 010 ..... ..... @rd_rn_rm +FTSMUL 01100101 .. 0 ..... 000 011 ..... ..... @rd_rn_rm +FRECPS 01100101 .. 0 ..... 000 110 ..... ..... @rd_rn_rm +FRSQRTS 01100101 .. 0 ..... 000 111 ..... ..... @rd_rn_rm + +### SVE FP Arithmetic Predicated Group + +# SVE floating-point arithmetic (predicated) +FADD_zpzz 01100101 .. 00 0000 100 ... ..... ..... @rdn_pg_rm +FSUB_zpzz 01100101 .. 00 0001 100 ... ..... ..... @rdn_pg_rm +FMUL_zpzz 01100101 .. 00 0010 100 ... ..... ..... @rdn_pg_rm +FSUB_zpzz 01100101 .. 00 0011 100 ... ..... ..... @rdm_pg_rn # FSUBR +FMAXNM_zpzz 01100101 .. 00 0100 100 ... ..... ..... @rdn_pg_rm +FMINNM_zpzz 01100101 .. 00 0101 100 ... ..... ..... @rdn_pg_rm +FMAX_zpzz 01100101 .. 00 0110 100 ... ..... ..... @rdn_pg_rm +FMIN_zpzz 01100101 .. 00 0111 100 ... ..... ..... @rdn_pg_rm +FABD 01100101 .. 00 1000 100 ... ..... ..... @rdn_pg_rm +FSCALE 01100101 .. 00 1001 100 ... ..... ..... @rdn_pg_rm +FMULX 01100101 .. 00 1010 100 ... ..... ..... @rdn_pg_rm +FDIV 01100101 .. 00 1100 100 ... ..... ..... @rdm_pg_rn # FDIVR +FDIV 01100101 .. 00 1101 100 ... ..... ..... @rdn_pg_rm + +# SVE floating-point arithmetic with immediate (predicated) +FADD_zpzi 01100101 .. 011 000 100 ... 0000 . ..... @rdn_i1 +FSUB_zpzi 01100101 .. 011 001 100 ... 0000 . ..... @rdn_i1 +FMUL_zpzi 01100101 .. 011 010 100 ... 0000 . ..... @rdn_i1 +FSUBR_zpzi 01100101 .. 011 011 100 ... 0000 . ..... @rdn_i1 +FMAXNM_zpzi 01100101 .. 011 100 100 ... 0000 . ..... @rdn_i1 +FMINNM_zpzi 01100101 .. 011 101 100 ... 0000 . ..... @rdn_i1 +FMAX_zpzi 01100101 .. 011 110 100 ... 0000 . ..... @rdn_i1 +FMIN_zpzi 01100101 .. 011 111 100 ... 0000 . ..... @rdn_i1 + +# SVE floating-point trig multiply-add coefficient +FTMAD 01100101 esz:2 010 imm:3 100000 rm:5 rd:5 rn=%reg_movprfx + +### SVE FP Multiply-Add Group + +# SVE floating-point multiply-accumulate writing addend +FMLA_zpzzz 01100101 .. 1 ..... 000 ... ..... ..... @rda_pg_rn_rm +FMLS_zpzzz 01100101 .. 1 ..... 001 ... ..... ..... @rda_pg_rn_rm +FNMLA_zpzzz 01100101 .. 1 ..... 010 ... ..... ..... @rda_pg_rn_rm +FNMLS_zpzzz 01100101 .. 1 ..... 011 ... ..... ..... @rda_pg_rn_rm + +# SVE floating-point multiply-accumulate writing multiplicand +# Alter the operand extraction order and reuse the helpers from above. +# FMAD, FMSB, FNMAD, FNMS +FMLA_zpzzz 01100101 .. 1 ..... 100 ... ..... ..... @rdn_pg_rm_ra +FMLS_zpzzz 01100101 .. 1 ..... 101 ... ..... ..... @rdn_pg_rm_ra +FNMLA_zpzzz 01100101 .. 1 ..... 110 ... ..... ..... @rdn_pg_rm_ra +FNMLS_zpzzz 01100101 .. 1 ..... 111 ... ..... ..... @rdn_pg_rm_ra + +### SVE FP Unary Operations Predicated Group + +# SVE floating-point convert precision +FCVT_sh 01100101 10 0010 00 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_hs 01100101 10 0010 01 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_dh 01100101 11 0010 00 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_hd 01100101 11 0010 01 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_ds 01100101 11 0010 10 101 ... ..... ..... @rd_pg_rn_e0 +FCVT_sd 01100101 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 + +# SVE floating-point convert to integer +FCVTZS_hh 01100101 01 011 01 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hh 01100101 01 011 01 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hs 01100101 01 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hs 01100101 01 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_hd 01100101 01 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_hd 01100101 01 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_ss 01100101 10 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_ss 01100101 10 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_ds 01100101 11 011 00 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_ds 01100101 11 011 00 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_sd 01100101 11 011 10 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_sd 01100101 11 011 10 1 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZS_dd 01100101 11 011 11 0 101 ... ..... ..... @rd_pg_rn_e0 +FCVTZU_dd 01100101 11 011 11 1 101 ... ..... ..... @rd_pg_rn_e0 + +# SVE floating-point round to integral value +FRINTN 01100101 .. 000 000 101 ... ..... ..... @rd_pg_rn +FRINTP 01100101 .. 000 001 101 ... ..... ..... @rd_pg_rn +FRINTM 01100101 .. 000 010 101 ... ..... ..... @rd_pg_rn +FRINTZ 01100101 .. 000 011 101 ... ..... ..... @rd_pg_rn +FRINTA 01100101 .. 000 100 101 ... ..... ..... @rd_pg_rn +FRINTX 01100101 .. 000 110 101 ... ..... ..... @rd_pg_rn +FRINTI 01100101 .. 000 111 101 ... ..... ..... @rd_pg_rn + +# SVE floating-point unary operations +FRECPX 01100101 .. 001 100 101 ... ..... ..... @rd_pg_rn +FSQRT 01100101 .. 001 101 101 ... ..... ..... @rd_pg_rn + +# SVE integer convert to floating-point +SCVTF_hh 01100101 01 010 01 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_sh 01100101 01 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_dh 01100101 01 010 11 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_ss 01100101 10 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_sd 01100101 11 010 00 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_ds 01100101 11 010 10 0 101 ... ..... ..... @rd_pg_rn_e0 +SCVTF_dd 01100101 11 010 11 0 101 ... ..... ..... @rd_pg_rn_e0 + +UCVTF_hh 01100101 01 010 01 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_sh 01100101 01 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_dh 01100101 01 010 11 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_ss 01100101 10 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_sd 01100101 11 010 00 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_ds 01100101 11 010 10 1 101 ... ..... ..... @rd_pg_rn_e0 +UCVTF_dd 01100101 11 010 11 1 101 ... ..... ..... @rd_pg_rn_e0 + +### SVE Memory - 32-bit Gather and Unsized Contiguous Group + +# SVE load predicate register +LDR_pri 10000101 10 ...... 000 ... ..... 0 .... @pd_rn_i9 + +# SVE load vector register +LDR_zri 10000101 10 ...... 010 ... ..... ..... @rd_rn_i9 + +# SVE load and broadcast element +LD1R_zpri 1000010 .. 1 imm:6 1.. pg:3 rn:5 rd:5 \ + &rpri_load dtype=%dtype_23_13 nreg=0 + +# SVE 32-bit gather load (scalar plus 32-bit unscaled offsets) +# SVE 32-bit gather load (scalar plus 32-bit scaled offsets) +LD1_zprz 1000010 00 .0 ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u esz=2 msz=0 scale=0 +LD1_zprz 1000010 01 .. ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u_sc esz=2 msz=1 +LD1_zprz 1000010 10 .. ..... 01. ... ..... ..... \ + @rprr_g_load_xs_sc esz=2 msz=2 u=1 + +# SVE 32-bit gather load (vector plus immediate) +LD1_zpiz 1000010 .. 01 ..... 1.. ... ..... ..... \ + @rpri_g_load esz=2 + +### SVE Memory Contiguous Load Group + +# SVE contiguous load (scalar plus scalar) +LD_zprr 1010010 .... ..... 010 ... ..... ..... @rprr_load_dt nreg=0 + +# SVE contiguous first-fault load (scalar plus scalar) +LDFF1_zprr 1010010 .... ..... 011 ... ..... ..... @rprr_load_dt nreg=0 + +# SVE contiguous load (scalar plus immediate) +LD_zpri 1010010 .... 0.... 101 ... ..... ..... @rpri_load_dt nreg=0 + +# SVE contiguous non-fault load (scalar plus immediate) +LDNF1_zpri 1010010 .... 1.... 101 ... ..... ..... @rpri_load_dt nreg=0 + +# SVE contiguous non-temporal load (scalar plus scalar) +# LDNT1B, LDNT1H, LDNT1W, LDNT1D +# SVE load multiple structures (scalar plus scalar) +# LD2B, LD2H, LD2W, LD2D; etc. +LD_zprr 1010010 .. nreg:2 ..... 110 ... ..... ..... @rprr_load_msz + +# SVE contiguous non-temporal load (scalar plus immediate) +# LDNT1B, LDNT1H, LDNT1W, LDNT1D +# SVE load multiple structures (scalar plus immediate) +# LD2B, LD2H, LD2W, LD2D; etc. +LD_zpri 1010010 .. nreg:2 0.... 111 ... ..... ..... @rpri_load_msz + +# SVE load and broadcast quadword (scalar plus scalar) +LD1RQ_zprr 1010010 .. 00 ..... 000 ... ..... ..... \ + @rprr_load_msz nreg=0 + +# SVE load and broadcast quadword (scalar plus immediate) +# LD1RQB, LD1RQH, LD1RQS, LD1RQD +LD1RQ_zpri 1010010 .. 00 0.... 001 ... ..... ..... \ + @rpri_load_msz nreg=0 + +# SVE 32-bit gather prefetch (scalar plus 32-bit scaled offsets) +PRF 1000010 00 -1 ----- 0-- --- ----- 0 ---- + +# SVE 32-bit gather prefetch (vector plus immediate) +PRF 1000010 -- 00 ----- 111 --- ----- 0 ---- + +# SVE contiguous prefetch (scalar plus immediate) +PRF 1000010 11 1- ----- 0-- --- ----- 0 ---- + +# SVE contiguous prefetch (scalar plus scalar) +PRF_rr 1000010 -- 00 rm:5 110 --- ----- 0 ---- + +### SVE Memory 64-bit Gather Group + +# SVE 64-bit gather load (scalar plus 32-bit unpacked unscaled offsets) +# SVE 64-bit gather load (scalar plus 32-bit unpacked scaled offsets) +LD1_zprz 1100010 00 .0 ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u esz=3 msz=0 scale=0 +LD1_zprz 1100010 01 .. ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u_sc esz=3 msz=1 +LD1_zprz 1100010 10 .. ..... 0.. ... ..... ..... \ + @rprr_g_load_xs_u_sc esz=3 msz=2 +LD1_zprz 1100010 11 .. ..... 01. ... ..... ..... \ + @rprr_g_load_xs_sc esz=3 msz=3 u=1 + +# SVE 64-bit gather load (scalar plus 64-bit unscaled offsets) +# SVE 64-bit gather load (scalar plus 64-bit scaled offsets) +LD1_zprz 1100010 00 10 ..... 1.. ... ..... ..... \ + @rprr_g_load_u esz=3 msz=0 scale=0 +LD1_zprz 1100010 01 1. ..... 1.. ... ..... ..... \ + @rprr_g_load_u_sc esz=3 msz=1 +LD1_zprz 1100010 10 1. ..... 1.. ... ..... ..... \ + @rprr_g_load_u_sc esz=3 msz=2 +LD1_zprz 1100010 11 1. ..... 11. ... ..... ..... \ + @rprr_g_load_sc esz=3 msz=3 u=1 + +# SVE 64-bit gather load (vector plus immediate) +LD1_zpiz 1100010 .. 01 ..... 1.. ... ..... ..... \ + @rpri_g_load esz=3 + +# SVE 64-bit gather prefetch (scalar plus 64-bit scaled offsets) +PRF 1100010 00 11 ----- 1-- --- ----- 0 ---- + +# SVE 64-bit gather prefetch (scalar plus unpacked 32-bit scaled offsets) +PRF 1100010 00 -1 ----- 0-- --- ----- 0 ---- + +# SVE 64-bit gather prefetch (vector plus immediate) +PRF 1100010 -- 00 ----- 111 --- ----- 0 ---- + +### SVE Memory Store Group + +# SVE store predicate register +STR_pri 1110010 11 0. ..... 000 ... ..... 0 .... @pd_rn_i9 + +# SVE store vector register +STR_zri 1110010 11 0. ..... 010 ... ..... ..... @rd_rn_i9 + +# SVE contiguous store (scalar plus immediate) +# ST1B, ST1H, ST1W, ST1D; require msz <= esz +ST_zpri 1110010 .. esz:2 0.... 111 ... ..... ..... \ + @rpri_store_msz nreg=0 + +# SVE contiguous store (scalar plus scalar) +# ST1B, ST1H, ST1W, ST1D; require msz <= esz +# Enumerate msz lest we conflict with STR_zri. +ST_zprr 1110010 00 .. ..... 010 ... ..... ..... \ + @rprr_store_esz_n0 msz=0 +ST_zprr 1110010 01 .. ..... 010 ... ..... ..... \ + @rprr_store_esz_n0 msz=1 +ST_zprr 1110010 10 .. ..... 010 ... ..... ..... \ + @rprr_store_esz_n0 msz=2 +ST_zprr 1110010 11 11 ..... 010 ... ..... ..... \ + @rprr_store msz=3 esz=3 nreg=0 + +# SVE contiguous non-temporal store (scalar plus immediate) (nreg == 0) +# SVE store multiple structures (scalar plus immediate) (nreg != 0) +ST_zpri 1110010 .. nreg:2 1.... 111 ... ..... ..... \ + @rpri_store_msz esz=%size_23 + +# SVE contiguous non-temporal store (scalar plus scalar) (nreg == 0) +# SVE store multiple structures (scalar plus scalar) (nreg != 0) +ST_zprr 1110010 msz:2 nreg:2 ..... 011 ... ..... ..... \ + @rprr_store esz=%size_23 + +# SVE 32-bit scatter store (scalar plus 32-bit scaled offsets) +# Require msz > 0 && msz <= esz. +ST1_zprz 1110010 .. 11 ..... 100 ... ..... ..... \ + @rprr_scatter_store xs=0 esz=2 scale=1 +ST1_zprz 1110010 .. 11 ..... 110 ... ..... ..... \ + @rprr_scatter_store xs=1 esz=2 scale=1 + +# SVE 32-bit scatter store (scalar plus 32-bit unscaled offsets) +# Require msz <= esz. +ST1_zprz 1110010 .. 10 ..... 100 ... ..... ..... \ + @rprr_scatter_store xs=0 esz=2 scale=0 +ST1_zprz 1110010 .. 10 ..... 110 ... ..... ..... \ + @rprr_scatter_store xs=1 esz=2 scale=0 + +# SVE 64-bit scatter store (scalar plus 64-bit scaled offset) +# Require msz > 0 +ST1_zprz 1110010 .. 01 ..... 101 ... ..... ..... \ + @rprr_scatter_store xs=2 esz=3 scale=1 + +# SVE 64-bit scatter store (scalar plus 64-bit unscaled offset) +ST1_zprz 1110010 .. 00 ..... 101 ... ..... ..... \ + @rprr_scatter_store xs=2 esz=3 scale=0 + +# SVE 64-bit scatter store (vector plus immediate) +ST1_zpiz 1110010 .. 10 ..... 101 ... ..... ..... \ + @rpri_scatter_store esz=3 + +# SVE 32-bit scatter store (vector plus immediate) +ST1_zpiz 1110010 .. 11 ..... 101 ... ..... ..... \ + @rpri_scatter_store esz=2 + +# SVE 64-bit scatter store (scalar plus unpacked 32-bit scaled offset) +# Require msz > 0 +ST1_zprz 1110010 .. 01 ..... 100 ... ..... ..... \ + @rprr_scatter_store xs=0 esz=3 scale=1 +ST1_zprz 1110010 .. 01 ..... 110 ... ..... ..... \ + @rprr_scatter_store xs=1 esz=3 scale=1 + +# SVE 64-bit scatter store (scalar plus unpacked 32-bit unscaled offset) +ST1_zprz 1110010 .. 00 ..... 100 ... ..... ..... \ + @rprr_scatter_store xs=0 esz=3 scale=0 +ST1_zprz 1110010 .. 00 ..... 110 ... ..... ..... \ + @rprr_scatter_store xs=1 esz=3 scale=0 diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c new file mode 100644 index 0000000000..54795c9194 --- /dev/null +++ b/target/arm/sve_helper.c @@ -0,0 +1,4677 @@ +/* + * ARM SVE Operations + * + * Copyright (c) 2018 Linaro, Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "tcg/tcg-gvec-desc.h" +#include "fpu/softfloat.h" + + +/* Note that vector data is stored in host-endian 64-bit chunks, + so addressing units smaller than that needs a host-endian fixup. */ +#ifdef HOST_WORDS_BIGENDIAN +#define H1(x) ((x) ^ 7) +#define H1_2(x) ((x) ^ 6) +#define H1_4(x) ((x) ^ 4) +#define H2(x) ((x) ^ 3) +#define H4(x) ((x) ^ 1) +#else +#define H1(x) (x) +#define H1_2(x) (x) +#define H1_4(x) (x) +#define H2(x) (x) +#define H4(x) (x) +#endif + +/* Return a value for NZCV as per the ARM PredTest pseudofunction. + * + * The return value has bit 31 set if N is set, bit 1 set if Z is clear, + * and bit 0 set if C is set. Compare the definitions of these variables + * within CPUARMState. + */ + +/* For no G bits set, NZCV = C. */ +#define PREDTEST_INIT 1 + +/* This is an iterative function, called for each Pd and Pg word + * moving forward. + */ +static uint32_t iter_predtest_fwd(uint64_t d, uint64_t g, uint32_t flags) +{ + if (likely(g)) { + /* Compute N from first D & G. + Use bit 2 to signal first G bit seen. */ + if (!(flags & 4)) { + flags |= ((d & (g & -g)) != 0) << 31; + flags |= 4; + } + + /* Accumulate Z from each D & G. */ + flags |= ((d & g) != 0) << 1; + + /* Compute C from last !(D & G). Replace previous. */ + flags = deposit32(flags, 0, 1, (d & pow2floor(g)) == 0); + } + return flags; +} + +/* This is an iterative function, called for each Pd and Pg word + * moving backward. + */ +static uint32_t iter_predtest_bwd(uint64_t d, uint64_t g, uint32_t flags) +{ + if (likely(g)) { + /* Compute C from first (i.e last) !(D & G). + Use bit 2 to signal first G bit seen. */ + if (!(flags & 4)) { + flags += 4 - 1; /* add bit 2, subtract C from PREDTEST_INIT */ + flags |= (d & pow2floor(g)) == 0; + } + + /* Accumulate Z from each D & G. */ + flags |= ((d & g) != 0) << 1; + + /* Compute N from last (i.e first) D & G. Replace previous. */ + flags = deposit32(flags, 31, 1, (d & (g & -g)) != 0); + } + return flags; +} + +/* The same for a single word predicate. */ +uint32_t HELPER(sve_predtest1)(uint64_t d, uint64_t g) +{ + return iter_predtest_fwd(d, g, PREDTEST_INIT); +} + +/* The same for a multi-word predicate. */ +uint32_t HELPER(sve_predtest)(void *vd, void *vg, uint32_t words) +{ + uint32_t flags = PREDTEST_INIT; + uint64_t *d = vd, *g = vg; + uintptr_t i = 0; + + do { + flags = iter_predtest_fwd(d[i], g[i], flags); + } while (++i < words); + + return flags; +} + +/* Expand active predicate bits to bytes, for byte elements. + * for (i = 0; i < 256; ++i) { + * unsigned long m = 0; + * for (j = 0; j < 8; j++) { + * if ((i >> j) & 1) { + * m |= 0xfful << (j << 3); + * } + * } + * printf("0x%016lx,\n", m); + * } + */ +static inline uint64_t expand_pred_b(uint8_t byte) +{ + static const uint64_t word[256] = { + 0x0000000000000000, 0x00000000000000ff, 0x000000000000ff00, + 0x000000000000ffff, 0x0000000000ff0000, 0x0000000000ff00ff, + 0x0000000000ffff00, 0x0000000000ffffff, 0x00000000ff000000, + 0x00000000ff0000ff, 0x00000000ff00ff00, 0x00000000ff00ffff, + 0x00000000ffff0000, 0x00000000ffff00ff, 0x00000000ffffff00, + 0x00000000ffffffff, 0x000000ff00000000, 0x000000ff000000ff, + 0x000000ff0000ff00, 0x000000ff0000ffff, 0x000000ff00ff0000, + 0x000000ff00ff00ff, 0x000000ff00ffff00, 0x000000ff00ffffff, + 0x000000ffff000000, 0x000000ffff0000ff, 0x000000ffff00ff00, + 0x000000ffff00ffff, 0x000000ffffff0000, 0x000000ffffff00ff, + 0x000000ffffffff00, 0x000000ffffffffff, 0x0000ff0000000000, + 0x0000ff00000000ff, 0x0000ff000000ff00, 0x0000ff000000ffff, + 0x0000ff0000ff0000, 0x0000ff0000ff00ff, 0x0000ff0000ffff00, + 0x0000ff0000ffffff, 0x0000ff00ff000000, 0x0000ff00ff0000ff, + 0x0000ff00ff00ff00, 0x0000ff00ff00ffff, 0x0000ff00ffff0000, + 0x0000ff00ffff00ff, 0x0000ff00ffffff00, 0x0000ff00ffffffff, + 0x0000ffff00000000, 0x0000ffff000000ff, 0x0000ffff0000ff00, + 0x0000ffff0000ffff, 0x0000ffff00ff0000, 0x0000ffff00ff00ff, + 0x0000ffff00ffff00, 0x0000ffff00ffffff, 0x0000ffffff000000, + 0x0000ffffff0000ff, 0x0000ffffff00ff00, 0x0000ffffff00ffff, + 0x0000ffffffff0000, 0x0000ffffffff00ff, 0x0000ffffffffff00, + 0x0000ffffffffffff, 0x00ff000000000000, 0x00ff0000000000ff, + 0x00ff00000000ff00, 0x00ff00000000ffff, 0x00ff000000ff0000, + 0x00ff000000ff00ff, 0x00ff000000ffff00, 0x00ff000000ffffff, + 0x00ff0000ff000000, 0x00ff0000ff0000ff, 0x00ff0000ff00ff00, + 0x00ff0000ff00ffff, 0x00ff0000ffff0000, 0x00ff0000ffff00ff, + 0x00ff0000ffffff00, 0x00ff0000ffffffff, 0x00ff00ff00000000, + 0x00ff00ff000000ff, 0x00ff00ff0000ff00, 0x00ff00ff0000ffff, + 0x00ff00ff00ff0000, 0x00ff00ff00ff00ff, 0x00ff00ff00ffff00, + 0x00ff00ff00ffffff, 0x00ff00ffff000000, 0x00ff00ffff0000ff, + 0x00ff00ffff00ff00, 0x00ff00ffff00ffff, 0x00ff00ffffff0000, + 0x00ff00ffffff00ff, 0x00ff00ffffffff00, 0x00ff00ffffffffff, + 0x00ffff0000000000, 0x00ffff00000000ff, 0x00ffff000000ff00, + 0x00ffff000000ffff, 0x00ffff0000ff0000, 0x00ffff0000ff00ff, + 0x00ffff0000ffff00, 0x00ffff0000ffffff, 0x00ffff00ff000000, + 0x00ffff00ff0000ff, 0x00ffff00ff00ff00, 0x00ffff00ff00ffff, + 0x00ffff00ffff0000, 0x00ffff00ffff00ff, 0x00ffff00ffffff00, + 0x00ffff00ffffffff, 0x00ffffff00000000, 0x00ffffff000000ff, + 0x00ffffff0000ff00, 0x00ffffff0000ffff, 0x00ffffff00ff0000, + 0x00ffffff00ff00ff, 0x00ffffff00ffff00, 0x00ffffff00ffffff, + 0x00ffffffff000000, 0x00ffffffff0000ff, 0x00ffffffff00ff00, + 0x00ffffffff00ffff, 0x00ffffffffff0000, 0x00ffffffffff00ff, + 0x00ffffffffffff00, 0x00ffffffffffffff, 0xff00000000000000, + 0xff000000000000ff, 0xff0000000000ff00, 0xff0000000000ffff, + 0xff00000000ff0000, 0xff00000000ff00ff, 0xff00000000ffff00, + 0xff00000000ffffff, 0xff000000ff000000, 0xff000000ff0000ff, + 0xff000000ff00ff00, 0xff000000ff00ffff, 0xff000000ffff0000, + 0xff000000ffff00ff, 0xff000000ffffff00, 0xff000000ffffffff, + 0xff0000ff00000000, 0xff0000ff000000ff, 0xff0000ff0000ff00, + 0xff0000ff0000ffff, 0xff0000ff00ff0000, 0xff0000ff00ff00ff, + 0xff0000ff00ffff00, 0xff0000ff00ffffff, 0xff0000ffff000000, + 0xff0000ffff0000ff, 0xff0000ffff00ff00, 0xff0000ffff00ffff, + 0xff0000ffffff0000, 0xff0000ffffff00ff, 0xff0000ffffffff00, + 0xff0000ffffffffff, 0xff00ff0000000000, 0xff00ff00000000ff, + 0xff00ff000000ff00, 0xff00ff000000ffff, 0xff00ff0000ff0000, + 0xff00ff0000ff00ff, 0xff00ff0000ffff00, 0xff00ff0000ffffff, + 0xff00ff00ff000000, 0xff00ff00ff0000ff, 0xff00ff00ff00ff00, + 0xff00ff00ff00ffff, 0xff00ff00ffff0000, 0xff00ff00ffff00ff, + 0xff00ff00ffffff00, 0xff00ff00ffffffff, 0xff00ffff00000000, + 0xff00ffff000000ff, 0xff00ffff0000ff00, 0xff00ffff0000ffff, + 0xff00ffff00ff0000, 0xff00ffff00ff00ff, 0xff00ffff00ffff00, + 0xff00ffff00ffffff, 0xff00ffffff000000, 0xff00ffffff0000ff, + 0xff00ffffff00ff00, 0xff00ffffff00ffff, 0xff00ffffffff0000, + 0xff00ffffffff00ff, 0xff00ffffffffff00, 0xff00ffffffffffff, + 0xffff000000000000, 0xffff0000000000ff, 0xffff00000000ff00, + 0xffff00000000ffff, 0xffff000000ff0000, 0xffff000000ff00ff, + 0xffff000000ffff00, 0xffff000000ffffff, 0xffff0000ff000000, + 0xffff0000ff0000ff, 0xffff0000ff00ff00, 0xffff0000ff00ffff, + 0xffff0000ffff0000, 0xffff0000ffff00ff, 0xffff0000ffffff00, + 0xffff0000ffffffff, 0xffff00ff00000000, 0xffff00ff000000ff, + 0xffff00ff0000ff00, 0xffff00ff0000ffff, 0xffff00ff00ff0000, + 0xffff00ff00ff00ff, 0xffff00ff00ffff00, 0xffff00ff00ffffff, + 0xffff00ffff000000, 0xffff00ffff0000ff, 0xffff00ffff00ff00, + 0xffff00ffff00ffff, 0xffff00ffffff0000, 0xffff00ffffff00ff, + 0xffff00ffffffff00, 0xffff00ffffffffff, 0xffffff0000000000, + 0xffffff00000000ff, 0xffffff000000ff00, 0xffffff000000ffff, + 0xffffff0000ff0000, 0xffffff0000ff00ff, 0xffffff0000ffff00, + 0xffffff0000ffffff, 0xffffff00ff000000, 0xffffff00ff0000ff, + 0xffffff00ff00ff00, 0xffffff00ff00ffff, 0xffffff00ffff0000, + 0xffffff00ffff00ff, 0xffffff00ffffff00, 0xffffff00ffffffff, + 0xffffffff00000000, 0xffffffff000000ff, 0xffffffff0000ff00, + 0xffffffff0000ffff, 0xffffffff00ff0000, 0xffffffff00ff00ff, + 0xffffffff00ffff00, 0xffffffff00ffffff, 0xffffffffff000000, + 0xffffffffff0000ff, 0xffffffffff00ff00, 0xffffffffff00ffff, + 0xffffffffffff0000, 0xffffffffffff00ff, 0xffffffffffffff00, + 0xffffffffffffffff, + }; + return word[byte]; +} + +/* Similarly for half-word elements. + * for (i = 0; i < 256; ++i) { + * unsigned long m = 0; + * if (i & 0xaa) { + * continue; + * } + * for (j = 0; j < 8; j += 2) { + * if ((i >> j) & 1) { + * m |= 0xfffful << (j << 3); + * } + * } + * printf("[0x%x] = 0x%016lx,\n", i, m); + * } + */ +static inline uint64_t expand_pred_h(uint8_t byte) +{ + static const uint64_t word[] = { + [0x01] = 0x000000000000ffff, [0x04] = 0x00000000ffff0000, + [0x05] = 0x00000000ffffffff, [0x10] = 0x0000ffff00000000, + [0x11] = 0x0000ffff0000ffff, [0x14] = 0x0000ffffffff0000, + [0x15] = 0x0000ffffffffffff, [0x40] = 0xffff000000000000, + [0x41] = 0xffff00000000ffff, [0x44] = 0xffff0000ffff0000, + [0x45] = 0xffff0000ffffffff, [0x50] = 0xffffffff00000000, + [0x51] = 0xffffffff0000ffff, [0x54] = 0xffffffffffff0000, + [0x55] = 0xffffffffffffffff, + }; + return word[byte & 0x55]; +} + +/* Similarly for single word elements. */ +static inline uint64_t expand_pred_s(uint8_t byte) +{ + static const uint64_t word[] = { + [0x01] = 0x00000000ffffffffull, + [0x10] = 0xffffffff00000000ull, + [0x11] = 0xffffffffffffffffull, + }; + return word[byte & 0x11]; +} + +/* Swap 16-bit words within a 32-bit word. */ +static inline uint32_t hswap32(uint32_t h) +{ + return rol32(h, 16); +} + +/* Swap 16-bit words within a 64-bit word. */ +static inline uint64_t hswap64(uint64_t h) +{ + uint64_t m = 0x0000ffff0000ffffull; + h = rol64(h, 32); + return ((h & m) << 16) | ((h >> 16) & m); +} + +/* Swap 32-bit words within a 64-bit word. */ +static inline uint64_t wswap64(uint64_t h) +{ + return rol64(h, 32); +} + +#define LOGICAL_PPPP(NAME, FUNC) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + uintptr_t opr_sz = simd_oprsz(desc); \ + uint64_t *d = vd, *n = vn, *m = vm, *g = vg; \ + uintptr_t i; \ + for (i = 0; i < opr_sz / 8; ++i) { \ + d[i] = FUNC(n[i], m[i], g[i]); \ + } \ +} + +#define DO_AND(N, M, G) (((N) & (M)) & (G)) +#define DO_BIC(N, M, G) (((N) & ~(M)) & (G)) +#define DO_EOR(N, M, G) (((N) ^ (M)) & (G)) +#define DO_ORR(N, M, G) (((N) | (M)) & (G)) +#define DO_ORN(N, M, G) (((N) | ~(M)) & (G)) +#define DO_NOR(N, M, G) (~((N) | (M)) & (G)) +#define DO_NAND(N, M, G) (~((N) & (M)) & (G)) +#define DO_SEL(N, M, G) (((N) & (G)) | ((M) & ~(G))) + +LOGICAL_PPPP(sve_and_pppp, DO_AND) +LOGICAL_PPPP(sve_bic_pppp, DO_BIC) +LOGICAL_PPPP(sve_eor_pppp, DO_EOR) +LOGICAL_PPPP(sve_sel_pppp, DO_SEL) +LOGICAL_PPPP(sve_orr_pppp, DO_ORR) +LOGICAL_PPPP(sve_orn_pppp, DO_ORN) +LOGICAL_PPPP(sve_nor_pppp, DO_NOR) +LOGICAL_PPPP(sve_nand_pppp, DO_NAND) + +#undef DO_AND +#undef DO_BIC +#undef DO_EOR +#undef DO_ORR +#undef DO_ORN +#undef DO_NOR +#undef DO_NAND +#undef DO_SEL +#undef LOGICAL_PPPP + +/* Fully general three-operand expander, controlled by a predicate. + * This is complicated by the host-endian storage of the register file. + */ +/* ??? I don't expect the compiler could ever vectorize this itself. + * With some tables we can convert bit masks to byte masks, and with + * extra care wrt byte/word ordering we could use gcc generic vectors + * and do 16 bytes at a time. + */ +#define DO_ZPZZ(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +/* Similarly, specialized for 64-bit operands. */ +#define DO_ZPZZ_D(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPE *d = vd, *n = vn, *m = vm; \ + uint8_t *pg = vg; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPE nn = n[i], mm = m[i]; \ + d[i] = OP(nn, mm); \ + } \ + } \ +} + +#define DO_AND(N, M) (N & M) +#define DO_EOR(N, M) (N ^ M) +#define DO_ORR(N, M) (N | M) +#define DO_BIC(N, M) (N & ~M) +#define DO_ADD(N, M) (N + M) +#define DO_SUB(N, M) (N - M) +#define DO_MAX(N, M) ((N) >= (M) ? (N) : (M)) +#define DO_MIN(N, M) ((N) >= (M) ? (M) : (N)) +#define DO_ABD(N, M) ((N) >= (M) ? (N) - (M) : (M) - (N)) +#define DO_MUL(N, M) (N * M) + + +/* + * We must avoid the C undefined behaviour cases: division by + * zero and signed division of INT_MIN by -1. Both of these + * have architecturally defined required results for Arm. + * We special case all signed divisions by -1 to avoid having + * to deduce the minimum integer for the type involved. + */ +#define DO_SDIV(N, M) (unlikely(M == 0) ? 0 : unlikely(M == -1) ? -N : N / M) +#define DO_UDIV(N, M) (unlikely(M == 0) ? 0 : N / M) + +DO_ZPZZ(sve_and_zpzz_b, uint8_t, H1, DO_AND) +DO_ZPZZ(sve_and_zpzz_h, uint16_t, H1_2, DO_AND) +DO_ZPZZ(sve_and_zpzz_s, uint32_t, H1_4, DO_AND) +DO_ZPZZ_D(sve_and_zpzz_d, uint64_t, DO_AND) + +DO_ZPZZ(sve_orr_zpzz_b, uint8_t, H1, DO_ORR) +DO_ZPZZ(sve_orr_zpzz_h, uint16_t, H1_2, DO_ORR) +DO_ZPZZ(sve_orr_zpzz_s, uint32_t, H1_4, DO_ORR) +DO_ZPZZ_D(sve_orr_zpzz_d, uint64_t, DO_ORR) + +DO_ZPZZ(sve_eor_zpzz_b, uint8_t, H1, DO_EOR) +DO_ZPZZ(sve_eor_zpzz_h, uint16_t, H1_2, DO_EOR) +DO_ZPZZ(sve_eor_zpzz_s, uint32_t, H1_4, DO_EOR) +DO_ZPZZ_D(sve_eor_zpzz_d, uint64_t, DO_EOR) + +DO_ZPZZ(sve_bic_zpzz_b, uint8_t, H1, DO_BIC) +DO_ZPZZ(sve_bic_zpzz_h, uint16_t, H1_2, DO_BIC) +DO_ZPZZ(sve_bic_zpzz_s, uint32_t, H1_4, DO_BIC) +DO_ZPZZ_D(sve_bic_zpzz_d, uint64_t, DO_BIC) + +DO_ZPZZ(sve_add_zpzz_b, uint8_t, H1, DO_ADD) +DO_ZPZZ(sve_add_zpzz_h, uint16_t, H1_2, DO_ADD) +DO_ZPZZ(sve_add_zpzz_s, uint32_t, H1_4, DO_ADD) +DO_ZPZZ_D(sve_add_zpzz_d, uint64_t, DO_ADD) + +DO_ZPZZ(sve_sub_zpzz_b, uint8_t, H1, DO_SUB) +DO_ZPZZ(sve_sub_zpzz_h, uint16_t, H1_2, DO_SUB) +DO_ZPZZ(sve_sub_zpzz_s, uint32_t, H1_4, DO_SUB) +DO_ZPZZ_D(sve_sub_zpzz_d, uint64_t, DO_SUB) + +DO_ZPZZ(sve_smax_zpzz_b, int8_t, H1, DO_MAX) +DO_ZPZZ(sve_smax_zpzz_h, int16_t, H1_2, DO_MAX) +DO_ZPZZ(sve_smax_zpzz_s, int32_t, H1_4, DO_MAX) +DO_ZPZZ_D(sve_smax_zpzz_d, int64_t, DO_MAX) + +DO_ZPZZ(sve_umax_zpzz_b, uint8_t, H1, DO_MAX) +DO_ZPZZ(sve_umax_zpzz_h, uint16_t, H1_2, DO_MAX) +DO_ZPZZ(sve_umax_zpzz_s, uint32_t, H1_4, DO_MAX) +DO_ZPZZ_D(sve_umax_zpzz_d, uint64_t, DO_MAX) + +DO_ZPZZ(sve_smin_zpzz_b, int8_t, H1, DO_MIN) +DO_ZPZZ(sve_smin_zpzz_h, int16_t, H1_2, DO_MIN) +DO_ZPZZ(sve_smin_zpzz_s, int32_t, H1_4, DO_MIN) +DO_ZPZZ_D(sve_smin_zpzz_d, int64_t, DO_MIN) + +DO_ZPZZ(sve_umin_zpzz_b, uint8_t, H1, DO_MIN) +DO_ZPZZ(sve_umin_zpzz_h, uint16_t, H1_2, DO_MIN) +DO_ZPZZ(sve_umin_zpzz_s, uint32_t, H1_4, DO_MIN) +DO_ZPZZ_D(sve_umin_zpzz_d, uint64_t, DO_MIN) + +DO_ZPZZ(sve_sabd_zpzz_b, int8_t, H1, DO_ABD) +DO_ZPZZ(sve_sabd_zpzz_h, int16_t, H1_2, DO_ABD) +DO_ZPZZ(sve_sabd_zpzz_s, int32_t, H1_4, DO_ABD) +DO_ZPZZ_D(sve_sabd_zpzz_d, int64_t, DO_ABD) + +DO_ZPZZ(sve_uabd_zpzz_b, uint8_t, H1, DO_ABD) +DO_ZPZZ(sve_uabd_zpzz_h, uint16_t, H1_2, DO_ABD) +DO_ZPZZ(sve_uabd_zpzz_s, uint32_t, H1_4, DO_ABD) +DO_ZPZZ_D(sve_uabd_zpzz_d, uint64_t, DO_ABD) + +/* Because the computation type is at least twice as large as required, + these work for both signed and unsigned source types. */ +static inline uint8_t do_mulh_b(int32_t n, int32_t m) +{ + return (n * m) >> 8; +} + +static inline uint16_t do_mulh_h(int32_t n, int32_t m) +{ + return (n * m) >> 16; +} + +static inline uint32_t do_mulh_s(int64_t n, int64_t m) +{ + return (n * m) >> 32; +} + +static inline uint64_t do_smulh_d(uint64_t n, uint64_t m) +{ + uint64_t lo, hi; + muls64(&lo, &hi, n, m); + return hi; +} + +static inline uint64_t do_umulh_d(uint64_t n, uint64_t m) +{ + uint64_t lo, hi; + mulu64(&lo, &hi, n, m); + return hi; +} + +DO_ZPZZ(sve_mul_zpzz_b, uint8_t, H1, DO_MUL) +DO_ZPZZ(sve_mul_zpzz_h, uint16_t, H1_2, DO_MUL) +DO_ZPZZ(sve_mul_zpzz_s, uint32_t, H1_4, DO_MUL) +DO_ZPZZ_D(sve_mul_zpzz_d, uint64_t, DO_MUL) + +DO_ZPZZ(sve_smulh_zpzz_b, int8_t, H1, do_mulh_b) +DO_ZPZZ(sve_smulh_zpzz_h, int16_t, H1_2, do_mulh_h) +DO_ZPZZ(sve_smulh_zpzz_s, int32_t, H1_4, do_mulh_s) +DO_ZPZZ_D(sve_smulh_zpzz_d, uint64_t, do_smulh_d) + +DO_ZPZZ(sve_umulh_zpzz_b, uint8_t, H1, do_mulh_b) +DO_ZPZZ(sve_umulh_zpzz_h, uint16_t, H1_2, do_mulh_h) +DO_ZPZZ(sve_umulh_zpzz_s, uint32_t, H1_4, do_mulh_s) +DO_ZPZZ_D(sve_umulh_zpzz_d, uint64_t, do_umulh_d) + +DO_ZPZZ(sve_sdiv_zpzz_s, int32_t, H1_4, DO_SDIV) +DO_ZPZZ_D(sve_sdiv_zpzz_d, int64_t, DO_SDIV) + +DO_ZPZZ(sve_udiv_zpzz_s, uint32_t, H1_4, DO_UDIV) +DO_ZPZZ_D(sve_udiv_zpzz_d, uint64_t, DO_UDIV) + +/* Note that all bits of the shift are significant + and not modulo the element size. */ +#define DO_ASR(N, M) (N >> MIN(M, sizeof(N) * 8 - 1)) +#define DO_LSR(N, M) (M < sizeof(N) * 8 ? N >> M : 0) +#define DO_LSL(N, M) (M < sizeof(N) * 8 ? N << M : 0) + +DO_ZPZZ(sve_asr_zpzz_b, int8_t, H1, DO_ASR) +DO_ZPZZ(sve_lsr_zpzz_b, uint8_t, H1_2, DO_LSR) +DO_ZPZZ(sve_lsl_zpzz_b, uint8_t, H1_4, DO_LSL) + +DO_ZPZZ(sve_asr_zpzz_h, int16_t, H1, DO_ASR) +DO_ZPZZ(sve_lsr_zpzz_h, uint16_t, H1_2, DO_LSR) +DO_ZPZZ(sve_lsl_zpzz_h, uint16_t, H1_4, DO_LSL) + +DO_ZPZZ(sve_asr_zpzz_s, int32_t, H1, DO_ASR) +DO_ZPZZ(sve_lsr_zpzz_s, uint32_t, H1_2, DO_LSR) +DO_ZPZZ(sve_lsl_zpzz_s, uint32_t, H1_4, DO_LSL) + +DO_ZPZZ_D(sve_asr_zpzz_d, int64_t, DO_ASR) +DO_ZPZZ_D(sve_lsr_zpzz_d, uint64_t, DO_LSR) +DO_ZPZZ_D(sve_lsl_zpzz_d, uint64_t, DO_LSL) + +#undef DO_ZPZZ +#undef DO_ZPZZ_D + +/* Three-operand expander, controlled by a predicate, in which the + * third operand is "wide". That is, for D = N op M, the same 64-bit + * value of M is used with all of the narrower values of N. + */ +#define DO_ZPZW(NAME, TYPE, TYPEW, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint8_t pg = *(uint8_t *)(vg + H1(i >> 3)); \ + TYPEW mm = *(TYPEW *)(vm + i); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 7); \ + } \ +} + +DO_ZPZW(sve_asr_zpzw_b, int8_t, uint64_t, H1, DO_ASR) +DO_ZPZW(sve_lsr_zpzw_b, uint8_t, uint64_t, H1, DO_LSR) +DO_ZPZW(sve_lsl_zpzw_b, uint8_t, uint64_t, H1, DO_LSL) + +DO_ZPZW(sve_asr_zpzw_h, int16_t, uint64_t, H1_2, DO_ASR) +DO_ZPZW(sve_lsr_zpzw_h, uint16_t, uint64_t, H1_2, DO_LSR) +DO_ZPZW(sve_lsl_zpzw_h, uint16_t, uint64_t, H1_2, DO_LSL) + +DO_ZPZW(sve_asr_zpzw_s, int32_t, uint64_t, H1_4, DO_ASR) +DO_ZPZW(sve_lsr_zpzw_s, uint32_t, uint64_t, H1_4, DO_LSR) +DO_ZPZW(sve_lsl_zpzw_s, uint32_t, uint64_t, H1_4, DO_LSL) + +#undef DO_ZPZW + +/* Fully general two-operand expander, controlled by a predicate. + */ +#define DO_ZPZ(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +/* Similarly, specialized for 64-bit operands. */ +#define DO_ZPZ_D(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPE *d = vd, *n = vn; \ + uint8_t *pg = vg; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPE nn = n[i]; \ + d[i] = OP(nn); \ + } \ + } \ +} + +#define DO_CLS_B(N) (clrsb32(N) - 24) +#define DO_CLS_H(N) (clrsb32(N) - 16) + +DO_ZPZ(sve_cls_b, int8_t, H1, DO_CLS_B) +DO_ZPZ(sve_cls_h, int16_t, H1_2, DO_CLS_H) +DO_ZPZ(sve_cls_s, int32_t, H1_4, clrsb32) +DO_ZPZ_D(sve_cls_d, int64_t, clrsb64) + +#define DO_CLZ_B(N) (clz32(N) - 24) +#define DO_CLZ_H(N) (clz32(N) - 16) + +DO_ZPZ(sve_clz_b, uint8_t, H1, DO_CLZ_B) +DO_ZPZ(sve_clz_h, uint16_t, H1_2, DO_CLZ_H) +DO_ZPZ(sve_clz_s, uint32_t, H1_4, clz32) +DO_ZPZ_D(sve_clz_d, uint64_t, clz64) + +DO_ZPZ(sve_cnt_zpz_b, uint8_t, H1, ctpop8) +DO_ZPZ(sve_cnt_zpz_h, uint16_t, H1_2, ctpop16) +DO_ZPZ(sve_cnt_zpz_s, uint32_t, H1_4, ctpop32) +DO_ZPZ_D(sve_cnt_zpz_d, uint64_t, ctpop64) + +#define DO_CNOT(N) (N == 0) + +DO_ZPZ(sve_cnot_b, uint8_t, H1, DO_CNOT) +DO_ZPZ(sve_cnot_h, uint16_t, H1_2, DO_CNOT) +DO_ZPZ(sve_cnot_s, uint32_t, H1_4, DO_CNOT) +DO_ZPZ_D(sve_cnot_d, uint64_t, DO_CNOT) + +#define DO_FABS(N) (N & ((__typeof(N))-1 >> 1)) + +DO_ZPZ(sve_fabs_h, uint16_t, H1_2, DO_FABS) +DO_ZPZ(sve_fabs_s, uint32_t, H1_4, DO_FABS) +DO_ZPZ_D(sve_fabs_d, uint64_t, DO_FABS) + +#define DO_FNEG(N) (N ^ ~((__typeof(N))-1 >> 1)) + +DO_ZPZ(sve_fneg_h, uint16_t, H1_2, DO_FNEG) +DO_ZPZ(sve_fneg_s, uint32_t, H1_4, DO_FNEG) +DO_ZPZ_D(sve_fneg_d, uint64_t, DO_FNEG) + +#define DO_NOT(N) (~N) + +DO_ZPZ(sve_not_zpz_b, uint8_t, H1, DO_NOT) +DO_ZPZ(sve_not_zpz_h, uint16_t, H1_2, DO_NOT) +DO_ZPZ(sve_not_zpz_s, uint32_t, H1_4, DO_NOT) +DO_ZPZ_D(sve_not_zpz_d, uint64_t, DO_NOT) + +#define DO_SXTB(N) ((int8_t)N) +#define DO_SXTH(N) ((int16_t)N) +#define DO_SXTS(N) ((int32_t)N) +#define DO_UXTB(N) ((uint8_t)N) +#define DO_UXTH(N) ((uint16_t)N) +#define DO_UXTS(N) ((uint32_t)N) + +DO_ZPZ(sve_sxtb_h, uint16_t, H1_2, DO_SXTB) +DO_ZPZ(sve_sxtb_s, uint32_t, H1_4, DO_SXTB) +DO_ZPZ(sve_sxth_s, uint32_t, H1_4, DO_SXTH) +DO_ZPZ_D(sve_sxtb_d, uint64_t, DO_SXTB) +DO_ZPZ_D(sve_sxth_d, uint64_t, DO_SXTH) +DO_ZPZ_D(sve_sxtw_d, uint64_t, DO_SXTS) + +DO_ZPZ(sve_uxtb_h, uint16_t, H1_2, DO_UXTB) +DO_ZPZ(sve_uxtb_s, uint32_t, H1_4, DO_UXTB) +DO_ZPZ(sve_uxth_s, uint32_t, H1_4, DO_UXTH) +DO_ZPZ_D(sve_uxtb_d, uint64_t, DO_UXTB) +DO_ZPZ_D(sve_uxth_d, uint64_t, DO_UXTH) +DO_ZPZ_D(sve_uxtw_d, uint64_t, DO_UXTS) + +#define DO_ABS(N) (N < 0 ? -N : N) + +DO_ZPZ(sve_abs_b, int8_t, H1, DO_ABS) +DO_ZPZ(sve_abs_h, int16_t, H1_2, DO_ABS) +DO_ZPZ(sve_abs_s, int32_t, H1_4, DO_ABS) +DO_ZPZ_D(sve_abs_d, int64_t, DO_ABS) + +#define DO_NEG(N) (-N) + +DO_ZPZ(sve_neg_b, uint8_t, H1, DO_NEG) +DO_ZPZ(sve_neg_h, uint16_t, H1_2, DO_NEG) +DO_ZPZ(sve_neg_s, uint32_t, H1_4, DO_NEG) +DO_ZPZ_D(sve_neg_d, uint64_t, DO_NEG) + +DO_ZPZ(sve_revb_h, uint16_t, H1_2, bswap16) +DO_ZPZ(sve_revb_s, uint32_t, H1_4, bswap32) +DO_ZPZ_D(sve_revb_d, uint64_t, bswap64) + +DO_ZPZ(sve_revh_s, uint32_t, H1_4, hswap32) +DO_ZPZ_D(sve_revh_d, uint64_t, hswap64) + +DO_ZPZ_D(sve_revw_d, uint64_t, wswap64) + +DO_ZPZ(sve_rbit_b, uint8_t, H1, revbit8) +DO_ZPZ(sve_rbit_h, uint16_t, H1_2, revbit16) +DO_ZPZ(sve_rbit_s, uint32_t, H1_4, revbit32) +DO_ZPZ_D(sve_rbit_d, uint64_t, revbit64) + +/* Three-operand expander, unpredicated, in which the third operand is "wide". + */ +#define DO_ZZW(NAME, TYPE, TYPEW, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + TYPEW mm = *(TYPEW *)(vm + i); \ + do { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm); \ + i += sizeof(TYPE); \ + } while (i & 7); \ + } \ +} + +DO_ZZW(sve_asr_zzw_b, int8_t, uint64_t, H1, DO_ASR) +DO_ZZW(sve_lsr_zzw_b, uint8_t, uint64_t, H1, DO_LSR) +DO_ZZW(sve_lsl_zzw_b, uint8_t, uint64_t, H1, DO_LSL) + +DO_ZZW(sve_asr_zzw_h, int16_t, uint64_t, H1_2, DO_ASR) +DO_ZZW(sve_lsr_zzw_h, uint16_t, uint64_t, H1_2, DO_LSR) +DO_ZZW(sve_lsl_zzw_h, uint16_t, uint64_t, H1_2, DO_LSL) + +DO_ZZW(sve_asr_zzw_s, int32_t, uint64_t, H1_4, DO_ASR) +DO_ZZW(sve_lsr_zzw_s, uint32_t, uint64_t, H1_4, DO_LSR) +DO_ZZW(sve_lsl_zzw_s, uint32_t, uint64_t, H1_4, DO_LSL) + +#undef DO_ZZW + +#undef DO_CLS_B +#undef DO_CLS_H +#undef DO_CLZ_B +#undef DO_CLZ_H +#undef DO_CNOT +#undef DO_FABS +#undef DO_FNEG +#undef DO_ABS +#undef DO_NEG +#undef DO_ZPZ +#undef DO_ZPZ_D + +/* Two-operand reduction expander, controlled by a predicate. + * The difference between TYPERED and TYPERET has to do with + * sign-extension. E.g. for SMAX, TYPERED must be signed, + * but TYPERET must be unsigned so that e.g. a 32-bit value + * is not sign-extended to the ABI uint64_t return type. + */ +/* ??? If we were to vectorize this by hand the reduction ordering + * would change. For integer operands, this is perfectly fine. + */ +#define DO_VPZ(NAME, TYPEELT, TYPERED, TYPERET, H, INIT, OP) \ +uint64_t HELPER(NAME)(void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPERED ret = INIT; \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEELT nn = *(TYPEELT *)(vn + H(i)); \ + ret = OP(ret, nn); \ + } \ + i += sizeof(TYPEELT), pg >>= sizeof(TYPEELT); \ + } while (i & 15); \ + } \ + return (TYPERET)ret; \ +} + +#define DO_VPZ_D(NAME, TYPEE, TYPER, INIT, OP) \ +uint64_t HELPER(NAME)(void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPEE *n = vn; \ + uint8_t *pg = vg; \ + TYPER ret = INIT; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPEE nn = n[i]; \ + ret = OP(ret, nn); \ + } \ + } \ + return ret; \ +} + +DO_VPZ(sve_orv_b, uint8_t, uint8_t, uint8_t, H1, 0, DO_ORR) +DO_VPZ(sve_orv_h, uint16_t, uint16_t, uint16_t, H1_2, 0, DO_ORR) +DO_VPZ(sve_orv_s, uint32_t, uint32_t, uint32_t, H1_4, 0, DO_ORR) +DO_VPZ_D(sve_orv_d, uint64_t, uint64_t, 0, DO_ORR) + +DO_VPZ(sve_eorv_b, uint8_t, uint8_t, uint8_t, H1, 0, DO_EOR) +DO_VPZ(sve_eorv_h, uint16_t, uint16_t, uint16_t, H1_2, 0, DO_EOR) +DO_VPZ(sve_eorv_s, uint32_t, uint32_t, uint32_t, H1_4, 0, DO_EOR) +DO_VPZ_D(sve_eorv_d, uint64_t, uint64_t, 0, DO_EOR) + +DO_VPZ(sve_andv_b, uint8_t, uint8_t, uint8_t, H1, -1, DO_AND) +DO_VPZ(sve_andv_h, uint16_t, uint16_t, uint16_t, H1_2, -1, DO_AND) +DO_VPZ(sve_andv_s, uint32_t, uint32_t, uint32_t, H1_4, -1, DO_AND) +DO_VPZ_D(sve_andv_d, uint64_t, uint64_t, -1, DO_AND) + +DO_VPZ(sve_saddv_b, int8_t, uint64_t, uint64_t, H1, 0, DO_ADD) +DO_VPZ(sve_saddv_h, int16_t, uint64_t, uint64_t, H1_2, 0, DO_ADD) +DO_VPZ(sve_saddv_s, int32_t, uint64_t, uint64_t, H1_4, 0, DO_ADD) + +DO_VPZ(sve_uaddv_b, uint8_t, uint64_t, uint64_t, H1, 0, DO_ADD) +DO_VPZ(sve_uaddv_h, uint16_t, uint64_t, uint64_t, H1_2, 0, DO_ADD) +DO_VPZ(sve_uaddv_s, uint32_t, uint64_t, uint64_t, H1_4, 0, DO_ADD) +DO_VPZ_D(sve_uaddv_d, uint64_t, uint64_t, 0, DO_ADD) + +DO_VPZ(sve_smaxv_b, int8_t, int8_t, uint8_t, H1, INT8_MIN, DO_MAX) +DO_VPZ(sve_smaxv_h, int16_t, int16_t, uint16_t, H1_2, INT16_MIN, DO_MAX) +DO_VPZ(sve_smaxv_s, int32_t, int32_t, uint32_t, H1_4, INT32_MIN, DO_MAX) +DO_VPZ_D(sve_smaxv_d, int64_t, int64_t, INT64_MIN, DO_MAX) + +DO_VPZ(sve_umaxv_b, uint8_t, uint8_t, uint8_t, H1, 0, DO_MAX) +DO_VPZ(sve_umaxv_h, uint16_t, uint16_t, uint16_t, H1_2, 0, DO_MAX) +DO_VPZ(sve_umaxv_s, uint32_t, uint32_t, uint32_t, H1_4, 0, DO_MAX) +DO_VPZ_D(sve_umaxv_d, uint64_t, uint64_t, 0, DO_MAX) + +DO_VPZ(sve_sminv_b, int8_t, int8_t, uint8_t, H1, INT8_MAX, DO_MIN) +DO_VPZ(sve_sminv_h, int16_t, int16_t, uint16_t, H1_2, INT16_MAX, DO_MIN) +DO_VPZ(sve_sminv_s, int32_t, int32_t, uint32_t, H1_4, INT32_MAX, DO_MIN) +DO_VPZ_D(sve_sminv_d, int64_t, int64_t, INT64_MAX, DO_MIN) + +DO_VPZ(sve_uminv_b, uint8_t, uint8_t, uint8_t, H1, -1, DO_MIN) +DO_VPZ(sve_uminv_h, uint16_t, uint16_t, uint16_t, H1_2, -1, DO_MIN) +DO_VPZ(sve_uminv_s, uint32_t, uint32_t, uint32_t, H1_4, -1, DO_MIN) +DO_VPZ_D(sve_uminv_d, uint64_t, uint64_t, -1, DO_MIN) + +#undef DO_VPZ +#undef DO_VPZ_D + +/* Two vector operand, one scalar operand, unpredicated. */ +#define DO_ZZI(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *vn, uint64_t s64, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(TYPE); \ + TYPE s = s64, *d = vd, *n = vn; \ + for (i = 0; i < opr_sz; ++i) { \ + d[i] = OP(n[i], s); \ + } \ +} + +#define DO_SUBR(X, Y) (Y - X) + +DO_ZZI(sve_subri_b, uint8_t, DO_SUBR) +DO_ZZI(sve_subri_h, uint16_t, DO_SUBR) +DO_ZZI(sve_subri_s, uint32_t, DO_SUBR) +DO_ZZI(sve_subri_d, uint64_t, DO_SUBR) + +DO_ZZI(sve_smaxi_b, int8_t, DO_MAX) +DO_ZZI(sve_smaxi_h, int16_t, DO_MAX) +DO_ZZI(sve_smaxi_s, int32_t, DO_MAX) +DO_ZZI(sve_smaxi_d, int64_t, DO_MAX) + +DO_ZZI(sve_smini_b, int8_t, DO_MIN) +DO_ZZI(sve_smini_h, int16_t, DO_MIN) +DO_ZZI(sve_smini_s, int32_t, DO_MIN) +DO_ZZI(sve_smini_d, int64_t, DO_MIN) + +DO_ZZI(sve_umaxi_b, uint8_t, DO_MAX) +DO_ZZI(sve_umaxi_h, uint16_t, DO_MAX) +DO_ZZI(sve_umaxi_s, uint32_t, DO_MAX) +DO_ZZI(sve_umaxi_d, uint64_t, DO_MAX) + +DO_ZZI(sve_umini_b, uint8_t, DO_MIN) +DO_ZZI(sve_umini_h, uint16_t, DO_MIN) +DO_ZZI(sve_umini_s, uint32_t, DO_MIN) +DO_ZZI(sve_umini_d, uint64_t, DO_MIN) + +#undef DO_ZZI + +#undef DO_AND +#undef DO_ORR +#undef DO_EOR +#undef DO_BIC +#undef DO_ADD +#undef DO_SUB +#undef DO_MAX +#undef DO_MIN +#undef DO_ABD +#undef DO_MUL +#undef DO_DIV +#undef DO_ASR +#undef DO_LSR +#undef DO_LSL +#undef DO_SUBR + +/* Similar to the ARM LastActiveElement pseudocode function, except the + result is multiplied by the element size. This includes the not found + indication; e.g. not found for esz=3 is -8. */ +static intptr_t last_active_element(uint64_t *g, intptr_t words, intptr_t esz) +{ + uint64_t mask = pred_esz_masks[esz]; + intptr_t i = words; + + do { + uint64_t this_g = g[--i] & mask; + if (this_g) { + return i * 64 + (63 - clz64(this_g)); + } + } while (i > 0); + return (intptr_t)-1 << esz; +} + +uint32_t HELPER(sve_pfirst)(void *vd, void *vg, uint32_t words) +{ + uint32_t flags = PREDTEST_INIT; + uint64_t *d = vd, *g = vg; + intptr_t i = 0; + + do { + uint64_t this_d = d[i]; + uint64_t this_g = g[i]; + + if (this_g) { + if (!(flags & 4)) { + /* Set in D the first bit of G. */ + this_d |= this_g & -this_g; + d[i] = this_d; + } + flags = iter_predtest_fwd(this_d, this_g, flags); + } + } while (++i < words); + + return flags; +} + +uint32_t HELPER(sve_pnext)(void *vd, void *vg, uint32_t pred_desc) +{ + intptr_t words = extract32(pred_desc, 0, SIMD_OPRSZ_BITS); + intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + uint32_t flags = PREDTEST_INIT; + uint64_t *d = vd, *g = vg, esz_mask; + intptr_t i, next; + + next = last_active_element(vd, words, esz) + (1 << esz); + esz_mask = pred_esz_masks[esz]; + + /* Similar to the pseudocode for pnext, but scaled by ESZ + so that we find the correct bit. */ + if (next < words * 64) { + uint64_t mask = -1; + + if (next & 63) { + mask = ~((1ull << (next & 63)) - 1); + next &= -64; + } + do { + uint64_t this_g = g[next / 64] & esz_mask & mask; + if (this_g != 0) { + next = (next & -64) + ctz64(this_g); + break; + } + next += 64; + mask = -1; + } while (next < words * 64); + } + + i = 0; + do { + uint64_t this_d = 0; + if (i == next / 64) { + this_d = 1ull << (next & 63); + } + d[i] = this_d; + flags = iter_predtest_fwd(this_d, g[i] & esz_mask, flags); + } while (++i < words); + + return flags; +} + +/* Store zero into every active element of Zd. We will use this for two + * and three-operand predicated instructions for which logic dictates a + * zero result. In particular, logical shift by element size, which is + * otherwise undefined on the host. + * + * For element sizes smaller than uint64_t, we use tables to expand + * the N bits of the controlling predicate to a byte mask, and clear + * those bytes. + */ +void HELPER(sve_clr_b)(void *vd, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] &= ~expand_pred_b(pg[H1(i)]); + } +} + +void HELPER(sve_clr_h)(void *vd, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] &= ~expand_pred_h(pg[H1(i)]); + } +} + +void HELPER(sve_clr_s)(void *vd, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] &= ~expand_pred_s(pg[H1(i)]); + } +} + +void HELPER(sve_clr_d)(void *vd, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + if (pg[H1(i)] & 1) { + d[i] = 0; + } + } +} + +/* Copy Zn into Zd, and store zero into inactive elements. */ +void HELPER(sve_movz_b)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] & expand_pred_b(pg[H1(i)]); + } +} + +void HELPER(sve_movz_h)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] & expand_pred_h(pg[H1(i)]); + } +} + +void HELPER(sve_movz_s)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] & expand_pred_s(pg[H1(i)]); + } +} + +void HELPER(sve_movz_d)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[1] & -(uint64_t)(pg[H1(i)] & 1); + } +} + +/* Three-operand expander, immediate operand, controlled by a predicate. + */ +#define DO_ZPZI(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPE imm = simd_data(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, imm); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +/* Similarly, specialized for 64-bit operands. */ +#define DO_ZPZI_D(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPE *d = vd, *n = vn; \ + TYPE imm = simd_data(desc); \ + uint8_t *pg = vg; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPE nn = n[i]; \ + d[i] = OP(nn, imm); \ + } \ + } \ +} + +#define DO_SHR(N, M) (N >> M) +#define DO_SHL(N, M) (N << M) + +/* Arithmetic shift right for division. This rounds negative numbers + toward zero as per signed division. Therefore before shifting, + when N is negative, add 2**M-1. */ +#define DO_ASRD(N, M) ((N + (N < 0 ? ((__typeof(N))1 << M) - 1 : 0)) >> M) + +DO_ZPZI(sve_asr_zpzi_b, int8_t, H1, DO_SHR) +DO_ZPZI(sve_asr_zpzi_h, int16_t, H1_2, DO_SHR) +DO_ZPZI(sve_asr_zpzi_s, int32_t, H1_4, DO_SHR) +DO_ZPZI_D(sve_asr_zpzi_d, int64_t, DO_SHR) + +DO_ZPZI(sve_lsr_zpzi_b, uint8_t, H1, DO_SHR) +DO_ZPZI(sve_lsr_zpzi_h, uint16_t, H1_2, DO_SHR) +DO_ZPZI(sve_lsr_zpzi_s, uint32_t, H1_4, DO_SHR) +DO_ZPZI_D(sve_lsr_zpzi_d, uint64_t, DO_SHR) + +DO_ZPZI(sve_lsl_zpzi_b, uint8_t, H1, DO_SHL) +DO_ZPZI(sve_lsl_zpzi_h, uint16_t, H1_2, DO_SHL) +DO_ZPZI(sve_lsl_zpzi_s, uint32_t, H1_4, DO_SHL) +DO_ZPZI_D(sve_lsl_zpzi_d, uint64_t, DO_SHL) + +DO_ZPZI(sve_asrd_b, int8_t, H1, DO_ASRD) +DO_ZPZI(sve_asrd_h, int16_t, H1_2, DO_ASRD) +DO_ZPZI(sve_asrd_s, int32_t, H1_4, DO_ASRD) +DO_ZPZI_D(sve_asrd_d, int64_t, DO_ASRD) + +#undef DO_SHR +#undef DO_SHL +#undef DO_ASRD +#undef DO_ZPZI +#undef DO_ZPZI_D + +/* Fully general four-operand expander, controlled by a predicate. + */ +#define DO_ZPZZZ(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *va, void *vn, void *vm, \ + void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + for (i = 0; i < opr_sz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + TYPE aa = *(TYPE *)(va + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(aa, nn, mm); \ + } \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ +} + +/* Similarly, specialized for 64-bit operands. */ +#define DO_ZPZZZ_D(NAME, TYPE, OP) \ +void HELPER(NAME)(void *vd, void *va, void *vn, void *vm, \ + void *vg, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; \ + TYPE *d = vd, *a = va, *n = vn, *m = vm; \ + uint8_t *pg = vg; \ + for (i = 0; i < opr_sz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + TYPE aa = a[i], nn = n[i], mm = m[i]; \ + d[i] = OP(aa, nn, mm); \ + } \ + } \ +} + +#define DO_MLA(A, N, M) (A + N * M) +#define DO_MLS(A, N, M) (A - N * M) + +DO_ZPZZZ(sve_mla_b, uint8_t, H1, DO_MLA) +DO_ZPZZZ(sve_mls_b, uint8_t, H1, DO_MLS) + +DO_ZPZZZ(sve_mla_h, uint16_t, H1_2, DO_MLA) +DO_ZPZZZ(sve_mls_h, uint16_t, H1_2, DO_MLS) + +DO_ZPZZZ(sve_mla_s, uint32_t, H1_4, DO_MLA) +DO_ZPZZZ(sve_mls_s, uint32_t, H1_4, DO_MLS) + +DO_ZPZZZ_D(sve_mla_d, uint64_t, DO_MLA) +DO_ZPZZZ_D(sve_mls_d, uint64_t, DO_MLS) + +#undef DO_MLA +#undef DO_MLS +#undef DO_ZPZZZ +#undef DO_ZPZZZ_D + +void HELPER(sve_index_b)(void *vd, uint32_t start, + uint32_t incr, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint8_t *d = vd; + for (i = 0; i < opr_sz; i += 1) { + d[H1(i)] = start + i * incr; + } +} + +void HELPER(sve_index_h)(void *vd, uint32_t start, + uint32_t incr, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 2; + uint16_t *d = vd; + for (i = 0; i < opr_sz; i += 1) { + d[H2(i)] = start + i * incr; + } +} + +void HELPER(sve_index_s)(void *vd, uint32_t start, + uint32_t incr, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 4; + uint32_t *d = vd; + for (i = 0; i < opr_sz; i += 1) { + d[H4(i)] = start + i * incr; + } +} + +void HELPER(sve_index_d)(void *vd, uint64_t start, + uint64_t incr, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + for (i = 0; i < opr_sz; i += 1) { + d[i] = start + i * incr; + } +} + +void HELPER(sve_adr_p32)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 4; + uint32_t sh = simd_data(desc); + uint32_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] + (m[i] << sh); + } +} + +void HELPER(sve_adr_p64)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t sh = simd_data(desc); + uint64_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] + (m[i] << sh); + } +} + +void HELPER(sve_adr_s32)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t sh = simd_data(desc); + uint64_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] + ((uint64_t)(int32_t)m[i] << sh); + } +} + +void HELPER(sve_adr_u32)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t sh = simd_data(desc); + uint64_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + d[i] = n[i] + ((uint64_t)(uint32_t)m[i] << sh); + } +} + +void HELPER(sve_fexpa_h)(void *vd, void *vn, uint32_t desc) +{ + /* These constants are cut-and-paste directly from the ARM pseudocode. */ + static const uint16_t coeff[] = { + 0x0000, 0x0016, 0x002d, 0x0045, 0x005d, 0x0075, 0x008e, 0x00a8, + 0x00c2, 0x00dc, 0x00f8, 0x0114, 0x0130, 0x014d, 0x016b, 0x0189, + 0x01a8, 0x01c8, 0x01e8, 0x0209, 0x022b, 0x024e, 0x0271, 0x0295, + 0x02ba, 0x02e0, 0x0306, 0x032e, 0x0356, 0x037f, 0x03a9, 0x03d4, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / 2; + uint16_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz; i++) { + uint16_t nn = n[i]; + intptr_t idx = extract32(nn, 0, 5); + uint16_t exp = extract32(nn, 5, 5); + d[i] = coeff[idx] | (exp << 10); + } +} + +void HELPER(sve_fexpa_s)(void *vd, void *vn, uint32_t desc) +{ + /* These constants are cut-and-paste directly from the ARM pseudocode. */ + static const uint32_t coeff[] = { + 0x000000, 0x0164d2, 0x02cd87, 0x043a29, + 0x05aac3, 0x071f62, 0x08980f, 0x0a14d5, + 0x0b95c2, 0x0d1adf, 0x0ea43a, 0x1031dc, + 0x11c3d3, 0x135a2b, 0x14f4f0, 0x16942d, + 0x1837f0, 0x19e046, 0x1b8d3a, 0x1d3eda, + 0x1ef532, 0x20b051, 0x227043, 0x243516, + 0x25fed7, 0x27cd94, 0x29a15b, 0x2b7a3a, + 0x2d583f, 0x2f3b79, 0x3123f6, 0x3311c4, + 0x3504f3, 0x36fd92, 0x38fbaf, 0x3aff5b, + 0x3d08a4, 0x3f179a, 0x412c4d, 0x4346cd, + 0x45672a, 0x478d75, 0x49b9be, 0x4bec15, + 0x4e248c, 0x506334, 0x52a81e, 0x54f35b, + 0x5744fd, 0x599d16, 0x5bfbb8, 0x5e60f5, + 0x60ccdf, 0x633f89, 0x65b907, 0x68396a, + 0x6ac0c7, 0x6d4f30, 0x6fe4ba, 0x728177, + 0x75257d, 0x77d0df, 0x7a83b3, 0x7d3e0c, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / 4; + uint32_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz; i++) { + uint32_t nn = n[i]; + intptr_t idx = extract32(nn, 0, 6); + uint32_t exp = extract32(nn, 6, 8); + d[i] = coeff[idx] | (exp << 23); + } +} + +void HELPER(sve_fexpa_d)(void *vd, void *vn, uint32_t desc) +{ + /* These constants are cut-and-paste directly from the ARM pseudocode. */ + static const uint64_t coeff[] = { + 0x0000000000000ull, 0x02C9A3E778061ull, 0x059B0D3158574ull, + 0x0874518759BC8ull, 0x0B5586CF9890Full, 0x0E3EC32D3D1A2ull, + 0x11301D0125B51ull, 0x1429AAEA92DE0ull, 0x172B83C7D517Bull, + 0x1A35BEB6FCB75ull, 0x1D4873168B9AAull, 0x2063B88628CD6ull, + 0x2387A6E756238ull, 0x26B4565E27CDDull, 0x29E9DF51FDEE1ull, + 0x2D285A6E4030Bull, 0x306FE0A31B715ull, 0x33C08B26416FFull, + 0x371A7373AA9CBull, 0x3A7DB34E59FF7ull, 0x3DEA64C123422ull, + 0x4160A21F72E2Aull, 0x44E086061892Dull, 0x486A2B5C13CD0ull, + 0x4BFDAD5362A27ull, 0x4F9B2769D2CA7ull, 0x5342B569D4F82ull, + 0x56F4736B527DAull, 0x5AB07DD485429ull, 0x5E76F15AD2148ull, + 0x6247EB03A5585ull, 0x6623882552225ull, 0x6A09E667F3BCDull, + 0x6DFB23C651A2Full, 0x71F75E8EC5F74ull, 0x75FEB564267C9ull, + 0x7A11473EB0187ull, 0x7E2F336CF4E62ull, 0x82589994CCE13ull, + 0x868D99B4492EDull, 0x8ACE5422AA0DBull, 0x8F1AE99157736ull, + 0x93737B0CDC5E5ull, 0x97D829FDE4E50ull, 0x9C49182A3F090ull, + 0xA0C667B5DE565ull, 0xA5503B23E255Dull, 0xA9E6B5579FDBFull, + 0xAE89F995AD3ADull, 0xB33A2B84F15FBull, 0xB7F76F2FB5E47ull, + 0xBCC1E904BC1D2ull, 0xC199BDD85529Cull, 0xC67F12E57D14Bull, + 0xCB720DCEF9069ull, 0xD072D4A07897Cull, 0xD5818DCFBA487ull, + 0xDA9E603DB3285ull, 0xDFC97337B9B5Full, 0xE502EE78B3FF6ull, + 0xEA4AFA2A490DAull, 0xEFA1BEE615A27ull, 0xF50765B6E4540ull, + 0xFA7C1819E90D8ull, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + + for (i = 0; i < opr_sz; i++) { + uint64_t nn = n[i]; + intptr_t idx = extract32(nn, 0, 6); + uint64_t exp = extract32(nn, 6, 11); + d[i] = coeff[idx] | (exp << 52); + } +} + +void HELPER(sve_ftssel_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 2; + uint16_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + uint16_t nn = n[i]; + uint16_t mm = m[i]; + if (mm & 1) { + nn = float16_one; + } + d[i] = nn ^ (mm & 2) << 14; + } +} + +void HELPER(sve_ftssel_s)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 4; + uint32_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + uint32_t nn = n[i]; + uint32_t mm = m[i]; + if (mm & 1) { + nn = float32_one; + } + d[i] = nn ^ (mm & 2) << 30; + } +} + +void HELPER(sve_ftssel_d)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + uint64_t mm = m[i]; + if (mm & 1) { + nn = float64_one; + } + d[i] = nn ^ (mm & 2) << 62; + } +} + +/* + * Signed saturating addition with scalar operand. + */ + +void HELPER(sve_sqaddi_b)(void *d, void *a, int32_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(int8_t)) { + int r = *(int8_t *)(a + i) + b; + if (r > INT8_MAX) { + r = INT8_MAX; + } else if (r < INT8_MIN) { + r = INT8_MIN; + } + *(int8_t *)(d + i) = r; + } +} + +void HELPER(sve_sqaddi_h)(void *d, void *a, int32_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(int16_t)) { + int r = *(int16_t *)(a + i) + b; + if (r > INT16_MAX) { + r = INT16_MAX; + } else if (r < INT16_MIN) { + r = INT16_MIN; + } + *(int16_t *)(d + i) = r; + } +} + +void HELPER(sve_sqaddi_s)(void *d, void *a, int64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(int32_t)) { + int64_t r = *(int32_t *)(a + i) + b; + if (r > INT32_MAX) { + r = INT32_MAX; + } else if (r < INT32_MIN) { + r = INT32_MIN; + } + *(int32_t *)(d + i) = r; + } +} + +void HELPER(sve_sqaddi_d)(void *d, void *a, int64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(int64_t)) { + int64_t ai = *(int64_t *)(a + i); + int64_t r = ai + b; + if (((r ^ ai) & ~(ai ^ b)) < 0) { + /* Signed overflow. */ + r = (r < 0 ? INT64_MAX : INT64_MIN); + } + *(int64_t *)(d + i) = r; + } +} + +/* + * Unsigned saturating addition with scalar operand. + */ + +void HELPER(sve_uqaddi_b)(void *d, void *a, int32_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + int r = *(uint8_t *)(a + i) + b; + if (r > UINT8_MAX) { + r = UINT8_MAX; + } else if (r < 0) { + r = 0; + } + *(uint8_t *)(d + i) = r; + } +} + +void HELPER(sve_uqaddi_h)(void *d, void *a, int32_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + int r = *(uint16_t *)(a + i) + b; + if (r > UINT16_MAX) { + r = UINT16_MAX; + } else if (r < 0) { + r = 0; + } + *(uint16_t *)(d + i) = r; + } +} + +void HELPER(sve_uqaddi_s)(void *d, void *a, int64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + int64_t r = *(uint32_t *)(a + i) + b; + if (r > UINT32_MAX) { + r = UINT32_MAX; + } else if (r < 0) { + r = 0; + } + *(uint32_t *)(d + i) = r; + } +} + +void HELPER(sve_uqaddi_d)(void *d, void *a, uint64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + uint64_t r = *(uint64_t *)(a + i) + b; + if (r < b) { + r = UINT64_MAX; + } + *(uint64_t *)(d + i) = r; + } +} + +void HELPER(sve_uqsubi_d)(void *d, void *a, uint64_t b, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + uint64_t ai = *(uint64_t *)(a + i); + *(uint64_t *)(d + i) = (ai < b ? 0 : ai - b); + } +} + +/* Two operand predicated copy immediate with merge. All valid immediates + * can fit within 17 signed bits in the simd_data field. + */ +void HELPER(sve_cpy_m_b)(void *vd, void *vn, void *vg, + uint64_t mm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + mm = dup_const(MO_8, mm); + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + uint64_t pp = expand_pred_b(pg[H1(i)]); + d[i] = (mm & pp) | (nn & ~pp); + } +} + +void HELPER(sve_cpy_m_h)(void *vd, void *vn, void *vg, + uint64_t mm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + mm = dup_const(MO_16, mm); + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + uint64_t pp = expand_pred_h(pg[H1(i)]); + d[i] = (mm & pp) | (nn & ~pp); + } +} + +void HELPER(sve_cpy_m_s)(void *vd, void *vn, void *vg, + uint64_t mm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + mm = dup_const(MO_32, mm); + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + uint64_t pp = expand_pred_s(pg[H1(i)]); + d[i] = (mm & pp) | (nn & ~pp); + } +} + +void HELPER(sve_cpy_m_d)(void *vd, void *vn, void *vg, + uint64_t mm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i]; + d[i] = (pg[H1(i)] & 1 ? mm : nn); + } +} + +void HELPER(sve_cpy_z_b)(void *vd, void *vg, uint64_t val, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + + val = dup_const(MO_8, val); + for (i = 0; i < opr_sz; i += 1) { + d[i] = val & expand_pred_b(pg[H1(i)]); + } +} + +void HELPER(sve_cpy_z_h)(void *vd, void *vg, uint64_t val, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + + val = dup_const(MO_16, val); + for (i = 0; i < opr_sz; i += 1) { + d[i] = val & expand_pred_h(pg[H1(i)]); + } +} + +void HELPER(sve_cpy_z_s)(void *vd, void *vg, uint64_t val, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + + val = dup_const(MO_32, val); + for (i = 0; i < opr_sz; i += 1) { + d[i] = val & expand_pred_s(pg[H1(i)]); + } +} + +void HELPER(sve_cpy_z_d)(void *vd, void *vg, uint64_t val, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + d[i] = (pg[H1(i)] & 1 ? val : 0); + } +} + +/* Big-endian hosts need to frob the byte indicies. If the copy + * happens to be 8-byte aligned, then no frobbing necessary. + */ +static void swap_memmove(void *vd, void *vs, size_t n) +{ + uintptr_t d = (uintptr_t)vd; + uintptr_t s = (uintptr_t)vs; + uintptr_t o = (d | s | n) & 7; + size_t i; + +#ifndef HOST_WORDS_BIGENDIAN + o = 0; +#endif + switch (o) { + case 0: + memmove(vd, vs, n); + break; + + case 4: + if (d < s || d >= s + n) { + for (i = 0; i < n; i += 4) { + *(uint32_t *)H1_4(d + i) = *(uint32_t *)H1_4(s + i); + } + } else { + for (i = n; i > 0; ) { + i -= 4; + *(uint32_t *)H1_4(d + i) = *(uint32_t *)H1_4(s + i); + } + } + break; + + case 2: + case 6: + if (d < s || d >= s + n) { + for (i = 0; i < n; i += 2) { + *(uint16_t *)H1_2(d + i) = *(uint16_t *)H1_2(s + i); + } + } else { + for (i = n; i > 0; ) { + i -= 2; + *(uint16_t *)H1_2(d + i) = *(uint16_t *)H1_2(s + i); + } + } + break; + + default: + if (d < s || d >= s + n) { + for (i = 0; i < n; i++) { + *(uint8_t *)H1(d + i) = *(uint8_t *)H1(s + i); + } + } else { + for (i = n; i > 0; ) { + i -= 1; + *(uint8_t *)H1(d + i) = *(uint8_t *)H1(s + i); + } + } + break; + } +} + +void HELPER(sve_ext)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t opr_sz = simd_oprsz(desc); + size_t n_ofs = simd_data(desc); + size_t n_siz = opr_sz - n_ofs; + + if (vd != vm) { + swap_memmove(vd, vn + n_ofs, n_siz); + swap_memmove(vd + n_siz, vm, n_ofs); + } else if (vd != vn) { + swap_memmove(vd + n_siz, vd, n_ofs); + swap_memmove(vd, vn + n_ofs, n_siz); + } else { + /* vd == vn == vm. Need temp space. */ + ARMVectorReg tmp; + swap_memmove(&tmp, vm, n_ofs); + swap_memmove(vd, vd + n_ofs, n_siz); + memcpy(vd + n_siz, &tmp, n_ofs); + } +} + +#define DO_INSR(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, uint64_t val, uint32_t desc) \ +{ \ + intptr_t opr_sz = simd_oprsz(desc); \ + swap_memmove(vd + sizeof(TYPE), vn, opr_sz - sizeof(TYPE)); \ + *(TYPE *)(vd + H(0)) = val; \ +} + +DO_INSR(sve_insr_b, uint8_t, H1) +DO_INSR(sve_insr_h, uint16_t, H1_2) +DO_INSR(sve_insr_s, uint32_t, H1_4) +DO_INSR(sve_insr_d, uint64_t, ) + +#undef DO_INSR + +void HELPER(sve_rev_b)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + for (i = 0, j = opr_sz - 8; i < opr_sz / 2; i += 8, j -= 8) { + uint64_t f = *(uint64_t *)(vn + i); + uint64_t b = *(uint64_t *)(vn + j); + *(uint64_t *)(vd + i) = bswap64(b); + *(uint64_t *)(vd + j) = bswap64(f); + } +} + +void HELPER(sve_rev_h)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + for (i = 0, j = opr_sz - 8; i < opr_sz / 2; i += 8, j -= 8) { + uint64_t f = *(uint64_t *)(vn + i); + uint64_t b = *(uint64_t *)(vn + j); + *(uint64_t *)(vd + i) = hswap64(b); + *(uint64_t *)(vd + j) = hswap64(f); + } +} + +void HELPER(sve_rev_s)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + for (i = 0, j = opr_sz - 8; i < opr_sz / 2; i += 8, j -= 8) { + uint64_t f = *(uint64_t *)(vn + i); + uint64_t b = *(uint64_t *)(vn + j); + *(uint64_t *)(vd + i) = rol64(b, 32); + *(uint64_t *)(vd + j) = rol64(f, 32); + } +} + +void HELPER(sve_rev_d)(void *vd, void *vn, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + for (i = 0, j = opr_sz - 8; i < opr_sz / 2; i += 8, j -= 8) { + uint64_t f = *(uint64_t *)(vn + i); + uint64_t b = *(uint64_t *)(vn + j); + *(uint64_t *)(vd + i) = b; + *(uint64_t *)(vd + j) = f; + } +} + +#define DO_TBL(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + uintptr_t elem = opr_sz / sizeof(TYPE); \ + TYPE *d = vd, *n = vn, *m = vm; \ + ARMVectorReg tmp; \ + if (unlikely(vd == vn)) { \ + n = memcpy(&tmp, vn, opr_sz); \ + } \ + for (i = 0; i < elem; i++) { \ + TYPE j = m[H(i)]; \ + d[H(i)] = j < elem ? n[H(j)] : 0; \ + } \ +} + +DO_TBL(sve_tbl_b, uint8_t, H1) +DO_TBL(sve_tbl_h, uint16_t, H2) +DO_TBL(sve_tbl_s, uint32_t, H4) +DO_TBL(sve_tbl_d, uint64_t, ) + +#undef TBL + +#define DO_UNPK(NAME, TYPED, TYPES, HD, HS) \ +void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPED *d = vd; \ + TYPES *n = vn; \ + ARMVectorReg tmp; \ + if (unlikely(vn - vd < opr_sz)) { \ + n = memcpy(&tmp, n, opr_sz / 2); \ + } \ + for (i = 0; i < opr_sz / sizeof(TYPED); i++) { \ + d[HD(i)] = n[HS(i)]; \ + } \ +} + +DO_UNPK(sve_sunpk_h, int16_t, int8_t, H2, H1) +DO_UNPK(sve_sunpk_s, int32_t, int16_t, H4, H2) +DO_UNPK(sve_sunpk_d, int64_t, int32_t, , H4) + +DO_UNPK(sve_uunpk_h, uint16_t, uint8_t, H2, H1) +DO_UNPK(sve_uunpk_s, uint32_t, uint16_t, H4, H2) +DO_UNPK(sve_uunpk_d, uint64_t, uint32_t, , H4) + +#undef DO_UNPK + +/* Mask of bits included in the even numbered predicates of width esz. + * We also use this for expand_bits/compress_bits, and so extend the + * same pattern out to 16-bit units. + */ +static const uint64_t even_bit_esz_masks[5] = { + 0x5555555555555555ull, + 0x3333333333333333ull, + 0x0f0f0f0f0f0f0f0full, + 0x00ff00ff00ff00ffull, + 0x0000ffff0000ffffull, +}; + +/* Zero-extend units of 2**N bits to units of 2**(N+1) bits. + * For N==0, this corresponds to the operation that in qemu/bitops.h + * we call half_shuffle64; this algorithm is from Hacker's Delight, + * section 7-2 Shuffling Bits. + */ +static uint64_t expand_bits(uint64_t x, int n) +{ + int i; + + x &= 0xffffffffu; + for (i = 4; i >= n; i--) { + int sh = 1 << i; + x = ((x << sh) | x) & even_bit_esz_masks[i]; + } + return x; +} + +/* Compress units of 2**(N+1) bits to units of 2**N bits. + * For N==0, this corresponds to the operation that in qemu/bitops.h + * we call half_unshuffle64; this algorithm is from Hacker's Delight, + * section 7-2 Shuffling Bits, where it is called an inverse half shuffle. + */ +static uint64_t compress_bits(uint64_t x, int n) +{ + int i; + + for (i = n; i <= 4; i++) { + int sh = 1 << i; + x &= even_bit_esz_masks[i]; + x = (x >> sh) | x; + } + return x & 0xffffffffu; +} + +void HELPER(sve_zip_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + int esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + intptr_t high = extract32(pred_desc, SIMD_DATA_SHIFT + 2, 1); + uint64_t *d = vd; + intptr_t i; + + if (oprsz <= 8) { + uint64_t nn = *(uint64_t *)vn; + uint64_t mm = *(uint64_t *)vm; + int half = 4 * oprsz; + + nn = extract64(nn, high * half, half); + mm = extract64(mm, high * half, half); + nn = expand_bits(nn, esz); + mm = expand_bits(mm, esz); + d[0] = nn + (mm << (1 << esz)); + } else { + ARMPredicateReg tmp_n, tmp_m; + + /* We produce output faster than we consume input. + Therefore we must be mindful of possible overlap. */ + if ((vn - vd) < (uintptr_t)oprsz) { + vn = memcpy(&tmp_n, vn, oprsz); + } + if ((vm - vd) < (uintptr_t)oprsz) { + vm = memcpy(&tmp_m, vm, oprsz); + } + if (high) { + high = oprsz >> 1; + } + + if ((high & 3) == 0) { + uint32_t *n = vn, *m = vm; + high >>= 2; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); i++) { + uint64_t nn = n[H4(high + i)]; + uint64_t mm = m[H4(high + i)]; + + nn = expand_bits(nn, esz); + mm = expand_bits(mm, esz); + d[i] = nn + (mm << (1 << esz)); + } + } else { + uint8_t *n = vn, *m = vm; + uint16_t *d16 = vd; + + for (i = 0; i < oprsz / 2; i++) { + uint16_t nn = n[H1(high + i)]; + uint16_t mm = m[H1(high + i)]; + + nn = expand_bits(nn, esz); + mm = expand_bits(mm, esz); + d16[H2(i)] = nn + (mm << (1 << esz)); + } + } + } +} + +void HELPER(sve_uzp_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + int esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + int odd = extract32(pred_desc, SIMD_DATA_SHIFT + 2, 1) << esz; + uint64_t *d = vd, *n = vn, *m = vm; + uint64_t l, h; + intptr_t i; + + if (oprsz <= 8) { + l = compress_bits(n[0] >> odd, esz); + h = compress_bits(m[0] >> odd, esz); + d[0] = extract64(l + (h << (4 * oprsz)), 0, 8 * oprsz); + } else { + ARMPredicateReg tmp_m; + intptr_t oprsz_16 = oprsz / 16; + + if ((vm - vd) < (uintptr_t)oprsz) { + m = memcpy(&tmp_m, vm, oprsz); + } + + for (i = 0; i < oprsz_16; i++) { + l = n[2 * i + 0]; + h = n[2 * i + 1]; + l = compress_bits(l >> odd, esz); + h = compress_bits(h >> odd, esz); + d[i] = l + (h << 32); + } + + /* For VL which is not a power of 2, the results from M do not + align nicely with the uint64_t for D. Put the aligned results + from M into TMP_M and then copy it into place afterward. */ + if (oprsz & 15) { + d[i] = compress_bits(n[2 * i] >> odd, esz); + + for (i = 0; i < oprsz_16; i++) { + l = m[2 * i + 0]; + h = m[2 * i + 1]; + l = compress_bits(l >> odd, esz); + h = compress_bits(h >> odd, esz); + tmp_m.p[i] = l + (h << 32); + } + tmp_m.p[i] = compress_bits(m[2 * i] >> odd, esz); + + swap_memmove(vd + oprsz / 2, &tmp_m, oprsz / 2); + } else { + for (i = 0; i < oprsz_16; i++) { + l = m[2 * i + 0]; + h = m[2 * i + 1]; + l = compress_bits(l >> odd, esz); + h = compress_bits(h >> odd, esz); + d[oprsz_16 + i] = l + (h << 32); + } + } + } +} + +void HELPER(sve_trn_p)(void *vd, void *vn, void *vm, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + uintptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + bool odd = extract32(pred_desc, SIMD_DATA_SHIFT + 2, 1); + uint64_t *d = vd, *n = vn, *m = vm; + uint64_t mask; + int shr, shl; + intptr_t i; + + shl = 1 << esz; + shr = 0; + mask = even_bit_esz_masks[esz]; + if (odd) { + mask <<= shl; + shr = shl; + shl = 0; + } + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); i++) { + uint64_t nn = (n[i] & mask) >> shr; + uint64_t mm = (m[i] & mask) << shl; + d[i] = nn + mm; + } +} + +/* Reverse units of 2**N bits. */ +static uint64_t reverse_bits_64(uint64_t x, int n) +{ + int i, sh; + + x = bswap64(x); + for (i = 2, sh = 4; i >= n; i--, sh >>= 1) { + uint64_t mask = even_bit_esz_masks[i]; + x = ((x & mask) << sh) | ((x >> sh) & mask); + } + return x; +} + +static uint8_t reverse_bits_8(uint8_t x, int n) +{ + static const uint8_t mask[3] = { 0x55, 0x33, 0x0f }; + int i, sh; + + for (i = 2, sh = 4; i >= n; i--, sh >>= 1) { + x = ((x & mask[i]) << sh) | ((x >> sh) & mask[i]); + } + return x; +} + +void HELPER(sve_rev_p)(void *vd, void *vn, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + int esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + intptr_t i, oprsz_2 = oprsz / 2; + + if (oprsz <= 8) { + uint64_t l = *(uint64_t *)vn; + l = reverse_bits_64(l << (64 - 8 * oprsz), esz); + *(uint64_t *)vd = l; + } else if ((oprsz & 15) == 0) { + for (i = 0; i < oprsz_2; i += 8) { + intptr_t ih = oprsz - 8 - i; + uint64_t l = reverse_bits_64(*(uint64_t *)(vn + i), esz); + uint64_t h = reverse_bits_64(*(uint64_t *)(vn + ih), esz); + *(uint64_t *)(vd + i) = h; + *(uint64_t *)(vd + ih) = l; + } + } else { + for (i = 0; i < oprsz_2; i += 1) { + intptr_t il = H1(i); + intptr_t ih = H1(oprsz - 1 - i); + uint8_t l = reverse_bits_8(*(uint8_t *)(vn + il), esz); + uint8_t h = reverse_bits_8(*(uint8_t *)(vn + ih), esz); + *(uint8_t *)(vd + il) = h; + *(uint8_t *)(vd + ih) = l; + } + } +} + +void HELPER(sve_punpk_p)(void *vd, void *vn, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t high = extract32(pred_desc, SIMD_DATA_SHIFT + 2, 1); + uint64_t *d = vd; + intptr_t i; + + if (oprsz <= 8) { + uint64_t nn = *(uint64_t *)vn; + int half = 4 * oprsz; + + nn = extract64(nn, high * half, half); + nn = expand_bits(nn, 0); + d[0] = nn; + } else { + ARMPredicateReg tmp_n; + + /* We produce output faster than we consume input. + Therefore we must be mindful of possible overlap. */ + if ((vn - vd) < (uintptr_t)oprsz) { + vn = memcpy(&tmp_n, vn, oprsz); + } + if (high) { + high = oprsz >> 1; + } + + if ((high & 3) == 0) { + uint32_t *n = vn; + high >>= 2; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); i++) { + uint64_t nn = n[H4(high + i)]; + d[i] = expand_bits(nn, 0); + } + } else { + uint16_t *d16 = vd; + uint8_t *n = vn; + + for (i = 0; i < oprsz / 2; i++) { + uint16_t nn = n[H1(high + i)]; + d16[H2(i)] = expand_bits(nn, 0); + } + } + } +} + +#define DO_ZIP(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t i, oprsz_2 = oprsz / 2; \ + ARMVectorReg tmp_n, tmp_m; \ + /* We produce output faster than we consume input. \ + Therefore we must be mindful of possible overlap. */ \ + if (unlikely((vn - vd) < (uintptr_t)oprsz)) { \ + vn = memcpy(&tmp_n, vn, oprsz_2); \ + } \ + if (unlikely((vm - vd) < (uintptr_t)oprsz)) { \ + vm = memcpy(&tmp_m, vm, oprsz_2); \ + } \ + for (i = 0; i < oprsz_2; i += sizeof(TYPE)) { \ + *(TYPE *)(vd + H(2 * i + 0)) = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(2 * i + sizeof(TYPE))) = *(TYPE *)(vm + H(i)); \ + } \ +} + +DO_ZIP(sve_zip_b, uint8_t, H1) +DO_ZIP(sve_zip_h, uint16_t, H1_2) +DO_ZIP(sve_zip_s, uint32_t, H1_4) +DO_ZIP(sve_zip_d, uint64_t, ) + +#define DO_UZP(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t oprsz_2 = oprsz / 2; \ + intptr_t odd_ofs = simd_data(desc); \ + intptr_t i; \ + ARMVectorReg tmp_m; \ + if (unlikely((vm - vd) < (uintptr_t)oprsz)) { \ + vm = memcpy(&tmp_m, vm, oprsz); \ + } \ + for (i = 0; i < oprsz_2; i += sizeof(TYPE)) { \ + *(TYPE *)(vd + H(i)) = *(TYPE *)(vn + H(2 * i + odd_ofs)); \ + } \ + for (i = 0; i < oprsz_2; i += sizeof(TYPE)) { \ + *(TYPE *)(vd + H(oprsz_2 + i)) = *(TYPE *)(vm + H(2 * i + odd_ofs)); \ + } \ +} + +DO_UZP(sve_uzp_b, uint8_t, H1) +DO_UZP(sve_uzp_h, uint16_t, H1_2) +DO_UZP(sve_uzp_s, uint32_t, H1_4) +DO_UZP(sve_uzp_d, uint64_t, ) + +#define DO_TRN(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + intptr_t odd_ofs = simd_data(desc); \ + intptr_t i; \ + for (i = 0; i < oprsz; i += 2 * sizeof(TYPE)) { \ + TYPE ae = *(TYPE *)(vn + H(i + odd_ofs)); \ + TYPE be = *(TYPE *)(vm + H(i + odd_ofs)); \ + *(TYPE *)(vd + H(i + 0)) = ae; \ + *(TYPE *)(vd + H(i + sizeof(TYPE))) = be; \ + } \ +} + +DO_TRN(sve_trn_b, uint8_t, H1) +DO_TRN(sve_trn_h, uint16_t, H1_2) +DO_TRN(sve_trn_s, uint32_t, H1_4) +DO_TRN(sve_trn_d, uint64_t, ) + +#undef DO_ZIP +#undef DO_UZP +#undef DO_TRN + +void HELPER(sve_compact_s)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc) / 4; + uint32_t *d = vd, *n = vn; + uint8_t *pg = vg; + + for (i = j = 0; i < opr_sz; i++) { + if (pg[H1(i / 2)] & (i & 1 ? 0x10 : 0x01)) { + d[H4(j)] = n[H4(i)]; + j++; + } + } + for (; j < opr_sz; j++) { + d[H4(j)] = 0; + } +} + +void HELPER(sve_compact_d)(void *vd, void *vn, void *vg, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn; + uint8_t *pg = vg; + + for (i = j = 0; i < opr_sz; i++) { + if (pg[H1(i)] & 1) { + d[j] = n[i]; + j++; + } + } + for (; j < opr_sz; j++) { + d[j] = 0; + } +} + +/* Similar to the ARM LastActiveElement pseudocode function, except the + * result is multiplied by the element size. This includes the not found + * indication; e.g. not found for esz=3 is -8. + */ +int32_t HELPER(sve_last_active_element)(void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + + return last_active_element(vg, DIV_ROUND_UP(oprsz, 8), esz); +} + +void HELPER(sve_splice)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) +{ + intptr_t opr_sz = simd_oprsz(desc) / 8; + int esz = simd_data(desc); + uint64_t pg, first_g, last_g, len, mask = pred_esz_masks[esz]; + intptr_t i, first_i, last_i; + ARMVectorReg tmp; + + first_i = last_i = 0; + first_g = last_g = 0; + + /* Find the extent of the active elements within VG. */ + for (i = QEMU_ALIGN_UP(opr_sz, 8) - 8; i >= 0; i -= 8) { + pg = *(uint64_t *)(vg + i) & mask; + if (pg) { + if (last_g == 0) { + last_g = pg; + last_i = i; + } + first_g = pg; + first_i = i; + } + } + + len = 0; + if (first_g != 0) { + first_i = first_i * 8 + ctz64(first_g); + last_i = last_i * 8 + 63 - clz64(last_g); + len = last_i - first_i + (1 << esz); + if (vd == vm) { + vm = memcpy(&tmp, vm, opr_sz * 8); + } + swap_memmove(vd, vn + first_i, len); + } + swap_memmove(vd + len, vm, opr_sz * 8 - len); +} + +void HELPER(sve_sel_zpzz_b)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i], mm = m[i]; + uint64_t pp = expand_pred_b(pg[H1(i)]); + d[i] = (nn & pp) | (mm & ~pp); + } +} + +void HELPER(sve_sel_zpzz_h)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i], mm = m[i]; + uint64_t pp = expand_pred_h(pg[H1(i)]); + d[i] = (nn & pp) | (mm & ~pp); + } +} + +void HELPER(sve_sel_zpzz_s)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i], mm = m[i]; + uint64_t pp = expand_pred_s(pg[H1(i)]); + d[i] = (nn & pp) | (mm & ~pp); + } +} + +void HELPER(sve_sel_zpzz_d)(void *vd, void *vn, void *vm, + void *vg, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc) / 8; + uint64_t *d = vd, *n = vn, *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i += 1) { + uint64_t nn = n[i], mm = m[i]; + d[i] = (pg[H1(i)] & 1 ? nn : mm); + } +} + +/* Two operand comparison controlled by a predicate. + * ??? It is very tempting to want to be able to expand this inline + * with x86 instructions, e.g. + * + * vcmpeqw zm, zn, %ymm0 + * vpmovmskb %ymm0, %eax + * and $0x5555, %eax + * and pg, %eax + * + * or even aarch64, e.g. + * + * // mask = 4000 1000 0400 0100 0040 0010 0004 0001 + * cmeq v0.8h, zn, zm + * and v0.8h, v0.8h, mask + * addv h0, v0.8h + * and v0.8b, pg + * + * However, coming up with an abstraction that allows vector inputs and + * a scalar output, and also handles the byte-ordering of sub-uint64_t + * scalar outputs, is tricky. + */ +#define DO_CMP_PPZZ(NAME, TYPE, OP, H, MASK) \ +uint32_t HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t opr_sz = simd_oprsz(desc); \ + uint32_t flags = PREDTEST_INIT; \ + intptr_t i = opr_sz; \ + do { \ + uint64_t out = 0, pg; \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + out |= nn OP mm; \ + } while (i & 63); \ + pg = *(uint64_t *)(vg + (i >> 3)) & MASK; \ + out &= pg; \ + *(uint64_t *)(vd + (i >> 3)) = out; \ + flags = iter_predtest_bwd(out, pg, flags); \ + } while (i > 0); \ + return flags; \ +} + +#define DO_CMP_PPZZ_B(NAME, TYPE, OP) \ + DO_CMP_PPZZ(NAME, TYPE, OP, H1, 0xffffffffffffffffull) +#define DO_CMP_PPZZ_H(NAME, TYPE, OP) \ + DO_CMP_PPZZ(NAME, TYPE, OP, H1_2, 0x5555555555555555ull) +#define DO_CMP_PPZZ_S(NAME, TYPE, OP) \ + DO_CMP_PPZZ(NAME, TYPE, OP, H1_4, 0x1111111111111111ull) +#define DO_CMP_PPZZ_D(NAME, TYPE, OP) \ + DO_CMP_PPZZ(NAME, TYPE, OP, , 0x0101010101010101ull) + +DO_CMP_PPZZ_B(sve_cmpeq_ppzz_b, uint8_t, ==) +DO_CMP_PPZZ_H(sve_cmpeq_ppzz_h, uint16_t, ==) +DO_CMP_PPZZ_S(sve_cmpeq_ppzz_s, uint32_t, ==) +DO_CMP_PPZZ_D(sve_cmpeq_ppzz_d, uint64_t, ==) + +DO_CMP_PPZZ_B(sve_cmpne_ppzz_b, uint8_t, !=) +DO_CMP_PPZZ_H(sve_cmpne_ppzz_h, uint16_t, !=) +DO_CMP_PPZZ_S(sve_cmpne_ppzz_s, uint32_t, !=) +DO_CMP_PPZZ_D(sve_cmpne_ppzz_d, uint64_t, !=) + +DO_CMP_PPZZ_B(sve_cmpgt_ppzz_b, int8_t, >) +DO_CMP_PPZZ_H(sve_cmpgt_ppzz_h, int16_t, >) +DO_CMP_PPZZ_S(sve_cmpgt_ppzz_s, int32_t, >) +DO_CMP_PPZZ_D(sve_cmpgt_ppzz_d, int64_t, >) + +DO_CMP_PPZZ_B(sve_cmpge_ppzz_b, int8_t, >=) +DO_CMP_PPZZ_H(sve_cmpge_ppzz_h, int16_t, >=) +DO_CMP_PPZZ_S(sve_cmpge_ppzz_s, int32_t, >=) +DO_CMP_PPZZ_D(sve_cmpge_ppzz_d, int64_t, >=) + +DO_CMP_PPZZ_B(sve_cmphi_ppzz_b, uint8_t, >) +DO_CMP_PPZZ_H(sve_cmphi_ppzz_h, uint16_t, >) +DO_CMP_PPZZ_S(sve_cmphi_ppzz_s, uint32_t, >) +DO_CMP_PPZZ_D(sve_cmphi_ppzz_d, uint64_t, >) + +DO_CMP_PPZZ_B(sve_cmphs_ppzz_b, uint8_t, >=) +DO_CMP_PPZZ_H(sve_cmphs_ppzz_h, uint16_t, >=) +DO_CMP_PPZZ_S(sve_cmphs_ppzz_s, uint32_t, >=) +DO_CMP_PPZZ_D(sve_cmphs_ppzz_d, uint64_t, >=) + +#undef DO_CMP_PPZZ_B +#undef DO_CMP_PPZZ_H +#undef DO_CMP_PPZZ_S +#undef DO_CMP_PPZZ_D +#undef DO_CMP_PPZZ + +/* Similar, but the second source is "wide". */ +#define DO_CMP_PPZW(NAME, TYPE, TYPEW, OP, H, MASK) \ +uint32_t HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ +{ \ + intptr_t opr_sz = simd_oprsz(desc); \ + uint32_t flags = PREDTEST_INIT; \ + intptr_t i = opr_sz; \ + do { \ + uint64_t out = 0, pg; \ + do { \ + TYPEW mm = *(TYPEW *)(vm + i - 8); \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + out |= nn OP mm; \ + } while (i & 7); \ + } while (i & 63); \ + pg = *(uint64_t *)(vg + (i >> 3)) & MASK; \ + out &= pg; \ + *(uint64_t *)(vd + (i >> 3)) = out; \ + flags = iter_predtest_bwd(out, pg, flags); \ + } while (i > 0); \ + return flags; \ +} + +#define DO_CMP_PPZW_B(NAME, TYPE, TYPEW, OP) \ + DO_CMP_PPZW(NAME, TYPE, TYPEW, OP, H1, 0xffffffffffffffffull) +#define DO_CMP_PPZW_H(NAME, TYPE, TYPEW, OP) \ + DO_CMP_PPZW(NAME, TYPE, TYPEW, OP, H1_2, 0x5555555555555555ull) +#define DO_CMP_PPZW_S(NAME, TYPE, TYPEW, OP) \ + DO_CMP_PPZW(NAME, TYPE, TYPEW, OP, H1_4, 0x1111111111111111ull) + +DO_CMP_PPZW_B(sve_cmpeq_ppzw_b, uint8_t, uint64_t, ==) +DO_CMP_PPZW_H(sve_cmpeq_ppzw_h, uint16_t, uint64_t, ==) +DO_CMP_PPZW_S(sve_cmpeq_ppzw_s, uint32_t, uint64_t, ==) + +DO_CMP_PPZW_B(sve_cmpne_ppzw_b, uint8_t, uint64_t, !=) +DO_CMP_PPZW_H(sve_cmpne_ppzw_h, uint16_t, uint64_t, !=) +DO_CMP_PPZW_S(sve_cmpne_ppzw_s, uint32_t, uint64_t, !=) + +DO_CMP_PPZW_B(sve_cmpgt_ppzw_b, int8_t, int64_t, >) +DO_CMP_PPZW_H(sve_cmpgt_ppzw_h, int16_t, int64_t, >) +DO_CMP_PPZW_S(sve_cmpgt_ppzw_s, int32_t, int64_t, >) + +DO_CMP_PPZW_B(sve_cmpge_ppzw_b, int8_t, int64_t, >=) +DO_CMP_PPZW_H(sve_cmpge_ppzw_h, int16_t, int64_t, >=) +DO_CMP_PPZW_S(sve_cmpge_ppzw_s, int32_t, int64_t, >=) + +DO_CMP_PPZW_B(sve_cmphi_ppzw_b, uint8_t, uint64_t, >) +DO_CMP_PPZW_H(sve_cmphi_ppzw_h, uint16_t, uint64_t, >) +DO_CMP_PPZW_S(sve_cmphi_ppzw_s, uint32_t, uint64_t, >) + +DO_CMP_PPZW_B(sve_cmphs_ppzw_b, uint8_t, uint64_t, >=) +DO_CMP_PPZW_H(sve_cmphs_ppzw_h, uint16_t, uint64_t, >=) +DO_CMP_PPZW_S(sve_cmphs_ppzw_s, uint32_t, uint64_t, >=) + +DO_CMP_PPZW_B(sve_cmplt_ppzw_b, int8_t, int64_t, <) +DO_CMP_PPZW_H(sve_cmplt_ppzw_h, int16_t, int64_t, <) +DO_CMP_PPZW_S(sve_cmplt_ppzw_s, int32_t, int64_t, <) + +DO_CMP_PPZW_B(sve_cmple_ppzw_b, int8_t, int64_t, <=) +DO_CMP_PPZW_H(sve_cmple_ppzw_h, int16_t, int64_t, <=) +DO_CMP_PPZW_S(sve_cmple_ppzw_s, int32_t, int64_t, <=) + +DO_CMP_PPZW_B(sve_cmplo_ppzw_b, uint8_t, uint64_t, <) +DO_CMP_PPZW_H(sve_cmplo_ppzw_h, uint16_t, uint64_t, <) +DO_CMP_PPZW_S(sve_cmplo_ppzw_s, uint32_t, uint64_t, <) + +DO_CMP_PPZW_B(sve_cmpls_ppzw_b, uint8_t, uint64_t, <=) +DO_CMP_PPZW_H(sve_cmpls_ppzw_h, uint16_t, uint64_t, <=) +DO_CMP_PPZW_S(sve_cmpls_ppzw_s, uint32_t, uint64_t, <=) + +#undef DO_CMP_PPZW_B +#undef DO_CMP_PPZW_H +#undef DO_CMP_PPZW_S +#undef DO_CMP_PPZW + +/* Similar, but the second source is immediate. */ +#define DO_CMP_PPZI(NAME, TYPE, OP, H, MASK) \ +uint32_t HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + intptr_t opr_sz = simd_oprsz(desc); \ + uint32_t flags = PREDTEST_INIT; \ + TYPE mm = simd_data(desc); \ + intptr_t i = opr_sz; \ + do { \ + uint64_t out = 0, pg; \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + out |= nn OP mm; \ + } while (i & 63); \ + pg = *(uint64_t *)(vg + (i >> 3)) & MASK; \ + out &= pg; \ + *(uint64_t *)(vd + (i >> 3)) = out; \ + flags = iter_predtest_bwd(out, pg, flags); \ + } while (i > 0); \ + return flags; \ +} + +#define DO_CMP_PPZI_B(NAME, TYPE, OP) \ + DO_CMP_PPZI(NAME, TYPE, OP, H1, 0xffffffffffffffffull) +#define DO_CMP_PPZI_H(NAME, TYPE, OP) \ + DO_CMP_PPZI(NAME, TYPE, OP, H1_2, 0x5555555555555555ull) +#define DO_CMP_PPZI_S(NAME, TYPE, OP) \ + DO_CMP_PPZI(NAME, TYPE, OP, H1_4, 0x1111111111111111ull) +#define DO_CMP_PPZI_D(NAME, TYPE, OP) \ + DO_CMP_PPZI(NAME, TYPE, OP, , 0x0101010101010101ull) + +DO_CMP_PPZI_B(sve_cmpeq_ppzi_b, uint8_t, ==) +DO_CMP_PPZI_H(sve_cmpeq_ppzi_h, uint16_t, ==) +DO_CMP_PPZI_S(sve_cmpeq_ppzi_s, uint32_t, ==) +DO_CMP_PPZI_D(sve_cmpeq_ppzi_d, uint64_t, ==) + +DO_CMP_PPZI_B(sve_cmpne_ppzi_b, uint8_t, !=) +DO_CMP_PPZI_H(sve_cmpne_ppzi_h, uint16_t, !=) +DO_CMP_PPZI_S(sve_cmpne_ppzi_s, uint32_t, !=) +DO_CMP_PPZI_D(sve_cmpne_ppzi_d, uint64_t, !=) + +DO_CMP_PPZI_B(sve_cmpgt_ppzi_b, int8_t, >) +DO_CMP_PPZI_H(sve_cmpgt_ppzi_h, int16_t, >) +DO_CMP_PPZI_S(sve_cmpgt_ppzi_s, int32_t, >) +DO_CMP_PPZI_D(sve_cmpgt_ppzi_d, int64_t, >) + +DO_CMP_PPZI_B(sve_cmpge_ppzi_b, int8_t, >=) +DO_CMP_PPZI_H(sve_cmpge_ppzi_h, int16_t, >=) +DO_CMP_PPZI_S(sve_cmpge_ppzi_s, int32_t, >=) +DO_CMP_PPZI_D(sve_cmpge_ppzi_d, int64_t, >=) + +DO_CMP_PPZI_B(sve_cmphi_ppzi_b, uint8_t, >) +DO_CMP_PPZI_H(sve_cmphi_ppzi_h, uint16_t, >) +DO_CMP_PPZI_S(sve_cmphi_ppzi_s, uint32_t, >) +DO_CMP_PPZI_D(sve_cmphi_ppzi_d, uint64_t, >) + +DO_CMP_PPZI_B(sve_cmphs_ppzi_b, uint8_t, >=) +DO_CMP_PPZI_H(sve_cmphs_ppzi_h, uint16_t, >=) +DO_CMP_PPZI_S(sve_cmphs_ppzi_s, uint32_t, >=) +DO_CMP_PPZI_D(sve_cmphs_ppzi_d, uint64_t, >=) + +DO_CMP_PPZI_B(sve_cmplt_ppzi_b, int8_t, <) +DO_CMP_PPZI_H(sve_cmplt_ppzi_h, int16_t, <) +DO_CMP_PPZI_S(sve_cmplt_ppzi_s, int32_t, <) +DO_CMP_PPZI_D(sve_cmplt_ppzi_d, int64_t, <) + +DO_CMP_PPZI_B(sve_cmple_ppzi_b, int8_t, <=) +DO_CMP_PPZI_H(sve_cmple_ppzi_h, int16_t, <=) +DO_CMP_PPZI_S(sve_cmple_ppzi_s, int32_t, <=) +DO_CMP_PPZI_D(sve_cmple_ppzi_d, int64_t, <=) + +DO_CMP_PPZI_B(sve_cmplo_ppzi_b, uint8_t, <) +DO_CMP_PPZI_H(sve_cmplo_ppzi_h, uint16_t, <) +DO_CMP_PPZI_S(sve_cmplo_ppzi_s, uint32_t, <) +DO_CMP_PPZI_D(sve_cmplo_ppzi_d, uint64_t, <) + +DO_CMP_PPZI_B(sve_cmpls_ppzi_b, uint8_t, <=) +DO_CMP_PPZI_H(sve_cmpls_ppzi_h, uint16_t, <=) +DO_CMP_PPZI_S(sve_cmpls_ppzi_s, uint32_t, <=) +DO_CMP_PPZI_D(sve_cmpls_ppzi_d, uint64_t, <=) + +#undef DO_CMP_PPZI_B +#undef DO_CMP_PPZI_H +#undef DO_CMP_PPZI_S +#undef DO_CMP_PPZI_D +#undef DO_CMP_PPZI + +/* Similar to the ARM LastActive pseudocode function. */ +static bool last_active_pred(void *vd, void *vg, intptr_t oprsz) +{ + intptr_t i; + + for (i = QEMU_ALIGN_UP(oprsz, 8) - 8; i >= 0; i -= 8) { + uint64_t pg = *(uint64_t *)(vg + i); + if (pg) { + return (pow2floor(pg) & *(uint64_t *)(vd + i)) != 0; + } + } + return 0; +} + +/* Compute a mask into RETB that is true for all G, up to and including + * (if after) or excluding (if !after) the first G & N. + * Return true if BRK found. + */ +static bool compute_brk(uint64_t *retb, uint64_t n, uint64_t g, + bool brk, bool after) +{ + uint64_t b; + + if (brk) { + b = 0; + } else if ((g & n) == 0) { + /* For all G, no N are set; break not found. */ + b = g; + } else { + /* Break somewhere in N. Locate it. */ + b = g & n; /* guard true, pred true */ + b = b & -b; /* first such */ + if (after) { + b = b | (b - 1); /* break after same */ + } else { + b = b - 1; /* break before same */ + } + brk = true; + } + + *retb = b; + return brk; +} + +/* Compute a zeroing BRK. */ +static void compute_brk_z(uint64_t *d, uint64_t *n, uint64_t *g, + intptr_t oprsz, bool after) +{ + bool brk = false; + intptr_t i; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); ++i) { + uint64_t this_b, this_g = g[i]; + + brk = compute_brk(&this_b, n[i], this_g, brk, after); + d[i] = this_b & this_g; + } +} + +/* Likewise, but also compute flags. */ +static uint32_t compute_brks_z(uint64_t *d, uint64_t *n, uint64_t *g, + intptr_t oprsz, bool after) +{ + uint32_t flags = PREDTEST_INIT; + bool brk = false; + intptr_t i; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); ++i) { + uint64_t this_b, this_d, this_g = g[i]; + + brk = compute_brk(&this_b, n[i], this_g, brk, after); + d[i] = this_d = this_b & this_g; + flags = iter_predtest_fwd(this_d, this_g, flags); + } + return flags; +} + +/* Compute a merging BRK. */ +static void compute_brk_m(uint64_t *d, uint64_t *n, uint64_t *g, + intptr_t oprsz, bool after) +{ + bool brk = false; + intptr_t i; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); ++i) { + uint64_t this_b, this_g = g[i]; + + brk = compute_brk(&this_b, n[i], this_g, brk, after); + d[i] = (this_b & this_g) | (d[i] & ~this_g); + } +} + +/* Likewise, but also compute flags. */ +static uint32_t compute_brks_m(uint64_t *d, uint64_t *n, uint64_t *g, + intptr_t oprsz, bool after) +{ + uint32_t flags = PREDTEST_INIT; + bool brk = false; + intptr_t i; + + for (i = 0; i < oprsz / 8; ++i) { + uint64_t this_b, this_d = d[i], this_g = g[i]; + + brk = compute_brk(&this_b, n[i], this_g, brk, after); + d[i] = this_d = (this_b & this_g) | (this_d & ~this_g); + flags = iter_predtest_fwd(this_d, this_g, flags); + } + return flags; +} + +static uint32_t do_zero(ARMPredicateReg *d, intptr_t oprsz) +{ + /* It is quicker to zero the whole predicate than loop on OPRSZ. + * The compiler should turn this into 4 64-bit integer stores. + */ + memset(d, 0, sizeof(ARMPredicateReg)); + return PREDTEST_INIT; +} + +void HELPER(sve_brkpa)(void *vd, void *vn, void *vm, void *vg, + uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + if (last_active_pred(vn, vg, oprsz)) { + compute_brk_z(vd, vm, vg, oprsz, true); + } else { + do_zero(vd, oprsz); + } +} + +uint32_t HELPER(sve_brkpas)(void *vd, void *vn, void *vm, void *vg, + uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + if (last_active_pred(vn, vg, oprsz)) { + return compute_brks_z(vd, vm, vg, oprsz, true); + } else { + return do_zero(vd, oprsz); + } +} + +void HELPER(sve_brkpb)(void *vd, void *vn, void *vm, void *vg, + uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + if (last_active_pred(vn, vg, oprsz)) { + compute_brk_z(vd, vm, vg, oprsz, false); + } else { + do_zero(vd, oprsz); + } +} + +uint32_t HELPER(sve_brkpbs)(void *vd, void *vn, void *vm, void *vg, + uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + if (last_active_pred(vn, vg, oprsz)) { + return compute_brks_z(vd, vm, vg, oprsz, false); + } else { + return do_zero(vd, oprsz); + } +} + +void HELPER(sve_brka_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + compute_brk_z(vd, vn, vg, oprsz, true); +} + +uint32_t HELPER(sve_brkas_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + return compute_brks_z(vd, vn, vg, oprsz, true); +} + +void HELPER(sve_brkb_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + compute_brk_z(vd, vn, vg, oprsz, false); +} + +uint32_t HELPER(sve_brkbs_z)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + return compute_brks_z(vd, vn, vg, oprsz, false); +} + +void HELPER(sve_brka_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + compute_brk_m(vd, vn, vg, oprsz, true); +} + +uint32_t HELPER(sve_brkas_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + return compute_brks_m(vd, vn, vg, oprsz, true); +} + +void HELPER(sve_brkb_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + compute_brk_m(vd, vn, vg, oprsz, false); +} + +uint32_t HELPER(sve_brkbs_m)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + return compute_brks_m(vd, vn, vg, oprsz, false); +} + +void HELPER(sve_brkn)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + + if (!last_active_pred(vn, vg, oprsz)) { + do_zero(vd, oprsz); + } +} + +/* As if PredTest(Ones(PL), D, esz). */ +static uint32_t predtest_ones(ARMPredicateReg *d, intptr_t oprsz, + uint64_t esz_mask) +{ + uint32_t flags = PREDTEST_INIT; + intptr_t i; + + for (i = 0; i < oprsz / 8; i++) { + flags = iter_predtest_fwd(d->p[i], esz_mask, flags); + } + if (oprsz & 7) { + uint64_t mask = ~(-1ULL << (8 * (oprsz & 7))); + flags = iter_predtest_fwd(d->p[i], esz_mask & mask, flags); + } + return flags; +} + +uint32_t HELPER(sve_brkns)(void *vd, void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + + if (last_active_pred(vn, vg, oprsz)) { + return predtest_ones(vd, oprsz, -1); + } else { + return do_zero(vd, oprsz); + } +} + +uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) +{ + intptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + uint64_t *n = vn, *g = vg, sum = 0, mask = pred_esz_masks[esz]; + intptr_t i; + + for (i = 0; i < DIV_ROUND_UP(oprsz, 8); ++i) { + uint64_t t = n[i] & g[i] & mask; + sum += ctpop64(t); + } + return sum; +} + +uint32_t HELPER(sve_while)(void *vd, uint32_t count, uint32_t pred_desc) +{ + uintptr_t oprsz = extract32(pred_desc, 0, SIMD_OPRSZ_BITS) + 2; + intptr_t esz = extract32(pred_desc, SIMD_DATA_SHIFT, 2); + uint64_t esz_mask = pred_esz_masks[esz]; + ARMPredicateReg *d = vd; + uint32_t flags; + intptr_t i; + + /* Begin with a zero predicate register. */ + flags = do_zero(d, oprsz); + if (count == 0) { + return flags; + } + + /* Scale from predicate element count to bits. */ + count <<= esz; + /* Bound to the bits in the predicate. */ + count = MIN(count, oprsz * 8); + + /* Set all of the requested bits. */ + for (i = 0; i < count / 64; ++i) { + d->p[i] = esz_mask; + } + if (count & 63) { + d->p[i] = MAKE_64BIT_MASK(0, count & 63) & esz_mask; + } + + return predtest_ones(d, oprsz, esz_mask); +} + +/* Recursive reduction on a function; + * C.f. the ARM ARM function ReducePredicated. + * + * While it would be possible to write this without the DATA temporary, + * it is much simpler to process the predicate register this way. + * The recursion is bounded to depth 7 (128 fp16 elements), so there's + * little to gain with a more complex non-recursive form. + */ +#define DO_REDUCE(NAME, TYPE, H, FUNC, IDENT) \ +static TYPE NAME##_reduce(TYPE *data, float_status *status, uintptr_t n) \ +{ \ + if (n == 1) { \ + return *data; \ + } else { \ + uintptr_t half = n / 2; \ + TYPE lo = NAME##_reduce(data, status, half); \ + TYPE hi = NAME##_reduce(data + half, status, half); \ + return TYPE##_##FUNC(lo, hi, status); \ + } \ +} \ +uint64_t HELPER(NAME)(void *vn, void *vg, void *vs, uint32_t desc) \ +{ \ + uintptr_t i, oprsz = simd_oprsz(desc), maxsz = simd_maxsz(desc); \ + TYPE data[sizeof(ARMVectorReg) / sizeof(TYPE)]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)((void *)data + i) = (pg & 1 ? nn : IDENT); \ + i += sizeof(TYPE), pg >>= sizeof(TYPE); \ + } while (i & 15); \ + } \ + for (; i < maxsz; i += sizeof(TYPE)) { \ + *(TYPE *)((void *)data + i) = IDENT; \ + } \ + return NAME##_reduce(data, vs, maxsz / sizeof(TYPE)); \ +} + +DO_REDUCE(sve_faddv_h, float16, H1_2, add, float16_zero) +DO_REDUCE(sve_faddv_s, float32, H1_4, add, float32_zero) +DO_REDUCE(sve_faddv_d, float64, , add, float64_zero) + +/* Identity is floatN_default_nan, without the function call. */ +DO_REDUCE(sve_fminnmv_h, float16, H1_2, minnum, 0x7E00) +DO_REDUCE(sve_fminnmv_s, float32, H1_4, minnum, 0x7FC00000) +DO_REDUCE(sve_fminnmv_d, float64, , minnum, 0x7FF8000000000000ULL) + +DO_REDUCE(sve_fmaxnmv_h, float16, H1_2, maxnum, 0x7E00) +DO_REDUCE(sve_fmaxnmv_s, float32, H1_4, maxnum, 0x7FC00000) +DO_REDUCE(sve_fmaxnmv_d, float64, , maxnum, 0x7FF8000000000000ULL) + +DO_REDUCE(sve_fminv_h, float16, H1_2, min, float16_infinity) +DO_REDUCE(sve_fminv_s, float32, H1_4, min, float32_infinity) +DO_REDUCE(sve_fminv_d, float64, , min, float64_infinity) + +DO_REDUCE(sve_fmaxv_h, float16, H1_2, max, float16_chs(float16_infinity)) +DO_REDUCE(sve_fmaxv_s, float32, H1_4, max, float32_chs(float32_infinity)) +DO_REDUCE(sve_fmaxv_d, float64, , max, float64_chs(float64_infinity)) + +#undef DO_REDUCE + +uint64_t HELPER(sve_fadda_h)(uint64_t nn, void *vm, void *vg, + void *status, uint32_t desc) +{ + intptr_t i = 0, opr_sz = simd_oprsz(desc); + float16 result = nn; + + do { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + float16 mm = *(float16 *)(vm + H1_2(i)); + result = float16_add(result, mm, status); + } + i += sizeof(float16), pg >>= sizeof(float16); + } while (i & 15); + } while (i < opr_sz); + + return result; +} + +uint64_t HELPER(sve_fadda_s)(uint64_t nn, void *vm, void *vg, + void *status, uint32_t desc) +{ + intptr_t i = 0, opr_sz = simd_oprsz(desc); + float32 result = nn; + + do { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + float32 mm = *(float32 *)(vm + H1_2(i)); + result = float32_add(result, mm, status); + } + i += sizeof(float32), pg >>= sizeof(float32); + } while (i & 15); + } while (i < opr_sz); + + return result; +} + +uint64_t HELPER(sve_fadda_d)(uint64_t nn, void *vm, void *vg, + void *status, uint32_t desc) +{ + intptr_t i = 0, opr_sz = simd_oprsz(desc) / 8; + uint64_t *m = vm; + uint8_t *pg = vg; + + for (i = 0; i < opr_sz; i++) { + if (pg[H1(i)] & 1) { + nn = float64_add(nn, m[i], status); + } + } + + return nn; +} + +/* Fully general three-operand expander, controlled by a predicate, + * With the extra float_status parameter. + */ +#define DO_ZPZZ_FP(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ + void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc); \ + uint64_t *g = vg; \ + do { \ + uint64_t pg = g[(i - 1) >> 6]; \ + do { \ + i -= sizeof(TYPE); \ + if (likely((pg >> (i & 63)) & 1)) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm, status); \ + } \ + } while (i & 63); \ + } while (i != 0); \ +} + +DO_ZPZZ_FP(sve_fadd_h, uint16_t, H1_2, float16_add) +DO_ZPZZ_FP(sve_fadd_s, uint32_t, H1_4, float32_add) +DO_ZPZZ_FP(sve_fadd_d, uint64_t, , float64_add) + +DO_ZPZZ_FP(sve_fsub_h, uint16_t, H1_2, float16_sub) +DO_ZPZZ_FP(sve_fsub_s, uint32_t, H1_4, float32_sub) +DO_ZPZZ_FP(sve_fsub_d, uint64_t, , float64_sub) + +DO_ZPZZ_FP(sve_fmul_h, uint16_t, H1_2, float16_mul) +DO_ZPZZ_FP(sve_fmul_s, uint32_t, H1_4, float32_mul) +DO_ZPZZ_FP(sve_fmul_d, uint64_t, , float64_mul) + +DO_ZPZZ_FP(sve_fdiv_h, uint16_t, H1_2, float16_div) +DO_ZPZZ_FP(sve_fdiv_s, uint32_t, H1_4, float32_div) +DO_ZPZZ_FP(sve_fdiv_d, uint64_t, , float64_div) + +DO_ZPZZ_FP(sve_fmin_h, uint16_t, H1_2, float16_min) +DO_ZPZZ_FP(sve_fmin_s, uint32_t, H1_4, float32_min) +DO_ZPZZ_FP(sve_fmin_d, uint64_t, , float64_min) + +DO_ZPZZ_FP(sve_fmax_h, uint16_t, H1_2, float16_max) +DO_ZPZZ_FP(sve_fmax_s, uint32_t, H1_4, float32_max) +DO_ZPZZ_FP(sve_fmax_d, uint64_t, , float64_max) + +DO_ZPZZ_FP(sve_fminnum_h, uint16_t, H1_2, float16_minnum) +DO_ZPZZ_FP(sve_fminnum_s, uint32_t, H1_4, float32_minnum) +DO_ZPZZ_FP(sve_fminnum_d, uint64_t, , float64_minnum) + +DO_ZPZZ_FP(sve_fmaxnum_h, uint16_t, H1_2, float16_maxnum) +DO_ZPZZ_FP(sve_fmaxnum_s, uint32_t, H1_4, float32_maxnum) +DO_ZPZZ_FP(sve_fmaxnum_d, uint64_t, , float64_maxnum) + +static inline float16 abd_h(float16 a, float16 b, float_status *s) +{ + return float16_abs(float16_sub(a, b, s)); +} + +static inline float32 abd_s(float32 a, float32 b, float_status *s) +{ + return float32_abs(float32_sub(a, b, s)); +} + +static inline float64 abd_d(float64 a, float64 b, float_status *s) +{ + return float64_abs(float64_sub(a, b, s)); +} + +DO_ZPZZ_FP(sve_fabd_h, uint16_t, H1_2, abd_h) +DO_ZPZZ_FP(sve_fabd_s, uint32_t, H1_4, abd_s) +DO_ZPZZ_FP(sve_fabd_d, uint64_t, , abd_d) + +static inline float64 scalbn_d(float64 a, int64_t b, float_status *s) +{ + int b_int = MIN(MAX(b, INT_MIN), INT_MAX); + return float64_scalbn(a, b_int, s); +} + +DO_ZPZZ_FP(sve_fscalbn_h, int16_t, H1_2, float16_scalbn) +DO_ZPZZ_FP(sve_fscalbn_s, int32_t, H1_4, float32_scalbn) +DO_ZPZZ_FP(sve_fscalbn_d, int64_t, , scalbn_d) + +DO_ZPZZ_FP(sve_fmulx_h, uint16_t, H1_2, helper_advsimd_mulxh) +DO_ZPZZ_FP(sve_fmulx_s, uint32_t, H1_4, helper_vfp_mulxs) +DO_ZPZZ_FP(sve_fmulx_d, uint64_t, , helper_vfp_mulxd) + +#undef DO_ZPZZ_FP + +/* Three-operand expander, with one scalar operand, controlled by + * a predicate, with the extra float_status parameter. + */ +#define DO_ZPZS_FP(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint64_t scalar, \ + void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc); \ + uint64_t *g = vg; \ + TYPE mm = scalar; \ + do { \ + uint64_t pg = g[(i - 1) >> 6]; \ + do { \ + i -= sizeof(TYPE); \ + if (likely((pg >> (i & 63)) & 1)) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, mm, status); \ + } \ + } while (i & 63); \ + } while (i != 0); \ +} + +DO_ZPZS_FP(sve_fadds_h, float16, H1_2, float16_add) +DO_ZPZS_FP(sve_fadds_s, float32, H1_4, float32_add) +DO_ZPZS_FP(sve_fadds_d, float64, , float64_add) + +DO_ZPZS_FP(sve_fsubs_h, float16, H1_2, float16_sub) +DO_ZPZS_FP(sve_fsubs_s, float32, H1_4, float32_sub) +DO_ZPZS_FP(sve_fsubs_d, float64, , float64_sub) + +DO_ZPZS_FP(sve_fmuls_h, float16, H1_2, float16_mul) +DO_ZPZS_FP(sve_fmuls_s, float32, H1_4, float32_mul) +DO_ZPZS_FP(sve_fmuls_d, float64, , float64_mul) + +static inline float16 subr_h(float16 a, float16 b, float_status *s) +{ + return float16_sub(b, a, s); +} + +static inline float32 subr_s(float32 a, float32 b, float_status *s) +{ + return float32_sub(b, a, s); +} + +static inline float64 subr_d(float64 a, float64 b, float_status *s) +{ + return float64_sub(b, a, s); +} + +DO_ZPZS_FP(sve_fsubrs_h, float16, H1_2, subr_h) +DO_ZPZS_FP(sve_fsubrs_s, float32, H1_4, subr_s) +DO_ZPZS_FP(sve_fsubrs_d, float64, , subr_d) + +DO_ZPZS_FP(sve_fmaxnms_h, float16, H1_2, float16_maxnum) +DO_ZPZS_FP(sve_fmaxnms_s, float32, H1_4, float32_maxnum) +DO_ZPZS_FP(sve_fmaxnms_d, float64, , float64_maxnum) + +DO_ZPZS_FP(sve_fminnms_h, float16, H1_2, float16_minnum) +DO_ZPZS_FP(sve_fminnms_s, float32, H1_4, float32_minnum) +DO_ZPZS_FP(sve_fminnms_d, float64, , float64_minnum) + +DO_ZPZS_FP(sve_fmaxs_h, float16, H1_2, float16_max) +DO_ZPZS_FP(sve_fmaxs_s, float32, H1_4, float32_max) +DO_ZPZS_FP(sve_fmaxs_d, float64, , float64_max) + +DO_ZPZS_FP(sve_fmins_h, float16, H1_2, float16_min) +DO_ZPZS_FP(sve_fmins_s, float32, H1_4, float32_min) +DO_ZPZS_FP(sve_fmins_d, float64, , float64_min) + +/* Fully general two-operand expander, controlled by a predicate, + * With the extra float_status parameter. + */ +#define DO_ZPZ_FP(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc); \ + uint64_t *g = vg; \ + do { \ + uint64_t pg = g[(i - 1) >> 6]; \ + do { \ + i -= sizeof(TYPE); \ + if (likely((pg >> (i & 63)) & 1)) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + *(TYPE *)(vd + H(i)) = OP(nn, status); \ + } \ + } while (i & 63); \ + } while (i != 0); \ +} + +/* SVE fp16 conversions always use IEEE mode. Like AdvSIMD, they ignore + * FZ16. When converting from fp16, this affects flushing input denormals; + * when converting to fp16, this affects flushing output denormals. + */ +static inline float32 sve_f16_to_f32(float16 f, float_status *fpst) +{ + flag save = get_flush_inputs_to_zero(fpst); + float32 ret; + + set_flush_inputs_to_zero(false, fpst); + ret = float16_to_float32(f, true, fpst); + set_flush_inputs_to_zero(save, fpst); + return ret; +} + +static inline float64 sve_f16_to_f64(float16 f, float_status *fpst) +{ + flag save = get_flush_inputs_to_zero(fpst); + float64 ret; + + set_flush_inputs_to_zero(false, fpst); + ret = float16_to_float64(f, true, fpst); + set_flush_inputs_to_zero(save, fpst); + return ret; +} + +static inline float16 sve_f32_to_f16(float32 f, float_status *fpst) +{ + flag save = get_flush_to_zero(fpst); + float16 ret; + + set_flush_to_zero(false, fpst); + ret = float32_to_float16(f, true, fpst); + set_flush_to_zero(save, fpst); + return ret; +} + +static inline float16 sve_f64_to_f16(float64 f, float_status *fpst) +{ + flag save = get_flush_to_zero(fpst); + float16 ret; + + set_flush_to_zero(false, fpst); + ret = float64_to_float16(f, true, fpst); + set_flush_to_zero(save, fpst); + return ret; +} + +static inline int16_t vfp_float16_to_int16_rtz(float16 f, float_status *s) +{ + if (float16_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float16_to_int16_round_to_zero(f, s); +} + +static inline int64_t vfp_float16_to_int64_rtz(float16 f, float_status *s) +{ + if (float16_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float16_to_int64_round_to_zero(f, s); +} + +static inline int64_t vfp_float32_to_int64_rtz(float32 f, float_status *s) +{ + if (float32_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float32_to_int64_round_to_zero(f, s); +} + +static inline int64_t vfp_float64_to_int64_rtz(float64 f, float_status *s) +{ + if (float64_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float64_to_int64_round_to_zero(f, s); +} + +static inline uint16_t vfp_float16_to_uint16_rtz(float16 f, float_status *s) +{ + if (float16_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float16_to_uint16_round_to_zero(f, s); +} + +static inline uint64_t vfp_float16_to_uint64_rtz(float16 f, float_status *s) +{ + if (float16_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float16_to_uint64_round_to_zero(f, s); +} + +static inline uint64_t vfp_float32_to_uint64_rtz(float32 f, float_status *s) +{ + if (float32_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float32_to_uint64_round_to_zero(f, s); +} + +static inline uint64_t vfp_float64_to_uint64_rtz(float64 f, float_status *s) +{ + if (float64_is_any_nan(f)) { + float_raise(float_flag_invalid, s); + return 0; + } + return float64_to_uint64_round_to_zero(f, s); +} + +DO_ZPZ_FP(sve_fcvt_sh, uint32_t, H1_4, sve_f32_to_f16) +DO_ZPZ_FP(sve_fcvt_hs, uint32_t, H1_4, sve_f16_to_f32) +DO_ZPZ_FP(sve_fcvt_dh, uint64_t, , sve_f64_to_f16) +DO_ZPZ_FP(sve_fcvt_hd, uint64_t, , sve_f16_to_f64) +DO_ZPZ_FP(sve_fcvt_ds, uint64_t, , float64_to_float32) +DO_ZPZ_FP(sve_fcvt_sd, uint64_t, , float32_to_float64) + +DO_ZPZ_FP(sve_fcvtzs_hh, uint16_t, H1_2, vfp_float16_to_int16_rtz) +DO_ZPZ_FP(sve_fcvtzs_hs, uint32_t, H1_4, helper_vfp_tosizh) +DO_ZPZ_FP(sve_fcvtzs_ss, uint32_t, H1_4, helper_vfp_tosizs) +DO_ZPZ_FP(sve_fcvtzs_hd, uint64_t, , vfp_float16_to_int64_rtz) +DO_ZPZ_FP(sve_fcvtzs_sd, uint64_t, , vfp_float32_to_int64_rtz) +DO_ZPZ_FP(sve_fcvtzs_ds, uint64_t, , helper_vfp_tosizd) +DO_ZPZ_FP(sve_fcvtzs_dd, uint64_t, , vfp_float64_to_int64_rtz) + +DO_ZPZ_FP(sve_fcvtzu_hh, uint16_t, H1_2, vfp_float16_to_uint16_rtz) +DO_ZPZ_FP(sve_fcvtzu_hs, uint32_t, H1_4, helper_vfp_touizh) +DO_ZPZ_FP(sve_fcvtzu_ss, uint32_t, H1_4, helper_vfp_touizs) +DO_ZPZ_FP(sve_fcvtzu_hd, uint64_t, , vfp_float16_to_uint64_rtz) +DO_ZPZ_FP(sve_fcvtzu_sd, uint64_t, , vfp_float32_to_uint64_rtz) +DO_ZPZ_FP(sve_fcvtzu_ds, uint64_t, , helper_vfp_touizd) +DO_ZPZ_FP(sve_fcvtzu_dd, uint64_t, , vfp_float64_to_uint64_rtz) + +DO_ZPZ_FP(sve_frint_h, uint16_t, H1_2, helper_advsimd_rinth) +DO_ZPZ_FP(sve_frint_s, uint32_t, H1_4, helper_rints) +DO_ZPZ_FP(sve_frint_d, uint64_t, , helper_rintd) + +DO_ZPZ_FP(sve_frintx_h, uint16_t, H1_2, float16_round_to_int) +DO_ZPZ_FP(sve_frintx_s, uint32_t, H1_4, float32_round_to_int) +DO_ZPZ_FP(sve_frintx_d, uint64_t, , float64_round_to_int) + +DO_ZPZ_FP(sve_frecpx_h, uint16_t, H1_2, helper_frecpx_f16) +DO_ZPZ_FP(sve_frecpx_s, uint32_t, H1_4, helper_frecpx_f32) +DO_ZPZ_FP(sve_frecpx_d, uint64_t, , helper_frecpx_f64) + +DO_ZPZ_FP(sve_fsqrt_h, uint16_t, H1_2, float16_sqrt) +DO_ZPZ_FP(sve_fsqrt_s, uint32_t, H1_4, float32_sqrt) +DO_ZPZ_FP(sve_fsqrt_d, uint64_t, , float64_sqrt) + +DO_ZPZ_FP(sve_scvt_hh, uint16_t, H1_2, int16_to_float16) +DO_ZPZ_FP(sve_scvt_sh, uint32_t, H1_4, int32_to_float16) +DO_ZPZ_FP(sve_scvt_ss, uint32_t, H1_4, int32_to_float32) +DO_ZPZ_FP(sve_scvt_sd, uint64_t, , int32_to_float64) +DO_ZPZ_FP(sve_scvt_dh, uint64_t, , int64_to_float16) +DO_ZPZ_FP(sve_scvt_ds, uint64_t, , int64_to_float32) +DO_ZPZ_FP(sve_scvt_dd, uint64_t, , int64_to_float64) + +DO_ZPZ_FP(sve_ucvt_hh, uint16_t, H1_2, uint16_to_float16) +DO_ZPZ_FP(sve_ucvt_sh, uint32_t, H1_4, uint32_to_float16) +DO_ZPZ_FP(sve_ucvt_ss, uint32_t, H1_4, uint32_to_float32) +DO_ZPZ_FP(sve_ucvt_sd, uint64_t, , uint32_to_float64) +DO_ZPZ_FP(sve_ucvt_dh, uint64_t, , uint64_to_float16) +DO_ZPZ_FP(sve_ucvt_ds, uint64_t, , uint64_to_float32) +DO_ZPZ_FP(sve_ucvt_dd, uint64_t, , uint64_to_float64) + +#undef DO_ZPZ_FP + +/* 4-operand predicated multiply-add. This requires 7 operands to pass + * "properly", so we need to encode some of the registers into DESC. + */ +QEMU_BUILD_BUG_ON(SIMD_DATA_SHIFT + 20 > 32); + +static void do_fmla_zpzzz_h(CPUARMState *env, void *vg, uint32_t desc, + uint16_t neg1, uint16_t neg3) +{ + intptr_t i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + i -= 2; + if (likely((pg >> (i & 63)) & 1)) { + float16 e1, e2, e3, r; + + e1 = *(uint16_t *)(vn + H1_2(i)) ^ neg1; + e2 = *(uint16_t *)(vm + H1_2(i)); + e3 = *(uint16_t *)(va + H1_2(i)) ^ neg3; + r = float16_muladd(e1, e2, e3, 0, &env->vfp.fp_status); + *(uint16_t *)(vd + H1_2(i)) = r; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fmla_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_h(env, vg, desc, 0, 0); +} + +void HELPER(sve_fmls_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_h(env, vg, desc, 0x8000, 0); +} + +void HELPER(sve_fnmla_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_h(env, vg, desc, 0x8000, 0x8000); +} + +void HELPER(sve_fnmls_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_h(env, vg, desc, 0, 0x8000); +} + +static void do_fmla_zpzzz_s(CPUARMState *env, void *vg, uint32_t desc, + uint32_t neg1, uint32_t neg3) +{ + intptr_t i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + i -= 4; + if (likely((pg >> (i & 63)) & 1)) { + float32 e1, e2, e3, r; + + e1 = *(uint32_t *)(vn + H1_4(i)) ^ neg1; + e2 = *(uint32_t *)(vm + H1_4(i)); + e3 = *(uint32_t *)(va + H1_4(i)) ^ neg3; + r = float32_muladd(e1, e2, e3, 0, &env->vfp.fp_status); + *(uint32_t *)(vd + H1_4(i)) = r; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fmla_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_s(env, vg, desc, 0, 0); +} + +void HELPER(sve_fmls_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_s(env, vg, desc, 0x80000000, 0); +} + +void HELPER(sve_fnmla_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_s(env, vg, desc, 0x80000000, 0x80000000); +} + +void HELPER(sve_fnmls_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_s(env, vg, desc, 0, 0x80000000); +} + +static void do_fmla_zpzzz_d(CPUARMState *env, void *vg, uint32_t desc, + uint64_t neg1, uint64_t neg3) +{ + intptr_t i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + i -= 8; + if (likely((pg >> (i & 63)) & 1)) { + float64 e1, e2, e3, r; + + e1 = *(uint64_t *)(vn + i) ^ neg1; + e2 = *(uint64_t *)(vm + i); + e3 = *(uint64_t *)(va + i) ^ neg3; + r = float64_muladd(e1, e2, e3, 0, &env->vfp.fp_status); + *(uint64_t *)(vd + i) = r; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fmla_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_d(env, vg, desc, 0, 0); +} + +void HELPER(sve_fmls_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_d(env, vg, desc, INT64_MIN, 0); +} + +void HELPER(sve_fnmla_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_d(env, vg, desc, INT64_MIN, INT64_MIN); +} + +void HELPER(sve_fnmls_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + do_fmla_zpzzz_d(env, vg, desc, 0, INT64_MIN); +} + +/* Two operand floating-point comparison controlled by a predicate. + * Unlike the integer version, we are not allowed to optimistically + * compare operands, since the comparison may have side effects wrt + * the FPSR. + */ +#define DO_FPCMP_PPZZ(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ + void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc), j = (i - 1) >> 6; \ + uint64_t *d = vd, *g = vg; \ + do { \ + uint64_t out = 0, pg = g[j]; \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + if (likely((pg >> (i & 63)) & 1)) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + TYPE mm = *(TYPE *)(vm + H(i)); \ + out |= OP(TYPE, nn, mm, status); \ + } \ + } while (i & 63); \ + d[j--] = out; \ + } while (i > 0); \ +} + +#define DO_FPCMP_PPZZ_H(NAME, OP) \ + DO_FPCMP_PPZZ(NAME##_h, float16, H1_2, OP) +#define DO_FPCMP_PPZZ_S(NAME, OP) \ + DO_FPCMP_PPZZ(NAME##_s, float32, H1_4, OP) +#define DO_FPCMP_PPZZ_D(NAME, OP) \ + DO_FPCMP_PPZZ(NAME##_d, float64, , OP) + +#define DO_FPCMP_PPZZ_ALL(NAME, OP) \ + DO_FPCMP_PPZZ_H(NAME, OP) \ + DO_FPCMP_PPZZ_S(NAME, OP) \ + DO_FPCMP_PPZZ_D(NAME, OP) + +#define DO_FCMGE(TYPE, X, Y, ST) TYPE##_compare(Y, X, ST) <= 0 +#define DO_FCMGT(TYPE, X, Y, ST) TYPE##_compare(Y, X, ST) < 0 +#define DO_FCMLE(TYPE, X, Y, ST) TYPE##_compare(X, Y, ST) <= 0 +#define DO_FCMLT(TYPE, X, Y, ST) TYPE##_compare(X, Y, ST) < 0 +#define DO_FCMEQ(TYPE, X, Y, ST) TYPE##_compare_quiet(X, Y, ST) == 0 +#define DO_FCMNE(TYPE, X, Y, ST) TYPE##_compare_quiet(X, Y, ST) != 0 +#define DO_FCMUO(TYPE, X, Y, ST) \ + TYPE##_compare_quiet(X, Y, ST) == float_relation_unordered +#define DO_FACGE(TYPE, X, Y, ST) \ + TYPE##_compare(TYPE##_abs(Y), TYPE##_abs(X), ST) <= 0 +#define DO_FACGT(TYPE, X, Y, ST) \ + TYPE##_compare(TYPE##_abs(Y), TYPE##_abs(X), ST) < 0 + +DO_FPCMP_PPZZ_ALL(sve_fcmge, DO_FCMGE) +DO_FPCMP_PPZZ_ALL(sve_fcmgt, DO_FCMGT) +DO_FPCMP_PPZZ_ALL(sve_fcmeq, DO_FCMEQ) +DO_FPCMP_PPZZ_ALL(sve_fcmne, DO_FCMNE) +DO_FPCMP_PPZZ_ALL(sve_fcmuo, DO_FCMUO) +DO_FPCMP_PPZZ_ALL(sve_facge, DO_FACGE) +DO_FPCMP_PPZZ_ALL(sve_facgt, DO_FACGT) + +#undef DO_FPCMP_PPZZ_ALL +#undef DO_FPCMP_PPZZ_D +#undef DO_FPCMP_PPZZ_S +#undef DO_FPCMP_PPZZ_H +#undef DO_FPCMP_PPZZ + +/* One operand floating-point comparison against zero, controlled + * by a predicate. + */ +#define DO_FPCMP_PPZ0(NAME, TYPE, H, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, \ + void *status, uint32_t desc) \ +{ \ + intptr_t i = simd_oprsz(desc), j = (i - 1) >> 6; \ + uint64_t *d = vd, *g = vg; \ + do { \ + uint64_t out = 0, pg = g[j]; \ + do { \ + i -= sizeof(TYPE), out <<= sizeof(TYPE); \ + if ((pg >> (i & 63)) & 1) { \ + TYPE nn = *(TYPE *)(vn + H(i)); \ + out |= OP(TYPE, nn, 0, status); \ + } \ + } while (i & 63); \ + d[j--] = out; \ + } while (i > 0); \ +} + +#define DO_FPCMP_PPZ0_H(NAME, OP) \ + DO_FPCMP_PPZ0(NAME##_h, float16, H1_2, OP) +#define DO_FPCMP_PPZ0_S(NAME, OP) \ + DO_FPCMP_PPZ0(NAME##_s, float32, H1_4, OP) +#define DO_FPCMP_PPZ0_D(NAME, OP) \ + DO_FPCMP_PPZ0(NAME##_d, float64, , OP) + +#define DO_FPCMP_PPZ0_ALL(NAME, OP) \ + DO_FPCMP_PPZ0_H(NAME, OP) \ + DO_FPCMP_PPZ0_S(NAME, OP) \ + DO_FPCMP_PPZ0_D(NAME, OP) + +DO_FPCMP_PPZ0_ALL(sve_fcmge0, DO_FCMGE) +DO_FPCMP_PPZ0_ALL(sve_fcmgt0, DO_FCMGT) +DO_FPCMP_PPZ0_ALL(sve_fcmle0, DO_FCMLE) +DO_FPCMP_PPZ0_ALL(sve_fcmlt0, DO_FCMLT) +DO_FPCMP_PPZ0_ALL(sve_fcmeq0, DO_FCMEQ) +DO_FPCMP_PPZ0_ALL(sve_fcmne0, DO_FCMNE) + +/* FP Trig Multiply-Add. */ + +void HELPER(sve_ftmad_h)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +{ + static const float16 coeff[16] = { + 0x3c00, 0xb155, 0x2030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x3c00, 0xb800, 0x293a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float16); + intptr_t x = simd_data(desc); + float16 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { + float16 mm = m[i]; + intptr_t xx = x; + if (float16_is_neg(mm)) { + mm = float16_abs(mm); + xx += 8; + } + d[i] = float16_muladd(n[i], mm, coeff[xx], 0, vs); + } +} + +void HELPER(sve_ftmad_s)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +{ + static const float32 coeff[16] = { + 0x3f800000, 0xbe2aaaab, 0x3c088886, 0xb95008b9, + 0x36369d6d, 0x00000000, 0x00000000, 0x00000000, + 0x3f800000, 0xbf000000, 0x3d2aaaa6, 0xbab60705, + 0x37cd37cc, 0x00000000, 0x00000000, 0x00000000, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float32); + intptr_t x = simd_data(desc); + float32 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { + float32 mm = m[i]; + intptr_t xx = x; + if (float32_is_neg(mm)) { + mm = float32_abs(mm); + xx += 8; + } + d[i] = float32_muladd(n[i], mm, coeff[xx], 0, vs); + } +} + +void HELPER(sve_ftmad_d)(void *vd, void *vn, void *vm, void *vs, uint32_t desc) +{ + static const float64 coeff[16] = { + 0x3ff0000000000000ull, 0xbfc5555555555543ull, + 0x3f8111111110f30cull, 0xbf2a01a019b92fc6ull, + 0x3ec71de351f3d22bull, 0xbe5ae5e2b60f7b91ull, + 0x3de5d8408868552full, 0x0000000000000000ull, + 0x3ff0000000000000ull, 0xbfe0000000000000ull, + 0x3fa5555555555536ull, 0xbf56c16c16c13a0bull, + 0x3efa01a019b1e8d8ull, 0xbe927e4f7282f468ull, + 0x3e21ee96d2641b13ull, 0xbda8f76380fbb401ull, + }; + intptr_t i, opr_sz = simd_oprsz(desc) / sizeof(float64); + intptr_t x = simd_data(desc); + float64 *d = vd, *n = vn, *m = vm; + for (i = 0; i < opr_sz; i++) { + float64 mm = m[i]; + intptr_t xx = x; + if (float64_is_neg(mm)) { + mm = float64_abs(mm); + xx += 8; + } + d[i] = float64_muladd(n[i], mm, coeff[xx], 0, vs); + } +} + +/* + * FP Complex Add + */ + +void HELPER(sve_fcadd_h)(void *vd, void *vn, void *vm, void *vg, + void *vs, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + uint64_t *g = vg; + float16 neg_imag = float16_set_sign(0, simd_data(desc)); + float16 neg_real = float16_chs(neg_imag); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float16 e0, e1, e2, e3; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float16); + i -= 2 * sizeof(float16); + + e0 = *(float16 *)(vn + H1_2(i)); + e1 = *(float16 *)(vm + H1_2(j)) ^ neg_real; + e2 = *(float16 *)(vn + H1_2(j)); + e3 = *(float16 *)(vm + H1_2(i)) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + *(float16 *)(vd + H1_2(i)) = float16_add(e0, e1, vs); + } + if (likely((pg >> (j & 63)) & 1)) { + *(float16 *)(vd + H1_2(j)) = float16_add(e2, e3, vs); + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fcadd_s)(void *vd, void *vn, void *vm, void *vg, + void *vs, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + uint64_t *g = vg; + float32 neg_imag = float32_set_sign(0, simd_data(desc)); + float32 neg_real = float32_chs(neg_imag); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float32 e0, e1, e2, e3; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float32); + i -= 2 * sizeof(float32); + + e0 = *(float32 *)(vn + H1_2(i)); + e1 = *(float32 *)(vm + H1_2(j)) ^ neg_real; + e2 = *(float32 *)(vn + H1_2(j)); + e3 = *(float32 *)(vm + H1_2(i)) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + *(float32 *)(vd + H1_2(i)) = float32_add(e0, e1, vs); + } + if (likely((pg >> (j & 63)) & 1)) { + *(float32 *)(vd + H1_2(j)) = float32_add(e2, e3, vs); + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fcadd_d)(void *vd, void *vn, void *vm, void *vg, + void *vs, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + uint64_t *g = vg; + float64 neg_imag = float64_set_sign(0, simd_data(desc)); + float64 neg_real = float64_chs(neg_imag); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float64 e0, e1, e2, e3; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float64); + i -= 2 * sizeof(float64); + + e0 = *(float64 *)(vn + H1_2(i)); + e1 = *(float64 *)(vm + H1_2(j)) ^ neg_real; + e2 = *(float64 *)(vn + H1_2(j)); + e3 = *(float64 *)(vm + H1_2(i)) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + *(float64 *)(vd + H1_2(i)) = float64_add(e0, e1, vs); + } + if (likely((pg >> (j & 63)) & 1)) { + *(float64 *)(vd + H1_2(j)) = float64_add(e2, e3, vs); + } + } while (i & 63); + } while (i != 0); +} + +/* + * FP Complex Multiply + */ + +QEMU_BUILD_BUG_ON(SIMD_DATA_SHIFT + 22 > 32); + +void HELPER(sve_fcmla_zpzzz_h)(CPUARMState *env, void *vg, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + unsigned rot = extract32(desc, SIMD_DATA_SHIFT + 20, 2); + bool flip = rot & 1; + float16 neg_imag, neg_real; + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + neg_imag = float16_set_sign(0, (rot & 2) != 0); + neg_real = float16_set_sign(0, rot == 1 || rot == 2); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float16 e1, e2, e3, e4, nr, ni, mr, mi, d; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float16); + i -= 2 * sizeof(float16); + + nr = *(float16 *)(vn + H1_2(i)); + ni = *(float16 *)(vn + H1_2(j)); + mr = *(float16 *)(vm + H1_2(i)); + mi = *(float16 *)(vm + H1_2(j)); + + e2 = (flip ? ni : nr); + e1 = (flip ? mi : mr) ^ neg_real; + e4 = e2; + e3 = (flip ? mr : mi) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + d = *(float16 *)(va + H1_2(i)); + d = float16_muladd(e2, e1, d, 0, &env->vfp.fp_status_f16); + *(float16 *)(vd + H1_2(i)) = d; + } + if (likely((pg >> (j & 63)) & 1)) { + d = *(float16 *)(va + H1_2(j)); + d = float16_muladd(e4, e3, d, 0, &env->vfp.fp_status_f16); + *(float16 *)(vd + H1_2(j)) = d; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fcmla_zpzzz_s)(CPUARMState *env, void *vg, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + unsigned rot = extract32(desc, SIMD_DATA_SHIFT + 20, 2); + bool flip = rot & 1; + float32 neg_imag, neg_real; + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + neg_imag = float32_set_sign(0, (rot & 2) != 0); + neg_real = float32_set_sign(0, rot == 1 || rot == 2); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float32 e1, e2, e3, e4, nr, ni, mr, mi, d; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float32); + i -= 2 * sizeof(float32); + + nr = *(float32 *)(vn + H1_2(i)); + ni = *(float32 *)(vn + H1_2(j)); + mr = *(float32 *)(vm + H1_2(i)); + mi = *(float32 *)(vm + H1_2(j)); + + e2 = (flip ? ni : nr); + e1 = (flip ? mi : mr) ^ neg_real; + e4 = e2; + e3 = (flip ? mr : mi) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + d = *(float32 *)(va + H1_2(i)); + d = float32_muladd(e2, e1, d, 0, &env->vfp.fp_status); + *(float32 *)(vd + H1_2(i)) = d; + } + if (likely((pg >> (j & 63)) & 1)) { + d = *(float32 *)(va + H1_2(j)); + d = float32_muladd(e4, e3, d, 0, &env->vfp.fp_status); + *(float32 *)(vd + H1_2(j)) = d; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fcmla_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) +{ + intptr_t j, i = simd_oprsz(desc); + unsigned rd = extract32(desc, SIMD_DATA_SHIFT, 5); + unsigned rn = extract32(desc, SIMD_DATA_SHIFT + 5, 5); + unsigned rm = extract32(desc, SIMD_DATA_SHIFT + 10, 5); + unsigned ra = extract32(desc, SIMD_DATA_SHIFT + 15, 5); + unsigned rot = extract32(desc, SIMD_DATA_SHIFT + 20, 2); + bool flip = rot & 1; + float64 neg_imag, neg_real; + void *vd = &env->vfp.zregs[rd]; + void *vn = &env->vfp.zregs[rn]; + void *vm = &env->vfp.zregs[rm]; + void *va = &env->vfp.zregs[ra]; + uint64_t *g = vg; + + neg_imag = float64_set_sign(0, (rot & 2) != 0); + neg_real = float64_set_sign(0, rot == 1 || rot == 2); + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + float64 e1, e2, e3, e4, nr, ni, mr, mi, d; + + /* I holds the real index; J holds the imag index. */ + j = i - sizeof(float64); + i -= 2 * sizeof(float64); + + nr = *(float64 *)(vn + H1_2(i)); + ni = *(float64 *)(vn + H1_2(j)); + mr = *(float64 *)(vm + H1_2(i)); + mi = *(float64 *)(vm + H1_2(j)); + + e2 = (flip ? ni : nr); + e1 = (flip ? mi : mr) ^ neg_real; + e4 = e2; + e3 = (flip ? mr : mi) ^ neg_imag; + + if (likely((pg >> (i & 63)) & 1)) { + d = *(float64 *)(va + H1_2(i)); + d = float64_muladd(e2, e1, d, 0, &env->vfp.fp_status); + *(float64 *)(vd + H1_2(i)) = d; + } + if (likely((pg >> (j & 63)) & 1)) { + d = *(float64 *)(va + H1_2(j)); + d = float64_muladd(e4, e3, d, 0, &env->vfp.fp_status); + *(float64 *)(vd + H1_2(j)) = d; + } + } while (i & 63); + } while (i != 0); +} + +/* + * Load contiguous data, protected by a governing predicate. + */ +#define DO_LD1(NAME, FN, TYPEE, TYPEM, H) \ +static void do_##NAME(CPUARMState *env, void *vd, void *vg, \ + target_ulong addr, intptr_t oprsz, \ + uintptr_t ra) \ +{ \ + intptr_t i = 0; \ + do { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m = 0; \ + if (pg & 1) { \ + m = FN(env, addr, ra); \ + } \ + *(TYPEE *)(vd + H(i)) = m; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += sizeof(TYPEM); \ + } while (i & 15); \ + } while (i < oprsz); \ +} \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + do_##NAME(env, &env->vfp.zregs[simd_data(desc)], vg, \ + addr, simd_oprsz(desc), GETPC()); \ +} + +#define DO_LD2(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m1 = 0, m2 = 0; \ + if (pg & 1) { \ + m1 = FN(env, addr, ra); \ + m2 = FN(env, addr + sizeof(TYPEM), ra); \ + } \ + *(TYPEE *)(d1 + H(i)) = m1; \ + *(TYPEE *)(d2 + H(i)) = m2; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 2 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_LD3(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m1 = 0, m2 = 0, m3 = 0; \ + if (pg & 1) { \ + m1 = FN(env, addr, ra); \ + m2 = FN(env, addr + sizeof(TYPEM), ra); \ + m3 = FN(env, addr + 2 * sizeof(TYPEM), ra); \ + } \ + *(TYPEE *)(d1 + H(i)) = m1; \ + *(TYPEE *)(d2 + H(i)) = m2; \ + *(TYPEE *)(d3 + H(i)) = m3; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 3 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_LD4(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ + void *d4 = &env->vfp.zregs[(rd + 3) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m1 = 0, m2 = 0, m3 = 0, m4 = 0; \ + if (pg & 1) { \ + m1 = FN(env, addr, ra); \ + m2 = FN(env, addr + sizeof(TYPEM), ra); \ + m3 = FN(env, addr + 2 * sizeof(TYPEM), ra); \ + m4 = FN(env, addr + 3 * sizeof(TYPEM), ra); \ + } \ + *(TYPEE *)(d1 + H(i)) = m1; \ + *(TYPEE *)(d2 + H(i)) = m2; \ + *(TYPEE *)(d3 + H(i)) = m3; \ + *(TYPEE *)(d4 + H(i)) = m4; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 4 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +DO_LD1(sve_ld1bhu_r, cpu_ldub_data_ra, uint16_t, uint8_t, H1_2) +DO_LD1(sve_ld1bhs_r, cpu_ldsb_data_ra, uint16_t, int8_t, H1_2) +DO_LD1(sve_ld1bsu_r, cpu_ldub_data_ra, uint32_t, uint8_t, H1_4) +DO_LD1(sve_ld1bss_r, cpu_ldsb_data_ra, uint32_t, int8_t, H1_4) +DO_LD1(sve_ld1bdu_r, cpu_ldub_data_ra, uint64_t, uint8_t, ) +DO_LD1(sve_ld1bds_r, cpu_ldsb_data_ra, uint64_t, int8_t, ) + +DO_LD1(sve_ld1hsu_r, cpu_lduw_data_ra, uint32_t, uint16_t, H1_4) +DO_LD1(sve_ld1hss_r, cpu_ldsw_data_ra, uint32_t, int8_t, H1_4) +DO_LD1(sve_ld1hdu_r, cpu_lduw_data_ra, uint64_t, uint16_t, ) +DO_LD1(sve_ld1hds_r, cpu_ldsw_data_ra, uint64_t, int16_t, ) + +DO_LD1(sve_ld1sdu_r, cpu_ldl_data_ra, uint64_t, uint32_t, ) +DO_LD1(sve_ld1sds_r, cpu_ldl_data_ra, uint64_t, int32_t, ) + +DO_LD1(sve_ld1bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) +DO_LD2(sve_ld2bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) +DO_LD3(sve_ld3bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) +DO_LD4(sve_ld4bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) + +DO_LD1(sve_ld1hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) +DO_LD2(sve_ld2hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) +DO_LD3(sve_ld3hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) +DO_LD4(sve_ld4hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) + +DO_LD1(sve_ld1ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) +DO_LD2(sve_ld2ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) +DO_LD3(sve_ld3ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) +DO_LD4(sve_ld4ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) + +DO_LD1(sve_ld1dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) +DO_LD2(sve_ld2dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) +DO_LD3(sve_ld3dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) +DO_LD4(sve_ld4dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) + +#undef DO_LD1 +#undef DO_LD2 +#undef DO_LD3 +#undef DO_LD4 + +/* + * Load contiguous data, first-fault and no-fault. + */ + +#ifdef CONFIG_USER_ONLY + +/* Fault on byte I. All bits in FFR from I are cleared. The vector + * result from I is CONSTRAINED UNPREDICTABLE; we choose the MERGE + * option, which leaves subsequent data unchanged. + */ +static void record_fault(CPUARMState *env, uintptr_t i, uintptr_t oprsz) +{ + uint64_t *ffr = env->vfp.pregs[FFR_PRED_NUM].p; + + if (i & 63) { + ffr[i / 64] &= MAKE_64BIT_MASK(0, i & 63); + i = ROUND_UP(i, 64); + } + for (; i < oprsz; i += 64) { + ffr[i / 64] = 0; + } +} + +/* Hold the mmap lock during the operation so that there is no race + * between page_check_range and the load operation. We expect the + * usual case to have no faults at all, so we check the whole range + * first and if successful defer to the normal load operation. + * + * TODO: Change mmap_lock to a rwlock so that multiple readers + * can run simultaneously. This will probably help other uses + * within QEMU as well. + */ +#define DO_LDFF1(PART, FN, TYPEE, TYPEM, H) \ +static void do_sve_ldff1##PART(CPUARMState *env, void *vd, void *vg, \ + target_ulong addr, intptr_t oprsz, \ + bool first, uintptr_t ra) \ +{ \ + intptr_t i = 0; \ + do { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m = 0; \ + if (pg & 1) { \ + if (!first && \ + unlikely(page_check_range(addr, sizeof(TYPEM), \ + PAGE_READ))) { \ + record_fault(env, i, oprsz); \ + return; \ + } \ + m = FN(env, addr, ra); \ + first = false; \ + } \ + *(TYPEE *)(vd + H(i)) = m; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += sizeof(TYPEM); \ + } while (i & 15); \ + } while (i < oprsz); \ +} \ +void HELPER(sve_ldff1##PART)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + unsigned rd = simd_data(desc); \ + void *vd = &env->vfp.zregs[rd]; \ + mmap_lock(); \ + if (likely(page_check_range(addr, oprsz, PAGE_READ) == 0)) { \ + do_sve_ld1##PART(env, vd, vg, addr, oprsz, GETPC()); \ + } else { \ + do_sve_ldff1##PART(env, vd, vg, addr, oprsz, true, GETPC()); \ + } \ + mmap_unlock(); \ +} + +/* No-fault loads are like first-fault loads without the + * first faulting special case. + */ +#define DO_LDNF1(PART) \ +void HELPER(sve_ldnf1##PART)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t oprsz = simd_oprsz(desc); \ + unsigned rd = simd_data(desc); \ + void *vd = &env->vfp.zregs[rd]; \ + mmap_lock(); \ + if (likely(page_check_range(addr, oprsz, PAGE_READ) == 0)) { \ + do_sve_ld1##PART(env, vd, vg, addr, oprsz, GETPC()); \ + } else { \ + do_sve_ldff1##PART(env, vd, vg, addr, oprsz, false, GETPC()); \ + } \ + mmap_unlock(); \ +} + +#else + +/* TODO: System mode is not yet supported. + * This would probably use tlb_vaddr_to_host. + */ +#define DO_LDFF1(PART, FN, TYPEE, TYPEM, H) \ +void HELPER(sve_ldff1##PART)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + g_assert_not_reached(); \ +} + +#define DO_LDNF1(PART) \ +void HELPER(sve_ldnf1##PART)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + g_assert_not_reached(); \ +} + +#endif + +DO_LDFF1(bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) +DO_LDFF1(bhu_r, cpu_ldub_data_ra, uint16_t, uint8_t, H1_2) +DO_LDFF1(bhs_r, cpu_ldsb_data_ra, uint16_t, int8_t, H1_2) +DO_LDFF1(bsu_r, cpu_ldub_data_ra, uint32_t, uint8_t, H1_4) +DO_LDFF1(bss_r, cpu_ldsb_data_ra, uint32_t, int8_t, H1_4) +DO_LDFF1(bdu_r, cpu_ldub_data_ra, uint64_t, uint8_t, ) +DO_LDFF1(bds_r, cpu_ldsb_data_ra, uint64_t, int8_t, ) + +DO_LDFF1(hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) +DO_LDFF1(hsu_r, cpu_lduw_data_ra, uint32_t, uint16_t, H1_4) +DO_LDFF1(hss_r, cpu_ldsw_data_ra, uint32_t, int8_t, H1_4) +DO_LDFF1(hdu_r, cpu_lduw_data_ra, uint64_t, uint16_t, ) +DO_LDFF1(hds_r, cpu_ldsw_data_ra, uint64_t, int16_t, ) + +DO_LDFF1(ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) +DO_LDFF1(sdu_r, cpu_ldl_data_ra, uint64_t, uint32_t, ) +DO_LDFF1(sds_r, cpu_ldl_data_ra, uint64_t, int32_t, ) + +DO_LDFF1(dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) + +#undef DO_LDFF1 + +DO_LDNF1(bb_r) +DO_LDNF1(bhu_r) +DO_LDNF1(bhs_r) +DO_LDNF1(bsu_r) +DO_LDNF1(bss_r) +DO_LDNF1(bdu_r) +DO_LDNF1(bds_r) + +DO_LDNF1(hh_r) +DO_LDNF1(hsu_r) +DO_LDNF1(hss_r) +DO_LDNF1(hdu_r) +DO_LDNF1(hds_r) + +DO_LDNF1(ss_r) +DO_LDNF1(sdu_r) +DO_LDNF1(sds_r) + +DO_LDNF1(dd_r) + +#undef DO_LDNF1 + +/* + * Store contiguous data, protected by a governing predicate. + */ +#define DO_ST1(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *vd = &env->vfp.zregs[rd]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEM m = *(TYPEE *)(vd + H(i)); \ + FN(env, addr, m, ra); \ + } \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_ST1_D(NAME, FN, TYPEM) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc) / 8; \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + uint64_t *d = &env->vfp.zregs[rd].d[0]; \ + uint8_t *pg = vg; \ + for (i = 0; i < oprsz; i += 1) { \ + if (pg[H1(i)] & 1) { \ + FN(env, addr, d[i], ra); \ + } \ + addr += sizeof(TYPEM); \ + } \ +} + +#define DO_ST2(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEM m1 = *(TYPEE *)(d1 + H(i)); \ + TYPEM m2 = *(TYPEE *)(d2 + H(i)); \ + FN(env, addr, m1, ra); \ + FN(env, addr + sizeof(TYPEM), m2, ra); \ + } \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 2 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_ST3(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEM m1 = *(TYPEE *)(d1 + H(i)); \ + TYPEM m2 = *(TYPEE *)(d2 + H(i)); \ + TYPEM m3 = *(TYPEE *)(d3 + H(i)); \ + FN(env, addr, m1, ra); \ + FN(env, addr + sizeof(TYPEM), m2, ra); \ + FN(env, addr + 2 * sizeof(TYPEM), m3, ra); \ + } \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 3 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +#define DO_ST4(NAME, FN, TYPEE, TYPEM, H) \ +void HELPER(NAME)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + intptr_t ra = GETPC(); \ + unsigned rd = simd_data(desc); \ + void *d1 = &env->vfp.zregs[rd]; \ + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ + void *d4 = &env->vfp.zregs[(rd + 3) & 31]; \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (pg & 1) { \ + TYPEM m1 = *(TYPEE *)(d1 + H(i)); \ + TYPEM m2 = *(TYPEE *)(d2 + H(i)); \ + TYPEM m3 = *(TYPEE *)(d3 + H(i)); \ + TYPEM m4 = *(TYPEE *)(d4 + H(i)); \ + FN(env, addr, m1, ra); \ + FN(env, addr + sizeof(TYPEM), m2, ra); \ + FN(env, addr + 2 * sizeof(TYPEM), m3, ra); \ + FN(env, addr + 3 * sizeof(TYPEM), m4, ra); \ + } \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + addr += 4 * sizeof(TYPEM); \ + } while (i & 15); \ + } \ +} + +DO_ST1(sve_st1bh_r, cpu_stb_data_ra, uint16_t, uint8_t, H1_2) +DO_ST1(sve_st1bs_r, cpu_stb_data_ra, uint32_t, uint8_t, H1_4) +DO_ST1_D(sve_st1bd_r, cpu_stb_data_ra, uint8_t) + +DO_ST1(sve_st1hs_r, cpu_stw_data_ra, uint32_t, uint16_t, H1_4) +DO_ST1_D(sve_st1hd_r, cpu_stw_data_ra, uint16_t) + +DO_ST1_D(sve_st1sd_r, cpu_stl_data_ra, uint32_t) + +DO_ST1(sve_st1bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) +DO_ST2(sve_st2bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) +DO_ST3(sve_st3bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) +DO_ST4(sve_st4bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) + +DO_ST1(sve_st1hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) +DO_ST2(sve_st2hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) +DO_ST3(sve_st3hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) +DO_ST4(sve_st4hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) + +DO_ST1(sve_st1ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) +DO_ST2(sve_st2ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) +DO_ST3(sve_st3ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) +DO_ST4(sve_st4ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) + +DO_ST1_D(sve_st1dd_r, cpu_stq_data_ra, uint64_t) + +void HELPER(sve_st2dd_r)(CPUARMState *env, void *vg, + target_ulong addr, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc) / 8; + intptr_t ra = GETPC(); + unsigned rd = simd_data(desc); + uint64_t *d1 = &env->vfp.zregs[rd].d[0]; + uint64_t *d2 = &env->vfp.zregs[(rd + 1) & 31].d[0]; + uint8_t *pg = vg; + + for (i = 0; i < oprsz; i += 1) { + if (pg[H1(i)] & 1) { + cpu_stq_data_ra(env, addr, d1[i], ra); + cpu_stq_data_ra(env, addr + 8, d2[i], ra); + } + addr += 2 * 8; + } +} + +void HELPER(sve_st3dd_r)(CPUARMState *env, void *vg, + target_ulong addr, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc) / 8; + intptr_t ra = GETPC(); + unsigned rd = simd_data(desc); + uint64_t *d1 = &env->vfp.zregs[rd].d[0]; + uint64_t *d2 = &env->vfp.zregs[(rd + 1) & 31].d[0]; + uint64_t *d3 = &env->vfp.zregs[(rd + 2) & 31].d[0]; + uint8_t *pg = vg; + + for (i = 0; i < oprsz; i += 1) { + if (pg[H1(i)] & 1) { + cpu_stq_data_ra(env, addr, d1[i], ra); + cpu_stq_data_ra(env, addr + 8, d2[i], ra); + cpu_stq_data_ra(env, addr + 16, d3[i], ra); + } + addr += 3 * 8; + } +} + +void HELPER(sve_st4dd_r)(CPUARMState *env, void *vg, + target_ulong addr, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc) / 8; + intptr_t ra = GETPC(); + unsigned rd = simd_data(desc); + uint64_t *d1 = &env->vfp.zregs[rd].d[0]; + uint64_t *d2 = &env->vfp.zregs[(rd + 1) & 31].d[0]; + uint64_t *d3 = &env->vfp.zregs[(rd + 2) & 31].d[0]; + uint64_t *d4 = &env->vfp.zregs[(rd + 3) & 31].d[0]; + uint8_t *pg = vg; + + for (i = 0; i < oprsz; i += 1) { + if (pg[H1(i)] & 1) { + cpu_stq_data_ra(env, addr, d1[i], ra); + cpu_stq_data_ra(env, addr + 8, d2[i], ra); + cpu_stq_data_ra(env, addr + 16, d3[i], ra); + cpu_stq_data_ra(env, addr + 24, d4[i], ra); + } + addr += 4 * 8; + } +} + +/* Loads with a vector index. */ + +#define DO_LD1_ZPZ_S(NAME, TYPEI, TYPEM, FN) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m = 0; \ + if (pg & 1) { \ + target_ulong off = *(TYPEI *)(vm + H1_4(i)); \ + m = FN(env, base + (off << scale), ra); \ + } \ + *(uint32_t *)(vd + H1_4(i)) = m; \ + i += 4, pg >>= 4; \ + } while (i & 15); \ + } \ +} + +#define DO_LD1_ZPZ_D(NAME, TYPEI, TYPEM, FN) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc) / 8; \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + uint64_t *d = vd, *m = vm; uint8_t *pg = vg; \ + for (i = 0; i < oprsz; i++) { \ + TYPEM mm = 0; \ + if (pg[H1(i)] & 1) { \ + target_ulong off = (TYPEI)m[i]; \ + mm = FN(env, base + (off << scale), ra); \ + } \ + d[i] = mm; \ + } \ +} + +DO_LD1_ZPZ_S(sve_ldbsu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_S(sve_ldhsu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_S(sve_ldssu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_S(sve_ldbss_zsu, uint32_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_S(sve_ldhss_zsu, uint32_t, int16_t, cpu_lduw_data_ra) + +DO_LD1_ZPZ_S(sve_ldbsu_zss, int32_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_S(sve_ldhsu_zss, int32_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_S(sve_ldssu_zss, int32_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_S(sve_ldbss_zss, int32_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_S(sve_ldhss_zss, int32_t, int16_t, cpu_lduw_data_ra) + +DO_LD1_ZPZ_D(sve_ldbdu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhdu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsdu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_D(sve_ldddu_zsu, uint32_t, uint64_t, cpu_ldq_data_ra) +DO_LD1_ZPZ_D(sve_ldbds_zsu, uint32_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhds_zsu, uint32_t, int16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsds_zsu, uint32_t, int32_t, cpu_ldl_data_ra) + +DO_LD1_ZPZ_D(sve_ldbdu_zss, int32_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhdu_zss, int32_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsdu_zss, int32_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_D(sve_ldddu_zss, int32_t, uint64_t, cpu_ldq_data_ra) +DO_LD1_ZPZ_D(sve_ldbds_zss, int32_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhds_zss, int32_t, int16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsds_zss, int32_t, int32_t, cpu_ldl_data_ra) + +DO_LD1_ZPZ_D(sve_ldbdu_zd, uint64_t, uint8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhdu_zd, uint64_t, uint16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsdu_zd, uint64_t, uint32_t, cpu_ldl_data_ra) +DO_LD1_ZPZ_D(sve_ldddu_zd, uint64_t, uint64_t, cpu_ldq_data_ra) +DO_LD1_ZPZ_D(sve_ldbds_zd, uint64_t, int8_t, cpu_ldub_data_ra) +DO_LD1_ZPZ_D(sve_ldhds_zd, uint64_t, int16_t, cpu_lduw_data_ra) +DO_LD1_ZPZ_D(sve_ldsds_zd, uint64_t, int32_t, cpu_ldl_data_ra) + +/* First fault loads with a vector index. */ + +#ifdef CONFIG_USER_ONLY + +#define DO_LDFF1_ZPZ(NAME, TYPEE, TYPEI, TYPEM, FN, H) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + bool first = true; \ + mmap_lock(); \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + TYPEM m = 0; \ + if (pg & 1) { \ + target_ulong off = *(TYPEI *)(vm + H(i)); \ + target_ulong addr = base + (off << scale); \ + if (!first && \ + page_check_range(addr, sizeof(TYPEM), PAGE_READ)) { \ + record_fault(env, i, oprsz); \ + goto exit; \ + } \ + m = FN(env, addr, ra); \ + first = false; \ + } \ + *(TYPEE *)(vd + H(i)) = m; \ + i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ + } while (i & 15); \ + } \ + exit: \ + mmap_unlock(); \ +} + +#else + +#define DO_LDFF1_ZPZ(NAME, TYPEE, TYPEI, TYPEM, FN, H) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + g_assert_not_reached(); \ +} + +#endif + +#define DO_LDFF1_ZPZ_S(NAME, TYPEI, TYPEM, FN) \ + DO_LDFF1_ZPZ(NAME, uint32_t, TYPEI, TYPEM, FN, H1_4) +#define DO_LDFF1_ZPZ_D(NAME, TYPEI, TYPEM, FN) \ + DO_LDFF1_ZPZ(NAME, uint64_t, TYPEI, TYPEM, FN, ) + +DO_LDFF1_ZPZ_S(sve_ldffbsu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffhsu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffssu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffbss_zsu, uint32_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffhss_zsu, uint32_t, int16_t, cpu_lduw_data_ra) + +DO_LDFF1_ZPZ_S(sve_ldffbsu_zss, int32_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffhsu_zss, int32_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffssu_zss, int32_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffbss_zss, int32_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_S(sve_ldffhss_zss, int32_t, int16_t, cpu_lduw_data_ra) + +DO_LDFF1_ZPZ_D(sve_ldffbdu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhdu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsdu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffddu_zsu, uint32_t, uint64_t, cpu_ldq_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffbds_zsu, uint32_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhds_zsu, uint32_t, int16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsds_zsu, uint32_t, int32_t, cpu_ldl_data_ra) + +DO_LDFF1_ZPZ_D(sve_ldffbdu_zss, int32_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhdu_zss, int32_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsdu_zss, int32_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffddu_zss, int32_t, uint64_t, cpu_ldq_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffbds_zss, int32_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhds_zss, int32_t, int16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsds_zss, int32_t, int32_t, cpu_ldl_data_ra) + +DO_LDFF1_ZPZ_D(sve_ldffbdu_zd, uint64_t, uint8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhdu_zd, uint64_t, uint16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsdu_zd, uint64_t, uint32_t, cpu_ldl_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffddu_zd, uint64_t, uint64_t, cpu_ldq_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffbds_zd, uint64_t, int8_t, cpu_ldub_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffhds_zd, uint64_t, int16_t, cpu_lduw_data_ra) +DO_LDFF1_ZPZ_D(sve_ldffsds_zd, uint64_t, int32_t, cpu_ldl_data_ra) + +/* Stores with a vector index. */ + +#define DO_ST1_ZPZ_S(NAME, TYPEI, FN) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + for (i = 0; i < oprsz; ) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ + do { \ + if (likely(pg & 1)) { \ + target_ulong off = *(TYPEI *)(vm + H1_4(i)); \ + uint32_t d = *(uint32_t *)(vd + H1_4(i)); \ + FN(env, base + (off << scale), d, ra); \ + } \ + i += sizeof(uint32_t), pg >>= sizeof(uint32_t); \ + } while (i & 15); \ + } \ +} + +#define DO_ST1_ZPZ_D(NAME, TYPEI, FN) \ +void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc) / 8; \ + unsigned scale = simd_data(desc); \ + uintptr_t ra = GETPC(); \ + uint64_t *d = vd, *m = vm; uint8_t *pg = vg; \ + for (i = 0; i < oprsz; i++) { \ + if (likely(pg[H1(i)] & 1)) { \ + target_ulong off = (target_ulong)(TYPEI)m[i] << scale; \ + FN(env, base + off, d[i], ra); \ + } \ + } \ +} + +DO_ST1_ZPZ_S(sve_stbs_zsu, uint32_t, cpu_stb_data_ra) +DO_ST1_ZPZ_S(sve_sths_zsu, uint32_t, cpu_stw_data_ra) +DO_ST1_ZPZ_S(sve_stss_zsu, uint32_t, cpu_stl_data_ra) + +DO_ST1_ZPZ_S(sve_stbs_zss, int32_t, cpu_stb_data_ra) +DO_ST1_ZPZ_S(sve_sths_zss, int32_t, cpu_stw_data_ra) +DO_ST1_ZPZ_S(sve_stss_zss, int32_t, cpu_stl_data_ra) + +DO_ST1_ZPZ_D(sve_stbd_zsu, uint32_t, cpu_stb_data_ra) +DO_ST1_ZPZ_D(sve_sthd_zsu, uint32_t, cpu_stw_data_ra) +DO_ST1_ZPZ_D(sve_stsd_zsu, uint32_t, cpu_stl_data_ra) +DO_ST1_ZPZ_D(sve_stdd_zsu, uint32_t, cpu_stq_data_ra) + +DO_ST1_ZPZ_D(sve_stbd_zss, int32_t, cpu_stb_data_ra) +DO_ST1_ZPZ_D(sve_sthd_zss, int32_t, cpu_stw_data_ra) +DO_ST1_ZPZ_D(sve_stsd_zss, int32_t, cpu_stl_data_ra) +DO_ST1_ZPZ_D(sve_stdd_zss, int32_t, cpu_stq_data_ra) + +DO_ST1_ZPZ_D(sve_stbd_zd, uint64_t, cpu_stb_data_ra) +DO_ST1_ZPZ_D(sve_sthd_zd, uint64_t, cpu_stw_data_ra) +DO_ST1_ZPZ_D(sve_stsd_zd, uint64_t, cpu_stl_data_ra) +DO_ST1_ZPZ_D(sve_stdd_zd, uint64_t, cpu_stq_data_ra) diff --git a/target/arm/trace-events b/target/arm/trace-events index 9e37131115..6b759f9d4f 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -8,3 +8,6 @@ arm_gt_tval_write(int timer, uint64_t value) "gt_tval_write: timer %d value 0x%" arm_gt_ctl_write(int timer, uint64_t value) "gt_ctl_write: timer %d value 0x%" PRIx64 arm_gt_imask_toggle(int timer, int irqstate) "gt_ctl_write: timer %d IMASK toggle, new irqstate %d" arm_gt_cntvoff_write(uint64_t value) "gt_cntvoff_write: value 0x%" PRIx64 + +# target/arm/kvm.c +kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64 diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index c91329249d..45a6c2a3aa 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -36,13 +36,13 @@ #include "exec/log.h" #include "trace-tcg.h" +#include "translate-a64.h" static TCGv_i64 cpu_X[32]; static TCGv_i64 cpu_pc; /* Load/store exclusive handling */ static TCGv_i64 cpu_exclusive_high; -static TCGv_i64 cpu_reg(DisasContext *s, int reg); static const char *regnames[] = { "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", @@ -84,13 +84,7 @@ typedef void NeonGenOneOpFn(TCGv_i64, TCGv_i64); typedef void CryptoTwoOpFn(TCGv_ptr, TCGv_ptr); typedef void CryptoThreeOpIntFn(TCGv_ptr, TCGv_ptr, TCGv_i32); typedef void CryptoThreeOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr); - -/* Note that the gvec expanders operate on offsets + sizes. */ -typedef void GVecGen2Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t); -typedef void GVecGen2iFn(unsigned, uint32_t, uint32_t, int64_t, - uint32_t, uint32_t); -typedef void GVecGen3Fn(unsigned, uint32_t, uint32_t, - uint32_t, uint32_t, uint32_t); +typedef void AtomicThreeOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGArg, TCGMemOp); /* initialize TCG globals. */ void a64_translate_init(void) @@ -389,7 +383,7 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) if (use_goto_tb(s, n, dest)) { tcg_gen_goto_tb(n); gen_a64_set_pc_im(dest); - tcg_gen_exit_tb((intptr_t)tb + n); + tcg_gen_exit_tb(tb, n); s->base.is_jmp = DISAS_NORETURN; } else { gen_a64_set_pc_im(dest); @@ -404,22 +398,13 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) } } -static void unallocated_encoding(DisasContext *s) +void unallocated_encoding(DisasContext *s) { /* Unallocated and reserved encodings are uncategorized */ gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), default_exception_el(s)); } -#define unsupported_encoding(s, insn) \ - do { \ - qemu_log_mask(LOG_UNIMP, \ - "%s:%d: unsupported instruction encoding 0x%08x " \ - "at pc=%016" PRIx64 "\n", \ - __FILE__, __LINE__, insn, s->pc - 4); \ - unallocated_encoding(s); \ - } while (0) - static void init_tmp_a64_array(DisasContext *s) { #ifdef CONFIG_DEBUG_TCG @@ -437,13 +422,13 @@ static void free_tmp_a64(DisasContext *s) init_tmp_a64_array(s); } -static TCGv_i64 new_tmp_a64(DisasContext *s) +TCGv_i64 new_tmp_a64(DisasContext *s) { assert(s->tmp_a64_count < TMP_A64_MAX); return s->tmp_a64[s->tmp_a64_count++] = tcg_temp_new_i64(); } -static TCGv_i64 new_tmp_a64_zero(DisasContext *s) +TCGv_i64 new_tmp_a64_zero(DisasContext *s) { TCGv_i64 t = new_tmp_a64(s); tcg_gen_movi_i64(t, 0); @@ -465,7 +450,7 @@ static TCGv_i64 new_tmp_a64_zero(DisasContext *s) * to cpu_X[31] and ZR accesses to a temporary which can be discarded. * This is the point of the _sp forms. */ -static TCGv_i64 cpu_reg(DisasContext *s, int reg) +TCGv_i64 cpu_reg(DisasContext *s, int reg) { if (reg == 31) { return new_tmp_a64_zero(s); @@ -475,7 +460,7 @@ static TCGv_i64 cpu_reg(DisasContext *s, int reg) } /* register access for when 31 == SP */ -static TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) +TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) { return cpu_X[reg]; } @@ -484,7 +469,7 @@ static TCGv_i64 cpu_reg_sp(DisasContext *s, int reg) * representing the register contents. This TCGv is an auto-freed * temporary so it need not be explicitly freed, and may be modified. */ -static TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) +TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) { TCGv_i64 v = new_tmp_a64(s); if (reg != 31) { @@ -499,7 +484,7 @@ static TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf) return v; } -static TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) +TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) { TCGv_i64 v = new_tmp_a64(s); if (sf) { @@ -510,72 +495,6 @@ static TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf) return v; } -/* We should have at some point before trying to access an FP register - * done the necessary access check, so assert that - * (a) we did the check and - * (b) we didn't then just plough ahead anyway if it failed. - * Print the instruction pattern in the abort message so we can figure - * out what we need to fix if a user encounters this problem in the wild. - */ -static inline void assert_fp_access_checked(DisasContext *s) -{ -#ifdef CONFIG_DEBUG_TCG - if (unlikely(!s->fp_access_checked || s->fp_excp_el)) { - fprintf(stderr, "target-arm: FP access check missing for " - "instruction 0x%08x\n", s->insn); - abort(); - } -#endif -} - -/* Return the offset into CPUARMState of an element of specified - * size, 'element' places in from the least significant end of - * the FP/vector register Qn. - */ -static inline int vec_reg_offset(DisasContext *s, int regno, - int element, TCGMemOp size) -{ - int offs = 0; -#ifdef HOST_WORDS_BIGENDIAN - /* This is complicated slightly because vfp.zregs[n].d[0] is - * still the low half and vfp.zregs[n].d[1] the high half - * of the 128 bit vector, even on big endian systems. - * Calculate the offset assuming a fully bigendian 128 bits, - * then XOR to account for the order of the two 64 bit halves. - */ - offs += (16 - ((element + 1) * (1 << size))); - offs ^= 8; -#else - offs += element * (1 << size); -#endif - offs += offsetof(CPUARMState, vfp.zregs[regno]); - assert_fp_access_checked(s); - return offs; -} - -/* Return the offset info CPUARMState of the "whole" vector register Qn. */ -static inline int vec_full_reg_offset(DisasContext *s, int regno) -{ - assert_fp_access_checked(s); - return offsetof(CPUARMState, vfp.zregs[regno]); -} - -/* Return a newly allocated pointer to the vector register. */ -static TCGv_ptr vec_full_reg_ptr(DisasContext *s, int regno) -{ - TCGv_ptr ret = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(ret, cpu_env, vec_full_reg_offset(s, regno)); - return ret; -} - -/* Return the byte size of the "whole" vector register, VL / 8. */ -static inline int vec_full_reg_size(DisasContext *s) -{ - /* FIXME SVE: We should put the composite ZCR_EL* value into tb->flags. - In the meantime this is just the AdvSIMD length of 128. */ - return 128 / 8; -} - /* Return the offset into CPUARMState of a slice (from * the least significant end) of FP register Qn (ie * Dn, Sn, Hn or Bn). @@ -614,6 +533,14 @@ static TCGv_i32 read_fp_sreg(DisasContext *s, int reg) return v; } +static TCGv_i32 read_fp_hreg(DisasContext *s, int reg) +{ + TCGv_i32 v = tcg_temp_new_i32(); + + tcg_gen_ld16u_i32(v, cpu_env, fp_reg_offset(s, reg, MO_16)); + return v; +} + /* Clear the bits above an N-bit vector, for N = (is_q ? 128 : 64). * If SVE is not enabled, then there are only 128 bits in the vector. */ @@ -632,7 +559,7 @@ static void clear_vec_high(DisasContext *s, bool is_q, int rd) } } -static void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v) +void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v) { unsigned ofs = fp_reg_offset(s, reg, MO_64); @@ -649,7 +576,7 @@ static void write_fp_sreg(DisasContext *s, int reg, TCGv_i32 v) tcg_temp_free_i64(tmp); } -static TCGv_ptr get_fpstatus_ptr(bool is_f16) +TCGv_ptr get_fpstatus_ptr(bool is_f16) { TCGv_ptr statusptr = tcg_temp_new_ptr(); int offset; @@ -713,6 +640,16 @@ static void gen_gvec_op3(DisasContext *s, bool is_q, int rd, vec_full_reg_size(s), gvec_op); } +/* Expand a 3-operand operation using an out-of-line helper. */ +static void gen_gvec_op3_ool(DisasContext *s, bool is_q, int rd, + int rn, int rm, int data, gen_helper_gvec_3 *fn) +{ + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + is_q ? 16 : 8, vec_full_reg_size(s), data, fn); +} + /* Expand a 3-operand + env pointer operation using * an out-of-line helper. */ @@ -1237,14 +1174,14 @@ static inline bool fp_access_check(DisasContext *s) /* Check that SVE access is enabled. If it is, return true. * If not, emit code to generate an appropriate exception and return false. */ -static inline bool sve_access_check(DisasContext *s) +bool sve_access_check(DisasContext *s) { if (s->sve_excp_el) { gen_exception_insn(s, 4, EXCP_UDEF, syn_sve_access_trap(), s->sve_excp_el); return false; } - return true; + return fp_access_check(s); } /* @@ -1696,11 +1633,10 @@ static void handle_sys(DisasContext *s, uint32_t insn, bool isread, default: break; } - if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { - return; - } if ((ri->type & ARM_CP_FPU) && !fp_access_check(s)) { return; + } else if ((ri->type & ARM_CP_SVE) && !sve_access_check(s)) { + return; } if ((tb_cflags(s->base.tb) & CF_USE_ICOUNT) && (ri->type & ARM_CP_IO)) { @@ -1930,7 +1866,13 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_exception_return(cpu_env); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; return; @@ -2107,6 +2049,103 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, tcg_gen_movi_i64(cpu_exclusive_addr, -1); } +static void gen_compare_and_swap(DisasContext *s, int rs, int rt, + int rn, int size) +{ + TCGv_i64 tcg_rs = cpu_reg(s, rs); + TCGv_i64 tcg_rt = cpu_reg(s, rt); + int memidx = get_mem_index(s); + TCGv_i64 addr = cpu_reg_sp(s, rn); + + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_gen_atomic_cmpxchg_i64(tcg_rs, addr, tcg_rs, tcg_rt, memidx, + size | MO_ALIGN | s->be_data); +} + +static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, + int rn, int size) +{ + TCGv_i64 s1 = cpu_reg(s, rs); + TCGv_i64 s2 = cpu_reg(s, rs + 1); + TCGv_i64 t1 = cpu_reg(s, rt); + TCGv_i64 t2 = cpu_reg(s, rt + 1); + TCGv_i64 addr = cpu_reg_sp(s, rn); + int memidx = get_mem_index(s); + + if (rn == 31) { + gen_check_sp_alignment(s); + } + + if (size == 2) { + TCGv_i64 cmp = tcg_temp_new_i64(); + TCGv_i64 val = tcg_temp_new_i64(); + + if (s->be_data == MO_LE) { + tcg_gen_concat32_i64(val, t1, t2); + tcg_gen_concat32_i64(cmp, s1, s2); + } else { + tcg_gen_concat32_i64(val, t2, t1); + tcg_gen_concat32_i64(cmp, s2, s1); + } + + tcg_gen_atomic_cmpxchg_i64(cmp, addr, cmp, val, memidx, + MO_64 | MO_ALIGN | s->be_data); + tcg_temp_free_i64(val); + + if (s->be_data == MO_LE) { + tcg_gen_extr32_i64(s1, s2, cmp); + } else { + tcg_gen_extr32_i64(s2, s1, cmp); + } + tcg_temp_free_i64(cmp); + } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { + TCGv_i32 tcg_rs = tcg_const_i32(rs); + + if (s->be_data == MO_LE) { + gen_helper_casp_le_parallel(cpu_env, tcg_rs, addr, t1, t2); + } else { + gen_helper_casp_be_parallel(cpu_env, tcg_rs, addr, t1, t2); + } + tcg_temp_free_i32(tcg_rs); + } else { + TCGv_i64 d1 = tcg_temp_new_i64(); + TCGv_i64 d2 = tcg_temp_new_i64(); + TCGv_i64 a2 = tcg_temp_new_i64(); + TCGv_i64 c1 = tcg_temp_new_i64(); + TCGv_i64 c2 = tcg_temp_new_i64(); + TCGv_i64 zero = tcg_const_i64(0); + + /* Load the two words, in memory order. */ + tcg_gen_qemu_ld_i64(d1, addr, memidx, + MO_64 | MO_ALIGN_16 | s->be_data); + tcg_gen_addi_i64(a2, addr, 8); + tcg_gen_qemu_ld_i64(d2, addr, memidx, MO_64 | s->be_data); + + /* Compare the two words, also in memory order. */ + tcg_gen_setcond_i64(TCG_COND_EQ, c1, d1, s1); + tcg_gen_setcond_i64(TCG_COND_EQ, c2, d2, s2); + tcg_gen_and_i64(c2, c2, c1); + + /* If compare equal, write back new data, else write back old data. */ + tcg_gen_movcond_i64(TCG_COND_NE, c1, c2, zero, t1, d1); + tcg_gen_movcond_i64(TCG_COND_NE, c2, c2, zero, t2, d2); + tcg_gen_qemu_st_i64(c1, addr, memidx, MO_64 | s->be_data); + tcg_gen_qemu_st_i64(c2, a2, memidx, MO_64 | s->be_data); + tcg_temp_free_i64(a2); + tcg_temp_free_i64(c1); + tcg_temp_free_i64(c2); + tcg_temp_free_i64(zero); + + /* Write back the data from memory to Rs. */ + tcg_gen_mov_i64(s1, d1); + tcg_gen_mov_i64(s2, d2); + tcg_temp_free_i64(d1); + tcg_temp_free_i64(d2); + } +} + /* Update the Sixty-Four bit (SF) registersize. This logic is derived * from the ARMv8 specs for LDR (Shared decode for all encodings). */ @@ -2141,62 +2180,114 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) int rt = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); int rt2 = extract32(insn, 10, 5); - int is_lasr = extract32(insn, 15, 1); int rs = extract32(insn, 16, 5); - int is_pair = extract32(insn, 21, 1); - int is_store = !extract32(insn, 22, 1); - int is_excl = !extract32(insn, 23, 1); + int is_lasr = extract32(insn, 15, 1); + int o2_L_o1_o0 = extract32(insn, 21, 3) * 2 | is_lasr; int size = extract32(insn, 30, 2); TCGv_i64 tcg_addr; - if ((!is_excl && !is_pair && !is_lasr) || - (!is_excl && is_pair) || - (is_pair && size < 2)) { - unallocated_encoding(s); + switch (o2_L_o1_o0) { + case 0x0: /* STXR */ + case 0x1: /* STLXR */ + if (rn == 31) { + gen_check_sp_alignment(s); + } + if (is_lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + tcg_addr = read_cpu_reg_sp(s, rn, 1); + gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, false); return; - } - if (rn == 31) { - gen_check_sp_alignment(s); - } - tcg_addr = read_cpu_reg_sp(s, rn, 1); - - /* Note that since TCG is single threaded load-acquire/store-release - * semantics require no extra if (is_lasr) { ... } handling. - */ - - if (is_excl) { - if (!is_store) { - s->is_ldex = true; - gen_load_exclusive(s, rt, rt2, tcg_addr, size, is_pair); - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - } - } else { - if (is_lasr) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, is_pair); + case 0x4: /* LDXR */ + case 0x5: /* LDAXR */ + if (rn == 31) { + gen_check_sp_alignment(s); } - } else { - TCGv_i64 tcg_rt = cpu_reg(s, rt); - bool iss_sf = disas_ldst_compute_iss_sf(size, false, 0); + tcg_addr = read_cpu_reg_sp(s, rn, 1); + s->is_ldex = true; + gen_load_exclusive(s, rt, rt2, tcg_addr, size, false); + if (is_lasr) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + return; + case 0x9: /* STLR */ /* Generate ISS for non-exclusive accesses including LASR. */ - if (is_store) { + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + tcg_addr = read_cpu_reg_sp(s, rn, 1); + do_gpr_st(s, cpu_reg(s, rt), tcg_addr, size, true, rt, + disas_ldst_compute_iss_sf(size, false, 0), is_lasr); + return; + + case 0xd: /* LDAR */ + /* Generate ISS for non-exclusive accesses including LASR. */ + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_addr = read_cpu_reg_sp(s, rn, 1); + do_gpr_ld(s, cpu_reg(s, rt), tcg_addr, size, false, false, true, rt, + disas_ldst_compute_iss_sf(size, false, 0), is_lasr); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + return; + + case 0x2: case 0x3: /* CASP / STXP */ + if (size & 2) { /* STXP / STLXP */ + if (rn == 31) { + gen_check_sp_alignment(s); + } if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } - do_gpr_st(s, tcg_rt, tcg_addr, size, - true, rt, iss_sf, is_lasr); - } else { - do_gpr_ld(s, tcg_rt, tcg_addr, size, false, false, - true, rt, iss_sf, is_lasr); + tcg_addr = read_cpu_reg_sp(s, rn, 1); + gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, true); + return; + } + if (rt2 == 31 + && ((rt | rs) & 1) == 0 + && arm_dc_feature(s, ARM_FEATURE_V8_ATOMICS)) { + /* CASP / CASPL */ + gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); + return; + } + break; + + case 0x6: case 0x7: /* CASPA / LDXP */ + if (size & 2) { /* LDXP / LDAXP */ + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_addr = read_cpu_reg_sp(s, rn, 1); + s->is_ldex = true; + gen_load_exclusive(s, rt, rt2, tcg_addr, size, true); if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); } + return; } + if (rt2 == 31 + && ((rt | rs) & 1) == 0 + && arm_dc_feature(s, ARM_FEATURE_V8_ATOMICS)) { + /* CASPA / CASPAL */ + gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); + return; + } + break; + + case 0xa: /* CAS */ + case 0xb: /* CASL */ + case 0xe: /* CASA */ + case 0xf: /* CASAL */ + if (rt2 == 31 && arm_dc_feature(s, ARM_FEATURE_V8_ATOMICS)) { + gen_compare_and_swap(s, rs, rt, rn, size); + return; + } + break; } + unallocated_encoding(s); } /* @@ -2709,6 +2800,88 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, } } +/* Atomic memory operations + * + * 31 30 27 26 24 22 21 16 15 12 10 5 0 + * +------+-------+---+-----+-----+---+----+----+-----+-----+----+-----+ + * | size | 1 1 1 | V | 0 0 | A R | 1 | Rs | o3 | opc | 0 0 | Rn | Rt | + * +------+-------+---+-----+-----+--------+----+-----+-----+----+-----+ + * + * Rt: the result register + * Rn: base address or SP + * Rs: the source register for the operation + * V: vector flag (always 0 as of v8.3) + * A: acquire flag + * R: release flag + */ +static void disas_ldst_atomic(DisasContext *s, uint32_t insn, + int size, int rt, bool is_vector) +{ + int rs = extract32(insn, 16, 5); + int rn = extract32(insn, 5, 5); + int o3_opc = extract32(insn, 12, 4); + int feature = ARM_FEATURE_V8_ATOMICS; + TCGv_i64 tcg_rn, tcg_rs; + AtomicThreeOpFn *fn; + + if (is_vector) { + unallocated_encoding(s); + return; + } + switch (o3_opc) { + case 000: /* LDADD */ + fn = tcg_gen_atomic_fetch_add_i64; + break; + case 001: /* LDCLR */ + fn = tcg_gen_atomic_fetch_and_i64; + break; + case 002: /* LDEOR */ + fn = tcg_gen_atomic_fetch_xor_i64; + break; + case 003: /* LDSET */ + fn = tcg_gen_atomic_fetch_or_i64; + break; + case 004: /* LDSMAX */ + fn = tcg_gen_atomic_fetch_smax_i64; + break; + case 005: /* LDSMIN */ + fn = tcg_gen_atomic_fetch_smin_i64; + break; + case 006: /* LDUMAX */ + fn = tcg_gen_atomic_fetch_umax_i64; + break; + case 007: /* LDUMIN */ + fn = tcg_gen_atomic_fetch_umin_i64; + break; + case 010: /* SWP */ + fn = tcg_gen_atomic_xchg_i64; + break; + default: + unallocated_encoding(s); + return; + } + if (!arm_dc_feature(s, feature)) { + unallocated_encoding(s); + return; + } + + if (rn == 31) { + gen_check_sp_alignment(s); + } + tcg_rn = cpu_reg_sp(s, rn); + tcg_rs = read_cpu_reg(s, rs, true); + + if (o3_opc == 1) { /* LDCLR */ + tcg_gen_not_i64(tcg_rs, tcg_rs); + } + + /* The tcg atomic primitives are all full barriers. Therefore we + * can ignore the Acquire and Release bits of this instruction. + */ + fn(cpu_reg(s, rt), tcg_rn, tcg_rs, get_mem_index(s), + s->be_data | size | MO_ALIGN); +} + /* Load/store register (all forms) */ static void disas_ldst_reg(DisasContext *s, uint32_t insn) { @@ -2719,23 +2892,28 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn) switch (extract32(insn, 24, 2)) { case 0: - if (extract32(insn, 21, 1) == 1 && extract32(insn, 10, 2) == 2) { - disas_ldst_reg_roffset(s, insn, opc, size, rt, is_vector); - } else { + if (extract32(insn, 21, 1) == 0) { /* Load/store register (unscaled immediate) * Load/store immediate pre/post-indexed * Load/store register unprivileged */ disas_ldst_reg_imm9(s, insn, opc, size, rt, is_vector); + return; + } + switch (extract32(insn, 10, 2)) { + case 0: + disas_ldst_atomic(s, insn, size, rt, is_vector); + return; + case 2: + disas_ldst_reg_roffset(s, insn, opc, size, rt, is_vector); + return; } break; case 1: disas_ldst_reg_unsigned_imm(s, insn, opc, size, rt, is_vector); - break; - default: - unallocated_encoding(s); - break; + return; } + unallocated_encoding(s); } /* AdvSIMD load/store multiple structures @@ -3168,8 +3346,8 @@ static inline uint64_t bitmask64(unsigned int length) * value (ie should cause a guest UNDEF exception), and true if they are * valid, in which case the decoded bit pattern is written to result. */ -static bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, - unsigned int imms, unsigned int immr) +bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, + unsigned int imms, unsigned int immr) { uint64_t mask; unsigned e, levels, s, r; @@ -4461,14 +4639,14 @@ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) } } -static void handle_fp_compare(DisasContext *s, bool is_double, +static void handle_fp_compare(DisasContext *s, int size, unsigned int rn, unsigned int rm, bool cmp_with_zero, bool signal_all_nans) { TCGv_i64 tcg_flags = tcg_temp_new_i64(); - TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_ptr fpst = get_fpstatus_ptr(size == MO_16); - if (is_double) { + if (size == MO_64) { TCGv_i64 tcg_vn, tcg_vm; tcg_vn = read_fp_dreg(s, rn); @@ -4485,19 +4663,35 @@ static void handle_fp_compare(DisasContext *s, bool is_double, tcg_temp_free_i64(tcg_vn); tcg_temp_free_i64(tcg_vm); } else { - TCGv_i32 tcg_vn, tcg_vm; + TCGv_i32 tcg_vn = tcg_temp_new_i32(); + TCGv_i32 tcg_vm = tcg_temp_new_i32(); - tcg_vn = read_fp_sreg(s, rn); + read_vec_element_i32(s, tcg_vn, rn, 0, size); if (cmp_with_zero) { - tcg_vm = tcg_const_i32(0); + tcg_gen_movi_i32(tcg_vm, 0); } else { - tcg_vm = read_fp_sreg(s, rm); + read_vec_element_i32(s, tcg_vm, rm, 0, size); } - if (signal_all_nans) { - gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst); - } else { - gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + + switch (size) { + case MO_32: + if (signal_all_nans) { + gen_helper_vfp_cmpes_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmps_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + break; + case MO_16: + if (signal_all_nans) { + gen_helper_vfp_cmpeh_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } else { + gen_helper_vfp_cmph_a64(tcg_flags, tcg_vn, tcg_vm, fpst); + } + break; + default: + g_assert_not_reached(); } + tcg_temp_free_i32(tcg_vn); tcg_temp_free_i32(tcg_vm); } @@ -4518,16 +4712,35 @@ static void handle_fp_compare(DisasContext *s, bool is_double, static void disas_fp_compare(DisasContext *s, uint32_t insn) { unsigned int mos, type, rm, op, rn, opc, op2r; + int size; mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ + type = extract32(insn, 22, 2); rm = extract32(insn, 16, 5); op = extract32(insn, 14, 2); rn = extract32(insn, 5, 5); opc = extract32(insn, 3, 2); op2r = extract32(insn, 0, 3); - if (mos || op || op2r || type > 1) { + if (mos || op || op2r) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: + size = MO_32; + break; + case 1: + size = MO_64; + break; + case 3: + size = MO_16; + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -4536,7 +4749,7 @@ static void disas_fp_compare(DisasContext *s, uint32_t insn) return; } - handle_fp_compare(s, type, rn, rm, opc & 1, opc & 2); + handle_fp_compare(s, size, rn, rm, opc & 1, opc & 2); } /* Floating point conditional compare @@ -4550,16 +4763,35 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) unsigned int mos, type, rm, cond, rn, op, nzcv; TCGv_i64 tcg_flags; TCGLabel *label_continue = NULL; + int size; mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ + type = extract32(insn, 22, 2); rm = extract32(insn, 16, 5); cond = extract32(insn, 12, 4); rn = extract32(insn, 5, 5); op = extract32(insn, 4, 1); nzcv = extract32(insn, 0, 4); - if (mos || type > 1) { + if (mos) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: + size = MO_32; + break; + case 1: + size = MO_64; + break; + case 3: + size = MO_16; + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -4580,7 +4812,7 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) gen_set_label(label_match); } - handle_fp_compare(s, type, rn, rm, false, op); + handle_fp_compare(s, size, rn, rm, false, op); if (cond < 0x0e) { gen_set_label(label_continue); @@ -4598,15 +4830,34 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) unsigned int mos, type, rm, cond, rn, rd; TCGv_i64 t_true, t_false, t_zero; DisasCompare64 c; + TCGMemOp sz; mos = extract32(insn, 29, 3); - type = extract32(insn, 22, 2); /* 0 = single, 1 = double */ + type = extract32(insn, 22, 2); rm = extract32(insn, 16, 5); cond = extract32(insn, 12, 4); rn = extract32(insn, 5, 5); rd = extract32(insn, 0, 5); - if (mos || type > 1) { + if (mos) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: + sz = MO_32; + break; + case 1: + sz = MO_64; + break; + case 3: + sz = MO_16; + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -4615,11 +4866,11 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) return; } - /* Zero extend sreg inputs to 64 bits now. */ + /* Zero extend sreg & hreg inputs to 64 bits now. */ t_true = tcg_temp_new_i64(); t_false = tcg_temp_new_i64(); - read_vec_element(s, t_true, rn, 0, type ? MO_64 : MO_32); - read_vec_element(s, t_false, rm, 0, type ? MO_64 : MO_32); + read_vec_element(s, t_true, rn, 0, sz); + read_vec_element(s, t_false, rm, 0, sz); a64_test_cc(&c, cond); t_zero = tcg_const_i64(0); @@ -4628,7 +4879,7 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) tcg_temp_free_i64(t_false); a64_free_cc(&c); - /* Note that sregs write back zeros to the high bits, + /* Note that sregs & hregs write back zeros to the high bits, and we've already done the zero-extension. */ write_fp_dreg(s, rd, t_true); tcg_temp_free_i64(t_true); @@ -4638,11 +4889,9 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) { TCGv_ptr fpst = NULL; - TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_op = read_fp_hreg(s, rn); TCGv_i32 tcg_res = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_op, rn, 0, MO_16); - switch (opcode) { case 0x0: /* FMOV */ tcg_gen_mov_i32(tcg_res, tcg_op); @@ -4654,7 +4903,8 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) tcg_gen_xori_i32(tcg_res, tcg_op, 0x8000); break; case 0x3: /* FSQRT */ - gen_helper_sqrt_f16(tcg_res, tcg_op, cpu_env); + fpst = get_fpstatus_ptr(true); + gen_helper_sqrt_f16(tcg_res, tcg_op, fpst); break; case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ @@ -4824,10 +5074,15 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, } else { /* Single to half */ TCGv_i32 tcg_rd = tcg_temp_new_i32(); - gen_helper_vfp_fcvt_f32_to_f16(tcg_rd, tcg_rn, cpu_env); + TCGv_i32 ahp = get_ahp_flag(); + TCGv_ptr fpst = get_fpstatus_ptr(false); + + gen_helper_vfp_fcvt_f32_to_f16(tcg_rd, tcg_rn, fpst, ahp); /* write_fp_sreg is OK here because top half of tcg_rd is zero */ write_fp_sreg(s, rd, tcg_rd); tcg_temp_free_i32(tcg_rd); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); } tcg_temp_free_i32(tcg_rn); break; @@ -4840,9 +5095,13 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, /* Double to single */ gen_helper_vfp_fcvtsd(tcg_rd, tcg_rn, cpu_env); } else { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); /* Double to half */ - gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, cpu_env); + gen_helper_vfp_fcvt_f64_to_f16(tcg_rd, tcg_rn, fpst, ahp); /* write_fp_sreg is OK here because top half of tcg_rd is zero */ + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(ahp); } write_fp_sreg(s, rd, tcg_rd); tcg_temp_free_i32(tcg_rd); @@ -4852,17 +5111,21 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, case 0x3: { TCGv_i32 tcg_rn = read_fp_sreg(s, rn); + TCGv_ptr tcg_fpst = get_fpstatus_ptr(false); + TCGv_i32 tcg_ahp = get_ahp_flag(); tcg_gen_ext16u_i32(tcg_rn, tcg_rn); if (dtype == 0) { /* Half to single */ TCGv_i32 tcg_rd = tcg_temp_new_i32(); - gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); write_fp_sreg(s, rd, tcg_rd); + tcg_temp_free_ptr(tcg_fpst); + tcg_temp_free_i32(tcg_ahp); tcg_temp_free_i32(tcg_rd); } else { /* Half to double */ TCGv_i64 tcg_rd = tcg_temp_new_i64(); - gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, cpu_env); + gen_helper_vfp_fcvt_f16_to_f64(tcg_rd, tcg_rn, tcg_fpst, tcg_ahp); write_fp_dreg(s, rd, tcg_rd); tcg_temp_free_i64(tcg_rd); } @@ -5050,6 +5313,61 @@ static void handle_fp_2src_double(DisasContext *s, int opcode, tcg_temp_free_i64(tcg_res); } +/* Floating-point data-processing (2 source) - half precision */ +static void handle_fp_2src_half(DisasContext *s, int opcode, + int rd, int rn, int rm) +{ + TCGv_i32 tcg_op1; + TCGv_i32 tcg_op2; + TCGv_i32 tcg_res; + TCGv_ptr fpst; + + tcg_res = tcg_temp_new_i32(); + fpst = get_fpstatus_ptr(true); + tcg_op1 = read_fp_hreg(s, rn); + tcg_op2 = read_fp_hreg(s, rm); + + switch (opcode) { + case 0x0: /* FMUL */ + gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x1: /* FDIV */ + gen_helper_advsimd_divh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x2: /* FADD */ + gen_helper_advsimd_addh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x3: /* FSUB */ + gen_helper_advsimd_subh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x4: /* FMAX */ + gen_helper_advsimd_maxh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x5: /* FMIN */ + gen_helper_advsimd_minh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x6: /* FMAXNM */ + gen_helper_advsimd_maxnumh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x7: /* FMINNM */ + gen_helper_advsimd_minnumh(tcg_res, tcg_op1, tcg_op2, fpst); + break; + case 0x8: /* FNMUL */ + gen_helper_advsimd_mulh(tcg_res, tcg_op1, tcg_op2, fpst); + tcg_gen_xori_i32(tcg_res, tcg_res, 0x8000); + break; + default: + g_assert_not_reached(); + } + + write_fp_sreg(s, rd, tcg_res); + + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(tcg_op1); + tcg_temp_free_i32(tcg_op2); + tcg_temp_free_i32(tcg_res); +} + /* Floating point data-processing (2 source) * 31 30 29 28 24 23 22 21 20 16 15 12 11 10 9 5 4 0 * +---+---+---+-----------+------+---+------+--------+-----+------+------+ @@ -5082,6 +5400,16 @@ static void disas_fp_2src(DisasContext *s, uint32_t insn) } handle_fp_2src_double(s, opcode, rd, rn, rm); break; + case 3: + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_fp_2src_half(s, opcode, rd, rn, rm); + break; default: unallocated_encoding(s); } @@ -5163,6 +5491,44 @@ static void handle_fp_3src_double(DisasContext *s, bool o0, bool o1, tcg_temp_free_i64(tcg_res); } +/* Floating-point data-processing (3 source) - half precision */ +static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1, + int rd, int rn, int rm, int ra) +{ + TCGv_i32 tcg_op1, tcg_op2, tcg_op3; + TCGv_i32 tcg_res = tcg_temp_new_i32(); + TCGv_ptr fpst = get_fpstatus_ptr(true); + + tcg_op1 = read_fp_hreg(s, rn); + tcg_op2 = read_fp_hreg(s, rm); + tcg_op3 = read_fp_hreg(s, ra); + + /* These are fused multiply-add, and must be done as one + * floating point operation with no rounding between the + * multiplication and addition steps. + * NB that doing the negations here as separate steps is + * correct : an input NaN should come out with its sign bit + * flipped if it is a negated-input. + */ + if (o1 == true) { + tcg_gen_xori_i32(tcg_op3, tcg_op3, 0x8000); + } + + if (o0 != o1) { + tcg_gen_xori_i32(tcg_op1, tcg_op1, 0x8000); + } + + gen_helper_advsimd_muladdh(tcg_res, tcg_op1, tcg_op2, tcg_op3, fpst); + + write_fp_sreg(s, rd, tcg_res); + + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(tcg_op1); + tcg_temp_free_i32(tcg_op2); + tcg_temp_free_i32(tcg_op3); + tcg_temp_free_i32(tcg_res); +} + /* Floating point data-processing (3 source) * 31 30 29 28 24 23 22 21 20 16 15 14 10 9 5 4 0 * +---+---+---+-----------+------+----+------+----+------+------+------+ @@ -5192,6 +5558,16 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn) } handle_fp_3src_double(s, o0, o1, rd, rn, rm, ra); break; + case 3: + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + if (!fp_access_check(s)) { + return; + } + handle_fp_3src_half(s, o0, o1, rd, rn, rm, ra); + break; default: unallocated_encoding(s); } @@ -5201,7 +5577,7 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn) * the range 01....1xx to 10....0xx, and the most significant 4 bits of * the mantissa; see VFPExpandImm() in the v8 ARM ARM. */ -static uint64_t vfp_expand_imm(int size, uint8_t imm8) +uint64_t vfp_expand_imm(int size, uint8_t imm8) { uint64_t imm; @@ -5239,11 +5615,25 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn) { int rd = extract32(insn, 0, 5); int imm8 = extract32(insn, 13, 8); - int is_double = extract32(insn, 22, 2); + int type = extract32(insn, 22, 2); uint64_t imm; TCGv_i64 tcg_res; + TCGMemOp sz; - if (is_double > 1) { + switch (type) { + case 0: + sz = MO_32; + break; + case 1: + sz = MO_64; + break; + case 3: + sz = MO_16; + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -5252,7 +5642,7 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn) return; } - imm = vfp_expand_imm(MO_32 + is_double, imm8); + imm = vfp_expand_imm(sz, imm8); tcg_res = tcg_const_i64(imm); write_fp_dreg(s, rd, tcg_res); @@ -5268,11 +5658,11 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, bool itof, int rmode, int scale, int sf, int type) { bool is_signed = !(opcode & 1); - bool is_double = type; TCGv_ptr tcg_fpstatus; - TCGv_i32 tcg_shift; + TCGv_i32 tcg_shift, tcg_single; + TCGv_i64 tcg_double; - tcg_fpstatus = get_fpstatus_ptr(false); + tcg_fpstatus = get_fpstatus_ptr(type == 3); tcg_shift = tcg_const_i32(64 - scale); @@ -5290,8 +5680,9 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_int = tcg_extend; } - if (is_double) { - TCGv_i64 tcg_double = tcg_temp_new_i64(); + switch (type) { + case 1: /* float64 */ + tcg_double = tcg_temp_new_i64(); if (is_signed) { gen_helper_vfp_sqtod(tcg_double, tcg_int, tcg_shift, tcg_fpstatus); @@ -5301,8 +5692,10 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, } write_fp_dreg(s, rd, tcg_double); tcg_temp_free_i64(tcg_double); - } else { - TCGv_i32 tcg_single = tcg_temp_new_i32(); + break; + + case 0: /* float32 */ + tcg_single = tcg_temp_new_i32(); if (is_signed) { gen_helper_vfp_sqtos(tcg_single, tcg_int, tcg_shift, tcg_fpstatus); @@ -5312,6 +5705,23 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, } write_fp_sreg(s, rd, tcg_single); tcg_temp_free_i32(tcg_single); + break; + + case 3: /* float16 */ + tcg_single = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_sqtoh(tcg_single, tcg_int, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_uqtoh(tcg_single, tcg_int, + tcg_shift, tcg_fpstatus); + } + write_fp_sreg(s, rd, tcg_single); + tcg_temp_free_i32(tcg_single); + break; + + default: + g_assert_not_reached(); } } else { TCGv_i64 tcg_int = cpu_reg(s, rd); @@ -5328,8 +5738,9 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); - if (is_double) { - TCGv_i64 tcg_double = read_fp_dreg(s, rn); + switch (type) { + case 1: /* float64 */ + tcg_double = read_fp_dreg(s, rn); if (is_signed) { if (!sf) { gen_helper_vfp_tosld(tcg_int, tcg_double, @@ -5347,9 +5758,14 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_shift, tcg_fpstatus); } } + if (!sf) { + tcg_gen_ext32u_i64(tcg_int, tcg_int); + } tcg_temp_free_i64(tcg_double); - } else { - TCGv_i32 tcg_single = read_fp_sreg(s, rn); + break; + + case 0: /* float32 */ + tcg_single = read_fp_sreg(s, rn); if (sf) { if (is_signed) { gen_helper_vfp_tosqs(tcg_int, tcg_single, @@ -5371,14 +5787,39 @@ static void handle_fpfpcvt(DisasContext *s, int rd, int rn, int opcode, tcg_temp_free_i32(tcg_dest); } tcg_temp_free_i32(tcg_single); + break; + + case 3: /* float16 */ + tcg_single = read_fp_sreg(s, rn); + if (sf) { + if (is_signed) { + gen_helper_vfp_tosqh(tcg_int, tcg_single, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_touqh(tcg_int, tcg_single, + tcg_shift, tcg_fpstatus); + } + } else { + TCGv_i32 tcg_dest = tcg_temp_new_i32(); + if (is_signed) { + gen_helper_vfp_toslh(tcg_dest, tcg_single, + tcg_shift, tcg_fpstatus); + } else { + gen_helper_vfp_toulh(tcg_dest, tcg_single, + tcg_shift, tcg_fpstatus); + } + tcg_gen_extu_i32_i64(tcg_int, tcg_dest); + tcg_temp_free_i32(tcg_dest); + } + tcg_temp_free_i32(tcg_single); + break; + + default: + g_assert_not_reached(); } gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); tcg_temp_free_i32(tcg_rmode); - - if (!sf) { - tcg_gen_ext32u_i64(tcg_int, tcg_int); - } } tcg_temp_free_ptr(tcg_fpstatus); @@ -5403,8 +5844,21 @@ static void disas_fp_fixed_conv(DisasContext *s, uint32_t insn) bool sf = extract32(insn, 31, 1); bool itof; - if (sbit || (type > 1) - || (!sf && scale < 32)) { + if (sbit || (!sf && scale < 32)) { + unallocated_encoding(s); + return; + } + + switch (type) { + case 0: /* float32 */ + case 1: /* float64 */ + break; + case 3: /* float16 */ + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -5438,32 +5892,34 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) if (itof) { TCGv_i64 tcg_rn = cpu_reg(s, rn); + TCGv_i64 tmp; switch (type) { case 0: - { /* 32 bit */ - TCGv_i64 tmp = tcg_temp_new_i64(); + tmp = tcg_temp_new_i64(); tcg_gen_ext32u_i64(tmp, tcg_rn); - tcg_gen_st_i64(tmp, cpu_env, fp_reg_offset(s, rd, MO_64)); - tcg_gen_movi_i64(tmp, 0); - tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(s, rd)); + write_fp_dreg(s, rd, tmp); tcg_temp_free_i64(tmp); break; - } case 1: - { /* 64 bit */ - TCGv_i64 tmp = tcg_const_i64(0); - tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_offset(s, rd, MO_64)); - tcg_gen_st_i64(tmp, cpu_env, fp_reg_hi_offset(s, rd)); - tcg_temp_free_i64(tmp); + write_fp_dreg(s, rd, tcg_rn); break; - } case 2: /* 64 bit to top half. */ tcg_gen_st_i64(tcg_rn, cpu_env, fp_reg_hi_offset(s, rd)); + clear_vec_high(s, true, rd); break; + case 3: + /* 16 bit */ + tmp = tcg_temp_new_i64(); + tcg_gen_ext16u_i64(tmp, tcg_rn); + write_fp_dreg(s, rd, tmp); + tcg_temp_free_i64(tmp); + break; + default: + g_assert_not_reached(); } } else { TCGv_i64 tcg_rd = cpu_reg(s, rd); @@ -5481,6 +5937,12 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) /* 64 bits from top half */ tcg_gen_ld_i64(tcg_rd, cpu_env, fp_reg_hi_offset(s, rn)); break; + case 3: + /* 16 bit */ + tcg_gen_ld16u_i64(tcg_rd, cpu_env, fp_reg_offset(s, rn, MO_16)); + break; + default: + g_assert_not_reached(); } } } @@ -5520,10 +5982,16 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) case 0xa: /* 64 bit */ case 0xd: /* 64 bit to top half of quad */ break; + case 0x6: /* 16-bit float, 32-bit int */ + case 0xe: /* 16-bit float, 64-bit int */ + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ default: /* all other sf/type/rmode combinations are invalid */ unallocated_encoding(s); - break; + return; } if (!fp_access_check(s)) { @@ -5534,7 +6002,20 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) /* actual FP conversions */ bool itof = extract32(opcode, 1, 1); - if (type > 1 || (rmode != 0 && opcode > 1)) { + if (rmode != 0 && opcode > 1) { + unallocated_encoding(s); + return; + } + switch (type) { + case 0: /* float32 */ + case 1: /* float64 */ + break; + case 3: /* float16 */ + if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + break; + } + /* fallthru */ + default: unallocated_encoding(s); return; } @@ -6015,15 +6496,18 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) tcg_gen_add_i64(tcg_res, tcg_res, tcg_elt); break; case 0x0a: /* SMAXV / UMAXV */ - tcg_gen_movcond_i64(is_u ? TCG_COND_GEU : TCG_COND_GE, - tcg_res, - tcg_res, tcg_elt, tcg_res, tcg_elt); + if (is_u) { + tcg_gen_umax_i64(tcg_res, tcg_res, tcg_elt); + } else { + tcg_gen_smax_i64(tcg_res, tcg_res, tcg_elt); + } break; case 0x1a: /* SMINV / UMINV */ - tcg_gen_movcond_i64(is_u ? TCG_COND_LEU : TCG_COND_LE, - tcg_res, - tcg_res, tcg_elt, tcg_res, tcg_elt); - break; + if (is_u) { + tcg_gen_umin_i64(tcg_res, tcg_res, tcg_elt); + } else { + tcg_gen_smin_i64(tcg_res, tcg_res, tcg_elt); + } break; default: g_assert_not_reached(); @@ -7159,13 +7643,26 @@ static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar, int immh, int immb, int opcode, int rn, int rd) { - bool is_double = extract32(immh, 3, 1); - int size = is_double ? MO_64 : MO_32; - int elements; + int size, elements, fracbits; int immhb = immh << 3 | immb; - int fracbits = (is_double ? 128 : 64) - immhb; - if (!extract32(immh, 2, 2)) { + if (immh & 8) { + size = MO_64; + if (!is_scalar && !is_q) { + unallocated_encoding(s); + return; + } + } else if (immh & 4) { + size = MO_32; + } else if (immh & 2) { + size = MO_16; + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + } else { + /* immh == 0 would be a failure of the decode logic */ + g_assert(immh == 1); unallocated_encoding(s); return; } @@ -7173,20 +7670,14 @@ static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar, if (is_scalar) { elements = 1; } else { - elements = is_double ? 2 : is_q ? 4 : 2; - if (is_double && !is_q) { - unallocated_encoding(s); - return; - } + elements = (8 << is_q) >> size; } + fracbits = (16 << size) - immhb; if (!fp_access_check(s)) { return; } - /* immh == 0 would be a failure of the decode logic */ - g_assert(immh); - handle_simd_intfp_conv(s, rd, rn, elements, !is_u, fracbits, size); } @@ -7195,19 +7686,28 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, bool is_q, bool is_u, int immh, int immb, int rn, int rd) { - bool is_double = extract32(immh, 3, 1); int immhb = immh << 3 | immb; - int fracbits = (is_double ? 128 : 64) - immhb; - int pass; + int pass, size, fracbits; TCGv_ptr tcg_fpstatus; TCGv_i32 tcg_rmode, tcg_shift; - if (!extract32(immh, 2, 2)) { - unallocated_encoding(s); - return; - } - - if (!is_scalar && !is_q && is_double) { + if (immh & 0x8) { + size = MO_64; + if (!is_scalar && !is_q) { + unallocated_encoding(s); + return; + } + } else if (immh & 0x4) { + size = MO_32; + } else if (immh & 0x2) { + size = MO_16; + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + unallocated_encoding(s); + return; + } + } else { + /* Should have split out AdvSIMD modified immediate earlier. */ + assert(immh == 1); unallocated_encoding(s); return; } @@ -7219,11 +7719,12 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, assert(!(is_scalar && is_q)); tcg_rmode = tcg_const_i32(arm_rmode_to_sf(FPROUNDING_ZERO)); - tcg_fpstatus = get_fpstatus_ptr(false); + tcg_fpstatus = get_fpstatus_ptr(size == MO_16); gen_helper_set_rmode(tcg_rmode, tcg_rmode, tcg_fpstatus); + fracbits = (16 << size) - immhb; tcg_shift = tcg_const_i32(fracbits); - if (is_double) { + if (size == MO_64) { int maxpass = is_scalar ? 1 : 2; for (pass = 0; pass < maxpass; pass++) { @@ -7240,20 +7741,37 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, } clear_vec_high(s, is_q, rd); } else { - int maxpass = is_scalar ? 1 : is_q ? 4 : 2; + void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr); + int maxpass = is_scalar ? 1 : ((8 << is_q) >> size); + + switch (size) { + case MO_16: + if (is_u) { + fn = gen_helper_vfp_touhh; + } else { + fn = gen_helper_vfp_toshh; + } + break; + case MO_32: + if (is_u) { + fn = gen_helper_vfp_touls; + } else { + fn = gen_helper_vfp_tosls; + } + break; + default: + g_assert_not_reached(); + } + for (pass = 0; pass < maxpass; pass++) { TCGv_i32 tcg_op = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_op, rn, pass, MO_32); - if (is_u) { - gen_helper_vfp_touls(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); - } else { - gen_helper_vfp_tosls(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); - } + read_vec_element_i32(s, tcg_op, rn, pass, size); + fn(tcg_op, tcg_op, tcg_shift, tcg_fpstatus); if (is_scalar) { write_fp_sreg(s, rd, tcg_op); } else { - write_vec_element_i32(s, tcg_op, rd, pass, MO_32); + write_vec_element_i32(s, tcg_op, rd, pass, size); } tcg_temp_free_i32(tcg_op); } @@ -7413,13 +7931,10 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) tcg_temp_free_i64(tcg_op2); tcg_temp_free_i64(tcg_res); } else { - TCGv_i32 tcg_op1 = tcg_temp_new_i32(); - TCGv_i32 tcg_op2 = tcg_temp_new_i32(); + TCGv_i32 tcg_op1 = read_fp_hreg(s, rn); + TCGv_i32 tcg_op2 = read_fp_hreg(s, rm); TCGv_i64 tcg_res = tcg_temp_new_i64(); - read_vec_element_i32(s, tcg_op1, rn, 0, MO_16); - read_vec_element_i32(s, tcg_op2, rm, 0, MO_16); - gen_helper_neon_mull_s16(tcg_res, tcg_op1, tcg_op2); gen_helper_neon_addl_saturate_s32(tcg_res, cpu_env, tcg_res, tcg_res); @@ -7960,13 +8475,10 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, fpst = get_fpstatus_ptr(true); - tcg_op1 = tcg_temp_new_i32(); - tcg_op2 = tcg_temp_new_i32(); + tcg_op1 = read_fp_hreg(s, rn); + tcg_op2 = read_fp_hreg(s, rm); tcg_res = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_op1, rn, 0, MO_16); - read_vec_element_i32(s, tcg_op2, rm, 0, MO_16); - switch (fpopcode) { case 0x03: /* FMULX */ gen_helper_advsimd_mulxh(tcg_res, tcg_op1, tcg_op2, fpst); @@ -8481,12 +8993,17 @@ static void handle_2misc_narrow(DisasContext *s, bool scalar, } else { TCGv_i32 tcg_lo = tcg_temp_new_i32(); TCGv_i32 tcg_hi = tcg_temp_new_i32(); + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); + tcg_gen_extr_i64_i32(tcg_lo, tcg_hi, tcg_op); - gen_helper_vfp_fcvt_f32_to_f16(tcg_lo, tcg_lo, cpu_env); - gen_helper_vfp_fcvt_f32_to_f16(tcg_hi, tcg_hi, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tcg_lo, tcg_lo, fpst, ahp); + gen_helper_vfp_fcvt_f32_to_f16(tcg_hi, tcg_hi, fpst, ahp); tcg_gen_deposit_i32(tcg_res[pass], tcg_lo, tcg_hi, 16, 16); tcg_temp_free_i32(tcg_lo); tcg_temp_free_i32(tcg_hi); + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(ahp); } break; case 0x56: /* FCVTXN, FCVTXN2 */ @@ -9013,11 +9530,7 @@ static void handle_vec_simd_shri(DisasContext *s, bool is_q, bool is_u, unallocated_encoding(s); return; } - - if (size > 3 && !is_q) { - unallocated_encoding(s); - return; - } + tcg_debug_assert(size <= 3); if (!fp_access_check(s)) { return; @@ -9925,27 +10438,6 @@ static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) } } -/* Helper functions for 32 bit comparisons */ -static void gen_max_s32(TCGv_i32 res, TCGv_i32 op1, TCGv_i32 op2) -{ - tcg_gen_movcond_i32(TCG_COND_GE, res, op1, op2, op1, op2); -} - -static void gen_max_u32(TCGv_i32 res, TCGv_i32 op1, TCGv_i32 op2) -{ - tcg_gen_movcond_i32(TCG_COND_GEU, res, op1, op2, op1, op2); -} - -static void gen_min_s32(TCGv_i32 res, TCGv_i32 op1, TCGv_i32 op2) -{ - tcg_gen_movcond_i32(TCG_COND_LE, res, op1, op2, op1, op2); -} - -static void gen_min_u32(TCGv_i32 res, TCGv_i32 op1, TCGv_i32 op2) -{ - tcg_gen_movcond_i32(TCG_COND_LEU, res, op1, op2, op1, op2); -} - /* Pairwise op subgroup of C3.6.16. * * This is called directly or via the handle_3same_float for float pairwise @@ -10045,7 +10537,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, static NeonGenTwoOpFn * const fns[3][2] = { { gen_helper_neon_pmax_s8, gen_helper_neon_pmax_u8 }, { gen_helper_neon_pmax_s16, gen_helper_neon_pmax_u16 }, - { gen_max_s32, gen_max_u32 }, + { tcg_gen_smax_i32, tcg_gen_umax_i32 }, }; genfn = fns[size][u]; break; @@ -10055,7 +10547,7 @@ static void handle_simd_3same_pair(DisasContext *s, int is_q, int u, int opcode, static NeonGenTwoOpFn * const fns[3][2] = { { gen_helper_neon_pmin_s8, gen_helper_neon_pmin_u8 }, { gen_helper_neon_pmin_s16, gen_helper_neon_pmin_u16 }, - { gen_min_s32, gen_min_u32 }, + { tcg_gen_smin_i32, tcg_gen_umin_i32 }, }; genfn = fns[size][u]; break; @@ -10510,7 +11002,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) static NeonGenTwoOpFn * const fns[3][2] = { { gen_helper_neon_max_s8, gen_helper_neon_max_u8 }, { gen_helper_neon_max_s16, gen_helper_neon_max_u16 }, - { gen_max_s32, gen_max_u32 }, + { tcg_gen_smax_i32, tcg_gen_umax_i32 }, }; genfn = fns[size][u]; break; @@ -10521,7 +11013,7 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) static NeonGenTwoOpFn * const fns[3][2] = { { gen_helper_neon_min_s8, gen_helper_neon_min_u8 }, { gen_helper_neon_min_s16, gen_helper_neon_min_u16 }, - { gen_min_s32, gen_min_u32 }, + { tcg_gen_smin_i32, tcg_gen_umin_i32 }, }; genfn = fns[size][u]; break; @@ -10853,6 +11345,14 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } feature = ARM_FEATURE_V8_RDM; break; + case 0x02: /* SDOT (vector) */ + case 0x12: /* UDOT (vector) */ + if (size != MO_32) { + unallocated_encoding(s); + return; + } + feature = ARM_FEATURE_V8_DOTPROD; + break; case 0x8: /* FCMLA, #0 */ case 0x9: /* FCMLA, #90 */ case 0xa: /* FCMLA, #180 */ @@ -10906,6 +11406,11 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) } return; + case 0x2: /* SDOT / UDOT */ + gen_gvec_op3_ool(s, is_q, rd, rn, rm, 0, + u ? gen_helper_gvec_udot_b : gen_helper_gvec_sdot_b); + return; + case 0x8: /* FCMLA, #0 */ case 0x9: /* FCMLA, #90 */ case 0xa: /* FCMLA, #180 */ @@ -10985,18 +11490,23 @@ static void handle_2misc_widening(DisasContext *s, int opcode, bool is_q, /* 16 -> 32 bit fp conversion */ int srcelt = is_q ? 4 : 0; TCGv_i32 tcg_res[4]; + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); for (pass = 0; pass < 4; pass++) { tcg_res[pass] = tcg_temp_new_i32(); read_vec_element_i32(s, tcg_res[pass], rn, srcelt + pass, MO_16); gen_helper_vfp_fcvt_f16_to_f32(tcg_res[pass], tcg_res[pass], - cpu_env); + fpst, ahp); } for (pass = 0; pass < 4; pass++) { write_vec_element_i32(s, tcg_res[pass], rd, pass, MO_32); tcg_temp_free_i32(tcg_res[pass]); } + + tcg_temp_free_ptr(fpst); + tcg_temp_free_i32(ahp); } } @@ -11471,7 +11981,11 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) /* All 64-bit element operations can be shared with scalar 2misc */ int pass; - for (pass = 0; pass < (is_q ? 2 : 1); pass++) { + /* Coverity claims (size == 3 && !is_q) has been eliminated + * from all paths leading to here. + */ + tcg_debug_assert(is_q); + for (pass = 0; pass < 2; pass++) { TCGv_i64 tcg_op = tcg_temp_new_i64(); TCGv_i64 tcg_res = tcg_temp_new_i64(); @@ -11885,11 +12399,9 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) } if (is_scalar) { - TCGv_i32 tcg_op = tcg_temp_new_i32(); + TCGv_i32 tcg_op = read_fp_hreg(s, rn); TCGv_i32 tcg_res = tcg_temp_new_i32(); - read_vec_element_i32(s, tcg_op, rn, 0, MO_16); - switch (fpop) { case 0x1a: /* FCVTNS */ case 0x1b: /* FCVTMS */ @@ -12078,6 +12590,13 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) return; } break; + case 0x0e: /* SDOT */ + case 0x1e: /* UDOT */ + if (size != MO_32 || !arm_dc_feature(s, ARM_FEATURE_V8_DOTPROD)) { + unallocated_encoding(s); + return; + } + break; case 0x11: /* FCMLA #0 */ case 0x13: /* FCMLA #90 */ case 0x15: /* FCMLA #180 */ @@ -12175,19 +12694,28 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } switch (16 * u + opcode) { + case 0x0e: /* SDOT */ + case 0x1e: /* UDOT */ + gen_gvec_op3_ool(s, is_q, rd, rn, rm, index, + u ? gen_helper_gvec_udot_idx_b + : gen_helper_gvec_sdot_idx_b); + return; case 0x11: /* FCMLA #0 */ case 0x13: /* FCMLA #90 */ case 0x15: /* FCMLA #180 */ case 0x17: /* FCMLA #270 */ - tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), - vec_full_reg_offset(s, rn), - vec_reg_offset(s, rm, index, size), fpst, - is_q ? 16 : 8, vec_full_reg_size(s), - extract32(insn, 13, 2), /* rot */ - size == MO_64 - ? gen_helper_gvec_fcmlas_idx - : gen_helper_gvec_fcmlah_idx); - tcg_temp_free_ptr(fpst); + { + int rot = extract32(insn, 13, 2); + int data = (index << 2) | rot; + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), fpst, + is_q ? 16 : 8, vec_full_reg_size(s), data, + size == MO_64 + ? gen_helper_gvec_fcmlas_idx + : gen_helper_gvec_fcmlah_idx); + tcg_temp_free_ptr(fpst); + } return; } @@ -13186,9 +13714,14 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) s->fp_access_checked = false; switch (extract32(insn, 25, 4)) { - case 0x0: case 0x1: case 0x2: case 0x3: /* UNALLOCATED */ + case 0x0: case 0x1: case 0x3: /* UNALLOCATED */ unallocated_encoding(s); break; + case 0x2: + if (!arm_dc_feature(s, ARM_FEATURE_SVE) || !disas_sve(s, insn)) { + unallocated_encoding(s); + } + break; case 0x8: case 0x9: /* Data processing - immediate */ disas_data_proc_imm(s, insn); break; @@ -13218,8 +13751,8 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) free_tmp_a64(s); } -static int aarch64_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cpu, int max_insns) +static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, + CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cpu->env_ptr; @@ -13282,11 +13815,9 @@ static int aarch64_tr_init_disas_context(DisasContextBase *dcbase, if (dc->ss_active) { bound = 1; } - max_insns = MIN(max_insns, bound); + dc->base.max_insns = MIN(dc->base.max_insns, bound); init_tmp_a64_array(dc); - - return max_insns; } static void aarch64_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -13390,7 +13921,7 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) gen_a64_set_pc_im(dc->pc); /* fall through */ case DISAS_EXIT: - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_JUMP: tcg_gen_lookup_and_goto_ptr(); @@ -13419,7 +13950,7 @@ static void aarch64_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; } } diff --git a/target/arm/translate-a64.h b/target/arm/translate-a64.h new file mode 100644 index 0000000000..63d958cf50 --- /dev/null +++ b/target/arm/translate-a64.h @@ -0,0 +1,126 @@ +/* + * AArch64 translation, common definitions. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef TARGET_ARM_TRANSLATE_A64_H +#define TARGET_ARM_TRANSLATE_A64_H + +void unallocated_encoding(DisasContext *s); + +#define unsupported_encoding(s, insn) \ + do { \ + qemu_log_mask(LOG_UNIMP, \ + "%s:%d: unsupported instruction encoding 0x%08x " \ + "at pc=%016" PRIx64 "\n", \ + __FILE__, __LINE__, insn, s->pc - 4); \ + unallocated_encoding(s); \ + } while (0) + +TCGv_i64 new_tmp_a64(DisasContext *s); +TCGv_i64 new_tmp_a64_zero(DisasContext *s); +TCGv_i64 cpu_reg(DisasContext *s, int reg); +TCGv_i64 cpu_reg_sp(DisasContext *s, int reg); +TCGv_i64 read_cpu_reg(DisasContext *s, int reg, int sf); +TCGv_i64 read_cpu_reg_sp(DisasContext *s, int reg, int sf); +void write_fp_dreg(DisasContext *s, int reg, TCGv_i64 v); +TCGv_ptr get_fpstatus_ptr(bool); +bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, + unsigned int imms, unsigned int immr); +uint64_t vfp_expand_imm(int size, uint8_t imm8); +bool sve_access_check(DisasContext *s); + +/* We should have at some point before trying to access an FP register + * done the necessary access check, so assert that + * (a) we did the check and + * (b) we didn't then just plough ahead anyway if it failed. + * Print the instruction pattern in the abort message so we can figure + * out what we need to fix if a user encounters this problem in the wild. + */ +static inline void assert_fp_access_checked(DisasContext *s) +{ +#ifdef CONFIG_DEBUG_TCG + if (unlikely(!s->fp_access_checked || s->fp_excp_el)) { + fprintf(stderr, "target-arm: FP access check missing for " + "instruction 0x%08x\n", s->insn); + abort(); + } +#endif +} + +/* Return the offset into CPUARMState of an element of specified + * size, 'element' places in from the least significant end of + * the FP/vector register Qn. + */ +static inline int vec_reg_offset(DisasContext *s, int regno, + int element, TCGMemOp size) +{ + int element_size = 1 << size; + int offs = element * element_size; +#ifdef HOST_WORDS_BIGENDIAN + /* This is complicated slightly because vfp.zregs[n].d[0] is + * still the lowest and vfp.zregs[n].d[15] the highest of the + * 256 byte vector, even on big endian systems. + * + * Calculate the offset assuming fully little-endian, + * then XOR to account for the order of the 8-byte units. + * + * For 16 byte elements, the two 8 byte halves will not form a + * host int128 if the host is bigendian, since they're in the + * wrong order. However the only 16 byte operation we have is + * a move, so we can ignore this for the moment. More complicated + * operations will have to special case loading and storing from + * the zregs array. + */ + if (element_size < 8) { + offs ^= 8 - element_size; + } +#endif + offs += offsetof(CPUARMState, vfp.zregs[regno]); + assert_fp_access_checked(s); + return offs; +} + +/* Return the offset info CPUARMState of the "whole" vector register Qn. */ +static inline int vec_full_reg_offset(DisasContext *s, int regno) +{ + assert_fp_access_checked(s); + return offsetof(CPUARMState, vfp.zregs[regno]); +} + +/* Return a newly allocated pointer to the vector register. */ +static inline TCGv_ptr vec_full_reg_ptr(DisasContext *s, int regno) +{ + TCGv_ptr ret = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(ret, cpu_env, vec_full_reg_offset(s, regno)); + return ret; +} + +/* Return the byte size of the "whole" vector register, VL / 8. */ +static inline int vec_full_reg_size(DisasContext *s) +{ + return s->sve_len; +} + +bool disas_sve(DisasContext *, uint32_t); + +/* Note that the gvec expanders operate on offsets + sizes. */ +typedef void GVecGen2Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t); +typedef void GVecGen2iFn(unsigned, uint32_t, uint32_t, int64_t, + uint32_t, uint32_t); +typedef void GVecGen3Fn(unsigned, uint32_t, uint32_t, + uint32_t, uint32_t, uint32_t); + +#endif /* TARGET_ARM_TRANSLATE_A64_H */ diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c new file mode 100644 index 0000000000..374051cd20 --- /dev/null +++ b/target/arm/translate-sve.c @@ -0,0 +1,5208 @@ +/* + * AArch64 SVE translation + * + * Copyright (c) 2018 Linaro, Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "tcg-op.h" +#include "tcg-op-gvec.h" +#include "tcg-gvec-desc.h" +#include "qemu/log.h" +#include "arm_ldst.h" +#include "translate.h" +#include "internals.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/log.h" +#include "trace-tcg.h" +#include "translate-a64.h" +#include "fpu/softfloat.h" + + +typedef void GVecGen2sFn(unsigned, uint32_t, uint32_t, + TCGv_i64, uint32_t, uint32_t); + +typedef void gen_helper_gvec_flags_3(TCGv_i32, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); +typedef void gen_helper_gvec_flags_4(TCGv_i32, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_ptr, TCGv_i32); + +typedef void gen_helper_gvec_mem(TCGv_env, TCGv_ptr, TCGv_i64, TCGv_i32); +typedef void gen_helper_gvec_mem_scatter(TCGv_env, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i64, TCGv_i32); + +/* + * Helpers for extracting complex instruction fields. + */ + +/* See e.g. ASR (immediate, predicated). + * Returns -1 for unallocated encoding; diagnose later. + */ +static int tszimm_esz(int x) +{ + x >>= 3; /* discard imm3 */ + return 31 - clz32(x); +} + +static int tszimm_shr(int x) +{ + return (16 << tszimm_esz(x)) - x; +} + +/* See e.g. LSL (immediate, predicated). */ +static int tszimm_shl(int x) +{ + return x - (8 << tszimm_esz(x)); +} + +static inline int plus1(int x) +{ + return x + 1; +} + +/* The SH bit is in bit 8. Extract the low 8 and shift. */ +static inline int expand_imm_sh8s(int x) +{ + return (int8_t)x << (x & 0x100 ? 8 : 0); +} + +static inline int expand_imm_sh8u(int x) +{ + return (uint8_t)x << (x & 0x100 ? 8 : 0); +} + +/* Convert a 2-bit memory size (msz) to a 4-bit data type (dtype) + * with unsigned data. C.f. SVE Memory Contiguous Load Group. + */ +static inline int msz_dtype(int msz) +{ + static const uint8_t dtype[4] = { 0, 5, 10, 15 }; + return dtype[msz]; +} + +/* + * Include the generated decoder. + */ + +#include "decode-sve.inc.c" + +/* + * Implement all of the translator functions referenced by the decoder. + */ + +/* Return the offset info CPUARMState of the predicate vector register Pn. + * Note for this purpose, FFR is P16. + */ +static inline int pred_full_reg_offset(DisasContext *s, int regno) +{ + return offsetof(CPUARMState, vfp.pregs[regno]); +} + +/* Return the byte size of the whole predicate register, VL / 64. */ +static inline int pred_full_reg_size(DisasContext *s) +{ + return s->sve_len >> 3; +} + +/* Round up the size of a register to a size allowed by + * the tcg vector infrastructure. Any operation which uses this + * size may assume that the bits above pred_full_reg_size are zero, + * and must leave them the same way. + * + * Note that this is not needed for the vector registers as they + * are always properly sized for tcg vectors. + */ +static int size_for_gvec(int size) +{ + if (size <= 8) { + return 8; + } else { + return QEMU_ALIGN_UP(size, 16); + } +} + +static int pred_gvec_reg_size(DisasContext *s) +{ + return size_for_gvec(pred_full_reg_size(s)); +} + +/* Invoke a vector expander on two Zregs. */ +static bool do_vector2_z(DisasContext *s, GVecGen2Fn *gvec_fn, + int esz, int rd, int rn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + gvec_fn(esz, vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), vsz, vsz); + } + return true; +} + +/* Invoke a vector expander on three Zregs. */ +static bool do_vector3_z(DisasContext *s, GVecGen3Fn *gvec_fn, + int esz, int rd, int rn, int rm) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + gvec_fn(esz, vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), vsz, vsz); + } + return true; +} + +/* Invoke a vector move on two Zregs. */ +static bool do_mov_z(DisasContext *s, int rd, int rn) +{ + return do_vector2_z(s, tcg_gen_gvec_mov, 0, rd, rn); +} + +/* Initialize a Zreg with replications of a 64-bit immediate. */ +static void do_dupi_z(DisasContext *s, int rd, uint64_t word) +{ + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_dup64i(vec_full_reg_offset(s, rd), vsz, vsz, word); +} + +/* Invoke a vector expander on two Pregs. */ +static bool do_vector2_p(DisasContext *s, GVecGen2Fn *gvec_fn, + int esz, int rd, int rn) +{ + if (sve_access_check(s)) { + unsigned psz = pred_gvec_reg_size(s); + gvec_fn(esz, pred_full_reg_offset(s, rd), + pred_full_reg_offset(s, rn), psz, psz); + } + return true; +} + +/* Invoke a vector expander on three Pregs. */ +static bool do_vector3_p(DisasContext *s, GVecGen3Fn *gvec_fn, + int esz, int rd, int rn, int rm) +{ + if (sve_access_check(s)) { + unsigned psz = pred_gvec_reg_size(s); + gvec_fn(esz, pred_full_reg_offset(s, rd), + pred_full_reg_offset(s, rn), + pred_full_reg_offset(s, rm), psz, psz); + } + return true; +} + +/* Invoke a vector operation on four Pregs. */ +static bool do_vecop4_p(DisasContext *s, const GVecGen4 *gvec_op, + int rd, int rn, int rm, int rg) +{ + if (sve_access_check(s)) { + unsigned psz = pred_gvec_reg_size(s); + tcg_gen_gvec_4(pred_full_reg_offset(s, rd), + pred_full_reg_offset(s, rn), + pred_full_reg_offset(s, rm), + pred_full_reg_offset(s, rg), + psz, psz, gvec_op); + } + return true; +} + +/* Invoke a vector move on two Pregs. */ +static bool do_mov_p(DisasContext *s, int rd, int rn) +{ + return do_vector2_p(s, tcg_gen_gvec_mov, 0, rd, rn); +} + +/* Set the cpu flags as per a return from an SVE helper. */ +static void do_pred_flags(TCGv_i32 t) +{ + tcg_gen_mov_i32(cpu_NF, t); + tcg_gen_andi_i32(cpu_ZF, t, 2); + tcg_gen_andi_i32(cpu_CF, t, 1); + tcg_gen_movi_i32(cpu_VF, 0); +} + +/* Subroutines computing the ARM PredTest psuedofunction. */ +static void do_predtest1(TCGv_i64 d, TCGv_i64 g) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + gen_helper_sve_predtest1(t, d, g); + do_pred_flags(t); + tcg_temp_free_i32(t); +} + +static void do_predtest(DisasContext *s, int dofs, int gofs, int words) +{ + TCGv_ptr dptr = tcg_temp_new_ptr(); + TCGv_ptr gptr = tcg_temp_new_ptr(); + TCGv_i32 t; + + tcg_gen_addi_ptr(dptr, cpu_env, dofs); + tcg_gen_addi_ptr(gptr, cpu_env, gofs); + t = tcg_const_i32(words); + + gen_helper_sve_predtest(t, dptr, gptr, t); + tcg_temp_free_ptr(dptr); + tcg_temp_free_ptr(gptr); + + do_pred_flags(t); + tcg_temp_free_i32(t); +} + +/* For each element size, the bits within a predicate word that are active. */ +const uint64_t pred_esz_masks[4] = { + 0xffffffffffffffffull, 0x5555555555555555ull, + 0x1111111111111111ull, 0x0101010101010101ull +}; + +/* + *** SVE Logical - Unpredicated Group + */ + +static bool trans_AND_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_and, 0, a->rd, a->rn, a->rm); +} + +static bool trans_ORR_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + if (a->rn == a->rm) { /* MOV */ + return do_mov_z(s, a->rd, a->rn); + } else { + return do_vector3_z(s, tcg_gen_gvec_or, 0, a->rd, a->rn, a->rm); + } +} + +static bool trans_EOR_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_xor, 0, a->rd, a->rn, a->rm); +} + +static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); +} + +/* + *** SVE Integer Arithmetic - Unpredicated Group + */ + +static bool trans_ADD_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_add, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_SUB_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_sub, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_SQADD_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_ssadd, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_SQSUB_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_sssub, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_UQADD_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_usadd, a->esz, a->rd, a->rn, a->rm); +} + +static bool trans_UQSUB_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_vector3_z(s, tcg_gen_gvec_ussub, a->esz, a->rd, a->rn, a->rm); +} + +/* + *** SVE Integer Arithmetic - Binary Predicated Group + */ + +static bool do_zpzz_ool(DisasContext *s, arg_rprr_esz *a, gen_helper_gvec_4 *fn) +{ + unsigned vsz = vec_full_reg_size(s); + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + tcg_gen_gvec_4_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + vsz, vsz, 0, fn); + } + return true; +} + +/* Select active elememnts from Zn and inactive elements from Zm, + * storing the result in Zd. + */ +static void do_sel_z(DisasContext *s, int rd, int rn, int rm, int pg, int esz) +{ + static gen_helper_gvec_4 * const fns[4] = { + gen_helper_sve_sel_zpzz_b, gen_helper_sve_sel_zpzz_h, + gen_helper_sve_sel_zpzz_s, gen_helper_sve_sel_zpzz_d + }; + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_4_ool(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + pred_full_reg_offset(s, pg), + vsz, vsz, 0, fns[esz]); +} + +#define DO_ZPZZ(NAME, name) \ +static bool trans_##NAME##_zpzz(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_4 * const fns[4] = { \ + gen_helper_sve_##name##_zpzz_b, gen_helper_sve_##name##_zpzz_h, \ + gen_helper_sve_##name##_zpzz_s, gen_helper_sve_##name##_zpzz_d, \ + }; \ + return do_zpzz_ool(s, a, fns[a->esz]); \ +} + +DO_ZPZZ(AND, and) +DO_ZPZZ(EOR, eor) +DO_ZPZZ(ORR, orr) +DO_ZPZZ(BIC, bic) + +DO_ZPZZ(ADD, add) +DO_ZPZZ(SUB, sub) + +DO_ZPZZ(SMAX, smax) +DO_ZPZZ(UMAX, umax) +DO_ZPZZ(SMIN, smin) +DO_ZPZZ(UMIN, umin) +DO_ZPZZ(SABD, sabd) +DO_ZPZZ(UABD, uabd) + +DO_ZPZZ(MUL, mul) +DO_ZPZZ(SMULH, smulh) +DO_ZPZZ(UMULH, umulh) + +DO_ZPZZ(ASR, asr) +DO_ZPZZ(LSR, lsr) +DO_ZPZZ(LSL, lsl) + +static bool trans_SDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_4 * const fns[4] = { + NULL, NULL, gen_helper_sve_sdiv_zpzz_s, gen_helper_sve_sdiv_zpzz_d + }; + return do_zpzz_ool(s, a, fns[a->esz]); +} + +static bool trans_UDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_4 * const fns[4] = { + NULL, NULL, gen_helper_sve_udiv_zpzz_s, gen_helper_sve_udiv_zpzz_d + }; + return do_zpzz_ool(s, a, fns[a->esz]); +} + +static bool trans_SEL_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_sel_z(s, a->rd, a->rn, a->rm, a->pg, a->esz); + } + return true; +} + +#undef DO_ZPZZ + +/* + *** SVE Integer Arithmetic - Unary Predicated Group + */ + +static bool do_zpz_ool(DisasContext *s, arg_rpr_esz *a, gen_helper_gvec_3 *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + pred_full_reg_offset(s, a->pg), + vsz, vsz, 0, fn); + } + return true; +} + +#define DO_ZPZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_3 * const fns[4] = { \ + gen_helper_sve_##name##_b, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d, \ + }; \ + return do_zpz_ool(s, a, fns[a->esz]); \ +} + +DO_ZPZ(CLS, cls) +DO_ZPZ(CLZ, clz) +DO_ZPZ(CNT_zpz, cnt_zpz) +DO_ZPZ(CNOT, cnot) +DO_ZPZ(NOT_zpz, not_zpz) +DO_ZPZ(ABS, abs) +DO_ZPZ(NEG, neg) + +static bool trans_FABS(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_fabs_h, + gen_helper_sve_fabs_s, + gen_helper_sve_fabs_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_FNEG(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_fneg_h, + gen_helper_sve_fneg_s, + gen_helper_sve_fneg_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_SXTB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_sxtb_h, + gen_helper_sve_sxtb_s, + gen_helper_sve_sxtb_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_UXTB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_uxtb_h, + gen_helper_sve_uxtb_s, + gen_helper_sve_uxtb_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_SXTH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, NULL, + gen_helper_sve_sxth_s, + gen_helper_sve_sxth_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_UXTH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, NULL, + gen_helper_sve_uxth_s, + gen_helper_sve_uxth_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_SXTW(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ool(s, a, a->esz == 3 ? gen_helper_sve_sxtw_d : NULL); +} + +static bool trans_UXTW(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ool(s, a, a->esz == 3 ? gen_helper_sve_uxtw_d : NULL); +} + +#undef DO_ZPZ + +/* + *** SVE Integer Reduction Group + */ + +typedef void gen_helper_gvec_reduc(TCGv_i64, TCGv_ptr, TCGv_ptr, TCGv_i32); +static bool do_vpz_ool(DisasContext *s, arg_rpr_esz *a, + gen_helper_gvec_reduc *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_zn, t_pg; + TCGv_i32 desc; + TCGv_i64 temp; + + if (fn == NULL) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + temp = tcg_temp_new_i64(); + t_zn = tcg_temp_new_ptr(); + t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); + fn(temp, t_zn, t_pg, desc); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); + + write_fp_dreg(s, a->rd, temp); + tcg_temp_free_i64(temp); + return true; +} + +#define DO_VPZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_reduc * const fns[4] = { \ + gen_helper_sve_##name##_b, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d, \ + }; \ + return do_vpz_ool(s, a, fns[a->esz]); \ +} + +DO_VPZ(ORV, orv) +DO_VPZ(ANDV, andv) +DO_VPZ(EORV, eorv) + +DO_VPZ(UADDV, uaddv) +DO_VPZ(SMAXV, smaxv) +DO_VPZ(UMAXV, umaxv) +DO_VPZ(SMINV, sminv) +DO_VPZ(UMINV, uminv) + +static bool trans_SADDV(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_reduc * const fns[4] = { + gen_helper_sve_saddv_b, gen_helper_sve_saddv_h, + gen_helper_sve_saddv_s, NULL + }; + return do_vpz_ool(s, a, fns[a->esz]); +} + +#undef DO_VPZ + +/* + *** SVE Shift by Immediate - Predicated Group + */ + +/* Store zero into every active element of Zd. We will use this for two + * and three-operand predicated instructions for which logic dictates a + * zero result. + */ +static bool do_clr_zp(DisasContext *s, int rd, int pg, int esz) +{ + static gen_helper_gvec_2 * const fns[4] = { + gen_helper_sve_clr_b, gen_helper_sve_clr_h, + gen_helper_sve_clr_s, gen_helper_sve_clr_d, + }; + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, rd), + pred_full_reg_offset(s, pg), + vsz, vsz, 0, fns[esz]); + } + return true; +} + +/* Copy Zn into Zd, storing zeros into inactive elements. */ +static void do_movz_zpz(DisasContext *s, int rd, int rn, int pg, int esz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_movz_b, gen_helper_sve_movz_h, + gen_helper_sve_movz_s, gen_helper_sve_movz_d, + }; + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + pred_full_reg_offset(s, pg), + vsz, vsz, 0, fns[esz]); +} + +static bool do_zpzi_ool(DisasContext *s, arg_rpri_esz *a, + gen_helper_gvec_3 *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + pred_full_reg_offset(s, a->pg), + vsz, vsz, a->imm, fn); + } + return true; +} + +static bool trans_ASR_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_asr_zpzi_b, gen_helper_sve_asr_zpzi_h, + gen_helper_sve_asr_zpzi_s, gen_helper_sve_asr_zpzi_d, + }; + if (a->esz < 0) { + /* Invalid tsz encoding -- see tszimm_esz. */ + return false; + } + /* Shift by element size is architecturally valid. For + arithmetic right-shift, it's the same as by one less. */ + a->imm = MIN(a->imm, (8 << a->esz) - 1); + return do_zpzi_ool(s, a, fns[a->esz]); +} + +static bool trans_LSR_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_lsr_zpzi_b, gen_helper_sve_lsr_zpzi_h, + gen_helper_sve_lsr_zpzi_s, gen_helper_sve_lsr_zpzi_d, + }; + if (a->esz < 0) { + return false; + } + /* Shift by element size is architecturally valid. + For logical shifts, it is a zeroing operation. */ + if (a->imm >= (8 << a->esz)) { + return do_clr_zp(s, a->rd, a->pg, a->esz); + } else { + return do_zpzi_ool(s, a, fns[a->esz]); + } +} + +static bool trans_LSL_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_lsl_zpzi_b, gen_helper_sve_lsl_zpzi_h, + gen_helper_sve_lsl_zpzi_s, gen_helper_sve_lsl_zpzi_d, + }; + if (a->esz < 0) { + return false; + } + /* Shift by element size is architecturally valid. + For logical shifts, it is a zeroing operation. */ + if (a->imm >= (8 << a->esz)) { + return do_clr_zp(s, a->rd, a->pg, a->esz); + } else { + return do_zpzi_ool(s, a, fns[a->esz]); + } +} + +static bool trans_ASRD(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_asrd_b, gen_helper_sve_asrd_h, + gen_helper_sve_asrd_s, gen_helper_sve_asrd_d, + }; + if (a->esz < 0) { + return false; + } + /* Shift by element size is architecturally valid. For arithmetic + right shift for division, it is a zeroing operation. */ + if (a->imm >= (8 << a->esz)) { + return do_clr_zp(s, a->rd, a->pg, a->esz); + } else { + return do_zpzi_ool(s, a, fns[a->esz]); + } +} + +/* + *** SVE Bitwise Shift - Predicated Group + */ + +#define DO_ZPZW(NAME, name) \ +static bool trans_##NAME##_zpzw(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_4 * const fns[3] = { \ + gen_helper_sve_##name##_zpzw_b, gen_helper_sve_##name##_zpzw_h, \ + gen_helper_sve_##name##_zpzw_s, \ + }; \ + if (a->esz < 0 || a->esz >= 3) { \ + return false; \ + } \ + return do_zpzz_ool(s, a, fns[a->esz]); \ +} + +DO_ZPZW(ASR, asr) +DO_ZPZW(LSR, lsr) +DO_ZPZW(LSL, lsl) + +#undef DO_ZPZW + +/* + *** SVE Bitwise Shift - Unpredicated Group + */ + +static bool do_shift_imm(DisasContext *s, arg_rri_esz *a, bool asr, + void (*gvec_fn)(unsigned, uint32_t, uint32_t, + int64_t, uint32_t, uint32_t)) +{ + if (a->esz < 0) { + /* Invalid tsz encoding -- see tszimm_esz. */ + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + /* Shift by element size is architecturally valid. For + arithmetic right-shift, it's the same as by one less. + Otherwise it is a zeroing operation. */ + if (a->imm >= 8 << a->esz) { + if (asr) { + a->imm = (8 << a->esz) - 1; + } else { + do_dupi_z(s, a->rd, 0); + return true; + } + } + gvec_fn(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), a->imm, vsz, vsz); + } + return true; +} + +static bool trans_ASR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_shift_imm(s, a, true, tcg_gen_gvec_sari); +} + +static bool trans_LSR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_shift_imm(s, a, false, tcg_gen_gvec_shri); +} + +static bool trans_LSL_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_shift_imm(s, a, false, tcg_gen_gvec_shli); +} + +static bool do_zzw_ool(DisasContext *s, arg_rrr_esz *a, gen_helper_gvec_3 *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, 0, fn); + } + return true; +} + +#define DO_ZZW(NAME, name) \ +static bool trans_##NAME##_zzw(DisasContext *s, arg_rrr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_3 * const fns[4] = { \ + gen_helper_sve_##name##_zzw_b, gen_helper_sve_##name##_zzw_h, \ + gen_helper_sve_##name##_zzw_s, NULL \ + }; \ + return do_zzw_ool(s, a, fns[a->esz]); \ +} + +DO_ZZW(ASR, asr) +DO_ZZW(LSR, lsr) +DO_ZZW(LSL, lsl) + +#undef DO_ZZW + +/* + *** SVE Integer Multiply-Add Group + */ + +static bool do_zpzzz_ool(DisasContext *s, arg_rprrr_esz *a, + gen_helper_gvec_5 *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_5_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->ra), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + vsz, vsz, 0, fn); + } + return true; +} + +#define DO_ZPZZZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rprrr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_5 * const fns[4] = { \ + gen_helper_sve_##name##_b, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d, \ + }; \ + return do_zpzzz_ool(s, a, fns[a->esz]); \ +} + +DO_ZPZZZ(MLA, mla) +DO_ZPZZZ(MLS, mls) + +#undef DO_ZPZZZ + +/* + *** SVE Index Generation Group + */ + +static void do_index(DisasContext *s, int esz, int rd, + TCGv_i64 start, TCGv_i64 incr) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + TCGv_ptr t_zd = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, rd)); + if (esz == 3) { + gen_helper_sve_index_d(t_zd, start, incr, desc); + } else { + typedef void index_fn(TCGv_ptr, TCGv_i32, TCGv_i32, TCGv_i32); + static index_fn * const fns[3] = { + gen_helper_sve_index_b, + gen_helper_sve_index_h, + gen_helper_sve_index_s, + }; + TCGv_i32 s32 = tcg_temp_new_i32(); + TCGv_i32 i32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(s32, start); + tcg_gen_extrl_i64_i32(i32, incr); + fns[esz](t_zd, s32, i32, desc); + + tcg_temp_free_i32(s32); + tcg_temp_free_i32(i32); + } + tcg_temp_free_ptr(t_zd); + tcg_temp_free_i32(desc); +} + +static bool trans_INDEX_ii(DisasContext *s, arg_INDEX_ii *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 start = tcg_const_i64(a->imm1); + TCGv_i64 incr = tcg_const_i64(a->imm2); + do_index(s, a->esz, a->rd, start, incr); + tcg_temp_free_i64(start); + tcg_temp_free_i64(incr); + } + return true; +} + +static bool trans_INDEX_ir(DisasContext *s, arg_INDEX_ir *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 start = tcg_const_i64(a->imm); + TCGv_i64 incr = cpu_reg(s, a->rm); + do_index(s, a->esz, a->rd, start, incr); + tcg_temp_free_i64(start); + } + return true; +} + +static bool trans_INDEX_ri(DisasContext *s, arg_INDEX_ri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 start = cpu_reg(s, a->rn); + TCGv_i64 incr = tcg_const_i64(a->imm); + do_index(s, a->esz, a->rd, start, incr); + tcg_temp_free_i64(incr); + } + return true; +} + +static bool trans_INDEX_rr(DisasContext *s, arg_INDEX_rr *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 start = cpu_reg(s, a->rn); + TCGv_i64 incr = cpu_reg(s, a->rm); + do_index(s, a->esz, a->rd, start, incr); + } + return true; +} + +/* + *** SVE Stack Allocation Group + */ + +static bool trans_ADDVL(DisasContext *s, arg_ADDVL *a, uint32_t insn) +{ + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * vec_full_reg_size(s)); + return true; +} + +static bool trans_ADDPL(DisasContext *s, arg_ADDPL *a, uint32_t insn) +{ + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * pred_full_reg_size(s)); + return true; +} + +static bool trans_RDVL(DisasContext *s, arg_RDVL *a, uint32_t insn) +{ + TCGv_i64 reg = cpu_reg(s, a->rd); + tcg_gen_movi_i64(reg, a->imm * vec_full_reg_size(s)); + return true; +} + +/* + *** SVE Compute Vector Address Group + */ + +static bool do_adr(DisasContext *s, arg_rrri *a, gen_helper_gvec_3 *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, a->imm, fn); + } + return true; +} + +static bool trans_ADR_p32(DisasContext *s, arg_rrri *a, uint32_t insn) +{ + return do_adr(s, a, gen_helper_sve_adr_p32); +} + +static bool trans_ADR_p64(DisasContext *s, arg_rrri *a, uint32_t insn) +{ + return do_adr(s, a, gen_helper_sve_adr_p64); +} + +static bool trans_ADR_s32(DisasContext *s, arg_rrri *a, uint32_t insn) +{ + return do_adr(s, a, gen_helper_sve_adr_s32); +} + +static bool trans_ADR_u32(DisasContext *s, arg_rrri *a, uint32_t insn) +{ + return do_adr(s, a, gen_helper_sve_adr_u32); +} + +/* + *** SVE Integer Misc - Unpredicated Group + */ + +static bool trans_FEXPA(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_2 * const fns[4] = { + NULL, + gen_helper_sve_fexpa_h, + gen_helper_sve_fexpa_s, + gen_helper_sve_fexpa_d, + }; + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + +static bool trans_FTSSEL(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_ftssel_h, + gen_helper_sve_ftssel_s, + gen_helper_sve_ftssel_d, + }; + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + +/* + *** SVE Predicate Logical Operations Group + */ + +static bool do_pppp_flags(DisasContext *s, arg_rprr_s *a, + const GVecGen4 *gvec_op) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned psz = pred_gvec_reg_size(s); + int dofs = pred_full_reg_offset(s, a->rd); + int nofs = pred_full_reg_offset(s, a->rn); + int mofs = pred_full_reg_offset(s, a->rm); + int gofs = pred_full_reg_offset(s, a->pg); + + if (psz == 8) { + /* Do the operation and the flags generation in temps. */ + TCGv_i64 pd = tcg_temp_new_i64(); + TCGv_i64 pn = tcg_temp_new_i64(); + TCGv_i64 pm = tcg_temp_new_i64(); + TCGv_i64 pg = tcg_temp_new_i64(); + + tcg_gen_ld_i64(pn, cpu_env, nofs); + tcg_gen_ld_i64(pm, cpu_env, mofs); + tcg_gen_ld_i64(pg, cpu_env, gofs); + + gvec_op->fni8(pd, pn, pm, pg); + tcg_gen_st_i64(pd, cpu_env, dofs); + + do_predtest1(pd, pg); + + tcg_temp_free_i64(pd); + tcg_temp_free_i64(pn); + tcg_temp_free_i64(pm); + tcg_temp_free_i64(pg); + } else { + /* The operation and flags generation is large. The computation + * of the flags depends on the original contents of the guarding + * predicate. If the destination overwrites the guarding predicate, + * then the easiest way to get this right is to save a copy. + */ + int tofs = gofs; + if (a->rd == a->pg) { + tofs = offsetof(CPUARMState, vfp.preg_tmp); + tcg_gen_gvec_mov(0, tofs, gofs, psz, psz); + } + + tcg_gen_gvec_4(dofs, nofs, mofs, gofs, psz, psz, gvec_op); + do_predtest(s, dofs, tofs, psz / 8); + } + return true; +} + +static void gen_and_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_and_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_and_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_and_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_AND_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_and_pg_i64, + .fniv = gen_and_pg_vec, + .fno = gen_helper_sve_and_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else if (a->rn == a->rm) { + if (a->pg == a->rn) { + return do_mov_p(s, a->rd, a->rn); + } else { + return do_vector3_p(s, tcg_gen_gvec_and, 0, a->rd, a->rn, a->pg); + } + } else if (a->pg == a->rn || a->pg == a->rm) { + return do_vector3_p(s, tcg_gen_gvec_and, 0, a->rd, a->rn, a->rm); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_bic_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_andc_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_bic_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_andc_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_BIC_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_bic_pg_i64, + .fniv = gen_bic_pg_vec, + .fno = gen_helper_sve_bic_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else if (a->pg == a->rn) { + return do_vector3_p(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_eor_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_xor_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_eor_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_xor_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_EOR_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_eor_pg_i64, + .fniv = gen_eor_pg_vec, + .fno = gen_helper_sve_eor_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_sel_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_and_i64(pn, pn, pg); + tcg_gen_andc_i64(pm, pm, pg); + tcg_gen_or_i64(pd, pn, pm); +} + +static void gen_sel_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_and_vec(vece, pn, pn, pg); + tcg_gen_andc_vec(vece, pm, pm, pg); + tcg_gen_or_vec(vece, pd, pn, pm); +} + +static bool trans_SEL_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_sel_pg_i64, + .fniv = gen_sel_pg_vec, + .fno = gen_helper_sve_sel_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return false; + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_orr_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_or_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_orr_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_or_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_ORR_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_orr_pg_i64, + .fniv = gen_orr_pg_vec, + .fno = gen_helper_sve_orr_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else if (a->pg == a->rn && a->rn == a->rm) { + return do_mov_p(s, a->rd, a->rn); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_orn_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_orc_i64(pd, pn, pm); + tcg_gen_and_i64(pd, pd, pg); +} + +static void gen_orn_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_orc_vec(vece, pd, pn, pm); + tcg_gen_and_vec(vece, pd, pd, pg); +} + +static bool trans_ORN_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_orn_pg_i64, + .fniv = gen_orn_pg_vec, + .fno = gen_helper_sve_orn_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_nor_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_or_i64(pd, pn, pm); + tcg_gen_andc_i64(pd, pg, pd); +} + +static void gen_nor_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_or_vec(vece, pd, pn, pm); + tcg_gen_andc_vec(vece, pd, pg, pd); +} + +static bool trans_NOR_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_nor_pg_i64, + .fniv = gen_nor_pg_vec, + .fno = gen_helper_sve_nor_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +static void gen_nand_pg_i64(TCGv_i64 pd, TCGv_i64 pn, TCGv_i64 pm, TCGv_i64 pg) +{ + tcg_gen_and_i64(pd, pn, pm); + tcg_gen_andc_i64(pd, pg, pd); +} + +static void gen_nand_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, + TCGv_vec pm, TCGv_vec pg) +{ + tcg_gen_and_vec(vece, pd, pn, pm); + tcg_gen_andc_vec(vece, pd, pg, pd); +} + +static bool trans_NAND_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + static const GVecGen4 op = { + .fni8 = gen_nand_pg_i64, + .fniv = gen_nand_pg_vec, + .fno = gen_helper_sve_nand_pppp, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + if (a->s) { + return do_pppp_flags(s, a, &op); + } else { + return do_vecop4_p(s, &op, a->rd, a->rn, a->rm, a->pg); + } +} + +/* + *** SVE Predicate Misc Group + */ + +static bool trans_PTEST(DisasContext *s, arg_PTEST *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int nofs = pred_full_reg_offset(s, a->rn); + int gofs = pred_full_reg_offset(s, a->pg); + int words = DIV_ROUND_UP(pred_full_reg_size(s), 8); + + if (words == 1) { + TCGv_i64 pn = tcg_temp_new_i64(); + TCGv_i64 pg = tcg_temp_new_i64(); + + tcg_gen_ld_i64(pn, cpu_env, nofs); + tcg_gen_ld_i64(pg, cpu_env, gofs); + do_predtest1(pn, pg); + + tcg_temp_free_i64(pn); + tcg_temp_free_i64(pg); + } else { + do_predtest(s, nofs, gofs, words); + } + } + return true; +} + +/* See the ARM pseudocode DecodePredCount. */ +static unsigned decode_pred_count(unsigned fullsz, int pattern, int esz) +{ + unsigned elements = fullsz >> esz; + unsigned bound; + + switch (pattern) { + case 0x0: /* POW2 */ + return pow2floor(elements); + case 0x1: /* VL1 */ + case 0x2: /* VL2 */ + case 0x3: /* VL3 */ + case 0x4: /* VL4 */ + case 0x5: /* VL5 */ + case 0x6: /* VL6 */ + case 0x7: /* VL7 */ + case 0x8: /* VL8 */ + bound = pattern; + break; + case 0x9: /* VL16 */ + case 0xa: /* VL32 */ + case 0xb: /* VL64 */ + case 0xc: /* VL128 */ + case 0xd: /* VL256 */ + bound = 16 << (pattern - 9); + break; + case 0x1d: /* MUL4 */ + return elements - elements % 4; + case 0x1e: /* MUL3 */ + return elements - elements % 3; + case 0x1f: /* ALL */ + return elements; + default: /* #uimm5 */ + return 0; + } + return elements >= bound ? bound : 0; +} + +/* This handles all of the predicate initialization instructions, + * PTRUE, PFALSE, SETFFR. For PFALSE, we will have set PAT == 32 + * so that decode_pred_count returns 0. For SETFFR, we will have + * set RD == 16 == FFR. + */ +static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned ofs = pred_full_reg_offset(s, rd); + unsigned numelem, setsz, i; + uint64_t word, lastword; + TCGv_i64 t; + + numelem = decode_pred_count(fullsz, pat, esz); + + /* Determine what we must store into each bit, and how many. */ + if (numelem == 0) { + lastword = word = 0; + setsz = fullsz; + } else { + setsz = numelem << esz; + lastword = word = pred_esz_masks[esz]; + if (setsz % 64) { + lastword &= MAKE_64BIT_MASK(0, setsz % 64); + } + } + + t = tcg_temp_new_i64(); + if (fullsz <= 64) { + tcg_gen_movi_i64(t, lastword); + tcg_gen_st_i64(t, cpu_env, ofs); + goto done; + } + + if (word == lastword) { + unsigned maxsz = size_for_gvec(fullsz / 8); + unsigned oprsz = size_for_gvec(setsz / 8); + + if (oprsz * 8 == setsz) { + tcg_gen_gvec_dup64i(ofs, oprsz, maxsz, word); + goto done; + } + } + + setsz /= 8; + fullsz /= 8; + + tcg_gen_movi_i64(t, word); + for (i = 0; i < QEMU_ALIGN_DOWN(setsz, 8); i += 8) { + tcg_gen_st_i64(t, cpu_env, ofs + i); + } + if (lastword != word) { + tcg_gen_movi_i64(t, lastword); + tcg_gen_st_i64(t, cpu_env, ofs + i); + i += 8; + } + if (i < fullsz) { + tcg_gen_movi_i64(t, 0); + for (; i < fullsz; i += 8) { + tcg_gen_st_i64(t, cpu_env, ofs + i); + } + } + + done: + tcg_temp_free_i64(t); + + /* PTRUES */ + if (setflag) { + tcg_gen_movi_i32(cpu_NF, -(word != 0)); + tcg_gen_movi_i32(cpu_CF, word == 0); + tcg_gen_movi_i32(cpu_VF, 0); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + } + return true; +} + +static bool trans_PTRUE(DisasContext *s, arg_PTRUE *a, uint32_t insn) +{ + return do_predset(s, a->esz, a->rd, a->pat, a->s); +} + +static bool trans_SETFFR(DisasContext *s, arg_SETFFR *a, uint32_t insn) +{ + /* Note pat == 31 is #all, to set all elements. */ + return do_predset(s, 0, FFR_PRED_NUM, 31, false); +} + +static bool trans_PFALSE(DisasContext *s, arg_PFALSE *a, uint32_t insn) +{ + /* Note pat == 32 is #unimp, to set no elements. */ + return do_predset(s, 0, a->rd, 32, false); +} + +static bool trans_RDFFR_p(DisasContext *s, arg_RDFFR_p *a, uint32_t insn) +{ + /* The path through do_pppp_flags is complicated enough to want to avoid + * duplication. Frob the arguments into the form of a predicated AND. + */ + arg_rprr_s alt_a = { + .rd = a->rd, .pg = a->pg, .s = a->s, + .rn = FFR_PRED_NUM, .rm = FFR_PRED_NUM, + }; + return trans_AND_pppp(s, &alt_a, insn); +} + +static bool trans_RDFFR(DisasContext *s, arg_RDFFR *a, uint32_t insn) +{ + return do_mov_p(s, a->rd, FFR_PRED_NUM); +} + +static bool trans_WRFFR(DisasContext *s, arg_WRFFR *a, uint32_t insn) +{ + return do_mov_p(s, FFR_PRED_NUM, a->rn); +} + +static bool do_pfirst_pnext(DisasContext *s, arg_rr_esz *a, + void (*gen_fn)(TCGv_i32, TCGv_ptr, + TCGv_ptr, TCGv_i32)) +{ + if (!sve_access_check(s)) { + return true; + } + + TCGv_ptr t_pd = tcg_temp_new_ptr(); + TCGv_ptr t_pg = tcg_temp_new_ptr(); + TCGv_i32 t; + unsigned desc; + + desc = DIV_ROUND_UP(pred_full_reg_size(s), 8); + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); + + tcg_gen_addi_ptr(t_pd, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->rn)); + t = tcg_const_i32(desc); + + gen_fn(t, t_pd, t_pg, t); + tcg_temp_free_ptr(t_pd); + tcg_temp_free_ptr(t_pg); + + do_pred_flags(t); + tcg_temp_free_i32(t); + return true; +} + +static bool trans_PFIRST(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + return do_pfirst_pnext(s, a, gen_helper_sve_pfirst); +} + +static bool trans_PNEXT(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + return do_pfirst_pnext(s, a, gen_helper_sve_pnext); +} + +/* + *** SVE Element Count Group + */ + +/* Perform an inline saturating addition of a 32-bit value within + * a 64-bit register. The second operand is known to be positive, + * which halves the comparisions we must perform to bound the result. + */ +static void do_sat_addsub_32(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) +{ + int64_t ibound; + TCGv_i64 bound; + TCGCond cond; + + /* Use normal 64-bit arithmetic to detect 32-bit overflow. */ + if (u) { + tcg_gen_ext32u_i64(reg, reg); + } else { + tcg_gen_ext32s_i64(reg, reg); + } + if (d) { + tcg_gen_sub_i64(reg, reg, val); + ibound = (u ? 0 : INT32_MIN); + cond = TCG_COND_LT; + } else { + tcg_gen_add_i64(reg, reg, val); + ibound = (u ? UINT32_MAX : INT32_MAX); + cond = TCG_COND_GT; + } + bound = tcg_const_i64(ibound); + tcg_gen_movcond_i64(cond, reg, reg, bound, bound, reg); + tcg_temp_free_i64(bound); +} + +/* Similarly with 64-bit values. */ +static void do_sat_addsub_64(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2; + + if (u) { + if (d) { + tcg_gen_sub_i64(t0, reg, val); + tcg_gen_movi_i64(t1, 0); + tcg_gen_movcond_i64(TCG_COND_LTU, reg, reg, val, t1, t0); + } else { + tcg_gen_add_i64(t0, reg, val); + tcg_gen_movi_i64(t1, -1); + tcg_gen_movcond_i64(TCG_COND_LTU, reg, t0, reg, t1, t0); + } + } else { + if (d) { + /* Detect signed overflow for subtraction. */ + tcg_gen_xor_i64(t0, reg, val); + tcg_gen_sub_i64(t1, reg, val); + tcg_gen_xor_i64(reg, reg, t0); + tcg_gen_and_i64(t0, t0, reg); + + /* Bound the result. */ + tcg_gen_movi_i64(reg, INT64_MIN); + t2 = tcg_const_i64(0); + tcg_gen_movcond_i64(TCG_COND_LT, reg, t0, t2, reg, t1); + } else { + /* Detect signed overflow for addition. */ + tcg_gen_xor_i64(t0, reg, val); + tcg_gen_add_i64(reg, reg, val); + tcg_gen_xor_i64(t1, reg, val); + tcg_gen_andc_i64(t0, t1, t0); + + /* Bound the result. */ + tcg_gen_movi_i64(t1, INT64_MAX); + t2 = tcg_const_i64(0); + tcg_gen_movcond_i64(TCG_COND_LT, reg, t0, t2, t1, reg); + } + tcg_temp_free_i64(t2); + } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); +} + +/* Similarly with a vector and a scalar operand. */ +static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, + TCGv_i64 val, bool u, bool d) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr dptr, nptr; + TCGv_i32 t32, desc; + TCGv_i64 t64; + + dptr = tcg_temp_new_ptr(); + nptr = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(dptr, cpu_env, vec_full_reg_offset(s, rd)); + tcg_gen_addi_ptr(nptr, cpu_env, vec_full_reg_offset(s, rn)); + desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + + switch (esz) { + case MO_8: + t32 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t32, val); + if (d) { + tcg_gen_neg_i32(t32, t32); + } + if (u) { + gen_helper_sve_uqaddi_b(dptr, nptr, t32, desc); + } else { + gen_helper_sve_sqaddi_b(dptr, nptr, t32, desc); + } + tcg_temp_free_i32(t32); + break; + + case MO_16: + t32 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t32, val); + if (d) { + tcg_gen_neg_i32(t32, t32); + } + if (u) { + gen_helper_sve_uqaddi_h(dptr, nptr, t32, desc); + } else { + gen_helper_sve_sqaddi_h(dptr, nptr, t32, desc); + } + tcg_temp_free_i32(t32); + break; + + case MO_32: + t64 = tcg_temp_new_i64(); + if (d) { + tcg_gen_neg_i64(t64, val); + } else { + tcg_gen_mov_i64(t64, val); + } + if (u) { + gen_helper_sve_uqaddi_s(dptr, nptr, t64, desc); + } else { + gen_helper_sve_sqaddi_s(dptr, nptr, t64, desc); + } + tcg_temp_free_i64(t64); + break; + + case MO_64: + if (u) { + if (d) { + gen_helper_sve_uqsubi_d(dptr, nptr, val, desc); + } else { + gen_helper_sve_uqaddi_d(dptr, nptr, val, desc); + } + } else if (d) { + t64 = tcg_temp_new_i64(); + tcg_gen_neg_i64(t64, val); + gen_helper_sve_sqaddi_d(dptr, nptr, t64, desc); + tcg_temp_free_i64(t64); + } else { + gen_helper_sve_sqaddi_d(dptr, nptr, val, desc); + } + break; + + default: + g_assert_not_reached(); + } + + tcg_temp_free_ptr(dptr); + tcg_temp_free_ptr(nptr); + tcg_temp_free_i32(desc); +} + +static bool trans_CNT_r(DisasContext *s, arg_CNT_r *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + tcg_gen_movi_i64(cpu_reg(s, a->rd), numelem * a->imm); + } + return true; +} + +static bool trans_INCDEC_r(DisasContext *s, arg_incdec_cnt *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm * (a->d ? -1 : 1); + TCGv_i64 reg = cpu_reg(s, a->rd); + + tcg_gen_addi_i64(reg, reg, inc); + } + return true; +} + +static bool trans_SINCDEC_r_32(DisasContext *s, arg_incdec_cnt *a, + uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm; + TCGv_i64 reg = cpu_reg(s, a->rd); + + /* Use normal 64-bit arithmetic to detect 32-bit overflow. */ + if (inc == 0) { + if (a->u) { + tcg_gen_ext32u_i64(reg, reg); + } else { + tcg_gen_ext32s_i64(reg, reg); + } + } else { + TCGv_i64 t = tcg_const_i64(inc); + do_sat_addsub_32(reg, t, a->u, a->d); + tcg_temp_free_i64(t); + } + return true; +} + +static bool trans_SINCDEC_r_64(DisasContext *s, arg_incdec_cnt *a, + uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm; + TCGv_i64 reg = cpu_reg(s, a->rd); + + if (inc != 0) { + TCGv_i64 t = tcg_const_i64(inc); + do_sat_addsub_64(reg, t, a->u, a->d); + tcg_temp_free_i64(t); + } + return true; +} + +static bool trans_INCDEC_v(DisasContext *s, arg_incdec2_cnt *a, uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm; + + if (inc != 0) { + if (sve_access_check(s)) { + TCGv_i64 t = tcg_const_i64(a->d ? -inc : inc); + tcg_gen_gvec_adds(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + t, fullsz, fullsz); + tcg_temp_free_i64(t); + } + } else { + do_mov_z(s, a->rd, a->rn); + } + return true; +} + +static bool trans_SINCDEC_v(DisasContext *s, arg_incdec2_cnt *a, + uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + + unsigned fullsz = vec_full_reg_size(s); + unsigned numelem = decode_pred_count(fullsz, a->pat, a->esz); + int inc = numelem * a->imm; + + if (inc != 0) { + if (sve_access_check(s)) { + TCGv_i64 t = tcg_const_i64(inc); + do_sat_addsub_vec(s, a->esz, a->rd, a->rn, t, a->u, a->d); + tcg_temp_free_i64(t); + } + } else { + do_mov_z(s, a->rd, a->rn); + } + return true; +} + +/* + *** SVE Bitwise Immediate Group + */ + +static bool do_zz_dbm(DisasContext *s, arg_rr_dbm *a, GVecGen2iFn *gvec_fn) +{ + uint64_t imm; + if (!logic_imm_decode_wmask(&imm, extract32(a->dbm, 12, 1), + extract32(a->dbm, 0, 6), + extract32(a->dbm, 6, 6))) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + gvec_fn(MO_64, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), imm, vsz, vsz); + } + return true; +} + +static bool trans_AND_zzi(DisasContext *s, arg_rr_dbm *a, uint32_t insn) +{ + return do_zz_dbm(s, a, tcg_gen_gvec_andi); +} + +static bool trans_ORR_zzi(DisasContext *s, arg_rr_dbm *a, uint32_t insn) +{ + return do_zz_dbm(s, a, tcg_gen_gvec_ori); +} + +static bool trans_EOR_zzi(DisasContext *s, arg_rr_dbm *a, uint32_t insn) +{ + return do_zz_dbm(s, a, tcg_gen_gvec_xori); +} + +static bool trans_DUPM(DisasContext *s, arg_DUPM *a, uint32_t insn) +{ + uint64_t imm; + if (!logic_imm_decode_wmask(&imm, extract32(a->dbm, 12, 1), + extract32(a->dbm, 0, 6), + extract32(a->dbm, 6, 6))) { + return false; + } + if (sve_access_check(s)) { + do_dupi_z(s, a->rd, imm); + } + return true; +} + +/* + *** SVE Integer Wide Immediate - Predicated Group + */ + +/* Implement all merging copies. This is used for CPY (immediate), + * FCPY, CPY (scalar), CPY (SIMD&FP scalar). + */ +static void do_cpy_m(DisasContext *s, int esz, int rd, int rn, int pg, + TCGv_i64 val) +{ + typedef void gen_cpy(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_i32); + static gen_cpy * const fns[4] = { + gen_helper_sve_cpy_m_b, gen_helper_sve_cpy_m_h, + gen_helper_sve_cpy_m_s, gen_helper_sve_cpy_m_d, + }; + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + TCGv_ptr t_zd = tcg_temp_new_ptr(); + TCGv_ptr t_zn = tcg_temp_new_ptr(); + TCGv_ptr t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, rd)); + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, rn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + + fns[esz](t_zd, t_zn, t_pg, val, desc); + + tcg_temp_free_ptr(t_zd); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); +} + +static bool trans_FCPY(DisasContext *s, arg_FCPY *a, uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + /* Decode the VFP immediate. */ + uint64_t imm = vfp_expand_imm(a->esz, a->imm); + TCGv_i64 t_imm = tcg_const_i64(imm); + do_cpy_m(s, a->esz, a->rd, a->rn, a->pg, t_imm); + tcg_temp_free_i64(t_imm); + } + return true; +} + +static bool trans_CPY_m_i(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +{ + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 t_imm = tcg_const_i64(a->imm); + do_cpy_m(s, a->esz, a->rd, a->rn, a->pg, t_imm); + tcg_temp_free_i64(t_imm); + } + return true; +} + +static bool trans_CPY_z_i(DisasContext *s, arg_CPY_z_i *a, uint32_t insn) +{ + static gen_helper_gvec_2i * const fns[4] = { + gen_helper_sve_cpy_z_b, gen_helper_sve_cpy_z_h, + gen_helper_sve_cpy_z_s, gen_helper_sve_cpy_z_d, + }; + + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i64 t_imm = tcg_const_i64(a->imm); + tcg_gen_gvec_2i_ool(vec_full_reg_offset(s, a->rd), + pred_full_reg_offset(s, a->pg), + t_imm, vsz, vsz, 0, fns[a->esz]); + tcg_temp_free_i64(t_imm); + } + return true; +} + +/* + *** SVE Permute Extract Group + */ + +static bool trans_EXT(DisasContext *s, arg_EXT *a, uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = vec_full_reg_size(s); + unsigned n_ofs = a->imm >= vsz ? 0 : a->imm; + unsigned n_siz = vsz - n_ofs; + unsigned d = vec_full_reg_offset(s, a->rd); + unsigned n = vec_full_reg_offset(s, a->rn); + unsigned m = vec_full_reg_offset(s, a->rm); + + /* Use host vector move insns if we have appropriate sizes + * and no unfortunate overlap. + */ + if (m != d + && n_ofs == size_for_gvec(n_ofs) + && n_siz == size_for_gvec(n_siz) + && (d != n || n_siz <= n_ofs)) { + tcg_gen_gvec_mov(0, d, n + n_ofs, n_siz, n_siz); + if (n_ofs != 0) { + tcg_gen_gvec_mov(0, d + n_siz, m, n_ofs, n_ofs); + } + } else { + tcg_gen_gvec_3_ool(d, n, m, vsz, vsz, n_ofs, gen_helper_sve_ext); + } + return true; +} + +/* + *** SVE Permute - Unpredicated Group + */ + +static bool trans_DUP_s(DisasContext *s, arg_DUP_s *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_dup_i64(a->esz, vec_full_reg_offset(s, a->rd), + vsz, vsz, cpu_reg_sp(s, a->rn)); + } + return true; +} + +static bool trans_DUP_x(DisasContext *s, arg_DUP_x *a, uint32_t insn) +{ + if ((a->imm & 0x1f) == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + unsigned dofs = vec_full_reg_offset(s, a->rd); + unsigned esz, index; + + esz = ctz32(a->imm); + index = a->imm >> (esz + 1); + + if ((index << esz) < vsz) { + unsigned nofs = vec_reg_offset(s, a->rn, index, esz); + tcg_gen_gvec_dup_mem(esz, dofs, nofs, vsz, vsz); + } else { + tcg_gen_gvec_dup64i(dofs, vsz, vsz, 0); + } + } + return true; +} + +static void do_insr_i64(DisasContext *s, arg_rrr_esz *a, TCGv_i64 val) +{ + typedef void gen_insr(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_i32); + static gen_insr * const fns[4] = { + gen_helper_sve_insr_b, gen_helper_sve_insr_h, + gen_helper_sve_insr_s, gen_helper_sve_insr_d, + }; + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + TCGv_ptr t_zd = tcg_temp_new_ptr(); + TCGv_ptr t_zn = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, a->rn)); + + fns[a->esz](t_zd, t_zn, val, desc); + + tcg_temp_free_ptr(t_zd); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_i32(desc); +} + +static bool trans_INSR_f(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 t = tcg_temp_new_i64(); + tcg_gen_ld_i64(t, cpu_env, vec_reg_offset(s, a->rm, 0, MO_64)); + do_insr_i64(s, a, t); + tcg_temp_free_i64(t); + } + return true; +} + +static bool trans_INSR_r(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_insr_i64(s, a, cpu_reg(s, a->rm)); + } + return true; +} + +static bool trans_REV_v(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_2 * const fns[4] = { + gen_helper_sve_rev_b, gen_helper_sve_rev_h, + gen_helper_sve_rev_s, gen_helper_sve_rev_d + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + +static bool trans_TBL(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_tbl_b, gen_helper_sve_tbl_h, + gen_helper_sve_tbl_s, gen_helper_sve_tbl_d + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + +static bool trans_UNPK(DisasContext *s, arg_UNPK *a, uint32_t insn) +{ + static gen_helper_gvec_2 * const fns[4][2] = { + { NULL, NULL }, + { gen_helper_sve_sunpk_h, gen_helper_sve_uunpk_h }, + { gen_helper_sve_sunpk_s, gen_helper_sve_uunpk_s }, + { gen_helper_sve_sunpk_d, gen_helper_sve_uunpk_d }, + }; + + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn) + + (a->h ? vsz / 2 : 0), + vsz, vsz, 0, fns[a->esz][a->u]); + } + return true; +} + +/* + *** SVE Permute - Predicates Group + */ + +static bool do_perm_pred3(DisasContext *s, arg_rrr_esz *a, bool high_odd, + gen_helper_gvec_3 *fn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = pred_full_reg_size(s); + + /* Predicate sizes may be smaller and cannot use simd_desc. + We cannot round up, as we do elsewhere, because we need + the exact size for ZIP2 and REV. We retain the style for + the other helpers for consistency. */ + TCGv_ptr t_d = tcg_temp_new_ptr(); + TCGv_ptr t_n = tcg_temp_new_ptr(); + TCGv_ptr t_m = tcg_temp_new_ptr(); + TCGv_i32 t_desc; + int desc; + + desc = vsz - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); + desc = deposit32(desc, SIMD_DATA_SHIFT + 2, 2, high_odd); + + tcg_gen_addi_ptr(t_d, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_n, cpu_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_m, cpu_env, pred_full_reg_offset(s, a->rm)); + t_desc = tcg_const_i32(desc); + + fn(t_d, t_n, t_m, t_desc); + + tcg_temp_free_ptr(t_d); + tcg_temp_free_ptr(t_n); + tcg_temp_free_ptr(t_m); + tcg_temp_free_i32(t_desc); + return true; +} + +static bool do_perm_pred2(DisasContext *s, arg_rr_esz *a, bool high_odd, + gen_helper_gvec_2 *fn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = pred_full_reg_size(s); + TCGv_ptr t_d = tcg_temp_new_ptr(); + TCGv_ptr t_n = tcg_temp_new_ptr(); + TCGv_i32 t_desc; + int desc; + + tcg_gen_addi_ptr(t_d, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(t_n, cpu_env, pred_full_reg_offset(s, a->rn)); + + /* Predicate sizes may be smaller and cannot use simd_desc. + We cannot round up, as we do elsewhere, because we need + the exact size for ZIP2 and REV. We retain the style for + the other helpers for consistency. */ + + desc = vsz - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); + desc = deposit32(desc, SIMD_DATA_SHIFT + 2, 2, high_odd); + t_desc = tcg_const_i32(desc); + + fn(t_d, t_n, t_desc); + + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(t_d); + tcg_temp_free_ptr(t_n); + return true; +} + +static bool trans_ZIP1_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 0, gen_helper_sve_zip_p); +} + +static bool trans_ZIP2_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 1, gen_helper_sve_zip_p); +} + +static bool trans_UZP1_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 0, gen_helper_sve_uzp_p); +} + +static bool trans_UZP2_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 1, gen_helper_sve_uzp_p); +} + +static bool trans_TRN1_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 0, gen_helper_sve_trn_p); +} + +static bool trans_TRN2_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_perm_pred3(s, a, 1, gen_helper_sve_trn_p); +} + +static bool trans_REV_p(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + return do_perm_pred2(s, a, 0, gen_helper_sve_rev_p); +} + +static bool trans_PUNPKLO(DisasContext *s, arg_PUNPKLO *a, uint32_t insn) +{ + return do_perm_pred2(s, a, 0, gen_helper_sve_punpk_p); +} + +static bool trans_PUNPKHI(DisasContext *s, arg_PUNPKHI *a, uint32_t insn) +{ + return do_perm_pred2(s, a, 1, gen_helper_sve_punpk_p); +} + +/* + *** SVE Permute - Interleaving Group + */ + +static bool do_zip(DisasContext *s, arg_rrr_esz *a, bool high) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_zip_b, gen_helper_sve_zip_h, + gen_helper_sve_zip_s, gen_helper_sve_zip_d, + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + unsigned high_ofs = high ? vsz / 2 : 0; + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn) + high_ofs, + vec_full_reg_offset(s, a->rm) + high_ofs, + vsz, vsz, 0, fns[a->esz]); + } + return true; +} + +static bool do_zzz_data_ool(DisasContext *s, arg_rrr_esz *a, int data, + gen_helper_gvec_3 *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, data, fn); + } + return true; +} + +static bool trans_ZIP1_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zip(s, a, false); +} + +static bool trans_ZIP2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zip(s, a, true); +} + +static gen_helper_gvec_3 * const uzp_fns[4] = { + gen_helper_sve_uzp_b, gen_helper_sve_uzp_h, + gen_helper_sve_uzp_s, gen_helper_sve_uzp_d, +}; + +static bool trans_UZP1_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zzz_data_ool(s, a, 0, uzp_fns[a->esz]); +} + +static bool trans_UZP2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zzz_data_ool(s, a, 1 << a->esz, uzp_fns[a->esz]); +} + +static gen_helper_gvec_3 * const trn_fns[4] = { + gen_helper_sve_trn_b, gen_helper_sve_trn_h, + gen_helper_sve_trn_s, gen_helper_sve_trn_d, +}; + +static bool trans_TRN1_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zzz_data_ool(s, a, 0, trn_fns[a->esz]); +} + +static bool trans_TRN2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +{ + return do_zzz_data_ool(s, a, 1 << a->esz, trn_fns[a->esz]); +} + +/* + *** SVE Permute Vector - Predicated Group + */ + +static bool trans_COMPACT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, NULL, gen_helper_sve_compact_s, gen_helper_sve_compact_d + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +/* Call the helper that computes the ARM LastActiveElement pseudocode + * function, scaled by the element size. This includes the not found + * indication; e.g. not found for esz=3 is -8. + */ +static void find_last_active(DisasContext *s, TCGv_i32 ret, int esz, int pg) +{ + /* Predicate sizes may be smaller and cannot use simd_desc. We cannot + * round up, as we do elsewhere, because we need the exact size. + */ + TCGv_ptr t_p = tcg_temp_new_ptr(); + TCGv_i32 t_desc; + unsigned vsz = pred_full_reg_size(s); + unsigned desc; + + desc = vsz - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, esz); + + tcg_gen_addi_ptr(t_p, cpu_env, pred_full_reg_offset(s, pg)); + t_desc = tcg_const_i32(desc); + + gen_helper_sve_last_active_element(ret, t_p, t_desc); + + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(t_p); +} + +/* Increment LAST to the offset of the next element in the vector, + * wrapping around to 0. + */ +static void incr_last_active(DisasContext *s, TCGv_i32 last, int esz) +{ + unsigned vsz = vec_full_reg_size(s); + + tcg_gen_addi_i32(last, last, 1 << esz); + if (is_power_of_2(vsz)) { + tcg_gen_andi_i32(last, last, vsz - 1); + } else { + TCGv_i32 max = tcg_const_i32(vsz); + TCGv_i32 zero = tcg_const_i32(0); + tcg_gen_movcond_i32(TCG_COND_GEU, last, last, max, zero, last); + tcg_temp_free_i32(max); + tcg_temp_free_i32(zero); + } +} + +/* If LAST < 0, set LAST to the offset of the last element in the vector. */ +static void wrap_last_active(DisasContext *s, TCGv_i32 last, int esz) +{ + unsigned vsz = vec_full_reg_size(s); + + if (is_power_of_2(vsz)) { + tcg_gen_andi_i32(last, last, vsz - 1); + } else { + TCGv_i32 max = tcg_const_i32(vsz - (1 << esz)); + TCGv_i32 zero = tcg_const_i32(0); + tcg_gen_movcond_i32(TCG_COND_LT, last, last, zero, max, last); + tcg_temp_free_i32(max); + tcg_temp_free_i32(zero); + } +} + +/* Load an unsigned element of ESZ from BASE+OFS. */ +static TCGv_i64 load_esz(TCGv_ptr base, int ofs, int esz) +{ + TCGv_i64 r = tcg_temp_new_i64(); + + switch (esz) { + case 0: + tcg_gen_ld8u_i64(r, base, ofs); + break; + case 1: + tcg_gen_ld16u_i64(r, base, ofs); + break; + case 2: + tcg_gen_ld32u_i64(r, base, ofs); + break; + case 3: + tcg_gen_ld_i64(r, base, ofs); + break; + default: + g_assert_not_reached(); + } + return r; +} + +/* Load an unsigned element of ESZ from RM[LAST]. */ +static TCGv_i64 load_last_active(DisasContext *s, TCGv_i32 last, + int rm, int esz) +{ + TCGv_ptr p = tcg_temp_new_ptr(); + TCGv_i64 r; + + /* Convert offset into vector into offset into ENV. + * The final adjustment for the vector register base + * is added via constant offset to the load. + */ +#ifdef HOST_WORDS_BIGENDIAN + /* Adjust for element ordering. See vec_reg_offset. */ + if (esz < 3) { + tcg_gen_xori_i32(last, last, 8 - (1 << esz)); + } +#endif + tcg_gen_ext_i32_ptr(p, last); + tcg_gen_add_ptr(p, p, cpu_env); + + r = load_esz(p, vec_full_reg_offset(s, rm), esz); + tcg_temp_free_ptr(p); + + return r; +} + +/* Compute CLAST for a Zreg. */ +static bool do_clast_vector(DisasContext *s, arg_rprr_esz *a, bool before) +{ + TCGv_i32 last; + TCGLabel *over; + TCGv_i64 ele; + unsigned vsz, esz = a->esz; + + if (!sve_access_check(s)) { + return true; + } + + last = tcg_temp_local_new_i32(); + over = gen_new_label(); + + find_last_active(s, last, esz, a->pg); + + /* There is of course no movcond for a 2048-bit vector, + * so we must branch over the actual store. + */ + tcg_gen_brcondi_i32(TCG_COND_LT, last, 0, over); + + if (!before) { + incr_last_active(s, last, esz); + } + + ele = load_last_active(s, last, a->rm, esz); + tcg_temp_free_i32(last); + + vsz = vec_full_reg_size(s); + tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd), vsz, vsz, ele); + tcg_temp_free_i64(ele); + + /* If this insn used MOVPRFX, we may need a second move. */ + if (a->rd != a->rn) { + TCGLabel *done = gen_new_label(); + tcg_gen_br(done); + + gen_set_label(over); + do_mov_z(s, a->rd, a->rn); + + gen_set_label(done); + } else { + gen_set_label(over); + } + return true; +} + +static bool trans_CLASTA_z(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + return do_clast_vector(s, a, false); +} + +static bool trans_CLASTB_z(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + return do_clast_vector(s, a, true); +} + +/* Compute CLAST for a scalar. */ +static void do_clast_scalar(DisasContext *s, int esz, int pg, int rm, + bool before, TCGv_i64 reg_val) +{ + TCGv_i32 last = tcg_temp_new_i32(); + TCGv_i64 ele, cmp, zero; + + find_last_active(s, last, esz, pg); + + /* Extend the original value of last prior to incrementing. */ + cmp = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(cmp, last); + + if (!before) { + incr_last_active(s, last, esz); + } + + /* The conceit here is that while last < 0 indicates not found, after + * adjusting for cpu_env->vfp.zregs[rm], it is still a valid address + * from which we can load garbage. We then discard the garbage with + * a conditional move. + */ + ele = load_last_active(s, last, rm, esz); + tcg_temp_free_i32(last); + + zero = tcg_const_i64(0); + tcg_gen_movcond_i64(TCG_COND_GE, reg_val, cmp, zero, ele, reg_val); + + tcg_temp_free_i64(zero); + tcg_temp_free_i64(cmp); + tcg_temp_free_i64(ele); +} + +/* Compute CLAST for a Vreg. */ +static bool do_clast_fp(DisasContext *s, arg_rpr_esz *a, bool before) +{ + if (sve_access_check(s)) { + int esz = a->esz; + int ofs = vec_reg_offset(s, a->rd, 0, esz); + TCGv_i64 reg = load_esz(cpu_env, ofs, esz); + + do_clast_scalar(s, esz, a->pg, a->rn, before, reg); + write_fp_dreg(s, a->rd, reg); + tcg_temp_free_i64(reg); + } + return true; +} + +static bool trans_CLASTA_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_clast_fp(s, a, false); +} + +static bool trans_CLASTB_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_clast_fp(s, a, true); +} + +/* Compute CLAST for a Xreg. */ +static bool do_clast_general(DisasContext *s, arg_rpr_esz *a, bool before) +{ + TCGv_i64 reg; + + if (!sve_access_check(s)) { + return true; + } + + reg = cpu_reg(s, a->rd); + switch (a->esz) { + case 0: + tcg_gen_ext8u_i64(reg, reg); + break; + case 1: + tcg_gen_ext16u_i64(reg, reg); + break; + case 2: + tcg_gen_ext32u_i64(reg, reg); + break; + case 3: + break; + default: + g_assert_not_reached(); + } + + do_clast_scalar(s, a->esz, a->pg, a->rn, before, reg); + return true; +} + +static bool trans_CLASTA_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_clast_general(s, a, false); +} + +static bool trans_CLASTB_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_clast_general(s, a, true); +} + +/* Compute LAST for a scalar. */ +static TCGv_i64 do_last_scalar(DisasContext *s, int esz, + int pg, int rm, bool before) +{ + TCGv_i32 last = tcg_temp_new_i32(); + TCGv_i64 ret; + + find_last_active(s, last, esz, pg); + if (before) { + wrap_last_active(s, last, esz); + } else { + incr_last_active(s, last, esz); + } + + ret = load_last_active(s, last, rm, esz); + tcg_temp_free_i32(last); + return ret; +} + +/* Compute LAST for a Vreg. */ +static bool do_last_fp(DisasContext *s, arg_rpr_esz *a, bool before) +{ + if (sve_access_check(s)) { + TCGv_i64 val = do_last_scalar(s, a->esz, a->pg, a->rn, before); + write_fp_dreg(s, a->rd, val); + tcg_temp_free_i64(val); + } + return true; +} + +static bool trans_LASTA_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_last_fp(s, a, false); +} + +static bool trans_LASTB_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_last_fp(s, a, true); +} + +/* Compute LAST for a Xreg. */ +static bool do_last_general(DisasContext *s, arg_rpr_esz *a, bool before) +{ + if (sve_access_check(s)) { + TCGv_i64 val = do_last_scalar(s, a->esz, a->pg, a->rn, before); + tcg_gen_mov_i64(cpu_reg(s, a->rd), val); + tcg_temp_free_i64(val); + } + return true; +} + +static bool trans_LASTA_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_last_general(s, a, false); +} + +static bool trans_LASTB_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_last_general(s, a, true); +} + +static bool trans_CPY_m_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_cpy_m(s, a->esz, a->rd, a->rd, a->pg, cpu_reg_sp(s, a->rn)); + } + return true; +} + +static bool trans_CPY_m_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int ofs = vec_reg_offset(s, a->rn, 0, a->esz); + TCGv_i64 t = load_esz(cpu_env, ofs, a->esz); + do_cpy_m(s, a->esz, a->rd, a->rd, a->pg, t); + tcg_temp_free_i64(t); + } + return true; +} + +static bool trans_REVB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + gen_helper_sve_revb_h, + gen_helper_sve_revb_s, + gen_helper_sve_revb_d, + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_REVH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + NULL, + NULL, + gen_helper_sve_revh_s, + gen_helper_sve_revh_d, + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_REVW(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ool(s, a, a->esz == 3 ? gen_helper_sve_revw_d : NULL); +} + +static bool trans_RBIT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve_rbit_b, + gen_helper_sve_rbit_h, + gen_helper_sve_rbit_s, + gen_helper_sve_rbit_d, + }; + return do_zpz_ool(s, a, fns[a->esz]); +} + +static bool trans_SPLICE(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_4_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + vsz, vsz, a->esz, gen_helper_sve_splice); + } + return true; +} + +/* + *** SVE Integer Compare - Vectors Group + */ + +static bool do_ppzz_flags(DisasContext *s, arg_rprr_esz *a, + gen_helper_gvec_flags_4 *gen_fn) +{ + TCGv_ptr pd, zn, zm, pg; + unsigned vsz; + TCGv_i32 t; + + if (gen_fn == NULL) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + vsz = vec_full_reg_size(s); + t = tcg_const_i32(simd_desc(vsz, vsz, 0)); + pd = tcg_temp_new_ptr(); + zn = tcg_temp_new_ptr(); + zm = tcg_temp_new_ptr(); + pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(pd, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(zn, cpu_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(zm, cpu_env, vec_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + + gen_fn(t, pd, zn, zm, pg, t); + + tcg_temp_free_ptr(pd); + tcg_temp_free_ptr(zn); + tcg_temp_free_ptr(zm); + tcg_temp_free_ptr(pg); + + do_pred_flags(t); + + tcg_temp_free_i32(t); + return true; +} + +#define DO_PPZZ(NAME, name) \ +static bool trans_##NAME##_ppzz(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_flags_4 * const fns[4] = { \ + gen_helper_sve_##name##_ppzz_b, gen_helper_sve_##name##_ppzz_h, \ + gen_helper_sve_##name##_ppzz_s, gen_helper_sve_##name##_ppzz_d, \ + }; \ + return do_ppzz_flags(s, a, fns[a->esz]); \ +} + +DO_PPZZ(CMPEQ, cmpeq) +DO_PPZZ(CMPNE, cmpne) +DO_PPZZ(CMPGT, cmpgt) +DO_PPZZ(CMPGE, cmpge) +DO_PPZZ(CMPHI, cmphi) +DO_PPZZ(CMPHS, cmphs) + +#undef DO_PPZZ + +#define DO_PPZW(NAME, name) \ +static bool trans_##NAME##_ppzw(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_flags_4 * const fns[4] = { \ + gen_helper_sve_##name##_ppzw_b, gen_helper_sve_##name##_ppzw_h, \ + gen_helper_sve_##name##_ppzw_s, NULL \ + }; \ + return do_ppzz_flags(s, a, fns[a->esz]); \ +} + +DO_PPZW(CMPEQ, cmpeq) +DO_PPZW(CMPNE, cmpne) +DO_PPZW(CMPGT, cmpgt) +DO_PPZW(CMPGE, cmpge) +DO_PPZW(CMPHI, cmphi) +DO_PPZW(CMPHS, cmphs) +DO_PPZW(CMPLT, cmplt) +DO_PPZW(CMPLE, cmple) +DO_PPZW(CMPLO, cmplo) +DO_PPZW(CMPLS, cmpls) + +#undef DO_PPZW + +/* + *** SVE Integer Compare - Immediate Groups + */ + +static bool do_ppzi_flags(DisasContext *s, arg_rpri_esz *a, + gen_helper_gvec_flags_3 *gen_fn) +{ + TCGv_ptr pd, zn, pg; + unsigned vsz; + TCGv_i32 t; + + if (gen_fn == NULL) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + vsz = vec_full_reg_size(s); + t = tcg_const_i32(simd_desc(vsz, vsz, a->imm)); + pd = tcg_temp_new_ptr(); + zn = tcg_temp_new_ptr(); + pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(pd, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(zn, cpu_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + + gen_fn(t, pd, zn, pg, t); + + tcg_temp_free_ptr(pd); + tcg_temp_free_ptr(zn); + tcg_temp_free_ptr(pg); + + do_pred_flags(t); + + tcg_temp_free_i32(t); + return true; +} + +#define DO_PPZI(NAME, name) \ +static bool trans_##NAME##_ppzi(DisasContext *s, arg_rpri_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_flags_3 * const fns[4] = { \ + gen_helper_sve_##name##_ppzi_b, gen_helper_sve_##name##_ppzi_h, \ + gen_helper_sve_##name##_ppzi_s, gen_helper_sve_##name##_ppzi_d, \ + }; \ + return do_ppzi_flags(s, a, fns[a->esz]); \ +} + +DO_PPZI(CMPEQ, cmpeq) +DO_PPZI(CMPNE, cmpne) +DO_PPZI(CMPGT, cmpgt) +DO_PPZI(CMPGE, cmpge) +DO_PPZI(CMPHI, cmphi) +DO_PPZI(CMPHS, cmphs) +DO_PPZI(CMPLT, cmplt) +DO_PPZI(CMPLE, cmple) +DO_PPZI(CMPLO, cmplo) +DO_PPZI(CMPLS, cmpls) + +#undef DO_PPZI + +/* + *** SVE Partition Break Group + */ + +static bool do_brk3(DisasContext *s, arg_rprr_s *a, + gen_helper_gvec_4 *fn, gen_helper_gvec_flags_4 *fn_s) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = pred_full_reg_size(s); + + /* Predicate sizes may be smaller and cannot use simd_desc. */ + TCGv_ptr d = tcg_temp_new_ptr(); + TCGv_ptr n = tcg_temp_new_ptr(); + TCGv_ptr m = tcg_temp_new_ptr(); + TCGv_ptr g = tcg_temp_new_ptr(); + TCGv_i32 t = tcg_const_i32(vsz - 2); + + tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(m, cpu_env, pred_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(g, cpu_env, pred_full_reg_offset(s, a->pg)); + + if (a->s) { + fn_s(t, d, n, m, g, t); + do_pred_flags(t); + } else { + fn(d, n, m, g, t); + } + tcg_temp_free_ptr(d); + tcg_temp_free_ptr(n); + tcg_temp_free_ptr(m); + tcg_temp_free_ptr(g); + tcg_temp_free_i32(t); + return true; +} + +static bool do_brk2(DisasContext *s, arg_rpr_s *a, + gen_helper_gvec_3 *fn, gen_helper_gvec_flags_3 *fn_s) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = pred_full_reg_size(s); + + /* Predicate sizes may be smaller and cannot use simd_desc. */ + TCGv_ptr d = tcg_temp_new_ptr(); + TCGv_ptr n = tcg_temp_new_ptr(); + TCGv_ptr g = tcg_temp_new_ptr(); + TCGv_i32 t = tcg_const_i32(vsz - 2); + + tcg_gen_addi_ptr(d, cpu_env, pred_full_reg_offset(s, a->rd)); + tcg_gen_addi_ptr(n, cpu_env, pred_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(g, cpu_env, pred_full_reg_offset(s, a->pg)); + + if (a->s) { + fn_s(t, d, n, g, t); + do_pred_flags(t); + } else { + fn(d, n, g, t); + } + tcg_temp_free_ptr(d); + tcg_temp_free_ptr(n); + tcg_temp_free_ptr(g); + tcg_temp_free_i32(t); + return true; +} + +static bool trans_BRKPA(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + return do_brk3(s, a, gen_helper_sve_brkpa, gen_helper_sve_brkpas); +} + +static bool trans_BRKPB(DisasContext *s, arg_rprr_s *a, uint32_t insn) +{ + return do_brk3(s, a, gen_helper_sve_brkpb, gen_helper_sve_brkpbs); +} + +static bool trans_BRKA_m(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brka_m, gen_helper_sve_brkas_m); +} + +static bool trans_BRKB_m(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brkb_m, gen_helper_sve_brkbs_m); +} + +static bool trans_BRKA_z(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brka_z, gen_helper_sve_brkas_z); +} + +static bool trans_BRKB_z(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brkb_z, gen_helper_sve_brkbs_z); +} + +static bool trans_BRKN(DisasContext *s, arg_rpr_s *a, uint32_t insn) +{ + return do_brk2(s, a, gen_helper_sve_brkn, gen_helper_sve_brkns); +} + +/* + *** SVE Predicate Count Group + */ + +static void do_cntp(DisasContext *s, TCGv_i64 val, int esz, int pn, int pg) +{ + unsigned psz = pred_full_reg_size(s); + + if (psz <= 8) { + uint64_t psz_mask; + + tcg_gen_ld_i64(val, cpu_env, pred_full_reg_offset(s, pn)); + if (pn != pg) { + TCGv_i64 g = tcg_temp_new_i64(); + tcg_gen_ld_i64(g, cpu_env, pred_full_reg_offset(s, pg)); + tcg_gen_and_i64(val, val, g); + tcg_temp_free_i64(g); + } + + /* Reduce the pred_esz_masks value simply to reduce the + * size of the code generated here. + */ + psz_mask = MAKE_64BIT_MASK(0, psz * 8); + tcg_gen_andi_i64(val, val, pred_esz_masks[esz] & psz_mask); + + tcg_gen_ctpop_i64(val, val); + } else { + TCGv_ptr t_pn = tcg_temp_new_ptr(); + TCGv_ptr t_pg = tcg_temp_new_ptr(); + unsigned desc; + TCGv_i32 t_desc; + + desc = psz - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, esz); + + tcg_gen_addi_ptr(t_pn, cpu_env, pred_full_reg_offset(s, pn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + t_desc = tcg_const_i32(desc); + + gen_helper_sve_cntp(val, t_pn, t_pg, t_desc); + tcg_temp_free_ptr(t_pn); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(t_desc); + } +} + +static bool trans_CNTP(DisasContext *s, arg_CNTP *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_cntp(s, cpu_reg(s, a->rd), a->esz, a->rn, a->pg); + } + return true; +} + +static bool trans_INCDECP_r(DisasContext *s, arg_incdec_pred *a, + uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 reg = cpu_reg(s, a->rd); + TCGv_i64 val = tcg_temp_new_i64(); + + do_cntp(s, val, a->esz, a->pg, a->pg); + if (a->d) { + tcg_gen_sub_i64(reg, reg, val); + } else { + tcg_gen_add_i64(reg, reg, val); + } + tcg_temp_free_i64(val); + } + return true; +} + +static bool trans_INCDECP_z(DisasContext *s, arg_incdec2_pred *a, + uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i64 val = tcg_temp_new_i64(); + GVecGen2sFn *gvec_fn = a->d ? tcg_gen_gvec_subs : tcg_gen_gvec_adds; + + do_cntp(s, val, a->esz, a->pg, a->pg); + gvec_fn(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), val, vsz, vsz); + } + return true; +} + +static bool trans_SINCDECP_r_32(DisasContext *s, arg_incdec_pred *a, + uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 reg = cpu_reg(s, a->rd); + TCGv_i64 val = tcg_temp_new_i64(); + + do_cntp(s, val, a->esz, a->pg, a->pg); + do_sat_addsub_32(reg, val, a->u, a->d); + } + return true; +} + +static bool trans_SINCDECP_r_64(DisasContext *s, arg_incdec_pred *a, + uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 reg = cpu_reg(s, a->rd); + TCGv_i64 val = tcg_temp_new_i64(); + + do_cntp(s, val, a->esz, a->pg, a->pg); + do_sat_addsub_64(reg, val, a->u, a->d); + } + return true; +} + +static bool trans_SINCDECP_z(DisasContext *s, arg_incdec2_pred *a, + uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 val = tcg_temp_new_i64(); + do_cntp(s, val, a->esz, a->pg, a->pg); + do_sat_addsub_vec(s, a->esz, a->rd, a->rn, val, a->u, a->d); + } + return true; +} + +/* + *** SVE Integer Compare Scalars Group + */ + +static bool trans_CTERM(DisasContext *s, arg_CTERM *a, uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + TCGCond cond = (a->ne ? TCG_COND_NE : TCG_COND_EQ); + TCGv_i64 rn = read_cpu_reg(s, a->rn, a->sf); + TCGv_i64 rm = read_cpu_reg(s, a->rm, a->sf); + TCGv_i64 cmp = tcg_temp_new_i64(); + + tcg_gen_setcond_i64(cond, cmp, rn, rm); + tcg_gen_extrl_i64_i32(cpu_NF, cmp); + tcg_temp_free_i64(cmp); + + /* VF = !NF & !CF. */ + tcg_gen_xori_i32(cpu_VF, cpu_NF, 1); + tcg_gen_andc_i32(cpu_VF, cpu_VF, cpu_CF); + + /* Both NF and VF actually look at bit 31. */ + tcg_gen_neg_i32(cpu_NF, cpu_NF); + tcg_gen_neg_i32(cpu_VF, cpu_VF); + return true; +} + +static bool trans_WHILE(DisasContext *s, arg_WHILE *a, uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + TCGv_i64 op0 = read_cpu_reg(s, a->rn, 1); + TCGv_i64 op1 = read_cpu_reg(s, a->rm, 1); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i32 t2, t3; + TCGv_ptr ptr; + unsigned desc, vsz = vec_full_reg_size(s); + TCGCond cond; + + if (!a->sf) { + if (a->u) { + tcg_gen_ext32u_i64(op0, op0); + tcg_gen_ext32u_i64(op1, op1); + } else { + tcg_gen_ext32s_i64(op0, op0); + tcg_gen_ext32s_i64(op1, op1); + } + } + + /* For the helper, compress the different conditions into a computation + * of how many iterations for which the condition is true. + * + * This is slightly complicated by 0 <= UINT64_MAX, which is nominally + * 2**64 iterations, overflowing to 0. Of course, predicate registers + * aren't that large, so any value >= predicate size is sufficient. + */ + tcg_gen_sub_i64(t0, op1, op0); + + /* t0 = MIN(op1 - op0, vsz). */ + tcg_gen_movi_i64(t1, vsz); + tcg_gen_umin_i64(t0, t0, t1); + if (a->eq) { + /* Equality means one more iteration. */ + tcg_gen_addi_i64(t0, t0, 1); + } + + /* t0 = (condition true ? t0 : 0). */ + cond = (a->u + ? (a->eq ? TCG_COND_LEU : TCG_COND_LTU) + : (a->eq ? TCG_COND_LE : TCG_COND_LT)); + tcg_gen_movi_i64(t1, 0); + tcg_gen_movcond_i64(cond, t0, op0, op1, t0, t1); + + t2 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t2, t0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + + desc = (vsz / 8) - 2; + desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); + t3 = tcg_const_i32(desc); + + ptr = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(ptr, cpu_env, pred_full_reg_offset(s, a->rd)); + + gen_helper_sve_while(t2, ptr, t2, t3); + do_pred_flags(t2); + + tcg_temp_free_ptr(ptr); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + return true; +} + +/* + *** SVE Integer Wide Immediate - Unpredicated Group + */ + +static bool trans_FDUP(DisasContext *s, arg_FDUP *a, uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + int dofs = vec_full_reg_offset(s, a->rd); + uint64_t imm; + + /* Decode the VFP immediate. */ + imm = vfp_expand_imm(a->esz, a->imm); + imm = dup_const(a->esz, imm); + + tcg_gen_gvec_dup64i(dofs, vsz, vsz, imm); + } + return true; +} + +static bool trans_DUP_i(DisasContext *s, arg_DUP_i *a, uint32_t insn) +{ + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + int dofs = vec_full_reg_offset(s, a->rd); + + tcg_gen_gvec_dup64i(dofs, vsz, vsz, dup_const(a->esz, a->imm)); + } + return true; +} + +static bool trans_ADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_addi(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), a->imm, vsz, vsz); + } + return true; +} + +static bool trans_SUB_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + a->imm = -a->imm; + return trans_ADD_zzi(s, a, insn); +} + +static bool trans_SUBR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + static const GVecGen2s op[4] = { + { .fni8 = tcg_gen_vec_sub8_i64, + .fniv = tcg_gen_sub_vec, + .fno = gen_helper_sve_subri_b, + .opc = INDEX_op_sub_vec, + .vece = MO_8, + .scalar_first = true }, + { .fni8 = tcg_gen_vec_sub16_i64, + .fniv = tcg_gen_sub_vec, + .fno = gen_helper_sve_subri_h, + .opc = INDEX_op_sub_vec, + .vece = MO_16, + .scalar_first = true }, + { .fni4 = tcg_gen_sub_i32, + .fniv = tcg_gen_sub_vec, + .fno = gen_helper_sve_subri_s, + .opc = INDEX_op_sub_vec, + .vece = MO_32, + .scalar_first = true }, + { .fni8 = tcg_gen_sub_i64, + .fniv = tcg_gen_sub_vec, + .fno = gen_helper_sve_subri_d, + .opc = INDEX_op_sub_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .vece = MO_64, + .scalar_first = true } + }; + + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i64 c = tcg_const_i64(a->imm); + tcg_gen_gvec_2s(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vsz, vsz, c, &op[a->esz]); + tcg_temp_free_i64(c); + } + return true; +} + +static bool trans_MUL_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_muli(a->esz, vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), a->imm, vsz, vsz); + } + return true; +} + +static bool do_zzi_sat(DisasContext *s, arg_rri_esz *a, uint32_t insn, + bool u, bool d) +{ + if (a->esz == 0 && extract32(insn, 13, 1)) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 val = tcg_const_i64(a->imm); + do_sat_addsub_vec(s, a->esz, a->rd, a->rn, val, u, d); + tcg_temp_free_i64(val); + } + return true; +} + +static bool trans_SQADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_zzi_sat(s, a, insn, false, false); +} + +static bool trans_UQADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_zzi_sat(s, a, insn, true, false); +} + +static bool trans_SQSUB_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_zzi_sat(s, a, insn, false, true); +} + +static bool trans_UQSUB_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +{ + return do_zzi_sat(s, a, insn, true, true); +} + +static bool do_zzi_ool(DisasContext *s, arg_rri_esz *a, gen_helper_gvec_2i *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i64 c = tcg_const_i64(a->imm); + + tcg_gen_gvec_2i_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + c, vsz, vsz, 0, fn); + tcg_temp_free_i64(c); + } + return true; +} + +#define DO_ZZI(NAME, name) \ +static bool trans_##NAME##_zzi(DisasContext *s, arg_rri_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_2i * const fns[4] = { \ + gen_helper_sve_##name##i_b, gen_helper_sve_##name##i_h, \ + gen_helper_sve_##name##i_s, gen_helper_sve_##name##i_d, \ + }; \ + return do_zzi_ool(s, a, fns[a->esz]); \ +} + +DO_ZZI(SMAX, smax) +DO_ZZI(UMAX, umax) +DO_ZZI(SMIN, smin) +DO_ZZI(UMIN, umin) + +#undef DO_ZZI + +static bool trans_DOT_zzz(DisasContext *s, arg_DOT_zzz *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[2][2] = { + { gen_helper_gvec_sdot_b, gen_helper_gvec_sdot_h }, + { gen_helper_gvec_udot_b, gen_helper_gvec_udot_h } + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, 0, fns[a->u][a->sz]); + } + return true; +} + +static bool trans_DOT_zzx(DisasContext *s, arg_DOT_zzx *a, uint32_t insn) +{ + static gen_helper_gvec_3 * const fns[2][2] = { + { gen_helper_gvec_sdot_idx_b, gen_helper_gvec_sdot_idx_h }, + { gen_helper_gvec_udot_idx_b, gen_helper_gvec_udot_idx_h } + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, a->index, fns[a->u][a->sz]); + } + return true; +} + + +/* + *** SVE Floating Point Multiply-Add Indexed Group + */ + +static bool trans_FMLA_zzxz(DisasContext *s, arg_FMLA_zzxz *a, uint32_t insn) +{ + static gen_helper_gvec_4_ptr * const fns[3] = { + gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_fmla_idx_s, + gen_helper_gvec_fmla_idx_d, + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vec_full_reg_offset(s, a->ra), + status, vsz, vsz, (a->index << 1) | a->sub, + fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + +/* + *** SVE Floating Point Multiply Indexed Group + */ + +static bool trans_FMUL_zzx(DisasContext *s, arg_FMUL_zzx *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_gvec_fmul_idx_h, + gen_helper_gvec_fmul_idx_s, + gen_helper_gvec_fmul_idx_d, + }; + + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + status, vsz, vsz, a->index, fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + +/* + *** SVE Floating Point Fast Reduction Group + */ + +typedef void gen_helper_fp_reduce(TCGv_i64, TCGv_ptr, TCGv_ptr, + TCGv_ptr, TCGv_i32); + +static void do_reduce(DisasContext *s, arg_rpr_esz *a, + gen_helper_fp_reduce *fn) +{ + unsigned vsz = vec_full_reg_size(s); + unsigned p2vsz = pow2ceil(vsz); + TCGv_i32 t_desc = tcg_const_i32(simd_desc(vsz, p2vsz, 0)); + TCGv_ptr t_zn, t_pg, status; + TCGv_i64 temp; + + temp = tcg_temp_new_i64(); + t_zn = tcg_temp_new_ptr(); + t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, a->rn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); + status = get_fpstatus_ptr(a->esz == MO_16); + + fn(temp, t_zn, t_pg, status, t_desc); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_ptr(status); + tcg_temp_free_i32(t_desc); + + write_fp_dreg(s, a->rd, temp); + tcg_temp_free_i64(temp); +} + +#define DO_VPZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_fp_reduce * const fns[3] = { \ + gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, \ + gen_helper_sve_##name##_d, \ + }; \ + if (a->esz == 0) { \ + return false; \ + } \ + if (sve_access_check(s)) { \ + do_reduce(s, a, fns[a->esz - 1]); \ + } \ + return true; \ +} + +DO_VPZ(FADDV, faddv) +DO_VPZ(FMINNMV, fminnmv) +DO_VPZ(FMAXNMV, fmaxnmv) +DO_VPZ(FMINV, fminv) +DO_VPZ(FMAXV, fmaxv) + +/* + *** SVE Floating Point Unary Operations - Unpredicated Group + */ + +static void do_zz_fp(DisasContext *s, arg_rr_esz *a, gen_helper_gvec_2_ptr *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + + tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); +} + +static bool trans_FRECPE(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_2_ptr * const fns[3] = { + gen_helper_gvec_frecpe_h, + gen_helper_gvec_frecpe_s, + gen_helper_gvec_frecpe_d, + }; + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + do_zz_fp(s, a, fns[a->esz - 1]); + } + return true; +} + +static bool trans_FRSQRTE(DisasContext *s, arg_rr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_2_ptr * const fns[3] = { + gen_helper_gvec_frsqrte_h, + gen_helper_gvec_frsqrte_s, + gen_helper_gvec_frsqrte_d, + }; + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + do_zz_fp(s, a, fns[a->esz - 1]); + } + return true; +} + +/* + *** SVE Floating Point Compare with Zero Group + */ + +static void do_ppz_fp(DisasContext *s, arg_rpr_esz *a, + gen_helper_gvec_3_ptr *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + + tcg_gen_gvec_3_ptr(pred_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); +} + +#define DO_PPZ(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_3_ptr * const fns[3] = { \ + gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, \ + gen_helper_sve_##name##_d, \ + }; \ + if (a->esz == 0) { \ + return false; \ + } \ + if (sve_access_check(s)) { \ + do_ppz_fp(s, a, fns[a->esz - 1]); \ + } \ + return true; \ +} + +DO_PPZ(FCMGE_ppz0, fcmge0) +DO_PPZ(FCMGT_ppz0, fcmgt0) +DO_PPZ(FCMLE_ppz0, fcmle0) +DO_PPZ(FCMLT_ppz0, fcmlt0) +DO_PPZ(FCMEQ_ppz0, fcmeq0) +DO_PPZ(FCMNE_ppz0, fcmne0) + +#undef DO_PPZ + +/* + *** SVE floating-point trig multiply-add coefficient + */ + +static bool trans_FTMAD(DisasContext *s, arg_FTMAD *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_sve_ftmad_h, + gen_helper_sve_ftmad_s, + gen_helper_sve_ftmad_d, + }; + + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + status, vsz, vsz, a->imm, fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + +/* + *** SVE Floating Point Accumulating Reduction Group + */ + +static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +{ + typedef void fadda_fn(TCGv_i64, TCGv_i64, TCGv_ptr, + TCGv_ptr, TCGv_ptr, TCGv_i32); + static fadda_fn * const fns[3] = { + gen_helper_sve_fadda_h, + gen_helper_sve_fadda_s, + gen_helper_sve_fadda_d, + }; + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_rm, t_pg, t_fpst; + TCGv_i64 t_val; + TCGv_i32 t_desc; + + if (a->esz == 0) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + t_val = load_esz(cpu_env, vec_reg_offset(s, a->rn, 0, a->esz), a->esz); + t_rm = tcg_temp_new_ptr(); + t_pg = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(t_rm, cpu_env, vec_full_reg_offset(s, a->rm)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, a->pg)); + t_fpst = get_fpstatus_ptr(a->esz == MO_16); + t_desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + + fns[a->esz - 1](t_val, t_val, t_rm, t_pg, t_fpst, t_desc); + + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(t_fpst); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_ptr(t_rm); + + write_fp_dreg(s, a->rd, t_val); + tcg_temp_free_i64(t_val); + return true; +} + +/* + *** SVE Floating Point Arithmetic - Unpredicated Group + */ + +static bool do_zzz_fp(DisasContext *s, arg_rrr_esz *a, + gen_helper_gvec_3_ptr *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); + } + return true; +} + + +#define DO_FP3(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rrr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_3_ptr * const fns[4] = { \ + NULL, gen_helper_gvec_##name##_h, \ + gen_helper_gvec_##name##_s, gen_helper_gvec_##name##_d \ + }; \ + return do_zzz_fp(s, a, fns[a->esz]); \ +} + +DO_FP3(FADD_zzz, fadd) +DO_FP3(FSUB_zzz, fsub) +DO_FP3(FMUL_zzz, fmul) +DO_FP3(FTSMUL, ftsmul) +DO_FP3(FRECPS, recps) +DO_FP3(FRSQRTS, rsqrts) + +#undef DO_FP3 + +/* + *** SVE Floating Point Arithmetic - Predicated Group + */ + +static bool do_zpzz_fp(DisasContext *s, arg_rprr_esz *a, + gen_helper_gvec_4_ptr *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); + } + return true; +} + +#define DO_FP3(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rprr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_gvec_4_ptr * const fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ + }; \ + return do_zpzz_fp(s, a, fns[a->esz]); \ +} + +DO_FP3(FADD_zpzz, fadd) +DO_FP3(FSUB_zpzz, fsub) +DO_FP3(FMUL_zpzz, fmul) +DO_FP3(FMIN_zpzz, fmin) +DO_FP3(FMAX_zpzz, fmax) +DO_FP3(FMINNM_zpzz, fminnum) +DO_FP3(FMAXNM_zpzz, fmaxnum) +DO_FP3(FABD, fabd) +DO_FP3(FSCALE, fscalbn) +DO_FP3(FDIV, fdiv) +DO_FP3(FMULX, fmulx) + +#undef DO_FP3 + +typedef void gen_helper_sve_fp2scalar(TCGv_ptr, TCGv_ptr, TCGv_ptr, + TCGv_i64, TCGv_ptr, TCGv_i32); + +static void do_fp_scalar(DisasContext *s, int zd, int zn, int pg, bool is_fp16, + TCGv_i64 scalar, gen_helper_sve_fp2scalar *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_zd, t_zn, t_pg, status; + TCGv_i32 desc; + + t_zd = tcg_temp_new_ptr(); + t_zn = tcg_temp_new_ptr(); + t_pg = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(t_zd, cpu_env, vec_full_reg_offset(s, zd)); + tcg_gen_addi_ptr(t_zn, cpu_env, vec_full_reg_offset(s, zn)); + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + + status = get_fpstatus_ptr(is_fp16); + desc = tcg_const_i32(simd_desc(vsz, vsz, 0)); + fn(t_zd, t_zn, t_pg, scalar, status, desc); + + tcg_temp_free_i32(desc); + tcg_temp_free_ptr(status); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_ptr(t_zn); + tcg_temp_free_ptr(t_zd); +} + +static void do_fp_imm(DisasContext *s, arg_rpri_esz *a, uint64_t imm, + gen_helper_sve_fp2scalar *fn) +{ + TCGv_i64 temp = tcg_const_i64(imm); + do_fp_scalar(s, a->rd, a->rn, a->pg, a->esz == MO_16, temp, fn); + tcg_temp_free_i64(temp); +} + +#define DO_FP_IMM(NAME, name, const0, const1) \ +static bool trans_##NAME##_zpzi(DisasContext *s, arg_rpri_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_sve_fp2scalar * const fns[3] = { \ + gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, \ + gen_helper_sve_##name##_d \ + }; \ + static uint64_t const val[3][2] = { \ + { float16_##const0, float16_##const1 }, \ + { float32_##const0, float32_##const1 }, \ + { float64_##const0, float64_##const1 }, \ + }; \ + if (a->esz == 0) { \ + return false; \ + } \ + if (sve_access_check(s)) { \ + do_fp_imm(s, a, val[a->esz - 1][a->imm], fns[a->esz - 1]); \ + } \ + return true; \ +} + +#define float16_two make_float16(0x4000) +#define float32_two make_float32(0x40000000) +#define float64_two make_float64(0x4000000000000000ULL) + +DO_FP_IMM(FADD, fadds, half, one) +DO_FP_IMM(FSUB, fsubs, half, one) +DO_FP_IMM(FMUL, fmuls, half, two) +DO_FP_IMM(FSUBR, fsubrs, half, one) +DO_FP_IMM(FMAXNM, fmaxnms, zero, one) +DO_FP_IMM(FMINNM, fminnms, zero, one) +DO_FP_IMM(FMAX, fmaxs, zero, one) +DO_FP_IMM(FMIN, fmins, zero, one) + +#undef DO_FP_IMM + +static bool do_fp_cmp(DisasContext *s, arg_rprr_esz *a, + gen_helper_gvec_4_ptr *fn) +{ + if (fn == NULL) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_4_ptr(pred_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); + } + return true; +} + +#define DO_FPCMP(NAME, name) \ +static bool trans_##NAME##_ppzz(DisasContext *s, arg_rprr_esz *a, \ + uint32_t insn) \ +{ \ + static gen_helper_gvec_4_ptr * const fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ + }; \ + return do_fp_cmp(s, a, fns[a->esz]); \ +} + +DO_FPCMP(FCMGE, fcmge) +DO_FPCMP(FCMGT, fcmgt) +DO_FPCMP(FCMEQ, fcmeq) +DO_FPCMP(FCMNE, fcmne) +DO_FPCMP(FCMUO, fcmuo) +DO_FPCMP(FACGE, facge) +DO_FPCMP(FACGT, facgt) + +#undef DO_FPCMP + +static bool trans_FCADD(DisasContext *s, arg_FCADD *a, uint32_t insn) +{ + static gen_helper_gvec_4_ptr * const fns[3] = { + gen_helper_sve_fcadd_h, + gen_helper_sve_fcadd_s, + gen_helper_sve_fcadd_d + }; + + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, a->rot, fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + +typedef void gen_helper_sve_fmla(TCGv_env, TCGv_ptr, TCGv_i32); + +static bool do_fmla(DisasContext *s, arg_rprrr_esz *a, gen_helper_sve_fmla *fn) +{ + if (fn == NULL) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = vec_full_reg_size(s); + unsigned desc; + TCGv_i32 t_desc; + TCGv_ptr pg = tcg_temp_new_ptr(); + + /* We would need 7 operands to pass these arguments "properly". + * So we encode all the register numbers into the descriptor. + */ + desc = deposit32(a->rd, 5, 5, a->rn); + desc = deposit32(desc, 10, 5, a->rm); + desc = deposit32(desc, 15, 5, a->ra); + desc = simd_desc(vsz, vsz, desc); + + t_desc = tcg_const_i32(desc); + tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + fn(cpu_env, pg, t_desc); + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(pg); + return true; +} + +#define DO_FMLA(NAME, name) \ +static bool trans_##NAME(DisasContext *s, arg_rprrr_esz *a, uint32_t insn) \ +{ \ + static gen_helper_sve_fmla * const fns[4] = { \ + NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ + }; \ + return do_fmla(s, a, fns[a->esz]); \ +} + +DO_FMLA(FMLA_zpzzz, fmla_zpzzz) +DO_FMLA(FMLS_zpzzz, fmls_zpzzz) +DO_FMLA(FNMLA_zpzzz, fnmla_zpzzz) +DO_FMLA(FNMLS_zpzzz, fnmls_zpzzz) + +#undef DO_FMLA + +static bool trans_FCMLA_zpzzz(DisasContext *s, + arg_FCMLA_zpzzz *a, uint32_t insn) +{ + static gen_helper_sve_fmla * const fns[3] = { + gen_helper_sve_fcmla_zpzzz_h, + gen_helper_sve_fcmla_zpzzz_s, + gen_helper_sve_fcmla_zpzzz_d, + }; + + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + unsigned desc; + TCGv_i32 t_desc; + TCGv_ptr pg = tcg_temp_new_ptr(); + + /* We would need 7 operands to pass these arguments "properly". + * So we encode all the register numbers into the descriptor. + */ + desc = deposit32(a->rd, 5, 5, a->rn); + desc = deposit32(desc, 10, 5, a->rm); + desc = deposit32(desc, 15, 5, a->ra); + desc = deposit32(desc, 20, 2, a->rot); + desc = sextract32(desc, 0, 22); + desc = simd_desc(vsz, vsz, desc); + + t_desc = tcg_const_i32(desc); + tcg_gen_addi_ptr(pg, cpu_env, pred_full_reg_offset(s, a->pg)); + fns[a->esz - 1](cpu_env, pg, t_desc); + tcg_temp_free_i32(t_desc); + tcg_temp_free_ptr(pg); + } + return true; +} + +static bool trans_FCMLA_zzxz(DisasContext *s, arg_FCMLA_zzxz *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[2] = { + gen_helper_gvec_fcmlah_idx, + gen_helper_gvec_fcmlas_idx, + }; + + tcg_debug_assert(a->esz == 1 || a->esz == 2); + tcg_debug_assert(a->rd == a->ra); + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + status, vsz, vsz, + a->index * 4 + a->rot, + fns[a->esz - 1]); + tcg_temp_free_ptr(status); + } + return true; +} + +/* + *** SVE Floating Point Unary Operations Predicated Group + */ + +static bool do_zpz_ptr(DisasContext *s, int rd, int rn, int pg, + bool is_fp16, gen_helper_gvec_3_ptr *fn) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr status = get_fpstatus_ptr(is_fp16); + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + pred_full_reg_offset(s, pg), + status, vsz, vsz, 0, fn); + tcg_temp_free_ptr(status); + } + return true; +} + +static bool trans_FCVT_sh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvt_sh); +} + +static bool trans_FCVT_hs(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_hs); +} + +static bool trans_FCVT_dh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvt_dh); +} + +static bool trans_FCVT_hd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_hd); +} + +static bool trans_FCVT_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_ds); +} + +static bool trans_FCVT_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_sd); +} + +static bool trans_FCVTZS_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzs_hh); +} + +static bool trans_FCVTZU_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzu_hh); +} + +static bool trans_FCVTZS_hs(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzs_hs); +} + +static bool trans_FCVTZU_hs(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzu_hs); +} + +static bool trans_FCVTZS_hd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzs_hd); +} + +static bool trans_FCVTZU_hd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzu_hd); +} + +static bool trans_FCVTZS_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_ss); +} + +static bool trans_FCVTZU_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_ss); +} + +static bool trans_FCVTZS_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_sd); +} + +static bool trans_FCVTZU_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_sd); +} + +static bool trans_FCVTZS_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_ds); +} + +static bool trans_FCVTZU_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_ds); +} + +static bool trans_FCVTZS_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_dd); +} + +static bool trans_FCVTZU_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_dd); +} + +static gen_helper_gvec_3_ptr * const frint_fns[3] = { + gen_helper_sve_frint_h, + gen_helper_sve_frint_s, + gen_helper_sve_frint_d +}; + +static bool trans_FRINTI(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (a->esz == 0) { + return false; + } + return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, + frint_fns[a->esz - 1]); +} + +static bool trans_FRINTX(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_sve_frintx_h, + gen_helper_sve_frintx_s, + gen_helper_sve_frintx_d + }; + if (a->esz == 0) { + return false; + } + return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, fns[a->esz - 1]); +} + +static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, int mode) +{ + if (a->esz == 0) { + return false; + } + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 tmode = tcg_const_i32(mode); + TCGv_ptr status = get_fpstatus_ptr(a->esz == MO_16); + + gen_helper_set_rmode(tmode, tmode, status); + + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + pred_full_reg_offset(s, a->pg), + status, vsz, vsz, 0, frint_fns[a->esz - 1]); + + gen_helper_set_rmode(tmode, tmode, status); + tcg_temp_free_i32(tmode); + tcg_temp_free_ptr(status); + } + return true; +} + +static bool trans_FRINTN(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_nearest_even); +} + +static bool trans_FRINTP(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_up); +} + +static bool trans_FRINTM(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_down); +} + +static bool trans_FRINTZ(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_to_zero); +} + +static bool trans_FRINTA(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_frint_mode(s, a, float_round_ties_away); +} + +static bool trans_FRECPX(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_sve_frecpx_h, + gen_helper_sve_frecpx_s, + gen_helper_sve_frecpx_d + }; + if (a->esz == 0) { + return false; + } + return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, fns[a->esz - 1]); +} + +static bool trans_FSQRT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + static gen_helper_gvec_3_ptr * const fns[3] = { + gen_helper_sve_fsqrt_h, + gen_helper_sve_fsqrt_s, + gen_helper_sve_fsqrt_d + }; + if (a->esz == 0) { + return false; + } + return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, fns[a->esz - 1]); +} + +static bool trans_SCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_hh); +} + +static bool trans_SCVTF_sh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_sh); +} + +static bool trans_SCVTF_dh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_dh); +} + +static bool trans_SCVTF_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_ss); +} + +static bool trans_SCVTF_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_ds); +} + +static bool trans_SCVTF_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_sd); +} + +static bool trans_SCVTF_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_dd); +} + +static bool trans_UCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_ucvt_hh); +} + +static bool trans_UCVTF_sh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_ucvt_sh); +} + +static bool trans_UCVTF_dh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_ucvt_dh); +} + +static bool trans_UCVTF_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_ss); +} + +static bool trans_UCVTF_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_ds); +} + +static bool trans_UCVTF_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_sd); +} + +static bool trans_UCVTF_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_dd); +} + +/* + *** SVE Memory - 32-bit Gather and Unsized Contiguous Group + */ + +/* Subroutine loading a vector register at VOFS of LEN bytes. + * The load should begin at the address Rn + IMM. + */ + +static void do_ldr(DisasContext *s, uint32_t vofs, uint32_t len, + int rn, int imm) +{ + uint32_t len_align = QEMU_ALIGN_DOWN(len, 8); + uint32_t len_remain = len % 8; + uint32_t nparts = len / 8 + ctpop8(len_remain); + int midx = get_mem_index(s); + TCGv_i64 addr, t0, t1; + + addr = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + + /* Note that unpredicated load/store of vector/predicate registers + * are defined as a stream of bytes, which equates to little-endian + * operations on larger quantities. There is no nice way to force + * a little-endian load for aarch64_be-linux-user out of line. + * + * Attempt to keep code expansion to a minimum by limiting the + * amount of unrolling done. + */ + if (nparts <= 4) { + int i; + + for (i = 0; i < len_align; i += 8) { + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm + i); + tcg_gen_qemu_ld_i64(t0, addr, midx, MO_LEQ); + tcg_gen_st_i64(t0, cpu_env, vofs + i); + } + } else { + TCGLabel *loop = gen_new_label(); + TCGv_ptr tp, i = tcg_const_local_ptr(0); + + gen_set_label(loop); + + /* Minimize the number of local temps that must be re-read from + * the stack each iteration. Instead, re-compute values other + * than the loop counter. + */ + tp = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(tp, i, imm); + tcg_gen_extu_ptr_i64(addr, tp); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, rn)); + + tcg_gen_qemu_ld_i64(t0, addr, midx, MO_LEQ); + + tcg_gen_add_ptr(tp, cpu_env, i); + tcg_gen_addi_ptr(i, i, 8); + tcg_gen_st_i64(t0, tp, vofs); + tcg_temp_free_ptr(tp); + + tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); + tcg_temp_free_ptr(i); + } + + /* Predicate register loads can be any multiple of 2. + * Note that we still store the entire 64-bit unit into cpu_env. + */ + if (len_remain) { + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm + len_align); + + switch (len_remain) { + case 2: + case 4: + case 8: + tcg_gen_qemu_ld_i64(t0, addr, midx, MO_LE | ctz32(len_remain)); + break; + + case 6: + t1 = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(t0, addr, midx, MO_LEUL); + tcg_gen_addi_i64(addr, addr, 4); + tcg_gen_qemu_ld_i64(t1, addr, midx, MO_LEUW); + tcg_gen_deposit_i64(t0, t0, t1, 32, 32); + tcg_temp_free_i64(t1); + break; + + default: + g_assert_not_reached(); + } + tcg_gen_st_i64(t0, cpu_env, vofs + len_align); + } + tcg_temp_free_i64(addr); + tcg_temp_free_i64(t0); +} + +/* Similarly for stores. */ +static void do_str(DisasContext *s, uint32_t vofs, uint32_t len, + int rn, int imm) +{ + uint32_t len_align = QEMU_ALIGN_DOWN(len, 8); + uint32_t len_remain = len % 8; + uint32_t nparts = len / 8 + ctpop8(len_remain); + int midx = get_mem_index(s); + TCGv_i64 addr, t0; + + addr = tcg_temp_new_i64(); + t0 = tcg_temp_new_i64(); + + /* Note that unpredicated load/store of vector/predicate registers + * are defined as a stream of bytes, which equates to little-endian + * operations on larger quantities. There is no nice way to force + * a little-endian store for aarch64_be-linux-user out of line. + * + * Attempt to keep code expansion to a minimum by limiting the + * amount of unrolling done. + */ + if (nparts <= 4) { + int i; + + for (i = 0; i < len_align; i += 8) { + tcg_gen_ld_i64(t0, cpu_env, vofs + i); + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm + i); + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LEQ); + } + } else { + TCGLabel *loop = gen_new_label(); + TCGv_ptr t2, i = tcg_const_local_ptr(0); + + gen_set_label(loop); + + t2 = tcg_temp_new_ptr(); + tcg_gen_add_ptr(t2, cpu_env, i); + tcg_gen_ld_i64(t0, t2, vofs); + + /* Minimize the number of local temps that must be re-read from + * the stack each iteration. Instead, re-compute values other + * than the loop counter. + */ + tcg_gen_addi_ptr(t2, i, imm); + tcg_gen_extu_ptr_i64(addr, t2); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, rn)); + tcg_temp_free_ptr(t2); + + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LEQ); + + tcg_gen_addi_ptr(i, i, 8); + + tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); + tcg_temp_free_ptr(i); + } + + /* Predicate register stores can be any multiple of 2. */ + if (len_remain) { + tcg_gen_ld_i64(t0, cpu_env, vofs + len_align); + tcg_gen_addi_i64(addr, cpu_reg_sp(s, rn), imm + len_align); + + switch (len_remain) { + case 2: + case 4: + case 8: + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LE | ctz32(len_remain)); + break; + + case 6: + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LEUL); + tcg_gen_addi_i64(addr, addr, 4); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_qemu_st_i64(t0, addr, midx, MO_LEUW); + break; + + default: + g_assert_not_reached(); + } + } + tcg_temp_free_i64(addr); + tcg_temp_free_i64(t0); +} + +static bool trans_LDR_zri(DisasContext *s, arg_rri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int size = vec_full_reg_size(s); + int off = vec_full_reg_offset(s, a->rd); + do_ldr(s, off, size, a->rn, a->imm * size); + } + return true; +} + +static bool trans_LDR_pri(DisasContext *s, arg_rri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int size = pred_full_reg_size(s); + int off = pred_full_reg_offset(s, a->rd); + do_ldr(s, off, size, a->rn, a->imm * size); + } + return true; +} + +static bool trans_STR_zri(DisasContext *s, arg_rri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int size = vec_full_reg_size(s); + int off = vec_full_reg_offset(s, a->rd); + do_str(s, off, size, a->rn, a->imm * size); + } + return true; +} + +static bool trans_STR_pri(DisasContext *s, arg_rri *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int size = pred_full_reg_size(s); + int off = pred_full_reg_offset(s, a->rd); + do_str(s, off, size, a->rn, a->imm * size); + } + return true; +} + +/* + *** SVE Memory - Contiguous Load Group + */ + +/* The memory mode of the dtype. */ +static const TCGMemOp dtype_mop[16] = { + MO_UB, MO_UB, MO_UB, MO_UB, + MO_SL, MO_UW, MO_UW, MO_UW, + MO_SW, MO_SW, MO_UL, MO_UL, + MO_SB, MO_SB, MO_SB, MO_Q +}; + +#define dtype_msz(x) (dtype_mop[x] & MO_SIZE) + +/* The vector element size of dtype. */ +static const uint8_t dtype_esz[16] = { + 0, 1, 2, 3, + 3, 1, 2, 3, + 3, 2, 2, 3, + 3, 2, 1, 3 +}; + +static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + gen_helper_gvec_mem *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_pg; + TCGv_i32 desc; + + /* For e.g. LD4, there are not enough arguments to pass all 4 + * registers as pointers, so encode the regno into the data field. + * For consistency, do this even for LD1. + */ + desc = tcg_const_i32(simd_desc(vsz, vsz, zt)); + t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + fn(cpu_env, t_pg, addr, desc); + + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); +} + +static void do_ld_zpa(DisasContext *s, int zt, int pg, + TCGv_i64 addr, int dtype, int nreg) +{ + static gen_helper_gvec_mem * const fns[16][4] = { + { gen_helper_sve_ld1bb_r, gen_helper_sve_ld2bb_r, + gen_helper_sve_ld3bb_r, gen_helper_sve_ld4bb_r }, + { gen_helper_sve_ld1bhu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bsu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bdu_r, NULL, NULL, NULL }, + + { gen_helper_sve_ld1sds_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hh_r, gen_helper_sve_ld2hh_r, + gen_helper_sve_ld3hh_r, gen_helper_sve_ld4hh_r }, + { gen_helper_sve_ld1hsu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hdu_r, NULL, NULL, NULL }, + + { gen_helper_sve_ld1hds_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hss_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1ss_r, gen_helper_sve_ld2ss_r, + gen_helper_sve_ld3ss_r, gen_helper_sve_ld4ss_r }, + { gen_helper_sve_ld1sdu_r, NULL, NULL, NULL }, + + { gen_helper_sve_ld1bds_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bss_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bhs_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1dd_r, gen_helper_sve_ld2dd_r, + gen_helper_sve_ld3dd_r, gen_helper_sve_ld4dd_r }, + }; + gen_helper_gvec_mem *fn = fns[dtype][nreg]; + + /* While there are holes in the table, they are not + * accessible via the instruction encoding. + */ + assert(fn != NULL); + do_mem_zpa(s, zt, pg, addr, fn); +} + +static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) +{ + if (a->rm == 31) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_muli_i64(addr, cpu_reg(s, a->rm), + (a->nreg + 1) << dtype_msz(a->dtype)); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + do_ld_zpa(s, a->rd, a->pg, addr, a->dtype, a->nreg); + } + return true; +} + +static bool trans_LD_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +{ + if (sve_access_check(s)) { + int vsz = vec_full_reg_size(s); + int elements = vsz >> dtype_esz[a->dtype]; + TCGv_i64 addr = new_tmp_a64(s); + + tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), + (a->imm * elements * (a->nreg + 1)) + << dtype_msz(a->dtype)); + do_ld_zpa(s, a->rd, a->pg, addr, a->dtype, a->nreg); + } + return true; +} + +static bool trans_LDFF1_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) +{ + static gen_helper_gvec_mem * const fns[16] = { + gen_helper_sve_ldff1bb_r, + gen_helper_sve_ldff1bhu_r, + gen_helper_sve_ldff1bsu_r, + gen_helper_sve_ldff1bdu_r, + + gen_helper_sve_ldff1sds_r, + gen_helper_sve_ldff1hh_r, + gen_helper_sve_ldff1hsu_r, + gen_helper_sve_ldff1hdu_r, + + gen_helper_sve_ldff1hds_r, + gen_helper_sve_ldff1hss_r, + gen_helper_sve_ldff1ss_r, + gen_helper_sve_ldff1sdu_r, + + gen_helper_sve_ldff1bds_r, + gen_helper_sve_ldff1bss_r, + gen_helper_sve_ldff1bhs_r, + gen_helper_sve_ldff1dd_r, + }; + + if (sve_access_check(s)) { + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + do_mem_zpa(s, a->rd, a->pg, addr, fns[a->dtype]); + } + return true; +} + +static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +{ + static gen_helper_gvec_mem * const fns[16] = { + gen_helper_sve_ldnf1bb_r, + gen_helper_sve_ldnf1bhu_r, + gen_helper_sve_ldnf1bsu_r, + gen_helper_sve_ldnf1bdu_r, + + gen_helper_sve_ldnf1sds_r, + gen_helper_sve_ldnf1hh_r, + gen_helper_sve_ldnf1hsu_r, + gen_helper_sve_ldnf1hdu_r, + + gen_helper_sve_ldnf1hds_r, + gen_helper_sve_ldnf1hss_r, + gen_helper_sve_ldnf1ss_r, + gen_helper_sve_ldnf1sdu_r, + + gen_helper_sve_ldnf1bds_r, + gen_helper_sve_ldnf1bss_r, + gen_helper_sve_ldnf1bhs_r, + gen_helper_sve_ldnf1dd_r, + }; + + if (sve_access_check(s)) { + int vsz = vec_full_reg_size(s); + int elements = vsz >> dtype_esz[a->dtype]; + int off = (a->imm * elements) << dtype_msz(a->dtype); + TCGv_i64 addr = new_tmp_a64(s); + + tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), off); + do_mem_zpa(s, a->rd, a->pg, addr, fns[a->dtype]); + } + return true; +} + +static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int msz) +{ + static gen_helper_gvec_mem * const fns[4] = { + gen_helper_sve_ld1bb_r, gen_helper_sve_ld1hh_r, + gen_helper_sve_ld1ss_r, gen_helper_sve_ld1dd_r, + }; + unsigned vsz = vec_full_reg_size(s); + TCGv_ptr t_pg; + TCGv_i32 desc; + + /* Load the first quadword using the normal predicated load helpers. */ + desc = tcg_const_i32(simd_desc(16, 16, zt)); + t_pg = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + fns[msz](cpu_env, t_pg, addr, desc); + + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); + + /* Replicate that first quadword. */ + if (vsz > 16) { + unsigned dofs = vec_full_reg_offset(s, zt); + tcg_gen_gvec_dup_mem(4, dofs + 16, dofs, vsz - 16, vsz - 16); + } +} + +static bool trans_LD1RQ_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) +{ + if (a->rm == 31) { + return false; + } + if (sve_access_check(s)) { + int msz = dtype_msz(a->dtype); + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), msz); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + do_ldrq(s, a->rd, a->pg, addr, msz); + } + return true; +} + +static bool trans_LD1RQ_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +{ + if (sve_access_check(s)) { + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), a->imm * 16); + do_ldrq(s, a->rd, a->pg, addr, dtype_msz(a->dtype)); + } + return true; +} + +/* Load and broadcast element. */ +static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +{ + if (!sve_access_check(s)) { + return true; + } + + unsigned vsz = vec_full_reg_size(s); + unsigned psz = pred_full_reg_size(s); + unsigned esz = dtype_esz[a->dtype]; + TCGLabel *over = gen_new_label(); + TCGv_i64 temp; + + /* If the guarding predicate has no bits set, no load occurs. */ + if (psz <= 8) { + /* Reduce the pred_esz_masks value simply to reduce the + * size of the code generated here. + */ + uint64_t psz_mask = MAKE_64BIT_MASK(0, psz * 8); + temp = tcg_temp_new_i64(); + tcg_gen_ld_i64(temp, cpu_env, pred_full_reg_offset(s, a->pg)); + tcg_gen_andi_i64(temp, temp, pred_esz_masks[esz] & psz_mask); + tcg_gen_brcondi_i64(TCG_COND_EQ, temp, 0, over); + tcg_temp_free_i64(temp); + } else { + TCGv_i32 t32 = tcg_temp_new_i32(); + find_last_active(s, t32, esz, a->pg); + tcg_gen_brcondi_i32(TCG_COND_LT, t32, 0, over); + tcg_temp_free_i32(t32); + } + + /* Load the data. */ + temp = tcg_temp_new_i64(); + tcg_gen_addi_i64(temp, cpu_reg_sp(s, a->rn), a->imm << esz); + tcg_gen_qemu_ld_i64(temp, temp, get_mem_index(s), + s->be_data | dtype_mop[a->dtype]); + + /* Broadcast to *all* elements. */ + tcg_gen_gvec_dup_i64(esz, vec_full_reg_offset(s, a->rd), + vsz, vsz, temp); + tcg_temp_free_i64(temp); + + /* Zero the inactive elements. */ + gen_set_label(over); + do_movz_zpz(s, a->rd, a->rd, a->pg, esz); + return true; +} + +static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, + int msz, int esz, int nreg) +{ + static gen_helper_gvec_mem * const fn_single[4][4] = { + { gen_helper_sve_st1bb_r, gen_helper_sve_st1bh_r, + gen_helper_sve_st1bs_r, gen_helper_sve_st1bd_r }, + { NULL, gen_helper_sve_st1hh_r, + gen_helper_sve_st1hs_r, gen_helper_sve_st1hd_r }, + { NULL, NULL, + gen_helper_sve_st1ss_r, gen_helper_sve_st1sd_r }, + { NULL, NULL, NULL, gen_helper_sve_st1dd_r }, + }; + static gen_helper_gvec_mem * const fn_multiple[3][4] = { + { gen_helper_sve_st2bb_r, gen_helper_sve_st2hh_r, + gen_helper_sve_st2ss_r, gen_helper_sve_st2dd_r }, + { gen_helper_sve_st3bb_r, gen_helper_sve_st3hh_r, + gen_helper_sve_st3ss_r, gen_helper_sve_st3dd_r }, + { gen_helper_sve_st4bb_r, gen_helper_sve_st4hh_r, + gen_helper_sve_st4ss_r, gen_helper_sve_st4dd_r }, + }; + gen_helper_gvec_mem *fn; + + if (nreg == 0) { + /* ST1 */ + fn = fn_single[msz][esz]; + } else { + /* ST2, ST3, ST4 -- msz == esz, enforced by encoding */ + assert(msz == esz); + fn = fn_multiple[nreg - 1][msz]; + } + assert(fn != NULL); + do_mem_zpa(s, zt, pg, addr, fn); +} + +static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a, uint32_t insn) +{ + if (a->rm == 31 || a->msz > a->esz) { + return false; + } + if (sve_access_check(s)) { + TCGv_i64 addr = new_tmp_a64(s); + tcg_gen_muli_i64(addr, cpu_reg(s, a->rm), (a->nreg + 1) << a->msz); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + do_st_zpa(s, a->rd, a->pg, addr, a->msz, a->esz, a->nreg); + } + return true; +} + +static bool trans_ST_zpri(DisasContext *s, arg_rpri_store *a, uint32_t insn) +{ + if (a->msz > a->esz) { + return false; + } + if (sve_access_check(s)) { + int vsz = vec_full_reg_size(s); + int elements = vsz >> a->esz; + TCGv_i64 addr = new_tmp_a64(s); + + tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), + (a->imm * elements * (a->nreg + 1)) << a->msz); + do_st_zpa(s, a->rd, a->pg, addr, a->msz, a->esz, a->nreg); + } + return true; +} + +/* + *** SVE gather loads / scatter stores + */ + +static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, int scale, + TCGv_i64 scalar, gen_helper_gvec_mem_scatter *fn) +{ + unsigned vsz = vec_full_reg_size(s); + TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, scale)); + TCGv_ptr t_zm = tcg_temp_new_ptr(); + TCGv_ptr t_pg = tcg_temp_new_ptr(); + TCGv_ptr t_zt = tcg_temp_new_ptr(); + + tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); + tcg_gen_addi_ptr(t_zm, cpu_env, vec_full_reg_offset(s, zm)); + tcg_gen_addi_ptr(t_zt, cpu_env, vec_full_reg_offset(s, zt)); + fn(cpu_env, t_zt, t_pg, t_zm, scalar, desc); + + tcg_temp_free_ptr(t_zt); + tcg_temp_free_ptr(t_zm); + tcg_temp_free_ptr(t_pg); + tcg_temp_free_i32(desc); +} + +/* Indexed by [ff][xs][u][msz]. */ +static gen_helper_gvec_mem_scatter * const gather_load_fn32[2][2][2][3] = { + { { { gen_helper_sve_ldbss_zsu, + gen_helper_sve_ldhss_zsu, + NULL, }, + { gen_helper_sve_ldbsu_zsu, + gen_helper_sve_ldhsu_zsu, + gen_helper_sve_ldssu_zsu, } }, + { { gen_helper_sve_ldbss_zss, + gen_helper_sve_ldhss_zss, + NULL, }, + { gen_helper_sve_ldbsu_zss, + gen_helper_sve_ldhsu_zss, + gen_helper_sve_ldssu_zss, } } }, + + { { { gen_helper_sve_ldffbss_zsu, + gen_helper_sve_ldffhss_zsu, + NULL, }, + { gen_helper_sve_ldffbsu_zsu, + gen_helper_sve_ldffhsu_zsu, + gen_helper_sve_ldffssu_zsu, } }, + { { gen_helper_sve_ldffbss_zss, + gen_helper_sve_ldffhss_zss, + NULL, }, + { gen_helper_sve_ldffbsu_zss, + gen_helper_sve_ldffhsu_zss, + gen_helper_sve_ldffssu_zss, } } } +}; + +/* Note that we overload xs=2 to indicate 64-bit offset. */ +static gen_helper_gvec_mem_scatter * const gather_load_fn64[2][3][2][4] = { + { { { gen_helper_sve_ldbds_zsu, + gen_helper_sve_ldhds_zsu, + gen_helper_sve_ldsds_zsu, + NULL, }, + { gen_helper_sve_ldbdu_zsu, + gen_helper_sve_ldhdu_zsu, + gen_helper_sve_ldsdu_zsu, + gen_helper_sve_ldddu_zsu, } }, + { { gen_helper_sve_ldbds_zss, + gen_helper_sve_ldhds_zss, + gen_helper_sve_ldsds_zss, + NULL, }, + { gen_helper_sve_ldbdu_zss, + gen_helper_sve_ldhdu_zss, + gen_helper_sve_ldsdu_zss, + gen_helper_sve_ldddu_zss, } }, + { { gen_helper_sve_ldbds_zd, + gen_helper_sve_ldhds_zd, + gen_helper_sve_ldsds_zd, + NULL, }, + { gen_helper_sve_ldbdu_zd, + gen_helper_sve_ldhdu_zd, + gen_helper_sve_ldsdu_zd, + gen_helper_sve_ldddu_zd, } } }, + + { { { gen_helper_sve_ldffbds_zsu, + gen_helper_sve_ldffhds_zsu, + gen_helper_sve_ldffsds_zsu, + NULL, }, + { gen_helper_sve_ldffbdu_zsu, + gen_helper_sve_ldffhdu_zsu, + gen_helper_sve_ldffsdu_zsu, + gen_helper_sve_ldffddu_zsu, } }, + { { gen_helper_sve_ldffbds_zss, + gen_helper_sve_ldffhds_zss, + gen_helper_sve_ldffsds_zss, + NULL, }, + { gen_helper_sve_ldffbdu_zss, + gen_helper_sve_ldffhdu_zss, + gen_helper_sve_ldffsdu_zss, + gen_helper_sve_ldffddu_zss, } }, + { { gen_helper_sve_ldffbds_zd, + gen_helper_sve_ldffhds_zd, + gen_helper_sve_ldffsds_zd, + NULL, }, + { gen_helper_sve_ldffbdu_zd, + gen_helper_sve_ldffhdu_zd, + gen_helper_sve_ldffsdu_zd, + gen_helper_sve_ldffddu_zd, } } } +}; + +static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a, uint32_t insn) +{ + gen_helper_gvec_mem_scatter *fn = NULL; + + if (!sve_access_check(s)) { + return true; + } + + switch (a->esz) { + case MO_32: + fn = gather_load_fn32[a->ff][a->xs][a->u][a->msz]; + break; + case MO_64: + fn = gather_load_fn64[a->ff][a->xs][a->u][a->msz]; + break; + } + assert(fn != NULL); + + do_mem_zpz(s, a->rd, a->pg, a->rm, a->scale * a->msz, + cpu_reg_sp(s, a->rn), fn); + return true; +} + +static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a, uint32_t insn) +{ + gen_helper_gvec_mem_scatter *fn = NULL; + TCGv_i64 imm; + + if (a->esz < a->msz || (a->esz == a->msz && !a->u)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + switch (a->esz) { + case MO_32: + fn = gather_load_fn32[a->ff][0][a->u][a->msz]; + break; + case MO_64: + fn = gather_load_fn64[a->ff][2][a->u][a->msz]; + break; + } + assert(fn != NULL); + + /* Treat LD1_zpiz (zn[x] + imm) the same way as LD1_zprz (rn + zm[x]) + * by loading the immediate into the scalar parameter. + */ + imm = tcg_const_i64(a->imm << a->msz); + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, fn); + tcg_temp_free_i64(imm); + return true; +} + +/* Indexed by [xs][msz]. */ +static gen_helper_gvec_mem_scatter * const scatter_store_fn32[2][3] = { + { gen_helper_sve_stbs_zsu, + gen_helper_sve_sths_zsu, + gen_helper_sve_stss_zsu, }, + { gen_helper_sve_stbs_zss, + gen_helper_sve_sths_zss, + gen_helper_sve_stss_zss, }, +}; + +/* Note that we overload xs=2 to indicate 64-bit offset. */ +static gen_helper_gvec_mem_scatter * const scatter_store_fn64[3][4] = { + { gen_helper_sve_stbd_zsu, + gen_helper_sve_sthd_zsu, + gen_helper_sve_stsd_zsu, + gen_helper_sve_stdd_zsu, }, + { gen_helper_sve_stbd_zss, + gen_helper_sve_sthd_zss, + gen_helper_sve_stsd_zss, + gen_helper_sve_stdd_zss, }, + { gen_helper_sve_stbd_zd, + gen_helper_sve_sthd_zd, + gen_helper_sve_stsd_zd, + gen_helper_sve_stdd_zd, }, +}; + +static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a, uint32_t insn) +{ + gen_helper_gvec_mem_scatter *fn; + + if (a->esz < a->msz || (a->msz == 0 && a->scale)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + switch (a->esz) { + case MO_32: + fn = scatter_store_fn32[a->xs][a->msz]; + break; + case MO_64: + fn = scatter_store_fn64[a->xs][a->msz]; + break; + default: + g_assert_not_reached(); + } + do_mem_zpz(s, a->rd, a->pg, a->rm, a->scale * a->msz, + cpu_reg_sp(s, a->rn), fn); + return true; +} + +static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a, uint32_t insn) +{ + gen_helper_gvec_mem_scatter *fn = NULL; + TCGv_i64 imm; + + if (a->esz < a->msz) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + switch (a->esz) { + case MO_32: + fn = scatter_store_fn32[0][a->msz]; + break; + case MO_64: + fn = scatter_store_fn64[2][a->msz]; + break; + } + assert(fn != NULL); + + /* Treat ST1_zpiz (zn[x] + imm) the same way as ST1_zprz (rn + zm[x]) + * by loading the immediate into the scalar parameter. + */ + imm = tcg_const_i64(a->imm << a->msz); + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, fn); + tcg_temp_free_i64(imm); + return true; +} + +/* + * Prefetches + */ + +static bool trans_PRF(DisasContext *s, arg_PRF *a, uint32_t insn) +{ + /* Prefetch is a nop within QEMU. */ + (void)sve_access_check(s); + return true; +} + +static bool trans_PRF_rr(DisasContext *s, arg_PRF_rr *a, uint32_t insn) +{ + if (a->rm == 31) { + return false; + } + /* Prefetch is a nop within QEMU. */ + (void)sve_access_check(s); + return true; +} + +/* + * Move Prefix + * + * TODO: The implementation so far could handle predicated merging movprfx. + * The helper functions as written take an extra source register to + * use in the operation, but the result is only written when predication + * succeeds. For unpredicated movprfx, we need to rearrange the helpers + * to allow the final write back to the destination to be unconditional. + * For predicated zeroing movprfx, we need to rearrange the helpers to + * allow the final write back to zero inactives. + * + * In the meantime, just emit the moves. + */ + +static bool trans_MOVPRFX(DisasContext *s, arg_MOVPRFX *a, uint32_t insn) +{ + return do_mov_z(s, a->rd, a->rn); +} + +static bool trans_MOVPRFX_m(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_sel_z(s, a->rd, a->rn, a->rd, a->pg, a->esz); + } + return true; +} + +static bool trans_MOVPRFX_z(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +{ + if (sve_access_check(s)) { + do_movz_zpz(s, a->rd, a->rn, a->pg, a->esz); + } + return true; +} diff --git a/target/arm/translate.c b/target/arm/translate.c index db1ce6510a..f845da7c63 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -995,7 +995,7 @@ static inline void gen_bx_excret_final_code(DisasContext *s) if (is_singlestepping(s)) { gen_singlestep_exception(s); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } gen_set_label(excret_label); /* Yes: this is an exception return. @@ -1100,7 +1100,14 @@ static inline TCGv gen_aa32_addr(DisasContext *s, TCGv_i32 a32, TCGMemOp op) static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, int index, TCGMemOp opc) { - TCGv addr = gen_aa32_addr(s, a32, opc); + TCGv addr; + + if (arm_dc_feature(s, ARM_FEATURE_M) && + !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) { + opc |= MO_ALIGN; + } + + addr = gen_aa32_addr(s, a32, opc); tcg_gen_qemu_ld_i32(val, addr, index, opc); tcg_temp_free(addr); } @@ -1108,7 +1115,14 @@ static void gen_aa32_ld_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, static void gen_aa32_st_i32(DisasContext *s, TCGv_i32 val, TCGv_i32 a32, int index, TCGMemOp opc) { - TCGv addr = gen_aa32_addr(s, a32, opc); + TCGv addr; + + if (arm_dc_feature(s, ARM_FEATURE_M) && + !arm_dc_feature(s, ARM_FEATURE_M_MAIN)) { + opc |= MO_ALIGN; + } + + addr = gen_aa32_addr(s, a32, opc); tcg_gen_qemu_st_i32(val, addr, index, opc); tcg_temp_free(addr); } @@ -3824,38 +3838,56 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) gen_vfp_sqrt(dp); break; case 4: /* vcvtb.f32.f16, vcvtb.f64.f16 */ + { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp_mode = get_ahp_flag(); tmp = gen_vfp_mrs(); tcg_gen_ext16u_i32(tmp, tmp); if (dp) { gen_helper_vfp_fcvt_f16_to_f64(cpu_F0d, tmp, - cpu_env); + fpst, ahp_mode); } else { gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, - cpu_env); + fpst, ahp_mode); } + tcg_temp_free_i32(ahp_mode); + tcg_temp_free_ptr(fpst); tcg_temp_free_i32(tmp); break; + } case 5: /* vcvtt.f32.f16, vcvtt.f64.f16 */ + { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); tmp = gen_vfp_mrs(); tcg_gen_shri_i32(tmp, tmp, 16); if (dp) { gen_helper_vfp_fcvt_f16_to_f64(cpu_F0d, tmp, - cpu_env); + fpst, ahp); } else { gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp, - cpu_env); + fpst, ahp); } tcg_temp_free_i32(tmp); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); break; + } case 6: /* vcvtb.f16.f32, vcvtb.f16.f64 */ + { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); tmp = tcg_temp_new_i32(); + if (dp) { gen_helper_vfp_fcvt_f64_to_f16(tmp, cpu_F0d, - cpu_env); + fpst, ahp); } else { gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, - cpu_env); + fpst, ahp); } + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); gen_mov_F0_vreg(0, rd); tmp2 = gen_vfp_mrs(); tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000); @@ -3863,15 +3895,21 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); gen_vfp_msr(tmp); break; + } case 7: /* vcvtt.f16.f32, vcvtt.f16.f64 */ + { + TCGv_ptr fpst = get_fpstatus_ptr(false); + TCGv_i32 ahp = get_ahp_flag(); tmp = tcg_temp_new_i32(); if (dp) { gen_helper_vfp_fcvt_f64_to_f16(tmp, cpu_F0d, - cpu_env); + fpst, ahp); } else { gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, - cpu_env); + fpst, ahp); } + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); tcg_gen_shli_i32(tmp, tmp, 16); gen_mov_F0_vreg(0, rd); tmp2 = gen_vfp_mrs(); @@ -3880,6 +3918,7 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); gen_vfp_msr(tmp); break; + } case 8: /* cmp */ gen_vfp_cmp(dp); break; @@ -4238,7 +4277,7 @@ static void gen_goto_tb(DisasContext *s, int n, target_ulong dest) if (use_goto_tb(s, dest)) { tcg_gen_goto_tb(n); gen_set_pc_im(s, dest); - tcg_gen_exit_tb((uintptr_t)s->base.tb + n); + tcg_gen_exit_tb(s->base.tb, n); } else { gen_set_pc_im(s, dest); gen_goto_ptr(); @@ -4548,7 +4587,13 @@ static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) * appropriately depending on the new Thumb bit, so it must * be called after storing the new PC. */ + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_cpsr_write_eret(cpu_env, cpsr); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } tcg_temp_free_i32(cpsr); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; @@ -7216,53 +7261,70 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } break; case NEON_2RM_VCVT_F16_F32: + { + TCGv_ptr fpst; + TCGv_i32 ahp; + if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) || q || (rm & 1)) { return 1; } tmp = tcg_temp_new_i32(); tmp2 = tcg_temp_new_i32(); + fpst = get_fpstatus_ptr(true); + ahp = get_ahp_flag(); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 0)); - gen_helper_neon_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, fpst, ahp); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 1)); - gen_helper_neon_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tmp2, cpu_F0s, fpst, ahp); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp2, tmp2, tmp); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 2)); - gen_helper_neon_fcvt_f32_to_f16(tmp, cpu_F0s, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tmp, cpu_F0s, fpst, ahp); tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, 3)); neon_store_reg(rd, 0, tmp2); tmp2 = tcg_temp_new_i32(); - gen_helper_neon_fcvt_f32_to_f16(tmp2, cpu_F0s, cpu_env); + gen_helper_vfp_fcvt_f32_to_f16(tmp2, cpu_F0s, fpst, ahp); tcg_gen_shli_i32(tmp2, tmp2, 16); tcg_gen_or_i32(tmp2, tmp2, tmp); neon_store_reg(rd, 1, tmp2); tcg_temp_free_i32(tmp); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); break; + } case NEON_2RM_VCVT_F32_F16: + { + TCGv_ptr fpst; + TCGv_i32 ahp; if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) || q || (rd & 1)) { return 1; } + fpst = get_fpstatus_ptr(true); + ahp = get_ahp_flag(); tmp3 = tcg_temp_new_i32(); tmp = neon_load_reg(rm, 0); tmp2 = neon_load_reg(rm, 1); tcg_gen_ext16u_i32(tmp3, tmp); - gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, fpst, ahp); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 0)); tcg_gen_shri_i32(tmp3, tmp, 16); - gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, fpst, ahp); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 1)); tcg_temp_free_i32(tmp); tcg_gen_ext16u_i32(tmp3, tmp2); - gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, fpst, ahp); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 2)); tcg_gen_shri_i32(tmp3, tmp2, 16); - gen_helper_neon_fcvt_f16_to_f32(cpu_F0s, tmp3, cpu_env); + gen_helper_vfp_fcvt_f16_to_f32(cpu_F0s, tmp3, fpst, ahp); tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, 3)); tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp3); + tcg_temp_free_i32(ahp); + tcg_temp_free_ptr(fpst); break; + } case NEON_2RM_AESE: case NEON_2RM_AESMC: if (!arm_dc_feature(s, ARM_FEATURE_V8_AES) || ((rm | rd) & 1)) { @@ -7700,9 +7762,10 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) */ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) { - gen_helper_gvec_3_ptr *fn_gvec_ptr; - int rd, rn, rm, rot, size, opr_sz; - TCGv_ptr fpst; + gen_helper_gvec_3 *fn_gvec = NULL; + gen_helper_gvec_3_ptr *fn_gvec_ptr = NULL; + int rd, rn, rm, opr_sz; + int data = 0; bool q; q = extract32(insn, 6, 1); @@ -7715,8 +7778,8 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) if ((insn & 0xfe200f10) == 0xfc200800) { /* VCMLA -- 1111 110R R.1S .... .... 1000 ...0 .... */ - size = extract32(insn, 20, 1); - rot = extract32(insn, 23, 2); + int size = extract32(insn, 20, 1); + data = extract32(insn, 23, 2); /* rot */ if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { return 1; @@ -7724,13 +7787,20 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) fn_gvec_ptr = size ? gen_helper_gvec_fcmlas : gen_helper_gvec_fcmlah; } else if ((insn & 0xfea00f10) == 0xfc800800) { /* VCADD -- 1111 110R 1.0S .... .... 1000 ...0 .... */ - size = extract32(insn, 20, 1); - rot = extract32(insn, 24, 1); + int size = extract32(insn, 20, 1); + data = extract32(insn, 24, 1); /* rot */ if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { return 1; } fn_gvec_ptr = size ? gen_helper_gvec_fcadds : gen_helper_gvec_fcaddh; + } else if ((insn & 0xfeb00f00) == 0xfc200d00) { + /* V[US]DOT -- 1111 1100 0.10 .... .... 1101 .Q.U .... */ + bool u = extract32(insn, 4, 1); + if (!arm_dc_feature(s, ARM_FEATURE_V8_DOTPROD)) { + return 1; + } + fn_gvec = u ? gen_helper_gvec_udot_b : gen_helper_gvec_sdot_b; } else { return 1; } @@ -7745,12 +7815,19 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) } opr_sz = (1 + q) * 8; - fpst = get_fpstatus_ptr(1); - tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), - vfp_reg_offset(1, rn), - vfp_reg_offset(1, rm), fpst, - opr_sz, opr_sz, rot, fn_gvec_ptr); - tcg_temp_free_ptr(fpst); + if (fn_gvec_ptr) { + TCGv_ptr fpst = get_fpstatus_ptr(1); + tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), fpst, + opr_sz, opr_sz, data, fn_gvec_ptr); + tcg_temp_free_ptr(fpst); + } else { + tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), + opr_sz, opr_sz, data, fn_gvec); + } return 0; } @@ -7764,26 +7841,52 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) { - int rd, rn, rm, rot, size, opr_sz; - TCGv_ptr fpst; + gen_helper_gvec_3 *fn_gvec = NULL; + gen_helper_gvec_3_ptr *fn_gvec_ptr = NULL; + int rd, rn, rm, opr_sz, data; bool q; q = extract32(insn, 6, 1); VFP_DREG_D(rd, insn); VFP_DREG_N(rn, insn); - VFP_DREG_M(rm, insn); if ((rd | rn) & q) { return 1; } if ((insn & 0xff000f10) == 0xfe000800) { /* VCMLA (indexed) -- 1111 1110 S.RR .... .... 1000 ...0 .... */ - rot = extract32(insn, 20, 2); - size = extract32(insn, 23, 1); - if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) - || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { + int rot = extract32(insn, 20, 2); + int size = extract32(insn, 23, 1); + int index; + + if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA)) { return 1; } + if (size == 0) { + if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + return 1; + } + /* For fp16, rm is just Vm, and index is M. */ + rm = extract32(insn, 0, 4); + index = extract32(insn, 5, 1); + } else { + /* For fp32, rm is the usual M:Vm, and index is 0. */ + VFP_DREG_M(rm, insn); + index = 0; + } + data = (index << 2) | rot; + fn_gvec_ptr = (size ? gen_helper_gvec_fcmlas_idx + : gen_helper_gvec_fcmlah_idx); + } else if ((insn & 0xffb00f00) == 0xfe200d00) { + /* V[US]DOT -- 1111 1110 0.10 .... .... 1101 .Q.U .... */ + int u = extract32(insn, 4, 1); + if (!arm_dc_feature(s, ARM_FEATURE_V8_DOTPROD)) { + return 1; + } + fn_gvec = u ? gen_helper_gvec_udot_idx_b : gen_helper_gvec_sdot_idx_b; + /* rm is just Vm, and index is M. */ + data = extract32(insn, 5, 1); /* index */ + rm = extract32(insn, 0, 4); } else { return 1; } @@ -7798,14 +7901,19 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) } opr_sz = (1 + q) * 8; - fpst = get_fpstatus_ptr(1); - tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), - vfp_reg_offset(1, rn), - vfp_reg_offset(1, rm), fpst, - opr_sz, opr_sz, rot, - size ? gen_helper_gvec_fcmlas_idx - : gen_helper_gvec_fcmlah_idx); - tcg_temp_free_ptr(fpst); + if (fn_gvec_ptr) { + TCGv_ptr fpst = get_fpstatus_ptr(1); + tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), fpst, + opr_sz, opr_sz, data, fn_gvec_ptr); + tcg_temp_free_ptr(fpst); + } else { + tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), + vfp_reg_offset(1, rn), + vfp_reg_offset(1, rm), + opr_sz, opr_sz, data, fn_gvec); + } return 0; } @@ -9843,7 +9951,13 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (exc_return) { /* Restore CPSR from SPSR. */ tmp = load_cpu_field(spsr); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_cpsr_write_eret(cpu_env, tmp); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } tcg_temp_free_i32(tmp); /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; @@ -9911,14 +10025,15 @@ static bool thumb_insn_is_16bit(DisasContext *s, uint32_t insn) * end up actually treating this as two 16-bit insns, though, * if it's half of a bl/blx pair that might span a page boundary. */ - if (arm_dc_feature(s, ARM_FEATURE_THUMB2)) { + if (arm_dc_feature(s, ARM_FEATURE_THUMB2) || + arm_dc_feature(s, ARM_FEATURE_M)) { /* Thumb2 cores (including all M profile ones) always treat * 32-bit insns as 32-bit. */ return false; } - if ((insn >> 11) == 0x1e && (s->pc < s->next_page_start - 3)) { + if ((insn >> 11) == 0x1e && s->pc - s->page_start < TARGET_PAGE_SIZE - 3) { /* 0b1111_0xxx_xxxx_xxxx : BL/BLX prefix, and the suffix * is not on the next page; we merge this into a 32-bit * insn. @@ -10031,10 +10146,38 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) int conds; int logic_cc; - /* The only 32 bit insn that's allowed for Thumb1 is the combined - * BL/BLX prefix and suffix. + /* + * ARMv6-M supports a limited subset of Thumb2 instructions. + * Other Thumb1 architectures allow only 32-bit + * combined BL/BLX prefix and suffix. */ - if ((insn & 0xf800e800) != 0xf000e800) { + if (arm_dc_feature(s, ARM_FEATURE_M) && + !arm_dc_feature(s, ARM_FEATURE_V7)) { + int i; + bool found = false; + static const uint32_t armv6m_insn[] = {0xf3808000 /* msr */, + 0xf3b08040 /* dsb */, + 0xf3b08050 /* dmb */, + 0xf3b08060 /* isb */, + 0xf3e08000 /* mrs */, + 0xf000d000 /* bl */}; + static const uint32_t armv6m_mask[] = {0xffe0d000, + 0xfff0d0f0, + 0xfff0d0f0, + 0xfff0d0f0, + 0xffe0d000, + 0xf800d000}; + + for (i = 0; i < ARRAY_SIZE(armv6m_insn); i++) { + if ((insn & armv6m_mask[i]) == armv6m_insn[i]) { + found = true; + break; + } + } + if (!found) { + goto illegal_op; + } + } else if ((insn & 0xf800e800) != 0xf000e800) { ARCH(6T2); } @@ -10783,8 +10926,23 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) /* Coprocessor. */ if (arm_dc_feature(s, ARM_FEATURE_M)) { /* We don't currently implement M profile FP support, - * so this entire space should give a NOCP fault. + * so this entire space should give a NOCP fault, with + * the exception of the v8M VLLDM and VLSTM insns, which + * must be NOPs in Secure state and UNDEF in Nonsecure state. */ + if (arm_dc_feature(s, ARM_FEATURE_V8) && + (insn & 0xffa00f00) == 0xec200a00) { + /* 0b1110_1100_0x1x_xxxx_xxxx_1010_xxxx_xxxx + * - VLLDM, VLSTM + * We choose to UNDEF if the RAZ bits are non-zero. + */ + if (!s->v8m_secure || (insn & 0x0040f0ff)) { + goto illegal_op; + } + /* Just NOP since FP support is not implemented */ + break; + } + /* All other insns: NOCP */ gen_exception_insn(s, 4, EXCP_NOCP, syn_uncategorized(), default_exception_el(s)); break; @@ -10940,7 +11098,10 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) } break; case 3: /* Special control operations. */ - ARCH(7); + if (!arm_dc_feature(s, ARM_FEATURE_V7) && + !arm_dc_feature(s, ARM_FEATURE_M)) { + goto illegal_op; + } op = (insn >> 4) & 0xf; switch (op) { case 2: /* clrex */ @@ -12216,8 +12377,7 @@ static bool insn_crosses_page(CPUARMState *env, DisasContext *s) return !thumb_insn_is_16bit(s, insn); } -static int arm_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cs, int max_insns) +static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cs->env_ptr; @@ -12274,19 +12434,18 @@ static int arm_tr_init_disas_context(DisasContextBase *dcbase, dc->is_ldex = false; dc->ss_same_el = false; /* Can't be true since EL_d must be AArch64 */ - dc->next_page_start = - (dc->base.pc_first & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + dc->page_start = dc->base.pc_first & TARGET_PAGE_MASK; /* If architectural single step active, limit to 1. */ if (is_singlestepping(dc)) { - max_insns = 1; + dc->base.max_insns = 1; } /* ARM is a fixed-length ISA. Bound the number of insns to execute to those left on the page. */ if (!dc->thumb) { - int bound = (dc->next_page_start - dc->base.pc_first) / 4; - max_insns = MIN(max_insns, bound); + int bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + dc->base.max_insns = MIN(dc->base.max_insns, bound); } cpu_F0s = tcg_temp_new_i32(); @@ -12297,8 +12456,6 @@ static int arm_tr_init_disas_context(DisasContextBase *dcbase, cpu_V1 = cpu_F1d; /* FIXME: cpu_M0 can probably be the same as cpu_V0. */ cpu_M0 = tcg_temp_new_i64(); - - return max_insns; } static void arm_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) @@ -12557,8 +12714,8 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * but isn't very efficient). */ if (dc->base.is_jmp == DISAS_NEXT - && (dc->pc >= dc->next_page_start - || (dc->pc >= dc->next_page_start - 3 + && (dc->pc - dc->page_start >= TARGET_PAGE_SIZE + || (dc->pc - dc->page_start >= TARGET_PAGE_SIZE - 3 && insn_crosses_page(env, dc)))) { dc->base.is_jmp = DISAS_TOO_MANY; } @@ -12634,7 +12791,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* fall through */ default: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_NORETURN: /* nothing more to generate */ @@ -12649,7 +12806,7 @@ static void arm_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) /* The helper doesn't necessarily throw an exception, but we * must go back to the main loop to check for interrupts anyway. */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; } case DISAS_WFE: diff --git a/target/arm/translate.h b/target/arm/translate.h index 4428c98e2e..45f04244be 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -9,7 +9,7 @@ typedef struct DisasContext { DisasContextBase base; target_ulong pc; - target_ulong next_page_start; + target_ulong page_start; uint32_t insn; /* Nonzero if this instruction has been conditionally skipped. */ int condjmp; @@ -177,4 +177,16 @@ void arm_free_cc(DisasCompare *cmp); void arm_jump_cc(DisasCompare *cmp, TCGLabel *label); void arm_gen_test_cc(int cc, TCGLabel *label); +/* Return state of Alternate Half-precision flag, caller frees result */ +static inline TCGv_i32 get_ahp_flag(void) +{ + TCGv_i32 ret = tcg_temp_new_i32(); + + tcg_gen_ld_i32(ret, cpu_env, + offsetof(CPUARMState, vfp.xregs[ARM_VFP_FPSCR])); + tcg_gen_extract_i32(ret, ret, 26, 1); + + return ret; +} + #endif /* TARGET_ARM_TRANSLATE_H */ diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index ec705cfca5..37f338732e 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" @@ -195,6 +194,197 @@ void HELPER(gvec_qrdmlsh_s32)(void *vd, void *vn, void *vm, clear_tail(d, opr_sz, simd_maxsz(desc)); } +/* Integer 8 and 16-bit dot-product. + * + * Note that for the loops herein, host endianness does not matter + * with respect to the ordering of data within the 64-bit lanes. + * All elements are treated equally, no matter where they are. + */ + +void HELPER(gvec_sdot_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *d = vd; + int8_t *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] += n[i * 4 + 0] * m[i * 4 + 0] + + n[i * 4 + 1] * m[i * 4 + 1] + + n[i * 4 + 2] * m[i * 4 + 2] + + n[i * 4 + 3] * m[i * 4 + 3]; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_udot_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint32_t *d = vd; + uint8_t *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 4; ++i) { + d[i] += n[i * 4 + 0] * m[i * 4 + 0] + + n[i * 4 + 1] * m[i * 4 + 1] + + n[i * 4 + 2] * m[i * 4 + 2] + + n[i * 4 + 3] * m[i * 4 + 3]; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_sdot_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd; + int16_t *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] += (int64_t)n[i * 4 + 0] * m[i * 4 + 0] + + (int64_t)n[i * 4 + 1] * m[i * 4 + 1] + + (int64_t)n[i * 4 + 2] * m[i * 4 + 2] + + (int64_t)n[i * 4 + 3] * m[i * 4 + 3]; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_udot_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc); + uint64_t *d = vd; + uint16_t *n = vn, *m = vm; + + for (i = 0; i < opr_sz / 8; ++i) { + d[i] += (uint64_t)n[i * 4 + 0] * m[i * 4 + 0] + + (uint64_t)n[i * 4 + 1] * m[i * 4 + 1] + + (uint64_t)n[i * 4 + 2] * m[i * 4 + 2] + + (uint64_t)n[i * 4 + 3] * m[i * 4 + 3]; + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_sdot_idx_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, segend, opr_sz = simd_oprsz(desc), opr_sz_4 = opr_sz / 4; + intptr_t index = simd_data(desc); + uint32_t *d = vd; + int8_t *n = vn; + int8_t *m_indexed = (int8_t *)vm + index * 4; + + /* Notice the special case of opr_sz == 8, from aa64/aa32 advsimd. + * Otherwise opr_sz is a multiple of 16. + */ + segend = MIN(4, opr_sz_4); + i = 0; + do { + int8_t m0 = m_indexed[i * 4 + 0]; + int8_t m1 = m_indexed[i * 4 + 1]; + int8_t m2 = m_indexed[i * 4 + 2]; + int8_t m3 = m_indexed[i * 4 + 3]; + + do { + d[i] += n[i * 4 + 0] * m0 + + n[i * 4 + 1] * m1 + + n[i * 4 + 2] * m2 + + n[i * 4 + 3] * m3; + } while (++i < segend); + segend = i + 4; + } while (i < opr_sz_4); + + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_udot_idx_b)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, segend, opr_sz = simd_oprsz(desc), opr_sz_4 = opr_sz / 4; + intptr_t index = simd_data(desc); + uint32_t *d = vd; + uint8_t *n = vn; + uint8_t *m_indexed = (uint8_t *)vm + index * 4; + + /* Notice the special case of opr_sz == 8, from aa64/aa32 advsimd. + * Otherwise opr_sz is a multiple of 16. + */ + segend = MIN(4, opr_sz_4); + i = 0; + do { + uint8_t m0 = m_indexed[i * 4 + 0]; + uint8_t m1 = m_indexed[i * 4 + 1]; + uint8_t m2 = m_indexed[i * 4 + 2]; + uint8_t m3 = m_indexed[i * 4 + 3]; + + do { + d[i] += n[i * 4 + 0] * m0 + + n[i * 4 + 1] * m1 + + n[i * 4 + 2] * m2 + + n[i * 4 + 3] * m3; + } while (++i < segend); + segend = i + 4; + } while (i < opr_sz_4); + + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_sdot_idx_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc), opr_sz_8 = opr_sz / 8; + intptr_t index = simd_data(desc); + uint64_t *d = vd; + int16_t *n = vn; + int16_t *m_indexed = (int16_t *)vm + index * 4; + + /* This is supported by SVE only, so opr_sz is always a multiple of 16. + * Process the entire segment all at once, writing back the results + * only after we've consumed all of the inputs. + */ + for (i = 0; i < opr_sz_8 ; i += 2) { + uint64_t d0, d1; + + d0 = n[i * 4 + 0] * (int64_t)m_indexed[i * 4 + 0]; + d0 += n[i * 4 + 1] * (int64_t)m_indexed[i * 4 + 1]; + d0 += n[i * 4 + 2] * (int64_t)m_indexed[i * 4 + 2]; + d0 += n[i * 4 + 3] * (int64_t)m_indexed[i * 4 + 3]; + d1 = n[i * 4 + 4] * (int64_t)m_indexed[i * 4 + 0]; + d1 += n[i * 4 + 5] * (int64_t)m_indexed[i * 4 + 1]; + d1 += n[i * 4 + 6] * (int64_t)m_indexed[i * 4 + 2]; + d1 += n[i * 4 + 7] * (int64_t)m_indexed[i * 4 + 3]; + + d[i + 0] += d0; + d[i + 1] += d1; + } + + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + +void HELPER(gvec_udot_idx_h)(void *vd, void *vn, void *vm, uint32_t desc) +{ + intptr_t i, opr_sz = simd_oprsz(desc), opr_sz_8 = opr_sz / 8; + intptr_t index = simd_data(desc); + uint64_t *d = vd; + uint16_t *n = vn; + uint16_t *m_indexed = (uint16_t *)vm + index * 4; + + /* This is supported by SVE only, so opr_sz is always a multiple of 16. + * Process the entire segment all at once, writing back the results + * only after we've consumed all of the inputs. + */ + for (i = 0; i < opr_sz_8 ; i += 2) { + uint64_t d0, d1; + + d0 = n[i * 4 + 0] * (uint64_t)m_indexed[i * 4 + 0]; + d0 += n[i * 4 + 1] * (uint64_t)m_indexed[i * 4 + 1]; + d0 += n[i * 4 + 2] * (uint64_t)m_indexed[i * 4 + 2]; + d0 += n[i * 4 + 3] * (uint64_t)m_indexed[i * 4 + 3]; + d1 = n[i * 4 + 4] * (uint64_t)m_indexed[i * 4 + 0]; + d1 += n[i * 4 + 5] * (uint64_t)m_indexed[i * 4 + 1]; + d1 += n[i * 4 + 6] * (uint64_t)m_indexed[i * 4 + 2]; + d1 += n[i * 4 + 7] * (uint64_t)m_indexed[i * 4 + 3]; + + d[i + 0] += d0; + d[i + 1] += d1; + } + + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, void *vfpst, uint32_t desc) { @@ -318,23 +508,29 @@ void HELPER(gvec_fcmlah_idx)(void *vd, void *vn, void *vm, float_status *fpst = vfpst; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); uint32_t neg_real = flip ^ neg_imag; - uintptr_t i; - float16 e1 = m[H2(flip)]; - float16 e3 = m[H2(1 - flip)]; + intptr_t elements = opr_sz / sizeof(float16); + intptr_t eltspersegment = 16 / sizeof(float16); + intptr_t i, j; /* Shift boolean to the sign bit so we can xor to negate. */ neg_real <<= 15; neg_imag <<= 15; - e1 ^= neg_real; - e3 ^= neg_imag; - for (i = 0; i < opr_sz / 2; i += 2) { - float16 e2 = n[H2(i + flip)]; - float16 e4 = e2; + for (i = 0; i < elements; i += eltspersegment) { + float16 mr = m[H2(i + 2 * index + 0)]; + float16 mi = m[H2(i + 2 * index + 1)]; + float16 e1 = neg_real ^ (flip ? mi : mr); + float16 e3 = neg_imag ^ (flip ? mr : mi); - d[H2(i)] = float16_muladd(e2, e1, d[H2(i)], 0, fpst); - d[H2(i + 1)] = float16_muladd(e4, e3, d[H2(i + 1)], 0, fpst); + for (j = i; j < i + eltspersegment; j += 2) { + float16 e2 = n[H2(j + flip)]; + float16 e4 = e2; + + d[H2(j)] = float16_muladd(e2, e1, d[H2(j)], 0, fpst); + d[H2(j + 1)] = float16_muladd(e4, e3, d[H2(j + 1)], 0, fpst); + } } clear_tail(d, opr_sz, simd_maxsz(desc)); } @@ -378,23 +574,29 @@ void HELPER(gvec_fcmlas_idx)(void *vd, void *vn, void *vm, float_status *fpst = vfpst; intptr_t flip = extract32(desc, SIMD_DATA_SHIFT, 1); uint32_t neg_imag = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 2, 2); uint32_t neg_real = flip ^ neg_imag; - uintptr_t i; - float32 e1 = m[H4(flip)]; - float32 e3 = m[H4(1 - flip)]; + intptr_t elements = opr_sz / sizeof(float32); + intptr_t eltspersegment = 16 / sizeof(float32); + intptr_t i, j; /* Shift boolean to the sign bit so we can xor to negate. */ neg_real <<= 31; neg_imag <<= 31; - e1 ^= neg_real; - e3 ^= neg_imag; - for (i = 0; i < opr_sz / 4; i += 2) { - float32 e2 = n[H4(i + flip)]; - float32 e4 = e2; + for (i = 0; i < elements; i += eltspersegment) { + float32 mr = m[H4(i + 2 * index + 0)]; + float32 mi = m[H4(i + 2 * index + 1)]; + float32 e1 = neg_real ^ (flip ? mi : mr); + float32 e3 = neg_imag ^ (flip ? mr : mi); - d[H4(i)] = float32_muladd(e2, e1, d[H4(i)], 0, fpst); - d[H4(i + 1)] = float32_muladd(e4, e3, d[H4(i + 1)], 0, fpst); + for (j = i; j < i + eltspersegment; j += 2) { + float32 e2 = n[H4(j + flip)]; + float32 e4 = e2; + + d[H4(j)] = float32_muladd(e2, e1, d[H4(j)], 0, fpst); + d[H4(j + 1)] = float32_muladd(e4, e3, d[H4(j + 1)], 0, fpst); + } } clear_tail(d, opr_sz, simd_maxsz(desc)); } @@ -427,3 +629,140 @@ void HELPER(gvec_fcmlad)(void *vd, void *vn, void *vm, } clear_tail(d, opr_sz, simd_maxsz(desc)); } + +#define DO_2OP(NAME, FUNC, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + TYPE *d = vd, *n = vn; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(n[i], stat); \ + } \ +} + +DO_2OP(gvec_frecpe_h, helper_recpe_f16, float16) +DO_2OP(gvec_frecpe_s, helper_recpe_f32, float32) +DO_2OP(gvec_frecpe_d, helper_recpe_f64, float64) + +DO_2OP(gvec_frsqrte_h, helper_rsqrte_f16, float16) +DO_2OP(gvec_frsqrte_s, helper_rsqrte_f32, float32) +DO_2OP(gvec_frsqrte_d, helper_rsqrte_f64, float64) + +#undef DO_2OP + +/* Floating-point trigonometric starting value. + * See the ARM ARM pseudocode function FPTrigSMul. + */ +static float16 float16_ftsmul(float16 op1, uint16_t op2, float_status *stat) +{ + float16 result = float16_mul(op1, op1, stat); + if (!float16_is_any_nan(result)) { + result = float16_set_sign(result, op2 & 1); + } + return result; +} + +static float32 float32_ftsmul(float32 op1, uint32_t op2, float_status *stat) +{ + float32 result = float32_mul(op1, op1, stat); + if (!float32_is_any_nan(result)) { + result = float32_set_sign(result, op2 & 1); + } + return result; +} + +static float64 float64_ftsmul(float64 op1, uint64_t op2, float_status *stat) +{ + float64 result = float64_mul(op1, op1, stat); + if (!float64_is_any_nan(result)) { + result = float64_set_sign(result, op2 & 1); + } + return result; +} + +#define DO_3OP(NAME, FUNC, TYPE) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ + d[i] = FUNC(n[i], m[i], stat); \ + } \ +} + +DO_3OP(gvec_fadd_h, float16_add, float16) +DO_3OP(gvec_fadd_s, float32_add, float32) +DO_3OP(gvec_fadd_d, float64_add, float64) + +DO_3OP(gvec_fsub_h, float16_sub, float16) +DO_3OP(gvec_fsub_s, float32_sub, float32) +DO_3OP(gvec_fsub_d, float64_sub, float64) + +DO_3OP(gvec_fmul_h, float16_mul, float16) +DO_3OP(gvec_fmul_s, float32_mul, float32) +DO_3OP(gvec_fmul_d, float64_mul, float64) + +DO_3OP(gvec_ftsmul_h, float16_ftsmul, float16) +DO_3OP(gvec_ftsmul_s, float32_ftsmul, float32) +DO_3OP(gvec_ftsmul_d, float64_ftsmul, float64) + +#ifdef TARGET_AARCH64 + +DO_3OP(gvec_recps_h, helper_recpsf_f16, float16) +DO_3OP(gvec_recps_s, helper_recpsf_f32, float32) +DO_3OP(gvec_recps_d, helper_recpsf_f64, float64) + +DO_3OP(gvec_rsqrts_h, helper_rsqrtsf_f16, float16) +DO_3OP(gvec_rsqrts_s, helper_rsqrtsf_f32, float32) +DO_3OP(gvec_rsqrts_d, helper_rsqrtsf_f64, float64) + +#endif +#undef DO_3OP + +/* For the indexed ops, SVE applies the index per 128-bit vector segment. + * For AdvSIMD, there is of course only one such vector segment. + */ + +#define DO_MUL_IDX(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ +{ \ + intptr_t i, j, oprsz = simd_oprsz(desc), segment = 16 / sizeof(TYPE); \ + intptr_t idx = simd_data(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ + TYPE mm = m[H(i + idx)]; \ + for (j = 0; j < segment; j++) { \ + d[i + j] = TYPE##_mul(n[i + j], mm, stat); \ + } \ + } \ +} + +DO_MUL_IDX(gvec_fmul_idx_h, float16, H2) +DO_MUL_IDX(gvec_fmul_idx_s, float32, H4) +DO_MUL_IDX(gvec_fmul_idx_d, float64, ) + +#undef DO_MUL_IDX + +#define DO_FMLA_IDX(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, \ + void *stat, uint32_t desc) \ +{ \ + intptr_t i, j, oprsz = simd_oprsz(desc), segment = 16 / sizeof(TYPE); \ + TYPE op1_neg = extract32(desc, SIMD_DATA_SHIFT, 1); \ + intptr_t idx = desc >> (SIMD_DATA_SHIFT + 1); \ + TYPE *d = vd, *n = vn, *m = vm, *a = va; \ + op1_neg <<= (8 * sizeof(TYPE) - 1); \ + for (i = 0; i < oprsz / sizeof(TYPE); i += segment) { \ + TYPE mm = m[H(i + idx)]; \ + for (j = 0; j < segment; j++) { \ + d[i + j] = TYPE##_muladd(n[i + j] ^ op1_neg, \ + mm, a[i + j], 0, stat); \ + } \ + } \ +} + +DO_FMLA_IDX(gvec_fmla_idx_h, float16, H2) +DO_FMLA_IDX(gvec_fmla_idx_s, float32, H4) +DO_FMLA_IDX(gvec_fmla_idx_d, float64, ) + +#undef DO_FMLA_IDX diff --git a/target/cris/cpu.c b/target/cris/cpu.c index db8d0884a1..a23aba2688 100644 --- a/target/cris/cpu.c +++ b/target/cris/cpu.c @@ -26,7 +26,6 @@ #include "cpu.h" #include "qemu-common.h" #include "mmu.h" -#include "exec/exec-all.h" static void cris_cpu_set_pc(CPUState *cs, vaddr value) diff --git a/target/cris/translate.c b/target/cris/translate.c index f51a731db9..4ae1c04daf 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -540,10 +540,10 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(env_pc, dest); - tcg_gen_exit_tb((uintptr_t)dc->tb + n); + tcg_gen_exit_tb(dc->tb, n); } else { tcg_gen_movi_tl(env_pc, dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -3047,7 +3047,7 @@ static unsigned int crisv32_decoder(CPUCRISState *env, DisasContext *dc) return insn_len; } -#include "translate_v10.c" +#include "translate_v10.inc.c" /* * Delay slots on QEMU/CRIS. @@ -3091,7 +3091,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) unsigned int insn_len; struct DisasContext ctx; struct DisasContext *dc = &ctx; - uint32_t next_page_start; + uint32_t page_start; target_ulong npc; int num_insns; int max_insns; @@ -3138,7 +3138,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) dc->cpustate_changed = 0; - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { @@ -3234,7 +3234,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } while (!dc->is_jmp && !dc->cpustate_changed && !tcg_op_buf_full() && !singlestep - && (dc->pc < next_page_start) + && (dc->pc - page_start < TARGET_PAGE_SIZE) && num_insns < max_insns); if (dc->clear_locked_irq) { @@ -3276,7 +3276,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_SWI: case DISAS_TB_JUMP: diff --git a/target/cris/translate_v10.c b/target/cris/translate_v10.inc.c similarity index 100% rename from target/cris/translate_v10.c rename to target/cris/translate_v10.inc.c diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index c261b6b090..00bf444620 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -141,7 +141,6 @@ static void hppa_cpu_initfn(Object *obj) cs->env_ptr = env; cs->exception_index = -1; cpu_hppa_loaded_fr0(env); - set_snan_bit_is_one(true, &env->fp_status); cpu_hppa_put_psw(env, PSW_W); } diff --git a/target/hppa/helper.c b/target/hppa/helper.c index 858ec205b6..6539061e52 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "fpu/softfloat.h" #include "exec/helper-proto.h" diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 787f3d6357..8d5edd3a20 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -19,8 +19,8 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +#include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qom/cpu.h" diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index a3af62daf7..912e8d5be4 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -341,7 +341,6 @@ float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) { float64 ret = float32_to_float64(arg, &env->fp_status); - ret = float64_maybe_silence_nan(ret, &env->fp_status); update_fr0_op(env, GETPC()); return ret; } @@ -349,7 +348,6 @@ float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) { float32 ret = float64_to_float32(arg, &env->fp_status); - ret = float32_maybe_silence_nan(ret, &env->fp_status); update_fr0_op(env, GETPC()); return ret; } diff --git a/target/hppa/translate.c b/target/hppa/translate.c index c532889b1f..ce05d5619d 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -151,13 +151,7 @@ #define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i64 #define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i64 #define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i64 -#if UINTPTR_MAX == UINT32_MAX -# define tcg_gen_trunc_reg_ptr(p, r) \ - tcg_gen_trunc_i64_i32(TCGV_PTR_TO_NAT(p), r) -#else -# define tcg_gen_trunc_reg_ptr(p, r) \ - tcg_gen_mov_i64(TCGV_PTR_TO_NAT(p), r) -#endif +#define tcg_gen_trunc_reg_ptr tcg_gen_trunc_i64_ptr #else #define TCGv_reg TCGv_i32 #define tcg_temp_new tcg_temp_new_i32 @@ -251,13 +245,7 @@ #define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i32 #define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i32 #define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i32 -#if UINTPTR_MAX == UINT32_MAX -# define tcg_gen_trunc_reg_ptr(p, r) \ - tcg_gen_mov_i32(TCGV_PTR_TO_NAT(p), r) -#else -# define tcg_gen_trunc_reg_ptr(p, r) \ - tcg_gen_extu_i32_i64(TCGV_PTR_TO_NAT(p), r) -#endif +#define tcg_gen_trunc_reg_ptr tcg_gen_ext_i32_ptr #endif /* TARGET_REGISTER_BITS */ typedef struct DisasCond { @@ -791,7 +779,7 @@ static void gen_goto_tb(DisasContext *ctx, int which, tcg_gen_goto_tb(which); tcg_gen_movi_reg(cpu_iaoq_f, f); tcg_gen_movi_reg(cpu_iaoq_b, b); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + which); + tcg_gen_exit_tb(ctx->base.tb, which); } else { copy_iaoq_entry(cpu_iaoq_f, f, cpu_iaoq_b); copy_iaoq_entry(cpu_iaoq_b, b, ctx->iaoq_n_var); @@ -2315,7 +2303,7 @@ static DisasJumpType trans_rfi(DisasContext *ctx, uint32_t insn, if (ctx->base.singlestep_enabled) { gen_excp_1(EXCP_DEBUG); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } /* Exit the TB to recognize new interrupts. */ @@ -4681,8 +4669,7 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) return gen_illegal(ctx); } -static int hppa_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cs, int max_insns) +static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); int bound; @@ -4712,14 +4699,12 @@ static int hppa_tr_init_disas_context(DisasContextBase *dcbase, /* Bound the number of instructions by those left on the page. */ bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; - bound = MIN(max_insns, bound); + ctx->base.max_insns = MIN(ctx->base.max_insns, bound); ctx->ntempr = 0; ctx->ntempl = 0; memset(ctx->tempr, 0, sizeof(ctx->tempr)); memset(ctx->templ, 0, sizeof(ctx->templ)); - - return bound; } static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) @@ -4859,7 +4844,7 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) if (ctx->base.singlestep_enabled) { gen_excp_1(EXCP_DEBUG); } else if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); } diff --git a/target/i386/arch_dump.c b/target/i386/arch_dump.c index 35b55fc200..004141fc04 100644 --- a/target/i386/arch_dump.c +++ b/target/i386/arch_dump.c @@ -258,6 +258,12 @@ struct QEMUCPUState { QEMUCPUSegment cs, ds, es, fs, gs, ss; QEMUCPUSegment ldt, tr, gdt, idt; uint64_t cr[5]; + /* + * Fields below are optional and are being added at the end without + * changing the version. External tools may identify their presence + * by checking 'size' field. + */ + uint64_t kernel_gs_base; }; typedef struct QEMUCPUState QEMUCPUState; @@ -315,6 +321,10 @@ static void qemu_get_cpustate(QEMUCPUState *s, CPUX86State *env) s->cr[2] = env->cr[2]; s->cr[3] = env->cr[3]; s->cr[4] = env->cr[4]; + +#ifdef TARGET_X86_64 + s->kernel_gs_base = env->kernelgsbase; +#endif } static inline int cpu_write_qemu_note(WriteCoreDumpFunction f, diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a20fe26573..723e02221e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -18,7 +18,9 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/cutils.h" +#include "qemu/bitops.h" #include "cpu.h" #include "exec/exec-all.h" @@ -40,9 +42,7 @@ #include "qom/qom-qobject.h" #include "sysemu/arch_init.h" -#if defined(CONFIG_KVM) -#include -#endif +#include "standard-headers/asm-x86/kvm_para.h" #include "sysemu/sysemu.h" #include "hw/qdev-properties.h" @@ -56,33 +56,237 @@ #include "disas/capstone.h" +/* Helpers for building CPUID[2] descriptors: */ -/* Cache topology CPUID constants: */ +struct CPUID2CacheDescriptorInfo { + enum CacheType type; + int level; + int size; + int line_size; + int associativity; +}; -/* CPUID Leaf 2 Descriptors */ +/* + * Known CPUID 2 cache descriptors. + * From Intel SDM Volume 2A, CPUID instruction + */ +struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { + [0x06] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 8 * KiB, + .associativity = 4, .line_size = 32, }, + [0x08] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 16 * KiB, + .associativity = 4, .line_size = 32, }, + [0x09] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 32 * KiB, + .associativity = 4, .line_size = 64, }, + [0x0A] = { .level = 1, .type = DATA_CACHE, .size = 8 * KiB, + .associativity = 2, .line_size = 32, }, + [0x0C] = { .level = 1, .type = DATA_CACHE, .size = 16 * KiB, + .associativity = 4, .line_size = 32, }, + [0x0D] = { .level = 1, .type = DATA_CACHE, .size = 16 * KiB, + .associativity = 4, .line_size = 64, }, + [0x0E] = { .level = 1, .type = DATA_CACHE, .size = 24 * KiB, + .associativity = 6, .line_size = 64, }, + [0x1D] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, + .associativity = 2, .line_size = 64, }, + [0x21] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, + .associativity = 8, .line_size = 64, }, + /* lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x22, 0x23 are not included + */ + [0x24] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 16, .line_size = 64, }, + /* lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x25, 0x20 are not included + */ + [0x2C] = { .level = 1, .type = DATA_CACHE, .size = 32 * KiB, + .associativity = 8, .line_size = 64, }, + [0x30] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 32 * KiB, + .associativity = 8, .line_size = 64, }, + [0x41] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, + .associativity = 4, .line_size = 32, }, + [0x42] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, + .associativity = 4, .line_size = 32, }, + [0x43] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 4, .line_size = 32, }, + [0x44] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 4, .line_size = 32, }, + [0x45] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 4, .line_size = 32, }, + [0x46] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, + .associativity = 4, .line_size = 64, }, + [0x47] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, + .associativity = 8, .line_size = 64, }, + [0x48] = { .level = 2, .type = UNIFIED_CACHE, .size = 3 * MiB, + .associativity = 12, .line_size = 64, }, + /* Descriptor 0x49 depends on CPU family/model, so it is not included */ + [0x4A] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, + .associativity = 12, .line_size = 64, }, + [0x4B] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, + .associativity = 16, .line_size = 64, }, + [0x4C] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, + .associativity = 12, .line_size = 64, }, + [0x4D] = { .level = 3, .type = UNIFIED_CACHE, .size = 16 * MiB, + .associativity = 16, .line_size = 64, }, + [0x4E] = { .level = 2, .type = UNIFIED_CACHE, .size = 6 * MiB, + .associativity = 24, .line_size = 64, }, + [0x60] = { .level = 1, .type = DATA_CACHE, .size = 16 * KiB, + .associativity = 8, .line_size = 64, }, + [0x66] = { .level = 1, .type = DATA_CACHE, .size = 8 * KiB, + .associativity = 4, .line_size = 64, }, + [0x67] = { .level = 1, .type = DATA_CACHE, .size = 16 * KiB, + .associativity = 4, .line_size = 64, }, + [0x68] = { .level = 1, .type = DATA_CACHE, .size = 32 * KiB, + .associativity = 4, .line_size = 64, }, + [0x78] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 4, .line_size = 64, }, + /* lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x79, 0x7A, 0x7B, 0x7C are not included. + */ + [0x7D] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 8, .line_size = 64, }, + [0x7F] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 2, .line_size = 64, }, + [0x80] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 8, .line_size = 64, }, + [0x82] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, + .associativity = 8, .line_size = 32, }, + [0x83] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 8, .line_size = 32, }, + [0x84] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 8, .line_size = 32, }, + [0x85] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 8, .line_size = 32, }, + [0x86] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 4, .line_size = 64, }, + [0x87] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 8, .line_size = 64, }, + [0xD0] = { .level = 3, .type = UNIFIED_CACHE, .size = 512 * KiB, + .associativity = 4, .line_size = 64, }, + [0xD1] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 4, .line_size = 64, }, + [0xD2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 4, .line_size = 64, }, + [0xD6] = { .level = 3, .type = UNIFIED_CACHE, .size = 1 * MiB, + .associativity = 8, .line_size = 64, }, + [0xD7] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 8, .line_size = 64, }, + [0xD8] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, + .associativity = 8, .line_size = 64, }, + [0xDC] = { .level = 3, .type = UNIFIED_CACHE, .size = 1.5 * MiB, + .associativity = 12, .line_size = 64, }, + [0xDD] = { .level = 3, .type = UNIFIED_CACHE, .size = 3 * MiB, + .associativity = 12, .line_size = 64, }, + [0xDE] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, + .associativity = 12, .line_size = 64, }, + [0xE2] = { .level = 3, .type = UNIFIED_CACHE, .size = 2 * MiB, + .associativity = 16, .line_size = 64, }, + [0xE3] = { .level = 3, .type = UNIFIED_CACHE, .size = 4 * MiB, + .associativity = 16, .line_size = 64, }, + [0xE4] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, + .associativity = 16, .line_size = 64, }, + [0xEA] = { .level = 3, .type = UNIFIED_CACHE, .size = 12 * MiB, + .associativity = 24, .line_size = 64, }, + [0xEB] = { .level = 3, .type = UNIFIED_CACHE, .size = 18 * MiB, + .associativity = 24, .line_size = 64, }, + [0xEC] = { .level = 3, .type = UNIFIED_CACHE, .size = 24 * MiB, + .associativity = 24, .line_size = 64, }, +}; -#define CPUID_2_L1D_32KB_8WAY_64B 0x2c -#define CPUID_2_L1I_32KB_8WAY_64B 0x30 -#define CPUID_2_L2_2MB_8WAY_64B 0x7d -#define CPUID_2_L3_16MB_16WAY_64B 0x4d +/* + * "CPUID leaf 2 does not report cache descriptor information, + * use CPUID leaf 4 to query cache parameters" + */ +#define CACHE_DESCRIPTOR_UNAVAILABLE 0xFF +/* + * Return a CPUID 2 cache descriptor for a given cache. + * If no known descriptor is found, return CACHE_DESCRIPTOR_UNAVAILABLE + */ +static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) +{ + int i; + + assert(cache->size > 0); + assert(cache->level > 0); + assert(cache->line_size > 0); + assert(cache->associativity > 0); + for (i = 0; i < ARRAY_SIZE(cpuid2_cache_descriptors); i++) { + struct CPUID2CacheDescriptorInfo *d = &cpuid2_cache_descriptors[i]; + if (d->level == cache->level && d->type == cache->type && + d->size == cache->size && d->line_size == cache->line_size && + d->associativity == cache->associativity) { + return i; + } + } + + return CACHE_DESCRIPTOR_UNAVAILABLE; +} /* CPUID Leaf 4 constants: */ /* EAX: */ -#define CPUID_4_TYPE_DCACHE 1 -#define CPUID_4_TYPE_ICACHE 2 -#define CPUID_4_TYPE_UNIFIED 3 +#define CACHE_TYPE_D 1 +#define CACHE_TYPE_I 2 +#define CACHE_TYPE_UNIFIED 3 -#define CPUID_4_LEVEL(l) ((l) << 5) +#define CACHE_LEVEL(l) (l << 5) -#define CPUID_4_SELF_INIT_LEVEL (1 << 8) -#define CPUID_4_FULLY_ASSOC (1 << 9) +#define CACHE_SELF_INIT_LEVEL (1 << 8) /* EDX: */ -#define CPUID_4_NO_INVD_SHARING (1 << 0) -#define CPUID_4_INCLUSIVE (1 << 1) -#define CPUID_4_COMPLEX_IDX (1 << 2) +#define CACHE_NO_INVD_SHARING (1 << 0) +#define CACHE_INCLUSIVE (1 << 1) +#define CACHE_COMPLEX_IDX (1 << 2) + +/* Encode CacheType for CPUID[4].EAX */ +#define CACHE_TYPE(t) (((t) == DATA_CACHE) ? CACHE_TYPE_D : \ + ((t) == INSTRUCTION_CACHE) ? CACHE_TYPE_I : \ + ((t) == UNIFIED_CACHE) ? CACHE_TYPE_UNIFIED : \ + 0 /* Invalid value */) + + +/* Encode cache info for CPUID[4] */ +static void encode_cache_cpuid4(CPUCacheInfo *cache, + int num_apic_ids, int num_cores, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + assert(cache->size == cache->line_size * cache->associativity * + cache->partitions * cache->sets); + + assert(num_apic_ids > 0); + *eax = CACHE_TYPE(cache->type) | + CACHE_LEVEL(cache->level) | + (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0) | + ((num_cores - 1) << 26) | + ((num_apic_ids - 1) << 14); + + assert(cache->line_size > 0); + assert(cache->partitions > 0); + assert(cache->associativity > 0); + /* We don't implement fully-associative caches */ + assert(cache->associativity < cache->sets); + *ebx = (cache->line_size - 1) | + ((cache->partitions - 1) << 12) | + ((cache->associativity - 1) << 22); + + assert(cache->sets > 0); + *ecx = cache->sets - 1; + + *edx = (cache->no_invd_sharing ? CACHE_NO_INVD_SHARING : 0) | + (cache->inclusive ? CACHE_INCLUSIVE : 0) | + (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); +} + +/* Encode cache info for CPUID[0x80000005].ECX or CPUID[0x80000005].EDX */ +static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) +{ + assert(cache->size % 1024 == 0); + assert(cache->lines_per_tag > 0); + assert(cache->associativity > 0); + assert(cache->line_size > 0); + return ((cache->size / 1024) << 24) | (cache->associativity << 16) | + (cache->lines_per_tag << 8) | (cache->line_size); +} #define ASSOC_FULL 0xFF @@ -100,57 +304,341 @@ a == ASSOC_FULL ? 0xF : \ 0 /* invalid value */) +/* + * Encode cache info for CPUID[0x80000006].ECX and CPUID[0x80000006].EDX + * @l3 can be NULL. + */ +static void encode_cache_cpuid80000006(CPUCacheInfo *l2, + CPUCacheInfo *l3, + uint32_t *ecx, uint32_t *edx) +{ + assert(l2->size % 1024 == 0); + assert(l2->associativity > 0); + assert(l2->lines_per_tag > 0); + assert(l2->line_size > 0); + *ecx = ((l2->size / 1024) << 16) | + (AMD_ENC_ASSOC(l2->associativity) << 12) | + (l2->lines_per_tag << 8) | (l2->line_size); -/* Definitions of the hardcoded cache entries we expose: */ + if (l3) { + assert(l3->size % (512 * 1024) == 0); + assert(l3->associativity > 0); + assert(l3->lines_per_tag > 0); + assert(l3->line_size > 0); + *edx = ((l3->size / (512 * 1024)) << 18) | + (AMD_ENC_ASSOC(l3->associativity) << 12) | + (l3->lines_per_tag << 8) | (l3->line_size); + } else { + *edx = 0; + } +} + +/* + * Definitions used for building CPUID Leaf 0x8000001D and 0x8000001E + * Please refer to the AMD64 Architecture Programmer’s Manual Volume 3. + * Define the constants to build the cpu topology. Right now, TOPOEXT + * feature is enabled only on EPYC. So, these constants are based on + * EPYC supported configurations. We may need to handle the cases if + * these values change in future. + */ +/* Maximum core complexes in a node */ +#define MAX_CCX 2 +/* Maximum cores in a core complex */ +#define MAX_CORES_IN_CCX 4 +/* Maximum cores in a node */ +#define MAX_CORES_IN_NODE 8 +/* Maximum nodes in a socket */ +#define MAX_NODES_PER_SOCKET 4 + +/* + * Figure out the number of nodes required to build this config. + * Max cores in a node is 8 + */ +static int nodes_in_socket(int nr_cores) +{ + int nodes; + + nodes = DIV_ROUND_UP(nr_cores, MAX_CORES_IN_NODE); + + /* Hardware does not support config with 3 nodes, return 4 in that case */ + return (nodes == 3) ? 4 : nodes; +} + +/* + * Decide the number of cores in a core complex with the given nr_cores using + * following set constants MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_NODE and + * MAX_NODES_PER_SOCKET. Maintain symmetry as much as possible + * L3 cache is shared across all cores in a core complex. So, this will also + * tell us how many cores are sharing the L3 cache. + */ +static int cores_in_core_complex(int nr_cores) +{ + int nodes; + + /* Check if we can fit all the cores in one core complex */ + if (nr_cores <= MAX_CORES_IN_CCX) { + return nr_cores; + } + /* Get the number of nodes required to build this config */ + nodes = nodes_in_socket(nr_cores); + + /* + * Divide the cores accros all the core complexes + * Return rounded up value + */ + return DIV_ROUND_UP(nr_cores, nodes * MAX_CCX); +} + +/* Encode cache info for CPUID[8000001D] */ +static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, CPUState *cs, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + uint32_t l3_cores; + assert(cache->size == cache->line_size * cache->associativity * + cache->partitions * cache->sets); + + *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | + (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0); + + /* L3 is shared among multiple cores */ + if (cache->level == 3) { + l3_cores = cores_in_core_complex(cs->nr_cores); + *eax |= ((l3_cores * cs->nr_threads) - 1) << 14; + } else { + *eax |= ((cs->nr_threads - 1) << 14); + } + + assert(cache->line_size > 0); + assert(cache->partitions > 0); + assert(cache->associativity > 0); + /* We don't implement fully-associative caches */ + assert(cache->associativity < cache->sets); + *ebx = (cache->line_size - 1) | + ((cache->partitions - 1) << 12) | + ((cache->associativity - 1) << 22); + + assert(cache->sets > 0); + *ecx = cache->sets - 1; + + *edx = (cache->no_invd_sharing ? CACHE_NO_INVD_SHARING : 0) | + (cache->inclusive ? CACHE_INCLUSIVE : 0) | + (cache->complex_indexing ? CACHE_COMPLEX_IDX : 0); +} + +/* Data structure to hold the configuration info for a given core index */ +struct core_topology { + /* core complex id of the current core index */ + int ccx_id; + /* + * Adjusted core index for this core in the topology + * This can be 0,1,2,3 with max 4 cores in a core complex + */ + int core_id; + /* Node id for this core index */ + int node_id; + /* Number of nodes in this config */ + int num_nodes; +}; + +/* + * Build the configuration closely match the EPYC hardware. Using the EPYC + * hardware configuration values (MAX_CCX, MAX_CORES_IN_CCX, MAX_CORES_IN_NODE) + * right now. This could change in future. + * nr_cores : Total number of cores in the config + * core_id : Core index of the current CPU + * topo : Data structure to hold all the config info for this core index + */ +static void build_core_topology(int nr_cores, int core_id, + struct core_topology *topo) +{ + int nodes, cores_in_ccx; + + /* First get the number of nodes required */ + nodes = nodes_in_socket(nr_cores); + + cores_in_ccx = cores_in_core_complex(nr_cores); + + topo->node_id = core_id / (cores_in_ccx * MAX_CCX); + topo->ccx_id = (core_id % (cores_in_ccx * MAX_CCX)) / cores_in_ccx; + topo->core_id = core_id % cores_in_ccx; + topo->num_nodes = nodes; +} + +/* Encode cache info for CPUID[8000001E] */ +static void encode_topo_cpuid8000001e(CPUState *cs, X86CPU *cpu, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + struct core_topology topo = {0}; + unsigned long nodes; + int shift; + + build_core_topology(cs->nr_cores, cpu->core_id, &topo); + *eax = cpu->apic_id; + /* + * CPUID_Fn8000001E_EBX + * 31:16 Reserved + * 15:8 Threads per core (The number of threads per core is + * Threads per core + 1) + * 7:0 Core id (see bit decoding below) + * SMT: + * 4:3 node id + * 2 Core complex id + * 1:0 Core id + * Non SMT: + * 5:4 node id + * 3 Core complex id + * 1:0 Core id + */ + if (cs->nr_threads - 1) { + *ebx = ((cs->nr_threads - 1) << 8) | (topo.node_id << 3) | + (topo.ccx_id << 2) | topo.core_id; + } else { + *ebx = (topo.node_id << 4) | (topo.ccx_id << 3) | topo.core_id; + } + /* + * CPUID_Fn8000001E_ECX + * 31:11 Reserved + * 10:8 Nodes per processor (Nodes per processor is number of nodes + 1) + * 7:0 Node id (see bit decoding below) + * 2 Socket id + * 1:0 Node id + */ + if (topo.num_nodes <= 4) { + *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << 2) | + topo.node_id; + } else { + /* + * Node id fix up. Actual hardware supports up to 4 nodes. But with + * more than 32 cores, we may end up with more than 4 nodes. + * Node id is a combination of socket id and node id. Only requirement + * here is that this number should be unique accross the system. + * Shift the socket id to accommodate more nodes. We dont expect both + * socket id and node id to be big number at the same time. This is not + * an ideal config but we need to to support it. Max nodes we can have + * is 32 (255/8) with 8 cores per node and 255 max cores. We only need + * 5 bits for nodes. Find the left most set bit to represent the total + * number of nodes. find_last_bit returns last set bit(0 based). Left + * shift(+1) the socket id to represent all the nodes. + */ + nodes = topo.num_nodes - 1; + shift = find_last_bit(&nodes, 8); + *ecx = ((topo.num_nodes - 1) << 8) | (cpu->socket_id << (shift + 1)) | + topo.node_id; + } + *edx = 0; +} + +/* + * Definitions of the hardcoded cache entries we expose: + * These are legacy cache values. If there is a need to change any + * of these values please use builtin_x86_defs + */ /* L1 data cache: */ -#define L1D_LINE_SIZE 64 -#define L1D_ASSOCIATIVITY 8 -#define L1D_SETS 64 -#define L1D_PARTITIONS 1 -/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ -#define L1D_DESCRIPTOR CPUID_2_L1D_32KB_8WAY_64B +static CPUCacheInfo legacy_l1d_cache = { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, +}; + /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ -#define L1D_LINES_PER_TAG 1 -#define L1D_SIZE_KB_AMD 64 -#define L1D_ASSOCIATIVITY_AMD 2 +static CPUCacheInfo legacy_l1d_cache_amd = { + .type = DATA_CACHE, + .level = 1, + .size = 64 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 2, + .sets = 512, + .partitions = 1, + .lines_per_tag = 1, + .no_invd_sharing = true, +}; /* L1 instruction cache: */ -#define L1I_LINE_SIZE 64 -#define L1I_ASSOCIATIVITY 8 -#define L1I_SETS 64 -#define L1I_PARTITIONS 1 -/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 32KiB */ -#define L1I_DESCRIPTOR CPUID_2_L1I_32KB_8WAY_64B +static CPUCacheInfo legacy_l1i_cache = { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, +}; + /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ -#define L1I_LINES_PER_TAG 1 -#define L1I_SIZE_KB_AMD 64 -#define L1I_ASSOCIATIVITY_AMD 2 +static CPUCacheInfo legacy_l1i_cache_amd = { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 64 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 2, + .sets = 512, + .partitions = 1, + .lines_per_tag = 1, + .no_invd_sharing = true, +}; /* Level 2 unified cache: */ -#define L2_LINE_SIZE 64 -#define L2_ASSOCIATIVITY 16 -#define L2_SETS 4096 -#define L2_PARTITIONS 1 -/* Size = LINE_SIZE*ASSOCIATIVITY*SETS*PARTITIONS = 4MiB */ +static CPUCacheInfo legacy_l2_cache = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 4 * MiB, + .self_init = 1, + .line_size = 64, + .associativity = 16, + .sets = 4096, + .partitions = 1, + .no_invd_sharing = true, +}; + /*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ -#define L2_DESCRIPTOR CPUID_2_L2_2MB_8WAY_64B +static CPUCacheInfo legacy_l2_cache_cpuid2 = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 2 * MiB, + .line_size = 64, + .associativity = 8, +}; + + /*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ -#define L2_LINES_PER_TAG 1 -#define L2_SIZE_KB_AMD 512 +static CPUCacheInfo legacy_l2_cache_amd = { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .lines_per_tag = 1, + .associativity = 16, + .sets = 512, + .partitions = 1, +}; /* Level 3 unified cache: */ -#define L3_SIZE_KB 0 /* disabled */ -#define L3_ASSOCIATIVITY 0 /* disabled */ -#define L3_LINES_PER_TAG 0 /* disabled */ -#define L3_LINE_SIZE 0 /* disabled */ -#define L3_N_LINE_SIZE 64 -#define L3_N_ASSOCIATIVITY 16 -#define L3_N_SETS 16384 -#define L3_N_PARTITIONS 1 -#define L3_N_DESCRIPTOR CPUID_2_L3_16MB_16WAY_64B -#define L3_N_LINES_PER_TAG 1 -#define L3_N_SIZE_KB_AMD 16384 +static CPUCacheInfo legacy_l3_cache = { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .sets = 16384, + .partitions = 1, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, +}; /* TLB definitions: */ @@ -261,7 +749,7 @@ static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A) #define TCG_EXT4_FEATURES 0 -#define TCG_SVM_FEATURES 0 +#define TCG_SVM_FEATURES CPUID_SVM_NPT #define TCG_KVM_FEATURES 0 #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ @@ -272,7 +760,8 @@ static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, CPUID_7_0_EBX_RDSEED */ -#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_OSPKE | \ +#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | \ + /* CPUID_7_0_ECX_OSPKE is dynamic */ \ CPUID_7_0_ECX_LA57) #define TCG_7_0_EDX_FEATURES 0 #define TCG_APM_FEATURES 0 @@ -322,7 +811,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "fma", "cx16", "xtpr", "pdcm", NULL, "pcid", "dca", "sse4.1", "sse4.2", "x2apic", "movbe", "popcnt", - "tsc-deadline", "aes", "xsave", "osxsave", + "tsc-deadline", "aes", "xsave", NULL /* osxsave */, "avx", "f16c", "rdrand", "hypervisor", }, .cpuid_eax = 1, .cpuid_reg = R_ECX, @@ -416,7 +905,8 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL /* hv_vpindex_access */, NULL /* hv_msr_reset_access */, NULL /* hv_msr_stats_access */, NULL /* hv_reftsc_access */, NULL /* hv_msr_idle_access */, NULL /* hv_msr_frequency_access */, - NULL, NULL, NULL, NULL, + NULL /* hv_msr_debug_access */, NULL /* hv_msr_reenlightenment_access */, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -488,12 +978,12 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_7_0_ECX] = { .feat_names = { NULL, "avx512vbmi", "umip", "pku", - "ospke", NULL, "avx512vbmi2", NULL, + NULL /* ospke */, NULL, "avx512vbmi2", NULL, "gfni", "vaes", "vpclmulqdq", "avx512vnni", "avx512bitalg", NULL, "avx512-vpopcntdq", NULL, "la57", NULL, NULL, NULL, NULL, NULL, "rdpid", NULL, - NULL, NULL, NULL, NULL, + NULL, "cldemote", NULL, NULL, NULL, NULL, NULL, NULL, }, .cpuid_eax = 7, @@ -510,7 +1000,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "spec-ctrl", NULL, - NULL, NULL, NULL, NULL, + NULL, NULL, NULL, "ssbd", }, .cpuid_eax = 7, .cpuid_needs_ecx = true, .cpuid_ecx = 0, @@ -541,7 +1031,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "ibpb", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + "amd-ssbd", "virt-ssbd", "amd-no-ssb", NULL, NULL, NULL, NULL, NULL, }, .cpuid_eax = 0x80000008, @@ -815,6 +1305,57 @@ struct X86CPUDefinition { int stepping; FeatureWordArray features; const char *model_id; + CPUCaches *cache_info; +}; + +static CPUCaches epyc_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 64 * KiB, + .line_size = 64, + .associativity = 4, + .partitions = 1, + .sets = 256, + .lines_per_tag = 1, + .self_init = 1, + .no_invd_sharing = true, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 8 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 8192, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + }, }; static X86CPUDefinition builtin_x86_defs[] = { @@ -1838,6 +2379,48 @@ static X86CPUDefinition builtin_x86_defs[] = { .xlevel = 0x80000008, .model_id = "Intel Xeon Processor (Skylake, IBRS)", }, + { + .name = "KnightsMill", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 133, + .stepping = 0, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SS | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | + CPUID_MMX | CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | + CPUID_MCA | CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | + CPUID_CX8 | CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | + CPUID_PSE | CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | + CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_AVX512F | + CPUID_7_0_EBX_AVX512CD | CPUID_7_0_EBX_AVX512PF | + CPUID_7_0_EBX_AVX512ER, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_AVX512_VPOPCNTDQ, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_AVX512_4VNNIW | CPUID_7_0_EDX_AVX512_4FMAPS, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .xlevel = 0x80000008, + .model_id = "Intel Xeon Phi Processor (Knights Mill)", + }, { .name = "Opteron_G1", .level = 5, @@ -1994,7 +2577,8 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_8000_0001_ECX] = CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | - CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, + CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | + CPUID_EXT3_TOPOEXT, .features[FEAT_7_0_EBX] = CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | @@ -2009,8 +2593,9 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_XSAVE_XGETBV1, .features[FEAT_6_EAX] = CPUID_6_EAX_ARAT, - .xlevel = 0x8000000A, + .xlevel = 0x8000001E, .model_id = "AMD EPYC Processor", + .cache_info = &epyc_cache_info, }, { .name = "EPYC-IBPB", @@ -2038,7 +2623,8 @@ static X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_8000_0001_ECX] = CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | - CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, + CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | + CPUID_EXT3_TOPOEXT, .features[FEAT_8000_0008_EBX] = CPUID_8000_0008_EBX_IBPB, .features[FEAT_7_0_EBX] = @@ -2055,8 +2641,9 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_XSAVE_XGETBV1, .features[FEAT_6_EAX] = CPUID_6_EAX_ARAT, - .xlevel = 0x8000000A, + .xlevel = 0x8000001E, .model_id = "AMD EPYC Processor (with IBPB)", + .cache_info = &epyc_cache_info, }, }; @@ -2249,13 +2836,13 @@ static void host_x86_cpu_class_init(ObjectClass *oc, void *data) xcc->host_cpuid_required = true; xcc->ordering = 8; - if (kvm_enabled()) { - xcc->model_description = - "KVM processor with all supported host features "; - } else if (hvf_enabled()) { - xcc->model_description = - "HVF processor with all supported host features "; - } +#if defined(CONFIG_KVM) + xcc->model_description = + "KVM processor with all supported host features "; +#elif defined(CONFIG_HVF) + xcc->model_description = + "HVF processor with all supported host features "; +#endif } static const TypeInfo host_x86_cpu_type_info = { @@ -2769,17 +3356,21 @@ static void x86_cpu_class_check_missing_features(X86CPUClass *xcc, /* Print all cpuid feature names in featureset */ -static void listflags(FILE *f, fprintf_function print, const char **featureset) +static void listflags(FILE *f, fprintf_function print, GList *features) { - int bit; - bool first = true; + size_t len = 0; + GList *tmp; - for (bit = 0; bit < 32; bit++) { - if (featureset[bit]) { - print(f, "%s%s", first ? "" : " ", featureset[bit]); - first = false; + for (tmp = features; tmp; tmp = tmp->next) { + const char *name = tmp->data; + if ((len + strlen(name) + 1) >= 75) { + print(f, "\n"); + len = 0; } + print(f, "%s%s", len == 0 ? " " : " ", name); + len += strlen(name) + 1; } + print(f, "\n"); } /* Sort alphabetically by type name, respecting X86CPUClass::ordering. */ @@ -2789,15 +3380,19 @@ static gint x86_cpu_list_compare(gconstpointer a, gconstpointer b) ObjectClass *class_b = (ObjectClass *)b; X86CPUClass *cc_a = X86_CPU_CLASS(class_a); X86CPUClass *cc_b = X86_CPU_CLASS(class_b); - const char *name_a, *name_b; + char *name_a, *name_b; + int ret; if (cc_a->ordering != cc_b->ordering) { - return cc_a->ordering - cc_b->ordering; + ret = cc_a->ordering - cc_b->ordering; } else { - name_a = object_class_get_name(class_a); - name_b = object_class_get_name(class_b); - return strcmp(name_a, name_b); + name_a = x86_cpu_class_get_model_name(cc_a); + name_b = x86_cpu_class_get_model_name(cc_b); + ret = strcmp(name_a, name_b); + g_free(name_a); + g_free(name_b); } + return ret; } static GSList *get_sorted_cpu_model_list(void) @@ -2818,7 +3413,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) desc = cc->cpu_def->model_id; } - (*s->cpu_fprintf)(s->file, "x86 %16s %-48s\n", + (*s->cpu_fprintf)(s->file, "x86 %-20s %-48s\n", name, desc); g_free(name); } @@ -2826,26 +3421,35 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) /* list available CPU models and flags */ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) { - int i; + int i, j; CPUListState s = { .file = f, .cpu_fprintf = cpu_fprintf, }; GSList *list; + GList *names = NULL; (*cpu_fprintf)(f, "Available CPUs:\n"); list = get_sorted_cpu_model_list(); g_slist_foreach(list, x86_cpu_list_entry, &s); g_slist_free(list); - (*cpu_fprintf)(f, "\nRecognized CPUID flags:\n"); + names = NULL; for (i = 0; i < ARRAY_SIZE(feature_word_info); i++) { FeatureWordInfo *fw = &feature_word_info[i]; - - (*cpu_fprintf)(f, " "); - listflags(f, cpu_fprintf, fw->feat_names); - (*cpu_fprintf)(f, "\n"); + for (j = 0; j < 32; j++) { + if (fw->feat_names[j]) { + names = g_list_append(names, (gpointer)fw->feat_names[j]); + } + } } + + names = g_list_sort(names, (GCompareFunc)strcmp); + + (*cpu_fprintf)(f, "\nRecognized CPUID flags:\n"); + listflags(f, cpu_fprintf, names); + (*cpu_fprintf)(f, "\n"); + g_list_free(names); } static void x86_cpu_definition_entry(gpointer data, gpointer user_data) @@ -2952,6 +3556,9 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) env->features[w] = def->features[w]; } + /* legacy-cache defaults to 'off' if CPU model provides cache info */ + cpu->legacy_cache = !def->cache_info; + /* Special cases not set in the X86CPUDefinition structs: */ /* TODO: in-kernel irqchip for hvf */ if (kvm_enabled()) { @@ -3301,92 +3908,60 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (!cpu->enable_l3_cache) { *ecx = 0; } else { - *ecx = L3_N_DESCRIPTOR; + *ecx = cpuid2_cache_descriptor(env->cache_info_cpuid2.l3_cache); } - *edx = (L1D_DESCRIPTOR << 16) | \ - (L1I_DESCRIPTOR << 8) | \ - (L2_DESCRIPTOR); + *edx = (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1d_cache) << 16) | + (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1i_cache) << 8) | + (cpuid2_cache_descriptor(env->cache_info_cpuid2.l2_cache)); break; case 4: /* cache info: needed for Core compatibility */ if (cpu->cache_info_passthrough) { host_cpuid(index, count, eax, ebx, ecx, edx); + /* QEMU gives out its own APIC IDs, never pass down bits 31..26. */ *eax &= ~0xFC000000; + if ((*eax & 31) && cs->nr_cores > 1) { + *eax |= (cs->nr_cores - 1) << 26; + } } else { *eax = 0; switch (count) { case 0: /* L1 dcache info */ - *eax |= CPUID_4_TYPE_DCACHE | \ - CPUID_4_LEVEL(1) | \ - CPUID_4_SELF_INIT_LEVEL; - *ebx = (L1D_LINE_SIZE - 1) | \ - ((L1D_PARTITIONS - 1) << 12) | \ - ((L1D_ASSOCIATIVITY - 1) << 22); - *ecx = L1D_SETS - 1; - *edx = CPUID_4_NO_INVD_SHARING; + encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, + 1, cs->nr_cores, + eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ - *eax |= CPUID_4_TYPE_ICACHE | \ - CPUID_4_LEVEL(1) | \ - CPUID_4_SELF_INIT_LEVEL; - *ebx = (L1I_LINE_SIZE - 1) | \ - ((L1I_PARTITIONS - 1) << 12) | \ - ((L1I_ASSOCIATIVITY - 1) << 22); - *ecx = L1I_SETS - 1; - *edx = CPUID_4_NO_INVD_SHARING; + encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, + 1, cs->nr_cores, + eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ - *eax |= CPUID_4_TYPE_UNIFIED | \ - CPUID_4_LEVEL(2) | \ - CPUID_4_SELF_INIT_LEVEL; - if (cs->nr_threads > 1) { - *eax |= (cs->nr_threads - 1) << 14; - } - *ebx = (L2_LINE_SIZE - 1) | \ - ((L2_PARTITIONS - 1) << 12) | \ - ((L2_ASSOCIATIVITY - 1) << 22); - *ecx = L2_SETS - 1; - *edx = CPUID_4_NO_INVD_SHARING; + encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, + cs->nr_threads, cs->nr_cores, + eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - if (!cpu->enable_l3_cache) { - *eax = 0; - *ebx = 0; - *ecx = 0; - *edx = 0; + pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); + if (cpu->enable_l3_cache) { + encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, + (1 << pkg_offset), cs->nr_cores, + eax, ebx, ecx, edx); break; } - *eax |= CPUID_4_TYPE_UNIFIED | \ - CPUID_4_LEVEL(3) | \ - CPUID_4_SELF_INIT_LEVEL; - pkg_offset = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); - *eax |= ((1 << pkg_offset) - 1) << 14; - *ebx = (L3_N_LINE_SIZE - 1) | \ - ((L3_N_PARTITIONS - 1) << 12) | \ - ((L3_N_ASSOCIATIVITY - 1) << 22); - *ecx = L3_N_SETS - 1; - *edx = CPUID_4_INCLUSIVE | CPUID_4_COMPLEX_IDX; - break; + /* fall through */ default: /* end of info */ - *eax = 0; - *ebx = 0; - *ecx = 0; - *edx = 0; + *eax = *ebx = *ecx = *edx = 0; break; } } - - /* QEMU gives out its own APIC IDs, never pass down bits 31..26. */ - if ((*eax & 31) && cs->nr_cores > 1) { - *eax |= (cs->nr_cores - 1) << 26; - } break; case 5: - /* mwait info: needed for Core compatibility */ - *eax = 0; /* Smallest monitor-line size in bytes */ - *ebx = 0; /* Largest monitor-line size in bytes */ - *ecx = CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; - *edx = 0; + /* MONITOR/MWAIT Leaf */ + *eax = cpu->mwait.eax; /* Smallest monitor-line size in bytes */ + *ebx = cpu->mwait.ebx; /* Largest monitor-line size in bytes */ + *ecx = cpu->mwait.ecx; /* flags */ + *edx = cpu->mwait.edx; /* mwait substates */ break; case 6: /* Thermal and Power Leaf */ @@ -3583,10 +4158,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | \ (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); - *ecx = (L1D_SIZE_KB_AMD << 24) | (L1D_ASSOCIATIVITY_AMD << 16) | \ - (L1D_LINES_PER_TAG << 8) | (L1D_LINE_SIZE); - *edx = (L1I_SIZE_KB_AMD << 24) | (L1I_ASSOCIATIVITY_AMD << 16) | \ - (L1I_LINES_PER_TAG << 8) | (L1I_LINE_SIZE); + *ecx = encode_cache_cpuid80000005(env->cache_info_amd.l1d_cache); + *edx = encode_cache_cpuid80000005(env->cache_info_amd.l1i_cache); break; case 0x80000006: /* cache info (L2 cache) */ @@ -3602,18 +4175,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L2_DTLB_4K_ENTRIES << 16) | \ (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | \ (L2_ITLB_4K_ENTRIES); - *ecx = (L2_SIZE_KB_AMD << 16) | \ - (AMD_ENC_ASSOC(L2_ASSOCIATIVITY) << 12) | \ - (L2_LINES_PER_TAG << 8) | (L2_LINE_SIZE); - if (!cpu->enable_l3_cache) { - *edx = ((L3_SIZE_KB / 512) << 18) | \ - (AMD_ENC_ASSOC(L3_ASSOCIATIVITY) << 12) | \ - (L3_LINES_PER_TAG << 8) | (L3_LINE_SIZE); - } else { - *edx = ((L3_N_SIZE_KB_AMD / 512) << 18) | \ - (AMD_ENC_ASSOC(L3_N_ASSOCIATIVITY) << 12) | \ - (L3_N_LINES_PER_TAG << 8) | (L3_N_LINE_SIZE); - } + encode_cache_cpuid80000006(env->cache_info_amd.l2_cache, + cpu->enable_l3_cache ? + env->cache_info_amd.l3_cache : NULL, + ecx, edx); break; case 0x80000007: *eax = 0; @@ -3654,6 +4219,35 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = 0; } break; + case 0x8000001D: + *eax = 0; + switch (count) { + case 0: /* L1 dcache info */ + encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, cs, + eax, ebx, ecx, edx); + break; + case 1: /* L1 icache info */ + encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, cs, + eax, ebx, ecx, edx); + break; + case 2: /* L2 cache info */ + encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, cs, + eax, ebx, ecx, edx); + break; + case 3: /* L3 cache info */ + encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, cs, + eax, ebx, ecx, edx); + break; + default: /* end of info */ + *eax = *ebx = *ecx = *edx = 0; + break; + } + break; + case 0x8000001E: + assert(cpu->core_id <= 255); + encode_topo_cpuid8000001e(cs, cpu, + eax, ebx, ecx, edx); + break; case 0xC0000000: *eax = env->cpuid_xlevel2; *ebx = 0; @@ -4208,13 +4802,25 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) Error *local_err = NULL; static bool ht_warned; - if (xcc->host_cpuid_required && !accel_uses_host_cpuid()) { - char *name = x86_cpu_class_get_model_name(xcc); - error_setg(&local_err, "CPU model '%s' requires KVM", name); - g_free(name); - goto out; + if (xcc->host_cpuid_required) { + if (!accel_uses_host_cpuid()) { + char *name = x86_cpu_class_get_model_name(xcc); + error_setg(&local_err, "CPU model '%s' requires KVM", name); + g_free(name); + goto out; + } + + if (enable_cpu_pm) { + host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, + &cpu->mwait.ecx, &cpu->mwait.edx); + env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR; + } } + /* mwait extended info: needed for Core compatibility */ + /* We always wake on interrupt even if host does not have the capability */ + cpu->mwait.ecx |= CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; + if (cpu->apic_id == UNASSIGNED_APIC_ID) { error_setg(errp, "apic-id property was not initialized properly"); return; @@ -4310,6 +4916,37 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) cpu->phys_bits = 32; } } + + /* Cache information initialization */ + if (!cpu->legacy_cache) { + if (!xcc->cpu_def || !xcc->cpu_def->cache_info) { + char *name = x86_cpu_class_get_model_name(xcc); + error_setg(errp, + "CPU model '%s' doesn't support legacy-cache=off", name); + g_free(name); + return; + } + env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd = + *xcc->cpu_def->cache_info; + } else { + /* Build legacy cache information */ + env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache; + env->cache_info_cpuid2.l1i_cache = &legacy_l1i_cache; + env->cache_info_cpuid2.l2_cache = &legacy_l2_cache_cpuid2; + env->cache_info_cpuid2.l3_cache = &legacy_l3_cache; + + env->cache_info_cpuid4.l1d_cache = &legacy_l1d_cache; + env->cache_info_cpuid4.l1i_cache = &legacy_l1i_cache; + env->cache_info_cpuid4.l2_cache = &legacy_l2_cache; + env->cache_info_cpuid4.l3_cache = &legacy_l3_cache; + + env->cache_info_amd.l1d_cache = &legacy_l1d_cache_amd; + env->cache_info_amd.l1i_cache = &legacy_l1i_cache_amd; + env->cache_info_amd.l2_cache = &legacy_l2_cache_amd; + env->cache_info_amd.l3_cache = &legacy_l3_cache; + } + + cpu_exec_realizefn(cs, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -4358,17 +4995,22 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) qemu_init_vcpu(cs); - /* Only Intel CPUs support hyperthreading. Even though QEMU fixes this - * issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX - * based on inputs (sockets,cores,threads), it is still better to gives + /* + * Most Intel and certain AMD CPUs support hyperthreading. Even though QEMU + * fixes this issue by adjusting CPUID_0000_0001_EBX and CPUID_8000_0008_ECX + * based on inputs (sockets,cores,threads), it is still better to give * users a warning. * * NOTE: the following code has to follow qemu_init_vcpu(). Otherwise * cs->nr_threads hasn't be populated yet and the checking is incorrect. */ - if (!IS_INTEL_CPU(env) && cs->nr_threads > 1 && !ht_warned) { - error_report("AMD CPU doesn't support hyperthreading. Please configure" - " -smp options properly."); + if (IS_AMD_CPU(env) && + !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && + cs->nr_threads > 1 && !ht_warned) { + error_report("This family of AMD CPU doesn't support " + "hyperthreading(%d). Please configure -smp " + "options properly or try enabling topoext feature.", + cs->nr_threads); ht_warned = true; } @@ -4770,6 +5412,8 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("hv-synic", X86CPU, hyperv_synic, false), DEFINE_PROP_BOOL("hv-stimer", X86CPU, hyperv_stimer, false), DEFINE_PROP_BOOL("hv-frequencies", X86CPU, hyperv_frequencies, false), + DEFINE_PROP_BOOL("hv-reenlightenment", X86CPU, hyperv_reenlightenment, false), + DEFINE_PROP_BOOL("hv-tlbflush", X86CPU, hyperv_tlbflush, false), DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true), DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false), DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true), @@ -4791,6 +5435,13 @@ static Property x86_cpu_properties[] = { false), DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true), + DEFINE_PROP_BOOL("x-migrate-smi-count", X86CPU, migrate_smi_count, + true), + /* + * lecacy_cache defaults to true unless the CPU model provides its + * own cache information (see x86_cpu_load_def()). + */ + DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true), /* * From "Requirements for Implementing the Microsoft diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 1b219fafc4..c18863ec7a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -211,6 +211,7 @@ typedef enum X86Seg { #define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */ #define HF2_SMM_INSIDE_NMI_SHIFT 4 /* CPU serving SMI nested inside NMI */ #define HF2_MPX_PR_SHIFT 5 /* BNDCFGx.BNDPRESERVE */ +#define HF2_NPT_SHIFT 6 /* Nested Paging enabled */ #define HF2_GIF_MASK (1 << HF2_GIF_SHIFT) #define HF2_HIF_MASK (1 << HF2_HIF_SHIFT) @@ -218,6 +219,7 @@ typedef enum X86Seg { #define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT) #define HF2_SMM_INSIDE_NMI_MASK (1 << HF2_SMM_INSIDE_NMI_SHIFT) #define HF2_MPX_PR_MASK (1 << HF2_MPX_PR_SHIFT) +#define HF2_NPT_MASK (1 << HF2_NPT_SHIFT) #define CR0_PE_SHIFT 0 #define CR0_MP_SHIFT 1 @@ -351,6 +353,7 @@ typedef enum X86Seg { #define MSR_IA32_FEATURE_CONTROL 0x0000003a #define MSR_TSC_ADJUST 0x0000003b #define MSR_IA32_SPEC_CTRL 0x48 +#define MSR_VIRT_SSBD 0xc001011f #define MSR_IA32_TSCDEADLINE 0x6e0 #define FEATURE_CONTROL_LOCKED (1<<0) @@ -680,12 +683,12 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_ECX_AVX512_VPOPCNTDQ (1U << 14) /* POPCNT for vectors of DW/QW */ #define CPUID_7_0_ECX_LA57 (1U << 16) #define CPUID_7_0_ECX_RDPID (1U << 22) +#define CPUID_7_0_ECX_CLDEMOTE (1U << 25) /* CLDEMOTE Instruction */ #define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */ #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ - -#define KVM_HINTS_DEDICATED (1U << 0) +#define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) /* Speculative Store Bypass Disable */ #define CPUID_8000_0008_EBX_IBPB (1U << 12) /* Indirect Branch Prediction Barrier */ @@ -1044,6 +1047,65 @@ typedef enum TPRAccess { TPR_ACCESS_WRITE, } TPRAccess; +/* Cache information data structures: */ + +enum CacheType { + DATA_CACHE, + INSTRUCTION_CACHE, + UNIFIED_CACHE +}; + +typedef struct CPUCacheInfo { + enum CacheType type; + uint8_t level; + /* Size in bytes */ + uint32_t size; + /* Line size, in bytes */ + uint16_t line_size; + /* + * Associativity. + * Note: representation of fully-associative caches is not implemented + */ + uint8_t associativity; + /* Physical line partitions. CPUID[0x8000001D].EBX, CPUID[4].EBX */ + uint8_t partitions; + /* Number of sets. CPUID[0x8000001D].ECX, CPUID[4].ECX */ + uint32_t sets; + /* + * Lines per tag. + * AMD-specific: CPUID[0x80000005], CPUID[0x80000006]. + * (Is this synonym to @partitions?) + */ + uint8_t lines_per_tag; + + /* Self-initializing cache */ + bool self_init; + /* + * WBINVD/INVD is not guaranteed to act upon lower level caches of + * non-originating threads sharing this cache. + * CPUID[4].EDX[bit 0], CPUID[0x8000001D].EDX[bit 0] + */ + bool no_invd_sharing; + /* + * Cache is inclusive of lower cache levels. + * CPUID[4].EDX[bit 1], CPUID[0x8000001D].EDX[bit 1]. + */ + bool inclusive; + /* + * A complex function is used to index the cache, potentially using all + * address bits. CPUID[4].EDX[bit 2]. + */ + bool complex_indexing; +} CPUCacheInfo; + + +typedef struct CPUCaches { + CPUCacheInfo *l1d_cache; + CPUCacheInfo *l1i_cache; + CPUCacheInfo *l2_cache; + CPUCacheInfo *l3_cache; +} CPUCaches; + typedef struct CPUX86State { /* standard registers */ target_ulong regs[CPU_NB_REGS]; @@ -1149,6 +1211,7 @@ typedef struct CPUX86State { uint32_t pkru; uint64_t spec_ctrl; + uint64_t virt_ssbd; /* End of state preserved by INIT (dummy marker). */ struct {} end_init_save; @@ -1174,6 +1237,9 @@ typedef struct CPUX86State { uint64_t msr_hv_synic_sint[HV_SINT_COUNT]; uint64_t msr_hv_stimer_config[HV_STIMER_COUNT]; uint64_t msr_hv_stimer_count[HV_STIMER_COUNT]; + uint64_t msr_hv_reenlightenment_control; + uint64_t msr_hv_tsc_emulation_control; + uint64_t msr_hv_tsc_emulation_status; uint64_t msr_rtit_ctrl; uint64_t msr_rtit_status; @@ -1201,12 +1267,16 @@ typedef struct CPUX86State { uint16_t intercept_dr_read; uint16_t intercept_dr_write; uint32_t intercept_exceptions; + uint64_t nested_cr3; + uint32_t nested_pg_mode; uint8_t v_tpr; /* KVM states, automatically cleared on reset */ uint8_t nmi_injected; uint8_t nmi_pending; + uintptr_t retaddr; + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; @@ -1229,6 +1299,11 @@ typedef struct CPUX86State { /* Features that were explicitly enabled/disabled */ FeatureWordArray user_features; uint32_t cpuid_model[12]; + /* Cache information for CPUID. When legacy-cache=on, the cache data + * on each CPUID leaf will be different, because we keep compatibility + * with old QEMU versions. + */ + CPUCaches cache_info_cpuid2, cache_info_cpuid4, cache_info_amd; /* MTRRs */ uint64_t mtrr_fixed[11]; @@ -1297,11 +1372,14 @@ struct X86CPU { bool hyperv_synic; bool hyperv_stimer; bool hyperv_frequencies; + bool hyperv_reenlightenment; + bool hyperv_tlbflush; bool check_cpuid; bool enforce_cpuid; bool expose_kvm; bool expose_tcg; bool migratable; + bool migrate_smi_count; bool max_features; /* Enable all supported features automatically */ uint32_t apic_id; @@ -1312,6 +1390,15 @@ struct X86CPU { /* if true the CPUID code directly forward host cache leaves to the guest */ bool cache_info_passthrough; + /* if true the CPUID code directly forwards + * host monitor/mwait leaves to the guest */ + struct { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + } mwait; + /* Features that were filtered out because of missing host capabilities */ uint32_t filtered_features[FEATURE_WORDS]; @@ -1334,6 +1421,11 @@ struct X86CPU { */ bool enable_l3_cache; + /* Compatibility bits for old machine types. + * If true present the old cache topology information + */ + bool legacy_cache; + /* Compatibility bits for old machine types: */ bool enable_cpuid_0xb; @@ -1765,8 +1857,8 @@ void helper_lock_init(void); /* svm_helper.c */ void cpu_svm_check_intercept_param(CPUX86State *env1, uint32_t type, uint64_t param, uintptr_t retaddr); -void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, - uintptr_t retaddr); +void QEMU_NORETURN cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, + uint64_t exit_info_1, uintptr_t retaddr); void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1); /* seg_helper.c */ diff --git a/target/i386/excp_helper.c b/target/i386/excp_helper.c index cb4d1b7d33..37a33d5ae0 100644 --- a/target/i386/excp_helper.c +++ b/target/i386/excp_helper.c @@ -157,6 +157,209 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, #else +static hwaddr get_hphys(CPUState *cs, hwaddr gphys, MMUAccessType access_type, + int *prot) +{ + CPUX86State *env = &X86_CPU(cs)->env; + uint64_t rsvd_mask = PG_HI_RSVD_MASK; + uint64_t ptep, pte; + uint64_t exit_info_1 = 0; + target_ulong pde_addr, pte_addr; + uint32_t page_offset; + int page_size; + + if (likely(!(env->hflags2 & HF2_NPT_MASK))) { + return gphys; + } + + if (!(env->nested_pg_mode & SVM_NPT_NXE)) { + rsvd_mask |= PG_NX_MASK; + } + + if (env->nested_pg_mode & SVM_NPT_PAE) { + uint64_t pde, pdpe; + target_ulong pdpe_addr; + +#ifdef TARGET_X86_64 + if (env->nested_pg_mode & SVM_NPT_LMA) { + uint64_t pml5e; + uint64_t pml4e_addr, pml4e; + + pml5e = env->nested_cr3; + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; + + pml4e_addr = (pml5e & PG_ADDRESS_MASK) + + (((gphys >> 39) & 0x1ff) << 3); + pml4e = x86_ldq_phys(cs, pml4e_addr); + if (!(pml4e & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pml4e & (rsvd_mask | PG_PSE_MASK)) { + goto do_fault_rsvd; + } + if (!(pml4e & PG_ACCESSED_MASK)) { + pml4e |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pml4e_addr, pml4e); + } + ptep &= pml4e ^ PG_NX_MASK; + pdpe_addr = (pml4e & PG_ADDRESS_MASK) + + (((gphys >> 30) & 0x1ff) << 3); + pdpe = x86_ldq_phys(cs, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pdpe & rsvd_mask) { + goto do_fault_rsvd; + } + ptep &= pdpe ^ PG_NX_MASK; + if (!(pdpe & PG_ACCESSED_MASK)) { + pdpe |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pdpe_addr, pdpe); + } + if (pdpe & PG_PSE_MASK) { + /* 1 GB page */ + page_size = 1024 * 1024 * 1024; + pte_addr = pdpe_addr; + pte = pdpe; + goto do_check_protect; + } + } else +#endif + { + pdpe_addr = (env->nested_cr3 & ~0x1f) + ((gphys >> 27) & 0x18); + pdpe = x86_ldq_phys(cs, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + goto do_fault; + } + rsvd_mask |= PG_HI_USER_MASK; + if (pdpe & (rsvd_mask | PG_NX_MASK)) { + goto do_fault_rsvd; + } + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; + } + + pde_addr = (pdpe & PG_ADDRESS_MASK) + (((gphys >> 21) & 0x1ff) << 3); + pde = x86_ldq_phys(cs, pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pde & rsvd_mask) { + goto do_fault_rsvd; + } + ptep &= pde ^ PG_NX_MASK; + if (pde & PG_PSE_MASK) { + /* 2 MB page */ + page_size = 2048 * 1024; + pte_addr = pde_addr; + pte = pde; + goto do_check_protect; + } + /* 4 KB page */ + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pde_addr, pde); + } + pte_addr = (pde & PG_ADDRESS_MASK) + (((gphys >> 12) & 0x1ff) << 3); + pte = x86_ldq_phys(cs, pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pte & rsvd_mask) { + goto do_fault_rsvd; + } + /* combine pde and pte nx, user and rw protections */ + ptep &= pte ^ PG_NX_MASK; + page_size = 4096; + } else { + uint32_t pde; + + /* page directory entry */ + pde_addr = (env->nested_cr3 & ~0xfff) + ((gphys >> 20) & 0xffc); + pde = x86_ldl_phys(cs, pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + goto do_fault; + } + ptep = pde | PG_NX_MASK; + + /* if PSE bit is set, then we use a 4MB page */ + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + page_size = 4096 * 1024; + pte_addr = pde_addr; + + /* Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. + * Leave bits 20-13 in place for setting accessed/dirty bits below. + */ + pte = pde | ((pde & 0x1fe000LL) << (32 - 13)); + rsvd_mask = 0x200000; + goto do_check_protect_pse36; + } + + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pde_addr, pde); + } + + /* page directory entry */ + pte_addr = (pde & ~0xfff) + ((gphys >> 10) & 0xffc); + pte = x86_ldl_phys(cs, pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + goto do_fault; + } + /* combine pde and pte user and rw protections */ + ptep &= pte | PG_NX_MASK; + page_size = 4096; + rsvd_mask = 0; + } + + do_check_protect: + rsvd_mask |= (page_size - 1) & PG_ADDRESS_MASK & ~PG_PSE_PAT_MASK; + do_check_protect_pse36: + if (pte & rsvd_mask) { + goto do_fault_rsvd; + } + ptep ^= PG_NX_MASK; + + if (!(ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + if (ptep & PG_NX_MASK) { + if (access_type == MMU_INST_FETCH) { + goto do_fault_protect; + } + *prot &= ~PAGE_EXEC; + } + if (!(ptep & PG_RW_MASK)) { + if (access_type == MMU_DATA_STORE) { + goto do_fault_protect; + } + *prot &= ~PAGE_WRITE; + } + + pte &= PG_ADDRESS_MASK & ~(page_size - 1); + page_offset = gphys & (page_size - 1); + return pte + page_offset; + + do_fault_rsvd: + exit_info_1 |= SVM_NPTEXIT_RSVD; + do_fault_protect: + exit_info_1 |= SVM_NPTEXIT_P; + do_fault: + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), + gphys); + exit_info_1 |= SVM_NPTEXIT_US; + if (access_type == MMU_DATA_STORE) { + exit_info_1 |= SVM_NPTEXIT_RW; + } else if (access_type == MMU_INST_FETCH) { + exit_info_1 |= SVM_NPTEXIT_ID; + } + if (prot) { + exit_info_1 |= SVM_NPTEXIT_GPA; + } else { /* page table access */ + exit_info_1 |= SVM_NPTEXIT_GPT; + } + cpu_vmexit(env, SVM_EXIT_NPF, exit_info_1, env->retaddr); +} + /* return value: * -1 = cannot handle fault * 0 = nothing more to do @@ -224,6 +427,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, if (la57) { pml5e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 48) & 0x1ff) << 3)) & a20_mask; + pml5e_addr = get_hphys(cs, pml5e_addr, MMU_DATA_STORE, NULL); pml5e = x86_ldq_phys(cs, pml5e_addr); if (!(pml5e & PG_PRESENT_MASK)) { goto do_fault; @@ -243,6 +447,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, pml4e_addr = ((pml5e & PG_ADDRESS_MASK) + (((addr >> 39) & 0x1ff) << 3)) & a20_mask; + pml4e_addr = get_hphys(cs, pml4e_addr, MMU_DATA_STORE, false); pml4e = x86_ldq_phys(cs, pml4e_addr); if (!(pml4e & PG_PRESENT_MASK)) { goto do_fault; @@ -257,6 +462,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, ptep &= pml4e ^ PG_NX_MASK; pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3)) & a20_mask; + pdpe_addr = get_hphys(cs, pdpe_addr, MMU_DATA_STORE, NULL); pdpe = x86_ldq_phys(cs, pdpe_addr); if (!(pdpe & PG_PRESENT_MASK)) { goto do_fault; @@ -282,6 +488,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, /* XXX: load them when cr3 is loaded ? */ pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) & a20_mask; + pdpe_addr = get_hphys(cs, pdpe_addr, MMU_DATA_STORE, false); pdpe = x86_ldq_phys(cs, pdpe_addr); if (!(pdpe & PG_PRESENT_MASK)) { goto do_fault; @@ -295,6 +502,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, pde_addr = ((pdpe & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3)) & a20_mask; + pde_addr = get_hphys(cs, pde_addr, MMU_DATA_STORE, NULL); pde = x86_ldq_phys(cs, pde_addr); if (!(pde & PG_PRESENT_MASK)) { goto do_fault; @@ -317,6 +525,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, } pte_addr = ((pde & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3)) & a20_mask; + pte_addr = get_hphys(cs, pte_addr, MMU_DATA_STORE, NULL); pte = x86_ldq_phys(cs, pte_addr); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; @@ -333,6 +542,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, /* page directory entry */ pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & a20_mask; + pde_addr = get_hphys(cs, pde_addr, MMU_DATA_STORE, NULL); pde = x86_ldl_phys(cs, pde_addr); if (!(pde & PG_PRESENT_MASK)) { goto do_fault; @@ -360,6 +570,7 @@ int x86_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, /* page directory entry */ pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & a20_mask; + pte_addr = get_hphys(cs, pte_addr, MMU_DATA_STORE, NULL); pte = x86_ldl_phys(cs, pte_addr); if (!(pte & PG_PRESENT_MASK)) { goto do_fault; @@ -442,12 +653,13 @@ do_check_protect_pse36: /* align to page_size */ pte &= PG_ADDRESS_MASK & ~(page_size - 1); + page_offset = addr & (page_size - 1); + paddr = get_hphys(cs, pte + page_offset, is_write1, &prot); /* Even if 4MB pages, we map only one 4KB page in the cache to avoid filling it too fast */ vaddr = addr & TARGET_PAGE_MASK; - page_offset = vaddr & (page_size - 1); - paddr = pte + page_offset; + paddr &= TARGET_PAGE_MASK; assert(prot & (1 << is_write1)); tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), diff --git a/target/i386/hax-all.c b/target/i386/hax-all.c index cad7531406..d2e512856b 100644 --- a/target/i386/hax-all.c +++ b/target/i386/hax-all.c @@ -26,8 +26,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/address-spaces.h" -#include "exec/exec-all.h" -#include "exec/ioport.h" #include "qemu-common.h" #include "hax-i386.h" diff --git a/target/i386/hax-darwin.c b/target/i386/hax-darwin.c index acdde476a0..a5426a6dac 100644 --- a/target/i386/hax-darwin.c +++ b/target/i386/hax-darwin.c @@ -257,10 +257,7 @@ int hax_host_setup_vcpu_channel(struct hax_vcpu_state *vcpu) int hax_vcpu_run(struct hax_vcpu_state *vcpu) { - int ret; - - ret = ioctl(vcpu->fd, HAX_VCPU_IOCTL_RUN, NULL); - return ret; + return ioctl(vcpu->fd, HAX_VCPU_IOCTL_RUN, NULL); } int hax_sync_fpu(CPUArchState *env, struct fx_layout *fl, int set) @@ -315,13 +312,12 @@ int hax_sync_vcpu_state(CPUArchState *env, struct vcpu_state_t *state, int set) int hax_inject_interrupt(CPUArchState *env, int vector) { - int ret, fd; + int fd; fd = hax_vcpu_get_fd(env); if (fd <= 0) { return -1; } - ret = ioctl(fd, HAX_VCPU_IOCTL_INTERRUPT, &vector); - return ret; + return ioctl(fd, HAX_VCPU_IOCTL_INTERRUPT, &vector); } diff --git a/target/i386/hax-mem.c b/target/i386/hax-mem.c index f46e85544d..5c37e94caa 100644 --- a/target/i386/hax-mem.c +++ b/target/i386/hax-mem.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/address-spaces.h" -#include "exec/exec-all.h" #include "qemu/error-report.h" #include "target/i386/hax-i386.h" diff --git a/target/i386/hax-windows.c b/target/i386/hax-windows.c index b1ac737ae4..5729ad9b48 100644 --- a/target/i386/hax-windows.c +++ b/target/i386/hax-windows.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "hax-i386.h" /* diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index c36753954b..df69e6d0a7 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -65,8 +65,6 @@ #include #include "exec/address-spaces.h" -#include "exec/exec-all.h" -#include "exec/ioport.h" #include "hw/i386/apic_internal.h" #include "hw/boards.h" #include "qemu/main-loop.h" diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index 4abf3db25e..7099335e89 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -26,9 +26,6 @@ #include #include -#include "exec/address-spaces.h" -#include "exec/exec-all.h" -#include "exec/ioport.h" #include "hw/i386/apic_internal.h" #include "hw/boards.h" #include "qemu/main-loop.h" diff --git a/target/i386/hyperv-proto.h b/target/i386/hyperv-proto.h index cb4d7f2b7a..d6d5a79293 100644 --- a/target/i386/hyperv-proto.h +++ b/target/i386/hyperv-proto.h @@ -35,7 +35,7 @@ #define HV_RESET_AVAILABLE (1u << 7) #define HV_REFERENCE_TSC_AVAILABLE (1u << 9) #define HV_ACCESS_FREQUENCY_MSRS (1u << 11) - +#define HV_ACCESS_REENLIGHTENMENTS_CONTROL (1u << 13) /* * HV_CPUID_FEATURES.EDX bits @@ -58,6 +58,7 @@ #define HV_APIC_ACCESS_RECOMMENDED (1u << 3) #define HV_SYSTEM_RESET_RECOMMENDED (1u << 4) #define HV_RELAXED_TIMING_RECOMMENDED (1u << 5) +#define HV_EX_PROCESSOR_MASKS_RECOMMENDED (1u << 11) /* * Basic virtualized MSRs @@ -129,6 +130,13 @@ #define HV_X64_MSR_CRASH_CTL 0x40000105 #define HV_CRASH_CTL_NOTIFY (1ull << 63) +/* + * Reenlightenment notification MSRs + */ +#define HV_X64_MSR_REENLIGHTENMENT_CONTROL 0x40000106 +#define HV_X64_MSR_TSC_EMULATION_CONTROL 0x40000107 +#define HV_X64_MSR_TSC_EMULATION_STATUS 0x40000108 + /* * Hypercall status code */ diff --git a/target/i386/hyperv.c b/target/i386/hyperv.c index a050c9d2d1..3065d765ed 100644 --- a/target/i386/hyperv.c +++ b/target/i386/hyperv.c @@ -16,6 +16,16 @@ #include "hyperv.h" #include "hyperv-proto.h" +uint32_t hyperv_vp_index(X86CPU *cpu) +{ + return CPU(cpu)->cpu_index; +} + +X86CPU *hyperv_find_vcpu(uint32_t vp_index) +{ + return X86_CPU(qemu_get_cpu(vp_index)); +} + int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) { CPUX86State *env = &cpu->env; @@ -72,7 +82,7 @@ static void kvm_hv_sint_ack_handler(EventNotifier *notifier) } } -HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, +HvSintRoute *kvm_hv_sint_route_create(uint32_t vp_index, uint32_t sint, HvSintAckClb sint_ack_clb) { HvSintRoute *sint_route; @@ -92,7 +102,7 @@ HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, event_notifier_set_handler(&sint_route->sint_ack_notifier, kvm_hv_sint_ack_handler); - gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vcpu_id, sint); + gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint); if (gsi < 0) { goto err_gsi; } @@ -105,7 +115,7 @@ HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, } sint_route->gsi = gsi; sint_route->sint_ack_clb = sint_ack_clb; - sint_route->vcpu_id = vcpu_id; + sint_route->vp_index = vp_index; sint_route->sint = sint; return sint_route; diff --git a/target/i386/hyperv.h b/target/i386/hyperv.h index 0c3b562018..00c9b454bb 100644 --- a/target/i386/hyperv.h +++ b/target/i386/hyperv.h @@ -23,7 +23,7 @@ typedef void (*HvSintAckClb)(HvSintRoute *sint_route); struct HvSintRoute { uint32_t sint; - uint32_t vcpu_id; + uint32_t vp_index; int gsi; EventNotifier sint_set_notifier; EventNotifier sint_ack_notifier; @@ -32,11 +32,14 @@ struct HvSintRoute { int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit); -HvSintRoute *kvm_hv_sint_route_create(uint32_t vcpu_id, uint32_t sint, +HvSintRoute *kvm_hv_sint_route_create(uint32_t vp_index, uint32_t sint, HvSintAckClb sint_ack_clb); void kvm_hv_sint_route_destroy(HvSintRoute *sint_route); int kvm_hv_sint_route_set_sint(HvSintRoute *sint_route); +uint32_t hyperv_vp_index(X86CPU *cpu); +X86CPU *hyperv_find_vcpu(uint32_t vp_index); + #endif diff --git a/target/i386/kvm-stub.c b/target/i386/kvm-stub.c index bda4dc2f0c..e7a673e5db 100644 --- a/target/i386/kvm-stub.c +++ b/target/i386/kvm-stub.c @@ -40,3 +40,8 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, abort(); } #endif + +bool kvm_hv_vpindex_settable(void) +{ + return false; +} diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 6c49954e68..9313602d3d 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -18,7 +18,7 @@ #include #include -#include +#include "standard-headers/asm-x86/kvm_para.h" #include "qemu-common.h" #include "cpu.h" @@ -40,7 +40,6 @@ #include "hw/i386/intel_iommu.h" #include "hw/i386/x86-iommu.h" -#include "exec/ioport.h" #include "hw/pci/pci.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" @@ -86,12 +85,15 @@ static bool has_msr_hv_hypercall; static bool has_msr_hv_crash; static bool has_msr_hv_reset; static bool has_msr_hv_vpindex; +static bool hv_vpindex_settable; static bool has_msr_hv_runtime; static bool has_msr_hv_synic; static bool has_msr_hv_stimer; static bool has_msr_hv_frequencies; +static bool has_msr_hv_reenlightenment; static bool has_msr_xss; static bool has_msr_spec_ctrl; +static bool has_msr_virt_ssbd; static bool has_msr_smi_count; static uint32_t has_architectural_pmu_version; @@ -161,6 +163,11 @@ bool kvm_enable_x2apic(void) has_x2apic_api); } +bool kvm_hv_vpindex_settable(void) +{ + return hv_vpindex_settable; +} + static int kvm_get_tsc(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); @@ -365,12 +372,28 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, if (!kvm_irqchip_in_kernel()) { ret &= ~CPUID_EXT_X2APIC; } + + if (enable_cpu_pm) { + int disable_exits = kvm_check_extension(s, + KVM_CAP_X86_DISABLE_EXITS); + + if (disable_exits & KVM_X86_DISABLE_EXITS_MWAIT) { + ret |= CPUID_EXT_MONITOR; + } + } } else if (function == 6 && reg == R_EAX) { ret |= CPUID_6_EAX_ARAT; /* safe to allow because of emulated APIC */ } else if (function == 7 && index == 0 && reg == R_EBX) { if (host_tsx_blacklisted()) { ret &= ~(CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_HLE); } + } else if (function == 0x80000001 && reg == R_ECX) { + /* + * It's safe to enable TOPOEXT even if it's not returned by + * GET_SUPPORTED_CPUID. Unconditionally enabling TOPOEXT here allows + * us to keep CPU models including TOPOEXT runnable on older kernels. + */ + ret |= CPUID_EXT3_TOPOEXT; } else if (function == 0x80000001 && reg == R_EDX) { /* On Intel, kvm returns cpuid according to the Intel spec, * so add missing bits according to the AMD spec: @@ -385,7 +408,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, ret &= ~(1U << KVM_FEATURE_PV_UNHALT); } } else if (function == KVM_CPUID_FEATURES && reg == R_EDX) { - ret |= KVM_HINTS_DEDICATED; + ret |= 1U << KVM_HINTS_REALTIME; found = 1; } @@ -583,7 +606,9 @@ static bool hyperv_enabled(X86CPU *cpu) cpu->hyperv_vpindex || cpu->hyperv_runtime || cpu->hyperv_synic || - cpu->hyperv_stimer); + cpu->hyperv_stimer || + cpu->hyperv_reenlightenment || + cpu->hyperv_tlbflush); } static int kvm_arch_set_tsc_khz(CPUState *cs) @@ -669,6 +694,16 @@ static int hyperv_handle_properties(CPUState *cs) } env->features[FEAT_HYPERV_EDX] |= HV_GUEST_CRASH_MSR_AVAILABLE; } + if (cpu->hyperv_reenlightenment) { + if (!has_msr_hv_reenlightenment) { + fprintf(stderr, + "Hyper-V Reenlightenment MSRs " + "(requested by 'hv-reenlightenment' cpu flag) " + "are not supported by kernel\n"); + return -ENOSYS; + } + env->features[FEAT_HYPERV_EAX] |= HV_ACCESS_REENLIGHTENMENTS_CONTROL; + } env->features[FEAT_HYPERV_EDX] |= HV_CPU_DYNAMIC_PARTITIONING_AVAILABLE; if (cpu->hyperv_reset) { if (!has_msr_hv_reset) { @@ -716,6 +751,37 @@ static int hyperv_handle_properties(CPUState *cs) return 0; } +static int hyperv_init_vcpu(X86CPU *cpu) +{ + if (cpu->hyperv_vpindex && !hv_vpindex_settable) { + /* + * the kernel doesn't support setting vp_index; assert that its value + * is in sync + */ + int ret; + struct { + struct kvm_msrs info; + struct kvm_msr_entry entries[1]; + } msr_data = { + .info.nmsrs = 1, + .entries[0].index = HV_X64_MSR_VP_INDEX, + }; + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, &msr_data); + if (ret < 0) { + return ret; + } + assert(ret == 1); + + if (msr_data.entries[0].data != hyperv_vp_index(cpu)) { + error_report("kernel's vp_index != QEMU's vp_index"); + return -ENXIO; + } + } + + return 0; +} + static Error *invtsc_mig_blocker; #define KVM_MAX_CPUID_ENTRIES 100 @@ -811,6 +877,18 @@ int kvm_arch_init_vcpu(CPUState *cs) if (cpu->hyperv_vapic) { c->eax |= HV_APIC_ACCESS_RECOMMENDED; } + if (cpu->hyperv_tlbflush) { + if (kvm_check_extension(cs->kvm_state, + KVM_CAP_HYPERV_TLBFLUSH) <= 0) { + fprintf(stderr, "Hyper-V TLB flush support " + "(requested by 'hv-tlbflush' cpu flag) " + " is not supported by kernel\n"); + return -ENOSYS; + } + c->eax |= HV_REMOTE_TLB_FLUSH_RECOMMENDED; + c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED; + } + c->ebx = cpu->hyperv_spinlock_attempts; c = &cpuid_data.entries[cpuid_i++]; @@ -967,9 +1045,32 @@ int kvm_arch_init_vcpu(CPUState *cs) } c = &cpuid_data.entries[cpuid_i++]; - c->function = i; - c->flags = 0; - cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + switch (i) { + case 0x8000001d: + /* Query for all AMD cache information leaves */ + for (j = 0; ; j++) { + c->function = i; + c->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + c->index = j; + cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); + + if (c->eax == 0) { + break; + } + if (cpuid_i == KVM_MAX_CPUID_ENTRIES) { + fprintf(stderr, "cpuid_data is full, no space for " + "cpuid(eax:0x%x,ecx:0x%x)\n", i, j); + abort(); + } + c = &cpuid_data.entries[cpuid_i++]; + } + break; + default: + c->function = i; + c->flags = 0; + cpu_x86_cpuid(env, i, 0, &c->eax, &c->ebx, &c->ecx, &c->edx); + break; + } } /* Call Centaur's CPUID instructions they are supported. */ @@ -1096,6 +1197,11 @@ int kvm_arch_init_vcpu(CPUState *cs) has_msr_tsc_aux = false; } + r = hyperv_init_vcpu(cpu); + if (r) { + goto fail; + } + return 0; fail: @@ -1215,9 +1321,15 @@ static int kvm_get_supported_msrs(KVMState *s) case HV_X64_MSR_TSC_FREQUENCY: has_msr_hv_frequencies = true; break; + case HV_X64_MSR_REENLIGHTENMENT_CONTROL: + has_msr_hv_reenlightenment = true; + break; case MSR_IA32_SPEC_CTRL: has_msr_spec_ctrl = true; break; + case MSR_VIRT_SSBD: + has_msr_virt_ssbd = true; + break; } } } @@ -1281,6 +1393,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2); #endif + hv_vpindex_settable = kvm_check_extension(s, KVM_CAP_HYPERV_VP_INDEX); + ret = kvm_get_supported_msrs(s); if (ret < 0) { return ret; @@ -1339,6 +1453,29 @@ int kvm_arch_init(MachineState *ms, KVMState *s) smram_machine_done.notify = register_smram_listener; qemu_add_machine_init_done_notifier(&smram_machine_done); } + + if (enable_cpu_pm) { + int disable_exits = kvm_check_extension(s, KVM_CAP_X86_DISABLE_EXITS); + int ret; + +/* Work around for kernel header with a typo. TODO: fix header and drop. */ +#if defined(KVM_X86_DISABLE_EXITS_HTL) && !defined(KVM_X86_DISABLE_EXITS_HLT) +#define KVM_X86_DISABLE_EXITS_HLT KVM_X86_DISABLE_EXITS_HTL +#endif + if (disable_exits) { + disable_exits &= (KVM_X86_DISABLE_EXITS_MWAIT | + KVM_X86_DISABLE_EXITS_HLT | + KVM_X86_DISABLE_EXITS_PAUSE); + } + + ret = kvm_vm_enable_cap(s, KVM_CAP_X86_DISABLE_EXITS, 0, + disable_exits); + if (ret < 0) { + error_report("kvm: guest stopping CPU not supported: %s", + strerror(-ret)); + } + } + return 0; } @@ -1485,7 +1622,7 @@ static int kvm_put_fpu(X86CPU *cpu) #define XSAVE_PKRU 672 #define XSAVE_BYTE_OFFSET(word_offset) \ - ((word_offset) * sizeof(((struct kvm_xsave *)0)->region[0])) + ((word_offset) * sizeof_field(struct kvm_xsave, region[0])) #define ASSERT_OFFSET(word_offset, field) \ QEMU_BUILD_BUG_ON(XSAVE_BYTE_OFFSET(word_offset) != \ @@ -1706,6 +1843,10 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_spec_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, env->spec_ctrl); } + if (has_msr_virt_ssbd) { + kvm_msr_entry_add(cpu, MSR_VIRT_SSBD, env->virt_ssbd); + } + #ifdef TARGET_X86_64 if (lm_capable_kernel) { kvm_msr_entry_add(cpu, MSR_CSTAR, env->cstar); @@ -1778,6 +1919,14 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, HV_X64_MSR_REFERENCE_TSC, env->msr_hv_tsc); } + if (cpu->hyperv_reenlightenment) { + kvm_msr_entry_add(cpu, HV_X64_MSR_REENLIGHTENMENT_CONTROL, + env->msr_hv_reenlightenment_control); + kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_CONTROL, + env->msr_hv_tsc_emulation_control); + kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_STATUS, + env->msr_hv_tsc_emulation_status); + } } if (cpu->hyperv_vapic) { kvm_msr_entry_add(cpu, HV_X64_MSR_APIC_ASSIST_PAGE, @@ -1795,6 +1944,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_hv_runtime) { kvm_msr_entry_add(cpu, HV_X64_MSR_VP_RUNTIME, env->msr_hv_runtime); } + if (cpu->hyperv_vpindex && hv_vpindex_settable) { + kvm_msr_entry_add(cpu, HV_X64_MSR_VP_INDEX, hyperv_vp_index(cpu)); + } if (cpu->hyperv_synic) { int j; @@ -2077,8 +2229,9 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_spec_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, 0); } - - + if (has_msr_virt_ssbd) { + kvm_msr_entry_add(cpu, MSR_VIRT_SSBD, 0); + } if (!env->tsc_valid) { kvm_msr_entry_add(cpu, MSR_IA32_TSC, 0); env->tsc_valid = !runstate_is_running(); @@ -2140,6 +2293,11 @@ static int kvm_get_msrs(X86CPU *cpu) if (cpu->hyperv_time) { kvm_msr_entry_add(cpu, HV_X64_MSR_REFERENCE_TSC, 0); } + if (cpu->hyperv_reenlightenment) { + kvm_msr_entry_add(cpu, HV_X64_MSR_REENLIGHTENMENT_CONTROL, 0); + kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_CONTROL, 0); + kvm_msr_entry_add(cpu, HV_X64_MSR_TSC_EMULATION_STATUS, 0); + } if (has_msr_hv_crash) { int j; @@ -2397,6 +2555,15 @@ static int kvm_get_msrs(X86CPU *cpu) env->msr_hv_stimer_count[(index - HV_X64_MSR_STIMER0_COUNT)/2] = msrs[i].data; break; + case HV_X64_MSR_REENLIGHTENMENT_CONTROL: + env->msr_hv_reenlightenment_control = msrs[i].data; + break; + case HV_X64_MSR_TSC_EMULATION_CONTROL: + env->msr_hv_tsc_emulation_control = msrs[i].data; + break; + case HV_X64_MSR_TSC_EMULATION_STATUS: + env->msr_hv_tsc_emulation_status = msrs[i].data; + break; case MSR_MTRRdefType: env->mtrr_deftype = msrs[i].data; break; @@ -2444,6 +2611,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_SPEC_CTRL: env->spec_ctrl = msrs[i].data; break; + case MSR_VIRT_SSBD: + env->virt_ssbd = msrs[i].data; + break; case MSR_IA32_RTIT_CTL: env->msr_rtit_ctrl = msrs[i].data; break; diff --git a/target/i386/kvm_i386.h b/target/i386/kvm_i386.h index 1de9876cd9..3057ba4f7d 100644 --- a/target/i386/kvm_i386.h +++ b/target/i386/kvm_i386.h @@ -30,12 +30,6 @@ #define kvm_pic_in_kernel() 0 #define kvm_ioapic_in_kernel() 0 -/* These constants must never be used at runtime if kvm_enabled() is false. - * They exist so we don't need #ifdefs around KVM-specific code that already - * checks kvm_enabled() properly. - */ -#define KVM_CPUID_FEATURES 0 - #endif /* CONFIG_KVM */ bool kvm_allows_irq0_override(void); @@ -69,4 +63,6 @@ void kvm_put_apicbase(X86CPU *cpu, uint64_t value); bool kvm_enable_x2apic(void); bool kvm_has_x2apic_api(void); + +bool kvm_hv_vpindex_settable(void); #endif diff --git a/target/i386/machine.c b/target/i386/machine.c index bd2d82e91b..084c2c73a8 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -400,7 +400,7 @@ static bool msr_smi_count_needed(void *opaque) X86CPU *cpu = opaque; CPUX86State *env = &cpu->env; - return env->msr_smi_count != 0; + return cpu->migrate_smi_count && env->msr_smi_count != 0; } static const VMStateDescription vmstate_msr_smi_count = { @@ -713,6 +713,29 @@ static const VMStateDescription vmstate_msr_hyperv_stimer = { } }; +static bool hyperv_reenlightenment_enable_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->msr_hv_reenlightenment_control != 0 || + env->msr_hv_tsc_emulation_control != 0 || + env->msr_hv_tsc_emulation_status != 0; +} + +static const VMStateDescription vmstate_msr_hyperv_reenlightenment = { + .name = "cpu/msr_hyperv_reenlightenment", + .version_id = 1, + .minimum_version_id = 1, + .needed = hyperv_reenlightenment_enable_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.msr_hv_reenlightenment_control, X86CPU), + VMSTATE_UINT64(env.msr_hv_tsc_emulation_control, X86CPU), + VMSTATE_UINT64(env.msr_hv_tsc_emulation_status, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + static bool avx512_needed(void *opaque) { X86CPU *cpu = opaque; @@ -893,6 +916,45 @@ static const VMStateDescription vmstate_msr_intel_pt = { } }; +static bool virt_ssbd_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->virt_ssbd != 0; +} + +static const VMStateDescription vmstate_msr_virt_ssbd = { + .name = "cpu/virt_ssbd", + .version_id = 1, + .minimum_version_id = 1, + .needed = virt_ssbd_needed, + .fields = (VMStateField[]){ + VMSTATE_UINT64(env.virt_ssbd, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + +static bool svm_npt_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return !!(env->hflags2 & HF2_NPT_MASK); +} + +static const VMStateDescription vmstate_svm_npt = { + .name = "cpu/svn_npt", + .version_id = 1, + .minimum_version_id = 1, + .needed = svm_npt_needed, + .fields = (VMStateField[]){ + VMSTATE_UINT64(env.nested_cr3, X86CPU), + VMSTATE_UINT32(env.nested_pg_mode, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + VMStateDescription vmstate_x86_cpu = { .name = "cpu", .version_id = 12, @@ -1005,6 +1067,7 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_msr_hyperv_runtime, &vmstate_msr_hyperv_synic, &vmstate_msr_hyperv_stimer, + &vmstate_msr_hyperv_reenlightenment, &vmstate_avx512, &vmstate_xss, &vmstate_tsc_khz, @@ -1015,6 +1078,8 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_spec_ctrl, &vmstate_mcg_ext_ctl, &vmstate_msr_intel_pt, + &vmstate_msr_virt_ssbd, + &vmstate_svm_npt, NULL } }; diff --git a/target/i386/mem_helper.c b/target/i386/mem_helper.c index a8ae694a9c..30c26b9d9c 100644 --- a/target/i386/mem_helper.c +++ b/target/i386/mem_helper.c @@ -202,13 +202,13 @@ void helper_boundl(CPUX86State *env, target_ulong a0, int v) void tlb_fill(CPUState *cs, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; int ret; + env->retaddr = retaddr; ret = x86_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx); if (ret) { - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - raise_exception_err_ra(env, cs->exception_index, env->error_code, retaddr); } } diff --git a/target/i386/misc_helper.c b/target/i386/misc_helper.c index 628f64aad5..78f2020ef2 100644 --- a/target/i386/misc_helper.c +++ b/target/i386/misc_helper.c @@ -447,6 +447,9 @@ void helper_rdmsr(CPUX86State *env) val = env->tsc_aux; break; #endif + case MSR_SMI_COUNT: + val = env->msr_smi_count; + break; case MSR_MTRRphysBase(0): case MSR_MTRRphysBase(1): case MSR_MTRRphysBase(2): diff --git a/target/i386/monitor.c b/target/i386/monitor.c index a890b3c2ab..74a13c571b 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -35,21 +35,28 @@ #include "sev_i386.h" #include "qapi/qapi-commands-misc.h" - -static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, - hwaddr pte, hwaddr mask) +/* Perform linear address sign extension */ +static hwaddr addr_canonical(CPUArchState *env, hwaddr addr) { #ifdef TARGET_X86_64 if (env->cr[4] & CR4_LA57_MASK) { if (addr & (1ULL << 56)) { - addr |= -1LL << 57; + addr |= (hwaddr)-(1LL << 57); } } else { if (addr & (1ULL << 47)) { - addr |= -1LL << 48; + addr |= (hwaddr)-(1LL << 48); } } #endif + return addr; +} + +static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr, + hwaddr pte, hwaddr mask) +{ + addr = addr_canonical(env, addr); + monitor_printf(mon, TARGET_FMT_plx ": " TARGET_FMT_plx " %c%c%c%c%c%c%c%c%c\n", addr, @@ -243,8 +250,8 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict) } } -static void mem_print(Monitor *mon, hwaddr *pstart, - int *plast_prot, +static void mem_print(Monitor *mon, CPUArchState *env, + hwaddr *pstart, int *plast_prot, hwaddr end, int prot) { int prot1; @@ -253,7 +260,9 @@ static void mem_print(Monitor *mon, hwaddr *pstart, if (*pstart != -1) { monitor_printf(mon, TARGET_FMT_plx "-" TARGET_FMT_plx " " TARGET_FMT_plx " %c%c%c\n", - *pstart, end, end - *pstart, + addr_canonical(env, *pstart), + addr_canonical(env, end), + addr_canonical(env, end - *pstart), prot1 & PG_USER_MASK ? 'u' : '-', 'r', prot1 & PG_RW_MASK ? 'w' : '-'); @@ -283,7 +292,7 @@ static void mem_info_32(Monitor *mon, CPUArchState *env) if (pde & PG_PRESENT_MASK) { if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } else { for(l2 = 0; l2 < 1024; l2++) { cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4); @@ -295,16 +304,16 @@ static void mem_info_32(Monitor *mon, CPUArchState *env) } else { prot = 0; } - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0); + mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 32, 0); } static void mem_info_pae32(Monitor *mon, CPUArchState *env) @@ -332,7 +341,7 @@ static void mem_info_pae32(Monitor *mon, CPUArchState *env) if (pde & PG_PSE_MASK) { prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } else { pt_addr = pde & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { @@ -345,21 +354,21 @@ static void mem_info_pae32(Monitor *mon, CPUArchState *env) } else { prot = 0; } - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 32, 0); + mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 32, 0); } @@ -389,7 +398,7 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env) prot = pdpe & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); prot &= pml4e; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } else { pd_addr = pdpe & 0x3fffffffff000ULL; for (l3 = 0; l3 < 512; l3++) { @@ -401,7 +410,8 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env) prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); prot &= pml4e & pdpe; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, + &last_prot, end, prot); } else { pt_addr = pde & 0x3fffffffff000ULL; for (l4 = 0; l4 < 512; l4++) { @@ -418,27 +428,29 @@ static void mem_info_la48(Monitor *mon, CPUArchState *env) } else { prot = 0; } - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, + &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, + &last_prot, end, prot); } } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } else { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 48, 0); + mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 48, 0); } static void mem_info_la57(Monitor *mon, CPUArchState *env) @@ -457,7 +469,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) end = l0 << 48; if (!(pml5e & PG_PRESENT_MASK)) { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -468,7 +480,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) end = (l0 << 48) + (l1 << 39); if (!(pml4e & PG_PRESENT_MASK)) { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -479,7 +491,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) end = (l0 << 48) + (l1 << 39) + (l2 << 30); if (pdpe & PG_PRESENT_MASK) { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -487,7 +499,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) prot = pdpe & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); prot &= pml5e & pml4e; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -498,7 +510,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) end = (l0 << 48) + (l1 << 39) + (l2 << 30) + (l3 << 21); if (pde & PG_PRESENT_MASK) { prot = 0; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -506,7 +518,7 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK); prot &= pml5e & pml4e & pdpe; - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); continue; } @@ -523,14 +535,14 @@ static void mem_info_la57(Monitor *mon, CPUArchState *env) } else { prot = 0; } - mem_print(mon, &start, &last_prot, end, prot); + mem_print(mon, env, &start, &last_prot, end, prot); } } } } } /* Flush last range */ - mem_print(mon, &start, &last_prot, (hwaddr)1 << 57, 0); + mem_print(mon, env, &start, &last_prot, (hwaddr)1 << 57, 0); } #endif /* TARGET_X86_64 */ @@ -658,12 +670,8 @@ void hmp_info_local_apic(Monitor *mon, const QDict *qdict) void hmp_info_io_apic(Monitor *mon, const QDict *qdict) { - if (kvm_irqchip_in_kernel() && - !kvm_irqchip_is_split()) { - kvm_ioapic_dump_state(mon, qdict); - } else { - ioapic_dump_state(mon, qdict); - } + monitor_printf(mon, "This command is obsolete and will be " + "removed soon. Please use 'info pic' instead.\n"); } SevInfo *qmp_query_sev(Error **errp) diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c index 600a4d7586..00301a0c04 100644 --- a/target/i386/seg_helper.c +++ b/target/i386/seg_helper.c @@ -1337,6 +1337,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) ret = true; } else if ((interrupt_request & CPU_INTERRUPT_NMI) && !(env->hflags2 & HF2_NMI_MASK)) { + cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0); cs->interrupt_request &= ~CPU_INTERRUPT_NMI; env->hflags2 |= HF2_NMI_MASK; do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); diff --git a/target/i386/sev.c b/target/i386/sev.c index c01167143f..2395171acf 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -430,7 +430,8 @@ static int sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain, size_t *cert_chain_len) { - guchar *pdh_data, *cert_chain_data; + guchar *pdh_data = NULL; + guchar *cert_chain_data = NULL; struct sev_user_data_pdh_cert_export export = {}; int err, r; @@ -471,8 +472,9 @@ e_free: SevCapability * sev_get_capabilities(void) { - SevCapability *cap; - guchar *pdh_data, *cert_chain_data; + SevCapability *cap = NULL; + guchar *pdh_data = NULL; + guchar *cert_chain_data = NULL; size_t pdh_len = 0, cert_chain_len = 0; uint32_t ebx; int fd; @@ -486,7 +488,7 @@ sev_get_capabilities(void) if (sev_get_pdh_info(fd, &pdh_data, &pdh_len, &cert_chain_data, &cert_chain_len)) { - return NULL; + goto out; } cap = g_new0(SevCapability, 1); @@ -502,9 +504,9 @@ sev_get_capabilities(void) */ cap->reduced_phys_bits = 1; +out: g_free(pdh_data); g_free(cert_chain_data); - close(fd); return cap; } @@ -530,7 +532,7 @@ sev_launch_start(SEVState *s) { gsize sz; int ret = 1; - int fw_error; + int fw_error, rc; QSevGuestInfo *sev = s->sev_info; struct kvm_sev_launch_start *start; guchar *session = NULL, *dh_cert = NULL; @@ -543,7 +545,7 @@ sev_launch_start(SEVState *s) &error_abort); if (sev->session_file) { if (sev_read_file_base64(sev->session_file, &session, &sz) < 0) { - return 1; + goto out; } start->session_uaddr = (unsigned long)session; start->session_len = sz; @@ -551,18 +553,18 @@ sev_launch_start(SEVState *s) if (sev->dh_cert_file) { if (sev_read_file_base64(sev->dh_cert_file, &dh_cert, &sz) < 0) { - return 1; + goto out; } start->dh_uaddr = (unsigned long)dh_cert; start->dh_len = sz; } trace_kvm_sev_launch_start(start->policy, session, dh_cert); - ret = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); - if (ret < 0) { + rc = sev_ioctl(s->sev_fd, KVM_SEV_LAUNCH_START, start, &fw_error); + if (rc < 0) { error_report("%s: LAUNCH_START ret=%d fw_error=%d '%s'", __func__, ret, fw_error, fw_error_to_str(fw_error)); - return 1; + goto out; } object_property_set_int(OBJECT(sev), start->handle, "handle", @@ -570,12 +572,13 @@ sev_launch_start(SEVState *s) sev_set_guest_state(SEV_STATE_LAUNCH_UPDATE); s->handle = start->handle; s->policy = start->policy; + ret = 0; +out: g_free(start); g_free(session); g_free(dh_cert); - - return 0; + return ret; } static int @@ -712,7 +715,7 @@ sev_guest_init(const char *id) uint32_t host_cbitpos; struct sev_user_data_status status = {}; - s = g_new0(SEVState, 1); + sev_state = s = g_new0(SEVState, 1); s->sev_info = lookup_sev_guest_info(id); if (!s->sev_info) { error_report("%s: '%s' is not a valid '%s' object", @@ -720,7 +723,6 @@ sev_guest_init(const char *id) goto err; } - sev_state = s; s->state = SEV_STATE_UNINIT; host_cpuid(0x8000001F, 0, NULL, &ebx, NULL, NULL); diff --git a/target/i386/smm_helper.c b/target/i386/smm_helper.c index 90621e5977..c1c34a75db 100644 --- a/target/i386/smm_helper.c +++ b/target/i386/smm_helper.c @@ -54,6 +54,7 @@ void do_smm_enter(X86CPU *cpu) qemu_log_mask(CPU_LOG_INT, "SMM: enter\n"); log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); + env->msr_smi_count++; env->hflags |= HF_SMM_MASK; if (env->hflags2 & HF2_NMI_MASK) { env->hflags2 |= HF2_SMM_INSIDE_NMI_MASK; diff --git a/target/i386/svm.h b/target/i386/svm.h index 922c8fd39c..23a3a040b8 100644 --- a/target/i386/svm.h +++ b/target/i386/svm.h @@ -130,6 +130,20 @@ #define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1) /* TS and MP */ +#define SVM_NPT_ENABLED (1 << 0) + +#define SVM_NPT_PAE (1 << 0) +#define SVM_NPT_LMA (1 << 1) +#define SVM_NPT_NXE (1 << 2) + +#define SVM_NPTEXIT_P (1ULL << 0) +#define SVM_NPTEXIT_RW (1ULL << 1) +#define SVM_NPTEXIT_US (1ULL << 2) +#define SVM_NPTEXIT_RSVD (1ULL << 3) +#define SVM_NPTEXIT_ID (1ULL << 4) +#define SVM_NPTEXIT_GPA (1ULL << 32) +#define SVM_NPTEXIT_GPT (1ULL << 33) + struct QEMU_PACKED vmcb_control_area { uint16_t intercept_cr_read; uint16_t intercept_cr_write; diff --git a/target/i386/svm_helper.c b/target/i386/svm_helper.c index 350492359c..342ece082f 100644 --- a/target/i386/svm_helper.c +++ b/target/i386/svm_helper.c @@ -62,6 +62,7 @@ void helper_invlpga(CPUX86State *env, int aflag) void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, uintptr_t retaddr) { + assert(0); } void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, @@ -123,6 +124,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) { CPUState *cs = CPU(x86_env_get_cpu(env)); target_ulong addr; + uint64_t nested_ctl; uint32_t event_inj; uint32_t int_ctl; @@ -205,6 +207,26 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) control.intercept_exceptions )); + nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.nested_ctl)); + if (nested_ctl & SVM_NPT_ENABLED) { + env->nested_cr3 = x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, + control.nested_cr3)); + env->hflags2 |= HF2_NPT_MASK; + + env->nested_pg_mode = 0; + if (env->cr[4] & CR4_PAE_MASK) { + env->nested_pg_mode |= SVM_NPT_PAE; + } + if (env->hflags & HF_LMA_MASK) { + env->nested_pg_mode |= SVM_NPT_LMA; + } + if (env->efer & MSR_EFER_NXE) { + env->nested_pg_mode |= SVM_NPT_NXE; + } + } + /* enable intercepts */ env->hflags |= HF_SVMI_MASK; @@ -615,6 +637,7 @@ void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0); } + env->hflags2 &= ~HF2_NPT_MASK; /* Save the VM state in the vmcb */ svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.es), diff --git a/target/i386/translate.c b/target/i386/translate.c index c9ed8dc709..07d185e7b6 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -113,7 +113,7 @@ typedef struct DisasContext { int rex_x, rex_b; #endif int vex_l; /* vex vector length */ - int vex_v; /* vex vvvv register, without 1's compliment. */ + int vex_v; /* vex vvvv register, without 1's complement. */ int ss32; /* 32 bit stack segment */ CCOp cc_op; /* current CC operation */ bool cc_op_dirty; @@ -2193,7 +2193,7 @@ static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) /* jump to same page: we can use a direct jump */ tcg_gen_goto_tb(tb_num); gen_jmp_im(eip); - tcg_gen_exit_tb((uintptr_t)s->base.tb + tb_num); + tcg_gen_exit_tb(s->base.tb, tb_num); s->base.is_jmp = DISAS_NORETURN; } else { /* jump to another page */ @@ -2572,13 +2572,13 @@ do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) gen_helper_debug(cpu_env); } else if (recheck_tf) { gen_helper_rechecking_single_step(cpu_env); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } else if (s->tf) { gen_helper_single_step(cpu_env); } else if (jr) { tcg_gen_lookup_and_goto_ptr(); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } s->base.is_jmp = DISAS_NORETURN; } @@ -4059,34 +4059,26 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, ot = mo_64_32(s->dflag); gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + tcg_gen_mov_tl(cpu_cc_src, cpu_T0); switch (reg & 7) { case 1: /* blsr By,Ey */ + tcg_gen_subi_tl(cpu_T1, cpu_T0, 1); + tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); + break; + case 2: /* blsmsk By,Ey */ + tcg_gen_subi_tl(cpu_T1, cpu_T0, 1); + tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_T1); + break; + case 3: /* blsi By, Ey */ tcg_gen_neg_tl(cpu_T1, cpu_T0); tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); - gen_op_mov_reg_v(ot, s->vex_v, cpu_T0); - gen_op_update2_cc(); - set_cc_op(s, CC_OP_BMILGB + ot); break; - - case 2: /* blsmsk By,Ey */ - tcg_gen_mov_tl(cpu_cc_src, cpu_T0); - tcg_gen_subi_tl(cpu_T0, cpu_T0, 1); - tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); - set_cc_op(s, CC_OP_BMILGB + ot); - break; - - case 3: /* blsi By, Ey */ - tcg_gen_mov_tl(cpu_cc_src, cpu_T0); - tcg_gen_subi_tl(cpu_T0, cpu_T0, 1); - tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); - set_cc_op(s, CC_OP_BMILGB + ot); - break; - default: goto unknown_op; } + tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + gen_op_mov_reg_v(ot, s->vex_v, cpu_T0); + set_cc_op(s, CC_OP_BMILGB + ot); break; default: @@ -7402,7 +7394,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_jmp_im(pc_start - s->cs_base); gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), tcg_const_i32(s->pc - pc_start)); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); s->base.is_jmp = DISAS_NORETURN; break; @@ -7452,8 +7444,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); gen_helper_stgi(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); break; case 0xdd: /* CLGI */ @@ -8402,8 +8395,7 @@ void tcg_x86_init(void) } } -static int i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu, - int max_insns) +static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *dc = container_of(dcbase, DisasContext, base); CPUX86State *env = cpu->env_ptr; @@ -8470,8 +8462,6 @@ static int i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu, cpu_ptr0 = tcg_temp_new_ptr(); cpu_ptr1 = tcg_temp_new_ptr(); cpu_cc_srcT = tcg_temp_local_new(); - - return max_insns; } static void i386_tr_tb_start(DisasContextBase *db, CPUState *cpu) diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h new file mode 100644 index 0000000000..d8d3485976 --- /dev/null +++ b/target/i386/whp-dispatch.h @@ -0,0 +1,56 @@ +#include "windows.h" +#include + +#include +#include + +#ifndef WHP_DISPATCH_H +#define WHP_DISPATCH_H + + +#define LIST_WINHVPLATFORM_FUNCTIONS(X) \ + X(HRESULT, WHvGetCapability, (WHV_CAPABILITY_CODE CapabilityCode, VOID* CapabilityBuffer, UINT32 CapabilityBufferSizeInBytes, UINT32* WrittenSizeInBytes)) \ + X(HRESULT, WHvCreatePartition, (WHV_PARTITION_HANDLE* Partition)) \ + X(HRESULT, WHvSetupPartition, (WHV_PARTITION_HANDLE Partition)) \ + X(HRESULT, WHvDeletePartition, (WHV_PARTITION_HANDLE Partition)) \ + X(HRESULT, WHvGetPartitionProperty, (WHV_PARTITION_HANDLE Partition, WHV_PARTITION_PROPERTY_CODE PropertyCode, VOID* PropertyBuffer, UINT32 PropertyBufferSizeInBytes, UINT32* WrittenSizeInBytes)) \ + X(HRESULT, WHvSetPartitionProperty, (WHV_PARTITION_HANDLE Partition, WHV_PARTITION_PROPERTY_CODE PropertyCode, const VOID* PropertyBuffer, UINT32 PropertyBufferSizeInBytes)) \ + X(HRESULT, WHvMapGpaRange, (WHV_PARTITION_HANDLE Partition, VOID* SourceAddress, WHV_GUEST_PHYSICAL_ADDRESS GuestAddress, UINT64 SizeInBytes, WHV_MAP_GPA_RANGE_FLAGS Flags)) \ + X(HRESULT, WHvUnmapGpaRange, (WHV_PARTITION_HANDLE Partition, WHV_GUEST_PHYSICAL_ADDRESS GuestAddress, UINT64 SizeInBytes)) \ + X(HRESULT, WHvTranslateGva, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, WHV_GUEST_VIRTUAL_ADDRESS Gva, WHV_TRANSLATE_GVA_FLAGS TranslateFlags, WHV_TRANSLATE_GVA_RESULT* TranslationResult, WHV_GUEST_PHYSICAL_ADDRESS* Gpa)) \ + X(HRESULT, WHvCreateVirtualProcessor, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, UINT32 Flags)) \ + X(HRESULT, WHvDeleteVirtualProcessor, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex)) \ + X(HRESULT, WHvRunVirtualProcessor, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, VOID* ExitContext, UINT32 ExitContextSizeInBytes)) \ + X(HRESULT, WHvCancelRunVirtualProcessor, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, UINT32 Flags)) \ + X(HRESULT, WHvGetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, WHV_REGISTER_VALUE* RegisterValues)) \ + X(HRESULT, WHvSetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, const WHV_REGISTER_VALUE* RegisterValues)) \ + + +#define LIST_WINHVEMULATION_FUNCTIONS(X) \ + X(HRESULT, WHvEmulatorCreateEmulator, (const WHV_EMULATOR_CALLBACKS* Callbacks, WHV_EMULATOR_HANDLE* Emulator)) \ + X(HRESULT, WHvEmulatorDestroyEmulator, (WHV_EMULATOR_HANDLE Emulator)) \ + X(HRESULT, WHvEmulatorTryIoEmulation, (WHV_EMULATOR_HANDLE Emulator, VOID* Context, const WHV_VP_EXIT_CONTEXT* VpContext, const WHV_X64_IO_PORT_ACCESS_CONTEXT* IoInstructionContext, WHV_EMULATOR_STATUS* EmulatorReturnStatus)) \ + X(HRESULT, WHvEmulatorTryMmioEmulation, (WHV_EMULATOR_HANDLE Emulator, VOID* Context, const WHV_VP_EXIT_CONTEXT* VpContext, const WHV_MEMORY_ACCESS_CONTEXT* MmioInstructionContext, WHV_EMULATOR_STATUS* EmulatorReturnStatus)) \ + + +#define WHP_DEFINE_TYPE(return_type, function_name, signature) \ + typedef return_type (WINAPI *function_name ## _t) signature; + +#define WHP_DECLARE_MEMBER(return_type, function_name, signature) \ + function_name ## _t function_name; + +/* Define function typedef */ +LIST_WINHVPLATFORM_FUNCTIONS(WHP_DEFINE_TYPE) +LIST_WINHVEMULATION_FUNCTIONS(WHP_DEFINE_TYPE) + +struct WHPDispatch { + LIST_WINHVPLATFORM_FUNCTIONS(WHP_DECLARE_MEMBER) + LIST_WINHVEMULATION_FUNCTIONS(WHP_DECLARE_MEMBER) +}; + +extern struct WHPDispatch whp_dispatch; + +bool init_whp_dispatch(void); + + +#endif /* WHP_DISPATCH_H */ diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c index 58435178a4..57e53e1f1f 100644 --- a/target/i386/whpx-all.c +++ b/target/i386/whpx-all.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/address-spaces.h" -#include "exec/exec-all.h" #include "exec/ioport.h" #include "qemu-common.h" #include "strings.h" @@ -25,6 +24,7 @@ #include "qemu/queue.h" #include "qapi/error.h" #include "migration/blocker.h" +#include "whp-dispatch.h" #include #include @@ -160,8 +160,11 @@ struct whpx_vcpu { }; static bool whpx_allowed; +static bool whp_dispatch_initialized; +static HMODULE hWinHvPlatform, hWinHvEmulation; struct whpx_state whpx_global; +struct WHPDispatch whp_dispatch; /* @@ -220,24 +223,31 @@ static void whpx_set_registers(CPUState *cpu) struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr); X86CPU *x86_cpu = X86_CPU(cpu); - struct whpx_register_set vcxt = {0}; + struct whpx_register_set vcxt; HRESULT hr; - int idx = 0; + int idx; + int idx_next; int i; int v86, r86; assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + memset(&vcxt, 0, sizeof(struct whpx_register_set)); + v86 = (env->eflags & VM_MASK); r86 = !(env->cr[0] & CR0_PE_MASK); vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state); vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state); + idx = 0; + /* Indexes for first 16 registers match between HV and QEMU definitions */ - for (idx = 0; idx < CPU_NB_REGS64; idx += 1) { - vcxt.values[idx].Reg64 = env->regs[idx]; + idx_next = 16; + for (idx = 0; idx < CPU_NB_REGS; idx += 1) { + vcxt.values[idx].Reg64 = (uint64_t)env->regs[idx]; } + idx = idx_next; /* Same goes for RIP and RFLAGS */ assert(whpx_register_names[idx] == WHvX64RegisterRip); @@ -284,10 +294,12 @@ static void whpx_set_registers(CPUState *cpu) /* 16 XMM registers */ assert(whpx_register_names[idx] == WHvX64RegisterXmm0); - for (i = 0; i < 16; i += 1, idx += 1) { + idx_next = idx + 16; + for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) { vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0); vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1); } + idx = idx_next; /* 8 FP registers */ assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0); @@ -355,10 +367,11 @@ static void whpx_set_registers(CPUState *cpu) assert(idx == RTL_NUMBER_OF(whpx_register_names)); - hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - whpx_register_names, - RTL_NUMBER_OF(whpx_register_names), - &vcxt.values[0]); + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + whpx_register_names, + RTL_NUMBER_OF(whpx_register_names), + &vcxt.values[0]); if (FAILED(hr)) { error_report("WHPX: Failed to set virtual processor context, hr=%08lx", @@ -377,24 +390,30 @@ static void whpx_get_registers(CPUState *cpu) struct whpx_register_set vcxt; uint64_t tpr, apic_base; HRESULT hr; - int idx = 0; + int idx; + int idx_next; int i; assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); - hr = WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - whpx_register_names, - RTL_NUMBER_OF(whpx_register_names), - &vcxt.values[0]); + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + whpx_register_names, + RTL_NUMBER_OF(whpx_register_names), + &vcxt.values[0]); if (FAILED(hr)) { error_report("WHPX: Failed to get virtual processor context, hr=%08lx", hr); } + idx = 0; + /* Indexes for first 16 registers match between HV and QEMU definitions */ - for (idx = 0; idx < CPU_NB_REGS64; idx += 1) { + idx_next = 16; + for (idx = 0; idx < CPU_NB_REGS; idx += 1) { env->regs[idx] = vcxt.values[idx].Reg64; } + idx = idx_next; /* Same goes for RIP and RFLAGS */ assert(whpx_register_names[idx] == WHvX64RegisterRip); @@ -441,10 +460,12 @@ static void whpx_get_registers(CPUState *cpu) /* 16 XMM registers */ assert(whpx_register_names[idx] == WHvX64RegisterXmm0); - for (i = 0; i < 16; i += 1, idx += 1) { + idx_next = idx + 16; + for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) { env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64; env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64; } + idx = idx_next; /* 8 FP registers */ assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0); @@ -545,9 +566,10 @@ static HRESULT CALLBACK whpx_emu_getreg_callback( struct whpx_state *whpx = &whpx_global; CPUState *cpu = (CPUState *)ctx; - hr = WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - RegisterNames, RegisterCount, - RegisterValues); + hr = whp_dispatch.WHvGetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + RegisterNames, RegisterCount, + RegisterValues); if (FAILED(hr)) { error_report("WHPX: Failed to get virtual processor registers," " hr=%08lx", hr); @@ -566,9 +588,10 @@ static HRESULT CALLBACK whpx_emu_setreg_callback( struct whpx_state *whpx = &whpx_global; CPUState *cpu = (CPUState *)ctx; - hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - RegisterNames, RegisterCount, - RegisterValues); + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + RegisterNames, RegisterCount, + RegisterValues); if (FAILED(hr)) { error_report("WHPX: Failed to set virtual processor registers," " hr=%08lx", hr); @@ -595,8 +618,8 @@ static HRESULT CALLBACK whpx_emu_translate_callback( CPUState *cpu = (CPUState *)ctx; WHV_TRANSLATE_GVA_RESULT res; - hr = WHvTranslateGva(whpx->partition, cpu->cpu_index, - Gva, TranslateFlags, &res, Gpa); + hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index, + Gva, TranslateFlags, &res, Gpa); if (FAILED(hr)) { error_report("WHPX: Failed to translate GVA, hr=%08lx", hr); } else { @@ -621,16 +644,18 @@ static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); WHV_EMULATOR_STATUS emu_status; - hr = WHvEmulatorTryMmioEmulation(vcpu->emulator, cpu, - &vcpu->exit_ctx.VpContext, ctx, - &emu_status); + hr = whp_dispatch.WHvEmulatorTryMmioEmulation( + vcpu->emulator, cpu, + &vcpu->exit_ctx.VpContext, ctx, + &emu_status); if (FAILED(hr)) { error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr); return -1; } if (!emu_status.EmulationSuccessful) { - error_report("WHPX: Failed to emulate MMIO access"); + error_report("WHPX: Failed to emulate MMIO access with" + " EmulatorReturnStatus: %u", emu_status.AsUINT32); return -1; } @@ -644,16 +669,18 @@ static int whpx_handle_portio(CPUState *cpu, struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); WHV_EMULATOR_STATUS emu_status; - hr = WHvEmulatorTryIoEmulation(vcpu->emulator, cpu, - &vcpu->exit_ctx.VpContext, ctx, - &emu_status); + hr = whp_dispatch.WHvEmulatorTryIoEmulation( + vcpu->emulator, cpu, + &vcpu->exit_ctx.VpContext, ctx, + &emu_status); if (FAILED(hr)) { error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr); return -1; } if (!emu_status.EmulationSuccessful) { - error_report("WHPX: Failed to emulate PortMMIO access"); + error_report("WHPX: Failed to emulate PortIO access with" + " EmulatorReturnStatus: %u", emu_status.AsUINT32); return -1; } @@ -687,11 +714,14 @@ static void whpx_vcpu_pre_run(CPUState *cpu) X86CPU *x86_cpu = X86_CPU(cpu); int irq; uint8_t tpr; - WHV_X64_PENDING_INTERRUPTION_REGISTER new_int = {0}; + WHV_X64_PENDING_INTERRUPTION_REGISTER new_int; UINT32 reg_count = 0; - WHV_REGISTER_VALUE reg_values[3] = {0}; + WHV_REGISTER_VALUE reg_values[3]; WHV_REGISTER_NAME reg_names[3]; + memset(&new_int, 0, sizeof(new_int)); + memset(reg_values, 0, sizeof(reg_values)); + qemu_mutex_lock_iothread(); /* Inject NMI */ @@ -768,8 +798,9 @@ static void whpx_vcpu_pre_run(CPUState *cpu) qemu_mutex_unlock_iothread(); if (reg_count) { - hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, - reg_names, reg_count, reg_values); + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + reg_names, reg_count, reg_values); if (FAILED(hr)) { error_report("WHPX: Failed to set interrupt state registers," " hr=%08lx", hr); @@ -877,8 +908,9 @@ static int whpx_vcpu_run(CPUState *cpu) whpx_vcpu_kick(cpu); } - hr = WHvRunVirtualProcessor(whpx->partition, cpu->cpu_index, - &vcpu->exit_ctx, sizeof(vcpu->exit_ctx)); + hr = whp_dispatch.WHvRunVirtualProcessor( + whpx->partition, cpu->cpu_index, + &vcpu->exit_ctx, sizeof(vcpu->exit_ctx)); if (FAILED(hr)) { error_report("WHPX: Failed to exec a virtual processor," @@ -900,6 +932,7 @@ static int whpx_vcpu_run(CPUState *cpu) case WHvRunVpExitReasonX64InterruptWindow: vcpu->window_registered = 0; + ret = 0; break; case WHvRunVpExitReasonX64Halt: @@ -911,12 +944,48 @@ static int whpx_vcpu_run(CPUState *cpu) ret = 1; break; + case WHvRunVpExitReasonX64MsrAccess: { + WHV_REGISTER_VALUE reg_values[3] = {0}; + WHV_REGISTER_NAME reg_names[3]; + UINT32 reg_count; + + reg_names[0] = WHvX64RegisterRip; + reg_names[1] = WHvX64RegisterRax; + reg_names[2] = WHvX64RegisterRdx; + + reg_values[0].Reg64 = + vcpu->exit_ctx.VpContext.Rip + + vcpu->exit_ctx.VpContext.InstructionLength; + + /* + * For all unsupported MSR access we: + * ignore writes + * return 0 on read. + */ + reg_count = vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite ? + 1 : 3; + + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, + cpu->cpu_index, + reg_names, reg_count, + reg_values); + + if (FAILED(hr)) { + error_report("WHPX: Failed to set MsrAccess state " + " registers, hr=%08lx", hr); + } + ret = 0; + break; + } case WHvRunVpExitReasonX64Cpuid: { - WHV_REGISTER_VALUE reg_values[5] = {0}; + WHV_REGISTER_VALUE reg_values[5]; WHV_REGISTER_NAME reg_names[5]; UINT32 reg_count = 5; UINT64 rip, rax, rcx, rdx, rbx; + memset(reg_values, 0, sizeof(reg_values)); + rip = vcpu->exit_ctx.VpContext.Rip + vcpu->exit_ctx.VpContext.InstructionLength; switch (vcpu->exit_ctx.CpuidAccess.Rax) { @@ -927,6 +996,16 @@ static int whpx_vcpu_run(CPUState *cpu) vcpu->exit_ctx.CpuidAccess.DefaultResultRcx | CPUID_EXT_HYPERVISOR; + rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; + rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; + break; + case 0x80000001: + rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax; + /* Remove any support of OSVW */ + rcx = + vcpu->exit_ctx.CpuidAccess.DefaultResultRcx & + ~CPUID_EXT3_OSVW; + rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx; rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx; break; @@ -949,11 +1028,11 @@ static int whpx_vcpu_run(CPUState *cpu) reg_values[3].Reg64 = rdx; reg_values[4].Reg64 = rbx; - hr = WHvSetVirtualProcessorRegisters(whpx->partition, - cpu->cpu_index, - reg_names, - reg_count, - reg_values); + hr = whp_dispatch.WHvSetVirtualProcessorRegisters( + whpx->partition, cpu->cpu_index, + reg_names, + reg_count, + reg_values); if (FAILED(hr)) { error_report("WHPX: Failed to set CpuidAccess state registers," @@ -966,7 +1045,6 @@ static int whpx_vcpu_run(CPUState *cpu) case WHvRunVpExitReasonUnrecoverableException: case WHvRunVpExitReasonInvalidVpRegisterValue: case WHvRunVpExitReasonUnsupportedFeature: - case WHvRunVpExitReasonX64MsrAccess: case WHvRunVpExitReasonException: default: error_report("WHPX: Unexpected VP exit code %d", @@ -1065,8 +1143,8 @@ int whpx_init_vcpu(CPUState *cpu) (void)migrate_add_blocker(whpx_migration_blocker, &local_error); if (local_error) { error_report_err(local_error); - error_free(whpx_migration_blocker); migrate_del_blocker(whpx_migration_blocker); + error_free(whpx_migration_blocker); return -EINVAL; } } @@ -1078,7 +1156,9 @@ int whpx_init_vcpu(CPUState *cpu) return -ENOMEM; } - hr = WHvEmulatorCreateEmulator(&whpx_emu_callbacks, &vcpu->emulator); + hr = whp_dispatch.WHvEmulatorCreateEmulator( + &whpx_emu_callbacks, + &vcpu->emulator); if (FAILED(hr)) { error_report("WHPX: Failed to setup instruction completion support," " hr=%08lx", hr); @@ -1086,11 +1166,12 @@ int whpx_init_vcpu(CPUState *cpu) return -EINVAL; } - hr = WHvCreateVirtualProcessor(whpx->partition, cpu->cpu_index, 0); + hr = whp_dispatch.WHvCreateVirtualProcessor( + whpx->partition, cpu->cpu_index, 0); if (FAILED(hr)) { error_report("WHPX: Failed to create a virtual processor," " hr=%08lx", hr); - WHvEmulatorDestroyEmulator(vcpu->emulator); + whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); g_free(vcpu); return -EINVAL; } @@ -1131,8 +1212,8 @@ void whpx_destroy_vcpu(CPUState *cpu) struct whpx_state *whpx = &whpx_global; struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu); - WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index); - WHvEmulatorDestroyEmulator(vcpu->emulator); + whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index); + whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); g_free(cpu->hax_vcpu); return; } @@ -1140,7 +1221,8 @@ void whpx_destroy_vcpu(CPUState *cpu) void whpx_vcpu_kick(CPUState *cpu) { struct whpx_state *whpx = &whpx_global; - WHvCancelRunVirtualProcessor(whpx->partition, cpu->cpu_index, 0); + whp_dispatch.WHvCancelRunVirtualProcessor( + whpx->partition, cpu->cpu_index, 0); } /* @@ -1166,24 +1248,24 @@ static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size, */ if (add) { - hr = WHvMapGpaRange(whpx->partition, - host_va, - start_pa, - size, - (WHvMapGpaRangeFlagRead | - WHvMapGpaRangeFlagExecute | - (rom ? 0 : WHvMapGpaRangeFlagWrite))); + hr = whp_dispatch.WHvMapGpaRange(whpx->partition, + host_va, + start_pa, + size, + (WHvMapGpaRangeFlagRead | + WHvMapGpaRangeFlagExecute | + (rom ? 0 : WHvMapGpaRangeFlagWrite))); } else { - hr = WHvUnmapGpaRange(whpx->partition, - start_pa, - size); + hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition, + start_pa, + size); } if (FAILED(hr)) { error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes," " Host:%p, hr=%08lx", (add ? "MAP" : "UNMAP"), name, - (void *)start_pa, (void *)size, host_va, hr); + (void *)(uintptr_t)start_pa, (void *)size, host_va, hr); } } @@ -1214,8 +1296,8 @@ static void whpx_process_section(MemoryRegionSection *section, int add) host_va = (uintptr_t)memory_region_get_ram_ptr(mr) + section->offset_within_region + delta; - whpx_update_mapping(start_pa, size, (void *)host_va, add, - memory_region_is_rom(mr), mr->name); + whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add, + memory_region_is_rom(mr), mr->name); } static void whpx_region_add(MemoryListener *listener, @@ -1290,18 +1372,24 @@ static int whpx_accel_init(MachineState *ms) whpx = &whpx_global; + if (!init_whp_dispatch()) { + ret = -ENOSYS; + goto error; + } + memset(whpx, 0, sizeof(struct whpx_state)); whpx->mem_quota = ms->ram_size; - hr = WHvGetCapability(WHvCapabilityCodeHypervisorPresent, &whpx_cap, - sizeof(whpx_cap), &whpx_cap_size); + hr = whp_dispatch.WHvGetCapability( + WHvCapabilityCodeHypervisorPresent, &whpx_cap, + sizeof(whpx_cap), &whpx_cap_size); if (FAILED(hr) || !whpx_cap.HypervisorPresent) { error_report("WHPX: No accelerator found, hr=%08lx", hr); ret = -ENOSPC; goto error; } - hr = WHvCreatePartition(&whpx->partition); + hr = whp_dispatch.WHvCreatePartition(&whpx->partition); if (FAILED(hr)) { error_report("WHPX: Failed to create partition, hr=%08lx", hr); ret = -EINVAL; @@ -1310,10 +1398,11 @@ static int whpx_accel_init(MachineState *ms) memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY)); prop.ProcessorCount = smp_cpus; - hr = WHvSetPartitionProperty(whpx->partition, - WHvPartitionPropertyCodeProcessorCount, - &prop, - sizeof(WHV_PARTITION_PROPERTY)); + hr = whp_dispatch.WHvSetPartitionProperty( + whpx->partition, + WHvPartitionPropertyCodeProcessorCount, + &prop, + sizeof(WHV_PARTITION_PROPERTY)); if (FAILED(hr)) { error_report("WHPX: Failed to set partition core count to %d," @@ -1323,24 +1412,27 @@ static int whpx_accel_init(MachineState *ms) } memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY)); + prop.ExtendedVmExits.X64MsrExit = 1; prop.ExtendedVmExits.X64CpuidExit = 1; - hr = WHvSetPartitionProperty(whpx->partition, - WHvPartitionPropertyCodeExtendedVmExits, - &prop, - sizeof(WHV_PARTITION_PROPERTY)); + hr = whp_dispatch.WHvSetPartitionProperty( + whpx->partition, + WHvPartitionPropertyCodeExtendedVmExits, + &prop, + sizeof(WHV_PARTITION_PROPERTY)); if (FAILED(hr)) { - error_report("WHPX: Failed to enable partition extended X64CpuidExit" - " hr=%08lx", hr); + error_report("WHPX: Failed to enable partition extended X64MsrExit and" + " X64CpuidExit hr=%08lx", hr); ret = -EINVAL; goto error; } - UINT32 cpuidExitList[] = {1}; - hr = WHvSetPartitionProperty(whpx->partition, - WHvPartitionPropertyCodeCpuidExitList, - cpuidExitList, - RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32)); + UINT32 cpuidExitList[] = {1, 0x80000001}; + hr = whp_dispatch.WHvSetPartitionProperty( + whpx->partition, + WHvPartitionPropertyCodeCpuidExitList, + cpuidExitList, + RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32)); if (FAILED(hr)) { error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx", @@ -1349,7 +1441,7 @@ static int whpx_accel_init(MachineState *ms) goto error; } - hr = WHvSetupPartition(whpx->partition); + hr = whp_dispatch.WHvSetupPartition(whpx->partition); if (FAILED(hr)) { error_report("WHPX: Failed to setup partition, hr=%08lx", hr); ret = -EINVAL; @@ -1366,7 +1458,7 @@ static int whpx_accel_init(MachineState *ms) error: if (NULL != whpx->partition) { - WHvDeletePartition(whpx->partition); + whp_dispatch.WHvDeletePartition(whpx->partition); whpx->partition = NULL; } @@ -1398,4 +1490,54 @@ static void whpx_type_init(void) type_register_static(&whpx_accel_type); } +bool init_whp_dispatch(void) +{ + const char *lib_name; + HMODULE hLib; + + if (whp_dispatch_initialized) { + return true; + } + + #define WHP_LOAD_FIELD(return_type, function_name, signature) \ + whp_dispatch.function_name = \ + (function_name ## _t)GetProcAddress(hLib, #function_name); \ + if (!whp_dispatch.function_name) { \ + error_report("Could not load function %s from library %s.", \ + #function_name, lib_name); \ + goto error; \ + } \ + + lib_name = "WinHvPlatform.dll"; + hWinHvPlatform = LoadLibrary(lib_name); + if (!hWinHvPlatform) { + error_report("Could not load library %s.", lib_name); + goto error; + } + hLib = hWinHvPlatform; + LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD) + + lib_name = "WinHvEmulation.dll"; + hWinHvEmulation = LoadLibrary(lib_name); + if (!hWinHvEmulation) { + error_report("Could not load library %s.", lib_name); + goto error; + } + hLib = hWinHvEmulation; + LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD) + + whp_dispatch_initialized = true; + return true; + + error: + + if (hWinHvPlatform) { + FreeLibrary(hWinHvPlatform); + } + if (hWinHvEmulation) { + FreeLibrary(hWinHvEmulation); + } + return false; +} + type_init(whpx_type_init); diff --git a/target/lm32/cpu.c b/target/lm32/cpu.c index 0003152469..b7499cb627 100644 --- a/target/lm32/cpu.c +++ b/target/lm32/cpu.c @@ -22,7 +22,6 @@ #include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" -#include "exec/exec-all.h" static void lm32_cpu_set_pc(CPUState *cs, vaddr value) diff --git a/target/lm32/op_helper.c b/target/lm32/op_helper.c index 577f8306e3..234d55e056 100644 --- a/target/lm32/op_helper.c +++ b/target/lm32/op_helper.c @@ -102,12 +102,16 @@ void HELPER(wcsr_dc)(CPULM32State *env, uint32_t dc) void HELPER(wcsr_im)(CPULM32State *env, uint32_t im) { + qemu_mutex_lock_iothread(); lm32_pic_set_im(env->pic_state, im); + qemu_mutex_unlock_iothread(); } void HELPER(wcsr_ip)(CPULM32State *env, uint32_t im) { + qemu_mutex_lock_iothread(); lm32_pic_set_ip(env->pic_state, im); + qemu_mutex_unlock_iothread(); } void HELPER(wcsr_jtx)(CPULM32State *env, uint32_t jtx) diff --git a/target/lm32/translate.c b/target/lm32/translate.c index 2e1c5e6d01..b32feb7564 100644 --- a/target/lm32/translate.c +++ b/target/lm32/translate.c @@ -159,13 +159,13 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)dc->tb + n); + tcg_gen_exit_tb(dc->tb, n); } else { tcg_gen_movi_tl(cpu_pc, dest); if (dc->singlestep_enabled) { t_gen_raise_exception(dc, EXCP_DEBUG); } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -1055,7 +1055,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) LM32CPU *cpu = lm32_env_get_cpu(env); struct DisasContext ctx, *dc = &ctx; uint32_t pc_start; - uint32_t next_page_start; + uint32_t page_start; int num_insns; int max_insns; @@ -1075,7 +1075,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) pc_start &= ~3; } - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { @@ -1115,7 +1115,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep - && (dc->pc < next_page_start) + && (dc->pc - page_start < TARGET_PAGE_SIZE) && num_insns < max_insns); if (tb_cflags(tb) & CF_LAST_IO) { @@ -1137,7 +1137,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: /* nothing more to generate */ diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index a4ed8770aa..582e3a73b3 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -23,7 +23,6 @@ #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" -#include "exec/exec-all.h" #include "fpu/softfloat.h" static void m68k_cpu_set_pc(CPUState *cs, vaddr value) diff --git a/target/m68k/softfloat.c b/target/m68k/softfloat.c index dffb371c71..b45a5e8690 100644 --- a/target/m68k/softfloat.c +++ b/target/m68k/softfloat.c @@ -31,13 +31,14 @@ static floatx80 propagateFloatx80NaNOneArg(floatx80 a, float_status *status) { if (floatx80_is_signaling_nan(a, status)) { float_raise(float_flag_invalid, status); + a = floatx80_silence_nan(a, status); } if (status->default_nan_mode) { return floatx80_default_nan(status); } - return floatx80_maybe_silence_nan(a, status); + return a; } /*---------------------------------------------------------------------------- @@ -103,6 +104,7 @@ floatx80 floatx80_mod(floatx80 a, floatx80 b, float_status *status) mul64To128(bSig, qTemp, &term0, &term1); sub128(aSig0, aSig1, term0, term1, &aSig0, &aSig1); shortShift128Left(aSig0, aSig1, 62, &aSig0, &aSig1); + expDiff -= 62; } expDiff += 64; if (0 < expDiff) { @@ -334,7 +336,8 @@ floatx80 floatx80_lognp1(floatx80 a, float_status *status) if (aSign && aExp >= one_exp) { if (aExp == one_exp && aSig == one_sig) { float_raise(float_flag_divbyzero, status); - packFloatx80(aSign, floatx80_infinity.high, floatx80_infinity.low); + return packFloatx80(aSign, floatx80_infinity.high, + floatx80_infinity.low); } float_raise(float_flag_invalid, status); return floatx80_default_nan(status); @@ -1496,7 +1499,7 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) int32_t compact, l, n, j; floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; float32 posneg1, twoto63; - flag adjn, endflag; + flag endflag; aSig = extractFloatx80Frac(a); aExp = extractFloatx80Exp(a); @@ -1514,8 +1517,6 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) return packFloatx80(aSign, 0, 0); } - adjn = 0; - user_rnd_mode = status->float_rounding_mode; user_rnd_prec = status->floatx80_rounding_precision; status->float_rounding_mode = float_round_nearest_even; @@ -1590,14 +1591,8 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) status->float_rounding_mode = user_rnd_mode; status->floatx80_rounding_precision = user_rnd_prec; - if (adjn) { - /* COSTINY */ - a = floatx80_sub(fp0, float32_to_floatx80( - make_float32(0x00800000), status), status); - } else { - /* SINTINY */ - a = floatx80_move(a, status); - } + /* SINTINY */ + a = floatx80_move(a, status); float_raise(float_flag_inexact, status); return a; @@ -1615,7 +1610,7 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) status); /* FP0 IS R = (X-Y1)-Y2 */ sincont: - if ((n + adjn) & 1) { + if (n & 1) { /* COSPOLY */ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ @@ -1628,7 +1623,7 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) xExp = extractFloatx80Exp(fp0); xSig = extractFloatx80Frac(fp0); - if (((n + adjn) >> 1) & 1) { + if ((n >> 1) & 1) { xSign ^= 1; posneg1 = make_float32(0xBF800000); /* -1 */ } else { @@ -1680,7 +1675,7 @@ floatx80 floatx80_sin(floatx80 a, float_status *status) xExp = extractFloatx80Exp(fp0); xSig = extractFloatx80Frac(fp0); - xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */ + xSign ^= (n >> 1) & 1; /* X IS NOW R'= SGN*R */ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ @@ -1743,7 +1738,7 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) int32_t compact, l, n, j; floatx80 fp0, fp1, fp2, fp3, fp4, fp5, x, invtwopi, twopi1, twopi2; float32 posneg1, twoto63; - flag adjn, endflag; + flag endflag; aSig = extractFloatx80Frac(a); aExp = extractFloatx80Exp(a); @@ -1761,8 +1756,6 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) return packFloatx80(0, one_exp, one_sig); } - adjn = 1; - user_rnd_mode = status->float_rounding_mode; user_rnd_prec = status->floatx80_rounding_precision; status->float_rounding_mode = float_round_nearest_even; @@ -1836,15 +1829,10 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) status->float_rounding_mode = user_rnd_mode; status->floatx80_rounding_precision = user_rnd_prec; - if (adjn) { - /* COSTINY */ - a = floatx80_sub(fp0, float32_to_floatx80( - make_float32(0x00800000), status), - status); - } else { - /* SINTINY */ - a = floatx80_move(a, status); - } + /* COSTINY */ + a = floatx80_sub(fp0, float32_to_floatx80( + make_float32(0x00800000), status), + status); float_raise(float_flag_inexact, status); return a; @@ -1862,7 +1850,7 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) status); /* FP0 IS R = (X-Y1)-Y2 */ sincont: - if ((n + adjn) & 1) { + if ((n + 1) & 1) { /* COSPOLY */ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ @@ -1875,7 +1863,7 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) xExp = extractFloatx80Exp(fp0); xSig = extractFloatx80Frac(fp0); - if (((n + adjn) >> 1) & 1) { + if (((n + 1) >> 1) & 1) { xSign ^= 1; posneg1 = make_float32(0xBF800000); /* -1 */ } else { @@ -1926,7 +1914,7 @@ floatx80 floatx80_cos(floatx80 a, float_status *status) xExp = extractFloatx80Exp(fp0); xSig = extractFloatx80Frac(fp0); - xSign ^= ((n + adjn) >> 1) & 1; /* X IS NOW R'= SGN*R */ + xSign ^= ((n + 1) >> 1) & 1; /* X IS NOW R'= SGN*R */ fp0 = floatx80_mul(fp0, fp0, status); /* FP0 IS S */ fp1 = floatx80_mul(fp0, fp0, status); /* FP1 IS T */ diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 6beaf9ed66..ae3651b867 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -111,14 +111,11 @@ void m68k_tcg_init(void) /* internal defines */ typedef struct DisasContext { + DisasContextBase base; CPUM68KState *env; - target_ulong insn_pc; /* Start of the current instruction. */ target_ulong pc; - int is_jmp; CCOp cc_op; /* Current CC operation */ int cc_op_synced; - struct TranslationBlock *tb; - int singlestep_enabled; TCGv_i64 mactmp; int done_mac; int writeback_mask; @@ -198,17 +195,15 @@ static void do_writebacks(DisasContext *s) /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ -#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ -#define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ -#define DISAS_JUMP_NEXT DISAS_TARGET_3 +#define DISAS_EXIT DISAS_TARGET_1 /* cpu state was modified dynamically */ #if defined(CONFIG_USER_ONLY) #define IS_USER(s) 1 #else -#define IS_USER(s) (!(s->tb->flags & TB_FLAGS_MSR_S)) -#define SFC_INDEX(s) ((s->tb->flags & TB_FLAGS_SFC_S) ? \ +#define IS_USER(s) (!(s->base.tb->flags & TB_FLAGS_MSR_S)) +#define SFC_INDEX(s) ((s->base.tb->flags & TB_FLAGS_SFC_S) ? \ MMU_KERNEL_IDX : MMU_USER_IDX) -#define DFC_INDEX(s) ((s->tb->flags & TB_FLAGS_DFC_S) ? \ +#define DFC_INDEX(s) ((s->base.tb->flags & TB_FLAGS_DFC_S) ? \ MMU_KERNEL_IDX : MMU_USER_IDX) #endif @@ -280,7 +275,7 @@ static void gen_jmp_im(DisasContext *s, uint32_t dest) { update_cc_op(s); tcg_gen_movi_i32(QREG_PC, dest); - s->is_jmp = DISAS_JUMP; + s->base.is_jmp = DISAS_JUMP; } /* Generate a jump to the address in qreg DEST. */ @@ -288,26 +283,26 @@ static void gen_jmp(DisasContext *s, TCGv dest) { update_cc_op(s); tcg_gen_mov_i32(QREG_PC, dest); - s->is_jmp = DISAS_JUMP; + s->base.is_jmp = DISAS_JUMP; } -static void gen_raise_exception(int nr) +static void gen_exception(DisasContext *s, uint32_t dest, int nr) { - TCGv_i32 tmp = tcg_const_i32(nr); + TCGv_i32 tmp; + update_cc_op(s); + tcg_gen_movi_i32(QREG_PC, dest); + + tmp = tcg_const_i32(nr); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); -} -static void gen_exception(DisasContext *s, uint32_t where, int nr) -{ - gen_jmp_im(s, where); - gen_raise_exception(nr); + s->base.is_jmp = DISAS_NORETURN; } static inline void gen_addr_fault(DisasContext *s) { - gen_exception(s, s->insn_pc, EXCP_ADDRESS); + gen_exception(s, s->base.pc_next, EXCP_ADDRESS); } /* Generate a load from the specified address. Narrow values are @@ -1005,7 +1000,7 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; } tcg_gen_qemu_ld32u(tmp, addr, index); @@ -1019,7 +1014,7 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, /* unimplemented data type on 68040/ColdFire * FIXME if needed for another FPU */ - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; default: g_assert_not_reached(); @@ -1059,7 +1054,7 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; } tcg_gen_ld16u_i32(tmp, fp, offsetof(FPReg, l.upper)); @@ -1073,7 +1068,7 @@ static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp, /* unimplemented data type on 68040/ColdFire * FIXME if needed for another FPU */ - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; default: g_assert_not_reached(); @@ -1205,7 +1200,7 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, break; case OS_EXTENDED: if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; } tmp = tcg_const_i32(read_im32(env, s) >> 16); @@ -1219,7 +1214,7 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, /* unimplemented data type on 68040/ColdFire * FIXME if needed for another FPU */ - gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP); break; default: g_assert_not_reached(); @@ -1448,11 +1443,11 @@ static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) } /* Force a TB lookup after an instruction that changes the CPU state. */ -static void gen_lookup_tb(DisasContext *s) +static void gen_exit_tb(DisasContext *s) { update_cc_op(s); tcg_gen_movi_i32(QREG_PC, s->pc); - s->is_jmp = DISAS_UPDATE; + s->base.is_jmp = DISAS_EXIT; } #define SRC_EA(env, result, opsize, op_sign, addrp) do { \ @@ -1476,8 +1471,8 @@ static void gen_lookup_tb(DisasContext *s) static inline bool use_goto_tb(DisasContext *s, uint32_t dest) { #ifndef CONFIG_USER_ONLY - return (s->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) || - (s->insn_pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); + return (s->base.pc_first & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) + || (s->base.pc_next & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif @@ -1486,17 +1481,17 @@ static inline bool use_goto_tb(DisasContext *s, uint32_t dest) /* Generate a jump to an immediate address. */ static void gen_jmp_tb(DisasContext *s, int n, uint32_t dest) { - if (unlikely(s->singlestep_enabled)) { + if (unlikely(s->base.singlestep_enabled)) { gen_exception(s, dest, EXCP_DEBUG); } else if (use_goto_tb(s, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(QREG_PC, dest); - tcg_gen_exit_tb((uintptr_t)s->tb + n); + tcg_gen_exit_tb(s->base.tb, n); } else { gen_jmp_im(s, dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } - s->is_jmp = DISAS_TB_JUMP; + s->base.is_jmp = DISAS_NORETURN; } DISAS_INSN(scc) @@ -1543,12 +1538,12 @@ DISAS_INSN(dbcc) DISAS_INSN(undef_mac) { - gen_exception(s, s->insn_pc, EXCP_LINEA); + gen_exception(s, s->base.pc_next, EXCP_LINEA); } DISAS_INSN(undef_fpu) { - gen_exception(s, s->insn_pc, EXCP_LINEF); + gen_exception(s, s->base.pc_next, EXCP_LINEF); } DISAS_INSN(undef) @@ -1556,9 +1551,9 @@ DISAS_INSN(undef) /* ??? This is both instructions that are as yet unimplemented for the 680x0 series, as well as those that are implemented but actually illegal for CPU32 or pre-68020. */ - qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x", - insn, s->insn_pc); - gen_exception(s, s->insn_pc, EXCP_UNSUPPORTED); + qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x\n", + insn, s->base.pc_next); + gen_exception(s, s->base.pc_next, EXCP_UNSUPPORTED); } DISAS_INSN(mulw) @@ -1618,7 +1613,7 @@ DISAS_INSN(divl) if (ext & 0x400) { if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } @@ -2297,7 +2292,7 @@ DISAS_INSN(arith_im) im = tcg_const_i32(read_im32(env, s)); break; default: - abort(); + g_assert_not_reached(); } if (with_SR) { @@ -2312,12 +2307,13 @@ DISAS_INSN(arith_im) break; case OS_WORD: if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } src1 = gen_get_sr(s); break; - case OS_LONG: + default: + /* OS_LONG; others already g_assert_not_reached. */ disas_undef(env, s, insn); return; } @@ -2480,7 +2476,7 @@ DISAS_INSN(cas2w) (REG(ext1, 6) << 3) | (REG(ext2, 0) << 6) | (REG(ext1, 0) << 9)); - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_exit_atomic(cpu_env); } else { gen_helper_cas2w(cpu_env, regs, addr1, addr2); @@ -2530,7 +2526,7 @@ DISAS_INSN(cas2l) (REG(ext1, 6) << 3) | (REG(ext2, 0) << 6) | (REG(ext1, 0) << 9)); - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_cas2l_parallel(cpu_env, regs, addr1, addr2); } else { gen_helper_cas2l(cpu_env, regs, addr1, addr2); @@ -2721,7 +2717,7 @@ DISAS_INSN(swap) DISAS_INSN(bkpt) { - gen_exception(s, s->insn_pc, EXCP_DEBUG); + gen_exception(s, s->base.pc_next, EXCP_DEBUG); } DISAS_INSN(pea) @@ -2774,7 +2770,7 @@ DISAS_INSN(pulse) DISAS_INSN(illegal) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); } /* ??? This should be atomic. */ @@ -2804,7 +2800,7 @@ DISAS_INSN(mull) if (ext & 0x400) { if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) { - gen_exception(s, s->insn_pc, EXCP_UNSUPPORTED); + gen_exception(s, s->base.pc_next, EXCP_UNSUPPORTED); return; } @@ -2905,7 +2901,7 @@ DISAS_INSN(unlk) DISAS_INSN(reset) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -3166,11 +3162,11 @@ DISAS_INSN(subx_mem) opsize = insn_opsize(insn); addr_src = AREG(insn, 0); - tcg_gen_subi_i32(addr_src, addr_src, opsize); + tcg_gen_subi_i32(addr_src, addr_src, opsize_bytes(opsize)); src = gen_load(s, opsize, addr_src, 1, IS_USER(s)); addr_dest = AREG(insn, 9); - tcg_gen_subi_i32(addr_dest, addr_dest, opsize); + tcg_gen_subi_i32(addr_dest, addr_dest, opsize_bytes(opsize)); dest = gen_load(s, opsize, addr_dest, 1, IS_USER(s)); gen_subx(s, src, dest, opsize); @@ -4001,7 +3997,7 @@ DISAS_INSN(bfext_reg) TCGv shift; /* In general, we're going to rotate the field so that it's at the - top of the word and then right-shift by the compliment of the + top of the word and then right-shift by the complement of the width to extend the field. */ if (ext & 0x20) { /* Variable width. */ @@ -4376,7 +4372,7 @@ DISAS_INSN(chk) } /* fallthru */ default: - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } SRC_EA(env, src, opsize, 1, NULL); @@ -4403,13 +4399,13 @@ DISAS_INSN(chk2) opsize = OS_LONG; break; default: - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } ext = read_im16(env, s); if ((ext & 0x0800) == 0) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } @@ -4469,7 +4465,7 @@ DISAS_INSN(move16_reg) ext = read_im16(env, s); if ((ext & (1 << 15)) == 0) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); } m68k_copy_line(AREG(ext, 12), AREG(insn, 0), index); @@ -4531,7 +4527,7 @@ DISAS_INSN(move_from_sr) TCGv sr; if (IS_USER(s) && !m68k_feature(env, M68K_FEATURE_M68000)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } sr = gen_get_sr(s); @@ -4548,7 +4544,7 @@ DISAS_INSN(moves) int extend; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4601,17 +4597,17 @@ DISAS_INSN(moves) DISAS_INSN(move_to_sr) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } gen_move_to_sr(env, s, insn, false); - gen_lookup_tb(s); + gen_exit_tb(s); } DISAS_INSN(move_from_usp) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } tcg_gen_ld_i32(AREG(insn, 0), cpu_env, @@ -4621,7 +4617,7 @@ DISAS_INSN(move_from_usp) DISAS_INSN(move_to_usp) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } tcg_gen_st_i32(AREG(insn, 0), cpu_env, @@ -4631,7 +4627,7 @@ DISAS_INSN(move_to_usp) DISAS_INSN(halt) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4643,7 +4639,7 @@ DISAS_INSN(stop) uint16_t ext; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4657,10 +4653,10 @@ DISAS_INSN(stop) DISAS_INSN(rte) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } - gen_exception(s, s->insn_pc, EXCP_RTE); + gen_exception(s, s->base.pc_next, EXCP_RTE); } DISAS_INSN(cf_movec) @@ -4669,7 +4665,7 @@ DISAS_INSN(cf_movec) TCGv reg; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4681,7 +4677,7 @@ DISAS_INSN(cf_movec) reg = DREG(ext, 12); } gen_helper_cf_movec_to(cpu_env, tcg_const_i32(ext & 0xfff), reg); - gen_lookup_tb(s); + gen_exit_tb(s); } DISAS_INSN(m68k_movec) @@ -4690,7 +4686,7 @@ DISAS_INSN(m68k_movec) TCGv reg; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4706,13 +4702,13 @@ DISAS_INSN(m68k_movec) } else { gen_helper_m68k_movec_from(reg, cpu_env, tcg_const_i32(ext & 0xfff)); } - gen_lookup_tb(s); + gen_exit_tb(s); } DISAS_INSN(intouch) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* ICache fetch. Implement as no-op. */ @@ -4721,7 +4717,7 @@ DISAS_INSN(intouch) DISAS_INSN(cpushl) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* Cache push/invalidate. Implement as no-op. */ @@ -4730,7 +4726,7 @@ DISAS_INSN(cpushl) DISAS_INSN(cpush) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* Cache push/invalidate. Implement as no-op. */ @@ -4739,7 +4735,7 @@ DISAS_INSN(cpush) DISAS_INSN(cinv) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* Invalidate cache line. Implement as no-op. */ @@ -4751,7 +4747,7 @@ DISAS_INSN(pflush) TCGv opmode; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -4765,7 +4761,7 @@ DISAS_INSN(ptest) TCGv is_read; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } is_read = tcg_const_i32((insn >> 5) & 1); @@ -4776,7 +4772,7 @@ DISAS_INSN(ptest) DISAS_INSN(wddata) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); } DISAS_INSN(wdebug) @@ -4784,7 +4780,7 @@ DISAS_INSN(wdebug) M68kCPU *cpu = m68k_env_get_cpu(env); if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } /* TODO: Implement wdebug. */ @@ -4794,7 +4790,7 @@ DISAS_INSN(wdebug) DISAS_INSN(trap) { - gen_exception(s, s->insn_pc, EXCP_TRAP0 + (insn & 0xf)); + gen_exception(s, s->base.pc_next, EXCP_TRAP0 + (insn & 0xf)); } static void gen_load_fcr(DisasContext *s, TCGv res, int reg) @@ -4861,7 +4857,7 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s, switch (mode) { case 0: /* Dn */ if (mask != M68K_FPIAR && mask != M68K_FPSR && mask != M68K_FPCR) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } if (is_write) { @@ -4872,7 +4868,7 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s, return; case 1: /* An, only with FPIAR */ if (mask != M68K_FPIAR) { - gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } if (is_write) { @@ -5430,7 +5426,7 @@ DISAS_INSN(frestore) TCGv addr; if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } if (m68k_feature(s->env, M68K_FEATURE_M68040)) { @@ -5444,7 +5440,7 @@ DISAS_INSN(frestore) DISAS_INSN(fsave) { if (IS_USER(s)) { - gen_exception(s, s->insn_pc, EXCP_PRIVILEGE); + gen_exception(s, s->base.pc_next, EXCP_PRIVILEGE); return; } @@ -5750,7 +5746,7 @@ DISAS_INSN(to_macsr) TCGv val; SRC_EA(env, val, OS_LONG, 0, NULL); gen_helper_set_macsr(cpu_env, val); - gen_lookup_tb(s); + gen_exit_tb(s); } DISAS_INSN(to_mask) @@ -6053,121 +6049,130 @@ void register_m68k_insns (CPUM68KState *env) #undef INSN } -/* ??? Some of this implementation is not exception safe. We should always - write back the result to memory before setting the condition codes. */ -static void disas_m68k_insn(CPUM68KState * env, DisasContext *s) +static void m68k_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) { - uint16_t insn = read_im16(env, s); - opcode_table[insn](env, s, insn); - do_writebacks(s); - do_release(s); -} - -/* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) -{ - CPUM68KState *env = cs->env_ptr; - DisasContext dc1, *dc = &dc1; - target_ulong pc_start; - int pc_offset; - int num_insns; - int max_insns; - - /* generate intermediate code */ - pc_start = tb->pc; - - dc->tb = tb; + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUM68KState *env = cpu->env_ptr; dc->env = env; - dc->is_jmp = DISAS_NEXT; - dc->pc = pc_start; + dc->pc = dc->base.pc_first; dc->cc_op = CC_OP_DYNAMIC; dc->cc_op_synced = 1; - dc->singlestep_enabled = cs->singlestep_enabled; dc->done_mac = 0; dc->writeback_mask = 0; - num_insns = 0; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } - init_release_array(dc); +} - gen_tb_start(tb); - do { - pc_offset = dc->pc - pc_start; - tcg_gen_insn_start(dc->pc, dc->cc_op); - num_insns++; +static void m68k_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) +{ +} - if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { - gen_exception(dc, dc->pc, EXCP_DEBUG); - dc->is_jmp = DISAS_JUMP; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - dc->pc += 2; - break; +static void m68k_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + tcg_gen_insn_start(dc->base.pc_next, dc->cc_op); +} + +static bool m68k_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + gen_exception(dc, dc->base.pc_next, EXCP_DEBUG); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->base.pc_next += 2; + + return true; +} + +static void m68k_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUM68KState *env = cpu->env_ptr; + uint16_t insn = read_im16(env, dc); + + opcode_table[insn](env, dc, insn); + do_writebacks(dc); + do_release(dc); + + dc->base.pc_next = dc->pc; + + if (dc->base.is_jmp == DISAS_NEXT) { + /* Stop translation when the next insn might touch a new page. + * This ensures that prefetch aborts at the right place. + * + * We cannot determine the size of the next insn without + * completely decoding it. However, the maximum insn size + * is 32 bytes, so end if we do not have that much remaining. + * This may produce several small TBs at the end of each page, + * but they will all be linked with goto_tb. + * + * ??? ColdFire maximum is 4 bytes; MC68000's maximum is also + * smaller than MC68020's. + */ + target_ulong start_page_offset + = dc->pc - (dc->base.pc_first & TARGET_PAGE_MASK); + + if (start_page_offset >= TARGET_PAGE_SIZE - 32) { + dc->base.is_jmp = DISAS_TOO_MANY; } + } +} - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } +static void m68k_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); - dc->insn_pc = dc->pc; - disas_m68k_insn(env, dc); - } while (!dc->is_jmp && !tcg_op_buf_full() && - !cs->singlestep_enabled && - !singlestep && - (pc_offset) < (TARGET_PAGE_SIZE - 32) && - num_insns < max_insns); - - if (tb_cflags(tb) & CF_LAST_IO) - gen_io_end(); - if (unlikely(cs->singlestep_enabled)) { - /* Make sure the pc is updated, and raise a debug exception. */ - if (!dc->is_jmp) { - update_cc_op(dc); - tcg_gen_movi_i32(QREG_PC, dc->pc); - } + if (dc->base.is_jmp == DISAS_NORETURN) { + return; + } + if (dc->base.singlestep_enabled) { gen_helper_raise_exception(cpu_env, tcg_const_i32(EXCP_DEBUG)); - } else { - switch(dc->is_jmp) { - case DISAS_NEXT: - update_cc_op(dc); - gen_jmp_tb(dc, 0, dc->pc); - break; - default: - case DISAS_JUMP: - case DISAS_UPDATE: - update_cc_op(dc); - /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); - break; - case DISAS_TB_JUMP: - /* nothing more to generate */ - break; - } + return; } - gen_tb_end(tb, num_insns); -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("----------------\n"); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc->pc - pc_start); - qemu_log("\n"); - qemu_log_unlock(); + switch (dc->base.is_jmp) { + case DISAS_TOO_MANY: + update_cc_op(dc); + gen_jmp_tb(dc, 0, dc->pc); + break; + case DISAS_JUMP: + /* We updated CC_OP and PC in gen_jmp/gen_jmp_im. */ + tcg_gen_lookup_and_goto_ptr(); + break; + case DISAS_EXIT: + /* We updated CC_OP and PC in gen_exit_tb, but also modified + other state that may require returning to the main loop. */ + tcg_gen_exit_tb(NULL, 0); + break; + default: + g_assert_not_reached(); } -#endif - tb->size = dc->pc - pc_start; - tb->icount = num_insns; +} + +static void m68k_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps m68k_tr_ops = { + .init_disas_context = m68k_tr_init_disas_context, + .tb_start = m68k_tr_tb_start, + .insn_start = m68k_tr_insn_start, + .breakpoint_check = m68k_tr_breakpoint_check, + .translate_insn = m68k_tr_translate_insn, + .tb_stop = m68k_tr_tb_stop, + .disas_log = m68k_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb) +{ + DisasContext dc; + translator_loop(&m68k_tr_ops, &dc.base, cpu, tb); } static double floatx80_to_double(CPUM68KState *env, uint16_t high, uint64_t low) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 4dc1404800..9b546a2c18 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -72,6 +72,9 @@ static const struct { {NULL, 0}, }; +/* If no specific version gets selected, default to the following. */ +#define DEFAULT_CPU_VERSION "10.0" + static void mb_cpu_set_pc(CPUState *cs, vaddr value) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); @@ -125,6 +128,7 @@ static void mb_cpu_reset(CPUState *s) env->mmu.c_mmu = 3; env->mmu.c_mmu_tlb_access = 3; env->mmu.c_mmu_zones = 16; + env->mmu.c_addr_mask = MAKE_64BIT_MASK(0, cpu->cfg.addr_size); #endif } @@ -141,6 +145,7 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; uint8_t version_code = 0; + const char *version; int i = 0; Error *local_err = NULL; @@ -150,6 +155,12 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) return; } + if (cpu->cfg.addr_size < 32 || cpu->cfg.addr_size > 64) { + error_setg(errp, "addr-size %d is out of range (32 - 64)", + cpu->cfg.addr_size); + return; + } + qemu_init_vcpu(cs); env->pvr.regs[0] = PVR0_USE_EXC_MASK \ @@ -162,8 +173,9 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) | PVR2_FPU_EXC_MASK \ | 0; - for (i = 0; mb_cpu_lookup[i].name && cpu->cfg.version; i++) { - if (strcmp(mb_cpu_lookup[i].name, cpu->cfg.version) == 0) { + version = cpu->cfg.version ? cpu->cfg.version : DEFAULT_CPU_VERSION; + for (i = 0; mb_cpu_lookup[i].name && version; i++) { + if (strcmp(mb_cpu_lookup[i].name, version) == 0) { version_code = mb_cpu_lookup[i].version_id; break; } @@ -195,8 +207,10 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) env->pvr.regs[5] |= cpu->cfg.dcache_writeback ? PVR5_DCACHE_WRITEBACK_MASK : 0; - env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */ - env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17); + env->pvr.regs[10] = 0x0c000000 | /* Default to spartan 3a dsp family. */ + (cpu->cfg.addr_size - 32) << PVR10_ASIZE_SHIFT; + env->pvr.regs[11] = (cpu->cfg.use_mmu ? PVR11_USE_MMU : 0) | + 16 << 17; mcc->parent_realize(dev, errp); } @@ -226,6 +240,14 @@ static Property mb_properties[] = { DEFINE_PROP_UINT32("base-vectors", MicroBlazeCPU, cfg.base_vectors, 0), DEFINE_PROP_BOOL("use-stack-protection", MicroBlazeCPU, cfg.stackprot, false), + /* + * This is the C_ADDR_SIZE synth-time configuration option of the + * MicroBlaze cores. Supported values range between 32 and 64. + * + * When set to > 32, 32bit MicroBlaze can emit load/stores + * with extended addressing. + */ + DEFINE_PROP_UINT8("addr-size", MicroBlazeCPU, cfg.addr_size, 32), /* If use-fpu > 0 - FPU is enabled * If use-fpu = 2 - Floating point conversion and square root instructions * are enabled diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 5be71bc320..3c4e0ba80a 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -23,7 +23,7 @@ #include "qemu-common.h" #include "cpu-qom.h" -#define TARGET_LONG_BITS 32 +#define TARGET_LONG_BITS 64 #define CPUArchState struct CPUMBState @@ -203,6 +203,7 @@ typedef struct CPUMBState CPUMBState; /* Target family PVR mask */ #define PVR10_TARGET_FAMILY_MASK 0xFF000000 +#define PVR10_ASIZE_SHIFT 18 /* MMU descrtiption */ #define PVR11_USE_MMU 0xC0000000 @@ -238,19 +239,19 @@ typedef struct CPUMBState CPUMBState; struct CPUMBState { uint32_t debug; uint32_t btaken; - uint32_t btarget; + uint64_t btarget; uint32_t bimm; uint32_t imm; - uint32_t regs[33]; - uint32_t sregs[24]; + uint32_t regs[32]; + uint64_t sregs[14]; float_status fp_status; /* Stack protectors. Yes, it's a hw feature. */ uint32_t slr, shr; /* lwx/swx reserved address */ #define RES_ADDR_NONE 0xffffffff /* Use 0xffffffff to indicate no reservation */ - uint32_t res_addr; + target_ulong res_addr; uint32_t res_val; /* Internal flags. */ @@ -277,7 +278,7 @@ struct CPUMBState { /* These fields are preserved on reset. */ struct { - uint32_t regs[16]; + uint32_t regs[13]; } pvr; }; @@ -297,6 +298,7 @@ struct MicroBlazeCPU { struct { bool stackprot; uint32_t base_vectors; + uint8_t addr_size; uint8_t use_fpu; uint8_t use_hw_mul; bool use_barrel; @@ -340,8 +342,8 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 -#define TARGET_PHYS_ADDR_SPACE_BITS 32 -#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 64 +#define TARGET_VIRT_ADDR_SPACE_BITS 64 #define CPU_RESOLVING_TYPE TYPE_MICROBLAZE_CPU @@ -358,13 +360,17 @@ int cpu_mb_signal_handler(int host_signum, void *pinfo, static inline int cpu_mmu_index (CPUMBState *env, bool ifetch) { - /* Are we in nommu mode?. */ - if (!(env->sregs[SR_MSR] & MSR_VM)) - return MMU_NOMMU_IDX; + MicroBlazeCPU *cpu = mb_env_get_cpu(env); - if (env->sregs[SR_MSR] & MSR_UM) - return MMU_USER_IDX; - return MMU_KERNEL_IDX; + /* Are we in nommu mode?. */ + if (!(env->sregs[SR_MSR] & MSR_VM) || !cpu->cfg.use_mmu) { + return MMU_NOMMU_IDX; + } + + if (env->sregs[SR_MSR] & MSR_UM) { + return MMU_USER_IDX; + } + return MMU_KERNEL_IDX; } int mb_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index fac6ee9263..bc753793ec 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -54,22 +54,12 @@ int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; unsigned int hit; - unsigned int mmu_available; int r = 1; int prot; - mmu_available = 0; - if (cpu->cfg.use_mmu) { - mmu_available = 1; - if ((cpu->cfg.pvr == C_PVR_FULL) && - (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) { - mmu_available = 0; - } - } - /* Translate if the MMU is available and enabled. */ - if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) { - target_ulong vaddr, paddr; + if (mmu_idx != MMU_NOMMU_IDX) { + uint32_t vaddr, paddr; struct microblaze_mmu_lookup lu; hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx); @@ -152,7 +142,8 @@ void mb_cpu_do_interrupt(CPUState *cs) env->sregs[SR_MSR] |= MSR_EIP; qemu_log_mask(CPU_LOG_INT, - "hw exception at pc=%x ear=%x esr=%x iflags=%x\n", + "hw exception at pc=%" PRIx64 " ear=%" PRIx64 " " + "esr=%" PRIx64 " iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_EAR], env->sregs[SR_ESR], env->iflags); log_cpu_state_mask(CPU_LOG_INT, cs, 0); @@ -175,7 +166,8 @@ void mb_cpu_do_interrupt(CPUState *cs) /* was the branch immprefixed?. */ if (env->bimm) { qemu_log_mask(CPU_LOG_INT, - "bimm exception at pc=%x iflags=%x\n", + "bimm exception at pc=%" PRIx64 " " + "iflags=%x\n", env->sregs[SR_PC], env->iflags); env->regs[17] -= 4; log_cpu_state_mask(CPU_LOG_INT, cs, 0); @@ -193,7 +185,8 @@ void mb_cpu_do_interrupt(CPUState *cs) env->sregs[SR_MSR] |= MSR_EIP; qemu_log_mask(CPU_LOG_INT, - "exception at pc=%x ear=%x iflags=%x\n", + "exception at pc=%" PRIx64 " ear=%" PRIx64 " " + "iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); log_cpu_state_mask(CPU_LOG_INT, cs, 0); env->iflags &= ~(IMM_FLAG | D_FLAG); @@ -230,7 +223,8 @@ void mb_cpu_do_interrupt(CPUState *cs) } #endif qemu_log_mask(CPU_LOG_INT, - "interrupt at pc=%x msr=%x %x iflags=%x\n", + "interrupt at pc=%" PRIx64 " msr=%" PRIx64 " %x " + "iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ @@ -248,7 +242,8 @@ void mb_cpu_do_interrupt(CPUState *cs) assert(!(env->iflags & D_FLAG)); t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; qemu_log_mask(CPU_LOG_INT, - "break at pc=%x msr=%x %x iflags=%x\n", + "break at pc=%" PRIx64 " msr=%" PRIx64 " %x " + "iflags=%x\n", env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); log_cpu_state_mask(CPU_LOG_INT, cs, 0); env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); @@ -274,9 +269,10 @@ hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) CPUMBState *env = &cpu->env; target_ulong vaddr, paddr = 0; struct microblaze_mmu_lookup lu; + int mmu_idx = cpu_mmu_index(env, false); unsigned int hit; - if (env->sregs[SR_MSR] & MSR_VM) { + if (mmu_idx != MMU_NOMMU_IDX) { hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); if (hit) { vaddr = addr & TARGET_PAGE_MASK; diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index 71a6c0858d..2f8bdea22b 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -25,12 +25,12 @@ DEF_HELPER_3(fcmp_ge, i32, env, i32, i32) DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_NO_RWG_SE, i32, i32, i32) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_2(mmu_read, i32, env, i32) -DEF_HELPER_3(mmu_write, void, env, i32, i32) +DEF_HELPER_3(mmu_read, i32, env, i32, i32) +DEF_HELPER_4(mmu_write, void, env, i32, i32, i32) #endif -DEF_HELPER_5(memalign, void, env, i32, i32, i32, i32) -DEF_HELPER_2(stackprot, void, env, i32) +DEF_HELPER_5(memalign, void, env, tl, i32, i32, i32) +DEF_HELPER_2(stackprot, void, env, tl) DEF_HELPER_2(get, i32, i32, i32) DEF_HELPER_3(put, void, i32, i32, i32) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index a0f06758f8..fcf86b12d5 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -22,8 +22,6 @@ #include "cpu.h" #include "exec/exec-all.h" -#define D(x) - static unsigned int tlb_decode_size(unsigned int f) { static const unsigned int sizes[] = { @@ -81,34 +79,29 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, { unsigned int i, hit = 0; unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel; - unsigned int tlb_size; - uint32_t tlb_tag, tlb_rpn, mask, t0; + uint64_t tlb_tag, tlb_rpn, mask; + uint32_t tlb_size, t0; lu->err = ERR_MISS; for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { - uint32_t t, d; + uint64_t t, d; /* Lookup and decode. */ t = mmu->rams[RAM_TAG][i]; - D(qemu_log("TLB %d valid=%d\n", i, t & TLB_VALID)); if (t & TLB_VALID) { tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); if (tlb_size < TARGET_PAGE_SIZE) { - qemu_log("%d pages not supported\n", tlb_size); + qemu_log_mask(LOG_UNIMP, "%d pages not supported\n", tlb_size); abort(); } - mask = ~(tlb_size - 1); + mask = ~((uint64_t)tlb_size - 1); tlb_tag = t & TLB_EPN_MASK; if ((vaddr & mask) != (tlb_tag & mask)) { - D(qemu_log("TLB %d vaddr=%x != tag=%x\n", - i, vaddr & mask, tlb_tag & mask)); continue; } if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) { - D(qemu_log("TLB %d pid=%x != tid=%x\n", - i, mmu->regs[MMU_R_PID], mmu->tids[i])); continue; } @@ -123,7 +116,8 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, t0 &= 0x3; if (tlb_zsel > mmu->c_mmu_zones) { - qemu_log_mask(LOG_GUEST_ERROR, "tlb zone select out of range! %d\n", tlb_zsel); + qemu_log_mask(LOG_GUEST_ERROR, + "tlb zone select out of range! %d\n", tlb_zsel); t0 = 1; /* Ignore. */ } @@ -164,7 +158,7 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, tlb_rpn = d & TLB_RPN_MASK; lu->vaddr = tlb_tag; - lu->paddr = tlb_rpn; + lu->paddr = tlb_rpn & mmu->c_addr_mask; lu->size = tlb_size; lu->err = ERR_HIT; lu->idx = i; @@ -173,62 +167,81 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, } } done: - D(qemu_log("MMU vaddr=%x rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", - vaddr, rw, tlb_wr, tlb_ex, hit)); + qemu_log_mask(CPU_LOG_MMU, + "MMU vaddr=%" PRIx64 " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", + vaddr, rw, tlb_wr, tlb_ex, hit); return hit; } /* Writes/reads to the MMU's special regs end up here. */ -uint32_t mmu_read(CPUMBState *env, uint32_t rn) +uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) { unsigned int i; - uint32_t r; + uint32_t r = 0; if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return 0; } + if (ext && rn != MMU_R_TLBLO) { + qemu_log_mask(LOG_GUEST_ERROR, "Extended access only to TLBLO.\n"); + return 0; + } switch (rn) { /* Reads to HI/LO trig reads from the mmu rams. */ case MMU_R_TLBLO: case MMU_R_TLBHI: if (!(env->mmu.c_mmu_tlb_access & 1)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return 0; } i = env->mmu.regs[MMU_R_TLBX] & 0xff; - r = env->mmu.rams[rn & 1][i]; + r = extract64(env->mmu.rams[rn & 1][i], ext * 32, 32); if (rn == MMU_R_TLBHI) env->mmu.regs[MMU_R_PID] = env->mmu.tids[i]; break; case MMU_R_PID: case MMU_R_ZPR: if (!(env->mmu.c_mmu_tlb_access & 1)) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return 0; } r = env->mmu.regs[rn]; break; - default: + case MMU_R_TLBX: r = env->mmu.regs[rn]; break; + case MMU_R_TLBSX: + qemu_log_mask(LOG_GUEST_ERROR, "TLBSX is write-only.\n"); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Invalid MMU register %d.\n", rn); + break; } - D(qemu_log("%s rn=%d=%x\n", __func__, rn, r)); + qemu_log_mask(CPU_LOG_MMU, "%s rn=%d=%x\n", __func__, rn, r); return r; } -void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) +void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) { MicroBlazeCPU *cpu = mb_env_get_cpu(env); + uint64_t tmp64; unsigned int i; - D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn])); + qemu_log_mask(CPU_LOG_MMU, + "%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn]); if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return; } + if (ext && rn != MMU_R_TLBLO) { + qemu_log_mask(LOG_GUEST_ERROR, "Extended access only to TLBLO.\n"); + return; + } switch (rn) { /* Writes to HI/LO trig writes to the mmu rams. */ @@ -237,18 +250,19 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) i = env->mmu.regs[MMU_R_TLBX] & 0xff; if (rn == MMU_R_TLBHI) { if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0)) - qemu_log_mask(LOG_GUEST_ERROR, "invalidating index %x at pc=%x\n", + qemu_log_mask(LOG_GUEST_ERROR, + "invalidating index %x at pc=%" PRIx64 "\n", i, env->sregs[SR_PC]); env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; mmu_flush_idx(env, i); } - env->mmu.rams[rn & 1][i] = v; - - D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v)); + tmp64 = env->mmu.rams[rn & 1][i]; + env->mmu.rams[rn & 1][i] = deposit64(tmp64, ext * 32, 32, v); break; case MMU_R_ZPR: if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return; } @@ -261,7 +275,8 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) break; case MMU_R_PID: if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return; } @@ -270,13 +285,18 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) env->mmu.regs[rn] = v; } break; + case MMU_R_TLBX: + /* Bit 31 is read-only. */ + env->mmu.regs[rn] = deposit32(env->mmu.regs[rn], 0, 31, v); + break; case MMU_R_TLBSX: { struct microblaze_mmu_lookup lu; int hit; if (env->mmu.c_mmu_tlb_access <= 1) { - qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid access to MMU reg %d\n", rn); return; } @@ -284,12 +304,13 @@ void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) v & TLB_EPN_MASK, 0, cpu_mmu_index(env, false)); if (hit) { env->mmu.regs[MMU_R_TLBX] = lu.idx; - } else - env->mmu.regs[MMU_R_TLBX] |= 0x80000000; + } else { + env->mmu.regs[MMU_R_TLBX] |= R_TBLX_MISS_MASK; + } break; } default: - env->mmu.regs[rn] = v; + qemu_log_mask(LOG_GUEST_ERROR, "Invalid MMU register %d.\n", rn); break; } } diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index 3b7a9983d5..a4272b6356 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -28,7 +28,7 @@ #define RAM_TAG 0 /* Tag portion */ -#define TLB_EPN_MASK 0xFFFFFC00 /* Effective Page Number */ +#define TLB_EPN_MASK MAKE_64BIT_MASK(10, 64 - 10) #define TLB_PAGESZ_MASK 0x00000380 #define TLB_PAGESZ(x) (((x) & 0x7) << 7) #define PAGESZ_1K 0 @@ -42,7 +42,7 @@ #define TLB_VALID 0x00000040 /* Entry is valid */ /* Data portion */ -#define TLB_RPN_MASK 0xFFFFFC00 /* Real Page Number */ +#define TLB_RPN_MASK MAKE_64BIT_MASK(10, 64 - 10) #define TLB_PERM_MASK 0x00000300 #define TLB_EX 0x00000200 /* Instruction execution allowed */ #define TLB_WR 0x00000100 /* Writes permitted */ @@ -54,20 +54,25 @@ #define TLB_M 0x00000002 /* Memory is coherent */ #define TLB_G 0x00000001 /* Memory is guarded from prefetch */ +/* TLBX */ +#define R_TBLX_MISS_SHIFT 31 +#define R_TBLX_MISS_MASK (1U << R_TBLX_MISS_SHIFT) + #define TLB_ENTRIES 64 struct microblaze_mmu { /* Data and tag brams. */ - uint32_t rams[2][TLB_ENTRIES]; + uint64_t rams[2][TLB_ENTRIES]; /* We keep a separate ram for the tids to avoid the 48 bit tag width. */ uint8_t tids[TLB_ENTRIES]; /* Control flops. */ - uint32_t regs[8]; + uint32_t regs[3]; int c_mmu; int c_mmu_tlb_access; int c_mmu_zones; + uint64_t c_addr_mask; /* Mask to apply to physical addresses. */ }; struct microblaze_mmu_lookup @@ -85,6 +90,6 @@ struct microblaze_mmu_lookup unsigned int mmu_translate(struct microblaze_mmu *mmu, struct microblaze_mmu_lookup *lu, target_ulong vaddr, int rw, int mmu_idx); -uint32_t mmu_read(CPUMBState *env, uint32_t rn); -void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v); +uint32_t mmu_read(CPUMBState *env, bool ea, uint32_t rn); +void mmu_write(CPUMBState *env, bool ea, uint32_t rn, uint32_t v); void mmu_init(struct microblaze_mmu *mmu); diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 1b4fe796e7..7cdbbcccae 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -94,16 +94,17 @@ void helper_debug(CPUMBState *env) { int i; - qemu_log("PC=%8.8x\n", env->sregs[SR_PC]); - qemu_log("rmsr=%x resr=%x rear=%x debug[%x] imm=%x iflags=%x\n", + qemu_log("PC=%" PRIx64 "\n", env->sregs[SR_PC]); + qemu_log("rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " " + "debug[%x] imm=%x iflags=%x\n", env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], env->debug, env->imm, env->iflags); - qemu_log("btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", + qemu_log("btaken=%d btarget=%" PRIx64 " mode=%s(saved=%s) eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", - (env->sregs[SR_MSR] & MSR_EIP), - (env->sregs[SR_MSR] & MSR_IE)); + (bool)(env->sregs[SR_MSR] & MSR_EIP), + (bool)(env->sregs[SR_MSR] & MSR_IE)); for (i = 0; i < 32; i++) { qemu_log("r%2.2d=%8.8x ", i, env->regs[i]); if ((i + 1) % 4 == 0) @@ -439,12 +440,14 @@ uint32_t helper_pcmpbf(uint32_t a, uint32_t b) return 0; } -void helper_memalign(CPUMBState *env, uint32_t addr, uint32_t dr, uint32_t wr, +void helper_memalign(CPUMBState *env, target_ulong addr, + uint32_t dr, uint32_t wr, uint32_t mask) { if (addr & mask) { qemu_log_mask(CPU_LOG_INT, - "unaligned access addr=%x mask=%x, wr=%d dr=r%d\n", + "unaligned access addr=" TARGET_FMT_lx + " mask=%x, wr=%d dr=r%d\n", addr, mask, wr, dr); env->sregs[SR_EAR] = addr; env->sregs[SR_ESR] = ESR_EC_UNALIGNED_DATA | (wr << 10) \ @@ -459,10 +462,11 @@ void helper_memalign(CPUMBState *env, uint32_t addr, uint32_t dr, uint32_t wr, } } -void helper_stackprot(CPUMBState *env, uint32_t addr) +void helper_stackprot(CPUMBState *env, target_ulong addr) { if (addr < env->slr || addr > env->shr) { - qemu_log_mask(CPU_LOG_INT, "Stack protector violation at %x %x %x\n", + qemu_log_mask(CPU_LOG_INT, "Stack protector violation at " + TARGET_FMT_lx " %x %x\n", addr, env->slr, env->shr); env->sregs[SR_EAR] = addr; env->sregs[SR_ESR] = ESR_EC_STACKPROT; @@ -472,14 +476,14 @@ void helper_stackprot(CPUMBState *env, uint32_t addr) #if !defined(CONFIG_USER_ONLY) /* Writes/reads to the MMU's special regs end up here. */ -uint32_t helper_mmu_read(CPUMBState *env, uint32_t rn) +uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn) { - return mmu_read(env, rn); + return mmu_read(env, ext, rn); } -void helper_mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) +void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v) { - mmu_write(env, rn, v); + mmu_write(env, ext, rn, v); } void mb_cpu_unassigned_access(CPUState *cs, hwaddr addr, diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 7628b0e25b..78ca265b04 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -52,22 +52,22 @@ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ #define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ -static TCGv env_debug; -static TCGv cpu_R[32]; -static TCGv cpu_SR[18]; -static TCGv env_imm; -static TCGv env_btaken; -static TCGv env_btarget; -static TCGv env_iflags; +static TCGv_i32 env_debug; +static TCGv_i32 cpu_R[32]; +static TCGv_i64 cpu_SR[14]; +static TCGv_i32 env_imm; +static TCGv_i32 env_btaken; +static TCGv_i64 env_btarget; +static TCGv_i32 env_iflags; static TCGv env_res_addr; -static TCGv env_res_val; +static TCGv_i32 env_res_val; #include "exec/gen-icount.h" /* This is the state at translation time. */ typedef struct DisasContext { MicroBlazeCPU *cpu; - target_ulong pc; + uint32_t pc; /* Decoder. */ int type_b; @@ -90,7 +90,6 @@ typedef struct DisasContext { uint32_t jmp_pc; int abort_at_next_insn; - int nr_nops; struct TranslationBlock *tb; int singlestep_enabled; } DisasContext; @@ -105,16 +104,15 @@ static const char *regnames[] = static const char *special_regnames[] = { - "rpc", "rmsr", "sr2", "sr3", "sr4", "sr5", "sr6", "sr7", - "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15", - "sr16", "sr17", "sr18" + "rpc", "rmsr", "sr2", "rear", "sr4", "resr", "sr6", "rfsr", + "sr8", "sr9", "sr10", "rbtr", "sr12", "redr" }; static inline void t_sync_flags(DisasContext *dc) { /* Synch the tb dependent flags between translator and runtime. */ if (dc->tb_flags != dc->synced_flags) { - tcg_gen_movi_tl(env_iflags, dc->tb_flags); + tcg_gen_movi_i32(env_iflags, dc->tb_flags); dc->synced_flags = dc->tb_flags; } } @@ -124,7 +122,7 @@ static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) TCGv_i32 tmp = tcg_const_i32(index); t_sync_flags(dc); - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); dc->is_jmp = DISAS_UPDATE; @@ -143,41 +141,70 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); - tcg_gen_movi_tl(cpu_SR[SR_PC], dest); - tcg_gen_exit_tb((uintptr_t)dc->tb + n); + tcg_gen_movi_i64(cpu_SR[SR_PC], dest); + tcg_gen_exit_tb(dc->tb, n); } else { - tcg_gen_movi_tl(cpu_SR[SR_PC], dest); - tcg_gen_exit_tb(0); + tcg_gen_movi_i64(cpu_SR[SR_PC], dest); + tcg_gen_exit_tb(NULL, 0); } } -static void read_carry(DisasContext *dc, TCGv d) +static void read_carry(DisasContext *dc, TCGv_i32 d) { - tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31); + tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); + tcg_gen_shri_i32(d, d, 31); } /* * write_carry sets the carry bits in MSR based on bit 0 of v. * v[31:1] are ignored. */ -static void write_carry(DisasContext *dc, TCGv v) +static void write_carry(DisasContext *dc, TCGv_i32 v) { - TCGv t0 = tcg_temp_new(); - tcg_gen_shli_tl(t0, v, 31); - tcg_gen_sari_tl(t0, t0, 31); - tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC)); - tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], - ~(MSR_C | MSR_CC)); - tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0); - tcg_temp_free(t0); + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(t0, v); + /* Deposit bit 0 into MSR_C and the alias MSR_CC. */ + tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 2, 1); + tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 31, 1); + tcg_temp_free_i64(t0); } static void write_carryi(DisasContext *dc, bool carry) { - TCGv t0 = tcg_temp_new(); - tcg_gen_movi_tl(t0, carry); + TCGv_i32 t0 = tcg_temp_new_i32(); + tcg_gen_movi_i32(t0, carry); write_carry(dc, t0); - tcg_temp_free(t0); + tcg_temp_free_i32(t0); +} + +/* + * Returns true if the insn an illegal operation. + * If exceptions are enabled, an exception is raised. + */ +static bool trap_illegal(DisasContext *dc, bool cond) +{ + if (cond && (dc->tb_flags & MSR_EE_FLAG) + && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { + tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); + t_gen_raise_exception(dc, EXCP_HW_EXCP); + } + return cond; +} + +/* + * Returns true if the insn is illegal in userspace. + * If exceptions are enabled, an exception is raised. + */ +static bool trap_userspace(DisasContext *dc, bool cond) +{ + int mem_index = cpu_mmu_index(&dc->cpu->env, false); + bool cond_user = cond && mem_index == MMU_USER_IDX; + + if (cond_user && (dc->tb_flags & MSR_EE_FLAG)) { + tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + t_gen_raise_exception(dc, EXCP_HW_EXCP); + } + return cond_user; } /* True if ALU operand b is a small immediate that may deserve @@ -188,13 +215,13 @@ static inline int dec_alu_op_b_is_small_imm(DisasContext *dc) return dc->type_b && !(dc->tb_flags & IMM_FLAG); } -static inline TCGv *dec_alu_op_b(DisasContext *dc) +static inline TCGv_i32 *dec_alu_op_b(DisasContext *dc) { if (dc->type_b) { if (dc->tb_flags & IMM_FLAG) - tcg_gen_ori_tl(env_imm, env_imm, dc->imm); + tcg_gen_ori_i32(env_imm, env_imm, dc->imm); else - tcg_gen_movi_tl(env_imm, (int32_t)((int16_t)dc->imm)); + tcg_gen_movi_i32(env_imm, (int32_t)((int16_t)dc->imm)); return &env_imm; } else return &cpu_R[dc->rb]; @@ -203,7 +230,7 @@ static inline TCGv *dec_alu_op_b(DisasContext *dc) static void dec_add(DisasContext *dc) { unsigned int k, c; - TCGv cf; + TCGv_i32 cf; k = dc->opcode & 4; c = dc->opcode & 2; @@ -217,15 +244,15 @@ static void dec_add(DisasContext *dc) /* k - keep carry, no need to update MSR. */ /* If rd == r0, it's a nop. */ if (dc->rd) { - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); if (c) { /* c - Add carry into the result. */ - cf = tcg_temp_new(); + cf = tcg_temp_new_i32(); read_carry(dc, cf); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free(cf); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_temp_free_i32(cf); } } return; @@ -233,31 +260,31 @@ static void dec_add(DisasContext *dc) /* From now on, we can assume k is zero. So we need to update MSR. */ /* Extract carry. */ - cf = tcg_temp_new(); + cf = tcg_temp_new_i32(); if (c) { read_carry(dc, cf); } else { - tcg_gen_movi_tl(cf, 0); + tcg_gen_movi_i32(cf, 0); } if (dc->rd) { - TCGv ncf = tcg_temp_new(); + TCGv_i32 ncf = tcg_temp_new_i32(); gen_helper_carry(ncf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); write_carry(dc, ncf); - tcg_temp_free(ncf); + tcg_temp_free_i32(ncf); } else { gen_helper_carry(cf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); write_carry(dc, cf); } - tcg_temp_free(cf); + tcg_temp_free_i32(cf); } static void dec_sub(DisasContext *dc) { unsigned int u, cmp, k, c; - TCGv cf, na; + TCGv_i32 cf, na; u = dc->imm & 2; k = dc->opcode & 4; @@ -283,15 +310,15 @@ static void dec_sub(DisasContext *dc) /* k - keep carry, no need to update MSR. */ /* If rd == r0, it's a nop. */ if (dc->rd) { - tcg_gen_sub_tl(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + tcg_gen_sub_i32(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); if (c) { /* c - Add carry into the result. */ - cf = tcg_temp_new(); + cf = tcg_temp_new_i32(); read_carry(dc, cf); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free(cf); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_temp_free_i32(cf); } } return; @@ -299,41 +326,38 @@ static void dec_sub(DisasContext *dc) /* From now on, we can assume k is zero. So we need to update MSR. */ /* Extract carry. And complement a into na. */ - cf = tcg_temp_new(); - na = tcg_temp_new(); + cf = tcg_temp_new_i32(); + na = tcg_temp_new_i32(); if (c) { read_carry(dc, cf); } else { - tcg_gen_movi_tl(cf, 1); + tcg_gen_movi_i32(cf, 1); } /* d = b + ~a + c. carry defaults to 1. */ - tcg_gen_not_tl(na, cpu_R[dc->ra]); + tcg_gen_not_i32(na, cpu_R[dc->ra]); if (dc->rd) { - TCGv ncf = tcg_temp_new(); + TCGv_i32 ncf = tcg_temp_new_i32(); gen_helper_carry(ncf, na, *(dec_alu_op_b(dc)), cf); - tcg_gen_add_tl(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); - tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_gen_add_i32(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); + tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); write_carry(dc, ncf); - tcg_temp_free(ncf); + tcg_temp_free_i32(ncf); } else { gen_helper_carry(cf, na, *(dec_alu_op_b(dc)), cf); write_carry(dc, cf); } - tcg_temp_free(cf); - tcg_temp_free(na); + tcg_temp_free_i32(cf); + tcg_temp_free_i32(na); } static void dec_pattern(DisasContext *dc) { unsigned int mode; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { + return; } mode = dc->opcode & 3; @@ -347,14 +371,14 @@ static void dec_pattern(DisasContext *dc) case 2: LOG_DIS("pcmpeq r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); if (dc->rd) { - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_R[dc->rd], + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); } break; case 3: LOG_DIS("pcmpne r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); if (dc->rd) { - tcg_gen_setcond_tl(TCG_COND_NE, cpu_R[dc->rd], + tcg_gen_setcond_i32(TCG_COND_NE, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); } break; @@ -381,9 +405,9 @@ static void dec_and(DisasContext *dc) return; if (not) { - tcg_gen_andc_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_andc_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } else - tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_and_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } static void dec_or(DisasContext *dc) @@ -395,7 +419,7 @@ static void dec_or(DisasContext *dc) LOG_DIS("or r%d r%d r%d imm=%x\n", dc->rd, dc->ra, dc->rb, dc->imm); if (dc->rd) - tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_or_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } static void dec_xor(DisasContext *dc) @@ -407,43 +431,54 @@ static void dec_xor(DisasContext *dc) LOG_DIS("xor r%d\n", dc->rd); if (dc->rd) - tcg_gen_xor_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_xor_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } -static inline void msr_read(DisasContext *dc, TCGv d) +static inline void msr_read(DisasContext *dc, TCGv_i32 d) { - tcg_gen_mov_tl(d, cpu_SR[SR_MSR]); + tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); } -static inline void msr_write(DisasContext *dc, TCGv v) +static inline void msr_write(DisasContext *dc, TCGv_i32 v) { - TCGv t; + TCGv_i64 t; - t = tcg_temp_new(); + t = tcg_temp_new_i64(); dc->cpustate_changed = 1; /* PVR bit is not writable. */ - tcg_gen_andi_tl(t, v, ~MSR_PVR); - tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); - tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], v); - tcg_temp_free(t); + tcg_gen_extu_i32_i64(t, v); + tcg_gen_andi_i64(t, t, ~MSR_PVR); + tcg_gen_andi_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); + tcg_gen_or_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); + tcg_temp_free_i64(t); } static void dec_msr(DisasContext *dc) { CPUState *cs = CPU(dc->cpu); - TCGv t0, t1; - unsigned int sr, to, rn; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); + TCGv_i32 t0, t1; + unsigned int sr, rn; + bool to, clrset, extended = false; - sr = dc->imm & ((1 << 14) - 1); - to = dc->imm & (1 << 14); + sr = extract32(dc->imm, 0, 14); + to = extract32(dc->imm, 14, 1); + clrset = extract32(dc->imm, 15, 1) == 0; dc->type_b = 1; - if (to) + if (to) { dc->cpustate_changed = 1; + } + + /* Extended MSRs are only available if addr_size > 32. */ + if (dc->cpu->cfg.addr_size > 32) { + /* The E-bit is encoded differently for To/From MSR. */ + static const unsigned int e_bit[] = { 19, 24 }; + + extended = extract32(dc->imm, e_bit[to], 1); + } /* msrclr and msrset. */ - if (!(dc->imm & (1 << 15))) { - unsigned int clr = dc->ir & (1 << 16); + if (clrset) { + bool clr = extract32(dc->ir, 16, 1); LOG_DIS("msr%s r%d imm=%x\n", clr ? "clr" : "set", dc->rd, dc->imm); @@ -453,52 +488,51 @@ static void dec_msr(DisasContext *dc) return; } - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX && (dc->imm != 4 && dc->imm != 0)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_userspace(dc, dc->imm != 4 && dc->imm != 0)) { return; } if (dc->rd) msr_read(dc, cpu_R[dc->rd]); - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); msr_read(dc, t0); - tcg_gen_mov_tl(t1, *(dec_alu_op_b(dc))); + tcg_gen_mov_i32(t1, *(dec_alu_op_b(dc))); if (clr) { - tcg_gen_not_tl(t1, t1); - tcg_gen_and_tl(t0, t0, t1); + tcg_gen_not_i32(t1, t1); + tcg_gen_and_i32(t0, t0, t1); } else - tcg_gen_or_tl(t0, t0, t1); + tcg_gen_or_i32(t0, t0, t1); msr_write(dc, t0); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc + 4); dc->is_jmp = DISAS_UPDATE; return; } - if (to) { - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + if (trap_userspace(dc, to)) { + return; } #if !defined(CONFIG_USER_ONLY) /* Catch read/writes to the mmu block. */ if ((sr & ~0xff) == 0x1000) { + TCGv_i32 tmp_ext = tcg_const_i32(extended); + TCGv_i32 tmp_sr; + sr &= 7; + tmp_sr = tcg_const_i32(sr); LOG_DIS("m%ss sr%d r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); - if (to) - gen_helper_mmu_write(cpu_env, tcg_const_tl(sr), cpu_R[dc->ra]); - else - gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tcg_const_tl(sr)); + if (to) { + gen_helper_mmu_write(cpu_env, tmp_ext, tmp_sr, cpu_R[dc->ra]); + } else { + gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tmp_ext, tmp_sr); + } + tcg_temp_free_i32(tmp_sr); + tcg_temp_free_i32(tmp_ext); return; } #endif @@ -511,20 +545,18 @@ static void dec_msr(DisasContext *dc) case 1: msr_write(dc, cpu_R[dc->ra]); break; - case 0x3: - tcg_gen_mov_tl(cpu_SR[SR_EAR], cpu_R[dc->ra]); - break; - case 0x5: - tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]); - break; - case 0x7: - tcg_gen_andi_tl(cpu_SR[SR_FSR], cpu_R[dc->ra], 31); + case SR_EAR: + case SR_ESR: + case SR_FSR: + tcg_gen_extu_i32_i64(cpu_SR[sr], cpu_R[dc->ra]); break; case 0x800: - tcg_gen_st_tl(cpu_R[dc->ra], cpu_env, offsetof(CPUMBState, slr)); + tcg_gen_st_i32(cpu_R[dc->ra], + cpu_env, offsetof(CPUMBState, slr)); break; case 0x802: - tcg_gen_st_tl(cpu_R[dc->ra], cpu_env, offsetof(CPUMBState, shr)); + tcg_gen_st_i32(cpu_R[dc->ra], + cpu_env, offsetof(CPUMBState, shr)); break; default: cpu_abort(CPU(dc->cpu), "unknown mts reg %x\n", sr); @@ -535,44 +567,32 @@ static void dec_msr(DisasContext *dc) switch (sr) { case 0: - tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); + tcg_gen_movi_i32(cpu_R[dc->rd], dc->pc); break; case 1: msr_read(dc, cpu_R[dc->rd]); break; - case 0x3: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_EAR]); - break; - case 0x5: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]); - break; - case 0x7: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_FSR]); - break; - case 0xb: - tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]); + case SR_EAR: + if (extended) { + tcg_gen_extrh_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); + break; + } + case SR_ESR: + case SR_FSR: + case SR_BTR: + tcg_gen_extrl_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); break; case 0x800: - tcg_gen_ld_tl(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, slr)); + tcg_gen_ld_i32(cpu_R[dc->rd], + cpu_env, offsetof(CPUMBState, slr)); break; case 0x802: - tcg_gen_ld_tl(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, shr)); + tcg_gen_ld_i32(cpu_R[dc->rd], + cpu_env, offsetof(CPUMBState, shr)); break; - case 0x2000: - case 0x2001: - case 0x2002: - case 0x2003: - case 0x2004: - case 0x2005: - case 0x2006: - case 0x2007: - case 0x2008: - case 0x2009: - case 0x200a: - case 0x200b: - case 0x200c: + case 0x2000 ... 0x200c: rn = sr & 0xf; - tcg_gen_ld_tl(cpu_R[dc->rd], + tcg_gen_ld_i32(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, pvr.regs[rn])); break; default: @@ -582,21 +602,17 @@ static void dec_msr(DisasContext *dc) } if (dc->rd == 0) { - tcg_gen_movi_tl(cpu_R[0], 0); + tcg_gen_movi_i32(cpu_R[0], 0); } } /* Multiplier unit. */ static void dec_mul(DisasContext *dc) { - TCGv tmp; + TCGv_i32 tmp; unsigned int subcode; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_hw_mul) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_hw_mul)) { return; } @@ -604,7 +620,7 @@ static void dec_mul(DisasContext *dc) if (dc->type_b) { LOG_DIS("muli r%d r%d %x\n", dc->rd, dc->ra, dc->imm); - tcg_gen_mul_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_mul_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); return; } @@ -613,29 +629,31 @@ static void dec_mul(DisasContext *dc) /* nop??? */ } - tmp = tcg_temp_new(); + tmp = tcg_temp_new_i32(); switch (subcode) { case 0: LOG_DIS("mul r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mul_tl(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_mul_i32(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); break; case 1: LOG_DIS("mulh r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_muls2_tl(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_muls2_i32(tmp, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); break; case 2: LOG_DIS("mulhsu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mulsu2_tl(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_mulsu2_i32(tmp, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); break; case 3: LOG_DIS("mulhu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mulu2_tl(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_mulu2_i32(tmp, cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); break; default: cpu_abort(CPU(dc->cpu), "unknown MUL insn %x\n", subcode); break; } - tcg_temp_free(tmp); + tcg_temp_free_i32(tmp); } /* Div unit. */ @@ -646,10 +664,8 @@ static void dec_div(DisasContext *dc) u = dc->imm & 2; LOG_DIS("div\n"); - if ((dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_div) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_div)) { + return; } if (u) @@ -659,20 +675,16 @@ static void dec_div(DisasContext *dc) gen_helper_divs(cpu_R[dc->rd], cpu_env, *(dec_alu_op_b(dc)), cpu_R[dc->ra]); if (!dc->rd) - tcg_gen_movi_tl(cpu_R[dc->rd], 0); + tcg_gen_movi_i32(cpu_R[dc->rd], 0); } static void dec_barrel(DisasContext *dc) { - TCGv t0; + TCGv_i32 t0; unsigned int imm_w, imm_s; bool s, t, e = false, i = false; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_barrel) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_barrel)) { return; } @@ -710,45 +722,45 @@ static void dec_barrel(DisasContext *dc) imm_s, width); } } else { - t0 = tcg_temp_new(); + t0 = tcg_temp_new_i32(); - tcg_gen_mov_tl(t0, *(dec_alu_op_b(dc))); - tcg_gen_andi_tl(t0, t0, 31); + tcg_gen_mov_i32(t0, *(dec_alu_op_b(dc))); + tcg_gen_andi_i32(t0, t0, 31); if (s) { - tcg_gen_shl_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + tcg_gen_shl_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); } else { if (t) { - tcg_gen_sar_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + tcg_gen_sar_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); } else { - tcg_gen_shr_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); + tcg_gen_shr_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); } } - tcg_temp_free(t0); + tcg_temp_free_i32(t0); } } static void dec_bit(DisasContext *dc) { CPUState *cs = CPU(dc->cpu); - TCGv t0; + TCGv_i32 t0; unsigned int op; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); op = dc->ir & ((1 << 9) - 1); switch (op) { case 0x21: /* src. */ - t0 = tcg_temp_new(); + t0 = tcg_temp_new_i32(); LOG_DIS("src r%d r%d\n", dc->rd, dc->ra); - tcg_gen_andi_tl(t0, cpu_SR[SR_MSR], MSR_CC); + tcg_gen_extrl_i64_i32(t0, cpu_SR[SR_MSR]); + tcg_gen_andi_i32(t0, t0, MSR_CC); write_carry(dc, cpu_R[dc->ra]); if (dc->rd) { - tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); - tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->rd], t0); + tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_or_i32(cpu_R[dc->rd], cpu_R[dc->rd], t0); } - tcg_temp_free(t0); + tcg_temp_free_i32(t0); break; case 0x1: @@ -760,9 +772,9 @@ static void dec_bit(DisasContext *dc) write_carry(dc, cpu_R[dc->ra]); if (dc->rd) { if (op == 0x41) - tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); else - tcg_gen_sari_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); + tcg_gen_sari_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); } break; case 0x60: @@ -779,29 +791,16 @@ static void dec_bit(DisasContext *dc) case 0x76: /* wdc. */ LOG_DIS("wdc r%d\n", dc->ra); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + trap_userspace(dc, true); break; case 0x68: /* wic. */ LOG_DIS("wic r%d\n", dc->ra); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } + trap_userspace(dc, true); break; case 0xe0: - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && !dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { + return; } if (dc->cpu->cfg.use_pcmp_instr) { tcg_gen_clzi_i32(cpu_R[dc->rd], cpu_R[dc->ra], 32); @@ -828,101 +827,128 @@ static inline void sync_jmpstate(DisasContext *dc) { if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { if (dc->jmp == JMP_DIRECT) { - tcg_gen_movi_tl(env_btaken, 1); + tcg_gen_movi_i32(env_btaken, 1); } dc->jmp = JMP_INDIRECT; - tcg_gen_movi_tl(env_btarget, dc->jmp_pc); + tcg_gen_movi_i64(env_btarget, dc->jmp_pc); } } static void dec_imm(DisasContext *dc) { LOG_DIS("imm %x\n", dc->imm << 16); - tcg_gen_movi_tl(env_imm, (dc->imm << 16)); + tcg_gen_movi_i32(env_imm, (dc->imm << 16)); dc->tb_flags |= IMM_FLAG; dc->clear_imm = 0; } -static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) +static inline void compute_ldst_addr(DisasContext *dc, bool ea, TCGv t) { - unsigned int extimm = dc->tb_flags & IMM_FLAG; - /* Should be set to one if r1 is used by loadstores. */ - int stackprot = 0; + bool extimm = dc->tb_flags & IMM_FLAG; + /* Should be set to true if r1 is used by loadstores. */ + bool stackprot = false; + TCGv_i32 t32; /* All load/stores use ra. */ if (dc->ra == 1 && dc->cpu->cfg.stackprot) { - stackprot = 1; + stackprot = true; } /* Treat the common cases first. */ if (!dc->type_b) { - /* If any of the regs is r0, return a ptr to the other. */ + if (ea) { + int addr_size = dc->cpu->cfg.addr_size; + + if (addr_size == 32) { + tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); + return; + } + + tcg_gen_concat_i32_i64(t, cpu_R[dc->rb], cpu_R[dc->ra]); + if (addr_size < 64) { + /* Mask off out of range bits. */ + tcg_gen_andi_i64(t, t, MAKE_64BIT_MASK(0, addr_size)); + } + return; + } + + /* If any of the regs is r0, set t to the value of the other reg. */ if (dc->ra == 0) { - return &cpu_R[dc->rb]; + tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); + return; } else if (dc->rb == 0) { - return &cpu_R[dc->ra]; + tcg_gen_extu_i32_tl(t, cpu_R[dc->ra]); + return; } if (dc->rb == 1 && dc->cpu->cfg.stackprot) { - stackprot = 1; + stackprot = true; } - *t = tcg_temp_new(); - tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]); + t32 = tcg_temp_new_i32(); + tcg_gen_add_i32(t32, cpu_R[dc->ra], cpu_R[dc->rb]); + tcg_gen_extu_i32_tl(t, t32); + tcg_temp_free_i32(t32); if (stackprot) { - gen_helper_stackprot(cpu_env, *t); + gen_helper_stackprot(cpu_env, t); } - return t; + return; } /* Immediate. */ + t32 = tcg_temp_new_i32(); if (!extimm) { - if (dc->imm == 0) { - return &cpu_R[dc->ra]; - } - *t = tcg_temp_new(); - tcg_gen_movi_tl(*t, (int32_t)((int16_t)dc->imm)); - tcg_gen_add_tl(*t, cpu_R[dc->ra], *t); + tcg_gen_addi_i32(t32, cpu_R[dc->ra], (int16_t)dc->imm); } else { - *t = tcg_temp_new(); - tcg_gen_add_tl(*t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_add_i32(t32, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } + tcg_gen_extu_i32_tl(t, t32); + tcg_temp_free_i32(t32); if (stackprot) { - gen_helper_stackprot(cpu_env, *t); + gen_helper_stackprot(cpu_env, t); } - return t; + return; } static void dec_load(DisasContext *dc) { - TCGv t, v, *addr; - unsigned int size, rev = 0, ex = 0; + TCGv_i32 v; + TCGv addr; + unsigned int size; + bool rev = false, ex = false, ea = false; + int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGMemOp mop; mop = dc->opcode & 3; size = 1 << mop; if (!dc->type_b) { - rev = (dc->ir >> 9) & 1; - ex = (dc->ir >> 10) & 1; + ea = extract32(dc->ir, 7, 1); + rev = extract32(dc->ir, 9, 1); + ex = extract32(dc->ir, 10, 1); } mop |= MO_TE; if (rev) { mop ^= MO_BSWAP; } - if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, size > 4)) { return; } - LOG_DIS("l%d%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : ""); + if (trap_userspace(dc, ea)) { + return; + } + + LOG_DIS("l%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", + ex ? "x" : "", + ea ? "ea" : ""); t_sync_flags(dc); - addr = compute_ldst_addr(dc, &t); + addr = tcg_temp_new(); + compute_ldst_addr(dc, ea, addr); + /* Extended addressing bypasses the MMU. */ + mem_index = ea ? MMU_NOMMU_IDX : mem_index; /* * When doing reverse accesses we need to do two things. @@ -941,18 +967,10 @@ static void dec_load(DisasContext *dc) 11 -> 00 */ TCGv low = tcg_temp_new(); - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_mov_tl(t, *addr); - addr = &t; - } - - tcg_gen_andi_tl(low, t, 3); + tcg_gen_andi_tl(low, addr, 3); tcg_gen_sub_tl(low, tcg_const_tl(3), low); - tcg_gen_andi_tl(t, t, ~3); - tcg_gen_or_tl(t, t, low); - tcg_gen_mov_tl(env_imm, t); + tcg_gen_andi_tl(addr, addr, ~3); + tcg_gen_or_tl(addr, addr, low); tcg_temp_free(low); break; } @@ -960,14 +978,7 @@ static void dec_load(DisasContext *dc) case 2: /* 00 -> 10 10 -> 00. */ - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_xori_tl(t, *addr, 2); - addr = &t; - } else { - tcg_gen_xori_tl(t, t, 2); - } + tcg_gen_xori_tl(addr, addr, 2); break; default: cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); @@ -977,13 +988,7 @@ static void dec_load(DisasContext *dc) /* lwx does not throw unaligned access errors, so force alignment */ if (ex) { - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_mov_tl(t, *addr); - addr = &t; - } - tcg_gen_andi_tl(t, t, ~3); + tcg_gen_andi_tl(addr, addr, ~3); } /* If we get a fault on a dslot, the jmpstate better be in sync. */ @@ -996,90 +1001,92 @@ static void dec_load(DisasContext *dc) * into v. If the load succeeds, we verify alignment of the * address and if that succeeds we write into the destination reg. */ - v = tcg_temp_new(); - tcg_gen_qemu_ld_tl(v, *addr, cpu_mmu_index(&dc->cpu->env, false), mop); + v = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(v, addr, mem_index, mop); if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); - gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), - tcg_const_tl(0), tcg_const_tl(size - 1)); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); + gen_helper_memalign(cpu_env, addr, tcg_const_i32(dc->rd), + tcg_const_i32(0), tcg_const_i32(size - 1)); } if (ex) { - tcg_gen_mov_tl(env_res_addr, *addr); - tcg_gen_mov_tl(env_res_val, v); + tcg_gen_mov_tl(env_res_addr, addr); + tcg_gen_mov_i32(env_res_val, v); } if (dc->rd) { - tcg_gen_mov_tl(cpu_R[dc->rd], v); + tcg_gen_mov_i32(cpu_R[dc->rd], v); } - tcg_temp_free(v); + tcg_temp_free_i32(v); if (ex) { /* lwx */ /* no support for AXI exclusive so always clear C */ write_carryi(dc, 0); } - if (addr == &t) - tcg_temp_free(t); + tcg_temp_free(addr); } static void dec_store(DisasContext *dc) { - TCGv t, *addr, swx_addr; + TCGv addr; TCGLabel *swx_skip = NULL; - unsigned int size, rev = 0, ex = 0; + unsigned int size; + bool rev = false, ex = false, ea = false; + int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGMemOp mop; mop = dc->opcode & 3; size = 1 << mop; if (!dc->type_b) { - rev = (dc->ir >> 9) & 1; - ex = (dc->ir >> 10) & 1; + ea = extract32(dc->ir, 7, 1); + rev = extract32(dc->ir, 9, 1); + ex = extract32(dc->ir, 10, 1); } mop |= MO_TE; if (rev) { mop ^= MO_BSWAP; } - if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, size > 4)) { return; } - LOG_DIS("s%d%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : ""); + trap_userspace(dc, ea); + + LOG_DIS("s%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", + ex ? "x" : "", + ea ? "ea" : ""); t_sync_flags(dc); /* If we get a fault on a dslot, the jmpstate better be in sync. */ sync_jmpstate(dc); - addr = compute_ldst_addr(dc, &t); + /* SWX needs a temp_local. */ + addr = ex ? tcg_temp_local_new() : tcg_temp_new(); + compute_ldst_addr(dc, ea, addr); + /* Extended addressing bypasses the MMU. */ + mem_index = ea ? MMU_NOMMU_IDX : mem_index; - swx_addr = tcg_temp_local_new(); if (ex) { /* swx */ - TCGv tval; + TCGv_i32 tval; - /* Force addr into the swx_addr. */ - tcg_gen_mov_tl(swx_addr, *addr); - addr = &swx_addr; /* swx does not throw unaligned access errors, so force alignment */ - tcg_gen_andi_tl(swx_addr, swx_addr, ~3); + tcg_gen_andi_tl(addr, addr, ~3); write_carryi(dc, 1); swx_skip = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, swx_addr, swx_skip); + tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, addr, swx_skip); /* Compare the value loaded at lwx with current contents of the reserved location. FIXME: This only works for system emulation where we can expect this compare and the following write to be atomic. For user emulation we need to add atomicity between threads. */ - tval = tcg_temp_new(); - tcg_gen_qemu_ld_tl(tval, swx_addr, cpu_mmu_index(&dc->cpu->env, false), - MO_TEUL); - tcg_gen_brcond_tl(TCG_COND_NE, env_res_val, tval, swx_skip); + tval = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(tval, addr, cpu_mmu_index(&dc->cpu->env, false), + MO_TEUL); + tcg_gen_brcond_i32(TCG_COND_NE, env_res_val, tval, swx_skip); write_carryi(dc, 0); - tcg_temp_free(tval); + tcg_temp_free_i32(tval); } if (rev && size != 4) { @@ -1093,18 +1100,10 @@ static void dec_store(DisasContext *dc) 11 -> 00 */ TCGv low = tcg_temp_new(); - /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_mov_tl(t, *addr); - addr = &t; - } - - tcg_gen_andi_tl(low, t, 3); + tcg_gen_andi_tl(low, addr, 3); tcg_gen_sub_tl(low, tcg_const_tl(3), low); - tcg_gen_andi_tl(t, t, ~3); - tcg_gen_or_tl(t, t, low); - tcg_gen_mov_tl(env_imm, t); + tcg_gen_andi_tl(addr, addr, ~3); + tcg_gen_or_tl(addr, addr, low); tcg_temp_free(low); break; } @@ -1113,79 +1112,74 @@ static void dec_store(DisasContext *dc) /* 00 -> 10 10 -> 00. */ /* Force addr into the temp. */ - if (addr != &t) { - t = tcg_temp_new(); - tcg_gen_xori_tl(t, *addr, 2); - addr = &t; - } else { - tcg_gen_xori_tl(t, t, 2); - } + tcg_gen_xori_tl(addr, addr, 2); break; default: cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); break; } } - tcg_gen_qemu_st_tl(cpu_R[dc->rd], *addr, cpu_mmu_index(&dc->cpu->env, false), mop); + tcg_gen_qemu_st_i32(cpu_R[dc->rd], addr, mem_index, mop); /* Verify alignment if needed. */ if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); /* FIXME: if the alignment is wrong, we should restore the value * in memory. One possible way to achieve this is to probe * the MMU prior to the memaccess, thay way we could put * the alignment checks in between the probe and the mem * access. */ - gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), - tcg_const_tl(1), tcg_const_tl(size - 1)); + gen_helper_memalign(cpu_env, addr, tcg_const_i32(dc->rd), + tcg_const_i32(1), tcg_const_i32(size - 1)); } if (ex) { gen_set_label(swx_skip); } - tcg_temp_free(swx_addr); - if (addr == &t) - tcg_temp_free(t); + tcg_temp_free(addr); } static inline void eval_cc(DisasContext *dc, unsigned int cc, - TCGv d, TCGv a, TCGv b) + TCGv_i32 d, TCGv_i32 a) { + static const int mb_to_tcg_cc[] = { + [CC_EQ] = TCG_COND_EQ, + [CC_NE] = TCG_COND_NE, + [CC_LT] = TCG_COND_LT, + [CC_LE] = TCG_COND_LE, + [CC_GE] = TCG_COND_GE, + [CC_GT] = TCG_COND_GT, + }; + switch (cc) { - case CC_EQ: - tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b); - break; - case CC_NE: - tcg_gen_setcond_tl(TCG_COND_NE, d, a, b); - break; - case CC_LT: - tcg_gen_setcond_tl(TCG_COND_LT, d, a, b); - break; - case CC_LE: - tcg_gen_setcond_tl(TCG_COND_LE, d, a, b); - break; - case CC_GE: - tcg_gen_setcond_tl(TCG_COND_GE, d, a, b); - break; - case CC_GT: - tcg_gen_setcond_tl(TCG_COND_GT, d, a, b); - break; - default: - cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); - break; + case CC_EQ: + case CC_NE: + case CC_LT: + case CC_LE: + case CC_GE: + case CC_GT: + tcg_gen_setcondi_i32(mb_to_tcg_cc[cc], d, a, 0); + break; + default: + cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); + break; } } -static void eval_cond_jmp(DisasContext *dc, TCGv pc_true, TCGv pc_false) +static void eval_cond_jmp(DisasContext *dc, TCGv_i64 pc_true, TCGv_i64 pc_false) { - TCGLabel *l1 = gen_new_label(); - /* Conditional jmp. */ - tcg_gen_mov_tl(cpu_SR[SR_PC], pc_false); - tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, l1); - tcg_gen_mov_tl(cpu_SR[SR_PC], pc_true); - gen_set_label(l1); + TCGv_i64 tmp_btaken = tcg_temp_new_i64(); + TCGv_i64 tmp_zero = tcg_const_i64(0); + + tcg_gen_extu_i32_i64(tmp_btaken, env_btaken); + tcg_gen_movcond_i64(TCG_COND_NE, cpu_SR[SR_PC], + tmp_btaken, tmp_zero, + pc_true, pc_false); + + tcg_temp_free_i64(tmp_btaken); + tcg_temp_free_i64(tmp_zero); } static void dec_bcc(DisasContext *dc) @@ -1201,28 +1195,28 @@ static void dec_bcc(DisasContext *dc) if (dslot) { dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; - tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), cpu_env, offsetof(CPUMBState, bimm)); } if (dec_alu_op_b_is_small_imm(dc)) { int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */ - tcg_gen_movi_tl(env_btarget, dc->pc + offset); + tcg_gen_movi_i64(env_btarget, dc->pc + offset); dc->jmp = JMP_DIRECT_CC; dc->jmp_pc = dc->pc + offset; } else { dc->jmp = JMP_INDIRECT; - tcg_gen_movi_tl(env_btarget, dc->pc); - tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); + tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); } - eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0)); + eval_cc(dc, cc, env_btaken, cpu_R[dc->ra]); } static void dec_br(DisasContext *dc) { unsigned int dslot, link, abs, mbar; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); dslot = dc->ir & (1 << 20); abs = dc->ir & (1 << 19); @@ -1242,7 +1236,7 @@ static void dec_br(DisasContext *dc) tcg_gen_st_i32(tmp_1, cpu_env, -offsetof(MicroBlazeCPU, env) +offsetof(CPUState, halted)); - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc + 4); gen_helper_raise_exception(cpu_env, tmp_hlt); tcg_temp_free_i32(tmp_hlt); tcg_temp_free_i32(tmp_1); @@ -1263,23 +1257,21 @@ static void dec_br(DisasContext *dc) if (dslot) { dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; - tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), cpu_env, offsetof(CPUMBState, bimm)); } if (link && dc->rd) - tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); + tcg_gen_movi_i32(cpu_R[dc->rd], dc->pc); dc->jmp = JMP_INDIRECT; if (abs) { - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_mov_tl(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btaken, 1); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); if (link && !dslot) { if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18)) t_gen_raise_exception(dc, EXCP_BREAK); if (dc->imm == 0) { - if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_userspace(dc, true)) { return; } @@ -1291,116 +1283,115 @@ static void dec_br(DisasContext *dc) dc->jmp = JMP_DIRECT; dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); } else { - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_movi_tl(env_btarget, dc->pc); - tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btaken, 1); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); + tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); } } } static inline void do_rti(DisasContext *dc) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - tcg_gen_shri_tl(t0, cpu_SR[SR_MSR], 1); - tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_IE); - tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); + tcg_gen_shri_i32(t0, t1, 1); + tcg_gen_ori_i32(t1, t1, MSR_IE); + tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); - tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_tl(t1, t1, t0); + tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(t1, t1, t0); msr_write(dc, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); dc->tb_flags &= ~DRTI_FLAG; } static inline void do_rtb(DisasContext *dc) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); - tcg_gen_andi_tl(t1, cpu_SR[SR_MSR], ~MSR_BIP); - tcg_gen_shri_tl(t0, t1, 1); - tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); + tcg_gen_andi_i32(t1, t1, ~MSR_BIP); + tcg_gen_shri_i32(t0, t1, 1); + tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); - tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_tl(t1, t1, t0); + tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(t1, t1, t0); msr_write(dc, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); dc->tb_flags &= ~DRTB_FLAG; } static inline void do_rte(DisasContext *dc) { - TCGv t0, t1; - t0 = tcg_temp_new(); - t1 = tcg_temp_new(); + TCGv_i32 t0, t1; + t0 = tcg_temp_new_i32(); + t1 = tcg_temp_new_i32(); - tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_EE); - tcg_gen_andi_tl(t1, t1, ~MSR_EIP); - tcg_gen_shri_tl(t0, t1, 1); - tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); + tcg_gen_ori_i32(t1, t1, MSR_EE); + tcg_gen_andi_i32(t1, t1, ~MSR_EIP); + tcg_gen_shri_i32(t0, t1, 1); + tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); - tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_tl(t1, t1, t0); + tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(t1, t1, t0); msr_write(dc, t1); - tcg_temp_free(t1); - tcg_temp_free(t0); + tcg_temp_free_i32(t1); + tcg_temp_free_i32(t0); dc->tb_flags &= ~DRTE_FLAG; } static void dec_rts(DisasContext *dc) { unsigned int b_bit, i_bit, e_bit; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); + TCGv_i64 tmp64; i_bit = dc->ir & (1 << 21); b_bit = dc->ir & (1 << 22); e_bit = dc->ir & (1 << 23); + if (trap_userspace(dc, i_bit || b_bit || e_bit)) { + return; + } + dc->delayed_branch = 2; dc->tb_flags |= D_FLAG; - tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), + tcg_gen_st_i32(tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)), cpu_env, offsetof(CPUMBState, bimm)); if (i_bit) { LOG_DIS("rtid ir=%x\n", dc->ir); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } dc->tb_flags |= DRTI_FLAG; } else if (b_bit) { LOG_DIS("rtbd ir=%x\n", dc->ir); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } dc->tb_flags |= DRTB_FLAG; } else if (e_bit) { LOG_DIS("rted ir=%x\n", dc->ir); - if ((dc->tb_flags & MSR_EE_FLAG) - && mem_index == MMU_USER_IDX) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } dc->tb_flags |= DRTE_FLAG; } else LOG_DIS("rts ir=%x\n", dc->ir); dc->jmp = JMP_INDIRECT; - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_movi_i32(env_btaken, 1); + + tmp64 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); + tcg_gen_extu_i32_i64(tmp64, cpu_R[dc->ra]); + tcg_gen_add_i64(env_btarget, env_btarget, tmp64); + tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); + tcg_temp_free_i64(tmp64); } static int dec_check_fpuv2(DisasContext *dc) { if ((dc->cpu->cfg.use_fpu != 2) && (dc->tb_flags & MSR_EE_FLAG)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU); + tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_FPU); t_gen_raise_exception(dc, EXCP_HW_EXCP); } return (dc->cpu->cfg.use_fpu == 2) ? 0 : PVR2_USE_FPU2_MASK; @@ -1410,11 +1401,7 @@ static void dec_fpu(DisasContext *dc) { unsigned int fpu_insn; - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && (dc->cpu->cfg.use_fpu != 1)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, !dc->cpu->cfg.use_fpu)) { return; } @@ -1513,10 +1500,7 @@ static void dec_fpu(DisasContext *dc) static void dec_null(DisasContext *dc) { - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_illegal(dc, true)) { return; } qemu_log_mask(LOG_GUEST_ERROR, "unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode); @@ -1526,37 +1510,34 @@ static void dec_null(DisasContext *dc) /* Insns connected to FSL or AXI stream attached devices. */ static void dec_stream(DisasContext *dc) { - int mem_index = cpu_mmu_index(&dc->cpu->env, false); TCGv_i32 t_id, t_ctrl; int ctrl; LOG_DIS("%s%s imm=%x\n", dc->rd ? "get" : "put", dc->type_b ? "" : "d", dc->imm); - if ((dc->tb_flags & MSR_EE_FLAG) && (mem_index == MMU_USER_IDX)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (trap_userspace(dc, true)) { return; } - t_id = tcg_temp_new(); + t_id = tcg_temp_new_i32(); if (dc->type_b) { - tcg_gen_movi_tl(t_id, dc->imm & 0xf); + tcg_gen_movi_i32(t_id, dc->imm & 0xf); ctrl = dc->imm >> 10; } else { - tcg_gen_andi_tl(t_id, cpu_R[dc->rb], 0xf); + tcg_gen_andi_i32(t_id, cpu_R[dc->rb], 0xf); ctrl = dc->imm >> 5; } - t_ctrl = tcg_const_tl(ctrl); + t_ctrl = tcg_const_i32(ctrl); if (dc->rd == 0) { gen_helper_put(t_id, t_ctrl, cpu_R[dc->ra]); } else { gen_helper_get(cpu_R[dc->rd], t_id, t_ctrl); } - tcg_temp_free(t_id); - tcg_temp_free(t_ctrl); + tcg_temp_free_i32(t_id); + tcg_temp_free_i32(t_ctrl); } static struct decoder_info { @@ -1594,23 +1575,12 @@ static inline void decode(DisasContext *dc, uint32_t ir) dc->ir = ir; LOG_DIS("%8.8x\t", dc->ir); - if (dc->ir) - dc->nr_nops = 0; - else { - if ((dc->tb_flags & MSR_EE_FLAG) - && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) - && (dc->cpu->env.pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK)) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - return; - } - - LOG_DIS("nr_nops=%d\t", dc->nr_nops); - dc->nr_nops++; - if (dc->nr_nops > 4) { - cpu_abort(CPU(dc->cpu), "fetching nop sequence\n"); - } + if (ir == 0) { + trap_illegal(dc, dc->cpu->env.pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK); + /* Don't decode nop/zero instructions any further. */ + return; } + /* bit 2 seems to indicate insn type. */ dc->type_b = ir & (1 << 29); @@ -1637,8 +1607,8 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) uint32_t pc_start; struct DisasContext ctx; struct DisasContext *dc = &ctx; - uint32_t next_page_start, org_flags; - target_ulong npc; + uint32_t page_start, org_flags; + uint32_t npc; int num_insns; int max_insns; @@ -1657,13 +1627,12 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) dc->singlestep_enabled = cs->singlestep_enabled; dc->cpustate_changed = 0; dc->abort_at_next_insn = 0; - dc->nr_nops = 0; if (pc_start & 3) { cpu_abort(cs, "Microblaze: unaligned PC=%x\n", pc_start); } - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { @@ -1681,7 +1650,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) #if SIM_COMPAT if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { - tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); + tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); gen_helper_debug(); } #endif @@ -1723,7 +1692,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) dc->tb_flags &= ~D_FLAG; /* If it is a direct jump, try direct chaining. */ if (dc->jmp == JMP_INDIRECT) { - eval_cond_jmp(dc, env_btarget, tcg_const_tl(dc->pc)); + eval_cond_jmp(dc, env_btarget, tcg_const_i64(dc->pc)); dc->is_jmp = DISAS_JUMP; } else if (dc->jmp == JMP_DIRECT) { t_sync_flags(dc); @@ -1733,7 +1702,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) TCGLabel *l1 = gen_new_label(); t_sync_flags(dc); /* Conditional jmp. */ - tcg_gen_brcondi_tl(TCG_COND_NE, env_btaken, 0, l1); + tcg_gen_brcondi_i32(TCG_COND_NE, env_btaken, 0, l1); gen_goto_tb(dc, 1, dc->pc); gen_set_label(l1); gen_goto_tb(dc, 0, dc->jmp_pc); @@ -1749,14 +1718,14 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } while (!dc->is_jmp && !dc->cpustate_changed && !tcg_op_buf_full() && !singlestep - && (dc->pc < next_page_start) + && (dc->pc - page_start < TARGET_PAGE_SIZE) && num_insns < max_insns); npc = dc->pc; if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { if (dc->tb_flags & D_FLAG) { dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + tcg_gen_movi_i64(cpu_SR[SR_PC], npc); sync_jmpstate(dc); } else npc = dc->jmp_pc; @@ -1768,7 +1737,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (dc->is_jmp == DISAS_NEXT && (dc->cpustate_changed || org_flags != dc->tb_flags)) { dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + tcg_gen_movi_i64(cpu_SR[SR_PC], npc); } t_sync_flags(dc); @@ -1776,7 +1745,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); if (dc->is_jmp != DISAS_JUMP) { - tcg_gen_movi_tl(cpu_SR[SR_PC], npc); + tcg_gen_movi_i64(cpu_SR[SR_PC], npc); } gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); @@ -1790,7 +1759,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: /* nothing more to generate */ @@ -1826,17 +1795,19 @@ void mb_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, if (!env || !f) return; - cpu_fprintf(f, "IN: PC=%x %s\n", + cpu_fprintf(f, "IN: PC=%" PRIx64 " %s\n", env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC])); - cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug=%x imm=%x iflags=%x fsr=%x\n", + cpu_fprintf(f, "rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " " + "debug=%x imm=%x iflags=%x fsr=%" PRIx64 "\n", env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], env->debug, env->imm, env->iflags, env->sregs[SR_FSR]); - cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", + cpu_fprintf(f, "btaken=%d btarget=%" PRIx64 " mode=%s(saved=%s) " + "eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", - (env->sregs[SR_MSR] & MSR_EIP), - (env->sregs[SR_MSR] & MSR_IE)); + (bool)(env->sregs[SR_MSR] & MSR_EIP), + (bool)(env->sregs[SR_MSR] & MSR_IE)); for (i = 0; i < 32; i++) { cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); @@ -1850,34 +1821,34 @@ void mb_tcg_init(void) { int i; - env_debug = tcg_global_mem_new(cpu_env, + env_debug = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, debug), "debug0"); - env_iflags = tcg_global_mem_new(cpu_env, + env_iflags = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, iflags), "iflags"); - env_imm = tcg_global_mem_new(cpu_env, + env_imm = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, imm), "imm"); - env_btarget = tcg_global_mem_new(cpu_env, + env_btarget = tcg_global_mem_new_i64(cpu_env, offsetof(CPUMBState, btarget), "btarget"); - env_btaken = tcg_global_mem_new(cpu_env, + env_btaken = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, btaken), "btaken"); env_res_addr = tcg_global_mem_new(cpu_env, offsetof(CPUMBState, res_addr), "res_addr"); - env_res_val = tcg_global_mem_new(cpu_env, + env_res_val = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, res_val), "res_val"); for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { - cpu_R[i] = tcg_global_mem_new(cpu_env, + cpu_R[i] = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMBState, regs[i]), regnames[i]); } for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) { - cpu_SR[i] = tcg_global_mem_new(cpu_env, + cpu_SR[i] = tcg_global_mem_new_i64(cpu_env, offsetof(CPUMBState, sregs[i]), special_regnames[i]); } diff --git a/target/mips/dsp_helper.c b/target/mips/dsp_helper.c index f152fea34a..739b69dd45 100644 --- a/target/mips/dsp_helper.c +++ b/target/mips/dsp_helper.c @@ -3274,14 +3274,11 @@ target_ulong helper_dextr_l(target_ulong ac, target_ulong shift, CPUMIPSState *env) { uint64_t temp[3]; - target_ulong result; shift = shift & 0x3F; mipsdsp_rndrashift_acc(temp, ac, shift, env); - result = (temp[1] << 63) | (temp[0] >> 1); - - return result; + return (temp[1] << 63) | (temp[0] >> 1); } target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, @@ -3289,7 +3286,6 @@ target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, { uint64_t temp[3]; uint32_t temp128; - target_ulong result; shift = shift & 0x3F; mipsdsp_rndrashift_acc(temp, ac, shift, env); @@ -3309,9 +3305,7 @@ target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, set_DSPControl_overflow_flag(1, 23, env); } - result = (temp[1] << 63) | (temp[0] >> 1); - - return result; + return (temp[1] << 63) | (temp[0] >> 1); } target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, @@ -3319,7 +3313,6 @@ target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, { uint64_t temp[3]; uint32_t temp128; - target_ulong result; shift = shift & 0x3F; mipsdsp_rndrashift_acc(temp, ac, shift, env); @@ -3345,9 +3338,7 @@ target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, } set_DSPControl_overflow_flag(1, 23, env); } - result = (temp[1] << 63) | (temp[0] >> 1); - - return result; + return (temp[1] << 63) | (temp[0] >> 1); } #endif diff --git a/target/mips/gdbstub.c b/target/mips/gdbstub.c index 6d1fb70f2c..18e0e6dce4 100644 --- a/target/mips/gdbstub.c +++ b/target/mips/gdbstub.c @@ -39,7 +39,7 @@ int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr0); default: if (env->CP0_Status & (1 << CP0St_FR)) { - return gdb_get_regl(mem_buf, + return gdb_get_reg64(mem_buf, env->active_fpu.fpr[n - 38].d); } else { return gdb_get_regl(mem_buf, @@ -100,6 +100,7 @@ int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) break; default: if (env->CP0_Status & (1 << CP0St_FR)) { + uint64_t tmp = ldq_p(mem_buf); env->active_fpu.fpr[n - 38].d = tmp; } else { env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX] = tmp; diff --git a/target/mips/msa_helper.c b/target/mips/msa_helper.c index 8fb7a369ca..c74e3cdc65 100644 --- a/target/mips/msa_helper.c +++ b/target/mips/msa_helper.c @@ -1615,7 +1615,6 @@ static inline float16 float16_from_float32(int32_t a, flag ieee, float16 f_val; f_val = float32_to_float16((float32)a, ieee, status); - f_val = float16_maybe_silence_nan(f_val, status); return a < 0 ? (f_val | (1 << 15)) : f_val; } @@ -1625,7 +1624,6 @@ static inline float32 float32_from_float64(int64_t a, float_status *status) float32 f_val; f_val = float64_to_float32((float64)a, status); - f_val = float32_maybe_silence_nan(f_val, status); return a < 0 ? (f_val | (1 << 31)) : f_val; } @@ -1636,7 +1634,6 @@ static inline float32 float32_from_float16(int16_t a, flag ieee, float32 f_val; f_val = float16_to_float32((float16)a, ieee, status); - f_val = float32_maybe_silence_nan(f_val, status); return a < 0 ? (f_val | (1 << 31)) : f_val; } @@ -1646,7 +1643,6 @@ static inline float64 float64_from_float32(int32_t a, float_status *status) float64 f_val; f_val = float32_to_float64((float64)a, status); - f_val = float64_maybe_silence_nan(f_val, status); return a < 0 ? (f_val | (1ULL << 63)) : f_val; } diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c index 798cdad030..41d3634289 100644 --- a/target/mips/op_helper.c +++ b/target/mips/op_helper.c @@ -2627,6 +2627,9 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) (env->active_fpu.fcr31 & ~(env->active_fpu.fcr31_rw_bitmask)); break; default: + if (env->insn_flags & ISA_MIPS32R6) { + do_raise_exception(env, EXCP_RI, GETPC()); + } return; } restore_fp_status(env); @@ -2700,7 +2703,6 @@ uint64_t helper_float_cvtd_s(CPUMIPSState *env, uint32_t fst0) uint64_t fdt2; fdt2 = float32_to_float64(fst0, &env->active_fpu.fp_status); - fdt2 = float64_maybe_silence_nan(fdt2, &env->active_fpu.fp_status); update_fcr31(env, GETPC()); return fdt2; } @@ -2790,7 +2792,6 @@ uint32_t helper_float_cvts_d(CPUMIPSState *env, uint64_t fdt0) uint32_t fst2; fst2 = float64_to_float32(fdt0, &env->active_fpu.fp_status); - fst2 = float32_maybe_silence_nan(fst2, &env->active_fpu.fp_status); update_fcr31(env, GETPC()); return fst2; } diff --git a/target/mips/translate.c b/target/mips/translate.c index d05ee67e63..20b43c0337 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -36,6 +36,7 @@ #include "target/mips/trace.h" #include "trace-tcg.h" +#include "exec/translator.h" #include "exec/log.h" #define MIPS_DEBUG_DISAS 0 @@ -1429,17 +1430,16 @@ static TCGv_i64 msa_wr_d[64]; } while(0) typedef struct DisasContext { - struct TranslationBlock *tb; - target_ulong pc, saved_pc; + DisasContextBase base; + target_ulong saved_pc; + target_ulong page_start; uint32_t opcode; - int singlestep_enabled; int insn_flags; int32_t CP0_Config1; /* Routine used to access memory */ int mem_idx; TCGMemOp default_tcg_memop_mask; uint32_t hflags, saved_hflags; - int bstate; target_ulong btarget; bool ulri; int kscrexist; @@ -1460,13 +1460,8 @@ typedef struct DisasContext { bool abs2008; } DisasContext; -enum { - BS_NONE = 0, /* We go out of the TB without reaching a branch or an - * exception condition */ - BS_STOP = 1, /* We want to stop translation for any reason */ - BS_BRANCH = 2, /* We reached a branch condition */ - BS_EXCP = 3, /* We reached an exception condition */ -}; +#define DISAS_STOP DISAS_TARGET_0 +#define DISAS_EXIT DISAS_TARGET_1 static const char * const regnames[] = { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", @@ -1521,8 +1516,9 @@ static const char * const msaregnames[] = { if (MIPS_DEBUG_DISAS) { \ qemu_log_mask(CPU_LOG_TB_IN_ASM, \ TARGET_FMT_lx ": %08x Invalid %s %03x %03x %03x\n", \ - ctx->pc, ctx->opcode, op, ctx->opcode >> 26, \ - ctx->opcode & 0x3F, ((ctx->opcode >> 16) & 0x1F)); \ + ctx->base.pc_next, ctx->opcode, op, \ + ctx->opcode >> 26, ctx->opcode & 0x3F, \ + ((ctx->opcode >> 16) & 0x1F)); \ } \ } while (0) @@ -1598,9 +1594,9 @@ static inline void gen_save_pc(target_ulong pc) static inline void save_cpu_state(DisasContext *ctx, int do_save_pc) { LOG_DISAS("hflags %08x saved %08x\n", ctx->hflags, ctx->saved_hflags); - if (do_save_pc && ctx->pc != ctx->saved_pc) { - gen_save_pc(ctx->pc); - ctx->saved_pc = ctx->pc; + if (do_save_pc && ctx->base.pc_next != ctx->saved_pc) { + gen_save_pc(ctx->base.pc_next); + ctx->saved_pc = ctx->base.pc_next; } if (ctx->hflags != ctx->saved_hflags) { tcg_gen_movi_i32(hflags, ctx->hflags); @@ -1639,7 +1635,7 @@ static inline void generate_exception_err(DisasContext *ctx, int excp, int err) gen_helper_raise_exception_err(cpu_env, texcp, terr); tcg_temp_free_i32(terr); tcg_temp_free_i32(texcp); - ctx->bstate = BS_EXCP; + ctx->base.is_jmp = DISAS_NORETURN; } static inline void generate_exception(DisasContext *ctx, int excp) @@ -2116,7 +2112,7 @@ OP_ST_ATOMIC(scd,st64,ld64,0x7); #undef OP_ST_ATOMIC static void gen_base_offset_addr (DisasContext *ctx, TCGv addr, - int base, int16_t offset) + int base, int offset) { if (base == 0) { tcg_gen_movi_tl(addr, offset); @@ -2130,7 +2126,7 @@ static void gen_base_offset_addr (DisasContext *ctx, TCGv addr, static target_ulong pc_relative_pc (DisasContext *ctx) { - target_ulong pc = ctx->pc; + target_ulong pc = ctx->base.pc_next; if (ctx->hflags & MIPS_HFLAG_BMASK) { int branch_bytes = ctx->hflags & MIPS_HFLAG_BDS16 ? 2 : 4; @@ -2144,7 +2140,7 @@ static target_ulong pc_relative_pc (DisasContext *ctx) /* Load */ static void gen_ld(DisasContext *ctx, uint32_t opc, - int rt, int base, int16_t offset) + int rt, int base, int offset) { TCGv t0, t1, t2; int mem_idx = ctx->mem_idx; @@ -2341,7 +2337,7 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, /* Store */ static void gen_st (DisasContext *ctx, uint32_t opc, int rt, - int base, int16_t offset) + int base, int offset) { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -2437,11 +2433,8 @@ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, /* Load and store */ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, - int base, int16_t offset) + TCGv t0) { - TCGv t0 = tcg_temp_new(); - - gen_base_offset_addr(ctx, t0, base, offset); /* Don't do NOP if destination is zero: we must perform the actual memory access. */ switch (opc) { @@ -2484,15 +2477,15 @@ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, default: MIPS_INVAL("flt_ldst"); generate_exception_end(ctx, EXCP_RI); - goto out; + break; } - out: - tcg_temp_free(t0); } static void gen_cop1_ldst(DisasContext *ctx, uint32_t op, int rt, int rs, int16_t imm) { + TCGv t0 = tcg_temp_new(); + if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { check_cp1_enabled(ctx); switch (op) { @@ -2501,16 +2494,18 @@ static void gen_cop1_ldst(DisasContext *ctx, uint32_t op, int rt, check_insn(ctx, ISA_MIPS2); /* Fallthrough */ default: - gen_flt_ldst(ctx, op, rt, rs, imm); + gen_base_offset_addr(ctx, t0, rs, imm); + gen_flt_ldst(ctx, op, rt, t0); } } else { generate_exception_err(ctx, EXCP_CpU, 1); } + tcg_temp_free(t0); } /* Arithmetic with immediate operand */ static void gen_arith_imm(DisasContext *ctx, uint32_t opc, - int rt, int rs, int16_t imm) + int rt, int rs, int imm) { target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ @@ -4279,12 +4274,12 @@ static void gen_trap (DisasContext *ctx, uint32_t opc, static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) { - if (unlikely(ctx->singlestep_enabled)) { + if (unlikely(ctx->base.singlestep_enabled)) { return false; } #ifndef CONFIG_USER_ONLY - return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); + return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif @@ -4295,10 +4290,10 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); gen_save_pc(dest); - tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + tcg_gen_exit_tb(ctx->base.tb, n); } else { gen_save_pc(dest); - if (ctx->singlestep_enabled) { + if (ctx->base.singlestep_enabled) { save_cpu_state(ctx, 0); gen_helper_raise_exception_debug(cpu_env); } @@ -4321,7 +4316,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS LOG_DISAS("Branch in delay / forbidden slot at PC 0x" - TARGET_FMT_lx "\n", ctx->pc); + TARGET_FMT_lx "\n", ctx->base.pc_next); #endif generate_exception_end(ctx, EXCP_RI); goto out; @@ -4339,7 +4334,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, gen_load_gpr(t1, rt); bcond_compute = 1; } - btgt = ctx->pc + insn_bytes + offset; + btgt = ctx->base.pc_next + insn_bytes + offset; break; case OPC_BGEZ: case OPC_BGEZAL: @@ -4358,7 +4353,7 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rs); bcond_compute = 1; } - btgt = ctx->pc + insn_bytes + offset; + btgt = ctx->base.pc_next + insn_bytes + offset; break; case OPC_BPOSGE32: #if defined(TARGET_MIPS64) @@ -4368,13 +4363,14 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, tcg_gen_andi_tl(t0, cpu_dspctrl, 0x3F); #endif bcond_compute = 1; - btgt = ctx->pc + insn_bytes + offset; + btgt = ctx->base.pc_next + insn_bytes + offset; break; case OPC_J: case OPC_JAL: case OPC_JALX: /* Jump to immediate */ - btgt = ((ctx->pc + insn_bytes) & (int32_t)0xF0000000) | (uint32_t)offset; + btgt = ((ctx->base.pc_next + insn_bytes) & (int32_t)0xF0000000) | + (uint32_t)offset; break; case OPC_JR: case OPC_JALR: @@ -4420,19 +4416,19 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, /* Handle as an unconditional branch to get correct delay slot checking. */ blink = 31; - btgt = ctx->pc + insn_bytes + delayslot_size; + btgt = ctx->base.pc_next + insn_bytes + delayslot_size; ctx->hflags |= MIPS_HFLAG_B; break; case OPC_BLTZALL: /* 0 < 0 likely */ - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 8); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 8); /* Skip the instruction in the delay slot */ - ctx->pc += 4; + ctx->base.pc_next += 4; goto out; case OPC_BNEL: /* rx != rx likely */ case OPC_BGTZL: /* 0 > 0 likely */ case OPC_BLTZL: /* 0 < 0 likely */ /* Skip the instruction in the delay slot */ - ctx->pc += 4; + ctx->base.pc_next += 4; goto out; case OPC_J: ctx->hflags |= MIPS_HFLAG_B; @@ -4544,7 +4540,8 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, int post_delay = insn_bytes + delayslot_size; int lowbit = !!(ctx->hflags & MIPS_HFLAG_M16); - tcg_gen_movi_tl(cpu_gpr[blink], ctx->pc + post_delay + lowbit); + tcg_gen_movi_tl(cpu_gpr[blink], + ctx->base.pc_next + post_delay + lowbit); } out: @@ -5326,18 +5323,18 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: /* Mark as an IO operation because we read the time. */ - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_mfc0_count(arg, cpu_env); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } /* Break the TB to be able to take timer interrupts immediately - after reading count. BS_STOP isn't sufficient, we need to ensure - we break completely out of translated code. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + after reading count. DISAS_STOP isn't sufficient, we need to + ensure we break completely out of translated code. */ + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Count"; break; /* 6,7 are implementation dependent */ @@ -5733,7 +5730,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) if (sel != 0) check_insn(ctx, ISA_MIPS32); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } @@ -5905,7 +5902,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_pagegrain(cpu_env, arg); rn = "PageGrain"; - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 2: CP0_CHECK(ctx->sc); @@ -5966,7 +5963,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "HWREna"; break; default: @@ -6028,30 +6025,30 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: save_cpu_state(ctx, 1); gen_helper_mtc0_status(cpu_env, arg); - /* BS_STOP isn't good enough here, hflags may have changed. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + /* DISAS_STOP isn't good enough here, hflags may have changed. */ + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "SRSMap"; break; default: @@ -6063,11 +6060,11 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: save_cpu_state(ctx, 1); gen_helper_mtc0_cause(cpu_env, arg); - /* Stop translation as we may have triggered an interrupt. BS_STOP - * isn't sufficient, we need to ensure we break out of translated - * code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + /* Stop translation as we may have triggered an interrupt. + * DISAS_STOP isn't sufficient, we need to ensure we break out of + * translated code to check for pending interrupts. */ + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Cause"; break; default: @@ -6105,7 +6102,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config0(cpu_env, arg); rn = "Config"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 1: /* ignored, read only */ @@ -6115,24 +6112,24 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config2(cpu_env, arg); rn = "Config2"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 3: gen_helper_mtc0_config3(cpu_env, arg); rn = "Config3"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 4: gen_helper_mtc0_config4(cpu_env, arg); rn = "Config4"; - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 5: gen_helper_mtc0_config5(cpu_env, arg); rn = "Config5"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; /* 6,7 are implementation dependent */ case 6: @@ -6221,35 +6218,35 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ - /* BS_STOP isn't good enough here, hflags may have changed. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + /* DISAS_STOP isn't good enough here, hflags may have changed. */ + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Debug"; break; case 1: // gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ rn = "TraceControl"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; case 2: // gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ rn = "TraceControl2"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; case 3: /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; // gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ rn = "UserTraceData"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; case 4: // gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "TraceBPC"; goto cp0_unimplemented; default: @@ -6309,7 +6306,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_errctl(cpu_env, arg); - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "ErrCtl"; break; default: @@ -6400,12 +6397,12 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) trace_mips_translate_c0("mtc0", rn, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); - /* BS_STOP isn't sufficient, we need to ensure we break out of + /* DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; } return; @@ -6678,18 +6675,18 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: /* Mark as an IO operation because we read the time. */ - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_mfc0_count(arg, cpu_env); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } /* Break the TB to be able to take timer interrupts immediately - after reading count. BS_STOP isn't sufficient, we need to ensure - we break completely out of translated code. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + after reading count. DISAS_STOP isn't sufficient, we need to + ensure we break completely out of translated code. */ + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Count"; break; /* 6,7 are implementation dependent */ @@ -7071,7 +7068,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) if (sel != 0) check_insn(ctx, ISA_MIPS64); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } @@ -7301,7 +7298,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "HWREna"; break; default: @@ -7337,7 +7334,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 10: switch (sel) { @@ -7360,37 +7357,37 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 12: switch (sel) { case 0: save_cpu_state(ctx, 1); gen_helper_mtc0_status(cpu_env, arg); - /* BS_STOP isn't good enough here, hflags may have changed. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + /* DISAS_STOP isn't good enough here, hflags may have changed. */ + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "SRSMap"; break; default: @@ -7402,11 +7399,11 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 0: save_cpu_state(ctx, 1); gen_helper_mtc0_cause(cpu_env, arg); - /* Stop translation as we may have triggered an intetrupt. BS_STOP - * isn't sufficient, we need to ensure we break out of translated - * code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + /* Stop translation as we may have triggered an interrupt. + * DISAS_STOP isn't sufficient, we need to ensure we break out of + * translated code to check for pending interrupts. */ + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Cause"; break; default: @@ -7444,7 +7441,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config0(cpu_env, arg); rn = "Config"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 1: /* ignored, read only */ @@ -7454,13 +7451,13 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config2(cpu_env, arg); rn = "Config2"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 3: gen_helper_mtc0_config3(cpu_env, arg); rn = "Config3"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case 4: /* currently ignored */ @@ -7470,7 +7467,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_helper_mtc0_config5(cpu_env, arg); rn = "Config5"; /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; /* 6,7 are implementation dependent */ default: @@ -7549,33 +7546,33 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ - /* BS_STOP isn't good enough here, hflags may have changed. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + /* DISAS_STOP isn't good enough here, hflags may have changed. */ + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; rn = "Debug"; break; case 1: // gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "TraceControl"; goto cp0_unimplemented; case 2: // gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "TraceControl2"; goto cp0_unimplemented; case 3: // gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "UserTraceData"; goto cp0_unimplemented; case 4: // gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "TraceBPC"; goto cp0_unimplemented; default: @@ -7635,7 +7632,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) switch (sel) { case 0: gen_helper_mtc0_errctl(cpu_env, arg); - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; rn = "ErrCtl"; break; default: @@ -7726,12 +7723,12 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) trace_mips_translate_c0("dmtc0", rn, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); - /* BS_STOP isn't sufficient, we need to ensure we break out of + /* DISAS_STOP isn't sufficient, we need to ensure we break out of * translated code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; } return; @@ -8142,7 +8139,7 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, tcg_temp_free_i32(fs_tmp); } /* Stop translation as we may have changed hflags */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; /* COP2: Not implemented. */ case 4: @@ -8301,7 +8298,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, check_insn(ctx, ISA_MIPS2); gen_helper_eret(cpu_env); } - ctx->bstate = BS_EXCP; + ctx->base.is_jmp = DISAS_EXIT; } break; case OPC_DERET: @@ -8316,7 +8313,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, generate_exception_end(ctx, EXCP_RI); } else { gen_helper_deret(cpu_env); - ctx->bstate = BS_EXCP; + ctx->base.is_jmp = DISAS_EXIT; } break; case OPC_WAIT: @@ -8327,11 +8324,11 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, goto die; } /* If we get an exception, we want to restart at next instruction */ - ctx->pc += 4; + ctx->base.pc_next += 4; save_cpu_state(ctx, 1); - ctx->pc -= 4; + ctx->base.pc_next -= 4; gen_helper_wait(cpu_env); - ctx->bstate = BS_EXCP; + ctx->base.is_jmp = DISAS_NORETURN; break; default: die: @@ -8358,7 +8355,7 @@ static void gen_compute_branch1(DisasContext *ctx, uint32_t op, if (cc != 0) check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); - btarget = ctx->pc + 4 + offset; + btarget = ctx->base.pc_next + 4 + offset; switch (op) { case OPC_BC1F: @@ -8461,7 +8458,7 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx - "\n", ctx->pc); + "\n", ctx->base.pc_next); #endif generate_exception_end(ctx, EXCP_RI); goto out; @@ -8470,7 +8467,7 @@ static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, gen_load_fpr64(ctx, t0, ft); tcg_gen_andi_i64(t0, t0, 1); - btarget = addr_add(ctx, ctx->pc + 4, offset); + btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); switch (op) { case OPC_BC1EQZ: @@ -8756,7 +8753,7 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) tcg_temp_free_i32(fs_tmp); } /* Stop translation as we may have changed hflags */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; #if defined(TARGET_MIPS64) case OPC_DMFC1: @@ -10755,19 +10752,19 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) gen_store_gpr(t0, rt); break; case 2: - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_rdhwr_cc(t0, cpu_env); - if (tb_cflags(ctx->tb) & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } gen_store_gpr(t0, rt); /* Break the TB to be able to take timer interrupts immediately - after reading count. BS_STOP isn't sufficient, we need to ensure + after reading count. DISAS_STOP isn't sufficient, we need to ensure we break completely out of translated code. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; break; case 3: gen_helper_rdhwr_ccres(t0, cpu_env); @@ -10817,7 +10814,7 @@ static void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel) static inline void clear_branch_hflags(DisasContext *ctx) { ctx->hflags &= ~MIPS_HFLAG_BMASK; - if (ctx->bstate == BS_NONE) { + if (ctx->base.is_jmp == DISAS_NEXT) { save_cpu_state(ctx, 0); } else { /* it is not safe to save ctx->hflags as hflags may be changed @@ -10832,11 +10829,11 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK; /* Branches completion */ clear_branch_hflags(ctx); - ctx->bstate = BS_BRANCH; + ctx->base.is_jmp = DISAS_NORETURN; /* FIXME: Need to clear can_do_io. */ switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { case MIPS_HFLAG_FBNSLOT: - gen_goto_tb(ctx, 0, ctx->pc + insn_bytes); + gen_goto_tb(ctx, 0, ctx->base.pc_next + insn_bytes); break; case MIPS_HFLAG_B: /* unconditional branch */ @@ -10855,7 +10852,7 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) TCGLabel *l1 = gen_new_label(); tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); - gen_goto_tb(ctx, 1, ctx->pc + insn_bytes); + gen_goto_tb(ctx, 1, ctx->base.pc_next + insn_bytes); gen_set_label(l1); gen_goto_tb(ctx, 0, ctx->btarget); } @@ -10878,7 +10875,7 @@ static void gen_branch(DisasContext *ctx, int insn_bytes) } else { tcg_gen_mov_tl(cpu_PC, btarget); } - if (ctx->singlestep_enabled) { + if (ctx->base.singlestep_enabled) { save_cpu_state(ctx, 0); gen_helper_raise_exception_debug(cpu_env); } @@ -10903,7 +10900,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, if (ctx->hflags & MIPS_HFLAG_BMASK) { #ifdef MIPS_DEBUG_DISAS LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx - "\n", ctx->pc); + "\n", ctx->base.pc_next); #endif generate_exception_end(ctx, EXCP_RI); goto out; @@ -10917,10 +10914,10 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); if (rs <= rt && rs == 0) { /* OPC_BEQZALC, OPC_BNEZALC */ - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4 + m16_lowbit); } break; case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ @@ -10928,23 +10925,23 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); break; case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ if (rs == 0 || rs == rt) { /* OPC_BLEZALC, OPC_BGEZALC */ /* OPC_BGTZALC, OPC_BLTZALC */ - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4 + m16_lowbit); } gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); break; case OPC_BC: case OPC_BALC: - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); break; case OPC_BEQZC: case OPC_BNEZC: @@ -10952,7 +10949,7 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, /* OPC_BEQZC, OPC_BNEZC */ gen_load_gpr(t0, rs); bcond_compute = 1; - ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); } else { /* OPC_JIC, OPC_JIALC */ TCGv tbase = tcg_temp_new(); @@ -10975,13 +10972,13 @@ static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, /* Uncoditional compact branch */ switch (opc) { case OPC_JIALC: - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4 + m16_lowbit); /* Fallthrough */ case OPC_JIC: ctx->hflags |= MIPS_HFLAG_BR; break; case OPC_BALC: - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4 + m16_lowbit); /* Fallthrough */ case OPC_BC: ctx->hflags |= MIPS_HFLAG_B; @@ -11606,7 +11603,7 @@ static void decode_i64_mips16 (DisasContext *ctx, static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) { - int extend = cpu_lduw_code(env, ctx->pc + 2); + int extend = cpu_lduw_code(env, ctx->base.pc_next + 2); int op, rx, ry, funct, sa; int16_t imm, offset; @@ -11846,7 +11843,7 @@ static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) /* No delay slot, so just process as a normal instruction */ break; case M16_OPC_JAL: - offset = cpu_lduw_code(env, ctx->pc + 2); + offset = cpu_lduw_code(env, ctx->base.pc_next + 2); offset = (((ctx->opcode & 0x1f) << 21) | ((ctx->opcode >> 5) & 0x1f) << 16 | offset) << 2; @@ -13574,7 +13571,7 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) gen_helper_di(t0, cpu_env); gen_store_gpr(t0, rs); /* Stop translation as we may have switched the execution mode */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; tcg_temp_free(t0); } break; @@ -13586,10 +13583,10 @@ static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) save_cpu_state(ctx, 1); gen_helper_ei(t0, cpu_env); gen_store_gpr(t0, rs); - /* BS_STOP isn't sufficient, we need to ensure we break out + /* DISAS_STOP isn't sufficient, we need to ensure we break out of translated code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; tcg_temp_free(t0); } break; @@ -13944,7 +13941,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) uint32_t op, minor, minor2, mips32_op; uint32_t cond, fmt, cc; - insn = cpu_lduw_code(env, ctx->pc + 2); + insn = cpu_lduw_code(env, ctx->base.pc_next + 2); ctx->opcode = (ctx->opcode << 16) | insn; rt = (ctx->opcode >> 21) & 0x1f; @@ -14745,7 +14742,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) /* SYNCI */ /* Break the TB to be able to sync copied instructions immediately */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; } else { /* TNEI */ mips32_op = OPC_TNEI; @@ -14776,7 +14773,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) check_insn_opc_removed(ctx, ISA_MIPS32R6); /* Break the TB to be able to sync copied instructions immediately */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case BC2F: case BC2T: @@ -15139,16 +15136,16 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) /* PCREL: ADDIUPC, AUIPC, ALUIPC, LWPC */ switch ((ctx->opcode >> 16) & 0x1f) { case ADDIUPC_00 ... ADDIUPC_07: - gen_pcrel(ctx, OPC_ADDIUPC, ctx->pc & ~0x3, rt); + gen_pcrel(ctx, OPC_ADDIUPC, ctx->base.pc_next & ~0x3, rt); break; case AUIPC: - gen_pcrel(ctx, OPC_AUIPC, ctx->pc, rt); + gen_pcrel(ctx, OPC_AUIPC, ctx->base.pc_next, rt); break; case ALUIPC: - gen_pcrel(ctx, OPC_ALUIPC, ctx->pc, rt); + gen_pcrel(ctx, OPC_ALUIPC, ctx->base.pc_next, rt); break; case LWPC_08 ... LWPC_0F: - gen_pcrel(ctx, R6_OPC_LWPC, ctx->pc & ~0x3, rt); + gen_pcrel(ctx, R6_OPC_LWPC, ctx->base.pc_next & ~0x3, rt); break; default: generate_exception(ctx, EXCP_RI); @@ -15280,8 +15277,8 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) uint32_t op; /* make sure instructions are on a halfword boundary */ - if (ctx->pc & 0x1) { - env->CP0_BadVAddr = ctx->pc; + if (ctx->base.pc_next & 0x1) { + env->CP0_BadVAddr = ctx->base.pc_next; generate_exception_end(ctx, EXCP_AdEL); return 2; } @@ -18507,7 +18504,7 @@ static void gen_msa_branch(CPUMIPSState *env, DisasContext *ctx, uint32_t op1) break; } - ctx->btarget = ctx->pc + (s16 << 2) + 4; + ctx->btarget = ctx->base.pc_next + (s16 << 2) + 4; ctx->hflags |= MIPS_HFLAG_BC; ctx->hflags |= MIPS_HFLAG_BDS32; @@ -19528,8 +19525,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) int16_t imm; /* make sure instructions are on a word boundary */ - if (ctx->pc & 0x3) { - env->CP0_BadVAddr = ctx->pc; + if (ctx->base.pc_next & 0x3) { + env->CP0_BadVAddr = ctx->base.pc_next; generate_exception_err(ctx, EXCP_AdEL, EXCP_INST_NOTAVAIL); return; } @@ -19540,7 +19537,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); tcg_gen_movi_i32(hflags, ctx->hflags & ~MIPS_HFLAG_BMASK); - gen_goto_tb(ctx, 1, ctx->pc + 4); + gen_goto_tb(ctx, 1, ctx->base.pc_next + 4); gen_set_label(l1); } @@ -19601,7 +19598,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) check_insn(ctx, ISA_MIPS32R2); /* Break the TB to be able to sync copied instructions immediately */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case OPC_BPOSGE32: /* MIPS DSP branch */ #if defined(TARGET_MIPS64) @@ -19704,17 +19701,17 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) gen_store_gpr(t0, rt); /* Stop translation as we may have switched the execution mode. */ - ctx->bstate = BS_STOP; + ctx->base.is_jmp = DISAS_STOP; break; case OPC_EI: check_insn(ctx, ISA_MIPS32R2); save_cpu_state(ctx, 1); gen_helper_ei(t0, cpu_env); gen_store_gpr(t0, rt); - /* BS_STOP isn't sufficient, we need to ensure we break out - of translated code to check for pending interrupts. */ - gen_save_pc(ctx->pc + 4); - ctx->bstate = BS_EXCP; + /* DISAS_STOP isn't sufficient, we need to ensure we break + out of translated code to check for pending interrupts */ + gen_save_pc(ctx->base.pc_next + 4); + ctx->base.is_jmp = DISAS_EXIT; break; default: /* Invalid */ MIPS_INVAL("mfmc0"); @@ -20188,7 +20185,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; case OPC_PCREL: check_insn(ctx, ISA_MIPS32R6); - gen_pcrel(ctx, ctx->opcode, ctx->pc, rs); + gen_pcrel(ctx, ctx->opcode, ctx->base.pc_next, rs); break; default: /* Invalid */ MIPS_INVAL("major opcode"); @@ -20197,183 +20194,186 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) } } -void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + CPUMIPSState *env = cs->env_ptr; + + ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; + ctx->saved_pc = -1; + ctx->insn_flags = env->insn_flags; + ctx->CP0_Config1 = env->CP0_Config1; + ctx->btarget = 0; + ctx->kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; + ctx->rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; + ctx->ie = (env->CP0_Config4 >> CP0C4_IE) & 3; + ctx->bi = (env->CP0_Config3 >> CP0C3_BI) & 1; + ctx->bp = (env->CP0_Config3 >> CP0C3_BP) & 1; + ctx->PAMask = env->PAMask; + ctx->mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1; + ctx->eva = (env->CP0_Config5 >> CP0C5_EVA) & 1; + ctx->sc = (env->CP0_Config3 >> CP0C3_SC) & 1; + ctx->CP0_LLAddr_shift = env->CP0_LLAddr_shift; + ctx->cmgcr = (env->CP0_Config3 >> CP0C3_CMGCR) & 1; + /* Restore delay slot state from the tb context. */ + ctx->hflags = (uint32_t)ctx->base.tb->flags; /* FIXME: maybe use 64 bits? */ + ctx->ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; + ctx->ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || + (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); + ctx->vp = (env->CP0_Config5 >> CP0C5_VP) & 1; + ctx->mrp = (env->CP0_Config5 >> CP0C5_MRP) & 1; + ctx->nan2008 = (env->active_fpu.fcr31 >> FCR31_NAN2008) & 1; + ctx->abs2008 = (env->active_fpu.fcr31 >> FCR31_ABS2008) & 1; + restore_cpu_state(env, ctx); +#ifdef CONFIG_USER_ONLY + ctx->mem_idx = MIPS_HFLAG_UM; +#else + ctx->mem_idx = hflags_mmu_index(ctx->hflags); +#endif + ctx->default_tcg_memop_mask = (ctx->insn_flags & ISA_MIPS32R6) ? + MO_UNALN : MO_ALIGN; + + LOG_DISAS("\ntb %p idx %d hflags %04x\n", ctx->base.tb, ctx->mem_idx, + ctx->hflags); +} + +static void mips_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) +{ +} + +static void mips_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(ctx->base.pc_next, ctx->hflags & MIPS_HFLAG_BMASK, + ctx->btarget); +} + +static bool mips_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + save_cpu_state(ctx, 1); + ctx->base.is_jmp = DISAS_NORETURN; + gen_helper_raise_exception_debug(cpu_env); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx->base.pc_next += 4; + return true; +} + +static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) { CPUMIPSState *env = cs->env_ptr; - DisasContext ctx; - target_ulong pc_start; - target_ulong next_page_start; - int num_insns; - int max_insns; + DisasContext *ctx = container_of(dcbase, DisasContext, base); int insn_bytes; int is_slot; - pc_start = tb->pc; - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - ctx.pc = pc_start; - ctx.saved_pc = -1; - ctx.singlestep_enabled = cs->singlestep_enabled; - ctx.insn_flags = env->insn_flags; - ctx.CP0_Config1 = env->CP0_Config1; - ctx.tb = tb; - ctx.bstate = BS_NONE; - ctx.btarget = 0; - ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; - ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; - ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 3; - ctx.bi = (env->CP0_Config3 >> CP0C3_BI) & 1; - ctx.bp = (env->CP0_Config3 >> CP0C3_BP) & 1; - ctx.PAMask = env->PAMask; - ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1; - ctx.eva = (env->CP0_Config5 >> CP0C5_EVA) & 1; - ctx.sc = (env->CP0_Config3 >> CP0C3_SC) & 1; - ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift; - ctx.cmgcr = (env->CP0_Config3 >> CP0C3_CMGCR) & 1; - /* Restore delay slot state from the tb context. */ - ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ - ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; - ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || - (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); - ctx.vp = (env->CP0_Config5 >> CP0C5_VP) & 1; - ctx.mrp = (env->CP0_Config5 >> CP0C5_MRP) & 1; - ctx.nan2008 = (env->active_fpu.fcr31 >> FCR31_NAN2008) & 1; - ctx.abs2008 = (env->active_fpu.fcr31 >> FCR31_ABS2008) & 1; - restore_cpu_state(env, &ctx); -#ifdef CONFIG_USER_ONLY - ctx.mem_idx = MIPS_HFLAG_UM; -#else - ctx.mem_idx = hflags_mmu_index(ctx.hflags); -#endif - ctx.default_tcg_memop_mask = (ctx.insn_flags & ISA_MIPS32R6) ? - MO_UNALN : MO_ALIGN; - num_insns = 0; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; + is_slot = ctx->hflags & MIPS_HFLAG_BMASK; + if (!(ctx->hflags & MIPS_HFLAG_M16)) { + ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); + insn_bytes = 4; + decode_opc(env, ctx); + } else if (ctx->insn_flags & ASE_MICROMIPS) { + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + insn_bytes = decode_micromips_opc(env, ctx); + } else if (ctx->insn_flags & ASE_MIPS16) { + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + insn_bytes = decode_mips16_opc(env, ctx); + } else { + generate_exception_end(ctx, EXCP_RI); + g_assert(ctx->base.is_jmp == DISAS_NORETURN); + return; } - LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); - gen_tb_start(tb); - while (ctx.bstate == BS_NONE) { - tcg_gen_insn_start(ctx.pc, ctx.hflags & MIPS_HFLAG_BMASK, ctx.btarget); - num_insns++; - - if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { - save_cpu_state(&ctx, 1); - ctx.bstate = BS_BRANCH; - gen_helper_raise_exception_debug(cpu_env); - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - ctx.pc += 4; - goto done_generating; + if (ctx->hflags & MIPS_HFLAG_BMASK) { + if (!(ctx->hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32 | + MIPS_HFLAG_FBNSLOT))) { + /* force to generate branch as there is neither delay nor + forbidden slot */ + is_slot = 1; } - - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); + if ((ctx->hflags & MIPS_HFLAG_M16) && + (ctx->hflags & MIPS_HFLAG_FBNSLOT)) { + /* Force to generate branch as microMIPS R6 doesn't restrict + branches in the forbidden slot. */ + is_slot = 1; } - - is_slot = ctx.hflags & MIPS_HFLAG_BMASK; - if (!(ctx.hflags & MIPS_HFLAG_M16)) { - ctx.opcode = cpu_ldl_code(env, ctx.pc); - insn_bytes = 4; - decode_opc(env, &ctx); - } else if (ctx.insn_flags & ASE_MICROMIPS) { - ctx.opcode = cpu_lduw_code(env, ctx.pc); - insn_bytes = decode_micromips_opc(env, &ctx); - } else if (ctx.insn_flags & ASE_MIPS16) { - ctx.opcode = cpu_lduw_code(env, ctx.pc); - insn_bytes = decode_mips16_opc(env, &ctx); - } else { - generate_exception_end(&ctx, EXCP_RI); - break; - } - - if (ctx.hflags & MIPS_HFLAG_BMASK) { - if (!(ctx.hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32 | - MIPS_HFLAG_FBNSLOT))) { - /* force to generate branch as there is neither delay nor - forbidden slot */ - is_slot = 1; - } - if ((ctx.hflags & MIPS_HFLAG_M16) && - (ctx.hflags & MIPS_HFLAG_FBNSLOT)) { - /* Force to generate branch as microMIPS R6 doesn't restrict - branches in the forbidden slot. */ - is_slot = 1; - } - } - if (is_slot) { - gen_branch(&ctx, insn_bytes); - } - ctx.pc += insn_bytes; - - /* Execute a branch and its delay slot as a single instruction. - This is what GDB expects and is consistent with what the - hardware does (e.g. if a delay slot instruction faults, the - reported PC is the PC of the branch). */ - if (cs->singlestep_enabled && (ctx.hflags & MIPS_HFLAG_BMASK) == 0) { - break; - } - - if (ctx.pc >= next_page_start) { - break; - } - - if (tcg_op_buf_full()) { - break; - } - - if (num_insns >= max_insns) - break; - - if (singlestep) - break; } - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); + if (is_slot) { + gen_branch(ctx, insn_bytes); } - if (cs->singlestep_enabled && ctx.bstate != BS_BRANCH) { - save_cpu_state(&ctx, ctx.bstate != BS_EXCP); + ctx->base.pc_next += insn_bytes; + + if (ctx->base.is_jmp != DISAS_NEXT) { + return; + } + /* Execute a branch and its delay slot as a single instruction. + This is what GDB expects and is consistent with what the + hardware does (e.g. if a delay slot instruction faults, the + reported PC is the PC of the branch). */ + if (ctx->base.singlestep_enabled && + (ctx->hflags & MIPS_HFLAG_BMASK) == 0) { + ctx->base.is_jmp = DISAS_TOO_MANY; + } + if (ctx->base.pc_next - ctx->page_start >= TARGET_PAGE_SIZE) { + ctx->base.is_jmp = DISAS_TOO_MANY; + } +} + +static void mips_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + if (ctx->base.singlestep_enabled && ctx->base.is_jmp != DISAS_NORETURN) { + save_cpu_state(ctx, ctx->base.is_jmp != DISAS_EXIT); gen_helper_raise_exception_debug(cpu_env); } else { - switch (ctx.bstate) { - case BS_STOP: - gen_goto_tb(&ctx, 0, ctx.pc); + switch (ctx->base.is_jmp) { + case DISAS_STOP: + gen_save_pc(ctx->base.pc_next); + tcg_gen_lookup_and_goto_ptr(); break; - case BS_NONE: - save_cpu_state(&ctx, 0); - gen_goto_tb(&ctx, 0, ctx.pc); + case DISAS_NEXT: + case DISAS_TOO_MANY: + save_cpu_state(ctx, 0); + gen_goto_tb(ctx, 0, ctx->base.pc_next); break; - case BS_EXCP: - tcg_gen_exit_tb(0); + case DISAS_EXIT: + tcg_gen_exit_tb(NULL, 0); + break; + case DISAS_NORETURN: break; - case BS_BRANCH: default: - break; + g_assert_not_reached(); } } -done_generating: - gen_tb_end(tb, num_insns); +} - tb->size = ctx.pc - pc_start; - tb->icount = num_insns; +static void mips_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); +} -#ifdef DEBUG_DISAS - LOG_DISAS("\n"); - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, ctx.pc - pc_start); - qemu_log("\n"); - qemu_log_unlock(); - } -#endif +static const TranslatorOps mips_tr_ops = { + .init_disas_context = mips_tr_init_disas_context, + .tb_start = mips_tr_tb_start, + .insn_start = mips_tr_insn_start, + .breakpoint_check = mips_tr_breakpoint_check, + .translate_insn = mips_tr_translate_insn, + .tb_stop = mips_tr_tb_stop, + .disas_log = mips_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +{ + DisasContext ctx; + + translator_loop(&mips_tr_ops, &ctx.base, cs, tb); } static void fpu_dump_state(CPUMIPSState *env, FILE *f, fprintf_function fpu_fprintf, @@ -20445,8 +20445,9 @@ void mips_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, env->CP0_Config2, env->CP0_Config3); cpu_fprintf(f, " Config4 0x%08x Config5 0x%08x\n", env->CP0_Config4, env->CP0_Config5); - if (env->hflags & MIPS_HFLAG_FPU) + if ((flags & CPU_DUMP_FPU) && (env->hflags & MIPS_HFLAG_FPU)) { fpu_dump_state(env, f, cpu_fprintf, flags); + } } void mips_tcg_init(void) @@ -20499,7 +20500,7 @@ void mips_tcg_init(void) "fcr31"); } -#include "translate_init.c" +#include "translate_init.inc.c" void cpu_mips_realize_env(CPUMIPSState *env) { @@ -20711,6 +20712,11 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Status |= (1 << CP0St_FR); } + if (env->CP0_Config3 & (1 << CP0C3_ISA)) { + /* microMIPS on reset when Config3.ISA == {1, 3} */ + env->hflags |= MIPS_HFLAG_M16; + } + /* MSA */ if (env->CP0_Config3 & (1 << CP0C3_MSAP)) { msa_reset(env); diff --git a/target/mips/translate_init.c b/target/mips/translate_init.inc.c similarity index 100% rename from target/mips/translate_init.c rename to target/mips/translate_init.inc.c diff --git a/target/moxie/cpu.c b/target/moxie/cpu.c index 4170284da6..8d67eb6727 100644 --- a/target/moxie/cpu.c +++ b/target/moxie/cpu.c @@ -23,7 +23,6 @@ #include "qemu-common.h" #include "migration/vmstate.h" #include "machine.h" -#include "exec/exec-all.h" static void moxie_cpu_set_pc(CPUState *cs, vaddr value) { diff --git a/target/moxie/mmu.c b/target/moxie/mmu.c index 9203330b3b..bd90b1eebc 100644 --- a/target/moxie/mmu.c +++ b/target/moxie/mmu.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "mmu.h" -#include "exec/exec-all.h" int moxie_mmu_translate(MoxieMMUResult *res, CPUMoxieState *env, uint32_t vaddr, diff --git a/target/moxie/translate.c b/target/moxie/translate.c index 28b405f0e4..29da02bc05 100644 --- a/target/moxie/translate.c +++ b/target/moxie/translate.c @@ -132,13 +132,13 @@ static inline void gen_goto_tb(CPUMoxieState *env, DisasContext *ctx, if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + tcg_gen_exit_tb(ctx->tb, n); } else { tcg_gen_movi_i32(cpu_pc, dest); if (ctx->singlestep_enabled) { gen_helper_debug(cpu_env); } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -328,7 +328,7 @@ static int decode_opc(MoxieCPU *cpu, DisasContext *ctx) tcg_temp_free_i32(t1); /* Jump... */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; } @@ -472,14 +472,14 @@ static int decode_opc(MoxieCPU *cpu, DisasContext *ctx) tcg_gen_mov_i32(cpu_pc, REG(fnreg)); tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; } break; case 0x1a: /* jmpa */ { tcg_gen_movi_i32(cpu_pc, cpu_ldl_code(env, ctx->pc+2)); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; length = 6; } @@ -584,7 +584,7 @@ static int decode_opc(MoxieCPU *cpu, DisasContext *ctx) { int reg = (opcode >> 4) & 0xf; tcg_gen_mov_i32(cpu_pc, REG(reg)); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; } break; @@ -878,7 +878,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) gen_goto_tb(env, &ctx, 0, ctx.pc); break; case BS_EXCP: - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case BS_BRANCH: default: diff --git a/target/nios2/cpu.h b/target/nios2/cpu.h index 145796e8ce..047f3764b7 100644 --- a/target/nios2/cpu.h +++ b/target/nios2/cpu.h @@ -260,7 +260,6 @@ static inline int cpu_interrupts_enabled(CPUNios2State *env) } #include "exec/cpu-all.h" -#include "exec/exec-all.h" static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) diff --git a/target/nios2/op_helper.c b/target/nios2/op_helper.c index c853aeae02..529ec6ac0e 100644 --- a/target/nios2/op_helper.c +++ b/target/nios2/op_helper.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" +#include "exec/exec-all.h" #include "qemu/main-loop.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/nios2/translate.c b/target/nios2/translate.c index cb8624e8d2..7fa03ed05a 100644 --- a/target/nios2/translate.c +++ b/target/nios2/translate.c @@ -171,10 +171,10 @@ static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); - tcg_gen_exit_tb((uintptr_t)tb + n); + tcg_gen_exit_tb(tb, n); } else { tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -880,14 +880,14 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) case DISAS_NEXT: /* Save the current PC back into the CPU register */ tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; default: case DISAS_JUMP: case DISAS_UPDATE: /* The jump will already have updated the PC register */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: diff --git a/target/openrisc/Makefile.objs b/target/openrisc/Makefile.objs index 918b1c6e9c..b5432f4684 100644 --- a/target/openrisc/Makefile.objs +++ b/target/openrisc/Makefile.objs @@ -1,5 +1,15 @@ obj-$(CONFIG_SOFTMMU) += machine.o -obj-y += cpu.o exception.o interrupt.o mmu.o translate.o +obj-y += cpu.o exception.o interrupt.o mmu.o translate.o disas.o obj-y += exception_helper.o fpu_helper.o \ - interrupt_helper.o mmu_helper.o sys_helper.o + interrupt_helper.o sys_helper.o obj-y += gdbstub.o + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +target/openrisc/decode.inc.c: \ + $(SRC_PATH)/target/openrisc/insns.decode $(DECODETREE) + $(call quiet-command,\ + $(PYTHON) $(DECODETREE) -o $@ $<, "GEN", $(TARGET_DIR)$@) + +target/openrisc/translate.o: target/openrisc/decode.inc.c +target/openrisc/disas.o: target/openrisc/decode.inc.c diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 20b115afae..fb7cb5c507 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -21,13 +21,13 @@ #include "qapi/error.h" #include "cpu.h" #include "qemu-common.h" -#include "exec/exec-all.h" static void openrisc_cpu_set_pc(CPUState *cs, vaddr value) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); cpu->env.pc = value; + cpu->env.dflag = 0; } static bool openrisc_cpu_has_work(CPUState *cs) @@ -36,6 +36,11 @@ static bool openrisc_cpu_has_work(CPUState *cs) CPU_INTERRUPT_TIMER); } +static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->print_insn = print_insn_or1k; +} + /* CPUClass::reset() */ static void openrisc_cpu_reset(CPUState *s) { @@ -53,8 +58,10 @@ static void openrisc_cpu_reset(CPUState *s) cpu->env.upr = UPR_UP | UPR_DMP | UPR_IMP | UPR_PICP | UPR_TTP | UPR_PMP; - cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2)) | (DMMUCFGR_NTS & (6 << 2)); - cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2)) | (IMMUCFGR_NTS & (6 << 2)); + cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2)) + | (DMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); + cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2)) + | (IMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); #ifndef CONFIG_USER_ONLY cpu->env.picmr = 0x00000000; @@ -88,10 +95,6 @@ static void openrisc_cpu_initfn(Object *obj) OpenRISCCPU *cpu = OPENRISC_CPU(obj); cs->env_ptr = &cpu->env; - -#ifndef CONFIG_USER_ONLY - cpu_openrisc_mmu_init(cpu); -#endif } /* CPU models */ @@ -153,6 +156,7 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) #endif cc->gdb_num_core_regs = 32 + 3; cc->tcg_initialize = openrisc_translate_init; + cc->disas_set_info = openrisc_disas_set_info; } /* Sort alphabetically by type name, except for "any". */ diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 35cab65f11..f1b31bc24a 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -222,12 +222,8 @@ enum { /* TLB size */ enum { - DTLB_WAYS = 1, - DTLB_SIZE = 64, - DTLB_MASK = (DTLB_SIZE-1), - ITLB_WAYS = 1, - ITLB_SIZE = 64, - ITLB_MASK = (ITLB_SIZE-1), + TLB_SIZE = 128, + TLB_MASK = TLB_SIZE - 1, }; /* TLB prot */ @@ -241,14 +237,6 @@ enum { UXE = (1 << 7), }; -/* check if tlb available */ -enum { - TLBRET_INVALID = -3, - TLBRET_NOMATCH = -2, - TLBRET_BADADDR = -1, - TLBRET_MATCH = 0 -}; - typedef struct OpenRISCTLBEntry { uint32_t mr; uint32_t tr; @@ -256,8 +244,8 @@ typedef struct OpenRISCTLBEntry { #ifndef CONFIG_USER_ONLY typedef struct CPUOpenRISCTLBContext { - OpenRISCTLBEntry itlb[ITLB_WAYS][ITLB_SIZE]; - OpenRISCTLBEntry dtlb[DTLB_WAYS][DTLB_SIZE]; + OpenRISCTLBEntry itlb[TLB_SIZE]; + OpenRISCTLBEntry dtlb[TLB_SIZE]; int (*cpu_openrisc_map_address_code)(struct OpenRISCCPU *cpu, hwaddr *physical, @@ -301,6 +289,10 @@ typedef struct CPUOpenRISCState { uint32_t dflag; /* In delay slot (boolean) */ +#ifndef CONFIG_USER_ONLY + CPUOpenRISCTLBContext tlb; +#endif + /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; @@ -310,8 +302,6 @@ typedef struct CPUOpenRISCState { uint32_t cpucfgr; /* CPU configure register */ #ifndef CONFIG_USER_ONLY - CPUOpenRISCTLBContext * tlb; - QEMUTimer *timer; uint32_t ttmr; /* Timer tick mode register */ int is_counting; @@ -358,6 +348,7 @@ void openrisc_translate_init(void); int openrisc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, int mmu_idx); int cpu_openrisc_signal_handler(int host_signum, void *pinfo, void *puc); +int print_insn_or1k(bfd_vma addr, disassemble_info *info); #define cpu_list cpu_openrisc_list #define cpu_signal_handler cpu_openrisc_signal_handler @@ -376,17 +367,6 @@ void cpu_openrisc_count_update(OpenRISCCPU *cpu); void cpu_openrisc_timer_update(OpenRISCCPU *cpu); void cpu_openrisc_count_start(OpenRISCCPU *cpu); void cpu_openrisc_count_stop(OpenRISCCPU *cpu); - -void cpu_openrisc_mmu_init(OpenRISCCPU *cpu); -int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw); -int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw); -int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw); #endif #define OPENRISC_CPU_TYPE_SUFFIX "-" TYPE_OPENRISC_CPU @@ -395,9 +375,12 @@ int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, #include "exec/cpu-all.h" -#define TB_FLAGS_DFLAG 1 -#define TB_FLAGS_R0_0 2 +#define TB_FLAGS_SM SR_SM +#define TB_FLAGS_DME SR_DME +#define TB_FLAGS_IME SR_IME #define TB_FLAGS_OVE SR_OVE +#define TB_FLAGS_DFLAG 2 /* reuse SR_TEE */ +#define TB_FLAGS_R0_0 4 /* reuse SR_IEE */ static inline uint32_t cpu_get_gpr(const CPUOpenRISCState *env, int i) { @@ -415,17 +398,21 @@ static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, { *pc = env->pc; *cs_base = 0; - *flags = (env->dflag - | (cpu_get_gpr(env, 0) == 0 ? TB_FLAGS_R0_0 : 0) - | (env->sr & SR_OVE)); + *flags = (env->dflag ? TB_FLAGS_DFLAG : 0) + | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0) + | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE)); } static inline int cpu_mmu_index(CPUOpenRISCState *env, bool ifetch) { - if (!(env->sr & SR_IME)) { - return MMU_NOMMU_IDX; + int ret = MMU_NOMMU_IDX; /* mmu is disabled */ + + if (env->sr & (ifetch ? SR_IME : SR_DME)) { + /* The mmu is enabled; test supervisor state. */ + ret = env->sr & SR_SM ? MMU_SUPERVISOR_IDX : MMU_USER_IDX; } - return (env->sr & SR_SM) == 0 ? MMU_USER_IDX : MMU_SUPERVISOR_IDX; + + return ret; } static inline uint32_t cpu_get_sr(const CPUOpenRISCState *env) diff --git a/target/openrisc/disas.c b/target/openrisc/disas.c new file mode 100644 index 0000000000..4bfd2dd8a6 --- /dev/null +++ b/target/openrisc/disas.c @@ -0,0 +1,170 @@ +/* + * OpenRISC disassembler + * + * Copyright (c) 2018 Richard Henderson + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "disas/bfd.h" +#include "qemu/bitops.h" +#include "cpu.h" + +typedef disassemble_info DisasContext; + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" + +#define output(mnemonic, format, ...) \ + (info->fprintf_func(info->stream, "%-9s " format, \ + mnemonic, ##__VA_ARGS__)) + +int print_insn_or1k(bfd_vma addr, disassemble_info *info) +{ + bfd_byte buffer[4]; + uint32_t insn; + int status; + + status = info->read_memory_func(addr, buffer, 4, info); + if (status != 0) { + info->memory_error_func(status, addr, info); + return -1; + } + insn = bfd_getb32(buffer); + + if (!decode(info, insn)) { + output(".long", "%#08x", insn); + } + return 4; +} + +#define INSN(opcode, format, ...) \ +static bool trans_l_##opcode(disassemble_info *info, \ + arg_l_##opcode *a, uint32_t insn) \ +{ \ + output("l." #opcode, format, ##__VA_ARGS__); \ + return true; \ +} + +INSN(add, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(addc, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(sub, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(and, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(or, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(xor, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(sll, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(srl, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(sra, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(ror, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(exths, "r%d, r%d", a->d, a->a) +INSN(extbs, "r%d, r%d", a->d, a->a) +INSN(exthz, "r%d, r%d", a->d, a->a) +INSN(extbz, "r%d, r%d", a->d, a->a) +INSN(cmov, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(ff1, "r%d, r%d", a->d, a->a) +INSN(fl1, "r%d, r%d", a->d, a->a) +INSN(mul, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(mulu, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(div, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(divu, "r%d, r%d, r%d", a->d, a->a, a->b) +INSN(muld, "r%d, r%d", a->a, a->b) +INSN(muldu, "r%d, r%d", a->a, a->b) +INSN(j, "%d", a->n) +INSN(jal, "%d", a->n) +INSN(bf, "%d", a->n) +INSN(bnf, "%d", a->n) +INSN(jr, "r%d", a->b) +INSN(jalr, "r%d", a->b) +INSN(lwa, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lwz, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lws, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lbz, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lbs, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lhz, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(lhs, "r%d, %d(r%d)", a->d, a->i, a->a) +INSN(swa, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(sw, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(sb, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(sh, "%d(r%d), r%d", a->i, a->a, a->b) +INSN(nop, "") +INSN(addi, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(addic, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(muli, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(maci, "r%d, %d", a->a, a->i) +INSN(andi, "r%d, r%d, %d", a->d, a->a, a->k) +INSN(ori, "r%d, r%d, %d", a->d, a->a, a->k) +INSN(xori, "r%d, r%d, %d", a->d, a->a, a->i) +INSN(mfspr, "r%d, r%d, %d", a->d, a->a, a->k) +INSN(mtspr, "r%d, r%d, %d", a->a, a->b, a->k) +INSN(mac, "r%d, r%d", a->a, a->b) +INSN(msb, "r%d, r%d", a->a, a->b) +INSN(macu, "r%d, r%d", a->a, a->b) +INSN(msbu, "r%d, r%d", a->a, a->b) +INSN(slli, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(srli, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(srai, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(rori, "r%d, r%d, %d", a->d, a->a, a->l) +INSN(movhi, "r%d, %d", a->d, a->k) +INSN(macrc, "r%d", a->d) +INSN(sfeq, "r%d, r%d", a->a, a->b) +INSN(sfne, "r%d, r%d", a->a, a->b) +INSN(sfgtu, "r%d, r%d", a->a, a->b) +INSN(sfgeu, "r%d, r%d", a->a, a->b) +INSN(sfltu, "r%d, r%d", a->a, a->b) +INSN(sfleu, "r%d, r%d", a->a, a->b) +INSN(sfgts, "r%d, r%d", a->a, a->b) +INSN(sfges, "r%d, r%d", a->a, a->b) +INSN(sflts, "r%d, r%d", a->a, a->b) +INSN(sfles, "r%d, r%d", a->a, a->b) +INSN(sfeqi, "r%d, %d", a->a, a->i) +INSN(sfnei, "r%d, %d", a->a, a->i) +INSN(sfgtui, "r%d, %d", a->a, a->i) +INSN(sfgeui, "r%d, %d", a->a, a->i) +INSN(sfltui, "r%d, %d", a->a, a->i) +INSN(sfleui, "r%d, %d", a->a, a->i) +INSN(sfgtsi, "r%d, %d", a->a, a->i) +INSN(sfgesi, "r%d, %d", a->a, a->i) +INSN(sfltsi, "r%d, %d", a->a, a->i) +INSN(sflesi, "r%d, %d", a->a, a->i) +INSN(sys, "%d", a->k) +INSN(trap, "%d", a->k) +INSN(msync, "") +INSN(psync, "") +INSN(csync, "") +INSN(rfe, "") + +#define FP_INSN(opcode, suffix, format, ...) \ +static bool trans_lf_##opcode##_##suffix(disassemble_info *info, \ + arg_lf_##opcode##_##suffix *a, uint32_t insn) \ +{ \ + output("lf." #opcode "." #suffix, format, ##__VA_ARGS__); \ + return true; \ +} + +FP_INSN(add, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(sub, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(mul, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(div, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(rem, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(itof, s, "r%d, r%d", a->d, a->a) +FP_INSN(ftoi, s, "r%d, r%d", a->d, a->a) +FP_INSN(madd, s, "r%d, r%d, r%d", a->d, a->a, a->b) +FP_INSN(sfeq, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfne, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfgt, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfge, s, "r%d, r%d", a->a, a->b) +FP_INSN(sflt, s, "r%d, r%d", a->a, a->b) +FP_INSN(sfle, s, "r%d, r%d", a->a, a->b) diff --git a/target/openrisc/fpu_helper.c b/target/openrisc/fpu_helper.c index 977a1e8e55..265ce13337 100644 --- a/target/openrisc/fpu_helper.c +++ b/target/openrisc/fpu_helper.c @@ -24,121 +24,70 @@ #include "exception.h" #include "fpu/softfloat.h" -static inline uint32_t ieee_ex_to_openrisc(OpenRISCCPU *cpu, int fexcp) +static int ieee_ex_to_openrisc(int fexcp) { int ret = 0; - if (fexcp) { - if (fexcp & float_flag_invalid) { - cpu->env.fpcsr |= FPCSR_IVF; - ret = 1; - } - if (fexcp & float_flag_overflow) { - cpu->env.fpcsr |= FPCSR_OVF; - ret = 1; - } - if (fexcp & float_flag_underflow) { - cpu->env.fpcsr |= FPCSR_UNF; - ret = 1; - } - if (fexcp & float_flag_divbyzero) { - cpu->env.fpcsr |= FPCSR_DZF; - ret = 1; - } - if (fexcp & float_flag_inexact) { - cpu->env.fpcsr |= FPCSR_IXF; - ret = 1; - } + if (fexcp & float_flag_invalid) { + ret |= FPCSR_IVF; + } + if (fexcp & float_flag_overflow) { + ret |= FPCSR_OVF; + } + if (fexcp & float_flag_underflow) { + ret |= FPCSR_UNF; + } + if (fexcp & float_flag_divbyzero) { + ret |= FPCSR_DZF; + } + if (fexcp & float_flag_inexact) { + ret |= FPCSR_IXF; } - return ret; } -static inline void update_fpcsr(OpenRISCCPU *cpu) +void HELPER(update_fpcsr)(CPUOpenRISCState *env) { - int tmp = ieee_ex_to_openrisc(cpu, - get_float_exception_flags(&cpu->env.fp_status)); + int tmp = get_float_exception_flags(&env->fp_status); - SET_FP_CAUSE(cpu->env.fpcsr, tmp); - if ((GET_FP_ENABLE(cpu->env.fpcsr) & tmp) && - (cpu->env.fpcsr & FPCSR_FPEE)) { - helper_exception(&cpu->env, EXCP_FPE); - } else { - UPDATE_FP_FLAGS(cpu->env.fpcsr, tmp); + if (tmp) { + set_float_exception_flags(0, &env->fp_status); + tmp = ieee_ex_to_openrisc(tmp); + if (tmp) { + env->fpcsr |= tmp; + if (env->fpcsr & FPCSR_FPEE) { + helper_exception(env, EXCP_FPE); + } + } } } uint64_t HELPER(itofd)(CPUOpenRISCState *env, uint64_t val) { - uint64_t itofd; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - - set_float_exception_flags(0, &cpu->env.fp_status); - itofd = int32_to_float64(val, &cpu->env.fp_status); - update_fpcsr(cpu); - - return itofd; + return int32_to_float64(val, &env->fp_status); } uint32_t HELPER(itofs)(CPUOpenRISCState *env, uint32_t val) { - uint32_t itofs; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - - set_float_exception_flags(0, &cpu->env.fp_status); - itofs = int32_to_float32(val, &cpu->env.fp_status); - update_fpcsr(cpu); - - return itofs; + return int32_to_float32(val, &env->fp_status); } uint64_t HELPER(ftoid)(CPUOpenRISCState *env, uint64_t val) { - uint64_t ftoid; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - - set_float_exception_flags(0, &cpu->env.fp_status); - ftoid = float32_to_int64(val, &cpu->env.fp_status); - update_fpcsr(cpu); - - return ftoid; + return float32_to_int64(val, &env->fp_status); } uint32_t HELPER(ftois)(CPUOpenRISCState *env, uint32_t val) { - uint32_t ftois; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - - set_float_exception_flags(0, &cpu->env.fp_status); - ftois = float32_to_int32(val, &cpu->env.fp_status); - update_fpcsr(cpu); - - return ftois; + return float32_to_int32(val, &env->fp_status); } -#define FLOAT_OP(name, p) void helper_float_##_##p(void) - #define FLOAT_CALC(name) \ uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ uint64_t fdt0, uint64_t fdt1) \ -{ \ - uint64_t result; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - result = float64_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return result; \ -} \ - \ +{ return float64_ ## name(fdt0, fdt1, &env->fp_status); } \ uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ uint32_t fdt0, uint32_t fdt1) \ -{ \ - uint32_t result; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - result = float32_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return result; \ -} \ +{ return float32_ ## name(fdt0, fdt1, &env->fp_status); } FLOAT_CALC(add) FLOAT_CALC(sub) @@ -151,132 +100,29 @@ FLOAT_CALC(rem) uint64_t helper_float_madd_d(CPUOpenRISCState *env, uint64_t a, uint64_t b, uint64_t c) { - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - uint64_t result; - set_float_exception_flags(0, &cpu->env.fp_status); - /* Note that or1ksim doesn't use merged operation. */ - result = float64_mul(b, c, &cpu->env.fp_status); - result = float64_add(result, a, &cpu->env.fp_status); - update_fpcsr(cpu); - return result; + /* Note that or1ksim doesn't use fused operation. */ + b = float64_mul(b, c, &env->fp_status); + return float64_add(a, b, &env->fp_status); } uint32_t helper_float_madd_s(CPUOpenRISCState *env, uint32_t a, uint32_t b, uint32_t c) { - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - uint32_t result; - set_float_exception_flags(0, &cpu->env.fp_status); - /* Note that or1ksim doesn't use merged operation. */ - result = float32_mul(b, c, &cpu->env.fp_status); - result = float32_add(result, a, &cpu->env.fp_status); - update_fpcsr(cpu); - return result; + /* Note that or1ksim doesn't use fused operation. */ + b = float32_mul(b, c, &env->fp_status); + return float32_add(a, b, &env->fp_status); } -#define FLOAT_CMP(name) \ -uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = float64_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ - uint32_t fdt0, uint32_t fdt1)\ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = float32_ ## name(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} +#define FLOAT_CMP(name, impl) \ +target_ulong helper_float_ ## name ## _d(CPUOpenRISCState *env, \ + uint64_t fdt0, uint64_t fdt1) \ +{ return float64_ ## impl(fdt0, fdt1, &env->fp_status); } \ +target_ulong helper_float_ ## name ## _s(CPUOpenRISCState *env, \ + uint32_t fdt0, uint32_t fdt1) \ +{ return float32_ ## impl(fdt0, fdt1, &env->fp_status); } -FLOAT_CMP(le) -FLOAT_CMP(eq) -FLOAT_CMP(lt) +FLOAT_CMP(le, le) +FLOAT_CMP(lt, lt) +FLOAT_CMP(eq, eq_quiet) #undef FLOAT_CMP - - -#define FLOAT_CMPNE(name) \ -uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float64_eq_quiet(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ - uint32_t fdt0, uint32_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float32_eq_quiet(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} - -FLOAT_CMPNE(ne) -#undef FLOAT_CMPNE - -#define FLOAT_CMPGT(name) \ -uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float64_le(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ - uint32_t fdt0, uint32_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float32_le(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} -FLOAT_CMPGT(gt) -#undef FLOAT_CMPGT - -#define FLOAT_CMPGE(name) \ -uint64_t helper_float_ ## name ## _d(CPUOpenRISCState *env, \ - uint64_t fdt0, uint64_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float64_lt(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} \ - \ -uint32_t helper_float_ ## name ## _s(CPUOpenRISCState *env, \ - uint32_t fdt0, uint32_t fdt1) \ -{ \ - int res; \ - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); \ - set_float_exception_flags(0, &cpu->env.fp_status); \ - res = !float32_lt(fdt0, fdt1, &cpu->env.fp_status); \ - update_fpcsr(cpu); \ - return res; \ -} - -FLOAT_CMPGE(ge) -#undef FLOAT_CMPGE diff --git a/target/openrisc/helper.h b/target/openrisc/helper.h index 4fd1a6bb8e..9db9bf3963 100644 --- a/target/openrisc/helper.h +++ b/target/openrisc/helper.h @@ -24,17 +24,19 @@ DEF_HELPER_FLAGS_1(ove_ov, TCG_CALL_NO_WG, void, env) DEF_HELPER_FLAGS_1(ove_cyov, TCG_CALL_NO_WG, void, env) /* float */ -DEF_HELPER_FLAGS_2(itofd, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(itofs, TCG_CALL_NO_WG, i32, env, i32) -DEF_HELPER_FLAGS_2(ftoid, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(ftois, TCG_CALL_NO_WG, i32, env, i32) +DEF_HELPER_FLAGS_1(update_fpcsr, TCG_CALL_NO_WG, void, env) -DEF_HELPER_FLAGS_4(float_madd_s, TCG_CALL_NO_WG, i32, env, i32, i32, i32) -DEF_HELPER_FLAGS_4(float_madd_d, TCG_CALL_NO_WG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_2(itofd, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(itofs, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(ftoid, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(ftois, TCG_CALL_NO_RWG, i32, env, i32) + +DEF_HELPER_FLAGS_4(float_madd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32) +DEF_HELPER_FLAGS_4(float_madd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) #define FOP_CALC(op) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_WG, i32, env, i32, i32) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_RWG, i32, env, i32, i32) \ +DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_RWG, i64, env, i64, i64) FOP_CALC(add) FOP_CALC(sub) FOP_CALC(mul) @@ -43,19 +45,16 @@ FOP_CALC(rem) #undef FOP_CALC #define FOP_CMP(op) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_WG, i32, env, i32, i32) \ -DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(float_ ## op ## _s, TCG_CALL_NO_RWG, tl, env, i32, i32) \ +DEF_HELPER_FLAGS_3(float_ ## op ## _d, TCG_CALL_NO_RWG, tl, env, i64, i64) FOP_CMP(eq) FOP_CMP(lt) FOP_CMP(le) -FOP_CMP(ne) -FOP_CMP(gt) -FOP_CMP(ge) #undef FOP_CMP /* interrupt */ DEF_HELPER_FLAGS_1(rfe, 0, void, env) /* sys */ -DEF_HELPER_FLAGS_4(mtspr, 0, void, env, tl, tl, tl) -DEF_HELPER_FLAGS_4(mfspr, TCG_CALL_NO_WG, tl, env, tl, tl, tl) +DEF_HELPER_FLAGS_3(mtspr, 0, void, env, tl, tl) +DEF_HELPER_FLAGS_3(mfspr, TCG_CALL_NO_WG, tl, env, tl, tl) diff --git a/target/openrisc/insns.decode b/target/openrisc/insns.decode new file mode 100644 index 0000000000..dad68c8422 --- /dev/null +++ b/target/openrisc/insns.decode @@ -0,0 +1,189 @@ +# +# OpenRISC instruction decode definitions. +# +# Copyright (c) 2018 Richard Henderson +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see . +# + +&dab d a b +&da d a +&ab a b +&dal d a l +&ai a i + +#### +# System Instructions +#### + +l_sys 001000 0000000000 k:16 +l_trap 001000 0100000000 k:16 +l_msync 001000 1000000000 00000000 00000000 +l_psync 001000 1010000000 00000000 00000000 +l_csync 001000 1100000000 00000000 00000000 + +l_rfe 001001 ----- ----- -------- -------- + +#### +# Branch Instructions +#### + +l_j 000000 n:s26 +l_jal 000001 n:s26 +l_bnf 000011 n:s26 +l_bf 000100 n:s26 + +l_jr 010001 ---------- b:5 ----------- +l_jalr 010010 ---------- b:5 ----------- + +#### +# Memory Instructions +#### + +&load d a i +@load ...... d:5 a:5 i:s16 &load + +%store_i 21:s5 0:11 +&store a b i +@store ...... ..... a:5 b:5 ........... &store i=%store_i + +l_lwa 011011 ..... ..... ........ ........ @load +l_lwz 100001 ..... ..... ........ ........ @load +l_lws 100010 ..... ..... ........ ........ @load +l_lbz 100011 ..... ..... ........ ........ @load +l_lbs 100100 ..... ..... ........ ........ @load +l_lhz 100101 ..... ..... ........ ........ @load +l_lhs 100110 ..... ..... ........ ........ @load + +l_swa 110011 ..... ..... ..... ........... @store +l_sw 110101 ..... ..... ..... ........... @store +l_sb 110110 ..... ..... ..... ........... @store +l_sh 110111 ..... ..... ..... ........... @store + +#### +# Immediate Operand Instructions +#### + +%mtspr_k 21:5 0:11 + +&rri d a i +&rrk d a k +@rri ...... d:5 a:5 i:s16 &rri +@rrk ...... d:5 a:5 k:16 &rrk + +l_nop 000101 01--- ----- k:16 + +l_addi 100111 ..... ..... ........ ........ @rri +l_addic 101000 ..... ..... ........ ........ @rri +l_andi 101001 ..... ..... ........ ........ @rrk +l_ori 101010 ..... ..... ........ ........ @rrk +l_xori 101011 ..... ..... ........ ........ @rri +l_muli 101100 ..... ..... ........ ........ @rri + +l_mfspr 101101 ..... ..... ........ ........ @rrk +l_mtspr 110000 ..... a:5 b:5 ........... k=%mtspr_k + +l_maci 010011 ----- a:5 i:s16 + +l_movhi 000110 d:5 ----0 k:16 +l_macrc 000110 d:5 ----1 00000000 00000000 + +#### +# Arithmetic Instructions +#### + +l_exths 111000 d:5 a:5 ----- - 0000 -- 1100 +l_extbs 111000 d:5 a:5 ----- - 0001 -- 1100 +l_exthz 111000 d:5 a:5 ----- - 0010 -- 1100 +l_extbz 111000 d:5 a:5 ----- - 0011 -- 1100 + +l_add 111000 d:5 a:5 b:5 - 00 ---- 0000 +l_addc 111000 d:5 a:5 b:5 - 00 ---- 0001 +l_sub 111000 d:5 a:5 b:5 - 00 ---- 0010 +l_and 111000 d:5 a:5 b:5 - 00 ---- 0011 +l_or 111000 d:5 a:5 b:5 - 00 ---- 0100 +l_xor 111000 d:5 a:5 b:5 - 00 ---- 0101 +l_cmov 111000 d:5 a:5 b:5 - 00 ---- 1110 +l_ff1 111000 d:5 a:5 ----- - 00 ---- 1111 +l_fl1 111000 d:5 a:5 ----- - 01 ---- 1111 + +l_sll 111000 d:5 a:5 b:5 - 0000 -- 1000 +l_srl 111000 d:5 a:5 b:5 - 0001 -- 1000 +l_sra 111000 d:5 a:5 b:5 - 0010 -- 1000 +l_ror 111000 d:5 a:5 b:5 - 0011 -- 1000 + +l_mul 111000 d:5 a:5 b:5 - 11 ---- 0110 +l_mulu 111000 d:5 a:5 b:5 - 11 ---- 1011 +l_div 111000 d:5 a:5 b:5 - 11 ---- 1001 +l_divu 111000 d:5 a:5 b:5 - 11 ---- 1010 + +l_muld 111000 ----- a:5 b:5 - 11 ---- 0111 +l_muldu 111000 ----- a:5 b:5 - 11 ---- 1100 + +l_mac 110001 ----- a:5 b:5 ------- 0001 +l_macu 110001 ----- a:5 b:5 ------- 0011 +l_msb 110001 ----- a:5 b:5 ------- 0010 +l_msbu 110001 ----- a:5 b:5 ------- 0100 + +l_slli 101110 d:5 a:5 -------- 00 l:6 +l_srli 101110 d:5 a:5 -------- 01 l:6 +l_srai 101110 d:5 a:5 -------- 10 l:6 +l_rori 101110 d:5 a:5 -------- 11 l:6 + +#### +# Compare Instructions +#### + +l_sfeq 111001 00000 a:5 b:5 ----------- +l_sfne 111001 00001 a:5 b:5 ----------- +l_sfgtu 111001 00010 a:5 b:5 ----------- +l_sfgeu 111001 00011 a:5 b:5 ----------- +l_sfltu 111001 00100 a:5 b:5 ----------- +l_sfleu 111001 00101 a:5 b:5 ----------- +l_sfgts 111001 01010 a:5 b:5 ----------- +l_sfges 111001 01011 a:5 b:5 ----------- +l_sflts 111001 01100 a:5 b:5 ----------- +l_sfles 111001 01101 a:5 b:5 ----------- + +l_sfeqi 101111 00000 a:5 i:s16 +l_sfnei 101111 00001 a:5 i:s16 +l_sfgtui 101111 00010 a:5 i:s16 +l_sfgeui 101111 00011 a:5 i:s16 +l_sfltui 101111 00100 a:5 i:s16 +l_sfleui 101111 00101 a:5 i:s16 +l_sfgtsi 101111 01010 a:5 i:s16 +l_sfgesi 101111 01011 a:5 i:s16 +l_sfltsi 101111 01100 a:5 i:s16 +l_sflesi 101111 01101 a:5 i:s16 + +#### +# FP Instructions +#### + +lf_add_s 110010 d:5 a:5 b:5 --- 00000000 +lf_sub_s 110010 d:5 a:5 b:5 --- 00000001 +lf_mul_s 110010 d:5 a:5 b:5 --- 00000010 +lf_div_s 110010 d:5 a:5 b:5 --- 00000011 +lf_rem_s 110010 d:5 a:5 b:5 --- 00000110 +lf_madd_s 110010 d:5 a:5 b:5 --- 00000111 + +lf_itof_s 110010 d:5 a:5 00000 --- 00000100 +lf_ftoi_s 110010 d:5 a:5 00000 --- 00000101 + +lf_sfeq_s 110010 ----- a:5 b:5 --- 00001000 +lf_sfne_s 110010 ----- a:5 b:5 --- 00001001 +lf_sfgt_s 110010 ----- a:5 b:5 --- 00001010 +lf_sfge_s 110010 ----- a:5 b:5 --- 00001011 +lf_sflt_s 110010 ----- a:5 b:5 --- 00001100 +lf_sfle_s 110010 ----- a:5 b:5 --- 00001101 diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index 3959671c59..bbae956361 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -32,29 +32,22 @@ void openrisc_cpu_do_interrupt(CPUState *cs) #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = OPENRISC_CPU(cs); CPUOpenRISCState *env = &cpu->env; + int exception = cs->exception_index; env->epcr = env->pc; - if (env->dflag) { - env->dflag = 0; - env->sr |= SR_DSX; - env->epcr -= 4; - } else { - env->sr &= ~SR_DSX; - } - if (cs->exception_index == EXCP_SYSCALL) { + if (exception == EXCP_SYSCALL) { env->epcr += 4; } /* When we have an illegal instruction the error effective address shall be set to the illegal instruction address. */ - if (cs->exception_index == EXCP_ILLEGAL) { + if (exception == EXCP_ILLEGAL) { env->eear = env->pc; } - /* For machine-state changed between user-mode and supervisor mode, - we need flush TLB when we enter&exit EXCP. */ - tlb_flush(cs); - + /* During exceptions esr is populared with the pre-exception sr. */ env->esr = cpu_get_sr(env); + /* In parallel sr is updated to disable mmu, interrupts, timers and + set the delay slot exception flag. */ env->sr &= ~SR_DME; env->sr &= ~SR_IME; env->sr |= SR_SM; @@ -62,12 +55,38 @@ void openrisc_cpu_do_interrupt(CPUState *cs) env->sr &= ~SR_TEE; env->pmr &= ~PMR_DME; env->pmr &= ~PMR_SME; - env->tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; - env->tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; env->lock_addr = -1; - if (cs->exception_index > 0 && cs->exception_index < EXCP_NR) { - hwaddr vect_pc = cs->exception_index << 8; + /* Set/clear dsx to indicate if we are in a delay slot exception. */ + if (env->dflag) { + env->dflag = 0; + env->sr |= SR_DSX; + env->epcr -= 4; + } else { + env->sr &= ~SR_DSX; + } + + if (exception > 0 && exception < EXCP_NR) { + static const char * const int_name[EXCP_NR] = { + [EXCP_RESET] = "RESET", + [EXCP_BUSERR] = "BUSERR (bus error)", + [EXCP_DPF] = "DFP (data protection fault)", + [EXCP_IPF] = "IPF (code protection fault)", + [EXCP_TICK] = "TICK (timer interrupt)", + [EXCP_ALIGN] = "ALIGN", + [EXCP_ILLEGAL] = "ILLEGAL", + [EXCP_INT] = "INT (device interrupt)", + [EXCP_DTLBMISS] = "DTLBMISS (data tlb miss)", + [EXCP_ITLBMISS] = "ITLBMISS (code tlb miss)", + [EXCP_RANGE] = "RANGE", + [EXCP_SYSCALL] = "SYSCALL", + [EXCP_FPE] = "FPE", + [EXCP_TRAP] = "TRAP", + }; + + qemu_log_mask(CPU_LOG_INT, "INT: %s\n", int_name[exception]); + + hwaddr vect_pc = exception << 8; if (env->cpucfgr & CPUCFGR_EVBARP) { vect_pc |= env->evbar; } @@ -76,7 +95,7 @@ void openrisc_cpu_do_interrupt(CPUState *cs) } env->pc = vect_pc; } else { - cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); + cpu_abort(cs, "Unhandled exception 0x%x\n", exception); } #endif diff --git a/target/openrisc/interrupt_helper.c b/target/openrisc/interrupt_helper.c index 56620e0571..9c5489f5f7 100644 --- a/target/openrisc/interrupt_helper.c +++ b/target/openrisc/interrupt_helper.c @@ -25,36 +25,7 @@ void HELPER(rfe)(CPUOpenRISCState *env) { - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - CPUState *cs = CPU(cpu); -#ifndef CONFIG_USER_ONLY - int need_flush_tlb = (cpu->env.sr & (SR_SM | SR_IME | SR_DME)) ^ - (cpu->env.esr & (SR_SM | SR_IME | SR_DME)); -#endif - cpu->env.pc = cpu->env.epcr; - cpu_set_sr(&cpu->env, cpu->env.esr); - cpu->env.lock_addr = -1; - -#ifndef CONFIG_USER_ONLY - if (cpu->env.sr & SR_DME) { - cpu->env.tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_data; - } else { - cpu->env.tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_nommu; - } - - if (cpu->env.sr & SR_IME) { - cpu->env.tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_code; - } else { - cpu->env.tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_nommu; - } - - if (need_flush_tlb) { - tlb_flush(cs); - } -#endif - cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + env->pc = env->epcr; + env->lock_addr = -1; + cpu_set_sr(env, env->esr); } diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index 0a793eb14d..1eedbf3dbe 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -24,31 +24,6 @@ #include "hw/boards.h" #include "migration/cpu.h" -static int env_post_load(void *opaque, int version_id) -{ - CPUOpenRISCState *env = opaque; - - /* Restore MMU handlers */ - if (env->sr & SR_DME) { - env->tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_data; - } else { - env->tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_nommu; - } - - if (env->sr & SR_IME) { - env->tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_code; - } else { - env->tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_nommu; - } - - - return 0; -} - static const VMStateDescription vmstate_tlb_entry = { .name = "tlb_entry", .version_id = 1, @@ -63,24 +38,17 @@ static const VMStateDescription vmstate_tlb_entry = { static const VMStateDescription vmstate_cpu_tlb = { .name = "cpu_tlb", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { - VMSTATE_STRUCT_2DARRAY(itlb, CPUOpenRISCTLBContext, - ITLB_WAYS, ITLB_SIZE, 0, + VMSTATE_STRUCT_ARRAY(itlb, CPUOpenRISCTLBContext, TLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), - VMSTATE_STRUCT_2DARRAY(dtlb, CPUOpenRISCTLBContext, - DTLB_WAYS, DTLB_SIZE, 0, + VMSTATE_STRUCT_ARRAY(dtlb, CPUOpenRISCTLBContext, TLB_SIZE, 0, vmstate_tlb_entry, OpenRISCTLBEntry), VMSTATE_END_OF_LIST() } }; -#define VMSTATE_CPU_TLB(_f, _s) \ - VMSTATE_STRUCT_POINTER(_f, _s, vmstate_cpu_tlb, CPUOpenRISCTLBContext) - - static int get_sr(QEMUFile *f, void *opaque, size_t size, VMStateField *field) { CPUOpenRISCState *env = opaque; @@ -106,7 +74,6 @@ static const VMStateDescription vmstate_env = { .name = "env", .version_id = 6, .minimum_version_id = 6, - .post_load = env_post_load, .fields = (VMStateField[]) { VMSTATE_UINTTL_2DARRAY(shadow_gpr, CPUOpenRISCState, 16, 32), VMSTATE_UINTTL(pc, CPUOpenRISCState), @@ -143,7 +110,8 @@ static const VMStateDescription vmstate_env = { VMSTATE_UINT32(fpcsr, CPUOpenRISCState), VMSTATE_UINT64(mac, CPUOpenRISCState), - VMSTATE_CPU_TLB(tlb, CPUOpenRISCState), + VMSTATE_STRUCT(tlb, CPUOpenRISCState, 1, + vmstate_cpu_tlb, CPUOpenRISCTLBContext), VMSTATE_TIMER_PTR(timer, CPUOpenRISCState), VMSTATE_UINT32(ttmr, CPUOpenRISCState), diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 2bd782f89b..e7d5219e11 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -29,227 +29,156 @@ #endif #ifndef CONFIG_USER_ONLY -int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw) +static inline void get_phys_nommu(hwaddr *phys_addr, int *prot, + target_ulong address) { - *physical = address; + *phys_addr = address; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return TLBRET_MATCH; } -int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw) +static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot, + target_ulong addr, int need, bool super) { - int vpn = address >> TARGET_PAGE_BITS; - int idx = vpn & ITLB_MASK; - int right = 0; + int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK; + uint32_t imr = cpu->env.tlb.itlb[idx].mr; + uint32_t itr = cpu->env.tlb.itlb[idx].tr; + uint32_t dmr = cpu->env.tlb.dtlb[idx].mr; + uint32_t dtr = cpu->env.tlb.dtlb[idx].tr; + int right, match, valid; - if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { - return TLBRET_NOMATCH; - } - if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) { - return TLBRET_INVALID; - } - - if (cpu->env.sr & SR_SM) { /* supervisor mode */ - if (cpu->env.tlb->itlb[0][idx].tr & SXE) { - right |= PAGE_EXEC; - } - } else { - if (cpu->env.tlb->itlb[0][idx].tr & UXE) { - right |= PAGE_EXEC; + /* If the ITLB and DTLB indexes map to the same page, we want to + load all permissions all at once. If the destination pages do + not match, zap the one we don't need. */ + if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) { + if (need & PAGE_EXEC) { + dmr = dtr = 0; + } else { + imr = itr = 0; } } - if ((rw & 2) && ((right & PAGE_EXEC) == 0)) { - return TLBRET_BADADDR; - } + /* Check if either of the entries matches the source address. */ + match = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC; + match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE; - *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) | - (address & (TARGET_PAGE_SIZE-1)); + /* Check if either of the entries is valid. */ + valid = imr & 1 ? PAGE_EXEC : 0; + valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0; + valid &= match; + + /* Collect the permissions from the entries. */ + right = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0; + right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0; + right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0; + right &= valid; + + /* Note that above we validated that itr and dtr match on page. + So oring them together changes nothing without having to + check which one we needed. We also want to store to these + variables even on failure, as it avoids compiler warnings. */ + *phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK); *prot = right; - return TLBRET_MATCH; -} -int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, int rw) -{ - int vpn = address >> TARGET_PAGE_BITS; - int idx = vpn & DTLB_MASK; - int right = 0; + qemu_log_mask(CPU_LOG_MMU, + "MMU lookup: need %d match %d valid %d right %d -> %s\n", + need, match, valid, right, (need & right) ? "OK" : "FAIL"); - if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) { - return TLBRET_NOMATCH; - } - if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) { - return TLBRET_INVALID; + /* Check the collective permissions are present. */ + if (likely(need & right)) { + return 0; /* success! */ } - if (cpu->env.sr & SR_SM) { /* supervisor mode */ - if (cpu->env.tlb->dtlb[0][idx].tr & SRE) { - right |= PAGE_READ; - } - if (cpu->env.tlb->dtlb[0][idx].tr & SWE) { - right |= PAGE_WRITE; - } + /* Determine what kind of failure we have. */ + if (need & valid) { + return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF; } else { - if (cpu->env.tlb->dtlb[0][idx].tr & URE) { - right |= PAGE_READ; - } - if (cpu->env.tlb->dtlb[0][idx].tr & UWE) { - right |= PAGE_WRITE; - } + return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS; } - - if (!(rw & 1) && ((right & PAGE_READ) == 0)) { - return TLBRET_BADADDR; - } - if ((rw & 1) && ((right & PAGE_WRITE) == 0)) { - return TLBRET_BADADDR; - } - - *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) | - (address & (TARGET_PAGE_SIZE-1)); - *prot = right; - return TLBRET_MATCH; -} - -static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu, - hwaddr *physical, - int *prot, target_ulong address, - int rw) -{ - int ret = TLBRET_MATCH; - - if (rw == MMU_INST_FETCH) { /* ITLB */ - *physical = 0; - ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical, - prot, address, rw); - } else { /* DTLB */ - ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical, - prot, address, rw); - } - - return ret; } #endif -static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu, - target_ulong address, - int rw, int tlb_error) +static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address, + int exception) { CPUState *cs = CPU(cpu); - int exception = 0; - - switch (tlb_error) { - default: - if (rw == 2) { - exception = EXCP_IPF; - } else { - exception = EXCP_DPF; - } - break; -#ifndef CONFIG_USER_ONLY - case TLBRET_BADADDR: - if (rw == 2) { - exception = EXCP_IPF; - } else { - exception = EXCP_DPF; - } - break; - case TLBRET_INVALID: - case TLBRET_NOMATCH: - /* No TLB match for a mapped address */ - if (rw == 2) { - exception = EXCP_ITLBMISS; - } else { - exception = EXCP_DTLBMISS; - } - break; -#endif - } cs->exception_index = exception; cpu->env.eear = address; cpu->env.lock_addr = -1; } -#ifndef CONFIG_USER_ONLY int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, int mmu_idx) { +#ifdef CONFIG_USER_ONLY OpenRISCCPU *cpu = OPENRISC_CPU(cs); - int ret = 0; - hwaddr physical = 0; - int prot = 0; - - ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot, - address, rw); - - if (ret == TLBRET_MATCH) { - tlb_set_page(cs, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, TARGET_PAGE_SIZE); - ret = 0; - } else if (ret < 0) { - cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); - ret = 1; - } - - return ret; -} + raise_mmu_exception(cpu, address, EXCP_DPF); + return 1; #else -int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, - int rw, int mmu_idx) -{ - OpenRISCCPU *cpu = OPENRISC_CPU(cs); - int ret = 0; - - cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret); - ret = 1; - - return ret; -} + g_assert_not_reached(); #endif +} #ifndef CONFIG_USER_ONLY hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { OpenRISCCPU *cpu = OPENRISC_CPU(cs); + int prot, excp, sr = cpu->env.sr; hwaddr phys_addr; - int prot; - int miss; - /* Check memory for any kind of address, since during debug the - gdb can ask for anything, check data tlb for address */ - miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0); + switch (sr & (SR_DME | SR_IME)) { + case SR_DME | SR_IME: + /* The mmu is definitely enabled. */ + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, + PAGE_EXEC | PAGE_READ | PAGE_WRITE, + (sr & SR_SM) != 0); + return excp ? -1 : phys_addr; - /* Check instruction tlb */ - if (miss) { - miss = cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, - MMU_INST_FETCH); - } + default: + /* The mmu is partially enabled, and we don't really have + a "real" access type. Begin by trying the mmu, but if + that fails try again without. */ + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, + PAGE_EXEC | PAGE_READ | PAGE_WRITE, + (sr & SR_SM) != 0); + if (!excp) { + return phys_addr; + } + /* fallthru */ - /* Last, fall back to a plain address */ - if (miss) { - miss = cpu_openrisc_get_phys_nommu(cpu, &phys_addr, &prot, addr, 0); - } - - if (miss) { - return -1; - } else { + case 0: + /* The mmu is definitely disabled; lookups never fail. */ + get_phys_nommu(&phys_addr, &prot, addr); return phys_addr; } } -void cpu_openrisc_mmu_init(OpenRISCCPU *cpu) +void tlb_fill(CPUState *cs, target_ulong addr, int size, + MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext)); + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + int prot, excp; + hwaddr phys_addr; - cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu; - cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu; + if (mmu_idx == MMU_NOMMU_IDX) { + /* The mmu is disabled; lookups never fail. */ + get_phys_nommu(&phys_addr, &prot, addr); + excp = 0; + } else { + bool super = mmu_idx == MMU_SUPERVISOR_IDX; + int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC + : access_type == MMU_DATA_STORE ? PAGE_WRITE + : PAGE_READ); + excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super); + } + + if (unlikely(excp)) { + raise_mmu_exception(cpu, addr, excp); + cpu_loop_exit_restore(cs, retaddr); + } + + tlb_set_page(cs, addr & TARGET_PAGE_MASK, + phys_addr & TARGET_PAGE_MASK, prot, + mmu_idx, TARGET_PAGE_SIZE); } #endif diff --git a/target/openrisc/mmu_helper.c b/target/openrisc/mmu_helper.c deleted file mode 100644 index 97e1d17b5a..0000000000 --- a/target/openrisc/mmu_helper.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * OpenRISC MMU helper routines - * - * Copyright (c) 2011-2012 Jia Liu - * Zhizhou Zhang - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" - -#ifndef CONFIG_USER_ONLY - -void tlb_fill(CPUState *cs, target_ulong addr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) -{ - int ret; - - ret = openrisc_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx); - - if (ret) { - /* Raise Exception. */ - cpu_loop_exit_restore(cs, retaddr); - } -} -#endif diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index b284064381..b66a45c1e0 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -27,13 +27,12 @@ #define TO_SPR(group, number) (((group) << 11) + (number)) -void HELPER(mtspr)(CPUOpenRISCState *env, - target_ulong ra, target_ulong rb, target_ulong offset) +void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) { #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = openrisc_env_get_cpu(env); CPUState *cs = CPU(cpu); - int spr = (ra | offset); + target_ulong mr; int idx; switch (spr) { @@ -57,26 +56,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, break; case TO_SPR(0, 17): /* SR */ - if ((env->sr & (SR_IME | SR_DME | SR_SM)) ^ - (rb & (SR_IME | SR_DME | SR_SM))) { - tlb_flush(cs); - } cpu_set_sr(env, rb); - if (env->sr & SR_DME) { - env->tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_data; - } else { - env->tlb->cpu_openrisc_map_address_data = - &cpu_openrisc_get_phys_nommu; - } - - if (env->sr & SR_IME) { - env->tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_code; - } else { - env->tlb->cpu_openrisc_map_address_code = - &cpu_openrisc_get_phys_nommu; - } break; case TO_SPR(0, 18): /* PPC */ @@ -98,18 +78,22 @@ void HELPER(mtspr)(CPUOpenRISCState *env, case TO_SPR(0, 1024) ... TO_SPR(0, 1024 + (16 * 32)): /* Shadow GPRs */ idx = (spr - 1024); env->shadow_gpr[idx / 32][idx % 32] = rb; - - case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ - idx = spr - TO_SPR(1, 512); - if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb->dtlb[0][idx].mr & TARGET_PAGE_MASK); - } - env->tlb->dtlb[0][idx].mr = rb; break; - case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ + case TO_SPR(1, 512) ... TO_SPR(1, 512 + TLB_SIZE - 1): /* DTLBW0MR 0-127 */ + idx = spr - TO_SPR(1, 512); + mr = env->tlb.dtlb[idx].mr; + if (mr & 1) { + tlb_flush_page(cs, mr & TARGET_PAGE_MASK); + } + if (rb & 1) { + tlb_flush_page(cs, rb & TARGET_PAGE_MASK); + } + env->tlb.dtlb[idx].mr = rb; + break; + case TO_SPR(1, 640) ... TO_SPR(1, 640 + TLB_SIZE - 1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); - env->tlb->dtlb[0][idx].tr = rb; + env->tlb.dtlb[idx].tr = rb; break; case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */ case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */ @@ -118,17 +102,21 @@ void HELPER(mtspr)(CPUOpenRISCState *env, case TO_SPR(1, 1280) ... TO_SPR(1, 1407): /* DTLBW3MR 0-127 */ case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */ break; - case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ - idx = spr - TO_SPR(2, 512); - if (!(rb & 1)) { - tlb_flush_page(cs, env->tlb->itlb[0][idx].mr & TARGET_PAGE_MASK); - } - env->tlb->itlb[0][idx].mr = rb; - break; - case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ + case TO_SPR(2, 512) ... TO_SPR(2, 512 + TLB_SIZE - 1): /* ITLBW0MR 0-127 */ + idx = spr - TO_SPR(2, 512); + mr = env->tlb.itlb[idx].mr; + if (mr & 1) { + tlb_flush_page(cs, mr & TARGET_PAGE_MASK); + } + if (rb & 1) { + tlb_flush_page(cs, rb & TARGET_PAGE_MASK); + } + env->tlb.itlb[idx].mr = rb; + break; + case TO_SPR(2, 640) ... TO_SPR(2, 640 + TLB_SIZE - 1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); - env->tlb->itlb[0][idx].tr = rb; + env->tlb.itlb[idx].tr = rb; break; case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */ case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */ @@ -137,6 +125,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, case TO_SPR(2, 1280) ... TO_SPR(2, 1407): /* ITLBW3MR 0-127 */ case TO_SPR(2, 1408) ... TO_SPR(2, 1535): /* ITLBW3TR 0-127 */ break; + case TO_SPR(5, 1): /* MACLO */ env->mac = deposit64(env->mac, 0, 32, rb); break; @@ -153,7 +142,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, } break; case TO_SPR(9, 0): /* PICMR */ - env->picmr |= rb; + env->picmr = rb; break; case TO_SPR(9, 2): /* PICSR */ env->picsr &= ~rb; @@ -201,13 +190,12 @@ void HELPER(mtspr)(CPUOpenRISCState *env, #endif } -target_ulong HELPER(mfspr)(CPUOpenRISCState *env, - target_ulong rd, target_ulong ra, uint32_t offset) +target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, + target_ulong spr) { #ifndef CONFIG_USER_ONLY OpenRISCCPU *cpu = openrisc_env_get_cpu(env); CPUState *cs = CPU(cpu); - int spr = (ra | offset); int idx; switch (spr) { @@ -259,13 +247,13 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, idx = (spr - 1024); return env->shadow_gpr[idx / 32][idx % 32]; - case TO_SPR(1, 512) ... TO_SPR(1, 512+DTLB_SIZE-1): /* DTLBW0MR 0-127 */ + case TO_SPR(1, 512) ... TO_SPR(1, 512 + TLB_SIZE - 1): /* DTLBW0MR 0-127 */ idx = spr - TO_SPR(1, 512); - return env->tlb->dtlb[0][idx].mr; + return env->tlb.dtlb[idx].mr; - case TO_SPR(1, 640) ... TO_SPR(1, 640+DTLB_SIZE-1): /* DTLBW0TR 0-127 */ + case TO_SPR(1, 640) ... TO_SPR(1, 640 + TLB_SIZE - 1): /* DTLBW0TR 0-127 */ idx = spr - TO_SPR(1, 640); - return env->tlb->dtlb[0][idx].tr; + return env->tlb.dtlb[idx].tr; case TO_SPR(1, 768) ... TO_SPR(1, 895): /* DTLBW1MR 0-127 */ case TO_SPR(1, 896) ... TO_SPR(1, 1023): /* DTLBW1TR 0-127 */ @@ -275,13 +263,13 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, case TO_SPR(1, 1408) ... TO_SPR(1, 1535): /* DTLBW3TR 0-127 */ break; - case TO_SPR(2, 512) ... TO_SPR(2, 512+ITLB_SIZE-1): /* ITLBW0MR 0-127 */ + case TO_SPR(2, 512) ... TO_SPR(2, 512 + TLB_SIZE - 1): /* ITLBW0MR 0-127 */ idx = spr - TO_SPR(2, 512); - return env->tlb->itlb[0][idx].mr; + return env->tlb.itlb[idx].mr; - case TO_SPR(2, 640) ... TO_SPR(2, 640+ITLB_SIZE-1): /* ITLBW0TR 0-127 */ + case TO_SPR(2, 640) ... TO_SPR(2, 640 + TLB_SIZE - 1): /* ITLBW0TR 0-127 */ idx = spr - TO_SPR(2, 640); - return env->tlb->itlb[0][idx].tr; + return env->tlb.itlb[idx].tr; case TO_SPR(2, 768) ... TO_SPR(2, 895): /* ITLBW1MR 0-127 */ case TO_SPR(2, 896) ... TO_SPR(2, 1023): /* ITLBW1TR 0-127 */ diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 2747b24cf0..a271cd3903 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -31,28 +31,37 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/gen-icount.h" #include "trace-tcg.h" #include "exec/log.h" -#define LOG_DIS(str, ...) \ - qemu_log_mask(CPU_LOG_TB_IN_ASM, "%08x: " str, dc->pc, ## __VA_ARGS__) - /* is_jmp field values */ -#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ -#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ -#define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ +#define DISAS_EXIT DISAS_TARGET_0 /* force exit to main loop */ +#define DISAS_JUMP DISAS_TARGET_1 /* exit via jmp_pc/jmp_pc_imm */ typedef struct DisasContext { - TranslationBlock *tb; - target_ulong pc; - uint32_t is_jmp; + DisasContextBase base; uint32_t mem_idx; uint32_t tb_flags; uint32_t delayed_branch; - bool singlestep_enabled; + + /* If not -1, jmp_pc contains this value and so is a direct jump. */ + target_ulong jmp_pc_imm; } DisasContext; +static inline bool is_user(DisasContext *dc) +{ +#ifdef CONFIG_USER_ONLY + return true; +#else + return !(dc->tb_flags & TB_FLAGS_SM); +#endif +} + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" + static TCGv cpu_sr; static TCGv cpu_R[32]; static TCGv cpu_R0; @@ -67,7 +76,6 @@ static TCGv cpu_lock_value; static TCGv_i32 fpcsr; static TCGv_i64 cpu_mac; /* MACHI:MACLO */ static TCGv_i32 cpu_dflag; -#include "exec/gen-icount.h" void openrisc_translate_init(void) { @@ -126,9 +134,9 @@ static void gen_exception(DisasContext *dc, unsigned int excp) static void gen_illegal_exception(DisasContext *dc) { - tcg_gen_movi_tl(cpu_pc, dc->pc); + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_ILLEGAL); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; } /* not used yet, open it when we need or64. */ @@ -164,80 +172,6 @@ static void check_ov64s(DisasContext *dc) } \ } while (0) -static inline bool use_goto_tb(DisasContext *dc, target_ulong dest) -{ - if (unlikely(dc->singlestep_enabled)) { - return false; - } - -#ifndef CONFIG_USER_ONLY - return (dc->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); -#else - return true; -#endif -} - -static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) -{ - if (use_goto_tb(dc, dest)) { - tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_goto_tb(n); - tcg_gen_exit_tb((uintptr_t)dc->tb + n); - } else { - tcg_gen_movi_tl(cpu_pc, dest); - if (dc->singlestep_enabled) { - gen_exception(dc, EXCP_DEBUG); - } - tcg_gen_exit_tb(0); - } -} - -static void gen_jump(DisasContext *dc, int32_t n26, uint32_t reg, uint32_t op0) -{ - target_ulong tmp_pc = dc->pc + n26 * 4; - - switch (op0) { - case 0x00: /* l.j */ - tcg_gen_movi_tl(jmp_pc, tmp_pc); - break; - case 0x01: /* l.jal */ - tcg_gen_movi_tl(cpu_R[9], dc->pc + 8); - /* Optimize jal being used to load the PC for PIC. */ - if (tmp_pc == dc->pc + 8) { - return; - } - tcg_gen_movi_tl(jmp_pc, tmp_pc); - break; - case 0x03: /* l.bnf */ - case 0x04: /* l.bf */ - { - TCGv t_next = tcg_const_tl(dc->pc + 8); - TCGv t_true = tcg_const_tl(tmp_pc); - TCGv t_zero = tcg_const_tl(0); - - tcg_gen_movcond_tl(op0 == 0x03 ? TCG_COND_EQ : TCG_COND_NE, - jmp_pc, cpu_sr_f, t_zero, t_true, t_next); - - tcg_temp_free(t_next); - tcg_temp_free(t_true); - tcg_temp_free(t_zero); - } - break; - case 0x11: /* l.jr */ - tcg_gen_mov_tl(jmp_pc, cpu_R[reg]); - break; - case 0x12: /* l.jalr */ - tcg_gen_movi_tl(cpu_R[9], (dc->pc + 8)); - tcg_gen_mov_tl(jmp_pc, cpu_R[reg]); - break; - default: - gen_illegal_exception(dc); - break; - } - - dc->delayed_branch = 2; -} - static void gen_ove_cy(DisasContext *dc) { if (dc->tb_flags & SR_OVE) { @@ -500,24 +434,307 @@ static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb) gen_ove_cy(dc); } -static void gen_lwa(DisasContext *dc, TCGv rd, TCGv ra, int32_t ofs) +static bool trans_l_add(DisasContext *dc, arg_dab *a, uint32_t insn) { - TCGv ea = tcg_temp_new(); + check_r0_write(a->d); + gen_add(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} - tcg_gen_addi_tl(ea, ra, ofs); - tcg_gen_qemu_ld_tl(rd, ea, dc->mem_idx, MO_TEUL); +static bool trans_l_addc(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + gen_addc(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sub(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + gen_sub(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_and(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_and_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_or(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_or_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_xor(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_xor_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sll(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_shl_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_srl(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_shr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sra(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_sar_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_ror(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_rotr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_exths(DisasContext *dc, arg_da *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_ext16s_tl(cpu_R[a->d], cpu_R[a->a]); + return true; +} + +static bool trans_l_extbs(DisasContext *dc, arg_da *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_ext8s_tl(cpu_R[a->d], cpu_R[a->a]); + return true; +} + +static bool trans_l_exthz(DisasContext *dc, arg_da *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_ext16u_tl(cpu_R[a->d], cpu_R[a->a]); + return true; +} + +static bool trans_l_extbz(DisasContext *dc, arg_da *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_ext8u_tl(cpu_R[a->d], cpu_R[a->a]); + return true; +} + +static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + TCGv zero; + + check_r0_write(a->d); + zero = tcg_const_tl(0); + tcg_gen_movcond_tl(TCG_COND_NE, cpu_R[a->d], cpu_sr_f, zero, + cpu_R[a->a], cpu_R[a->b]); + tcg_temp_free(zero); + return true; +} + +static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_ctzi_tl(cpu_R[a->d], cpu_R[a->a], -1); + tcg_gen_addi_tl(cpu_R[a->d], cpu_R[a->d], 1); + return true; +} + +static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_clzi_tl(cpu_R[a->d], cpu_R[a->a], TARGET_LONG_BITS); + tcg_gen_subfi_tl(cpu_R[a->d], TARGET_LONG_BITS, cpu_R[a->d]); + return true; +} + +static bool trans_l_mul(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + gen_mul(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_mulu(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + gen_mulu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_div(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + gen_div(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_divu(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + gen_divu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_muld(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + gen_muld(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_muldu(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + gen_muldu(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_j(DisasContext *dc, arg_l_j *a, uint32_t insn) +{ + target_ulong tmp_pc = dc->base.pc_next + a->n * 4; + + tcg_gen_movi_tl(jmp_pc, tmp_pc); + dc->jmp_pc_imm = tmp_pc; + dc->delayed_branch = 2; + return true; +} + +static bool trans_l_jal(DisasContext *dc, arg_l_jal *a, uint32_t insn) +{ + target_ulong tmp_pc = dc->base.pc_next + a->n * 4; + target_ulong ret_pc = dc->base.pc_next + 8; + + tcg_gen_movi_tl(cpu_R[9], ret_pc); + /* Optimize jal being used to load the PC for PIC. */ + if (tmp_pc != ret_pc) { + tcg_gen_movi_tl(jmp_pc, tmp_pc); + dc->jmp_pc_imm = tmp_pc; + dc->delayed_branch = 2; + } + return true; +} + +static void do_bf(DisasContext *dc, arg_l_bf *a, TCGCond cond) +{ + target_ulong tmp_pc = dc->base.pc_next + a->n * 4; + TCGv t_next = tcg_const_tl(dc->base.pc_next + 8); + TCGv t_true = tcg_const_tl(tmp_pc); + TCGv t_zero = tcg_const_tl(0); + + tcg_gen_movcond_tl(cond, jmp_pc, cpu_sr_f, t_zero, t_true, t_next); + + tcg_temp_free(t_next); + tcg_temp_free(t_true); + tcg_temp_free(t_zero); + dc->delayed_branch = 2; +} + +static bool trans_l_bf(DisasContext *dc, arg_l_bf *a, uint32_t insn) +{ + do_bf(dc, a, TCG_COND_NE); + return true; +} + +static bool trans_l_bnf(DisasContext *dc, arg_l_bf *a, uint32_t insn) +{ + do_bf(dc, a, TCG_COND_EQ); + return true; +} + +static bool trans_l_jr(DisasContext *dc, arg_l_jr *a, uint32_t insn) +{ + tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); + dc->delayed_branch = 2; + return true; +} + +static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a, uint32_t insn) +{ + tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); + tcg_gen_movi_tl(cpu_R[9], dc->base.pc_next + 8); + dc->delayed_branch = 2; + return true; +} + +static bool trans_l_lwa(DisasContext *dc, arg_load *a, uint32_t insn) +{ + TCGv ea; + + check_r0_write(a->d); + ea = tcg_temp_new(); + tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); + tcg_gen_qemu_ld_tl(cpu_R[a->d], ea, dc->mem_idx, MO_TEUL); tcg_gen_mov_tl(cpu_lock_addr, ea); - tcg_gen_mov_tl(cpu_lock_value, rd); + tcg_gen_mov_tl(cpu_lock_value, cpu_R[a->d]); + tcg_temp_free(ea); + return true; +} + +static void do_load(DisasContext *dc, arg_load *a, TCGMemOp mop) +{ + TCGv ea; + + check_r0_write(a->d); + ea = tcg_temp_new(); + tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); + tcg_gen_qemu_ld_tl(cpu_R[a->d], ea, dc->mem_idx, mop); tcg_temp_free(ea); } -static void gen_swa(DisasContext *dc, int b, TCGv ra, int32_t ofs) +static bool trans_l_lwz(DisasContext *dc, arg_load *a, uint32_t insn) +{ + do_load(dc, a, MO_TEUL); + return true; +} + +static bool trans_l_lws(DisasContext *dc, arg_load *a, uint32_t insn) +{ + do_load(dc, a, MO_TESL); + return true; +} + +static bool trans_l_lbz(DisasContext *dc, arg_load *a, uint32_t insn) +{ + do_load(dc, a, MO_UB); + return true; +} + +static bool trans_l_lbs(DisasContext *dc, arg_load *a, uint32_t insn) +{ + do_load(dc, a, MO_SB); + return true; +} + +static bool trans_l_lhz(DisasContext *dc, arg_load *a, uint32_t insn) +{ + do_load(dc, a, MO_TEUW); + return true; +} + +static bool trans_l_lhs(DisasContext *dc, arg_load *a, uint32_t insn) +{ + do_load(dc, a, MO_TESW); + return true; +} + +static bool trans_l_swa(DisasContext *dc, arg_store *a, uint32_t insn) { TCGv ea, val; TCGLabel *lab_fail, *lab_done; ea = tcg_temp_new(); - tcg_gen_addi_tl(ea, ra, ofs); + tcg_gen_addi_tl(ea, cpu_R[a->a], a->i); /* For TB_FLAGS_R0_0, the branch below invalidates the temporary assigned to cpu_R[0]. Since l.swa is quite often immediately followed by a @@ -532,7 +749,7 @@ static void gen_swa(DisasContext *dc, int b, TCGv ra, int32_t ofs) val = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(val, cpu_lock_addr, cpu_lock_value, - cpu_R[b], dc->mem_idx, MO_TEUL); + cpu_R[a->b], dc->mem_idx, MO_TEUL); tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, val, cpu_lock_value); tcg_temp_free(val); @@ -543,1023 +760,513 @@ static void gen_swa(DisasContext *dc, int b, TCGv ra, int32_t ofs) gen_set_label(lab_done); tcg_gen_movi_tl(cpu_lock_addr, -1); + return true; } -static void dec_calc(DisasContext *dc, uint32_t insn) +static void do_store(DisasContext *dc, arg_store *a, TCGMemOp mop) { - uint32_t op0, op1, op2; - uint32_t ra, rb, rd; - op0 = extract32(insn, 0, 4); - op1 = extract32(insn, 8, 2); - op2 = extract32(insn, 6, 2); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); - rd = extract32(insn, 21, 5); - - switch (op1) { - case 0: - switch (op0) { - case 0x0: /* l.add */ - LOG_DIS("l.add r%d, r%d, r%d\n", rd, ra, rb); - gen_add(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0x1: /* l.addc */ - LOG_DIS("l.addc r%d, r%d, r%d\n", rd, ra, rb); - gen_addc(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0x2: /* l.sub */ - LOG_DIS("l.sub r%d, r%d, r%d\n", rd, ra, rb); - gen_sub(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0x3: /* l.and */ - LOG_DIS("l.and r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_and_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0x4: /* l.or */ - LOG_DIS("l.or r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_or_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0x5: /* l.xor */ - LOG_DIS("l.xor r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_xor_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0x8: - switch (op2) { - case 0: /* l.sll */ - LOG_DIS("l.sll r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_shl_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - case 1: /* l.srl */ - LOG_DIS("l.srl r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_shr_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - case 2: /* l.sra */ - LOG_DIS("l.sra r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_sar_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - case 3: /* l.ror */ - LOG_DIS("l.ror r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_rotr_tl(cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - } - break; - - case 0xc: - switch (op2) { - case 0: /* l.exths */ - LOG_DIS("l.exths r%d, r%d\n", rd, ra); - tcg_gen_ext16s_tl(cpu_R[rd], cpu_R[ra]); - return; - case 1: /* l.extbs */ - LOG_DIS("l.extbs r%d, r%d\n", rd, ra); - tcg_gen_ext8s_tl(cpu_R[rd], cpu_R[ra]); - return; - case 2: /* l.exthz */ - LOG_DIS("l.exthz r%d, r%d\n", rd, ra); - tcg_gen_ext16u_tl(cpu_R[rd], cpu_R[ra]); - return; - case 3: /* l.extbz */ - LOG_DIS("l.extbz r%d, r%d\n", rd, ra); - tcg_gen_ext8u_tl(cpu_R[rd], cpu_R[ra]); - return; - } - break; - - case 0xd: - switch (op2) { - case 0: /* l.extws */ - LOG_DIS("l.extws r%d, r%d\n", rd, ra); - tcg_gen_ext32s_tl(cpu_R[rd], cpu_R[ra]); - return; - case 1: /* l.extwz */ - LOG_DIS("l.extwz r%d, r%d\n", rd, ra); - tcg_gen_ext32u_tl(cpu_R[rd], cpu_R[ra]); - return; - } - break; - - case 0xe: /* l.cmov */ - LOG_DIS("l.cmov r%d, r%d, r%d\n", rd, ra, rb); - { - TCGv zero = tcg_const_tl(0); - tcg_gen_movcond_tl(TCG_COND_NE, cpu_R[rd], cpu_sr_f, zero, - cpu_R[ra], cpu_R[rb]); - tcg_temp_free(zero); - } - return; - - case 0xf: /* l.ff1 */ - LOG_DIS("l.ff1 r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_ctzi_tl(cpu_R[rd], cpu_R[ra], -1); - tcg_gen_addi_tl(cpu_R[rd], cpu_R[rd], 1); - return; - } - break; - - case 1: - switch (op0) { - case 0xf: /* l.fl1 */ - LOG_DIS("l.fl1 r%d, r%d, r%d\n", rd, ra, rb); - tcg_gen_clzi_tl(cpu_R[rd], cpu_R[ra], TARGET_LONG_BITS); - tcg_gen_subfi_tl(cpu_R[rd], TARGET_LONG_BITS, cpu_R[rd]); - return; - } - break; - - case 2: - break; - - case 3: - switch (op0) { - case 0x6: /* l.mul */ - LOG_DIS("l.mul r%d, r%d, r%d\n", rd, ra, rb); - gen_mul(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0x7: /* l.muld */ - LOG_DIS("l.muld r%d, r%d\n", ra, rb); - gen_muld(dc, cpu_R[ra], cpu_R[rb]); - break; - - case 0x9: /* l.div */ - LOG_DIS("l.div r%d, r%d, r%d\n", rd, ra, rb); - gen_div(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0xa: /* l.divu */ - LOG_DIS("l.divu r%d, r%d, r%d\n", rd, ra, rb); - gen_divu(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0xb: /* l.mulu */ - LOG_DIS("l.mulu r%d, r%d, r%d\n", rd, ra, rb); - gen_mulu(dc, cpu_R[rd], cpu_R[ra], cpu_R[rb]); - return; - - case 0xc: /* l.muldu */ - LOG_DIS("l.muldu r%d, r%d\n", ra, rb); - gen_muldu(dc, cpu_R[ra], cpu_R[rb]); - return; - } - break; - } - gen_illegal_exception(dc); + TCGv t0 = tcg_temp_new(); + tcg_gen_addi_tl(t0, cpu_R[a->a], a->i); + tcg_gen_qemu_st_tl(cpu_R[a->b], t0, dc->mem_idx, mop); + tcg_temp_free(t0); } -static void dec_misc(DisasContext *dc, uint32_t insn) +static bool trans_l_sw(DisasContext *dc, arg_store *a, uint32_t insn) +{ + do_store(dc, a, MO_TEUL); + return true; +} + +static bool trans_l_sb(DisasContext *dc, arg_store *a, uint32_t insn) +{ + do_store(dc, a, MO_UB); + return true; +} + +static bool trans_l_sh(DisasContext *dc, arg_store *a, uint32_t insn) +{ + do_store(dc, a, MO_TEUW); + return true; +} + +static bool trans_l_nop(DisasContext *dc, arg_l_nop *a, uint32_t insn) +{ + return true; +} + +static bool trans_l_addi(DisasContext *dc, arg_rri *a, uint32_t insn) { - uint32_t op0, op1; - uint32_t ra, rb, rd; - uint32_t L6, K5, K16, K5_11; - int32_t I16, I5_11, N26; - TCGMemOp mop; TCGv t0; - op0 = extract32(insn, 26, 6); - op1 = extract32(insn, 24, 2); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); - rd = extract32(insn, 21, 5); - L6 = extract32(insn, 5, 6); - K5 = extract32(insn, 0, 5); - K16 = extract32(insn, 0, 16); - I16 = (int16_t)K16; - N26 = sextract32(insn, 0, 26); - K5_11 = (extract32(insn, 21, 5) << 11) | extract32(insn, 0, 11); - I5_11 = (int16_t)K5_11; + check_r0_write(a->d); + t0 = tcg_const_tl(a->i); + gen_add(dc, cpu_R[a->d], cpu_R[a->a], t0); + tcg_temp_free(t0); + return true; +} - switch (op0) { - case 0x00: /* l.j */ - LOG_DIS("l.j %d\n", N26); - gen_jump(dc, N26, 0, op0); - break; +static bool trans_l_addic(DisasContext *dc, arg_rri *a, uint32_t insn) +{ + TCGv t0; - case 0x01: /* l.jal */ - LOG_DIS("l.jal %d\n", N26); - gen_jump(dc, N26, 0, op0); - break; + check_r0_write(a->d); + t0 = tcg_const_tl(a->i); + gen_addc(dc, cpu_R[a->d], cpu_R[a->a], t0); + tcg_temp_free(t0); + return true; +} - case 0x03: /* l.bnf */ - LOG_DIS("l.bnf %d\n", N26); - gen_jump(dc, N26, 0, op0); - break; +static bool trans_l_muli(DisasContext *dc, arg_rri *a, uint32_t insn) +{ + TCGv t0; - case 0x04: /* l.bf */ - LOG_DIS("l.bf %d\n", N26); - gen_jump(dc, N26, 0, op0); - break; + check_r0_write(a->d); + t0 = tcg_const_tl(a->i); + gen_mul(dc, cpu_R[a->d], cpu_R[a->a], t0); + tcg_temp_free(t0); + return true; +} - case 0x05: - switch (op1) { - case 0x01: /* l.nop */ - LOG_DIS("l.nop %d\n", I16); - break; +static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn) +{ + TCGv t0; - default: - gen_illegal_exception(dc); - break; + t0 = tcg_const_tl(a->i); + gen_mac(dc, cpu_R[a->a], t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_l_andi(DisasContext *dc, arg_rrk *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_andi_tl(cpu_R[a->d], cpu_R[a->a], a->k); + return true; +} + +static bool trans_l_ori(DisasContext *dc, arg_rrk *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_ori_tl(cpu_R[a->d], cpu_R[a->a], a->k); + return true; +} + +static bool trans_l_xori(DisasContext *dc, arg_rri *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_xori_tl(cpu_R[a->d], cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn) +{ + check_r0_write(a->d); + + if (is_user(dc)) { + gen_illegal_exception(dc); + } else { + TCGv spr = tcg_temp_new(); + tcg_gen_ori_tl(spr, cpu_R[a->a], a->k); + gen_helper_mfspr(cpu_R[a->d], cpu_env, cpu_R[a->d], spr); + tcg_temp_free(spr); + } + return true; +} + +static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) +{ + if (is_user(dc)) { + gen_illegal_exception(dc); + } else { + TCGv spr; + + /* For SR, we will need to exit the TB to recognize the new + * exception state. For NPC, in theory this counts as a branch + * (although the SPR only exists for use by an ICE). Save all + * of the cpu state first, allowing it to be overwritten. + */ + if (dc->delayed_branch) { + tcg_gen_mov_tl(cpu_pc, jmp_pc); + tcg_gen_discard_tl(jmp_pc); + } else { + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next + 4); } - break; + dc->base.is_jmp = DISAS_EXIT; - case 0x11: /* l.jr */ - LOG_DIS("l.jr r%d\n", rb); - gen_jump(dc, 0, rb, op0); - break; - - case 0x12: /* l.jalr */ - LOG_DIS("l.jalr r%d\n", rb); - gen_jump(dc, 0, rb, op0); - break; - - case 0x13: /* l.maci */ - LOG_DIS("l.maci r%d, %d\n", ra, I16); - t0 = tcg_const_tl(I16); - gen_mac(dc, cpu_R[ra], t0); - tcg_temp_free(t0); - break; - - case 0x09: /* l.rfe */ - LOG_DIS("l.rfe\n"); - { -#if defined(CONFIG_USER_ONLY) - return; -#else - if (dc->mem_idx == MMU_USER_IDX) { - gen_illegal_exception(dc); - return; - } - gen_helper_rfe(cpu_env); - dc->is_jmp = DISAS_UPDATE; -#endif - } - break; - - case 0x1b: /* l.lwa */ - LOG_DIS("l.lwa r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - gen_lwa(dc, cpu_R[rd], cpu_R[ra], I16); - break; - - case 0x1c: /* l.cust1 */ - LOG_DIS("l.cust1\n"); - break; - - case 0x1d: /* l.cust2 */ - LOG_DIS("l.cust2\n"); - break; - - case 0x1e: /* l.cust3 */ - LOG_DIS("l.cust3\n"); - break; - - case 0x1f: /* l.cust4 */ - LOG_DIS("l.cust4\n"); - break; - - case 0x3c: /* l.cust5 */ - LOG_DIS("l.cust5 r%d, r%d, r%d, %d, %d\n", rd, ra, rb, L6, K5); - break; - - case 0x3d: /* l.cust6 */ - LOG_DIS("l.cust6\n"); - break; - - case 0x3e: /* l.cust7 */ - LOG_DIS("l.cust7\n"); - break; - - case 0x3f: /* l.cust8 */ - LOG_DIS("l.cust8\n"); - break; - -/* not used yet, open it when we need or64. */ -/*#ifdef TARGET_OPENRISC64 - case 0x20: l.ld - LOG_DIS("l.ld r%d, r%d, %d\n", rd, ra, I16); - check_ob64s(dc); - mop = MO_TEQ; - goto do_load; -#endif*/ - - case 0x21: /* l.lwz */ - LOG_DIS("l.lwz r%d, r%d, %d\n", rd, ra, I16); - mop = MO_TEUL; - goto do_load; - - case 0x22: /* l.lws */ - LOG_DIS("l.lws r%d, r%d, %d\n", rd, ra, I16); - mop = MO_TESL; - goto do_load; - - case 0x23: /* l.lbz */ - LOG_DIS("l.lbz r%d, r%d, %d\n", rd, ra, I16); - mop = MO_UB; - goto do_load; - - case 0x24: /* l.lbs */ - LOG_DIS("l.lbs r%d, r%d, %d\n", rd, ra, I16); - mop = MO_SB; - goto do_load; - - case 0x25: /* l.lhz */ - LOG_DIS("l.lhz r%d, r%d, %d\n", rd, ra, I16); - mop = MO_TEUW; - goto do_load; - - case 0x26: /* l.lhs */ - LOG_DIS("l.lhs r%d, r%d, %d\n", rd, ra, I16); - mop = MO_TESW; - goto do_load; - - do_load: - check_r0_write(rd); - t0 = tcg_temp_new(); - tcg_gen_addi_tl(t0, cpu_R[ra], I16); - tcg_gen_qemu_ld_tl(cpu_R[rd], t0, dc->mem_idx, mop); - tcg_temp_free(t0); - break; - - case 0x27: /* l.addi */ - LOG_DIS("l.addi r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - t0 = tcg_const_tl(I16); - gen_add(dc, cpu_R[rd], cpu_R[ra], t0); - tcg_temp_free(t0); - break; - - case 0x28: /* l.addic */ - LOG_DIS("l.addic r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - t0 = tcg_const_tl(I16); - gen_addc(dc, cpu_R[rd], cpu_R[ra], t0); - tcg_temp_free(t0); - break; - - case 0x29: /* l.andi */ - LOG_DIS("l.andi r%d, r%d, %d\n", rd, ra, K16); - check_r0_write(rd); - tcg_gen_andi_tl(cpu_R[rd], cpu_R[ra], K16); - break; - - case 0x2a: /* l.ori */ - LOG_DIS("l.ori r%d, r%d, %d\n", rd, ra, K16); - check_r0_write(rd); - tcg_gen_ori_tl(cpu_R[rd], cpu_R[ra], K16); - break; - - case 0x2b: /* l.xori */ - LOG_DIS("l.xori r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - tcg_gen_xori_tl(cpu_R[rd], cpu_R[ra], I16); - break; - - case 0x2c: /* l.muli */ - LOG_DIS("l.muli r%d, r%d, %d\n", rd, ra, I16); - check_r0_write(rd); - t0 = tcg_const_tl(I16); - gen_mul(dc, cpu_R[rd], cpu_R[ra], t0); - tcg_temp_free(t0); - break; - - case 0x2d: /* l.mfspr */ - LOG_DIS("l.mfspr r%d, r%d, %d\n", rd, ra, K16); - check_r0_write(rd); - { -#if defined(CONFIG_USER_ONLY) - return; -#else - TCGv_i32 ti = tcg_const_i32(K16); - if (dc->mem_idx == MMU_USER_IDX) { - gen_illegal_exception(dc); - return; - } - gen_helper_mfspr(cpu_R[rd], cpu_env, cpu_R[rd], cpu_R[ra], ti); - tcg_temp_free_i32(ti); -#endif - } - break; - - case 0x30: /* l.mtspr */ - LOG_DIS("l.mtspr r%d, r%d, %d\n", ra, rb, K5_11); - { -#if defined(CONFIG_USER_ONLY) - return; -#else - TCGv_i32 im = tcg_const_i32(K5_11); - if (dc->mem_idx == MMU_USER_IDX) { - gen_illegal_exception(dc); - return; - } - gen_helper_mtspr(cpu_env, cpu_R[ra], cpu_R[rb], im); - tcg_temp_free_i32(im); -#endif - } - break; - - case 0x33: /* l.swa */ - LOG_DIS("l.swa r%d, r%d, %d\n", ra, rb, I5_11); - gen_swa(dc, rb, cpu_R[ra], I5_11); - break; - -/* not used yet, open it when we need or64. */ -/*#ifdef TARGET_OPENRISC64 - case 0x34: l.sd - LOG_DIS("l.sd r%d, r%d, %d\n", ra, rb, I5_11); - check_ob64s(dc); - mop = MO_TEQ; - goto do_store; -#endif*/ - - case 0x35: /* l.sw */ - LOG_DIS("l.sw r%d, r%d, %d\n", ra, rb, I5_11); - mop = MO_TEUL; - goto do_store; - - case 0x36: /* l.sb */ - LOG_DIS("l.sb r%d, r%d, %d\n", ra, rb, I5_11); - mop = MO_UB; - goto do_store; - - case 0x37: /* l.sh */ - LOG_DIS("l.sh r%d, r%d, %d\n", ra, rb, I5_11); - mop = MO_TEUW; - goto do_store; - - do_store: - { - TCGv t0 = tcg_temp_new(); - tcg_gen_addi_tl(t0, cpu_R[ra], I5_11); - tcg_gen_qemu_st_tl(cpu_R[rb], t0, dc->mem_idx, mop); - tcg_temp_free(t0); - } - break; - - default: - gen_illegal_exception(dc); - break; + spr = tcg_temp_new(); + tcg_gen_ori_tl(spr, cpu_R[a->a], a->k); + gen_helper_mtspr(cpu_env, spr, cpu_R[a->b]); + tcg_temp_free(spr); } + return true; } -static void dec_mac(DisasContext *dc, uint32_t insn) +static bool trans_l_mac(DisasContext *dc, arg_ab *a, uint32_t insn) { - uint32_t op0; - uint32_t ra, rb; - op0 = extract32(insn, 0, 4); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); + gen_mac(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} - switch (op0) { - case 0x0001: /* l.mac */ - LOG_DIS("l.mac r%d, r%d\n", ra, rb); - gen_mac(dc, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_msb(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + gen_msb(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x0002: /* l.msb */ - LOG_DIS("l.msb r%d, r%d\n", ra, rb); - gen_msb(dc, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_macu(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + gen_macu(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x0003: /* l.macu */ - LOG_DIS("l.macu r%d, r%d\n", ra, rb); - gen_macu(dc, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_msbu(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + gen_msbu(dc, cpu_R[a->a], cpu_R[a->b]); + return true; +} - case 0x0004: /* l.msbu */ - LOG_DIS("l.msbu r%d, r%d\n", ra, rb); - gen_msbu(dc, cpu_R[ra], cpu_R[rb]); - break; +static bool trans_l_slli(DisasContext *dc, arg_dal *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_shli_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); + return true; +} - default: +static bool trans_l_srli(DisasContext *dc, arg_dal *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_shri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); + return true; +} + +static bool trans_l_srai(DisasContext *dc, arg_dal *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_sari_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); + return true; +} + +static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_rotri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); + return true; +} + +static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_movi_tl(cpu_R[a->d], a->k << 16); + return true; +} + +static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn) +{ + check_r0_write(a->d); + tcg_gen_trunc_i64_tl(cpu_R[a->d], cpu_mac); + tcg_gen_movi_i64(cpu_mac, 0); + return true; +} + +static bool trans_l_sfeq(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sfne(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sfgtu(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sfgeu(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sfltu(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sfleu(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sfgts(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sfges(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sflts(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sfles(DisasContext *dc, arg_ab *a, TCGCond cond) +{ + tcg_gen_setcond_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); + return true; +} + +static bool trans_l_sfeqi(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sfnei(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sfgtui(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sfgeui(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sfltui(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sfleui(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sfgtsi(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sfgesi(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sfltsi(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sflesi(DisasContext *dc, arg_ai *a, TCGCond cond) +{ + tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], a->i); + return true; +} + +static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn) +{ + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + gen_exception(dc, EXCP_SYSCALL); + dc->base.is_jmp = DISAS_NORETURN; + return true; +} + +static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn) +{ + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + gen_exception(dc, EXCP_TRAP); + dc->base.is_jmp = DISAS_NORETURN; + return true; +} + +static bool trans_l_msync(DisasContext *dc, arg_l_msync *a, uint32_t insn) +{ + tcg_gen_mb(TCG_MO_ALL); + return true; +} + +static bool trans_l_psync(DisasContext *dc, arg_l_psync *a, uint32_t insn) +{ + return true; +} + +static bool trans_l_csync(DisasContext *dc, arg_l_csync *a, uint32_t insn) +{ + return true; +} + +static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a, uint32_t insn) +{ + if (is_user(dc)) { gen_illegal_exception(dc); - break; - } -} - -static void dec_logic(DisasContext *dc, uint32_t insn) -{ - uint32_t op0; - uint32_t rd, ra, L6, S6; - op0 = extract32(insn, 6, 2); - rd = extract32(insn, 21, 5); - ra = extract32(insn, 16, 5); - L6 = extract32(insn, 0, 6); - S6 = L6 & (TARGET_LONG_BITS - 1); - - check_r0_write(rd); - switch (op0) { - case 0x00: /* l.slli */ - LOG_DIS("l.slli r%d, r%d, %d\n", rd, ra, L6); - tcg_gen_shli_tl(cpu_R[rd], cpu_R[ra], S6); - break; - - case 0x01: /* l.srli */ - LOG_DIS("l.srli r%d, r%d, %d\n", rd, ra, L6); - tcg_gen_shri_tl(cpu_R[rd], cpu_R[ra], S6); - break; - - case 0x02: /* l.srai */ - LOG_DIS("l.srai r%d, r%d, %d\n", rd, ra, L6); - tcg_gen_sari_tl(cpu_R[rd], cpu_R[ra], S6); - break; - - case 0x03: /* l.rori */ - LOG_DIS("l.rori r%d, r%d, %d\n", rd, ra, L6); - tcg_gen_rotri_tl(cpu_R[rd], cpu_R[ra], S6); - break; - - default: - gen_illegal_exception(dc); - break; + } else { + gen_helper_rfe(cpu_env); + dc->base.is_jmp = DISAS_EXIT; } + return true; } -static void dec_M(DisasContext *dc, uint32_t insn) +static void do_fp2(DisasContext *dc, arg_da *a, + void (*fn)(TCGv, TCGv_env, TCGv)) { - uint32_t op0; - uint32_t rd; - uint32_t K16; - op0 = extract32(insn, 16, 1); - rd = extract32(insn, 21, 5); - K16 = extract32(insn, 0, 16); + check_r0_write(a->d); + fn(cpu_R[a->d], cpu_env, cpu_R[a->a]); + gen_helper_update_fpcsr(cpu_env); +} - check_r0_write(rd); - switch (op0) { - case 0x0: /* l.movhi */ - LOG_DIS("l.movhi r%d, %d\n", rd, K16); - tcg_gen_movi_tl(cpu_R[rd], (K16 << 16)); - break; +static void do_fp3(DisasContext *dc, arg_dab *a, + void (*fn)(TCGv, TCGv_env, TCGv, TCGv)) +{ + check_r0_write(a->d); + fn(cpu_R[a->d], cpu_env, cpu_R[a->a], cpu_R[a->b]); + gen_helper_update_fpcsr(cpu_env); +} - case 0x1: /* l.macrc */ - LOG_DIS("l.macrc r%d\n", rd); - tcg_gen_trunc_i64_tl(cpu_R[rd], cpu_mac); - tcg_gen_movi_i64(cpu_mac, 0); - break; - - default: - gen_illegal_exception(dc); - break; +static void do_fpcmp(DisasContext *dc, arg_ab *a, + void (*fn)(TCGv, TCGv_env, TCGv, TCGv), + bool inv, bool swap) +{ + if (swap) { + fn(cpu_sr_f, cpu_env, cpu_R[a->b], cpu_R[a->a]); + } else { + fn(cpu_sr_f, cpu_env, cpu_R[a->a], cpu_R[a->b]); } -} - -static void dec_comp(DisasContext *dc, uint32_t insn) -{ - uint32_t op0; - uint32_t ra, rb; - - op0 = extract32(insn, 21, 5); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); - - /* unsigned integers */ - tcg_gen_ext32u_tl(cpu_R[ra], cpu_R[ra]); - tcg_gen_ext32u_tl(cpu_R[rb], cpu_R[rb]); - - switch (op0) { - case 0x0: /* l.sfeq */ - LOG_DIS("l.sfeq r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0x1: /* l.sfne */ - LOG_DIS("l.sfne r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0x2: /* l.sfgtu */ - LOG_DIS("l.sfgtu r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0x3: /* l.sfgeu */ - LOG_DIS("l.sfgeu r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0x4: /* l.sfltu */ - LOG_DIS("l.sfltu r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0x5: /* l.sfleu */ - LOG_DIS("l.sfleu r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0xa: /* l.sfgts */ - LOG_DIS("l.sfgts r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0xb: /* l.sfges */ - LOG_DIS("l.sfges r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0xc: /* l.sflts */ - LOG_DIS("l.sflts r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - case 0xd: /* l.sfles */ - LOG_DIS("l.sfles r%d, r%d\n", ra, rb); - tcg_gen_setcond_tl(TCG_COND_LE, cpu_sr_f, cpu_R[ra], cpu_R[rb]); - break; - - default: - gen_illegal_exception(dc); - break; + if (inv) { + tcg_gen_xori_tl(cpu_sr_f, cpu_sr_f, 1); } + gen_helper_update_fpcsr(cpu_env); } -static void dec_compi(DisasContext *dc, uint32_t insn) +static bool trans_lf_add_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - uint32_t op0, ra; - int32_t I16; - - op0 = extract32(insn, 21, 5); - ra = extract32(insn, 16, 5); - I16 = sextract32(insn, 0, 16); - - switch (op0) { - case 0x0: /* l.sfeqi */ - LOG_DIS("l.sfeqi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0x1: /* l.sfnei */ - LOG_DIS("l.sfnei r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0x2: /* l.sfgtui */ - LOG_DIS("l.sfgtui r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0x3: /* l.sfgeui */ - LOG_DIS("l.sfgeui r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0x4: /* l.sfltui */ - LOG_DIS("l.sfltui r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0x5: /* l.sfleui */ - LOG_DIS("l.sfleui r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0xa: /* l.sfgtsi */ - LOG_DIS("l.sfgtsi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0xb: /* l.sfgesi */ - LOG_DIS("l.sfgesi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0xc: /* l.sfltsi */ - LOG_DIS("l.sfltsi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R[ra], I16); - break; - - case 0xd: /* l.sflesi */ - LOG_DIS("l.sflesi r%d, %d\n", ra, I16); - tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R[ra], I16); - break; - - default: - gen_illegal_exception(dc); - break; - } + do_fp3(dc, a, gen_helper_float_add_s); + return true; } -static void dec_sys(DisasContext *dc, uint32_t insn) +static bool trans_lf_sub_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - uint32_t op0; - uint32_t K16; - - op0 = extract32(insn, 16, 10); - K16 = extract32(insn, 0, 16); - - switch (op0) { - case 0x000: /* l.sys */ - LOG_DIS("l.sys %d\n", K16); - tcg_gen_movi_tl(cpu_pc, dc->pc); - gen_exception(dc, EXCP_SYSCALL); - dc->is_jmp = DISAS_UPDATE; - break; - - case 0x100: /* l.trap */ - LOG_DIS("l.trap %d\n", K16); - tcg_gen_movi_tl(cpu_pc, dc->pc); - gen_exception(dc, EXCP_TRAP); - break; - - case 0x300: /* l.csync */ - LOG_DIS("l.csync\n"); - break; - - case 0x200: /* l.msync */ - LOG_DIS("l.msync\n"); - tcg_gen_mb(TCG_MO_ALL); - break; - - case 0x270: /* l.psync */ - LOG_DIS("l.psync\n"); - break; - - default: - gen_illegal_exception(dc); - break; - } + do_fp3(dc, a, gen_helper_float_sub_s); + return true; } -static void dec_float(DisasContext *dc, uint32_t insn) +static bool trans_lf_mul_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - uint32_t op0; - uint32_t ra, rb, rd; - op0 = extract32(insn, 0, 8); - ra = extract32(insn, 16, 5); - rb = extract32(insn, 11, 5); - rd = extract32(insn, 21, 5); - - switch (op0) { - case 0x00: /* lf.add.s */ - LOG_DIS("lf.add.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_add_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x01: /* lf.sub.s */ - LOG_DIS("lf.sub.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_sub_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x02: /* lf.mul.s */ - LOG_DIS("lf.mul.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_mul_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x03: /* lf.div.s */ - LOG_DIS("lf.div.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_div_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x04: /* lf.itof.s */ - LOG_DIS("lf.itof r%d, r%d\n", rd, ra); - check_r0_write(rd); - gen_helper_itofs(cpu_R[rd], cpu_env, cpu_R[ra]); - break; - - case 0x05: /* lf.ftoi.s */ - LOG_DIS("lf.ftoi r%d, r%d\n", rd, ra); - check_r0_write(rd); - gen_helper_ftois(cpu_R[rd], cpu_env, cpu_R[ra]); - break; - - case 0x06: /* lf.rem.s */ - LOG_DIS("lf.rem.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_rem_s(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x07: /* lf.madd.s */ - LOG_DIS("lf.madd.s r%d, r%d, r%d\n", rd, ra, rb); - check_r0_write(rd); - gen_helper_float_madd_s(cpu_R[rd], cpu_env, cpu_R[rd], - cpu_R[ra], cpu_R[rb]); - break; - - case 0x08: /* lf.sfeq.s */ - LOG_DIS("lf.sfeq.s r%d, r%d\n", ra, rb); - gen_helper_float_eq_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x09: /* lf.sfne.s */ - LOG_DIS("lf.sfne.s r%d, r%d\n", ra, rb); - gen_helper_float_ne_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x0a: /* lf.sfgt.s */ - LOG_DIS("lf.sfgt.s r%d, r%d\n", ra, rb); - gen_helper_float_gt_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x0b: /* lf.sfge.s */ - LOG_DIS("lf.sfge.s r%d, r%d\n", ra, rb); - gen_helper_float_ge_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x0c: /* lf.sflt.s */ - LOG_DIS("lf.sflt.s r%d, r%d\n", ra, rb); - gen_helper_float_lt_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x0d: /* lf.sfle.s */ - LOG_DIS("lf.sfle.s r%d, r%d\n", ra, rb); - gen_helper_float_le_s(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - -/* not used yet, open it when we need or64. */ -/*#ifdef TARGET_OPENRISC64 - case 0x10: lf.add.d - LOG_DIS("lf.add.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_add_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x11: lf.sub.d - LOG_DIS("lf.sub.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_sub_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x12: lf.mul.d - LOG_DIS("lf.mul.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_mul_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x13: lf.div.d - LOG_DIS("lf.div.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_div_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x14: lf.itof.d - LOG_DIS("lf.itof r%d, r%d\n", rd, ra); - check_of64s(dc); - check_r0_write(rd); - gen_helper_itofd(cpu_R[rd], cpu_env, cpu_R[ra]); - break; - - case 0x15: lf.ftoi.d - LOG_DIS("lf.ftoi r%d, r%d\n", rd, ra); - check_of64s(dc); - check_r0_write(rd); - gen_helper_ftoid(cpu_R[rd], cpu_env, cpu_R[ra]); - break; - - case 0x16: lf.rem.d - LOG_DIS("lf.rem.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_rem_d(cpu_R[rd], cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x17: lf.madd.d - LOG_DIS("lf.madd.d r%d, r%d, r%d\n", rd, ra, rb); - check_of64s(dc); - check_r0_write(rd); - gen_helper_float_madd_d(cpu_R[rd], cpu_env, cpu_R[rd], - cpu_R[ra], cpu_R[rb]); - break; - - case 0x18: lf.sfeq.d - LOG_DIS("lf.sfeq.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_eq_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x1a: lf.sfgt.d - LOG_DIS("lf.sfgt.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_gt_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x1b: lf.sfge.d - LOG_DIS("lf.sfge.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_ge_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x19: lf.sfne.d - LOG_DIS("lf.sfne.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_ne_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x1c: lf.sflt.d - LOG_DIS("lf.sflt.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_lt_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; - - case 0x1d: lf.sfle.d - LOG_DIS("lf.sfle.d r%d, r%d\n", ra, rb); - check_of64s(dc); - gen_helper_float_le_d(cpu_sr_f, cpu_env, cpu_R[ra], cpu_R[rb]); - break; -#endif*/ - - default: - gen_illegal_exception(dc); - break; - } + do_fp3(dc, a, gen_helper_float_mul_s); + return true; } -static void disas_openrisc_insn(DisasContext *dc, OpenRISCCPU *cpu) +static bool trans_lf_div_s(DisasContext *dc, arg_dab *a, uint32_t insn) { - uint32_t op0; - uint32_t insn; - insn = cpu_ldl_code(&cpu->env, dc->pc); - op0 = extract32(insn, 26, 6); - - switch (op0) { - case 0x06: - dec_M(dc, insn); - break; - - case 0x08: - dec_sys(dc, insn); - break; - - case 0x2e: - dec_logic(dc, insn); - break; - - case 0x2f: - dec_compi(dc, insn); - break; - - case 0x31: - dec_mac(dc, insn); - break; - - case 0x32: - dec_float(dc, insn); - break; - - case 0x38: - dec_calc(dc, insn); - break; - - case 0x39: - dec_comp(dc, insn); - break; - - default: - dec_misc(dc, insn); - break; - } + do_fp3(dc, a, gen_helper_float_div_s); + return true; } -void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +static bool trans_lf_rem_s(DisasContext *dc, arg_dab *a, uint32_t insn) { + do_fp3(dc, a, gen_helper_float_rem_s); + return true; +} + +static bool trans_lf_itof_s(DisasContext *dc, arg_da *a, uint32_t insn) +{ + do_fp2(dc, a, gen_helper_itofs); + return true; +} + +static bool trans_lf_ftoi_s(DisasContext *dc, arg_da *a, uint32_t insn) +{ + do_fp2(dc, a, gen_helper_ftois); + return true; +} + +static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn) +{ + check_r0_write(a->d); + gen_helper_float_madd_s(cpu_R[a->d], cpu_env, cpu_R[a->d], + cpu_R[a->a], cpu_R[a->b]); + gen_helper_update_fpcsr(cpu_env); + return true; +} + +static bool trans_lf_sfeq_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + do_fpcmp(dc, a, gen_helper_float_eq_s, false, false); + return true; +} + +static bool trans_lf_sfne_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + do_fpcmp(dc, a, gen_helper_float_eq_s, true, false); + return true; +} + +static bool trans_lf_sfgt_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + do_fpcmp(dc, a, gen_helper_float_lt_s, false, true); + return true; +} + +static bool trans_lf_sfge_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + do_fpcmp(dc, a, gen_helper_float_le_s, false, true); + return true; +} + +static bool trans_lf_sflt_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + do_fpcmp(dc, a, gen_helper_float_lt_s, false, false); + return true; +} + +static bool trans_lf_sfle_s(DisasContext *dc, arg_ab *a, uint32_t insn) +{ + do_fpcmp(dc, a, gen_helper_float_le_s, false, false); + return true; +} + +static void openrisc_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) +{ + DisasContext *dc = container_of(dcb, DisasContext, base); CPUOpenRISCState *env = cs->env_ptr; - OpenRISCCPU *cpu = openrisc_env_get_cpu(env); - struct DisasContext ctx, *dc = &ctx; - uint32_t pc_start; - uint32_t next_page_start; - int num_insns; - int max_insns; + int bound; - pc_start = tb->pc; - dc->tb = tb; - - dc->is_jmp = DISAS_NEXT; - dc->pc = pc_start; - dc->mem_idx = cpu_mmu_index(&cpu->env, false); - dc->tb_flags = tb->flags; + dc->mem_idx = cpu_mmu_index(env, false); + dc->tb_flags = dc->base.tb->flags; dc->delayed_branch = (dc->tb_flags & TB_FLAGS_DFLAG) != 0; - dc->singlestep_enabled = cs->singlestep_enabled; + dc->jmp_pc_imm = -1; - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - num_insns = 0; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + dc->base.max_insns = MIN(dc->base.max_insns, bound); +} - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } - - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("----------------\n"); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - } - - gen_tb_start(tb); +static void openrisc_tr_tb_start(DisasContextBase *db, CPUState *cs) +{ + DisasContext *dc = container_of(db, DisasContext, base); /* Allow the TCG optimizer to see that R0 == 0, when it's true, which is the common case. */ @@ -1568,92 +1275,144 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } else { cpu_R[0] = cpu_R0; } +} - do { - tcg_gen_insn_start(dc->pc, (dc->delayed_branch ? 1 : 0) - | (num_insns ? 2 : 0)); - num_insns++; +static void openrisc_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); - if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { - tcg_gen_movi_tl(cpu_pc, dc->pc); - gen_exception(dc, EXCP_DEBUG); - dc->is_jmp = DISAS_UPDATE; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - dc->pc += 4; - break; - } + tcg_gen_insn_start(dc->base.pc_next, (dc->delayed_branch ? 1 : 0) + | (dc->base.num_insns > 1 ? 2 : 0)); +} - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } - disas_openrisc_insn(dc, cpu); - dc->pc = dc->pc + 4; +static bool openrisc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); - /* delay slot */ - if (dc->delayed_branch) { - dc->delayed_branch--; - if (!dc->delayed_branch) { - tcg_gen_mov_tl(cpu_pc, jmp_pc); - tcg_gen_discard_tl(jmp_pc); - dc->is_jmp = DISAS_UPDATE; - break; - } - } - } while (!dc->is_jmp - && !tcg_op_buf_full() - && !cs->singlestep_enabled - && !singlestep - && (dc->pc < next_page_start) - && num_insns < max_insns); + tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); + gen_exception(dc, EXCP_DEBUG); + dc->base.is_jmp = DISAS_NORETURN; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->base.pc_next += 4; + return true; +} - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); +static void openrisc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + OpenRISCCPU *cpu = OPENRISC_CPU(cs); + uint32_t insn = cpu_ldl_code(&cpu->env, dc->base.pc_next); + + if (!decode(dc, insn)) { + gen_illegal_exception(dc); + } + dc->base.pc_next += 4; + + /* When exiting the delay slot normally, exit via jmp_pc. + * For DISAS_NORETURN, we have raised an exception and already exited. + * For DISAS_EXIT, we found l.rfe in a delay slot. There's nothing + * in the manual saying this is illegal, but it surely it should. + * At least or1ksim overrides pcnext and ignores the branch. + */ + if (dc->delayed_branch + && --dc->delayed_branch == 0 + && dc->base.is_jmp == DISAS_NEXT) { + dc->base.is_jmp = DISAS_JUMP; + } +} + +static void openrisc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + target_ulong jmp_dest; + + /* If we have already exited the TB, nothing following has effect. */ + if (dc->base.is_jmp == DISAS_NORETURN) { + return; } + /* Adjust the delayed branch state for the next TB. */ if ((dc->tb_flags & TB_FLAGS_DFLAG ? 1 : 0) != (dc->delayed_branch != 0)) { tcg_gen_movi_i32(cpu_dflag, dc->delayed_branch != 0); } - tcg_gen_movi_tl(cpu_ppc, dc->pc - 4); - if (dc->is_jmp == DISAS_NEXT) { - dc->is_jmp = DISAS_UPDATE; - tcg_gen_movi_tl(cpu_pc, dc->pc); - } - if (unlikely(cs->singlestep_enabled)) { - gen_exception(dc, EXCP_DEBUG); - } else { - switch (dc->is_jmp) { - case DISAS_NEXT: - gen_goto_tb(dc, 0, dc->pc); - break; - default: - case DISAS_JUMP: - break; - case DISAS_UPDATE: - /* indicate that the hash table must be used - to find the next TB */ - tcg_gen_exit_tb(0); - break; - case DISAS_TB_JUMP: - /* nothing more to generate */ + /* For DISAS_TOO_MANY, jump to the next insn. */ + jmp_dest = dc->base.pc_next; + tcg_gen_movi_tl(cpu_ppc, jmp_dest - 4); + + switch (dc->base.is_jmp) { + case DISAS_JUMP: + jmp_dest = dc->jmp_pc_imm; + if (jmp_dest == -1) { + /* The jump destination is indirect/computed; use jmp_pc. */ + tcg_gen_mov_tl(cpu_pc, jmp_pc); + tcg_gen_discard_tl(jmp_pc); + if (unlikely(dc->base.singlestep_enabled)) { + gen_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_lookup_and_goto_ptr(); + } break; } + /* The jump destination is direct; use jmp_pc_imm. + However, we will have stored into jmp_pc as well; + we know now that it wasn't needed. */ + tcg_gen_discard_tl(jmp_pc); + /* fallthru */ + + case DISAS_TOO_MANY: + if (unlikely(dc->base.singlestep_enabled)) { + tcg_gen_movi_tl(cpu_pc, jmp_dest); + gen_exception(dc, EXCP_DEBUG); + } else if ((dc->base.pc_first ^ jmp_dest) & TARGET_PAGE_MASK) { + tcg_gen_movi_tl(cpu_pc, jmp_dest); + tcg_gen_lookup_and_goto_ptr(); + } else { + tcg_gen_goto_tb(0); + tcg_gen_movi_tl(cpu_pc, jmp_dest); + tcg_gen_exit_tb(dc->base.tb, 0); + } + break; + + case DISAS_EXIT: + if (unlikely(dc->base.singlestep_enabled)) { + gen_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_exit_tb(NULL, 0); + } + break; + default: + g_assert_not_reached(); } +} - gen_tb_end(tb, num_insns); +static void openrisc_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *s = container_of(dcbase, DisasContext, base); - tb->size = dc->pc - pc_start; - tb->icount = num_insns; + qemu_log("IN: %s\n", lookup_symbol(s->base.pc_first)); + log_target_disas(cs, s->base.pc_first, s->base.tb->size); +} - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - log_target_disas(cs, pc_start, tb->size); - qemu_log("\n"); - qemu_log_unlock(); - } +static const TranslatorOps openrisc_tr_ops = { + .init_disas_context = openrisc_tr_init_disas_context, + .tb_start = openrisc_tr_tb_start, + .insn_start = openrisc_tr_insn_start, + .breakpoint_check = openrisc_tr_breakpoint_check, + .translate_insn = openrisc_tr_translate_insn, + .tb_stop = openrisc_tr_tb_stop, + .disas_log = openrisc_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +{ + DisasContext ctx; + + translator_loop(&openrisc_tr_ops, &ctx.base, cs, tb); } void openrisc_cpu_dump_state(CPUState *cs, FILE *f, diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index 351a65b22f..cc1460e4e3 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -210,11 +210,11 @@ static const struct NoteFuncDescStruct { int contents_size; void (*note_contents_func)(NoteFuncArg *arg, PowerPCCPU *cpu); } note_func[] = { - {sizeof(((Note *)0)->contents.prstatus), ppc_write_elf_prstatus}, - {sizeof(((Note *)0)->contents.fpregset), ppc_write_elf_fpregset}, - {sizeof(((Note *)0)->contents.vmxregset), ppc_write_elf_vmxregset}, - {sizeof(((Note *)0)->contents.vsxregset), ppc_write_elf_vsxregset}, - {sizeof(((Note *)0)->contents.speregset), ppc_write_elf_speregset}, + {sizeof_field(Note, contents.prstatus), ppc_write_elf_prstatus}, + {sizeof_field(Note, contents.fpregset), ppc_write_elf_fpregset}, + {sizeof_field(Note, contents.vmxregset), ppc_write_elf_vmxregset}, + {sizeof_field(Note, contents.vsxregset), ppc_write_elf_vsxregset}, + {sizeof_field(Note, contents.speregset), ppc_write_elf_speregset}, { 0, NULL} }; diff --git a/target/ppc/compat.c b/target/ppc/compat.c index 807c906f68..7de4bf3122 100644 --- a/target/ppc/compat.c +++ b/target/ppc/compat.c @@ -105,17 +105,13 @@ static const CompatInfo *compat_by_pvr(uint32_t pvr) return NULL; } -bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, - uint32_t min_compat_pvr, uint32_t max_compat_pvr) +static bool pcc_compat(PowerPCCPUClass *pcc, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr) { - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); const CompatInfo *compat = compat_by_pvr(compat_pvr); const CompatInfo *min = compat_by_pvr(min_compat_pvr); const CompatInfo *max = compat_by_pvr(max_compat_pvr); -#if !defined(CONFIG_USER_ONLY) - g_assert(cpu->vhyp); -#endif g_assert(!min_compat_pvr || min); g_assert(!max_compat_pvr || max); @@ -134,6 +130,25 @@ bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, return true; } +bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + +#if !defined(CONFIG_USER_ONLY) + g_assert(cpu->vhyp); +#endif + + return pcc_compat(pcc, compat_pvr, min_compat_pvr, max_compat_pvr); +} + +bool ppc_type_check_compat(const char *cputype, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(object_class_by_name(cputype)); + return pcc_compat(pcc, compat_pvr, min_compat_pvr, max_compat_pvr); +} + void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp) { const CompatInfo *compat = compat_by_pvr(compat_pvr); diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index deaa46a14b..433a71e484 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -68,34 +68,17 @@ enum powerpc_mmu_t { /* PowerPC 601 MMU model (specific BATs format) */ POWERPC_MMU_601 = 0x0000000A, #define POWERPC_MMU_64 0x00010000 -#define POWERPC_MMU_1TSEG 0x00020000 -#define POWERPC_MMU_AMR 0x00040000 -#define POWERPC_MMU_64K 0x00080000 -#define POWERPC_MMU_V3 0x00100000 /* ISA V3.00 MMU Support */ /* 64 bits PowerPC MMU */ POWERPC_MMU_64B = POWERPC_MMU_64 | 0x00000001, /* Architecture 2.03 and later (has LPCR) */ POWERPC_MMU_2_03 = POWERPC_MMU_64 | 0x00000002, /* Architecture 2.06 variant */ - POWERPC_MMU_2_06 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG - | POWERPC_MMU_64K - | POWERPC_MMU_AMR | 0x00000003, + POWERPC_MMU_2_06 = POWERPC_MMU_64 | 0x00000003, /* Architecture 2.07 variant */ - POWERPC_MMU_2_07 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG - | POWERPC_MMU_64K - | POWERPC_MMU_AMR | 0x00000004, + POWERPC_MMU_2_07 = POWERPC_MMU_64 | 0x00000004, /* Architecture 3.00 variant */ - POWERPC_MMU_3_00 = POWERPC_MMU_64 | POWERPC_MMU_1TSEG - | POWERPC_MMU_64K - | POWERPC_MMU_AMR | POWERPC_MMU_V3 - | 0x00000005, + POWERPC_MMU_3_00 = POWERPC_MMU_64 | 0x00000005, }; -#define POWERPC_MMU_VER(x) ((x) & (POWERPC_MMU_64 | 0xFFFF)) -#define POWERPC_MMU_VER_64B POWERPC_MMU_VER(POWERPC_MMU_64B) -#define POWERPC_MMU_VER_2_03 POWERPC_MMU_VER(POWERPC_MMU_2_03) -#define POWERPC_MMU_VER_2_06 POWERPC_MMU_VER(POWERPC_MMU_2_06) -#define POWERPC_MMU_VER_2_07 POWERPC_MMU_VER(POWERPC_MMU_2_07) -#define POWERPC_MMU_VER_3_00 POWERPC_MMU_VER(POWERPC_MMU_3_00) /*****************************************************************************/ /* Exception model */ @@ -164,7 +147,7 @@ enum powerpc_input_t { PPC_FLAGS_INPUT_RCPU, }; -struct ppc_segment_page_sizes; +typedef struct PPCHash64Options PPCHash64Options; /** * PowerPCCPUClass: @@ -198,7 +181,7 @@ typedef struct PowerPCCPUClass { uint32_t flags; int bfd_mach; uint32_t l1_dcache_size, l1_icache_size; - const struct ppc_segment_page_sizes *sps; + const PPCHash64Options *hash64_opts; struct ppc_radix_page_info *radix_page_info; void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index c621a6bd5e..4edcf62cf7 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -196,7 +196,6 @@ enum { /* QEMU exceptions: special cases we want to stop translation */ POWERPC_EXCP_SYNC = 0x202, /* context synchronizing instruction */ POWERPC_EXCP_SYSCALL_USER = 0x203, /* System call in user mode only */ - POWERPC_EXCP_STCX = 0x204 /* Conditional stores in user mode */ }; /* Exceptions error codes */ @@ -327,11 +326,13 @@ union ppc_tlb_t { #define TLB_MAS 3 #endif +typedef struct PPCHash64SegmentPageSizes PPCHash64SegmentPageSizes; + typedef struct ppc_slb_t ppc_slb_t; struct ppc_slb_t { uint64_t esid; uint64_t vsid; - const struct ppc_one_seg_page_size *sps; + const PPCHash64SegmentPageSizes *sps; }; #define MAX_SLB_ENTRIES 64 @@ -948,28 +949,8 @@ enum { #define DBELL_PROCIDTAG_MASK PPC_BITMASK(44, 63) -/*****************************************************************************/ -/* Segment page size information, used by recent hash MMUs - * The format of this structure mirrors kvm_ppc_smmu_info - */ - #define PPC_PAGE_SIZES_MAX_SZ 8 -struct ppc_one_page_size { - uint32_t page_shift; /* Page shift (or 0) */ - uint32_t pte_enc; /* Encoding in the HPTE (>>12) */ -}; - -struct ppc_one_seg_page_size { - uint32_t page_shift; /* Base page shift of segment (or 0) */ - uint32_t slb_enc; /* SLB encoding for BookS */ - struct ppc_one_page_size enc[PPC_PAGE_SIZES_MAX_SZ]; -}; - -struct ppc_segment_page_sizes { - struct ppc_one_seg_page_size sps[PPC_PAGE_SIZES_MAX_SZ]; -}; - struct ppc_radix_page_info { uint32_t count; uint32_t entries[PPC_PAGE_SIZES_MAX_SZ]; @@ -1012,10 +993,6 @@ struct CPUPPCState { /* Reservation value */ target_ulong reserve_val; target_ulong reserve_val2; - /* Reservation store address */ - target_ulong reserve_ea; - /* Reserved store source register and size */ - target_ulong reserve_info; /* Those ones are used in supervisor mode only */ /* machine state register */ @@ -1033,6 +1010,9 @@ struct CPUPPCState { /* Next instruction pointer */ target_ulong nip; + /* High part of 128-bit helper return. */ + uint64_t retxh; + int access_type; /* when a memory exception occurs, the access type is stored here */ @@ -1043,7 +1023,6 @@ struct CPUPPCState { #if defined(TARGET_PPC64) /* PowerPC 64 SLB area */ ppc_slb_t slb[MAX_SLB_ENTRIES]; - int32_t slb_nr; /* tcg TLB needs flush (deferred slb inval instruction typically) */ #endif /* segment registers */ @@ -1106,18 +1085,10 @@ struct CPUPPCState { uint64_t insns_flags; uint64_t insns_flags2; #if defined(TARGET_PPC64) - struct ppc_segment_page_sizes sps; ppc_slb_t vrma_slb; target_ulong rmls; - bool ci_large_pages; #endif -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) - uint64_t vpa_addr; - uint64_t slb_shadow_addr, slb_shadow_size; - uint64_t dtl_addr, dtl_size; -#endif /* TARGET_PPC64 */ - int error_code; uint32_t pending_interrupts; #if !defined(CONFIG_USER_ONLY) @@ -1226,7 +1197,9 @@ struct PowerPCCPU { uint32_t compat_pvr; PPCVirtualHypervisor *vhyp; Object *intc; + void *machine_data; int32_t node_id; /* NUMA node this CPU belongs to */ + PPCHash64Options *hash64_opts; /* Fields related to migration compatibility hacks */ bool pre_2_8_migration; @@ -1235,6 +1208,8 @@ struct PowerPCCPU { uint64_t mig_insns_flags2; uint32_t mig_nb_BATs; bool pre_2_10_migration; + bool pre_3_0_migration; + int32_t mig_slb_nr; }; static inline PowerPCCPU *ppc_env_get_cpu(CPUPPCState *env) @@ -1313,12 +1288,11 @@ int ppc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, #if !defined(CONFIG_USER_ONLY) void ppc_store_sdr1 (CPUPPCState *env, target_ulong value); +void ppc_store_ptcr(CPUPPCState *env, target_ulong value); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf); -#if defined(TARGET_PPC64) -#endif /* Time-base and decrementer management */ #ifndef NO_CPU_IO_DEFS @@ -1349,7 +1323,7 @@ void store_booke_tcr (CPUPPCState *env, target_ulong val); void store_booke_tsr (CPUPPCState *env, target_ulong val); void ppc_tlb_invalidate_all (CPUPPCState *env); void ppc_tlb_invalidate_one (CPUPPCState *env, target_ulong addr); -void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); +void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp); #endif #endif @@ -1393,7 +1367,11 @@ static inline int cpu_mmu_index (CPUPPCState *env, bool ifetch) #if defined(TARGET_PPC64) bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr, uint32_t min_compat_pvr, uint32_t max_compat_pvr); +bool ppc_type_check_compat(const char *cputype, uint32_t compat_pvr, + uint32_t min_compat_pvr, uint32_t max_compat_pvr); + void ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp); + #if !defined(CONFIG_USER_ONLY) void ppc_set_compat_all(uint32_t compat_pvr, Error **errp); #endif @@ -1603,6 +1581,7 @@ void ppc_compat_add_property(Object *obj, const char *name, #define SPR_BOOKE_GIVOR13 (0x1BC) #define SPR_BOOKE_GIVOR14 (0x1BD) #define SPR_TIR (0x1BE) +#define SPR_PTCR (0x1D0) #define SPR_BOOKE_SPEFSCR (0x200) #define SPR_Exxx_BBEAR (0x201) #define SPR_Exxx_BBTAR (0x202) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index c092fbead0..d6e97a90e0 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -22,7 +22,7 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" - +#include "internal.h" #include "helper_regs.h" //#define DEBUG_OP @@ -1198,3 +1198,19 @@ void helper_book3s_msgsnd(target_ulong rb) qemu_mutex_unlock_iothread(); } #endif + +void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + CPUPPCState *env = cs->env_ptr; + uint32_t insn; + + /* Restore state and reload the insn we executed, for filling in DSISR. */ + cpu_restore_state(cs, retaddr, true); + insn = cpu_ldl_code(env, env->nip); + + cs->exception_index = POWERPC_EXCP_ALIGN; + env->error_code = insn & 0x03FF0000; + cpu_loop_exit(cs); +} diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 9ae418a577..8675d931b6 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -274,6 +274,7 @@ static inline void float_inexact_excp(CPUPPCState *env) { CPUState *cs = CPU(ppc_env_get_cpu(env)); + env->fpscr |= 1 << FPSCR_FI; env->fpscr |= 1 << FPSCR_XX; /* Update the floating-point exception summary */ env->fpscr |= FP_FX; @@ -325,6 +326,34 @@ void helper_fpscr_clrbit(CPUPPCState *env, uint32_t bit) case FPSCR_RN: fpscr_set_rounding_mode(env); break; + case FPSCR_VXSNAN: + case FPSCR_VXISI: + case FPSCR_VXIDI: + case FPSCR_VXZDZ: + case FPSCR_VXIMZ: + case FPSCR_VXVC: + case FPSCR_VXSOFT: + case FPSCR_VXSQRT: + case FPSCR_VXCVI: + if (!fpscr_ix) { + /* Set VX bit to zero */ + env->fpscr &= ~(1 << FPSCR_VX); + } + break; + case FPSCR_OX: + case FPSCR_UX: + case FPSCR_ZX: + case FPSCR_XX: + case FPSCR_VE: + case FPSCR_OE: + case FPSCR_UE: + case FPSCR_ZE: + case FPSCR_XE: + if (!fpscr_eex) { + /* Set the FEX bit */ + env->fpscr &= ~(1 << FPSCR_FEX); + } + break; default: break; } @@ -505,6 +534,7 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) { CPUState *cs = CPU(ppc_env_get_cpu(env)); int status = get_float_exception_flags(&env->fp_status); + bool inexact_happened = false; if (status & float_flag_divbyzero) { float_zero_divide_excp(env, raddr); @@ -514,6 +544,12 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) float_underflow_excp(env); } else if (status & float_flag_inexact) { float_inexact_excp(env); + inexact_happened = true; + } + + /* if the inexact flag was not set */ + if (inexact_happened == false) { + env->fpscr &= ~(1 << FPSCR_FI); /* clear the FPSCR[FI] bit */ } if (cs->exception_index == POWERPC_EXCP_PROGRAM && @@ -3382,7 +3418,6 @@ void helper_xssqrtqp(CPUPPCState *env, uint32_t opcode) xt.f128 = xb.f128; } else if (float128_is_neg(xb.f128) && !float128_is_zero(xb.f128)) { float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); - set_snan_bit_is_one(0, &env->fp_status); xt.f128 = float128_default_nan(&env->fp_status); } } diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index 7a338136a8..b6f6693583 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -37,10 +37,10 @@ static int ppc_gdb_register_len_apple(int n) case 65+32: /* msr */ case 67+32: /* lr */ case 68+32: /* ctr */ - case 69+32: /* xer */ case 70+32: /* fpscr */ return 8; case 66+32: /* cr */ + case 69+32: /* xer */ return 4; default: return 0; @@ -61,6 +61,8 @@ static int ppc_gdb_register_len(int n) return 8; case 66: /* cr */ + case 69: + /* xer */ return 4; case 64: /* nip */ @@ -70,8 +72,6 @@ static int ppc_gdb_register_len(int n) /* lr */ case 68: /* ctr */ - case 69: - /* xer */ return sizeof(target_ulong); case 70: /* fpscr */ @@ -152,7 +152,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) gdb_get_regl(mem_buf, env->ctr); break; case 69: - gdb_get_regl(mem_buf, env->xer); + gdb_get_reg32(mem_buf, env->xer); break; case 70: gdb_get_reg32(mem_buf, env->fpscr); @@ -208,7 +208,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) gdb_get_reg64(mem_buf, env->ctr); break; case 69 + 32: - gdb_get_reg64(mem_buf, env->xer); + gdb_get_reg32(mem_buf, env->xer); break; case 70 + 32: gdb_get_reg64(mem_buf, env->fpscr); @@ -259,7 +259,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldtul_p(mem_buf); break; case 69: - env->xer = ldtul_p(mem_buf); + env->xer = ldl_p(mem_buf); break; case 70: /* fpscr */ @@ -309,7 +309,7 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) env->ctr = ldq_p(mem_buf); break; case 69 + 32: - env->xer = ldq_p(mem_buf); + env->xer = ldl_p(mem_buf); break; case 70 + 32: /* fpscr */ diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 5b739179b8..5706c2497f 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -17,6 +17,7 @@ DEF_HELPER_2(pminsn, void, env, i32) DEF_HELPER_1(rfid, void, env) DEF_HELPER_1(hrfid, void, env) DEF_HELPER_2(store_lpcr, void, env, tl) +DEF_HELPER_2(store_pcr, void, env, tl) #endif DEF_HELPER_1(check_tlb_flush_local, void, env) DEF_HELPER_1(check_tlb_flush_global, void, env) @@ -709,6 +710,7 @@ DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env) #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env) +DEF_HELPER_2(store_ptcr, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) @@ -797,3 +799,14 @@ DEF_HELPER_4(dscliq, void, env, fprp, fprp, i32) DEF_HELPER_1(tbegin, void, env) DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) + +#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128) +DEF_HELPER_FLAGS_3(lq_le_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) +DEF_HELPER_FLAGS_3(lq_be_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) +DEF_HELPER_FLAGS_5(stq_le_parallel, TCG_CALL_NO_WG, + void, env, tl, i64, i64, i32) +DEF_HELPER_FLAGS_5(stq_be_parallel, TCG_CALL_NO_WG, + void, env, tl, i64, i64, i32) +DEF_HELPER_5(stqcx_le_parallel, i32, env, tl, i64, i64, i32) +DEF_HELPER_5(stqcx_be_parallel, i32, env, tl, i64, i64, i32) +#endif diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h index 84fd30c2db..5efd18049e 100644 --- a/target/ppc/helper_regs.h +++ b/target/ppc/helper_regs.h @@ -21,6 +21,7 @@ #define HELPER_REGS_H #include "qemu/main-loop.h" +#include "exec/exec-all.h" /* Swap temporary saved registers with GPRs */ static inline void hreg_swap_gpr_tgpr(CPUPPCState *env) diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index 35bdf09773..d52338ed71 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "crypto/aes.h" @@ -379,7 +378,7 @@ target_ulong helper_divso(CPUPPCState *env, target_ulong arg1, target_ulong helper_602_mfrom(target_ulong arg) { if (likely(arg < 602)) { -#include "mfrom_table.c" +#include "mfrom_table.inc.c" return mfrom_ROM_table[arg]; } else { return 0; @@ -1952,7 +1951,7 @@ VSPLT(w, u32) #define VINSERT(suffix, element) \ void helper_vinsert##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ { \ - memmove(&r->u8[index], &b->u8[8 - sizeof(r->element)], \ + memmove(&r->u8[index], &b->u8[8 - sizeof(r->element[0])], \ sizeof(r->element[0])); \ } #else diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 1f441c6483..a9bcadff42 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -252,4 +252,9 @@ static inline void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) void helper_compute_fprf_float16(CPUPPCState *env, float16 arg); void helper_compute_fprf_float32(CPUPPCState *env, float32 arg); void helper_compute_fprf_float128(CPUPPCState *env, float128 arg); + +/* Raise a data fault alignment exception for the specified virtual address */ +void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr); #endif /* PPC_INTERNAL_H */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 79a436a384..9211ee2ee1 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -72,7 +72,6 @@ static int cap_segstate; static int cap_booke_sregs; static int cap_ppc_smt; static int cap_ppc_smt_possible; -static int cap_ppc_rma; static int cap_spapr_tce; static int cap_spapr_tce_64; static int cap_spapr_multitce; @@ -133,7 +132,6 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_segstate = kvm_check_extension(s, KVM_CAP_PPC_SEGSTATE); cap_booke_sregs = kvm_check_extension(s, KVM_CAP_PPC_BOOKE_SREGS); cap_ppc_smt_possible = kvm_vm_check_extension(s, KVM_CAP_PPC_SMT_POSSIBLE); - cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA); cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE); cap_spapr_tce_64 = kvm_check_extension(s, KVM_CAP_SPAPR_TCE_64); cap_spapr_multitce = kvm_check_extension(s, KVM_CAP_SPAPR_MULTITCE); @@ -250,107 +248,25 @@ static int kvm_booke206_tlb_init(PowerPCCPU *cpu) #if defined(TARGET_PPC64) -static void kvm_get_fallback_smmu_info(PowerPCCPU *cpu, - struct kvm_ppc_smmu_info *info) +static void kvm_get_smmu_info(struct kvm_ppc_smmu_info *info, Error **errp) { - CPUPPCState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - memset(info, 0, sizeof(*info)); - - /* We don't have the new KVM_PPC_GET_SMMU_INFO ioctl, so - * need to "guess" what the supported page sizes are. - * - * For that to work we make a few assumptions: - * - * - Check whether we are running "PR" KVM which only supports 4K - * and 16M pages, but supports them regardless of the backing - * store characteritics. We also don't support 1T segments. - * - * This is safe as if HV KVM ever supports that capability or PR - * KVM grows supports for more page/segment sizes, those versions - * will have implemented KVM_CAP_PPC_GET_SMMU_INFO and thus we - * will not hit this fallback - * - * - Else we are running HV KVM. This means we only support page - * sizes that fit in the backing store. Additionally we only - * advertize 64K pages if the processor is ARCH 2.06 and we assume - * P7 encodings for the SLB and hash table. Here too, we assume - * support for any newer processor will mean a kernel that - * implements KVM_CAP_PPC_GET_SMMU_INFO and thus doesn't hit - * this fallback. - */ - if (kvmppc_is_pr(cs->kvm_state)) { - /* No flags */ - info->flags = 0; - info->slb_size = 64; - - /* Standard 4k base page size segment */ - info->sps[0].page_shift = 12; - info->sps[0].slb_enc = 0; - info->sps[0].enc[0].page_shift = 12; - info->sps[0].enc[0].pte_enc = 0; - - /* Standard 16M large page size segment */ - info->sps[1].page_shift = 24; - info->sps[1].slb_enc = SLB_VSID_L; - info->sps[1].enc[0].page_shift = 24; - info->sps[1].enc[0].pte_enc = 0; - } else { - int i = 0; - - /* HV KVM has backing store size restrictions */ - info->flags = KVM_PPC_PAGE_SIZES_REAL; - - if (env->mmu_model & POWERPC_MMU_1TSEG) { - info->flags |= KVM_PPC_1T_SEGMENTS; - } - - if (POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_06 || - POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_07) { - info->slb_size = 32; - } else { - info->slb_size = 64; - } - - /* Standard 4k base page size segment */ - info->sps[i].page_shift = 12; - info->sps[i].slb_enc = 0; - info->sps[i].enc[0].page_shift = 12; - info->sps[i].enc[0].pte_enc = 0; - i++; - - /* 64K on MMU 2.06 and later */ - if (POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_06 || - POWERPC_MMU_VER(env->mmu_model) == POWERPC_MMU_VER_2_07) { - info->sps[i].page_shift = 16; - info->sps[i].slb_enc = 0x110; - info->sps[i].enc[0].page_shift = 16; - info->sps[i].enc[0].pte_enc = 1; - i++; - } - - /* Standard 16M large page size segment */ - info->sps[i].page_shift = 24; - info->sps[i].slb_enc = SLB_VSID_L; - info->sps[i].enc[0].page_shift = 24; - info->sps[i].enc[0].pte_enc = 0; - } -} - -static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info) -{ - CPUState *cs = CPU(cpu); int ret; - if (kvm_check_extension(cs->kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { - ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_GET_SMMU_INFO, info); - if (ret == 0) { - return; - } + assert(kvm_state != NULL); + + if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_GET_SMMU_INFO)) { + error_setg(errp, "KVM doesn't expose the MMU features it supports"); + error_append_hint(errp, "Consider switching to a newer KVM\n"); + return; } - kvm_get_fallback_smmu_info(cpu, info); + ret = kvm_vm_ioctl(kvm_state, KVM_PPC_GET_SMMU_INFO, info); + if (ret == 0) { + return; + } + + error_setg_errno(errp, -ret, + "KVM failed to provide the MMU features it supports"); } struct ppc_radix_page_info *kvm_get_radix_page_info(void) @@ -408,118 +324,110 @@ target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, } } -static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) -{ - if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { - return true; - } - - return (1ul << shift) <= rampgsize; -} - -static long max_cpu_page_size; - -static void kvm_fixup_page_sizes(PowerPCCPU *cpu) +bool kvmppc_hpt_needs_host_contiguous_pages(void) { static struct kvm_ppc_smmu_info smmu_info; - static bool has_smmu_info; - CPUPPCState *env = &cpu->env; - int iq, ik, jq, jk; - bool has_64k_pages = false; - /* We only handle page sizes for 64-bit server guests for now */ - if (!(env->mmu_model & POWERPC_MMU_64)) { + if (!kvm_enabled()) { + return false; + } + + kvm_get_smmu_info(&smmu_info, &error_fatal); + return !!(smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL); +} + +void kvm_check_mmu(PowerPCCPU *cpu, Error **errp) +{ + struct kvm_ppc_smmu_info smmu_info; + int iq, ik, jq, jk; + Error *local_err = NULL; + + /* For now, we only have anything to check on hash64 MMUs */ + if (!cpu->hash64_opts || !kvm_enabled()) { return; } - /* Collect MMU info from kernel if not already */ - if (!has_smmu_info) { - kvm_get_smmu_info(cpu, &smmu_info); - has_smmu_info = true; + kvm_get_smmu_info(&smmu_info, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } - if (!max_cpu_page_size) { - max_cpu_page_size = qemu_getrampagesize(); + if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG) + && !(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) { + error_setg(errp, + "KVM does not support 1TiB segments which guest expects"); + return; } - /* Convert to QEMU form */ - memset(&env->sps, 0, sizeof(env->sps)); - - /* If we have HV KVM, we need to forbid CI large pages if our - * host page size is smaller than 64K. - */ - if (smmu_info.flags & KVM_PPC_PAGE_SIZES_REAL) { - env->ci_large_pages = getpagesize() >= 0x10000; + if (smmu_info.slb_size < cpu->hash64_opts->slb_size) { + error_setg(errp, "KVM only supports %u SLB entries, but guest needs %u", + smmu_info.slb_size, cpu->hash64_opts->slb_size); + return; } /* - * XXX This loop should be an entry wide AND of the capabilities that - * the selected CPU has with the capabilities that KVM supports. + * Verify that every pagesize supported by the cpu model is + * supported by KVM with the same encodings */ - for (ik = iq = 0; ik < KVM_PPC_PAGE_SIZES_MAX_SZ; ik++) { - struct ppc_one_seg_page_size *qsps = &env->sps.sps[iq]; - struct kvm_ppc_one_seg_page_size *ksps = &smmu_info.sps[ik]; + for (iq = 0; iq < ARRAY_SIZE(cpu->hash64_opts->sps); iq++) { + PPCHash64SegmentPageSizes *qsps = &cpu->hash64_opts->sps[iq]; + struct kvm_ppc_one_seg_page_size *ksps; - if (!kvm_valid_page_size(smmu_info.flags, max_cpu_page_size, - ksps->page_shift)) { - continue; - } - qsps->page_shift = ksps->page_shift; - qsps->slb_enc = ksps->slb_enc; - for (jk = jq = 0; jk < KVM_PPC_PAGE_SIZES_MAX_SZ; jk++) { - if (!kvm_valid_page_size(smmu_info.flags, max_cpu_page_size, - ksps->enc[jk].page_shift)) { - continue; - } - if (ksps->enc[jk].page_shift == 16) { - has_64k_pages = true; - } - qsps->enc[jq].page_shift = ksps->enc[jk].page_shift; - qsps->enc[jq].pte_enc = ksps->enc[jk].pte_enc; - if (++jq >= PPC_PAGE_SIZES_MAX_SZ) { + for (ik = 0; ik < ARRAY_SIZE(smmu_info.sps); ik++) { + if (qsps->page_shift == smmu_info.sps[ik].page_shift) { break; } } - if (++iq >= PPC_PAGE_SIZES_MAX_SZ) { - break; + if (ik >= ARRAY_SIZE(smmu_info.sps)) { + error_setg(errp, "KVM doesn't support for base page shift %u", + qsps->page_shift); + return; + } + + ksps = &smmu_info.sps[ik]; + if (ksps->slb_enc != qsps->slb_enc) { + error_setg(errp, +"KVM uses SLB encoding 0x%x for page shift %u, but guest expects 0x%x", + ksps->slb_enc, ksps->page_shift, qsps->slb_enc); + return; + } + + for (jq = 0; jq < ARRAY_SIZE(qsps->enc); jq++) { + for (jk = 0; jk < ARRAY_SIZE(ksps->enc); jk++) { + if (qsps->enc[jq].page_shift == ksps->enc[jk].page_shift) { + break; + } + } + + if (jk >= ARRAY_SIZE(ksps->enc)) { + error_setg(errp, "KVM doesn't support page shift %u/%u", + qsps->enc[jq].page_shift, qsps->page_shift); + return; + } + if (qsps->enc[jq].pte_enc != ksps->enc[jk].pte_enc) { + error_setg(errp, +"KVM uses PTE encoding 0x%x for page shift %u/%u, but guest expects 0x%x", + ksps->enc[jk].pte_enc, qsps->enc[jq].page_shift, + qsps->page_shift, qsps->enc[jq].pte_enc); + return; + } } } - env->slb_nr = smmu_info.slb_size; - if (!(smmu_info.flags & KVM_PPC_1T_SEGMENTS)) { - env->mmu_model &= ~POWERPC_MMU_1TSEG; - } - if (!has_64k_pages) { - env->mmu_model &= ~POWERPC_MMU_64K; + + if (ppc_hash64_has(cpu, PPC_HASH64_CI_LARGEPAGE)) { + /* Mostly what guest pagesizes we can use are related to the + * host pages used to map guest RAM, which is handled in the + * platform code. Cache-Inhibited largepages (64k) however are + * used for I/O, so if they're mapped to the host at all it + * will be a normal mapping, not a special hugepage one used + * for RAM. */ + if (getpagesize() < 0x10000) { + error_setg(errp, + "KVM can't supply 64kiB CI pages, which guest expects"); + } } } - -bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) -{ - Object *mem_obj = object_resolve_path(obj_path, NULL); - char *mempath = object_property_get_str(mem_obj, "mem-path", NULL); - long pagesize; - - if (mempath) { - pagesize = qemu_mempath_getpagesize(mempath); - g_free(mempath); - } else { - pagesize = getpagesize(); - } - - return pagesize >= max_cpu_page_size; -} - -#else /* defined (TARGET_PPC64) */ - -static inline void kvm_fixup_page_sizes(PowerPCCPU *cpu) -{ -} - -bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) -{ - return true; -} - #endif /* !defined (TARGET_PPC64) */ unsigned long kvm_arch_vcpu_id(CPUState *cpu) @@ -565,9 +473,6 @@ int kvm_arch_init_vcpu(CPUState *cs) CPUPPCState *cenv = &cpu->env; int ret; - /* Gather server mmu info from KVM and update the CPU state */ - kvm_fixup_page_sizes(cpu); - /* Synchronize sregs with kvm */ ret = kvm_arch_sync_sregs(cpu); if (ret) { @@ -842,22 +747,22 @@ static int kvm_get_fp(CPUState *cs) static int kvm_get_vpa(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); struct kvm_one_reg reg; int ret; reg.id = KVM_REG_PPC_VPA_ADDR; - reg.addr = (uintptr_t)&env->vpa_addr; + reg.addr = (uintptr_t)&spapr_cpu->vpa_addr; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to get VPA address from KVM: %s\n", strerror(errno)); return ret; } - assert((uintptr_t)&env->slb_shadow_size - == ((uintptr_t)&env->slb_shadow_addr + 8)); + assert((uintptr_t)&spapr_cpu->slb_shadow_size + == ((uintptr_t)&spapr_cpu->slb_shadow_addr + 8)); reg.id = KVM_REG_PPC_VPA_SLB; - reg.addr = (uintptr_t)&env->slb_shadow_addr; + reg.addr = (uintptr_t)&spapr_cpu->slb_shadow_addr; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to get SLB shadow state from KVM: %s\n", @@ -865,9 +770,10 @@ static int kvm_get_vpa(CPUState *cs) return ret; } - assert((uintptr_t)&env->dtl_size == ((uintptr_t)&env->dtl_addr + 8)); + assert((uintptr_t)&spapr_cpu->dtl_size + == ((uintptr_t)&spapr_cpu->dtl_addr + 8)); reg.id = KVM_REG_PPC_VPA_DTL; - reg.addr = (uintptr_t)&env->dtl_addr; + reg.addr = (uintptr_t)&spapr_cpu->dtl_addr; ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to get dispatch trace log state from KVM: %s\n", @@ -881,7 +787,7 @@ static int kvm_get_vpa(CPUState *cs) static int kvm_put_vpa(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; + sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); struct kvm_one_reg reg; int ret; @@ -889,11 +795,12 @@ static int kvm_put_vpa(CPUState *cs) * registered. That means when restoring state, if a VPA *is* * registered, we need to set that up first. If not, we need to * deregister the others before deregistering the master VPA */ - assert(env->vpa_addr || !(env->slb_shadow_addr || env->dtl_addr)); + assert(spapr_cpu->vpa_addr + || !(spapr_cpu->slb_shadow_addr || spapr_cpu->dtl_addr)); - if (env->vpa_addr) { + if (spapr_cpu->vpa_addr) { reg.id = KVM_REG_PPC_VPA_ADDR; - reg.addr = (uintptr_t)&env->vpa_addr; + reg.addr = (uintptr_t)&spapr_cpu->vpa_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to set VPA address to KVM: %s\n", strerror(errno)); @@ -901,19 +808,20 @@ static int kvm_put_vpa(CPUState *cs) } } - assert((uintptr_t)&env->slb_shadow_size - == ((uintptr_t)&env->slb_shadow_addr + 8)); + assert((uintptr_t)&spapr_cpu->slb_shadow_size + == ((uintptr_t)&spapr_cpu->slb_shadow_addr + 8)); reg.id = KVM_REG_PPC_VPA_SLB; - reg.addr = (uintptr_t)&env->slb_shadow_addr; + reg.addr = (uintptr_t)&spapr_cpu->slb_shadow_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to set SLB shadow state to KVM: %s\n", strerror(errno)); return ret; } - assert((uintptr_t)&env->dtl_size == ((uintptr_t)&env->dtl_addr + 8)); + assert((uintptr_t)&spapr_cpu->dtl_size + == ((uintptr_t)&spapr_cpu->dtl_addr + 8)); reg.id = KVM_REG_PPC_VPA_DTL; - reg.addr = (uintptr_t)&env->dtl_addr; + reg.addr = (uintptr_t)&spapr_cpu->dtl_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to set dispatch trace log state to KVM: %s\n", @@ -921,9 +829,9 @@ static int kvm_put_vpa(CPUState *cs) return ret; } - if (!env->vpa_addr) { + if (!spapr_cpu->vpa_addr) { reg.id = KVM_REG_PPC_VPA_ADDR; - reg.addr = (uintptr_t)&env->vpa_addr; + reg.addr = (uintptr_t)&spapr_cpu->vpa_addr; ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to set VPA address to KVM: %s\n", strerror(errno)); @@ -2101,6 +2009,10 @@ void kvmppc_set_papr(PowerPCCPU *cpu) CPUState *cs = CPU(cpu); int ret; + if (!kvm_enabled()) { + return; + } + ret = kvm_vcpu_enable_cap(cs, KVM_CAP_PPC_PAPR, 0); if (ret) { error_report("This vCPU type or KVM version does not support PAPR"); @@ -2170,55 +2082,15 @@ void kvmppc_hint_smt_possible(Error **errp) #ifdef TARGET_PPC64 -off_t kvmppc_alloc_rma(void **rma) -{ - off_t size; - int fd; - struct kvm_allocate_rma ret; - - /* If cap_ppc_rma == 0, contiguous RMA allocation is not supported - * if cap_ppc_rma == 1, contiguous RMA allocation is supported, but - * not necessary on this hardware - * if cap_ppc_rma == 2, contiguous RMA allocation is needed on this hardware - * - * FIXME: We should allow the user to force contiguous RMA - * allocation in the cap_ppc_rma==1 case. - */ - if (cap_ppc_rma < 2) { - return 0; - } - - fd = kvm_vm_ioctl(kvm_state, KVM_ALLOCATE_RMA, &ret); - if (fd < 0) { - fprintf(stderr, "KVM: Error on KVM_ALLOCATE_RMA: %s\n", - strerror(errno)); - return -1; - } - - size = MIN(ret.rma_size, 256ul << 20); - - *rma = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (*rma == MAP_FAILED) { - fprintf(stderr, "KVM: Error mapping RMA: %s\n", strerror(errno)); - return -1; - }; - - return size; -} - uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift) { struct kvm_ppc_smmu_info info; long rampagesize, best_page_shift; int i; - if (cap_ppc_rma >= 2) { - return current_size; - } - /* Find the largest hardware supported page size that's less than * or equal to the (logical) backing page size of guest RAM */ - kvm_get_smmu_info(POWERPC_CPU(first_cpu), &info); + kvm_get_smmu_info(&info, &error_fatal); rampagesize = qemu_getrampagesize(); best_page_shift = 0; @@ -2461,6 +2333,58 @@ bool kvmppc_has_cap_mmu_hash_v3(void) return cap_mmu_hash_v3; } +static bool kvmppc_power8_host(void) +{ + bool ret = false; +#ifdef TARGET_PPC64 + { + uint32_t base_pvr = CPU_POWERPC_POWER_SERVER_MASK & mfpvr(); + ret = (base_pvr == CPU_POWERPC_POWER8E_BASE) || + (base_pvr == CPU_POWERPC_POWER8NVL_BASE) || + (base_pvr == CPU_POWERPC_POWER8_BASE); + } +#endif /* TARGET_PPC64 */ + return ret; +} + +static int parse_cap_ppc_safe_cache(struct kvm_ppc_cpu_char c) +{ + bool l1d_thread_priv_req = !kvmppc_power8_host(); + + if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_L1D_FLUSH_PR) { + return 2; + } else if ((!l1d_thread_priv_req || + c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && + (c.character & c.character_mask + & (H_CPU_CHAR_L1D_FLUSH_ORI30 | H_CPU_CHAR_L1D_FLUSH_TRIG2))) { + return 1; + } + + return 0; +} + +static int parse_cap_ppc_safe_bounds_check(struct kvm_ppc_cpu_char c) +{ + if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_BNDS_CHK_SPEC_BAR) { + return 2; + } else if (c.character & c.character_mask & H_CPU_CHAR_SPEC_BAR_ORI31) { + return 1; + } + + return 0; +} + +static int parse_cap_ppc_safe_indirect_branch(struct kvm_ppc_cpu_char c) +{ + if (c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) { + return SPAPR_CAP_FIXED_CCD; + } else if (c.character & c.character_mask & H_CPU_CHAR_BCCTRL_SERIALISED) { + return SPAPR_CAP_FIXED_IBS; + } + + return 0; +} + static void kvmppc_get_cpu_characteristics(KVMState *s) { struct kvm_ppc_cpu_char c; @@ -2479,26 +2403,10 @@ static void kvmppc_get_cpu_characteristics(KVMState *s) if (ret < 0) { return; } - /* Parse and set cap_ppc_safe_cache */ - if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_L1D_FLUSH_PR) { - cap_ppc_safe_cache = 2; - } else if ((c.character & c.character_mask & H_CPU_CHAR_L1D_THREAD_PRIV) && - (c.character & c.character_mask - & (H_CPU_CHAR_L1D_FLUSH_ORI30 | H_CPU_CHAR_L1D_FLUSH_TRIG2))) { - cap_ppc_safe_cache = 1; - } - /* Parse and set cap_ppc_safe_bounds_check */ - if (~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_BNDS_CHK_SPEC_BAR) { - cap_ppc_safe_bounds_check = 2; - } else if (c.character & c.character_mask & H_CPU_CHAR_SPEC_BAR_ORI31) { - cap_ppc_safe_bounds_check = 1; - } - /* Parse and set cap_ppc_safe_indirect_branch */ - if (c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) { - cap_ppc_safe_indirect_branch = SPAPR_CAP_FIXED_CCD; - } else if (c.character & c.character_mask & H_CPU_CHAR_BCCTRL_SERIALISED) { - cap_ppc_safe_indirect_branch = SPAPR_CAP_FIXED_IBS; - } + + cap_ppc_safe_cache = parse_cap_ppc_safe_cache(c); + cap_ppc_safe_bounds_check = parse_cap_ppc_safe_bounds_check(c); + cap_ppc_safe_indirect_branch = parse_cap_ppc_safe_indirect_branch(c); } int kvmppc_get_cap_safe_cache(void) diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 4d2789eef6..657582bb32 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -37,7 +37,6 @@ target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, bool radix, bool gtse, uint64_t proc_tbl); #ifndef CONFIG_USER_ONLY -off_t kvmppc_alloc_rma(void **rma); bool kvmppc_spapr_use_multitce(void); int kvmppc_spapr_enable_inkernel_multitce(void); void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, @@ -71,7 +70,8 @@ int kvmppc_resize_hpt_prepare(PowerPCCPU *cpu, target_ulong flags, int shift); int kvmppc_resize_hpt_commit(PowerPCCPU *cpu, target_ulong flags, int shift); bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu); -bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path); +bool kvmppc_hpt_needs_host_contiguous_pages(void); +void kvm_check_mmu(PowerPCCPU *cpu, Error **errp); #else @@ -188,11 +188,6 @@ static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, } #ifndef CONFIG_USER_ONLY -static inline off_t kvmppc_alloc_rma(void **rma) -{ - return 0; -} - static inline bool kvmppc_spapr_use_multitce(void) { return false; @@ -228,9 +223,13 @@ static inline uint64_t kvmppc_rma_size(uint64_t current_size, return ram_size; } -static inline bool kvmppc_is_mem_backend_page_size_ok(const char *obj_path) +static inline bool kvmppc_hpt_needs_host_contiguous_pages(void) +{ + return false; +} + +static inline void kvm_check_mmu(PowerPCCPU *cpu, Error **errp) { - return true; } static inline bool kvmppc_has_cap_spapr_vfio(void) diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 0634cdb295..b2745ec4e5 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -18,6 +18,9 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) unsigned int i, j; target_ulong sdr1; uint32_t fpscr; +#if defined(TARGET_PPC64) + int32_t slb_nr; +#endif target_ulong xer; for (i = 0; i < 32; i++) @@ -49,7 +52,7 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_sbe32s(f, &env->access_type); #if defined(TARGET_PPC64) qemu_get_betls(f, &env->spr[SPR_ASR]); - qemu_get_sbe32s(f, &env->slb_nr); + qemu_get_sbe32s(f, &slb_nr); #endif qemu_get_betls(f, &sdr1); for (i = 0; i < 32; i++) @@ -146,6 +149,15 @@ static bool cpu_pre_2_8_migration(void *opaque, int version_id) return cpu->pre_2_8_migration; } +#if defined(TARGET_PPC64) +static bool cpu_pre_3_0_migration(void *opaque, int version_id) +{ + PowerPCCPU *cpu = opaque; + + return cpu->pre_3_0_migration; +} +#endif + static int cpu_pre_save(void *opaque) { PowerPCCPU *cpu = opaque; @@ -200,9 +212,19 @@ static int cpu_pre_save(void *opaque) ; cpu->mig_msr_mask = env->msr_mask & ~metamask; cpu->mig_insns_flags = env->insns_flags & insns_compat_mask; + /* CPU models supported by old machines all have PPC_MEM_TLBIE, + * so we set it unconditionally to allow backward migration from + * a POWER9 host to a POWER8 host. + */ + cpu->mig_insns_flags |= PPC_MEM_TLBIE; cpu->mig_insns_flags2 = env->insns_flags2 & insns_compat_mask2; cpu->mig_nb_BATs = env->nb_BATs; } + if (cpu->pre_3_0_migration) { + if (cpu->hash64_opts) { + cpu->mig_slb_nr = cpu->hash64_opts->slb_size; + } + } return 0; } @@ -478,7 +500,7 @@ static int slb_post_load(void *opaque, int version_id) /* We've pulled in the raw esid and vsid values from the migration * stream, but we need to recompute the page size pointers */ - for (i = 0; i < env->slb_nr; i++) { + for (i = 0; i < cpu->hash64_opts->slb_size; i++) { if (ppc_store_slb(cpu, i, env->slb[i].esid, env->slb[i].vsid) < 0) { /* Migration source had bad values in its SLB */ return -1; @@ -495,7 +517,7 @@ static const VMStateDescription vmstate_slb = { .needed = slb_needed, .post_load = slb_post_load, .fields = (VMStateField[]) { - VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU, NULL), + VMSTATE_INT32_TEST(mig_slb_nr, PowerPCCPU, cpu_pre_3_0_migration), VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), VMSTATE_END_OF_LIST() } diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index a34e604db3..8f0d86d104 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -21,9 +21,9 @@ #include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" - #include "helper_regs.h" #include "exec/cpu_ldst.h" +#include "tcg.h" #include "internal.h" //#define DEBUG_OP @@ -215,6 +215,76 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, return i; } +#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128) +uint64_t helper_lq_le_parallel(CPUPPCState *env, target_ulong addr, + uint32_t opidx) +{ + Int128 ret = helper_atomic_ldo_le_mmu(env, addr, opidx, GETPC()); + env->retxh = int128_gethi(ret); + return int128_getlo(ret); +} + +uint64_t helper_lq_be_parallel(CPUPPCState *env, target_ulong addr, + uint32_t opidx) +{ + Int128 ret = helper_atomic_ldo_be_mmu(env, addr, opidx, GETPC()); + env->retxh = int128_gethi(ret); + return int128_getlo(ret); +} + +void helper_stq_le_parallel(CPUPPCState *env, target_ulong addr, + uint64_t lo, uint64_t hi, uint32_t opidx) +{ + Int128 val = int128_make128(lo, hi); + helper_atomic_sto_le_mmu(env, addr, val, opidx, GETPC()); +} + +void helper_stq_be_parallel(CPUPPCState *env, target_ulong addr, + uint64_t lo, uint64_t hi, uint32_t opidx) +{ + Int128 val = int128_make128(lo, hi); + helper_atomic_sto_be_mmu(env, addr, val, opidx, GETPC()); +} + +uint32_t helper_stqcx_le_parallel(CPUPPCState *env, target_ulong addr, + uint64_t new_lo, uint64_t new_hi, + uint32_t opidx) +{ + bool success = false; + + if (likely(addr == env->reserve_addr)) { + Int128 oldv, cmpv, newv; + + cmpv = int128_make128(env->reserve_val2, env->reserve_val); + newv = int128_make128(new_lo, new_hi); + oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, + opidx, GETPC()); + success = int128_eq(oldv, cmpv); + } + env->reserve_addr = -1; + return env->so + success * CRF_EQ_BIT; +} + +uint32_t helper_stqcx_be_parallel(CPUPPCState *env, target_ulong addr, + uint64_t new_lo, uint64_t new_hi, + uint32_t opidx) +{ + bool success = false; + + if (likely(addr == env->reserve_addr)) { + Int128 oldv, cmpv, newv; + + cmpv = int128_make128(env->reserve_val2, env->reserve_val); + newv = int128_make128(new_lo, new_hi); + oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, + opidx, GETPC()); + success = int128_eq(oldv, cmpv); + } + env->reserve_addr = -1; + return env->so + success * CRF_EQ_BIT; +} +#endif + /*****************************************************************************/ /* Altivec extension helpers */ #if defined(HOST_WORDS_BIGENDIAN) diff --git a/target/ppc/mfrom_table.c b/target/ppc/mfrom_table.inc.c similarity index 100% rename from target/ppc/mfrom_table.c rename to target/ppc/mfrom_table.inc.c diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 0e4217821b..b884930096 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -20,6 +20,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" +#include "qemu/error-report.h" #include "helper_regs.h" @@ -88,6 +89,26 @@ void helper_store_sdr1(CPUPPCState *env, target_ulong val) } } +#if defined(TARGET_PPC64) +void helper_store_ptcr(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + if (env->spr[SPR_PTCR] != val) { + ppc_store_ptcr(env, val); + tlb_flush(CPU(cpu)); + } +} + +void helper_store_pcr(CPUPPCState *env, target_ulong value) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + + env->spr[SPR_PCR] = value & pcc->pcr_mask; +} +#endif /* defined(TARGET_PPC64) */ + void helper_store_pidr(CPUPPCState *env, target_ulong val) { PowerPCCPU *cpu = ppc_env_get_cpu(env); diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index 56095dab52..fdf80987d7 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -22,6 +22,12 @@ #ifndef CONFIG_USER_ONLY +/* + * Partition table definitions + */ +#define PTCR_PATB 0x0FFFFFFFFFFFF000ULL /* Partition Table Base */ +#define PTCR_PATS 0x000000000000001FULL /* Partition Table Size */ + /* Partition Table Entry Fields */ #define PATBE1_GR 0x8000000000000000 diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index c9b72b7429..276d9015e7 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -52,7 +52,7 @@ static ppc_slb_t *slb_lookup(PowerPCCPU *cpu, target_ulong eaddr) esid_256M = (eaddr & SEGMENT_MASK_256M) | SLB_ESID_V; esid_1T = (eaddr & SEGMENT_MASK_1T) | SLB_ESID_V; - for (n = 0; n < env->slb_nr; n++) { + for (n = 0; n < cpu->hash64_opts->slb_size; n++) { ppc_slb_t *slb = &env->slb[n]; LOG_SLB("%s: slot %d %016" PRIx64 " %016" @@ -80,7 +80,7 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu) cpu_synchronize_state(CPU(cpu)); cpu_fprintf(f, "SLB\tESID\t\t\tVSID\n"); - for (i = 0; i < env->slb_nr; i++) { + for (i = 0; i < cpu->hash64_opts->slb_size; i++) { slbe = env->slb[i].esid; slbv = env->slb[i].vsid; if (slbe == 0 && slbv == 0) { @@ -93,10 +93,11 @@ void dump_slb(FILE *f, fprintf_function cpu_fprintf, PowerPCCPU *cpu) void helper_slbia(CPUPPCState *env) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); int n; /* XXX: Warning: slbia never invalidates the first segment */ - for (n = 1; n < env->slb_nr; n++) { + for (n = 1; n < cpu->hash64_opts->slb_size; n++) { ppc_slb_t *slb = &env->slb[n]; if (slb->esid & SLB_ESID_V) { @@ -148,10 +149,10 @@ int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, { CPUPPCState *env = &cpu->env; ppc_slb_t *slb = &env->slb[slot]; - const struct ppc_one_seg_page_size *sps = NULL; + const PPCHash64SegmentPageSizes *sps = NULL; int i; - if (slot >= env->slb_nr) { + if (slot >= cpu->hash64_opts->slb_size) { return -1; /* Bad slot number */ } if (esid & ~(SLB_ESID_ESID | SLB_ESID_V)) { @@ -160,12 +161,12 @@ int ppc_store_slb(PowerPCCPU *cpu, target_ulong slot, if (vsid & (SLB_VSID_B & ~SLB_VSID_B_1T)) { return -1; /* Bad segment size */ } - if ((vsid & SLB_VSID_B) && !(env->mmu_model & POWERPC_MMU_1TSEG)) { + if ((vsid & SLB_VSID_B) && !(ppc_hash64_has(cpu, PPC_HASH64_1TSEG))) { return -1; /* 1T segment on MMU that doesn't support it */ } for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const struct ppc_one_seg_page_size *sps1 = &env->sps.sps[i]; + const PPCHash64SegmentPageSizes *sps1 = &cpu->hash64_opts->sps[i]; if (!sps1->page_shift) { break; @@ -202,7 +203,7 @@ static int ppc_load_slb_esid(PowerPCCPU *cpu, target_ulong rb, int slot = rb & 0xfff; ppc_slb_t *slb = &env->slb[slot]; - if (slot >= env->slb_nr) { + if (slot >= cpu->hash64_opts->slb_size) { return -1; } @@ -217,7 +218,7 @@ static int ppc_load_slb_vsid(PowerPCCPU *cpu, target_ulong rb, int slot = rb & 0xfff; ppc_slb_t *slb = &env->slb[slot]; - if (slot >= env->slb_nr) { + if (slot >= cpu->hash64_opts->slb_size) { return -1; } @@ -369,7 +370,7 @@ static int ppc_hash64_amr_prot(PowerPCCPU *cpu, ppc_hash_pte64_t pte) int prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; /* Only recent MMUs implement Virtual Page Class Key Protection */ - if (!(env->mmu_model & POWERPC_MMU_AMR)) { + if (!ppc_hash64_has(cpu, PPC_HASH64_AMR)) { return prot; } @@ -430,7 +431,8 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, return NULL; } - hptes = address_space_map(CPU(cpu)->as, base + pte_offset, &plen, false); + hptes = address_space_map(CPU(cpu)->as, base + pte_offset, &plen, false, + MEMTXATTRS_UNSPECIFIED); if (plen < (n * HASH_PTE_SIZE_64)) { hw_error("%s: Unable to map all requested HPTEs\n", __func__); } @@ -451,8 +453,8 @@ void ppc_hash64_unmap_hptes(PowerPCCPU *cpu, const ppc_hash_pte64_t *hptes, false, n * HASH_PTE_SIZE_64); } -static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, - uint64_t pte0, uint64_t pte1) +static unsigned hpte_page_shift(const PPCHash64SegmentPageSizes *sps, + uint64_t pte0, uint64_t pte1) { int i; @@ -466,7 +468,7 @@ static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, } for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const struct ppc_one_page_size *ps = &sps->enc[i]; + const PPCHash64PageSize *ps = &sps->enc[i]; uint64_t mask; if (!ps->page_shift) { @@ -489,7 +491,7 @@ static unsigned hpte_page_shift(const struct ppc_one_seg_page_size *sps, } static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, - const struct ppc_one_seg_page_size *sps, + const PPCHash64SegmentPageSizes *sps, target_ulong ptem, ppc_hash_pte64_t *pte, unsigned *pshift) { @@ -543,7 +545,7 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, CPUPPCState *env = &cpu->env; hwaddr hash, ptex; uint64_t vsid, epnmask, epn, ptem; - const struct ppc_one_seg_page_size *sps = slb->sps; + const PPCHash64SegmentPageSizes *sps = slb->sps; /* The SLB store path should prevent any bad page size encodings * getting in there, so: */ @@ -552,7 +554,7 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, /* If ISL is set in LPCR we need to clamp the page size to 4K */ if (env->spr[SPR_LPCR] & LPCR_ISL) { /* We assume that when using TCG, 4k is first entry of SPS */ - sps = &env->sps.sps[0]; + sps = &cpu->hash64_opts->sps[0]; assert(sps->page_shift == 12); } @@ -605,7 +607,6 @@ static hwaddr ppc_hash64_htab_lookup(PowerPCCPU *cpu, unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, uint64_t pte0, uint64_t pte1) { - CPUPPCState *env = &cpu->env; int i; if (!(pte0 & HPTE64_V_LARGE)) { @@ -617,7 +618,7 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, * this gives an unambiguous result. */ for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const struct ppc_one_seg_page_size *sps = &env->sps.sps[i]; + const PPCHash64SegmentPageSizes *sps = &cpu->hash64_opts->sps[i]; unsigned shift; if (!sps->page_shift) { @@ -633,9 +634,9 @@ unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, return 0; } -static void ppc_hash64_set_isi(CPUState *cs, CPUPPCState *env, - uint64_t error_code) +static void ppc_hash64_set_isi(CPUState *cs, uint64_t error_code) { + CPUPPCState *env = &POWERPC_CPU(cs)->env; bool vpm; if (msr_ir) { @@ -659,9 +660,9 @@ static void ppc_hash64_set_isi(CPUState *cs, CPUPPCState *env, env->error_code = error_code; } -static void ppc_hash64_set_dsi(CPUState *cs, CPUPPCState *env, uint64_t dar, - uint64_t dsisr) +static void ppc_hash64_set_dsi(CPUState *cs, uint64_t dar, uint64_t dsisr) { + CPUPPCState *env = &POWERPC_CPU(cs)->env; bool vpm; if (msr_dr) { @@ -741,13 +742,13 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, } else { /* The access failed, generate the approriate interrupt */ if (rwx == 2) { - ppc_hash64_set_isi(cs, env, SRR1_PROTFAULT); + ppc_hash64_set_isi(cs, SRR1_PROTFAULT); } else { int dsisr = DSISR_PROTFAULT; if (rwx == 1) { dsisr |= DSISR_ISSTORE; } - ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + ppc_hash64_set_dsi(cs, eaddr, dsisr); } return 1; } @@ -762,7 +763,7 @@ int ppc_hash64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, slb = slb_lookup(cpu, eaddr); if (!slb) { /* No entry found, check if in-memory segment tables are in use */ - if ((env->mmu_model & POWERPC_MMU_V3) && ppc64_use_proc_tbl(cpu)) { + if (ppc64_use_proc_tbl(cpu)) { /* TODO - Unsupported */ error_report("Segment Table Support Unimplemented"); exit(1); @@ -783,7 +784,7 @@ skip_slb_search: /* 3. Check for segment level no-execute violation */ if ((rwx == 2) && (slb->vsid & SLB_VSID_N)) { - ppc_hash64_set_isi(cs, env, SRR1_NOEXEC_GUARD); + ppc_hash64_set_isi(cs, SRR1_NOEXEC_GUARD); return 1; } @@ -791,13 +792,13 @@ skip_slb_search: ptex = ppc_hash64_htab_lookup(cpu, slb, eaddr, &pte, &apshift); if (ptex == -1) { if (rwx == 2) { - ppc_hash64_set_isi(cs, env, SRR1_NOPTE); + ppc_hash64_set_isi(cs, SRR1_NOPTE); } else { int dsisr = DSISR_NOPTE; if (rwx == 1) { dsisr |= DSISR_ISSTORE; } - ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + ppc_hash64_set_dsi(cs, eaddr, dsisr); } return 1; } @@ -824,7 +825,7 @@ skip_slb_search: if (PAGE_EXEC & ~amr_prot) { srr1 |= SRR1_IAMR; /* Access violates virt pg class key prot */ } - ppc_hash64_set_isi(cs, env, srr1); + ppc_hash64_set_isi(cs, srr1); } else { int dsisr = 0; if (need_prot[rwx] & ~pp_prot) { @@ -836,7 +837,7 @@ skip_slb_search: if (need_prot[rwx] & ~amr_prot) { dsisr |= DSISR_AMR; } - ppc_hash64_set_dsi(cs, env, eaddr, dsisr); + ppc_hash64_set_dsi(cs, eaddr, dsisr); } return 1; } @@ -942,8 +943,9 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong ptex, cpu->env.tlb_need_flush = TLB_NEED_GLOBAL_FLUSH | TLB_NEED_LOCAL_FLUSH; } -void ppc_hash64_update_rmls(CPUPPCState *env) +static void ppc_hash64_update_rmls(PowerPCCPU *cpu) { + CPUPPCState *env = &cpu->env; uint64_t lpcr = env->spr[SPR_LPCR]; /* @@ -976,9 +978,10 @@ void ppc_hash64_update_rmls(CPUPPCState *env) } } -void ppc_hash64_update_vrma(CPUPPCState *env) +static void ppc_hash64_update_vrma(PowerPCCPU *cpu) { - const struct ppc_one_seg_page_size *sps = NULL; + CPUPPCState *env = &cpu->env; + const PPCHash64SegmentPageSizes *sps = NULL; target_ulong esid, vsid, lpcr; ppc_slb_t *slb = &env->vrma_slb; uint32_t vrmasd; @@ -1002,8 +1005,8 @@ void ppc_hash64_update_vrma(CPUPPCState *env) vsid |= (vrmasd << 4) & (SLB_VSID_L | SLB_VSID_LP); esid = SLB_ESID_V; - for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { - const struct ppc_one_seg_page_size *sps1 = &env->sps.sps[i]; + for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { + const PPCHash64SegmentPageSizes *sps1 = &cpu->hash64_opts->sps[i]; if (!sps1->page_shift) { break; @@ -1026,13 +1029,14 @@ void ppc_hash64_update_vrma(CPUPPCState *env) slb->sps = sps; } -void helper_store_lpcr(CPUPPCState *env, target_ulong val) +void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) { + CPUPPCState *env = &cpu->env; uint64_t lpcr = 0; /* Filter out bits */ - switch (POWERPC_MMU_VER(env->mmu_model)) { - case POWERPC_MMU_VER_64B: /* 970 */ + switch (env->mmu_model) { + case POWERPC_MMU_64B: /* 970 */ if (val & 0x40) { lpcr |= LPCR_LPES0; } @@ -1058,26 +1062,26 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val) * to dig HRMOR out of HID5 */ break; - case POWERPC_MMU_VER_2_03: /* P5p */ + case POWERPC_MMU_2_03: /* P5p */ lpcr = val & (LPCR_RMLS | LPCR_ILE | LPCR_LPES0 | LPCR_LPES1 | LPCR_RMI | LPCR_HDICE); break; - case POWERPC_MMU_VER_2_06: /* P7 */ + case POWERPC_MMU_2_06: /* P7 */ lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | LPCR_P7_PECE0 | LPCR_P7_PECE1 | LPCR_P7_PECE2 | LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_LPES1 | LPCR_HDICE); break; - case POWERPC_MMU_VER_2_07: /* P8 */ + case POWERPC_MMU_2_07: /* P8 */ lpcr = val & (LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | LPCR_VRMASD | LPCR_RMLS | LPCR_ILE | LPCR_AIL | LPCR_ONL | LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 | LPCR_P8_PECE3 | LPCR_P8_PECE4 | LPCR_MER | LPCR_TC | LPCR_LPES0 | LPCR_HDICE); break; - case POWERPC_MMU_VER_3_00: /* P9 */ + case POWERPC_MMU_3_00: /* P9 */ lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | @@ -1089,6 +1093,135 @@ void helper_store_lpcr(CPUPPCState *env, target_ulong val) ; } env->spr[SPR_LPCR] = lpcr; - ppc_hash64_update_rmls(env); - ppc_hash64_update_vrma(env); + ppc_hash64_update_rmls(cpu); + ppc_hash64_update_vrma(cpu); +} + +void helper_store_lpcr(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + ppc_store_lpcr(cpu, val); +} + +void ppc_hash64_init(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + + if (!pcc->hash64_opts) { + assert(!(env->mmu_model & POWERPC_MMU_64)); + return; + } + + cpu->hash64_opts = g_memdup(pcc->hash64_opts, sizeof(*cpu->hash64_opts)); +} + +void ppc_hash64_finalize(PowerPCCPU *cpu) +{ + g_free(cpu->hash64_opts); +} + +const PPCHash64Options ppc_hash64_opts_basic = { + .flags = 0, + .slb_size = 64, + .sps = { + { .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 } } + }, + { .page_shift = 24, /* 16M */ + .slb_enc = 0x100, + .enc = { { .page_shift = 24, .pte_enc = 0 } } + }, + }, +}; + +const PPCHash64Options ppc_hash64_opts_POWER7 = { + .flags = PPC_HASH64_1TSEG | PPC_HASH64_AMR | PPC_HASH64_CI_LARGEPAGE, + .slb_size = 32, + .sps = { + { + .page_shift = 12, /* 4K */ + .slb_enc = 0, + .enc = { { .page_shift = 12, .pte_enc = 0 }, + { .page_shift = 16, .pte_enc = 0x7 }, + { .page_shift = 24, .pte_enc = 0x38 }, }, + }, + { + .page_shift = 16, /* 64K */ + .slb_enc = SLB_VSID_64K, + .enc = { { .page_shift = 16, .pte_enc = 0x1 }, + { .page_shift = 24, .pte_enc = 0x8 }, }, + }, + { + .page_shift = 24, /* 16M */ + .slb_enc = SLB_VSID_16M, + .enc = { { .page_shift = 24, .pte_enc = 0 }, }, + }, + { + .page_shift = 34, /* 16G */ + .slb_enc = SLB_VSID_16G, + .enc = { { .page_shift = 34, .pte_enc = 0x3 }, }, + }, + } +}; + +void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu, + bool (*cb)(void *, uint32_t, uint32_t), + void *opaque) +{ + PPCHash64Options *opts = cpu->hash64_opts; + int i; + int n = 0; + bool ci_largepage = false; + + assert(opts); + + n = 0; + for (i = 0; i < ARRAY_SIZE(opts->sps); i++) { + PPCHash64SegmentPageSizes *sps = &opts->sps[i]; + int j; + int m = 0; + + assert(n <= i); + + if (!sps->page_shift) { + break; + } + + for (j = 0; j < ARRAY_SIZE(sps->enc); j++) { + PPCHash64PageSize *ps = &sps->enc[j]; + + assert(m <= j); + if (!ps->page_shift) { + break; + } + + if (cb(opaque, sps->page_shift, ps->page_shift)) { + if (ps->page_shift >= 16) { + ci_largepage = true; + } + sps->enc[m++] = *ps; + } + } + + /* Clear rest of the row */ + for (j = m; j < ARRAY_SIZE(sps->enc); j++) { + memset(&sps->enc[j], 0, sizeof(sps->enc[j])); + } + + if (m) { + n++; + } + } + + /* Clear the rest of the table */ + for (i = n; i < ARRAY_SIZE(opts->sps); i++) { + memset(&opts->sps[i], 0, sizeof(opts->sps[i])); + } + + if (!ci_largepage) { + opts->flags &= ~PPC_HASH64_CI_LARGEPAGE; + } } diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index d297b97d37..f11efc9cbc 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -17,8 +17,12 @@ void ppc_hash64_tlb_flush_hpte(PowerPCCPU *cpu, target_ulong pte0, target_ulong pte1); unsigned ppc_hash64_hpte_page_shift_noslb(PowerPCCPU *cpu, uint64_t pte0, uint64_t pte1); -void ppc_hash64_update_vrma(CPUPPCState *env); -void ppc_hash64_update_rmls(CPUPPCState *env); +void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val); +void ppc_hash64_init(PowerPCCPU *cpu); +void ppc_hash64_finalize(PowerPCCPU *cpu); +void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu, + bool (*cb)(void *, uint32_t, uint32_t), + void *opaque); #endif /* @@ -100,6 +104,9 @@ void ppc_hash64_update_rmls(CPUPPCState *env); static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) { + if (cpu->vhyp) { + return 0; + } return cpu->env.spr[SPR_SDR1] & SDR_64_HTABORG; } @@ -134,6 +141,48 @@ static inline uint64_t ppc_hash64_hpte1(PowerPCCPU *cpu, return ldq_p(&(hptes[i].pte1)); } +/* + * MMU Options + */ + +struct PPCHash64PageSize { + uint32_t page_shift; /* Page shift (or 0) */ + uint32_t pte_enc; /* Encoding in the HPTE (>>12) */ +}; +typedef struct PPCHash64PageSize PPCHash64PageSize; + +struct PPCHash64SegmentPageSizes { + uint32_t page_shift; /* Base page shift of segment (or 0) */ + uint32_t slb_enc; /* SLB encoding for BookS */ + PPCHash64PageSize enc[PPC_PAGE_SIZES_MAX_SZ]; +}; + +struct PPCHash64Options { +#define PPC_HASH64_1TSEG 0x00001 +#define PPC_HASH64_AMR 0x00002 +#define PPC_HASH64_CI_LARGEPAGE 0x00004 + unsigned flags; + unsigned slb_size; + PPCHash64SegmentPageSizes sps[PPC_PAGE_SIZES_MAX_SZ]; +}; + +extern const PPCHash64Options ppc_hash64_opts_basic; +extern const PPCHash64Options ppc_hash64_opts_POWER7; + +static inline bool ppc_hash64_has(PowerPCCPU *cpu, unsigned feature) +{ + return !!(cpu->hash64_opts->flags & feature); +} + #endif /* CONFIG_USER_ONLY */ +#if defined(CONFIG_USER_ONLY) || !defined(TARGET_PPC64) +static inline void ppc_hash64_init(PowerPCCPU *cpu) +{ +} +static inline void ppc_hash64_finalize(PowerPCCPU *cpu) +{ +} +#endif + #endif /* MMU_HASH64_H */ diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 5568d1642b..e6739e6c24 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -17,6 +17,7 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "exec/helper-proto.h" #include "sysemu/kvm.h" @@ -1090,11 +1091,10 @@ static void mmubooke_dump_mmu(FILE *f, fprintf_function cpu_fprintf, pa = entry->RPN & mask; /* Extend the physical address to 36 bits */ pa |= (hwaddr)(entry->RPN & 0xF) << 32; - size /= 1024; - if (size >= 1024) { - snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / 1024); + if (size >= 1 * MiB) { + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "M", size / MiB); } else { - snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "k", size); + snprintf(size_buf, sizeof(size_buf), "%3" PRId64 "k", size / KiB); } cpu_fprintf(f, "0x%016" PRIx64 " 0x%016" PRIx64 " %s %-5u %08x %08x\n", (uint64_t)ea, (uint64_t)pa, size_buf, (uint32_t)entry->PID, @@ -1266,7 +1266,7 @@ static void mmu6xx_dump_mmu(FILE *f, fprintf_function cpu_fprintf, void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) { - switch (POWERPC_MMU_VER(env->mmu_model)) { + switch (env->mmu_model) { case POWERPC_MMU_BOOKE: mmubooke_dump_mmu(f, cpu_fprintf, env); break; @@ -1278,13 +1278,13 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) mmu6xx_dump_mmu(f, cpu_fprintf, env); break; #if defined(TARGET_PPC64) - case POWERPC_MMU_VER_64B: - case POWERPC_MMU_VER_2_03: - case POWERPC_MMU_VER_2_06: - case POWERPC_MMU_VER_2_07: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env)); break; - case POWERPC_MMU_VER_3_00: + case POWERPC_MMU_3_00: if (ppc64_radix_guest(ppc_env_get_cpu(env))) { /* TODO - Unsupported */ } else { @@ -1423,14 +1423,14 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) CPUPPCState *env = &cpu->env; mmu_ctx_t ctx; - switch (POWERPC_MMU_VER(env->mmu_model)) { + switch (env->mmu_model) { #if defined(TARGET_PPC64) - case POWERPC_MMU_VER_64B: - case POWERPC_MMU_VER_2_03: - case POWERPC_MMU_VER_2_06: - case POWERPC_MMU_VER_2_07: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: return ppc_hash64_get_phys_page_debug(cpu, addr); - case POWERPC_MMU_VER_3_00: + case POWERPC_MMU_3_00: if (ppc64_radix_guest(ppc_env_get_cpu(env))) { return ppc_radix64_get_phys_page_debug(cpu, addr); } else { @@ -2028,6 +2028,35 @@ void ppc_store_sdr1(CPUPPCState *env, target_ulong value) env->spr[SPR_SDR1] = value; } +#if defined(TARGET_PPC64) +void ppc_store_ptcr(CPUPPCState *env, target_ulong value) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + target_ulong ptcr_mask = PTCR_PATB | PTCR_PATS; + target_ulong patbsize = value & PTCR_PATS; + + qemu_log_mask(CPU_LOG_MMU, "%s: " TARGET_FMT_lx "\n", __func__, value); + + assert(!cpu->vhyp); + assert(env->mmu_model & POWERPC_MMU_3_00); + + if (value & ~ptcr_mask) { + error_report("Invalid bits 0x"TARGET_FMT_lx" set in PTCR", + value & ~ptcr_mask); + value &= ptcr_mask; + } + + if (patbsize > 24) { + error_report("Invalid Partition Table size 0x" TARGET_FMT_lx + " stored in PTCR", patbsize); + return; + } + + env->spr[SPR_PTCR] = value; +} + +#endif /* defined(TARGET_PPC64) */ + /* Segment registers load and store */ target_ulong helper_load_sr(CPUPPCState *env, target_ulong sr_num) { diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 3457d29f8e..9eaa10b421 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -2388,23 +2388,6 @@ static inline void gen_addr_add(DisasContext *ctx, TCGv ret, TCGv arg1, } } -static inline void gen_check_align(DisasContext *ctx, TCGv EA, int mask) -{ - TCGLabel *l1 = gen_new_label(); - TCGv t0 = tcg_temp_new(); - TCGv_i32 t1, t2; - tcg_gen_andi_tl(t0, EA, mask); - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); - t1 = tcg_const_i32(POWERPC_EXCP_ALIGN); - t2 = tcg_const_i32(ctx->opcode & 0x03FF0000); - gen_update_nip(ctx, ctx->base.pc_next - 4); - gen_helper_raise_exception_err(cpu_env, t1, t2); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t2); - gen_set_label(l1); - tcg_temp_free(t0); -} - static inline void gen_align_no_le(DisasContext *ctx) { gen_exception_err(ctx, POWERPC_EXCP_ALIGN, @@ -2607,7 +2590,7 @@ static void gen_ld(DisasContext *ctx) static void gen_lq(DisasContext *ctx) { int ra, rd; - TCGv EA; + TCGv EA, hi, lo; /* lq is a legal user mode instruction starting in ISA 2.07 */ bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; @@ -2633,16 +2616,35 @@ static void gen_lq(DisasContext *ctx) EA = tcg_temp_new(); gen_addr_imm_index(ctx, EA, 0x0F); - /* We only need to swap high and low halves. gen_qemu_ld64_i64 does - necessary 64-bit byteswap already. */ - if (unlikely(ctx->le_mode)) { - gen_qemu_ld64_i64(ctx, cpu_gpr[rd + 1], EA); + /* Note that the low part is always in RD+1, even in LE mode. */ + lo = cpu_gpr[rd + 1]; + hi = cpu_gpr[rd]; + + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { +#ifdef CONFIG_ATOMIC128 + TCGv_i32 oi = tcg_temp_new_i32(); + if (ctx->le_mode) { + tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); + gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); + } else { + tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); + gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); + } + tcg_temp_free_i32(oi); + tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); +#else + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; +#endif + } else if (ctx->le_mode) { + tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ); gen_addr_add(ctx, EA, EA, 8); - gen_qemu_ld64_i64(ctx, cpu_gpr[rd], EA); + tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_LEQ); } else { - gen_qemu_ld64_i64(ctx, cpu_gpr[rd], EA); + tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_BEQ); gen_addr_add(ctx, EA, EA, 8); - gen_qemu_ld64_i64(ctx, cpu_gpr[rd + 1], EA); + tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_BEQ); } tcg_temp_free(EA); } @@ -2741,6 +2743,7 @@ static void gen_std(DisasContext *ctx) if ((ctx->opcode & 0x3) == 0x2) { /* stq */ bool legal_in_user_mode = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; bool le_is_supported = (ctx->insns_flags2 & PPC2_LSQ_ISA207) != 0; + TCGv hi, lo; if (!(ctx->insns_flags & PPC_64BX)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); @@ -2764,20 +2767,38 @@ static void gen_std(DisasContext *ctx) EA = tcg_temp_new(); gen_addr_imm_index(ctx, EA, 0x03); - /* We only need to swap high and low halves. gen_qemu_st64_i64 does - necessary 64-bit byteswap already. */ - if (unlikely(ctx->le_mode)) { - gen_qemu_st64_i64(ctx, cpu_gpr[rs + 1], EA); + /* Note that the low part is always in RS+1, even in LE mode. */ + lo = cpu_gpr[rs + 1]; + hi = cpu_gpr[rs]; + + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { +#ifdef CONFIG_ATOMIC128 + TCGv_i32 oi = tcg_temp_new_i32(); + if (ctx->le_mode) { + tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); + gen_helper_stq_le_parallel(cpu_env, EA, lo, hi, oi); + } else { + tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); + gen_helper_stq_be_parallel(cpu_env, EA, lo, hi, oi); + } + tcg_temp_free_i32(oi); +#else + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; +#endif + } else if (ctx->le_mode) { + tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_LEQ); gen_addr_add(ctx, EA, EA, 8); - gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA); + tcg_gen_qemu_st_i64(hi, EA, ctx->mem_idx, MO_LEQ); } else { - gen_qemu_st64_i64(ctx, cpu_gpr[rs], EA); + tcg_gen_qemu_st_i64(hi, EA, ctx->mem_idx, MO_BEQ); gen_addr_add(ctx, EA, EA, 8); - gen_qemu_st64_i64(ctx, cpu_gpr[rs + 1], EA); + tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_BEQ); } tcg_temp_free(EA); } else { - /* std / stdu*/ + /* std / stdu */ if (Rc(ctx->opcode)) { if (unlikely(rA(ctx->opcode) == 0)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); @@ -2967,7 +2988,28 @@ static void gen_stswx(DisasContext *ctx) /* eieio */ static void gen_eieio(DisasContext *ctx) { - tcg_gen_mb(TCG_MO_LD_ST | TCG_BAR_SC); + TCGBar bar = TCG_MO_LD_ST; + + /* + * POWER9 has a eieio instruction variant using bit 6 as a hint to + * tell the CPU it is a store-forwarding barrier. + */ + if (ctx->opcode & 0x2000000) { + /* + * ISA says that "Reserved fields in instructions are ignored + * by the processor". So ignore the bit 6 on non-POWER9 CPU but + * as this is not an instruction software should be using, + * complain to the user. + */ + if (!(ctx->insns_flags2 & PPC2_ISA300)) { + qemu_log_mask(LOG_GUEST_ERROR, "invalid eieio using bit 6 at @" + TARGET_FMT_lx "\n", ctx->base.pc_next - 4); + } else { + bar = TCG_MO_ST_LD; + } + } + + tcg_gen_mb(bar | TCG_BAR_SC); } #if !defined(CONFIG_USER_ONLY) @@ -3011,23 +3053,24 @@ static void gen_isync(DisasContext *ctx) #define MEMOP_GET_SIZE(x) (1 << ((x) & MO_SIZE)) -#define LARX(name, memop) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv t0; \ - TCGv gpr = cpu_gpr[rD(ctx->opcode)]; \ - int len = MEMOP_GET_SIZE(memop); \ - gen_set_access_type(ctx, ACCESS_RES); \ - t0 = tcg_temp_local_new(); \ - gen_addr_reg_index(ctx, t0); \ - if ((len) > 1) { \ - gen_check_align(ctx, t0, (len)-1); \ - } \ - tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop); \ - tcg_gen_mov_tl(cpu_reserve, t0); \ - tcg_gen_mov_tl(cpu_reserve_val, gpr); \ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); \ - tcg_temp_free(t0); \ +static void gen_load_locked(DisasContext *ctx, TCGMemOp memop) +{ + TCGv gpr = cpu_gpr[rD(ctx->opcode)]; + TCGv t0 = tcg_temp_new(); + + gen_set_access_type(ctx, ACCESS_RES); + gen_addr_reg_index(ctx, t0); + tcg_gen_qemu_ld_tl(gpr, t0, ctx->mem_idx, memop | MO_ALIGN); + tcg_gen_mov_tl(cpu_reserve, t0); + tcg_gen_mov_tl(cpu_reserve_val, gpr); + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + tcg_temp_free(t0); +} + +#define LARX(name, memop) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + gen_load_locked(ctx, memop); \ } /* lwarx */ @@ -3035,134 +3078,239 @@ LARX(lbarx, DEF_MEMOP(MO_UB)) LARX(lharx, DEF_MEMOP(MO_UW)) LARX(lwarx, DEF_MEMOP(MO_UL)) -#define LD_ATOMIC(name, memop, tp, op, eop) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - int len = MEMOP_GET_SIZE(memop); \ - uint32_t gpr_FC = FC(ctx->opcode); \ - TCGv EA = tcg_temp_local_new(); \ - TCGv_##tp t0, t1; \ - \ - gen_addr_register(ctx, EA); \ - if (len > 1) { \ - gen_check_align(ctx, EA, len - 1); \ - } \ - t0 = tcg_temp_new_##tp(); \ - t1 = tcg_temp_new_##tp(); \ - tcg_gen_##op(t0, cpu_gpr[rD(ctx->opcode) + 1]); \ - \ - switch (gpr_FC) { \ - case 0: /* Fetch and add */ \ - tcg_gen_atomic_fetch_add_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 1: /* Fetch and xor */ \ - tcg_gen_atomic_fetch_xor_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 2: /* Fetch and or */ \ - tcg_gen_atomic_fetch_or_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 3: /* Fetch and 'and' */ \ - tcg_gen_atomic_fetch_and_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 8: /* Swap */ \ - tcg_gen_atomic_xchg_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 4: /* Fetch and max unsigned */ \ - case 5: /* Fetch and max signed */ \ - case 6: /* Fetch and min unsigned */ \ - case 7: /* Fetch and min signed */ \ - case 16: /* compare and swap not equal */ \ - case 24: /* Fetch and increment bounded */ \ - case 25: /* Fetch and increment equal */ \ - case 28: /* Fetch and decrement bounded */ \ - gen_invalid(ctx); \ - break; \ - default: \ - /* invoke data storage error handler */ \ - gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); \ - } \ - tcg_gen_##eop(cpu_gpr[rD(ctx->opcode)], t1); \ - tcg_temp_free_##tp(t0); \ - tcg_temp_free_##tp(t1); \ - tcg_temp_free(EA); \ -} - -LD_ATOMIC(lwat, DEF_MEMOP(MO_UL), i32, trunc_tl_i32, extu_i32_tl) -#if defined(TARGET_PPC64) -LD_ATOMIC(ldat, DEF_MEMOP(MO_Q), i64, mov_i64, mov_i64) -#endif - -#define ST_ATOMIC(name, memop, tp, op) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - int len = MEMOP_GET_SIZE(memop); \ - uint32_t gpr_FC = FC(ctx->opcode); \ - TCGv EA = tcg_temp_local_new(); \ - TCGv_##tp t0, t1; \ - \ - gen_addr_register(ctx, EA); \ - if (len > 1) { \ - gen_check_align(ctx, EA, len - 1); \ - } \ - t0 = tcg_temp_new_##tp(); \ - t1 = tcg_temp_new_##tp(); \ - tcg_gen_##op(t0, cpu_gpr[rD(ctx->opcode) + 1]); \ - \ - switch (gpr_FC) { \ - case 0: /* add and Store */ \ - tcg_gen_atomic_add_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 1: /* xor and Store */ \ - tcg_gen_atomic_xor_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 2: /* Or and Store */ \ - tcg_gen_atomic_or_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 3: /* 'and' and Store */ \ - tcg_gen_atomic_and_fetch_##tp(t1, EA, t0, ctx->mem_idx, memop); \ - break; \ - case 4: /* Store max unsigned */ \ - case 5: /* Store max signed */ \ - case 6: /* Store min unsigned */ \ - case 7: /* Store min signed */ \ - case 24: /* Store twin */ \ - gen_invalid(ctx); \ - break; \ - default: \ - /* invoke data storage error handler */ \ - gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); \ - } \ - tcg_temp_free_##tp(t0); \ - tcg_temp_free_##tp(t1); \ - tcg_temp_free(EA); \ -} - -ST_ATOMIC(stwat, DEF_MEMOP(MO_UL), i32, trunc_tl_i32) -#if defined(TARGET_PPC64) -ST_ATOMIC(stdat, DEF_MEMOP(MO_Q), i64, mov_i64) -#endif - -#if defined(CONFIG_USER_ONLY) -static void gen_conditional_store(DisasContext *ctx, TCGv EA, - int reg, int memop) +static void gen_fetch_inc_conditional(DisasContext *ctx, TCGMemOp memop, + TCGv EA, TCGCond cond, int addend) { - TCGv t0 = tcg_temp_new(); + TCGv t = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv u = tcg_temp_new(); - tcg_gen_st_tl(EA, cpu_env, offsetof(CPUPPCState, reserve_ea)); - tcg_gen_movi_tl(t0, (MEMOP_GET_SIZE(memop) << 5) | reg); - tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, reserve_info)); - tcg_temp_free(t0); - gen_exception_err(ctx, POWERPC_EXCP_STCX, 0); + tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); + tcg_gen_addi_tl(t2, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_qemu_ld_tl(t2, t2, ctx->mem_idx, memop); + tcg_gen_addi_tl(u, t, addend); + + /* E.g. for fetch and increment bounded... */ + /* mem(EA,s) = (t != t2 ? u = t + 1 : t) */ + tcg_gen_movcond_tl(cond, u, t, t2, u, t); + tcg_gen_qemu_st_tl(u, EA, ctx->mem_idx, memop); + + /* RT = (t != t2 ? t : u = 1<<(s*8-1)) */ + tcg_gen_movi_tl(u, 1 << (MEMOP_GET_SIZE(memop) * 8 - 1)); + tcg_gen_movcond_tl(cond, cpu_gpr[rD(ctx->opcode)], t, t2, t, u); + + tcg_temp_free(t); + tcg_temp_free(t2); + tcg_temp_free(u); } -#else -static void gen_conditional_store(DisasContext *ctx, TCGv EA, - int reg, int memop) + +static void gen_ld_atomic(DisasContext *ctx, TCGMemOp memop) +{ + uint32_t gpr_FC = FC(ctx->opcode); + TCGv EA = tcg_temp_new(); + int rt = rD(ctx->opcode); + bool need_serial; + TCGv src, dst; + + gen_addr_register(ctx, EA); + dst = cpu_gpr[rt]; + src = cpu_gpr[(rt + 1) & 31]; + + need_serial = false; + memop |= MO_ALIGN; + switch (gpr_FC) { + case 0: /* Fetch and add */ + tcg_gen_atomic_fetch_add_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 1: /* Fetch and xor */ + tcg_gen_atomic_fetch_xor_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 2: /* Fetch and or */ + tcg_gen_atomic_fetch_or_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 3: /* Fetch and 'and' */ + tcg_gen_atomic_fetch_and_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 4: /* Fetch and max unsigned */ + tcg_gen_atomic_fetch_umax_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 5: /* Fetch and max signed */ + tcg_gen_atomic_fetch_smax_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 6: /* Fetch and min unsigned */ + tcg_gen_atomic_fetch_umin_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 7: /* Fetch and min signed */ + tcg_gen_atomic_fetch_smin_tl(dst, EA, src, ctx->mem_idx, memop); + break; + case 8: /* Swap */ + tcg_gen_atomic_xchg_tl(dst, EA, src, ctx->mem_idx, memop); + break; + + case 16: /* Compare and swap not equal */ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + need_serial = true; + } else { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + tcg_gen_qemu_ld_tl(t0, EA, ctx->mem_idx, memop); + if ((memop & MO_SIZE) == MO_64 || TARGET_LONG_BITS == 32) { + tcg_gen_mov_tl(t1, src); + } else { + tcg_gen_ext32u_tl(t1, src); + } + tcg_gen_movcond_tl(TCG_COND_NE, t1, t0, t1, + cpu_gpr[(rt + 2) & 31], t0); + tcg_gen_qemu_st_tl(t1, EA, ctx->mem_idx, memop); + tcg_gen_mov_tl(dst, t0); + + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + + case 24: /* Fetch and increment bounded */ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + need_serial = true; + } else { + gen_fetch_inc_conditional(ctx, memop, EA, TCG_COND_NE, 1); + } + break; + case 25: /* Fetch and increment equal */ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + need_serial = true; + } else { + gen_fetch_inc_conditional(ctx, memop, EA, TCG_COND_EQ, 1); + } + break; + case 28: /* Fetch and decrement bounded */ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + need_serial = true; + } else { + gen_fetch_inc_conditional(ctx, memop, EA, TCG_COND_NE, -1); + } + break; + + default: + /* invoke data storage error handler */ + gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); + } + tcg_temp_free(EA); + + if (need_serial) { + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + } +} + +static void gen_lwat(DisasContext *ctx) +{ + gen_ld_atomic(ctx, DEF_MEMOP(MO_UL)); +} + +#ifdef TARGET_PPC64 +static void gen_ldat(DisasContext *ctx) +{ + gen_ld_atomic(ctx, DEF_MEMOP(MO_Q)); +} +#endif + +static void gen_st_atomic(DisasContext *ctx, TCGMemOp memop) +{ + uint32_t gpr_FC = FC(ctx->opcode); + TCGv EA = tcg_temp_new(); + TCGv src, discard; + + gen_addr_register(ctx, EA); + src = cpu_gpr[rD(ctx->opcode)]; + discard = tcg_temp_new(); + + memop |= MO_ALIGN; + switch (gpr_FC) { + case 0: /* add and Store */ + tcg_gen_atomic_add_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 1: /* xor and Store */ + tcg_gen_atomic_xor_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 2: /* Or and Store */ + tcg_gen_atomic_or_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 3: /* 'and' and Store */ + tcg_gen_atomic_and_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 4: /* Store max unsigned */ + tcg_gen_atomic_umax_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 5: /* Store max signed */ + tcg_gen_atomic_smax_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 6: /* Store min unsigned */ + tcg_gen_atomic_umin_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 7: /* Store min signed */ + tcg_gen_atomic_smin_fetch_tl(discard, EA, src, ctx->mem_idx, memop); + break; + case 24: /* Store twin */ + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + } else { + TCGv t = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv s = tcg_temp_new(); + TCGv s2 = tcg_temp_new(); + TCGv ea_plus_s = tcg_temp_new(); + + tcg_gen_qemu_ld_tl(t, EA, ctx->mem_idx, memop); + tcg_gen_addi_tl(ea_plus_s, EA, MEMOP_GET_SIZE(memop)); + tcg_gen_qemu_ld_tl(t2, ea_plus_s, ctx->mem_idx, memop); + tcg_gen_movcond_tl(TCG_COND_EQ, s, t, t2, src, t); + tcg_gen_movcond_tl(TCG_COND_EQ, s2, t, t2, src, t2); + tcg_gen_qemu_st_tl(s, EA, ctx->mem_idx, memop); + tcg_gen_qemu_st_tl(s2, ea_plus_s, ctx->mem_idx, memop); + + tcg_temp_free(ea_plus_s); + tcg_temp_free(s2); + tcg_temp_free(s); + tcg_temp_free(t2); + tcg_temp_free(t); + } + break; + default: + /* invoke data storage error handler */ + gen_exception_err(ctx, POWERPC_EXCP_DSI, POWERPC_EXCP_INVAL); + } + tcg_temp_free(discard); + tcg_temp_free(EA); +} + +static void gen_stwat(DisasContext *ctx) +{ + gen_st_atomic(ctx, DEF_MEMOP(MO_UL)); +} + +#ifdef TARGET_PPC64 +static void gen_stdat(DisasContext *ctx) +{ + gen_st_atomic(ctx, DEF_MEMOP(MO_Q)); +} +#endif + +static void gen_conditional_store(DisasContext *ctx, TCGMemOp memop) { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); - TCGv t0; + TCGv t0 = tcg_temp_new(); + int reg = rS(ctx->opcode); - tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); + gen_set_access_type(ctx, ACCESS_RES); + gen_addr_reg_index(ctx, t0); + tcg_gen_brcond_tl(TCG_COND_NE, t0, cpu_reserve, l1); + tcg_temp_free(t0); t0 = tcg_temp_new(); tcg_gen_atomic_cmpxchg_tl(t0, cpu_reserve, cpu_reserve_val, @@ -3185,21 +3333,11 @@ static void gen_conditional_store(DisasContext *ctx, TCGv EA, gen_set_label(l2); tcg_gen_movi_tl(cpu_reserve, -1); } -#endif -#define STCX(name, memop) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - TCGv t0; \ - int len = MEMOP_GET_SIZE(memop); \ - gen_set_access_type(ctx, ACCESS_RES); \ - t0 = tcg_temp_local_new(); \ - gen_addr_reg_index(ctx, t0); \ - if (len > 1) { \ - gen_check_align(ctx, t0, (len) - 1); \ - } \ - gen_conditional_store(ctx, t0, rS(ctx->opcode), memop); \ - tcg_temp_free(t0); \ +#define STCX(name, memop) \ +static void gen_##name(DisasContext *ctx) \ +{ \ + gen_conditional_store(ctx, memop); \ } STCX(stbcx_, DEF_MEMOP(MO_UB)) @@ -3215,9 +3353,8 @@ STCX(stdcx_, DEF_MEMOP(MO_Q)) /* lqarx */ static void gen_lqarx(DisasContext *ctx) { - TCGv EA; int rd = rD(ctx->opcode); - TCGv gpr1, gpr2; + TCGv EA, hi, lo; if (unlikely((rd & 1) || (rd == rA(ctx->opcode)) || (rd == rB(ctx->opcode)))) { @@ -3226,73 +3363,125 @@ static void gen_lqarx(DisasContext *ctx) } gen_set_access_type(ctx, ACCESS_RES); - EA = tcg_temp_local_new(); + EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); - gen_check_align(ctx, EA, 15); - if (unlikely(ctx->le_mode)) { - gpr1 = cpu_gpr[rd+1]; - gpr2 = cpu_gpr[rd]; - } else { - gpr1 = cpu_gpr[rd]; - gpr2 = cpu_gpr[rd+1]; - } - tcg_gen_qemu_ld_i64(gpr1, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); - tcg_gen_mov_tl(cpu_reserve, EA); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_ld_i64(gpr2, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); - tcg_gen_st_tl(gpr1, cpu_env, offsetof(CPUPPCState, reserve_val)); - tcg_gen_st_tl(gpr2, cpu_env, offsetof(CPUPPCState, reserve_val2)); + /* Note that the low part is always in RD+1, even in LE mode. */ + lo = cpu_gpr[rd + 1]; + hi = cpu_gpr[rd]; + + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { +#ifdef CONFIG_ATOMIC128 + TCGv_i32 oi = tcg_temp_new_i32(); + if (ctx->le_mode) { + tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ | MO_ALIGN_16, + ctx->mem_idx)); + gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); + } else { + tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ | MO_ALIGN_16, + ctx->mem_idx)); + gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); + } + tcg_temp_free_i32(oi); + tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); +#else + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + tcg_temp_free(EA); + return; +#endif + } else if (ctx->le_mode) { + tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ | MO_ALIGN_16); + tcg_gen_mov_tl(cpu_reserve, EA); + gen_addr_add(ctx, EA, EA, 8); + tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_LEQ); + } else { + tcg_gen_qemu_ld_i64(hi, EA, ctx->mem_idx, MO_BEQ | MO_ALIGN_16); + tcg_gen_mov_tl(cpu_reserve, EA); + gen_addr_add(ctx, EA, EA, 8); + tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_BEQ); + } tcg_temp_free(EA); + + tcg_gen_st_tl(hi, cpu_env, offsetof(CPUPPCState, reserve_val)); + tcg_gen_st_tl(lo, cpu_env, offsetof(CPUPPCState, reserve_val2)); } /* stqcx. */ static void gen_stqcx_(DisasContext *ctx) { - TCGv EA; - int reg = rS(ctx->opcode); - int len = 16; -#if !defined(CONFIG_USER_ONLY) - TCGLabel *l1; - TCGv gpr1, gpr2; -#endif + int rs = rS(ctx->opcode); + TCGv EA, hi, lo; - if (unlikely((rD(ctx->opcode) & 1))) { + if (unlikely(rs & 1)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } + gen_set_access_type(ctx, ACCESS_RES); - EA = tcg_temp_local_new(); + EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); - if (len > 1) { - gen_check_align(ctx, EA, (len) - 1); - } -#if defined(CONFIG_USER_ONLY) - gen_conditional_store(ctx, EA, reg, 16); + /* Note that the low part is always in RS+1, even in LE mode. */ + lo = cpu_gpr[rs + 1]; + hi = cpu_gpr[rs]; + + if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { + TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_Q) | MO_ALIGN_16); +#ifdef CONFIG_ATOMIC128 + if (ctx->le_mode) { + gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi); + } else { + gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi); + } #else - tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); - l1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, l1); - tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); - - if (unlikely(ctx->le_mode)) { - gpr1 = cpu_gpr[reg + 1]; - gpr2 = cpu_gpr[reg]; - } else { - gpr1 = cpu_gpr[reg]; - gpr2 = cpu_gpr[reg + 1]; - } - tcg_gen_qemu_st_tl(gpr1, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); - gen_addr_add(ctx, EA, EA, 8); - tcg_gen_qemu_st_tl(gpr2, EA, ctx->mem_idx, DEF_MEMOP(MO_Q)); - - gen_set_label(l1); - tcg_gen_movi_tl(cpu_reserve, -1); + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; #endif - tcg_temp_free(EA); -} + tcg_temp_free(EA); + tcg_temp_free_i32(oi); + } else { + TCGLabel *lab_fail = gen_new_label(); + TCGLabel *lab_over = gen_new_label(); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + tcg_gen_brcond_tl(TCG_COND_NE, EA, cpu_reserve, lab_fail); + tcg_temp_free(EA); + + gen_qemu_ld64_i64(ctx, t0, cpu_reserve); + tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode + ? offsetof(CPUPPCState, reserve_val2) + : offsetof(CPUPPCState, reserve_val))); + tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail); + + tcg_gen_addi_i64(t0, cpu_reserve, 8); + gen_qemu_ld64_i64(ctx, t0, t0); + tcg_gen_ld_i64(t1, cpu_env, (ctx->le_mode + ? offsetof(CPUPPCState, reserve_val) + : offsetof(CPUPPCState, reserve_val2))); + tcg_gen_brcond_i64(TCG_COND_NE, t0, t1, lab_fail); + + /* Success */ + gen_qemu_st64_i64(ctx, ctx->le_mode ? lo : hi, cpu_reserve); + tcg_gen_addi_i64(t0, cpu_reserve, 8); + gen_qemu_st64_i64(ctx, ctx->le_mode ? hi : lo, t0); + + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + tcg_gen_ori_i32(cpu_crf[0], cpu_crf[0], CRF_EQ); + tcg_gen_br(lab_over); + + gen_set_label(lab_fail); + tcg_gen_trunc_tl_i32(cpu_crf[0], cpu_so); + + gen_set_label(lab_over); + tcg_gen_movi_tl(cpu_reserve, -1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } +} #endif /* defined(TARGET_PPC64) */ /* sync */ @@ -3422,7 +3611,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_nip, dest & ~3); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + n); + tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_tl(cpu_nip, dest & ~3); if (unlikely(ctx->singlestep_enabled)) { @@ -3933,13 +4122,9 @@ static inline void gen_op_mfspr(DisasContext *ctx) * allowing userland application to read the PVR */ if (sprn != SPR_PVR) { - fprintf(stderr, "Trying to read privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - if (qemu_log_separate()) { - qemu_log("Trying to read privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, - ctx->base.pc_next - 4); - } + qemu_log_mask(LOG_GUEST_ERROR, "Trying to read privileged spr " + "%d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, + ctx->base.pc_next - 4); } gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } @@ -3951,12 +4136,9 @@ static inline void gen_op_mfspr(DisasContext *ctx) return; } /* Not defined */ - fprintf(stderr, "Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - if (qemu_log_separate()) { - qemu_log("Trying to read invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - } + qemu_log_mask(LOG_GUEST_ERROR, + "Trying to read invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); /* The behaviour depends on MSR:PR and SPR# bit 0x10, * it can generate a priv, a hv emu or a no-op @@ -4097,12 +4279,9 @@ static void gen_mtspr(DisasContext *ctx) (*write_cb)(ctx, sprn, rS(ctx->opcode)); } else { /* Privilege exception */ - fprintf(stderr, "Trying to write privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - if (qemu_log_separate()) { - qemu_log("Trying to write privileged spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - } + qemu_log_mask(LOG_GUEST_ERROR, "Trying to write privileged spr " + "%d (0x%03x) at " TARGET_FMT_lx "\n", sprn, sprn, + ctx->base.pc_next - 4); gen_priv_exception(ctx, POWERPC_EXCP_PRIV_REG); } } else { @@ -4114,12 +4293,9 @@ static void gen_mtspr(DisasContext *ctx) } /* Not defined */ - if (qemu_log_separate()) { - qemu_log("Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); - } - fprintf(stderr, "Trying to write invalid spr %d (0x%03x) at " - TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); + qemu_log_mask(LOG_GUEST_ERROR, + "Trying to write invalid spr %d (0x%03x) at " + TARGET_FMT_lx "\n", sprn, sprn, ctx->base.pc_next - 4); /* The behaviour depends on MSR:PR and SPR# bit 0x10, @@ -4628,8 +4804,8 @@ static void gen_eciwx(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_EXT); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x03); - gen_qemu_ld32u(ctx, cpu_gpr[rD(ctx->opcode)], t0); + tcg_gen_qemu_ld_tl(cpu_gpr[rD(ctx->opcode)], t0, ctx->mem_idx, + DEF_MEMOP(MO_UL | MO_ALIGN)); tcg_temp_free(t0); } @@ -4641,8 +4817,8 @@ static void gen_ecowx(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_EXT); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_check_align(ctx, t0, 0x03); - gen_qemu_st32(ctx, cpu_gpr[rD(ctx->opcode)], t0); + tcg_gen_qemu_st_tl(cpu_gpr[rD(ctx->opcode)], t0, ctx->mem_idx, + DEF_MEMOP(MO_UL | MO_ALIGN)); tcg_temp_free(t0); } @@ -6496,7 +6672,7 @@ GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_STRING), GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_STRING), GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_STRING), GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING), -GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FFF801, PPC_MEM_EIEIO), +GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x01FFF801, PPC_MEM_EIEIO), GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM), GEN_HANDLER_E(lbarx, 0x1F, 0x14, 0x01, 0, PPC_NONE, PPC2_ATOMIC_ISA206), GEN_HANDLER_E(lharx, 0x1F, 0x14, 0x03, 0, PPC_NONE, PPC2_ATOMIC_ISA206), @@ -6561,7 +6737,7 @@ GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x00000001, PPC_CACHE), GEN_HANDLER_E(dcbtls, 0x1F, 0x06, 0x05, 0x02000001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ), GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC), -GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x02000001, PPC_ALTIVEC), +GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x01800001, PPC_ALTIVEC), GEN_HANDLER(dss, 0x1F, 0x16, 0x19, 0x019FF801, PPC_ALTIVEC), GEN_HANDLER(icbi, 0x1F, 0x16, 0x1E, 0x03E00001, PPC_CACHE_ICBI), GEN_HANDLER(dcba, 0x1F, 0x16, 0x17, 0x03E00001, PPC_CACHE_DCBA), @@ -6699,6 +6875,8 @@ GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, GEN_HANDLER(msync_4xx, 0x1F, 0x16, 0x12, 0x03FFF801, PPC_BOOKE), GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE, PPC2_BOOKE206), +GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, + PPC_440_SPEC), GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), @@ -6876,7 +7054,7 @@ GEN_HANDLER(stop##u, opc, 0xFF, 0xFF, 0x00000000, type), #define GEN_STUX(name, stop, opc2, opc3, type) \ GEN_HANDLER(name##ux, 0x1F, opc2, opc3, 0x00000001, type), #define GEN_STX_E(name, stop, opc2, opc3, type, type2, chk) \ -GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000001, type, type2), +GEN_HANDLER_E(name##x, 0x1F, opc2, opc3, 0x00000000, type, type2), #define GEN_STS(name, stop, op, type) \ GEN_ST(name, stop, op | 0x20, type) \ GEN_STU(name, stop, op | 0x21, type) \ @@ -6991,7 +7169,7 @@ GEN_HANDLER2_E(trechkpt, "trechkpt", 0x1F, 0x0E, 0x1F, 0x03FFF800, \ }; #include "helper_regs.h" -#include "translate_init.c" +#include "translate_init.inc.c" /*****************************************************************************/ /* Misc PowerPC helpers */ @@ -7048,14 +7226,20 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, } cpu_fprintf(f, " ] RES " TARGET_FMT_lx "\n", env->reserve_addr); - for (i = 0; i < 32; i++) { - if ((i & (RFPL - 1)) == 0) - cpu_fprintf(f, "FPR%02d", i); - cpu_fprintf(f, " %016" PRIx64, *((uint64_t *)&env->fpr[i])); - if ((i & (RFPL - 1)) == (RFPL - 1)) - cpu_fprintf(f, "\n"); + + if (flags & CPU_DUMP_FPU) { + for (i = 0; i < 32; i++) { + if ((i & (RFPL - 1)) == 0) { + cpu_fprintf(f, "FPR%02d", i); + } + cpu_fprintf(f, " %016" PRIx64, *((uint64_t *)&env->fpr[i])); + if ((i & (RFPL - 1)) == (RFPL - 1)) { + cpu_fprintf(f, "\n"); + } + } + cpu_fprintf(f, "FPSCR " TARGET_FMT_lx "\n", env->fpscr); } - cpu_fprintf(f, "FPSCR " TARGET_FMT_lx "\n", env->fpscr); + #if !defined(CONFIG_USER_ONLY) cpu_fprintf(f, " SRR0 " TARGET_FMT_lx " SRR1 " TARGET_FMT_lx " PVR " TARGET_FMT_lx " VRSAVE " TARGET_FMT_lx "\n", @@ -7121,21 +7305,24 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, if (env->spr_cb[SPR_LPCR].name) cpu_fprintf(f, " LPCR " TARGET_FMT_lx "\n", env->spr[SPR_LPCR]); - switch (POWERPC_MMU_VER(env->mmu_model)) { + switch (env->mmu_model) { case POWERPC_MMU_32B: case POWERPC_MMU_601: case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: #if defined(TARGET_PPC64) - case POWERPC_MMU_VER_64B: - case POWERPC_MMU_VER_2_03: - case POWERPC_MMU_VER_2_06: - case POWERPC_MMU_VER_2_07: - case POWERPC_MMU_VER_3_00: + case POWERPC_MMU_64B: + case POWERPC_MMU_2_03: + case POWERPC_MMU_2_06: + case POWERPC_MMU_2_07: + case POWERPC_MMU_3_00: #endif if (env->spr_cb[SPR_SDR1].name) { /* SDR1 Exists */ cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " ", env->spr[SPR_SDR1]); } + if (env->spr_cb[SPR_PTCR].name) { /* PTCR Exists */ + cpu_fprintf(f, " PTCR " TARGET_FMT_lx " ", env->spr[SPR_PTCR]); + } cpu_fprintf(f, " DAR " TARGET_FMT_lx " DSISR " TARGET_FMT_lx "\n", env->spr[SPR_DAR], env->spr[SPR_DSISR]); break; @@ -7212,8 +7399,7 @@ void ppc_cpu_dump_statistics(CPUState *cs, FILE*f, #endif } -static int ppc_tr_init_disas_context(DisasContextBase *dcbase, - CPUState *cs, int max_insns) +static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUPPCState *env = cs->env_ptr; @@ -7278,7 +7464,7 @@ static int ppc_tr_init_disas_context(DisasContextBase *dcbase, #endif bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; - return MIN(max_insns, bound); + ctx->base.max_insns = MIN(ctx->base.max_insns, bound); } static void ppc_tr_tb_start(DisasContextBase *db, CPUState *cs) @@ -7296,6 +7482,7 @@ static bool ppc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, DisasContext *ctx = container_of(dcbase, DisasContext, base); gen_debug_exception(ctx); + dcbase->is_jmp = DISAS_NORETURN; /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that @@ -7402,7 +7589,7 @@ static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) gen_debug_exception(ctx); } /* Generate the return instruction */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.inc.c similarity index 98% rename from target/ppc/translate_init.c rename to target/ppc/translate_init.inc.c index 391b94b97d..7813b1b004 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.inc.c @@ -18,7 +18,6 @@ * License along with this library; if not, see . */ -#include "qemu/osdep.h" #include "disas/bfd.h" #include "exec/gdbstub.h" #include "kvm_ppc.h" @@ -420,6 +419,15 @@ static void spr_write_hior(DisasContext *ctx, int sprn, int gprn) tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, excp_prefix)); tcg_temp_free(t0); } +static void spr_write_ptcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_ptcr(cpu_env, cpu_gpr[gprn]); +} + +static void spr_write_pcr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_pcr(cpu_env, cpu_gpr[gprn]); +} #endif #endif @@ -7811,7 +7819,7 @@ static void gen_spr_book3s_ids(CPUPPCState *env) /* Processor identification */ spr_register_hv(env, SPR_PIR, "PIR", SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, SPR_NOACCESS, &spr_read_generic, NULL, 0x00000000); spr_register_hv(env, SPR_HID0, "HID0", @@ -7953,11 +7961,12 @@ static void gen_spr_power6_common(CPUPPCState *env) #endif /* * Register PCR to report POWERPC_EXCP_PRIV_REG instead of - * POWERPC_EXCP_INVAL_SPR. + * POWERPC_EXCP_INVAL_SPR in userspace. Permit hypervisor access. */ - spr_register(env, SPR_PCR, "PCR", + spr_register_hv(env, SPR_PCR, "PCR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_pcr, 0x00000000); } @@ -8167,6 +8176,18 @@ static void gen_spr_power8_rpr(CPUPPCState *env) #endif } +static void gen_spr_power9_mmu(CPUPPCState *env) +{ +#if !defined(CONFIG_USER_ONLY) + /* Partition Table Control */ + spr_register_hv(env, SPR_PTCR, "PTCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_ptcr, + 0x00000000); +#endif +} + static void init_proc_book3s_common(CPUPPCState *env) { gen_spr_ne_601(env); @@ -8195,9 +8216,6 @@ static void init_proc_970(CPUPPCState *env) gen_spr_970_dbg(env); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 64; -#endif env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8242,6 +8260,7 @@ POWERPC_FAMILY(970)(ObjectClass *oc, void *data) pcc->mmu_model = POWERPC_MMU_64B; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; + pcc->hash64_opts = &ppc_hash64_opts_basic; #endif pcc->excp_model = POWERPC_EXCP_970; pcc->bus_model = PPC_FLAGS_INPUT_970; @@ -8271,9 +8290,6 @@ static void init_proc_power5plus(CPUPPCState *env) gen_spr_power5p_ear(env); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 64; -#endif env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8319,6 +8335,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) pcc->mmu_model = POWERPC_MMU_2_03; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; + pcc->hash64_opts = &ppc_hash64_opts_basic; #endif pcc->excp_model = POWERPC_EXCP_970; pcc->bus_model = PPC_FLAGS_INPUT_970; @@ -8351,7 +8368,7 @@ static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name, "use max-cpu-compat machine property instead"); } visit_type_null(v, name, &null, NULL); - QDECREF(null); + qobject_unref(null); } static const PropertyInfo ppc_compat_deprecated_propinfo = { @@ -8368,36 +8385,6 @@ static Property powerpc_servercpu_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -#ifdef CONFIG_SOFTMMU -static const struct ppc_segment_page_sizes POWER7_POWER8_sps = { - .sps = { - { - .page_shift = 12, /* 4K */ - .slb_enc = 0, - .enc = { { .page_shift = 12, .pte_enc = 0 }, - { .page_shift = 16, .pte_enc = 0x7 }, - { .page_shift = 24, .pte_enc = 0x38 }, }, - }, - { - .page_shift = 16, /* 64K */ - .slb_enc = SLB_VSID_64K, - .enc = { { .page_shift = 16, .pte_enc = 0x1 }, - { .page_shift = 24, .pte_enc = 0x8 }, }, - }, - { - .page_shift = 24, /* 16M */ - .slb_enc = SLB_VSID_16M, - .enc = { { .page_shift = 24, .pte_enc = 0 }, }, - }, - { - .page_shift = 34, /* 16G */ - .slb_enc = SLB_VSID_16G, - .enc = { { .page_shift = 34, .pte_enc = 0x3 }, }, - }, - } -}; -#endif /* CONFIG_SOFTMMU */ - static void init_proc_POWER7(CPUPPCState *env) { /* Common Registers */ @@ -8417,10 +8404,6 @@ static void init_proc_POWER7(CPUPPCState *env) gen_spr_power7_book4(env); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 32; -#endif - env->ci_large_pages = true; env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8526,7 +8509,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) pcc->mmu_model = POWERPC_MMU_2_06; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; - pcc->sps = &POWER7_POWER8_sps; + pcc->hash64_opts = &ppc_hash64_opts_POWER7; #endif pcc->excp_model = POWERPC_EXCP_POWER7; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; @@ -8572,10 +8555,6 @@ static void init_proc_POWER8(CPUPPCState *env) gen_spr_power8_rpr(env); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 32; -#endif - env->ci_large_pages = true; env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8698,7 +8677,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) pcc->mmu_model = POWERPC_MMU_2_07; #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; - pcc->sps = &POWER7_POWER8_sps; + pcc->hash64_opts = &ppc_hash64_opts_POWER7; #endif pcc->excp_model = POWERPC_EXCP_POWER8; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; @@ -8761,6 +8740,7 @@ static void init_proc_POWER9(CPUPPCState *env) gen_spr_power8_ic(env); gen_spr_power8_book4(env); gen_spr_power8_rpr(env); + gen_spr_power9_mmu(env); /* POWER9 Specific registers */ spr_register_kvm(env, SPR_TIDR, "TIDR", NULL, NULL, @@ -8773,10 +8753,6 @@ static void init_proc_POWER9(CPUPPCState *env) KVM_REG_PPC_PSSCR, 0); /* env variables */ -#if !defined(CONFIG_USER_ONLY) - env->slb_nr = 32; -#endif - env->ci_large_pages = true; env->dcache_line_size = 128; env->icache_line_size = 128; @@ -8893,7 +8869,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc64_v3_handle_mmu_fault; /* segment page size remain the same */ - pcc->sps = &POWER7_POWER8_sps; + pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->radix_page_info = &POWER9_radix_page_info; #endif pcc->excp_model = POWERPC_EXCP_POWER8; @@ -8910,78 +8886,17 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) } #if !defined(CONFIG_USER_ONLY) -void cpu_ppc_set_papr(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) +void cpu_ppc_set_vhyp(PowerPCCPU *cpu, PPCVirtualHypervisor *vhyp) { - PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); CPUPPCState *env = &cpu->env; - ppc_spr_t *lpcr = &env->spr_cb[SPR_LPCR]; - ppc_spr_t *amor = &env->spr_cb[SPR_AMOR]; - CPUState *cs = CPU(cpu); cpu->vhyp = vhyp; - /* PAPR always has exception vectors in RAM not ROM. To ensure this, - * MSR[IP] should never be set. - * - * We also disallow setting of MSR_HV + /* + * With a virtual hypervisor mode we never allow the CPU to go + * hypervisor mode itself */ - env->msr_mask &= ~((1ull << MSR_EP) | MSR_HVB); - - /* Set emulated LPCR to not send interrupts to hypervisor. Note that - * under KVM, the actual HW LPCR will be set differently by KVM itself, - * the settings below ensure proper operations with TCG in absence of - * a real hypervisor. - * - * Clearing VPM0 will also cause us to use RMOR in mmu-hash64.c for - * real mode accesses, which thankfully defaults to 0 and isn't - * accessible in guest mode. - */ - lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV); - lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1; - - /* Set RMLS to the max (ie, 16G) */ - lpcr->default_value &= ~LPCR_RMLS; - lpcr->default_value |= 1ull << LPCR_RMLS_SHIFT; - - if (env->mmu_model == POWERPC_MMU_3_00) { - /* By default we choose legacy mode and switch to new hash or radix - * when a register process table hcall is made. So disable process - * tables and guest translation shootdown by default - * - * Hot-plugged CPUs inherit from the guest radix setting under - * KVM but not under TCG. Update the default LPCR to keep new - * CPUs in sync when radix is enabled. - */ - if (ppc64_radix_guest(cpu)) { - lpcr->default_value |= LPCR_UPRT | LPCR_GTSE; - } else { - lpcr->default_value &= ~(LPCR_UPRT | LPCR_GTSE); - } - } - - /* Only enable Power-saving mode Exit Cause exceptions on the boot - * CPU. The RTAS command start-cpu will enable them on secondaries. - */ - if (cs == first_cpu) { - lpcr->default_value |= pcc->lpcr_pm; - } - - /* We should be followed by a CPU reset but update the active value - * just in case... - */ - env->spr[SPR_LPCR] = lpcr->default_value; - - /* Set a full AMOR so guest can use the AMR as it sees fit */ - env->spr[SPR_AMOR] = amor->default_value = 0xffffffffffffffffull; - - /* Update some env bits based on new LPCR value */ - ppc_hash64_update_rmls(env); - ppc_hash64_update_vrma(env); - - /* Tell KVM that we're in PAPR mode */ - if (kvm_enabled()) { - kvmppc_set_papr(cpu); - } + env->msr_mask &= ~MSR_HVB; } #endif /* !defined(CONFIG_USER_ONLY) */ @@ -9726,7 +9641,7 @@ static inline bool ppc_cpu_is_valid(PowerPCCPUClass *pcc) #endif } -static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) +static void ppc_cpu_realize(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); PowerPCCPU *cpu = POWERPC_CPU(dev); @@ -9749,14 +9664,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) } } -#if defined(TARGET_PPCEMB) - if (!ppc_cpu_is_valid(pcc)) { - error_setg(errp, "CPU does not possess a BookE or 4xx MMU. " - "Please use qemu-system-ppc or qemu-system-ppc64 instead " - "or choose another CPU model."); - goto unrealize; - } -#endif + assert(ppc_cpu_is_valid(pcc)); create_ppc_opcodes(cpu, &local_err); if (local_err != NULL) { @@ -9952,7 +9860,7 @@ unrealize: cpu_exec_unrealizefn(cs); } -static void ppc_cpu_unrealizefn(DeviceState *dev, Error **errp) +static void ppc_cpu_unrealize(DeviceState *dev, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(dev); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); @@ -10408,14 +10316,6 @@ static void ppc_cpu_reset(CPUState *s) s->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; -#if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY) - env->vpa_addr = 0; - env->slb_shadow_addr = 0; - env->slb_shadow_size = 0; - env->dtl_addr = 0; - env->dtl_size = 0; -#endif /* TARGET_PPC64 */ - for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { ppc_spr_t *spr = &env->spr_cb[i]; @@ -10438,7 +10338,7 @@ static bool ppc_cpu_is_big_endian(CPUState *cs) } #endif -static void ppc_cpu_initfn(Object *obj) +static void ppc_cpu_instance_init(Object *obj) { CPUState *cs = CPU(obj); PowerPCCPU *cpu = POWERPC_CPU(obj); @@ -10471,42 +10371,14 @@ static void ppc_cpu_initfn(Object *obj) env->has_hv_mode = !!(env->msr_mask & MSR_HVB); #endif -#if defined(TARGET_PPC64) - if (pcc->sps) { - env->sps = *pcc->sps; - } else if (env->mmu_model & POWERPC_MMU_64) { - /* Use default sets of page sizes. We don't support MPSS */ - static const struct ppc_segment_page_sizes defsps_4k = { - .sps = { - { .page_shift = 12, /* 4K */ - .slb_enc = 0, - .enc = { { .page_shift = 12, .pte_enc = 0 } } - }, - { .page_shift = 24, /* 16M */ - .slb_enc = 0x100, - .enc = { { .page_shift = 24, .pte_enc = 0 } } - }, - }, - }; - static const struct ppc_segment_page_sizes defsps_64k = { - .sps = { - { .page_shift = 12, /* 4K */ - .slb_enc = 0, - .enc = { { .page_shift = 12, .pte_enc = 0 } } - }, - { .page_shift = 16, /* 64K */ - .slb_enc = 0x110, - .enc = { { .page_shift = 16, .pte_enc = 1 } } - }, - { .page_shift = 24, /* 16M */ - .slb_enc = 0x100, - .enc = { { .page_shift = 24, .pte_enc = 0 } } - }, - }, - }; - env->sps = (env->mmu_model & POWERPC_MMU_64K) ? defsps_64k : defsps_4k; - } -#endif /* defined(TARGET_PPC64) */ + ppc_hash64_init(cpu); +} + +static void ppc_cpu_instance_finalize(Object *obj) +{ + PowerPCCPU *cpu = POWERPC_CPU(obj); + + ppc_hash64_finalize(cpu); } static bool ppc_pvr_match_default(PowerPCCPUClass *pcc, uint32_t pvr) @@ -10552,6 +10424,8 @@ static Property ppc_cpu_properties[] = { DEFINE_PROP_BOOL("pre-2.8-migration", PowerPCCPU, pre_2_8_migration, false), DEFINE_PROP_BOOL("pre-2.10-migration", PowerPCCPU, pre_2_10_migration, false), + DEFINE_PROP_BOOL("pre-3.0-migration", PowerPCCPU, pre_3_0_migration, + false), DEFINE_PROP_END_OF_LIST(), }; @@ -10561,9 +10435,9 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); - device_class_set_parent_realize(dc, ppc_cpu_realizefn, + device_class_set_parent_realize(dc, ppc_cpu_realize, &pcc->parent_realize); - device_class_set_parent_unrealize(dc, ppc_cpu_unrealizefn, + device_class_set_parent_unrealize(dc, ppc_cpu_unrealize, &pcc->parent_unrealize); pcc->pvr_match = ppc_pvr_match_default; pcc->interrupts_big_endian = ppc_cpu_interrupts_big_endian_always; @@ -10583,6 +10457,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) cc->set_pc = ppc_cpu_set_pc; cc->gdb_read_register = ppc_cpu_gdb_read_register; cc->gdb_write_register = ppc_cpu_gdb_write_register; + cc->do_unaligned_access = ppc_cpu_do_unaligned_access; #ifdef CONFIG_USER_ONLY cc->handle_mmu_fault = ppc_cpu_handle_mmu_fault; #else @@ -10623,7 +10498,8 @@ static const TypeInfo ppc_cpu_type_info = { .name = TYPE_POWERPC_CPU, .parent = TYPE_CPU, .instance_size = sizeof(PowerPCCPU), - .instance_init = ppc_cpu_initfn, + .instance_init = ppc_cpu_instance_init, + .instance_finalize = ppc_cpu_instance_finalize, .abstract = true, .class_size = sizeof(PowerPCCPUClass), .class_init = ppc_cpu_class_init, diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5a527fbba0..d630e8fd6c 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -26,7 +26,7 @@ /* RISC-V CPU definitions */ -static const char riscv_exts[26] = "IMAFDQECLBJTPVNSUHKORWXYZG"; +static const char riscv_exts[26] = "IEMAFDQCLBJTPVNSUHKORWXYZG"; const char * const riscv_int_regnames[] = { "zero", "ra ", "sp ", "gp ", "tp ", "t0 ", "t1 ", "t2 ", @@ -219,11 +219,13 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, cpu_fprintf(f, "\n"); } } - for (i = 0; i < 32; i++) { - cpu_fprintf(f, " %s %016" PRIx64, - riscv_fpr_regnames[i], env->fpr[i]); - if ((i & 3) == 3) { - cpu_fprintf(f, "\n"); + if (flags & CPU_DUMP_FPU) { + for (i = 0; i < 32; i++) { + cpu_fprintf(f, " %s %016" PRIx64, + riscv_fpr_regnames[i], env->fpr[i]); + if ((i & 3) == 3) { + cpu_fprintf(f, "\n"); + } } } } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 41e06ac0f9..34abc383e3 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -34,7 +34,6 @@ #define TCG_GUEST_DEFAULT_MO 0 -#define ELF_MACHINE EM_RISCV #define CPUArchState struct CPURISCVState #include "qemu-common.h" @@ -72,6 +71,7 @@ #define RV(x) ((target_ulong)1 << (x - 'A')) #define RVI RV('I') +#define RVE RV('E') /* E and I are mutually exclusive */ #define RVM RV('M') #define RVA RV('A') #define RVF RV('F') @@ -151,10 +151,8 @@ struct CPURISCVState { target_ulong mcause; target_ulong mtval; /* since: priv-1.10.0 */ - uint32_t mucounteren; - uint32_t mscounteren; - target_ulong scounteren; /* since: priv-1.10.0 */ - target_ulong mcounteren; /* since: priv-1.10.0 */ + target_ulong scounteren; + target_ulong mcounteren; target_ulong sscratch; target_ulong mscratch; diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index abbadead5c..fdb87d8d82 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -279,14 +279,12 @@ uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1) { - rs1 = float64_to_float32(rs1, &env->fp_status); - return float32_maybe_silence_nan(rs1, &env->fp_status); + return float64_to_float32(rs1, &env->fp_status); } uint64_t helper_fcvt_d_s(CPURISCVState *env, uint64_t rs1) { - rs1 = float32_to_float64(rs1, &env->fp_status); - return float64_maybe_silence_nan(rs1, &env->fp_status); + return float32_to_float64(rs1, &env->fp_status); } uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1) diff --git a/target/riscv/helper.c b/target/riscv/helper.c index 02cbcea2b7..29e1a603dc 100644 --- a/target/riscv/helper.c +++ b/target/riscv/helper.c @@ -210,7 +210,7 @@ restart: MemoryRegion *mr; hwaddr l = sizeof(target_ulong), addr1; mr = address_space_translate(cs->as, pte_addr, - &addr1, &l, false); + &addr1, &l, false, MEMTXATTRS_UNSPECIFIED); if (memory_access_is_direct(mr, true)) { target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); @@ -466,6 +466,10 @@ void riscv_cpu_do_interrupt(CPUState *cs) ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); } env->sbadaddr = env->badaddr; + } else { + /* otherwise we must clear sbadaddr/stval + * todo: support populating stval on illegal instructions */ + env->sbadaddr = 0; } target_ulong s = env->mstatus; @@ -487,6 +491,10 @@ void riscv_cpu_do_interrupt(CPUState *cs) ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); } env->mbadaddr = env->badaddr; + } else { + /* otherwise we must clear mbadaddr/mtval + * todo: support populating mtval on illegal instructions */ + env->mbadaddr = 0; } target_ulong s = env->mstatus; diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 7c6068bac9..aec7558e1b 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -213,28 +213,41 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, break; } case CSR_MINSTRET: - qemu_log_mask(LOG_UNIMP, "CSR_MINSTRET: write not implemented"); - goto do_illegal; + /* minstret is WARL so unsupported writes are ignored */ + break; case CSR_MCYCLE: - qemu_log_mask(LOG_UNIMP, "CSR_MCYCLE: write not implemented"); - goto do_illegal; + /* mcycle is WARL so unsupported writes are ignored */ + break; +#if defined(TARGET_RISCV32) case CSR_MINSTRETH: - qemu_log_mask(LOG_UNIMP, "CSR_MINSTRETH: write not implemented"); - goto do_illegal; + /* minstreth is WARL so unsupported writes are ignored */ + break; case CSR_MCYCLEH: - qemu_log_mask(LOG_UNIMP, "CSR_MCYCLEH: write not implemented"); - goto do_illegal; + /* mcycleh is WARL so unsupported writes are ignored */ + break; +#endif case CSR_MUCOUNTEREN: - env->mucounteren = val_to_write; - break; + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + env->scounteren = val_to_write; + break; + } else { + goto do_illegal; + } case CSR_MSCOUNTEREN: - env->mscounteren = val_to_write; - break; + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + env->mcounteren = val_to_write; + break; + } else { + goto do_illegal; + } case CSR_SSTATUS: { target_ulong ms = env->mstatus; target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS - | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD; + | SSTATUS_SUM | SSTATUS_SD; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + mask |= SSTATUS_MXR; + } ms = (ms & ~mask) | (val_to_write & mask); csr_write_helper(env, ms, CSR_MSTATUS); break; @@ -255,7 +268,7 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, } case CSR_SATP: /* CSR_SPTBR */ { if (!riscv_feature(env, RISCV_FEATURE_MMU)) { - goto do_illegal; + break; } if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val_to_write ^ env->sptbr)) { @@ -276,15 +289,21 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, env->sepc = val_to_write; break; case CSR_STVEC: - if (val_to_write & 1) { - qemu_log_mask(LOG_UNIMP, "CSR_STVEC: vectored traps not supported"); - goto do_illegal; + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val_to_write & 3) == 0) { + env->stvec = val_to_write >> 2 << 2; + } else { + qemu_log_mask(LOG_UNIMP, + "CSR_STVEC: vectored traps not supported\n"); } - env->stvec = val_to_write >> 2 << 2; break; case CSR_SCOUNTEREN: - env->scounteren = val_to_write; - break; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + env->scounteren = val_to_write; + break; + } else { + goto do_illegal; + } case CSR_SSCRATCH: env->sscratch = val_to_write; break; @@ -298,15 +317,21 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, env->mepc = val_to_write; break; case CSR_MTVEC: - if (val_to_write & 1) { - qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: vectored traps not supported"); - goto do_illegal; + /* bits [1:0] indicate mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val_to_write & 3) == 0) { + env->mtvec = val_to_write >> 2 << 2; + } else { + qemu_log_mask(LOG_UNIMP, + "CSR_MTVEC: vectored traps not supported\n"); } - env->mtvec = val_to_write >> 2 << 2; break; case CSR_MCOUNTEREN: - env->mcounteren = val_to_write; - break; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + env->mcounteren = val_to_write; + break; + } else { + goto do_illegal; + } case CSR_MSCRATCH: env->mscratch = val_to_write; break; @@ -316,10 +341,9 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, case CSR_MBADADDR: env->mbadaddr = val_to_write; break; - case CSR_MISA: { - qemu_log_mask(LOG_UNIMP, "CSR_MISA: misa writes not supported"); - goto do_illegal; - } + case CSR_MISA: + /* misa is WARL so unsupported writes are ignored */ + break; case CSR_PMPCFG0: case CSR_PMPCFG1: case CSR_PMPCFG2: @@ -344,6 +368,8 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, case CSR_PMPADDR15: pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val_to_write); break; +#endif +#if !defined(CONFIG_USER_ONLY) do_illegal: #endif default: @@ -359,8 +385,8 @@ void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) { #ifndef CONFIG_USER_ONLY - target_ulong ctr_en = env->priv == PRV_U ? env->mucounteren : - env->priv == PRV_S ? env->mscounteren : -1U; + target_ulong ctr_en = env->priv == PRV_U ? env->scounteren : + env->priv == PRV_S ? env->mcounteren : -1U; #else target_ulong ctr_en = -1; #endif @@ -413,35 +439,67 @@ target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) case CSR_INSTRET: case CSR_CYCLE: if (ctr_ok) { +#if !defined(CONFIG_USER_ONLY) + if (use_icount) { + return cpu_get_icount(); + } else { + return cpu_get_host_ticks(); + } +#else return cpu_get_host_ticks(); +#endif } break; #if defined(TARGET_RISCV32) case CSR_INSTRETH: case CSR_CYCLEH: if (ctr_ok) { +#if !defined(CONFIG_USER_ONLY) + if (use_icount) { + return cpu_get_icount() >> 32; + } else { + return cpu_get_host_ticks() >> 32; + } +#else return cpu_get_host_ticks() >> 32; +#endif } break; #endif #ifndef CONFIG_USER_ONLY case CSR_MINSTRET: case CSR_MCYCLE: - return cpu_get_host_ticks(); + if (use_icount) { + return cpu_get_icount(); + } else { + return cpu_get_host_ticks(); + } case CSR_MINSTRETH: case CSR_MCYCLEH: #if defined(TARGET_RISCV32) - return cpu_get_host_ticks() >> 32; + if (use_icount) { + return cpu_get_icount() >> 32; + } else { + return cpu_get_host_ticks() >> 32; + } #endif break; case CSR_MUCOUNTEREN: - return env->mucounteren; + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + return env->scounteren; + } else { + break; /* illegal instruction */ + } case CSR_MSCOUNTEREN: - return env->mscounteren; + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + return env->mcounteren; + } else { + break; /* illegal instruction */ + } case CSR_SSTATUS: { target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS - | SSTATUS_SUM | SSTATUS_SD; + | SSTATUS_SUM | SSTATUS_SD; if (env->priv_ver >= PRIV_VERSION_1_10_0) { mask |= SSTATUS_MXR; } @@ -462,10 +520,17 @@ target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) case CSR_STVEC: return env->stvec; case CSR_SCOUNTEREN: - return env->scounteren; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + return env->scounteren; + } else { + break; /* illegal instruction */ + } case CSR_SCAUSE: return env->scause; - case CSR_SPTBR: + case CSR_SATP: /* CSR_SPTBR */ + if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + return 0; + } if (env->priv_ver >= PRIV_VERSION_1_10_0) { return env->satp; } else { @@ -504,7 +569,11 @@ target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) case CSR_MTVEC: return env->mtvec; case CSR_MCOUNTEREN: - return env->mcounteren; + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + return env->mcounteren; + } else { + break; /* illegal instruction */ + } case CSR_MEDELEG: return env->medeleg; case CSR_MIDELEG: diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 808eab7f50..0b6be74f2d 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -26,6 +26,7 @@ #include "exec/helper-proto.h" #include "exec/helper-gen.h" +#include "exec/translator.h" #include "exec/log.h" #include "instmap.h" @@ -39,14 +40,12 @@ static TCGv load_val; #include "exec/gen-icount.h" typedef struct DisasContext { - struct TranslationBlock *tb; - target_ulong pc; - target_ulong next_pc; + DisasContextBase base; + /* pc_succ_insn points to the instruction following base.pc_next */ + target_ulong pc_succ_insn; uint32_t opcode; uint32_t flags; uint32_t mem_idx; - int singlestep_enabled; - int bstate; /* Remember the rounding mode encoded in the previous fp instruction, which we have already installed into env->fp_status. Or -1 for no previous fp instruction. Note that we exit the TB when writing @@ -55,13 +54,6 @@ typedef struct DisasContext { int frm; } DisasContext; -enum { - BS_NONE = 0, /* When seen outside of translation while loop, indicates - need to exit tb due to end of page. */ - BS_STOP = 1, /* Need to exit tb for syscall, sret, etc. */ - BS_BRANCH = 2, /* Need to exit tb for branch, jal, etc. */ -}; - /* convert riscv funct3 to qemu memop for load/store */ static const int tcg_memop_lookup[8] = { [0 ... 7] = -1, @@ -84,21 +76,21 @@ static const int tcg_memop_lookup[8] = { static void generate_exception(DisasContext *ctx, int excp) { - tcg_gen_movi_tl(cpu_pc, ctx->pc); + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); TCGv_i32 helper_tmp = tcg_const_i32(excp); gen_helper_raise_exception(cpu_env, helper_tmp); tcg_temp_free_i32(helper_tmp); - ctx->bstate = BS_BRANCH; + ctx->base.is_jmp = DISAS_NORETURN; } static void generate_exception_mbadaddr(DisasContext *ctx, int excp) { - tcg_gen_movi_tl(cpu_pc, ctx->pc); + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr)); TCGv_i32 helper_tmp = tcg_const_i32(excp); gen_helper_raise_exception(cpu_env, helper_tmp); tcg_temp_free_i32(helper_tmp); - ctx->bstate = BS_BRANCH; + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_exception_debug(void) @@ -120,12 +112,12 @@ static void gen_exception_inst_addr_mis(DisasContext *ctx) static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) { - if (unlikely(ctx->singlestep_enabled)) { + if (unlikely(ctx->base.singlestep_enabled)) { return false; } #ifndef CONFIG_USER_ONLY - return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); + return (ctx->base.tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif @@ -137,13 +129,13 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) /* chaining is only allowed when the jump is to the same page */ tcg_gen_goto_tb(n); tcg_gen_movi_tl(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_tl(cpu_pc, dest); - if (ctx->singlestep_enabled) { + if (ctx->base.singlestep_enabled) { gen_exception_debug(); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } } @@ -280,7 +272,6 @@ static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, tcg_gen_andi_tl(source2, source2, 0x1F); tcg_gen_sar_tl(source1, source1, source2); break; - /* fall through to SRA */ #endif case OPC_RISC_SRA: tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); @@ -520,7 +511,7 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, target_ulong next_pc; /* check misaligned: */ - next_pc = ctx->pc + imm; + next_pc = ctx->base.pc_next + imm; if (!riscv_has_ext(env, RVC)) { if ((next_pc & 0x3) != 0) { gen_exception_inst_addr_mis(ctx); @@ -528,11 +519,11 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, } } if (rd != 0) { - tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc); + tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn); } - gen_goto_tb(ctx, 0, ctx->pc + imm); /* must use this for safety */ - ctx->bstate = BS_BRANCH; + gen_goto_tb(ctx, 0, ctx->base.pc_next + imm); /* must use this for safety */ + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, @@ -555,15 +546,15 @@ static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, } if (rd != 0) { - tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc); + tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn); } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); if (misaligned) { gen_set_label(misaligned); gen_exception_inst_addr_mis(ctx); } - ctx->bstate = BS_BRANCH; + ctx->base.is_jmp = DISAS_NORETURN; break; default: @@ -609,15 +600,15 @@ static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, tcg_temp_free(source1); tcg_temp_free(source2); - gen_goto_tb(ctx, 1, ctx->next_pc); + gen_goto_tb(ctx, 1, ctx->pc_succ_insn); gen_set_label(l); /* branch taken */ - if (!riscv_has_ext(env, RVC) && ((ctx->pc + bimm) & 0x3)) { + if (!riscv_has_ext(env, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) { /* misaligned */ gen_exception_inst_addr_mis(ctx); } else { - gen_goto_tb(ctx, 0, ctx->pc + bimm); + gen_goto_tb(ctx, 0, ctx->base.pc_next + bimm); } - ctx->bstate = BS_BRANCH; + ctx->base.is_jmp = DISAS_NORETURN; } static void gen_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, @@ -725,7 +716,6 @@ static void gen_atomic(DisasContext *ctx, uint32_t opc, TCGv src1, src2, dat; TCGLabel *l1, *l2; TCGMemOp mop; - TCGCond cond; bool aq, rl; /* Extract the size of the atomic operation. */ @@ -823,60 +813,29 @@ static void gen_atomic(DisasContext *ctx, uint32_t opc, tcg_gen_atomic_fetch_or_tl(src2, src1, src2, ctx->mem_idx, mop); gen_set_gpr(rd, src2); break; - case OPC_RISC_AMOMIN: - cond = TCG_COND_LT; - goto do_minmax; - case OPC_RISC_AMOMAX: - cond = TCG_COND_GT; - goto do_minmax; - case OPC_RISC_AMOMINU: - cond = TCG_COND_LTU; - goto do_minmax; - case OPC_RISC_AMOMAXU: - cond = TCG_COND_GTU; - goto do_minmax; - do_minmax: - /* Handle the RL barrier. The AQ barrier is handled along the - parallel path by the SC atomic cmpxchg. On the serial path, - of course, barriers do not matter. */ - if (rl) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - if (tb_cflags(ctx->tb) & CF_PARALLEL) { - l1 = gen_new_label(); - gen_set_label(l1); - } else { - l1 = NULL; - } - gen_get_gpr(src1, rs1); gen_get_gpr(src2, rs2); - if ((mop & MO_SSIZE) == MO_SL) { - /* Sign-extend the register comparison input. */ - tcg_gen_ext32s_tl(src2, src2); - } - dat = tcg_temp_local_new(); - tcg_gen_qemu_ld_tl(dat, src1, ctx->mem_idx, mop); - tcg_gen_movcond_tl(cond, src2, dat, src2, dat, src2); - - if (tb_cflags(ctx->tb) & CF_PARALLEL) { - /* Parallel context. Make this operation atomic by verifying - that the memory didn't change while we computed the result. */ - tcg_gen_atomic_cmpxchg_tl(src2, src1, dat, src2, ctx->mem_idx, mop); - - /* If the cmpxchg failed, retry. */ - /* ??? There is an assumption here that this will eventually - succeed, such that we don't live-lock. This is not unlike - a similar loop that the compiler would generate for e.g. - __atomic_fetch_and_xor, so don't worry about it. */ - tcg_gen_brcond_tl(TCG_COND_NE, dat, src2, l1); - } else { - /* Serial context. Directly store the result. */ - tcg_gen_qemu_st_tl(src2, src1, ctx->mem_idx, mop); - } - gen_set_gpr(rd, dat); - tcg_temp_free(dat); + tcg_gen_atomic_fetch_smin_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOMAX: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_smax_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOMINU: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_umin_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); + break; + case OPC_RISC_AMOMAXU: + gen_get_gpr(src1, rs1); + gen_get_gpr(src2, rs2); + tcg_gen_atomic_fetch_umax_tl(src2, src1, src2, ctx->mem_idx, mop); + gen_set_gpr(rd, src2); break; default: @@ -1324,7 +1283,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, rs1_pass = tcg_temp_new(); imm_rs1 = tcg_temp_new(); gen_get_gpr(source1, rs1); - tcg_gen_movi_tl(cpu_pc, ctx->pc); + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); tcg_gen_movi_tl(rs1_pass, rs1); tcg_gen_movi_tl(csr_store, csr); /* copy into temp reg to feed to helper */ @@ -1344,13 +1303,13 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, case 0x0: /* ECALL */ /* always generates U-level ECALL, fixed in do_interrupt handler */ generate_exception(ctx, RISCV_EXCP_U_ECALL); - tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; break; case 0x1: /* EBREAK */ generate_exception(ctx, RISCV_EXCP_BREAKPOINT); - tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; break; #ifndef CONFIG_USER_ONLY case 0x002: /* URET */ @@ -1359,8 +1318,8 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, case 0x102: /* SRET */ if (riscv_has_ext(env, RVS)) { gen_helper_sret(cpu_pc, cpu_env, cpu_pc); - tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; } else { gen_exception_illegal(ctx); } @@ -1370,14 +1329,14 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, break; case 0x302: /* MRET */ gen_helper_mret(cpu_pc, cpu_env, cpu_pc); - tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; break; case 0x7b2: /* DRET */ gen_exception_illegal(ctx); break; case 0x105: /* WFI */ - tcg_gen_movi_tl(cpu_pc, ctx->next_pc); + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); gen_helper_wfi(cpu_env); break; case 0x104: /* SFENCE.VM */ @@ -1391,6 +1350,7 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, break; default: tcg_gen_movi_tl(imm_rs1, rs1); + gen_io_start(); switch (opc) { case OPC_RISC_CSRRW: gen_helper_csrrw(dest, cpu_env, source1, csr_store); @@ -1414,11 +1374,12 @@ static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, gen_exception_illegal(ctx); return; } + gen_io_end(); gen_set_gpr(rd, dest); /* end tb since we may be changing priv modes, to get mmu_index right */ - tcg_gen_movi_tl(cpu_pc, ctx->next_pc); - tcg_gen_exit_tb(0); /* no chaining */ - ctx->bstate = BS_BRANCH; + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; break; } tcg_temp_free(source1); @@ -1736,7 +1697,7 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) break; /* NOP */ } tcg_gen_movi_tl(cpu_gpr[rd], (sextract64(ctx->opcode, 12, 20) << 12) + - ctx->pc); + ctx->base.pc_next); break; case OPC_RISC_JAL: imm = GET_JAL_IMM(ctx->opcode); @@ -1809,9 +1770,9 @@ static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) if (ctx->opcode & 0x1000) { /* FENCE_I is a no-op in QEMU, * however we need to end the translation block */ - tcg_gen_movi_tl(cpu_pc, ctx->next_pc); - tcg_gen_exit_tb(0); - ctx->bstate = BS_BRANCH; + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); + tcg_gen_exit_tb(NULL, 0); + ctx->base.is_jmp = DISAS_NORETURN; } else { /* FENCE is a full memory barrier. */ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); @@ -1835,120 +1796,113 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx) if (!riscv_has_ext(env, RVC)) { gen_exception_illegal(ctx); } else { - ctx->next_pc = ctx->pc + 2; + ctx->pc_succ_insn = ctx->base.pc_next + 2; decode_RV32_64C(env, ctx); } } else { - ctx->next_pc = ctx->pc + 4; + ctx->pc_succ_insn = ctx->base.pc_next + 4; decode_RV32_64G(env, ctx); } } -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { - CPURISCVState *env = cs->env_ptr; - DisasContext ctx; - target_ulong pc_start; - target_ulong next_page_start; - int num_insns; - int max_insns; - pc_start = tb->pc; - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - ctx.pc = pc_start; + DisasContext *ctx = container_of(dcbase, DisasContext, base); - /* once we have GDB, the rest of the translate.c implementation should be - ready for singlestep */ - ctx.singlestep_enabled = cs->singlestep_enabled; + ctx->pc_succ_insn = ctx->base.pc_first; + ctx->flags = ctx->base.tb->flags; + ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; + ctx->frm = -1; /* unknown rounding mode */ +} - ctx.tb = tb; - ctx.bstate = BS_NONE; - ctx.flags = tb->flags; - ctx.mem_idx = tb->flags & TB_FLAGS_MMU_MASK; - ctx.frm = -1; /* unknown rounding mode */ +static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) +{ +} - num_insns = 0; - max_insns = tb->cflags & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; +static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(ctx->base.pc_next); +} + +static bool riscv_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, + const CPUBreakpoint *bp) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + ctx->base.is_jmp = DISAS_NORETURN; + gen_exception_debug(); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx->base.pc_next += 4; + return true; +} + + +static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + CPURISCVState *env = cpu->env_ptr; + + ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); + decode_opc(env, ctx); + ctx->base.pc_next = ctx->pc_succ_insn; + + if (ctx->base.is_jmp == DISAS_NEXT) { + target_ulong page_start; + + page_start = ctx->base.pc_first & TARGET_PAGE_MASK; + if (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE) { + ctx->base.is_jmp = DISAS_TOO_MANY; + } } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } - gen_tb_start(tb); +} - while (ctx.bstate == BS_NONE) { - tcg_gen_insn_start(ctx.pc); - num_insns++; +static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); - if (unlikely(cpu_breakpoint_test(cs, ctx.pc, BP_ANY))) { - tcg_gen_movi_tl(cpu_pc, ctx.pc); - ctx.bstate = BS_BRANCH; - gen_exception_debug(); - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - ctx.pc += 4; - goto done_generating; - } - - if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { - gen_io_start(); - } - - ctx.opcode = cpu_ldl_code(env, ctx.pc); - decode_opc(env, &ctx); - ctx.pc = ctx.next_pc; - - if (cs->singlestep_enabled) { - break; - } - if (ctx.pc >= next_page_start) { - break; - } - if (tcg_op_buf_full()) { - break; - } - if (num_insns >= max_insns) { - break; - } - if (singlestep) { - break; - } - - } - if (tb->cflags & CF_LAST_IO) { - gen_io_end(); - } - switch (ctx.bstate) { - case BS_STOP: - gen_goto_tb(&ctx, 0, ctx.pc); - break; - case BS_NONE: /* handle end of page - DO NOT CHAIN. See gen_goto_tb. */ - tcg_gen_movi_tl(cpu_pc, ctx.pc); - if (cs->singlestep_enabled) { + switch (ctx->base.is_jmp) { + case DISAS_TOO_MANY: + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + if (ctx->base.singlestep_enabled) { gen_exception_debug(); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } break; - case BS_BRANCH: /* ops using BS_BRANCH generate own exit seq */ - default: + case DISAS_NORETURN: break; + default: + g_assert_not_reached(); } -done_generating: - gen_tb_end(tb, num_insns); - tb->size = ctx.pc - pc_start; - tb->icount = num_insns; +} -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, ctx.pc - pc_start); - qemu_log("\n"); - } -#endif +static void riscv_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps riscv_tr_ops = { + .init_disas_context = riscv_tr_init_disas_context, + .tb_start = riscv_tr_tb_start, + .insn_start = riscv_tr_insn_start, + .breakpoint_check = riscv_tr_breakpoint_check, + .translate_insn = riscv_tr_translate_insn, + .tb_stop = riscv_tr_tb_stop, + .disas_log = riscv_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +{ + DisasContext ctx; + + translator_loop(&riscv_tr_ops, &ctx.base, cs, tb); } void riscv_translate_init(void) diff --git a/target/s390x/Makefile.objs b/target/s390x/Makefile.objs index 31932de9cf..22a9a9927a 100644 --- a/target/s390x/Makefile.objs +++ b/target/s390x/Makefile.objs @@ -5,6 +5,7 @@ obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o arch_dump.o mmu_helper.o diag.o obj-$(CONFIG_SOFTMMU) += sigp.o obj-$(CONFIG_KVM) += kvm.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o +obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o # build and run feature list generator feat-src = $(SRC_PATH)/target/$(TARGET_BASE_ARCH)/ diff --git a/target/s390x/arch_dump.c b/target/s390x/arch_dump.c index 6f61ff95af..c9ef0a6e60 100644 --- a/target/s390x/arch_dump.c +++ b/target/s390x/arch_dump.c @@ -184,20 +184,20 @@ typedef struct NoteFuncDescStruct { } NoteFuncDesc; static const NoteFuncDesc note_core[] = { - {sizeof(((Note *)0)->contents.prstatus), s390x_write_elf64_prstatus}, - {sizeof(((Note *)0)->contents.fpregset), s390x_write_elf64_fpregset}, + {sizeof_field(Note, contents.prstatus), s390x_write_elf64_prstatus}, + {sizeof_field(Note, contents.fpregset), s390x_write_elf64_fpregset}, { 0, NULL} }; static const NoteFuncDesc note_linux[] = { - {sizeof(((Note *)0)->contents.prefix), s390x_write_elf64_prefix}, - {sizeof(((Note *)0)->contents.ctrs), s390x_write_elf64_ctrs}, - {sizeof(((Note *)0)->contents.timer), s390x_write_elf64_timer}, - {sizeof(((Note *)0)->contents.todcmp), s390x_write_elf64_todcmp}, - {sizeof(((Note *)0)->contents.todpreg), s390x_write_elf64_todpreg}, - {sizeof(((Note *)0)->contents.vregslo), s390x_write_elf64_vregslo}, - {sizeof(((Note *)0)->contents.vregshi), s390x_write_elf64_vregshi}, - {sizeof(((Note *)0)->contents.gscb), s390x_write_elf64_gscb}, + {sizeof_field(Note, contents.prefix), s390x_write_elf64_prefix}, + {sizeof_field(Note, contents.ctrs), s390x_write_elf64_ctrs}, + {sizeof_field(Note, contents.timer), s390x_write_elf64_timer}, + {sizeof_field(Note, contents.todcmp), s390x_write_elf64_todcmp}, + {sizeof_field(Note, contents.todpreg), s390x_write_elf64_todpreg}, + {sizeof_field(Note, contents.vregslo), s390x_write_elf64_vregslo}, + {sizeof_field(Note, contents.vregshi), s390x_write_elf64_vregshi}, + {sizeof_field(Note, contents.gscb), s390x_write_elf64_gscb}, { 0, NULL} }; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index c2b775f4eb..271c5ce652 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -30,7 +30,6 @@ #include "kvm_s390x.h" #include "sysemu/kvm.h" #include "qemu-common.h" -#include "qemu/cutils.h" #include "qemu/timer.h" #include "qemu/error-report.h" #include "trace.h" @@ -38,7 +37,6 @@ #include "qapi/qapi-visit-misc.h" #include "qapi/qapi-visit-run-state.h" #include "sysemu/hw_accel.h" -#include "exec/exec-all.h" #include "hw/qdev-properties.h" #ifndef CONFIG_USER_ONLY #include "hw/hw.h" @@ -220,11 +218,18 @@ static void s390_cpu_realizefn(DeviceState *dev, Error **errp) #endif s390_cpu_gdb_init(cs); qemu_init_vcpu(cs); -#if !defined(CONFIG_USER_ONLY) - run_on_cpu(cs, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); -#else - cpu_reset(cs); -#endif + + /* + * KVM requires the initial CPU reset ioctl to be executed on the target + * CPU thread. CPU hotplug under single-threaded TCG will not work with + * run_on_cpu(), as run_on_cpu() will not work properly if called while + * the main thread is already running but the CPU hasn't been realized. + */ + if (kvm_enabled()) { + run_on_cpu(cs, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); + } else { + cpu_reset(cs); + } scc->parent_realize(dev, &err); out: @@ -276,9 +281,6 @@ static void s390_cpu_initfn(Object *obj) CPUState *cs = CPU(obj); S390CPU *cpu = S390_CPU(obj); CPUS390XState *env = &cpu->env; -#if !defined(CONFIG_USER_ONLY) - struct tm tm; -#endif cs->env_ptr = env; cs->halted = 1; @@ -287,10 +289,6 @@ static void s390_cpu_initfn(Object *obj) s390_cpu_get_crash_info_qom, NULL, NULL, NULL, NULL); s390_cpu_model_register_props(obj); #if !defined(CONFIG_USER_ONLY) - qemu_get_timedate(&tm, 0); - env->tod_offset = TOD_UNIX_EPOCH + - (time2tod(mktimegm(&tm)) * 1000000000ULL); - env->tod_basetime = 0; env->tod_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_tod_timer, cpu); env->cpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, s390x_cpu_timer, cpu); s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu); @@ -391,38 +389,6 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu) return s390_count_running_cpus(); } -int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low) -{ - int r = 0; - - if (kvm_enabled()) { - r = kvm_s390_get_clock_ext(tod_high, tod_low); - if (r == -ENXIO) { - return kvm_s390_get_clock(tod_high, tod_low); - } - } else { - /* Fixme TCG */ - *tod_high = 0; - *tod_low = 0; - } - - return r; -} - -int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) -{ - int r = 0; - - if (kvm_enabled()) { - r = kvm_s390_set_clock_ext(tod_high, tod_low); - if (r == -ENXIO) { - return kvm_s390_set_clock(tod_high, tod_low); - } - } - /* Fixme TCG */ - return r; -} - int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit) { if (kvm_enabled()) { diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 3ee40f08b7..2c3dd2d189 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -130,8 +130,6 @@ struct CPUS390XState { uint64_t cpuid; #endif - uint64_t tod_offset; - uint64_t tod_basetime; QEMUTimer *tod_timer; QEMUTimer *cpu_timer; @@ -686,10 +684,34 @@ static inline uint64_t s390_build_validity_mcic(void) return mcic; } +static inline void s390_do_cpu_full_reset(CPUState *cs, run_on_cpu_data arg) +{ + cpu_reset(cs); +} + +static inline void s390_do_cpu_reset(CPUState *cs, run_on_cpu_data arg) +{ + S390CPUClass *scc = S390_CPU_GET_CLASS(cs); + + scc->cpu_reset(cs); +} + +static inline void s390_do_cpu_initial_reset(CPUState *cs, run_on_cpu_data arg) +{ + S390CPUClass *scc = S390_CPU_GET_CLASS(cs); + + scc->initial_cpu_reset(cs); +} + +static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) +{ + S390CPUClass *scc = S390_CPU_GET_CLASS(cs); + + scc->load_normal(cs); +} + /* cpu.c */ -int s390_get_clock(uint8_t *tod_high, uint64_t *tod_low); -int s390_set_clock(uint8_t *tod_high, uint64_t *tod_low); void s390_crypto_reset(void); bool s390_get_squash_mcss(void); int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit); diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 2741b6803f..604898a882 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -79,6 +79,7 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x2964, 13, 2, 47, 0x08000000U, "z13.2", "IBM z13 GA2"), CPUDEF_INIT(0x2965, 13, 2, 47, 0x08000000U, "z13s", "IBM z13s GA1"), CPUDEF_INIT(0x3906, 14, 1, 47, 0x08000000U, "z14", "IBM z14 GA1"), + CPUDEF_INIT(0x3907, 14, 1, 47, 0x08000000U, "z14ZR1", "IBM z14 Model ZR1 GA1"), }; #define QEMU_MAX_CPU_TYPE 0x2827 @@ -551,7 +552,7 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, } if (!qdict_size(qdict)) { - QDECREF(qdict); + qobject_unref(qdict); } else { info->props = QOBJECT(qdict); info->has_props = true; @@ -715,6 +716,14 @@ CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *infoa, model.def = s390_find_cpu_def(cpu_type, max_gen, max_gen_ga, model.features); + + /* models without early base features (esan3) are bad */ + if (!model.def) { + error_setg(errp, "No compatible CPU model could be created as" + " important base features are disabled"); + return NULL; + } + /* strip off features not part of the max model */ bitmap_and(model.features, model.features, model.def->full_feat, S390_FEAT_MAX); diff --git a/target/s390x/diag.c b/target/s390x/diag.c index a755837ad5..acb0f3d4af 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -16,57 +16,11 @@ #include "cpu.h" #include "internal.h" #include "exec/address-spaces.h" -#include "exec/exec-all.h" #include "hw/watchdog/wdt_diag288.h" #include "sysemu/cpus.h" #include "hw/s390x/ipl.h" #include "hw/s390x/s390-virtio-ccw.h" -static int modified_clear_reset(S390CPU *cpu) -{ - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); - CPUState *t; - - pause_all_vcpus(); - cpu_synchronize_all_states(); - CPU_FOREACH(t) { - run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL); - } - s390_cmma_reset(); - subsystem_reset(); - s390_crypto_reset(); - scc->load_normal(CPU(cpu)); - cpu_synchronize_all_post_reset(); - resume_all_vcpus(); - return 0; -} - -static inline void s390_do_cpu_reset(CPUState *cs, run_on_cpu_data arg) -{ - S390CPUClass *scc = S390_CPU_GET_CLASS(cs); - - scc->cpu_reset(cs); -} - -static int load_normal_reset(S390CPU *cpu) -{ - S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); - CPUState *t; - - pause_all_vcpus(); - cpu_synchronize_all_states(); - CPU_FOREACH(t) { - run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL); - } - s390_cmma_reset(); - subsystem_reset(); - scc->initial_cpu_reset(CPU(cpu)); - scc->load_normal(CPU(cpu)); - cpu_synchronize_all_post_reset(); - resume_all_vcpus(); - return 0; -} - int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) { uint64_t func = env->regs[r1]; @@ -101,6 +55,7 @@ int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3) void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) { + CPUState *cs = CPU(s390_env_get_cpu(env)); uint64_t addr = env->regs[r1]; uint64_t subcode = env->regs[r3]; IplParameterBlock *iplb; @@ -117,22 +72,13 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) switch (subcode) { case 0: - modified_clear_reset(s390_env_get_cpu(env)); - if (tcg_enabled()) { - cpu_loop_exit(CPU(s390_env_get_cpu(env))); - } + s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR); break; case 1: - load_normal_reset(s390_env_get_cpu(env)); - if (tcg_enabled()) { - cpu_loop_exit(CPU(s390_env_get_cpu(env))); - } + s390_ipl_reset_request(cs, S390_RESET_LOAD_NORMAL); break; case 3: - s390_reipl_request(); - if (tcg_enabled()) { - cpu_loop_exit(CPU(s390_env_get_cpu(env))); - } + s390_ipl_reset_request(cs, S390_RESET_REIPL); break; case 5: if ((r1 & 1) || (addr & 0x0fffULL)) { @@ -140,7 +86,8 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra) return; } if (!address_space_access_valid(&address_space_memory, addr, - sizeof(IplParameterBlock), false)) { + sizeof(IplParameterBlock), false, + MEMTXATTRS_UNSPECIFIED)) { s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); return; } @@ -169,7 +116,8 @@ out: return; } if (!address_space_access_valid(&address_space_memory, addr, - sizeof(IplParameterBlock), true)) { + sizeof(IplParameterBlock), true, + MEMTXATTRS_UNSPECIFIED)) { s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); return; } diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index dfee221111..f0ce60cff2 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -120,7 +120,8 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int size, /* check out of RAM access */ if (!address_space_access_valid(&address_space_memory, raddr, - TARGET_PAGE_SIZE, rw)) { + TARGET_PAGE_SIZE, rw, + MEMTXATTRS_UNSPECIFIED)) { DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, (uint64_t)raddr, (uint64_t)ram_size); trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO); diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c index 43f8bf1c94..5c5b451b3b 100644 --- a/target/s390x/fpu_helper.c +++ b/target/s390x/fpu_helper.c @@ -269,7 +269,7 @@ uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2) { float64 ret = float32_to_float64(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return float64_maybe_silence_nan(ret, &env->fpu_status); + return ret; } /* convert 128-bit float to 64-bit float */ @@ -277,7 +277,7 @@ uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al) { float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status); handle_exceptions(env, GETPC()); - return float64_maybe_silence_nan(ret, &env->fpu_status); + return ret; } /* convert 64-bit float to 128-bit float */ @@ -285,7 +285,7 @@ uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2) { float128 ret = float64_to_float128(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return RET128(float128_maybe_silence_nan(ret, &env->fpu_status)); + return RET128(ret); } /* convert 32-bit float to 128-bit float */ @@ -293,7 +293,7 @@ uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2) { float128 ret = float32_to_float128(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return RET128(float128_maybe_silence_nan(ret, &env->fpu_status)); + return RET128(ret); } /* convert 64-bit float to 32-bit float */ @@ -301,7 +301,7 @@ uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2) { float32 ret = float64_to_float32(f2, &env->fpu_status); handle_exceptions(env, GETPC()); - return float32_maybe_silence_nan(ret, &env->fpu_status); + return ret; } /* convert 128-bit float to 32-bit float */ @@ -309,7 +309,7 @@ uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al) { float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status); handle_exceptions(env, GETPC()); - return float32_maybe_silence_nan(ret, &env->fpu_status); + return ret; } /* 32-bit FP compare */ diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 0cdbc15378..6626b6f565 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -512,6 +512,8 @@ static uint16_t default_GEN11_GA1[] = { S390_FEAT_IPTE_RANGE, S390_FEAT_ACCESS_EXCEPTION_FS_INDICATION, S390_FEAT_GROUP_MSA_EXT_4, + S390_FEAT_PPA15, + S390_FEAT_BPB, }; #define default_GEN11_GA2 EmptyFeat diff --git a/target/s390x/helper.c b/target/s390x/helper.c index e8548f340a..254631693d 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -23,7 +23,6 @@ #include "internal.h" #include "exec/gdbstub.h" #include "qemu/timer.h" -#include "exec/exec-all.h" #include "hw/s390x/ioinst.h" #include "sysemu/hw_accel.h" #ifndef CONFIG_USER_ONLY @@ -327,21 +326,22 @@ void s390_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, } } - for (i = 0; i < 16; i++) { - cpu_fprintf(f, "F%02d=%016" PRIx64, i, get_freg(env, i)->ll); - if ((i % 4) == 3) { - cpu_fprintf(f, "\n"); + if (flags & CPU_DUMP_FPU) { + if (s390_has_feat(S390_FEAT_VECTOR)) { + for (i = 0; i < 32; i++) { + cpu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64 "%c", + i, env->vregs[i][0].ll, env->vregs[i][1].ll, + i % 2 ? '\n' : ' '); + } } else { - cpu_fprintf(f, " "); + for (i = 0; i < 16; i++) { + cpu_fprintf(f, "F%02d=%016" PRIx64 "%c", + i, get_freg(env, i)->ll, + (i % 4) == 3 ? '\n' : ' '); + } } } - for (i = 0; i < 32; i++) { - cpu_fprintf(f, "V%02d=%016" PRIx64 "%016" PRIx64, i, - env->vregs[i][0].ll, env->vregs[i][1].ll); - cpu_fprintf(f, (i % 2) ? "\n" : " "); - } - #ifndef CONFIG_USER_ONLY for (i = 0; i < 16; i++) { cpu_fprintf(f, "C%02d=%016" PRIx64, i, env->cregs[i]); diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 59cba86a27..97c60ca7bc 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -127,6 +127,7 @@ DEF_HELPER_4(diag, void, env, i32, i32, i32) DEF_HELPER_3(load_psw, noreturn, env, i64, i64) DEF_HELPER_FLAGS_2(spx, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_1(stck, TCG_CALL_NO_RWG_SE, i64, env) +DEF_HELPER_FLAGS_2(sck, TCG_CALL_NO_RWG, i32, env, i64) DEF_HELPER_FLAGS_2(sckc, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_2(sckpf, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_1(stckc, TCG_CALL_NO_RWG, i64, env) diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def index 157619403d..5c6f33ed9c 100644 --- a/target/s390x/insn-data.def +++ b/target/s390x/insn-data.def @@ -997,8 +997,7 @@ /* SET ADDRESS SPACE CONTROL FAST */ C(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0) /* SET CLOCK */ - /* ??? Not implemented - is it necessary? */ - C(0xb204, SCK, S, Z, 0, 0, 0, 0, 0, 0) + C(0xb204, SCK, S, Z, la2, 0, 0, 0, sck, 0) /* SET CLOCK COMPARATOR */ C(0xb206, SCKC, S, Z, 0, m2_64a, 0, 0, sckc, 0) /* SET CLOCK PROGRAMMABLE FIELD */ diff --git a/target/s390x/internal.h b/target/s390x/internal.h index d911e84958..f2a771e2b4 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -237,21 +237,6 @@ enum cc_op { CC_OP_MAX }; -/* The value of the TOD clock for 1.1.1970. */ -#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL - -/* Converts ns to s390's clock format */ -static inline uint64_t time2tod(uint64_t ns) -{ - return (ns << 9) / 125; -} - -/* Converts s390's clock format to ns */ -static inline uint64_t tod2time(uint64_t t) -{ - return (t * 125) >> 9; -} - static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb, uint8_t *ar) { @@ -273,12 +258,6 @@ static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb, /* Base/displacement are at the same locations. */ #define decode_basedisp_rs decode_basedisp_s -static inline void s390_do_cpu_full_reset(CPUState *cs, run_on_cpu_data arg) -{ - cpu_reset(cs); -} - - /* arch_dump.c */ int s390_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); diff --git a/target/s390x/kvm-stub.c b/target/s390x/kvm-stub.c index 29b10542cc..bf7795e47a 100644 --- a/target/s390x/kvm-stub.c +++ b/target/s390x/kvm-stub.c @@ -60,12 +60,12 @@ int kvm_s390_get_clock_ext(uint8_t *tod_high, uint64_t *tod_low) return -ENOSYS; } -int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) +int kvm_s390_set_clock(uint8_t tod_high, uint64_t tod_low) { return -ENOSYS; } -int kvm_s390_set_clock_ext(uint8_t *tod_high, uint64_t *tod_low) +int kvm_s390_set_clock_ext(uint8_t tod_high, uint64_t tod_low) { return -ENOSYS; } diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index fb59d92def..d923cf4240 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -39,7 +39,6 @@ #include "hw/hw.h" #include "sysemu/device_tree.h" #include "exec/gdbstub.h" -#include "exec/address-spaces.h" #include "trace.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" @@ -667,13 +666,13 @@ int kvm_s390_get_clock_ext(uint8_t *tod_high, uint64_t *tod_low) return r; } -int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) +int kvm_s390_set_clock(uint8_t tod_high, uint64_t tod_low) { int r; struct kvm_device_attr attr = { .group = KVM_S390_VM_TOD, .attr = KVM_S390_VM_TOD_LOW, - .addr = (uint64_t)tod_low, + .addr = (uint64_t)&tod_low, }; r = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); @@ -682,15 +681,15 @@ int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_low) } attr.attr = KVM_S390_VM_TOD_HIGH; - attr.addr = (uint64_t)tod_high; + attr.addr = (uint64_t)&tod_high; return kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); } -int kvm_s390_set_clock_ext(uint8_t *tod_high, uint64_t *tod_low) +int kvm_s390_set_clock_ext(uint8_t tod_high, uint64_t tod_low) { struct kvm_s390_vm_tod_clock gtod = { - .epoch_idx = *tod_high, - .tod = *tod_low, + .epoch_idx = tod_high, + .tod = tod_low, }; struct kvm_device_attr attr = { .group = KVM_S390_VM_TOD, @@ -753,12 +752,23 @@ int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf, */ static void *legacy_s390_alloc(size_t size, uint64_t *align, bool shared) { - void *mem; + static void *mem; + + if (mem) { + /* we only support one allocation, which is enough for initial ram */ + return NULL; + } mem = mmap((void *) 0x800000000ULL, size, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); - return mem == MAP_FAILED ? NULL : mem; + if (mem == MAP_FAILED) { + mem = NULL; + } + if (mem && align) { + *align = QEMU_VMALLOC_ALIGN; + } + return mem; } static uint8_t const *sw_bp_inst; @@ -1081,7 +1091,6 @@ static int kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run, uint32_t code; int r = 0; - cpu_synchronize_state(CPU(cpu)); sccb = env->regs[ipbh0 & 0xf]; code = env->regs[(ipbh0 & 0xf0) >> 4]; @@ -1101,8 +1110,6 @@ static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1) int rc = 0; uint16_t ipbh0 = (run->s390_sieic.ipb & 0xffff0000) >> 16; - cpu_synchronize_state(CPU(cpu)); - switch (ipa1) { case PRIV_B2_XSCH: ioinst_handle_xsch(cpu, env->regs[1], RA_IGNORED); @@ -1248,7 +1255,6 @@ static int kvm_stpcifc_service_call(S390CPU *cpu, struct kvm_run *run) uint8_t ar; if (s390_has_feat(S390_FEAT_ZPCI)) { - cpu_synchronize_state(CPU(cpu)); fiba = get_base_disp_rxy(cpu, run, &ar); return stpcifc_service_call(cpu, r1, fiba, ar, RA_IGNORED); @@ -1266,7 +1272,6 @@ static int kvm_sic_service_call(S390CPU *cpu, struct kvm_run *run) uint16_t mode; int r; - cpu_synchronize_state(CPU(cpu)); mode = env->regs[r1] & 0xffff; isc = (env->regs[r3] >> 27) & 0x7; r = css_do_sic(env, isc, mode); @@ -1297,7 +1302,6 @@ static int kvm_pcistb_service_call(S390CPU *cpu, struct kvm_run *run) uint8_t ar; if (s390_has_feat(S390_FEAT_ZPCI)) { - cpu_synchronize_state(CPU(cpu)); gaddr = get_base_disp_rsy(cpu, run, &ar); return pcistb_service_call(cpu, r1, r3, gaddr, ar, RA_IGNORED); @@ -1313,7 +1317,6 @@ static int kvm_mpcifc_service_call(S390CPU *cpu, struct kvm_run *run) uint8_t ar; if (s390_has_feat(S390_FEAT_ZPCI)) { - cpu_synchronize_state(CPU(cpu)); fiba = get_base_disp_rxy(cpu, run, &ar); return mpcifc_service_call(cpu, r1, fiba, ar, RA_IGNORED); @@ -1401,7 +1404,6 @@ static int handle_hypercall(S390CPU *cpu, struct kvm_run *run) CPUS390XState *env = &cpu->env; int ret; - cpu_synchronize_state(CPU(cpu)); ret = s390_virtio_hypercall(env); if (ret == -EINVAL) { kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION); @@ -1416,7 +1418,6 @@ static void kvm_handle_diag_288(S390CPU *cpu, struct kvm_run *run) uint64_t r1, r3; int rc; - cpu_synchronize_state(CPU(cpu)); r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; r3 = run->s390_sieic.ipa & 0x000f; rc = handle_diag_288(&cpu->env, r1, r3); @@ -1429,7 +1430,6 @@ static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run) { uint64_t r1, r3; - cpu_synchronize_state(CPU(cpu)); r1 = (run->s390_sieic.ipa & 0x00f0) >> 4; r3 = run->s390_sieic.ipa & 0x000f; handle_diag_308(&cpu->env, r1, r3, RA_IGNORED); @@ -1440,8 +1440,6 @@ static int handle_sw_breakpoint(S390CPU *cpu, struct kvm_run *run) CPUS390XState *env = &cpu->env; unsigned long pc; - cpu_synchronize_state(CPU(cpu)); - pc = env->psw.addr - sw_bp_ilen; if (kvm_find_sw_breakpoint(CPU(cpu), pc)) { env->psw.addr = pc; @@ -1493,8 +1491,6 @@ static int kvm_s390_handle_sigp(S390CPU *cpu, uint8_t ipa1, uint32_t ipb) int ret; uint8_t order; - cpu_synchronize_state(CPU(cpu)); - /* get order code */ order = decode_basedisp_rs(env, ipb, NULL) & SIGP_ORDER_MASK; @@ -1556,7 +1552,6 @@ static int handle_oper_loop(S390CPU *cpu, struct kvm_run *run) CPUState *cs = CPU(cpu); PSW oldpsw, newpsw; - cpu_synchronize_state(cs); newpsw.mask = ldq_phys(cs->as, cpu->env.psa + offsetof(LowCore, program_new_psw)); newpsw.addr = ldq_phys(cs->as, cpu->env.psa + @@ -1609,7 +1604,6 @@ static int handle_intercept(S390CPU *cpu) break; case ICPT_WAITPSW: /* disabled wait, since enabled wait is handled in kernel */ - cpu_synchronize_state(cs); s390_handle_wait(cpu); r = EXCP_HALTED; break; @@ -1651,8 +1645,6 @@ static int handle_tsch(S390CPU *cpu) struct kvm_run *run = cs->kvm_run; int ret; - cpu_synchronize_state(cs); - ret = ioinst_handle_tsch(cpu, cpu->env.regs[1], run->s390_tsch.ipb, RA_IGNORED); if (ret < 0) { @@ -1778,14 +1770,14 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) qemu_mutex_lock_iothread(); - cpu_synchronize_state(cs); + kvm_cpu_synchronize_state(cs); switch (run->exit_reason) { case KVM_EXIT_S390_SIEIC: ret = handle_intercept(cpu); break; case KVM_EXIT_S390_RESET: - s390_reipl_request(); + s390_ipl_reset_request(cs, S390_RESET_REIPL); break; case KVM_EXIT_S390_TSCH: ret = handle_tsch(cpu); diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm_s390x.h index c383bf4ee9..6e52287da3 100644 --- a/target/s390x/kvm_s390x.h +++ b/target/s390x/kvm_s390x.h @@ -10,6 +10,8 @@ #ifndef KVM_S390X_H #define KVM_S390X_H +#include "cpu-qom.h" + struct kvm_s390_irq; void kvm_s390_floating_interrupt_legacy(struct kvm_s390_irq *irq); @@ -25,8 +27,8 @@ int kvm_s390_get_ri(void); int kvm_s390_get_gs(void); int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_clock); int kvm_s390_get_clock_ext(uint8_t *tod_high, uint64_t *tod_clock); -int kvm_s390_set_clock(uint8_t *tod_high, uint64_t *tod_clock); -int kvm_s390_set_clock_ext(uint8_t *tod_high, uint64_t *tod_clock); +int kvm_s390_set_clock(uint8_t tod_high, uint64_t tod_clock); +int kvm_s390_set_clock_ext(uint8_t tod_high, uint64_t tod_clock); void kvm_s390_enable_css_support(S390CPU *cpu); int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, int vq, bool assign); diff --git a/target/s390x/machine.c b/target/s390x/machine.c index 84b4928755..bd3230d027 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -19,6 +19,7 @@ #include "cpu.h" #include "internal.h" #include "kvm_s390x.h" +#include "tcg_s390x.h" #include "sysemu/kvm.h" static int cpu_post_load(void *opaque, int version_id) @@ -34,6 +35,11 @@ static int cpu_post_load(void *opaque, int version_id) return kvm_s390_vcpu_interrupt_post_load(cpu); } + if (tcg_enabled()) { + /* Rearm the CKC timer if necessary */ + tcg_s390_tod_updated(CPU(cpu), RUN_ON_CPU_NULL); + } + return 0; } diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index a0e28bd124..e21a47fb4d 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "exec/address-spaces.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index e0b23c1fd1..3f91579570 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -26,9 +26,10 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "qemu/timer.h" -#include "exec/address-spaces.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +#include "qapi/error.h" +#include "tcg_s390x.h" #if !defined(CONFIG_USER_ONLY) #include "sysemu/cpus.h" @@ -40,6 +41,7 @@ #include "hw/s390x/ioinst.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/boards.h" +#include "hw/s390x/tod.h" #endif /* #define DEBUG_HELPER */ @@ -139,30 +141,69 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1) /* Store Clock */ uint64_t HELPER(stck)(CPUS390XState *env) { - uint64_t time; + S390TODState *td = s390_get_todstate(); + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + S390TOD tod; - time = env->tod_offset + - time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - env->tod_basetime); - - return time; + tdc->get(td, &tod, &error_abort); + return tod.low; } -/* Set Clock Comparator */ -void HELPER(sckc)(CPUS390XState *env, uint64_t time) +static void update_ckc_timer(CPUS390XState *env) { - if (time == -1ULL) { + S390TODState *td = s390_get_todstate(); + uint64_t time; + + /* stop the timer and remove pending CKC IRQs */ + timer_del(env->tod_timer); + g_assert(qemu_mutex_iothread_locked()); + env->pending_int &= ~INTERRUPT_EXT_CLOCK_COMPARATOR; + + /* the tod has to exceed the ckc, this can never happen if ckc is all 1's */ + if (env->ckc == -1ULL) { return; } - env->ckc = time; - /* difference between origins */ - time -= env->tod_offset; + time = env->ckc - td->base.low; /* nanoseconds */ time = tod2time(time); - timer_mod(env->tod_timer, env->tod_basetime + time); + timer_mod(env->tod_timer, time); +} + +/* Set Clock Comparator */ +void HELPER(sckc)(CPUS390XState *env, uint64_t ckc) +{ + env->ckc = ckc; + + qemu_mutex_lock_iothread(); + update_ckc_timer(env); + qemu_mutex_unlock_iothread(); +} + +void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) +{ + S390CPU *cpu = S390_CPU(cs); + + update_ckc_timer(&cpu->env); +} + +/* Set Clock */ +uint32_t HELPER(sck)(CPUS390XState *env, uint64_t tod_low) +{ + S390TODState *td = s390_get_todstate(); + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + S390TOD tod = { + .high = 0, + .low = tod_low, + }; + + qemu_mutex_lock_iothread(); + tdc->set(td, &tod, &error_abort); + qemu_mutex_unlock_iothread(); + return 0; } /* Set Tod Programmable Field */ @@ -206,7 +247,7 @@ uint32_t HELPER(stsi)(CPUS390XState *env, uint64_t a0, uint64_t r0, uint64_t r1) const MachineState *ms = MACHINE(qdev_get_machine()); uint16_t total_cpus = 0, conf_cpus = 0, reserved_cpus = 0; S390CPU *cpu = s390_env_get_cpu(env); - SysIB sysib = { 0 }; + SysIB sysib = { }; int i, cc = 0; if ((r0 & STSI_R0_FC_MASK) > STSI_R0_FC_LEVEL_3) { diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index a25deef5dd..145b62a7ef 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -461,7 +461,8 @@ static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages, return ret; } if (!address_space_access_valid(&address_space_memory, pages[i], - TARGET_PAGE_SIZE, is_write)) { + TARGET_PAGE_SIZE, is_write, + MEMTXATTRS_UNSPECIFIED)) { trigger_access_exception(env, PGM_ADDRESSING, ILEN_AUTO, 0); return -EFAULT; } diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index aff1530c82..c1f9245797 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -280,7 +280,8 @@ static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg) cpu_synchronize_state(cs); if (!address_space_access_valid(&address_space_memory, addr, - sizeof(struct LowCore), false)) { + sizeof(struct LowCore), false, + MEMTXATTRS_UNSPECIFIED)) { set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); return; } diff --git a/target/s390x/tcg-stub.c b/target/s390x/tcg-stub.c new file mode 100644 index 0000000000..c93501db0b --- /dev/null +++ b/target/s390x/tcg-stub.c @@ -0,0 +1,20 @@ +/* + * QEMU TCG support -- s390x specific function stubs. + * + * Copyright (C) 2018 Red Hat Inc + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "tcg_s390x.h" + +void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) +{ +} diff --git a/target/s390x/tcg_s390x.h b/target/s390x/tcg_s390x.h new file mode 100644 index 0000000000..4e308aa0ce --- /dev/null +++ b/target/s390x/tcg_s390x.h @@ -0,0 +1,18 @@ +/* + * QEMU TCG support -- s390x specific functions. + * + * Copyright 2018 Red Hat, Inc. + * + * Authors: + * David Hildenbrand + * + * 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 TCG_S390X_H +#define TCG_S390X_H + +void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque); + +#endif /* TCG_S390X_H */ diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 7d39ab350d..57c03cbf58 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -42,6 +42,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/translator.h" #include "exec/log.h" @@ -51,14 +52,19 @@ typedef struct DisasInsn DisasInsn; typedef struct DisasFields DisasFields; struct DisasContext { - struct TranslationBlock *tb; + DisasContextBase base; const DisasInsn *insn; DisasFields *fields; uint64_t ex_value; - uint64_t pc, next_pc; + /* + * During translate_one(), pc_tmp is used to determine the instruction + * to be executed after base.pc_next - e.g. next sequential instruction + * or a branch target. + */ + uint64_t pc_tmp; uint32_t ilen; enum cc_op cc_op; - bool singlestep_enabled; + bool do_debug; }; /* Information carried about a condition to be evaluated. */ @@ -73,9 +79,6 @@ typedef struct { } u; } DisasCompare; -/* is_jmp field values */ -#define DISAS_EXCP DISAS_TARGET_0 - #ifdef DEBUG_INLINE_BRANCHES static uint64_t inline_branch_hit[CC_OP_MAX]; static uint64_t inline_branch_miss[CC_OP_MAX]; @@ -83,8 +86,8 @@ static uint64_t inline_branch_miss[CC_OP_MAX]; static uint64_t pc_to_link_info(DisasContext *s, uint64_t pc) { - if (!(s->tb->flags & FLAG_MASK_64)) { - if (s->tb->flags & FLAG_MASK_32) { + if (!(s->base.tb->flags & FLAG_MASK_64)) { + if (s->base.tb->flags & FLAG_MASK_32) { return pc | 0x80000000; } } @@ -190,16 +193,16 @@ static void return_low128(TCGv_i64 dest) static void update_psw_addr(DisasContext *s) { /* psw.addr */ - tcg_gen_movi_i64(psw_addr, s->pc); + tcg_gen_movi_i64(psw_addr, s->base.pc_next); } static void per_branch(DisasContext *s, bool to_next) { #ifndef CONFIG_USER_ONLY - tcg_gen_movi_i64(gbea, s->pc); + tcg_gen_movi_i64(gbea, s->base.pc_next); - if (s->tb->flags & FLAG_MASK_PER) { - TCGv_i64 next_pc = to_next ? tcg_const_i64(s->next_pc) : psw_addr; + if (s->base.tb->flags & FLAG_MASK_PER) { + TCGv_i64 next_pc = to_next ? tcg_const_i64(s->pc_tmp) : psw_addr; gen_helper_per_branch(cpu_env, gbea, next_pc); if (to_next) { tcg_temp_free_i64(next_pc); @@ -212,16 +215,16 @@ static void per_branch_cond(DisasContext *s, TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2) { #ifndef CONFIG_USER_ONLY - if (s->tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER) { TCGLabel *lab = gen_new_label(); tcg_gen_brcond_i64(tcg_invert_cond(cond), arg1, arg2, lab); - tcg_gen_movi_i64(gbea, s->pc); + tcg_gen_movi_i64(gbea, s->base.pc_next); gen_helper_per_branch(cpu_env, gbea, psw_addr); gen_set_label(lab); } else { - TCGv_i64 pc = tcg_const_i64(s->pc); + TCGv_i64 pc = tcg_const_i64(s->base.pc_next); tcg_gen_movcond_i64(cond, gbea, arg1, arg2, gbea, pc); tcg_temp_free_i64(pc); } @@ -230,7 +233,7 @@ static void per_branch_cond(DisasContext *s, TCGCond cond, static void per_breaking_event(DisasContext *s) { - tcg_gen_movi_i64(gbea, s->pc); + tcg_gen_movi_i64(gbea, s->base.pc_next); } static void update_cc_op(DisasContext *s) @@ -252,11 +255,11 @@ static inline uint64_t ld_code4(CPUS390XState *env, uint64_t pc) static int get_mem_index(DisasContext *s) { - if (!(s->tb->flags & FLAG_MASK_DAT)) { + if (!(s->base.tb->flags & FLAG_MASK_DAT)) { return MMU_REAL_IDX; } - switch (s->tb->flags & FLAG_MASK_ASC) { + switch (s->base.tb->flags & FLAG_MASK_ASC) { case PSW_ASC_PRIMARY >> FLAG_MASK_PSW_SHIFT: return MMU_PRIMARY_IDX; case PSW_ASC_SECONDARY >> FLAG_MASK_PSW_SHIFT: @@ -321,7 +324,7 @@ static inline void gen_trap(DisasContext *s) #ifndef CONFIG_USER_ONLY static void check_privileged(DisasContext *s) { - if (s->tb->flags & FLAG_MASK_PSTATE) { + if (s->base.tb->flags & FLAG_MASK_PSTATE) { gen_program_exception(s, PGM_PRIVILEGED); } } @@ -330,7 +333,7 @@ static void check_privileged(DisasContext *s) static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2) { TCGv_i64 tmp = tcg_temp_new_i64(); - bool need_31 = !(s->tb->flags & FLAG_MASK_64); + bool need_31 = !(s->base.tb->flags & FLAG_MASK_64); /* Note that d2 is limited to 20 bits, signed. If we crop negative displacements early we create larger immedate addends. */ @@ -543,9 +546,9 @@ static void gen_op_calc_cc(DisasContext *s) static bool use_exit_tb(DisasContext *s) { - return (s->singlestep_enabled || - (tb_cflags(s->tb) & CF_LAST_IO) || - (s->tb->flags & FLAG_MASK_PER)); + return s->base.singlestep_enabled || + (tb_cflags(s->base.tb) & CF_LAST_IO) || + (s->base.tb->flags & FLAG_MASK_PER); } static bool use_goto_tb(DisasContext *s, uint64_t dest) @@ -554,8 +557,8 @@ static bool use_goto_tb(DisasContext *s, uint64_t dest) return false; } #ifndef CONFIG_USER_ONLY - return (dest & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK) || - (dest & TARGET_PAGE_MASK) == (s->pc & TARGET_PAGE_MASK); + return (dest & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK) || + (dest & TARGET_PAGE_MASK) == (s->base.pc_next & TARGET_PAGE_MASK); #else return true; #endif @@ -1091,26 +1094,24 @@ typedef struct { #define SPEC_r2_f128 16 /* Return values from translate_one, indicating the state of the TB. */ -typedef enum { - /* Continue the TB. */ - NO_EXIT, - /* We have emitted one or more goto_tb. No fixup required. */ - EXIT_GOTO_TB, - /* We are not using a goto_tb (for whatever reason), but have updated - the PC (for whatever reason), so there's no need to do it again on - exiting the TB. */ - EXIT_PC_UPDATED, - /* We have updated the PC and CC values. */ - EXIT_PC_CC_UPDATED, - /* We are exiting the TB, but have neither emitted a goto_tb, nor - updated the PC for the next instruction to be executed. */ - EXIT_PC_STALE, - /* We are exiting the TB to the main loop. */ - EXIT_PC_STALE_NOCHAIN, - /* We are ending the TB with a noreturn function call, e.g. longjmp. - No following code will be executed. */ - EXIT_NORETURN, -} ExitStatus; + +/* We are not using a goto_tb (for whatever reason), but have updated + the PC (for whatever reason), so there's no need to do it again on + exiting the TB. */ +#define DISAS_PC_UPDATED DISAS_TARGET_0 + +/* We have emitted one or more goto_tb. No fixup required. */ +#define DISAS_GOTO_TB DISAS_TARGET_1 + +/* We have updated the PC and CC values. */ +#define DISAS_PC_CC_UPDATED DISAS_TARGET_2 + +/* We are exiting the TB, but have neither emitted a goto_tb, nor + updated the PC for the next instruction to be executed. */ +#define DISAS_PC_STALE DISAS_TARGET_3 + +/* We are exiting the TB to the main loop. */ +#define DISAS_PC_STALE_NOCHAIN DISAS_TARGET_4 struct DisasInsn { unsigned opc:16; @@ -1125,7 +1126,7 @@ struct DisasInsn { void (*help_prep)(DisasContext *, DisasFields *, DisasOps *); void (*help_wout)(DisasContext *, DisasFields *, DisasOps *); void (*help_cout)(DisasContext *, DisasOps *); - ExitStatus (*help_op)(DisasContext *, DisasOps *); + DisasJumpType (*help_op)(DisasContext *, DisasOps *); uint64_t data; }; @@ -1147,43 +1148,43 @@ static void help_l2_shift(DisasContext *s, DisasFields *f, } } -static ExitStatus help_goto_direct(DisasContext *s, uint64_t dest) +static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest) { - if (dest == s->next_pc) { + if (dest == s->pc_tmp) { per_branch(s, true); - return NO_EXIT; + return DISAS_NEXT; } if (use_goto_tb(s, dest)) { update_cc_op(s); per_breaking_event(s); tcg_gen_goto_tb(0); tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb((uintptr_t)s->tb); - return EXIT_GOTO_TB; + tcg_gen_exit_tb(s->base.tb, 0); + return DISAS_GOTO_TB; } else { tcg_gen_movi_i64(psw_addr, dest); per_branch(s, false); - return EXIT_PC_UPDATED; + return DISAS_PC_UPDATED; } } -static ExitStatus help_branch(DisasContext *s, DisasCompare *c, - bool is_imm, int imm, TCGv_i64 cdest) +static DisasJumpType help_branch(DisasContext *s, DisasCompare *c, + bool is_imm, int imm, TCGv_i64 cdest) { - ExitStatus ret; - uint64_t dest = s->pc + 2 * imm; + DisasJumpType ret; + uint64_t dest = s->base.pc_next + 2 * imm; TCGLabel *lab; /* Take care of the special cases first. */ if (c->cond == TCG_COND_NEVER) { - ret = NO_EXIT; + ret = DISAS_NEXT; goto egress; } if (is_imm) { - if (dest == s->next_pc) { + if (dest == s->pc_tmp) { /* Branch to next. */ per_branch(s, true); - ret = NO_EXIT; + ret = DISAS_NEXT; goto egress; } if (c->cond == TCG_COND_ALWAYS) { @@ -1193,18 +1194,18 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, } else { if (!cdest) { /* E.g. bcr %r0 -> no branch. */ - ret = NO_EXIT; + ret = DISAS_NEXT; goto egress; } if (c->cond == TCG_COND_ALWAYS) { tcg_gen_mov_i64(psw_addr, cdest); per_branch(s, false); - ret = EXIT_PC_UPDATED; + ret = DISAS_PC_UPDATED; goto egress; } } - if (use_goto_tb(s, s->next_pc)) { + if (use_goto_tb(s, s->pc_tmp)) { if (is_imm && use_goto_tb(s, dest)) { /* Both exits can use goto_tb. */ update_cc_op(s); @@ -1218,17 +1219,17 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, /* Branch not taken. */ tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->next_pc); - tcg_gen_exit_tb((uintptr_t)s->tb + 0); + tcg_gen_movi_i64(psw_addr, s->pc_tmp); + tcg_gen_exit_tb(s->base.tb, 0); /* Branch taken. */ gen_set_label(lab); per_breaking_event(s); tcg_gen_goto_tb(1); tcg_gen_movi_i64(psw_addr, dest); - tcg_gen_exit_tb((uintptr_t)s->tb + 1); + tcg_gen_exit_tb(s->base.tb, 1); - ret = EXIT_GOTO_TB; + ret = DISAS_GOTO_TB; } else { /* Fallthru can use goto_tb, but taken branch cannot. */ /* Store taken branch destination before the brcond. This @@ -1248,22 +1249,22 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, /* Branch not taken. */ update_cc_op(s); tcg_gen_goto_tb(0); - tcg_gen_movi_i64(psw_addr, s->next_pc); - tcg_gen_exit_tb((uintptr_t)s->tb + 0); + tcg_gen_movi_i64(psw_addr, s->pc_tmp); + tcg_gen_exit_tb(s->base.tb, 0); gen_set_label(lab); if (is_imm) { tcg_gen_movi_i64(psw_addr, dest); } per_breaking_event(s); - ret = EXIT_PC_UPDATED; + ret = DISAS_PC_UPDATED; } } else { /* Fallthru cannot use goto_tb. This by itself is vanishingly rare. Most commonly we're single-stepping or some other condition that disables all use of goto_tb. Just update the PC and exit. */ - TCGv_i64 next = tcg_const_i64(s->next_pc); + TCGv_i64 next = tcg_const_i64(s->pc_tmp); if (is_imm) { cdest = tcg_const_i64(dest); } @@ -1290,7 +1291,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, } tcg_temp_free_i64(next); - ret = EXIT_PC_UPDATED; + ret = DISAS_PC_UPDATED; } egress: @@ -1302,7 +1303,7 @@ static ExitStatus help_branch(DisasContext *s, DisasCompare *c, /* The operations. These perform the bulk of the work for any insn, usually after the operands have been loaded and output initialized. */ -static ExitStatus op_abs(DisasContext *s, DisasOps *o) +static DisasJumpType op_abs(DisasContext *s, DisasOps *o) { TCGv_i64 z, n; z = tcg_const_i64(0); @@ -1311,35 +1312,35 @@ static ExitStatus op_abs(DisasContext *s, DisasOps *o) tcg_gen_movcond_i64(TCG_COND_LT, o->out, o->in2, z, n, o->in2); tcg_temp_free_i64(n); tcg_temp_free_i64(z); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_absf32(DisasContext *s, DisasOps *o) +static DisasJumpType op_absf32(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_absf64(DisasContext *s, DisasOps *o) +static DisasJumpType op_absf64(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffffffffffull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_absf128(DisasContext *s, DisasOps *o) +static DisasJumpType op_absf128(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in1, 0x7fffffffffffffffull); tcg_gen_mov_i64(o->out2, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_add(DisasContext *s, DisasOps *o) +static DisasJumpType op_add(DisasContext *s, DisasOps *o) { tcg_gen_add_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_addc(DisasContext *s, DisasOps *o) +static DisasJumpType op_addc(DisasContext *s, DisasOps *o) { DisasCompare cmp; TCGv_i64 carry; @@ -1363,10 +1364,10 @@ static ExitStatus op_addc(DisasContext *s, DisasOps *o) tcg_gen_add_i64(o->out, o->out, carry); tcg_temp_free_i64(carry); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_asi(DisasContext *s, DisasOps *o) +static DisasJumpType op_asi(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); @@ -1384,35 +1385,35 @@ static ExitStatus op_asi(DisasContext *s, DisasOps *o) if (!s390_has_feat(S390_FEAT_STFLE_45)) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), s->insn->data); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_aeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_aeb(DisasContext *s, DisasOps *o) { gen_helper_aeb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_adb(DisasContext *s, DisasOps *o) +static DisasJumpType op_adb(DisasContext *s, DisasOps *o) { gen_helper_adb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_axb(DisasContext *s, DisasOps *o) +static DisasJumpType op_axb(DisasContext *s, DisasOps *o) { gen_helper_axb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_and(DisasContext *s, DisasOps *o) +static DisasJumpType op_and(DisasContext *s, DisasOps *o) { tcg_gen_and_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_andi(DisasContext *s, DisasOps *o) +static DisasJumpType op_andi(DisasContext *s, DisasOps *o) { int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; @@ -1426,10 +1427,10 @@ static ExitStatus op_andi(DisasContext *s, DisasOps *o) /* Produce the CC from only the bits manipulated. */ tcg_gen_andi_i64(cc_dst, o->out, mask); set_cc_nz_u64(s, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ni(DisasContext *s, DisasOps *o) +static DisasJumpType op_ni(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); @@ -1447,28 +1448,28 @@ static ExitStatus op_ni(DisasContext *s, DisasOps *o) if (!s390_has_feat(S390_FEAT_INTERLOCKED_ACCESS_2)) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), s->insn->data); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_bas(DisasContext *s, DisasOps *o) +static DisasJumpType op_bas(DisasContext *s, DisasOps *o) { - tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc)); + tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->pc_tmp)); if (o->in2) { tcg_gen_mov_i64(psw_addr, o->in2); per_branch(s, false); - return EXIT_PC_UPDATED; + return DISAS_PC_UPDATED; } else { - return NO_EXIT; + return DISAS_NEXT; } } -static ExitStatus op_basi(DisasContext *s, DisasOps *o) +static DisasJumpType op_basi(DisasContext *s, DisasOps *o) { - tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->next_pc)); - return help_goto_direct(s, s->pc + 2 * get_field(s->fields, i2)); + tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->pc_tmp)); + return help_goto_direct(s, s->base.pc_next + 2 * get_field(s->fields, i2)); } -static ExitStatus op_bc(DisasContext *s, DisasOps *o) +static DisasJumpType op_bc(DisasContext *s, DisasOps *o) { int m1 = get_field(s->fields, m1); bool is_imm = have_field(s->fields, i2); @@ -1487,14 +1488,14 @@ static ExitStatus op_bc(DisasContext *s, DisasOps *o) /* FIXME: perform checkpoint-synchronisation */ tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); } - return NO_EXIT; + return DISAS_NEXT; } disas_jcc(s, &c, m1); return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_bct32(DisasContext *s, DisasOps *o) +static DisasJumpType op_bct32(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); bool is_imm = have_field(s->fields, i2); @@ -1518,7 +1519,7 @@ static ExitStatus op_bct32(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_bcth(DisasContext *s, DisasOps *o) +static DisasJumpType op_bcth(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int imm = get_field(s->fields, i2); @@ -1542,7 +1543,7 @@ static ExitStatus op_bcth(DisasContext *s, DisasOps *o) return help_branch(s, &c, 1, imm, o->in2); } -static ExitStatus op_bct64(DisasContext *s, DisasOps *o) +static DisasJumpType op_bct64(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); bool is_imm = have_field(s->fields, i2); @@ -1561,7 +1562,7 @@ static ExitStatus op_bct64(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_bx32(DisasContext *s, DisasOps *o) +static DisasJumpType op_bx32(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -1587,7 +1588,7 @@ static ExitStatus op_bx32(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_bx64(DisasContext *s, DisasOps *o) +static DisasJumpType op_bx64(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -1613,7 +1614,7 @@ static ExitStatus op_bx64(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->in2); } -static ExitStatus op_cj(DisasContext *s, DisasOps *o) +static DisasJumpType op_cj(DisasContext *s, DisasOps *o) { int imm, m3 = get_field(s->fields, m3); bool is_imm; @@ -1639,186 +1640,186 @@ static ExitStatus op_cj(DisasContext *s, DisasOps *o) return help_branch(s, &c, is_imm, imm, o->out); } -static ExitStatus op_ceb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ceb(DisasContext *s, DisasOps *o) { gen_helper_ceb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cdb(DisasContext *s, DisasOps *o) { gen_helper_cdb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cxb(DisasContext *s, DisasOps *o) { gen_helper_cxb(cc_op, cpu_env, o->out, o->out2, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cfeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cfeb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cfeb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f32(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cfdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cfdb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cfdb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f64(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cfxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cfxb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cfxb(o->out, cpu_env, o->in1, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f128(s, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cgeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cgeb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cgeb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f32(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cgdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cgdb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cgdb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f64(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cgxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cgxb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cgxb(o->out, cpu_env, o->in1, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f128(s, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clfeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clfeb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clfeb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f32(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clfdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clfdb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clfdb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f64(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clfxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clfxb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clfxb(o->out, cpu_env, o->in1, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f128(s, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clgeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clgeb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clgeb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f32(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clgdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clgdb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clgdb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f64(s, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clgxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_clgxb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_clgxb(o->out, cpu_env, o->in1, o->in2, m3); tcg_temp_free_i32(m3); gen_set_cc_nz_f128(s, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cegb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cegb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cegb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cdgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cdgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cdgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cxgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cxgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cxgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_celgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_celgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_celgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cdlgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cdlgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cdlgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cxlgb(DisasContext *s, DisasOps *o) +static DisasJumpType op_cxlgb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_cxlgb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cksm(DisasContext *s, DisasOps *o) +static DisasJumpType op_cksm(DisasContext *s, DisasOps *o) { int r2 = get_field(s->fields, r2); TCGv_i64 len = tcg_temp_new_i64(); @@ -1831,10 +1832,10 @@ static ExitStatus op_cksm(DisasContext *s, DisasOps *o) tcg_gen_sub_i64(regs[r2 + 1], regs[r2 + 1], len); tcg_temp_free_i64(len); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clc(DisasContext *s, DisasOps *o) +static DisasJumpType op_clc(DisasContext *s, DisasOps *o) { int l = get_field(s->fields, l1); TCGv_i32 vl; @@ -1861,13 +1862,13 @@ static ExitStatus op_clc(DisasContext *s, DisasOps *o) gen_helper_clc(cc_op, cpu_env, vl, o->addr1, o->in2); tcg_temp_free_i32(vl); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } gen_op_update2_cc_i64(s, CC_OP_LTUGTU_64, cc_src, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clcl(DisasContext *s, DisasOps *o) +static DisasJumpType op_clcl(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r2 = get_field(s->fields, r2); @@ -1876,7 +1877,7 @@ static ExitStatus op_clcl(DisasContext *s, DisasOps *o) /* r1 and r2 must be even. */ if (r1 & 1 || r2 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -1885,10 +1886,10 @@ static ExitStatus op_clcl(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clcle(DisasContext *s, DisasOps *o) +static DisasJumpType op_clcle(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -1897,7 +1898,7 @@ static ExitStatus op_clcle(DisasContext *s, DisasOps *o) /* r1 and r3 must be even. */ if (r1 & 1 || r3 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -1906,10 +1907,10 @@ static ExitStatus op_clcle(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clclu(DisasContext *s, DisasOps *o) +static DisasJumpType op_clclu(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -1918,7 +1919,7 @@ static ExitStatus op_clclu(DisasContext *s, DisasOps *o) /* r1 and r3 must be even. */ if (r1 & 1 || r3 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -1927,10 +1928,10 @@ static ExitStatus op_clclu(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clm(DisasContext *s, DisasOps *o) +static DisasJumpType op_clm(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); TCGv_i32 t1 = tcg_temp_new_i32(); @@ -1939,28 +1940,28 @@ static ExitStatus op_clm(DisasContext *s, DisasOps *o) set_cc_static(s); tcg_temp_free_i32(t1); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_clst(DisasContext *s, DisasOps *o) +static DisasJumpType op_clst(DisasContext *s, DisasOps *o) { gen_helper_clst(o->in1, cpu_env, regs[0], o->in1, o->in2); set_cc_static(s); return_low128(o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cps(DisasContext *s, DisasOps *o) +static DisasJumpType op_cps(DisasContext *s, DisasOps *o) { TCGv_i64 t = tcg_temp_new_i64(); tcg_gen_andi_i64(t, o->in1, 0x8000000000000000ull); tcg_gen_andi_i64(o->out, o->in2, 0x7fffffffffffffffull); tcg_gen_or_i64(o->out, o->out, t); tcg_temp_free_i64(t); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cs(DisasContext *s, DisasOps *o) +static DisasJumpType op_cs(DisasContext *s, DisasOps *o) { int d2 = get_field(s->fields, d2); int b2 = get_field(s->fields, b2); @@ -1982,10 +1983,10 @@ static ExitStatus op_cs(DisasContext *s, DisasOps *o) tcg_temp_free_i64(cc); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) +static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -1998,7 +1999,7 @@ static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) addr = get_address(s, 0, b2, d2); t_r1 = tcg_const_i32(r1); t_r3 = tcg_const_i32(r3); - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_cdsg_parallel(cpu_env, addr, t_r1, t_r3); } else { gen_helper_cdsg(cpu_env, addr, t_r1, t_r3); @@ -2008,15 +2009,15 @@ static ExitStatus op_cdsg(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t_r3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_csst(DisasContext *s, DisasOps *o) +static DisasJumpType op_csst(DisasContext *s, DisasOps *o) { int r3 = get_field(s->fields, r3); TCGv_i32 t_r3 = tcg_const_i32(r3); - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_csst_parallel(cc_op, cpu_env, t_r3, o->in1, o->in2); } else { gen_helper_csst(cc_op, cpu_env, t_r3, o->in1, o->in2); @@ -2024,11 +2025,11 @@ static ExitStatus op_csst(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t_r3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_csp(DisasContext *s, DisasOps *o) +static DisasJumpType op_csp(DisasContext *s, DisasOps *o) { TCGMemOp mop = s->insn->data; TCGv_i64 addr, old, cc; @@ -2069,11 +2070,11 @@ static ExitStatus op_csp(DisasContext *s, DisasOps *o) gen_helper_purge(cpu_env); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_cvd(DisasContext *s, DisasOps *o) +static DisasJumpType op_cvd(DisasContext *s, DisasOps *o) { TCGv_i64 t1 = tcg_temp_new_i64(); TCGv_i32 t2 = tcg_temp_new_i32(); @@ -2082,10 +2083,10 @@ static ExitStatus op_cvd(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t2); tcg_gen_qemu_st64(t1, o->in2, get_mem_index(s)); tcg_temp_free_i64(t1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ct(DisasContext *s, DisasOps *o) +static DisasJumpType op_ct(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); TCGLabel *lab = gen_new_label(); @@ -2101,10 +2102,10 @@ static ExitStatus op_ct(DisasContext *s, DisasOps *o) gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_cuXX(DisasContext *s, DisasOps *o) +static DisasJumpType op_cuXX(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); int r1 = get_field(s->fields, r1); @@ -2114,7 +2115,7 @@ static ExitStatus op_cuXX(DisasContext *s, DisasOps *o) /* R1 and R2 must both be even. */ if ((r1 | r2) & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } if (!s390_has_feat(S390_FEAT_ETF3_ENH)) { m3 = 0; @@ -2151,11 +2152,11 @@ static ExitStatus op_cuXX(DisasContext *s, DisasOps *o) tcg_temp_free_i32(tr2); tcg_temp_free_i32(chk); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_diag(DisasContext *s, DisasOps *o) +static DisasJumpType op_diag(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -2167,78 +2168,78 @@ static ExitStatus op_diag(DisasContext *s, DisasOps *o) tcg_temp_free_i32(func_code); tcg_temp_free_i32(r3); tcg_temp_free_i32(r1); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_divs32(DisasContext *s, DisasOps *o) +static DisasJumpType op_divs32(DisasContext *s, DisasOps *o) { gen_helper_divs32(o->out2, cpu_env, o->in1, o->in2); return_low128(o->out); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_divu32(DisasContext *s, DisasOps *o) +static DisasJumpType op_divu32(DisasContext *s, DisasOps *o) { gen_helper_divu32(o->out2, cpu_env, o->in1, o->in2); return_low128(o->out); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_divs64(DisasContext *s, DisasOps *o) +static DisasJumpType op_divs64(DisasContext *s, DisasOps *o) { gen_helper_divs64(o->out2, cpu_env, o->in1, o->in2); return_low128(o->out); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_divu64(DisasContext *s, DisasOps *o) +static DisasJumpType op_divu64(DisasContext *s, DisasOps *o) { gen_helper_divu64(o->out2, cpu_env, o->out, o->out2, o->in2); return_low128(o->out); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_deb(DisasContext *s, DisasOps *o) +static DisasJumpType op_deb(DisasContext *s, DisasOps *o) { gen_helper_deb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ddb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ddb(DisasContext *s, DisasOps *o) { gen_helper_ddb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_dxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_dxb(DisasContext *s, DisasOps *o) { gen_helper_dxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ear(DisasContext *s, DisasOps *o) +static DisasJumpType op_ear(DisasContext *s, DisasOps *o) { int r2 = get_field(s->fields, r2); tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, aregs[r2])); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ecag(DisasContext *s, DisasOps *o) +static DisasJumpType op_ecag(DisasContext *s, DisasOps *o) { /* No cache information provided. */ tcg_gen_movi_i64(o->out, -1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_efpc(DisasContext *s, DisasOps *o) +static DisasJumpType op_efpc(DisasContext *s, DisasOps *o) { tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, fpc)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_epsw(DisasContext *s, DisasOps *o) +static DisasJumpType op_epsw(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r2 = get_field(s->fields, r2); @@ -2253,10 +2254,10 @@ static ExitStatus op_epsw(DisasContext *s, DisasOps *o) } tcg_temp_free_i64(t); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ex(DisasContext *s, DisasOps *o) +static DisasJumpType op_ex(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); TCGv_i32 ilen; @@ -2265,7 +2266,7 @@ static ExitStatus op_ex(DisasContext *s, DisasOps *o) /* Nested EXECUTE is not allowed. */ if (unlikely(s->ex_value)) { gen_program_exception(s, PGM_EXECUTE); - return EXIT_NORETURN; + return DISAS_NORETURN; } update_psw_addr(s); @@ -2285,35 +2286,35 @@ static ExitStatus op_ex(DisasContext *s, DisasOps *o) tcg_temp_free_i64(v1); } - return EXIT_PC_CC_UPDATED; + return DISAS_PC_CC_UPDATED; } -static ExitStatus op_fieb(DisasContext *s, DisasOps *o) +static DisasJumpType op_fieb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_fieb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_fidb(DisasContext *s, DisasOps *o) +static DisasJumpType op_fidb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_fidb(o->out, cpu_env, o->in2, m3); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_fixb(DisasContext *s, DisasOps *o) +static DisasJumpType op_fixb(DisasContext *s, DisasOps *o) { TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m3); return_low128(o->out2); tcg_temp_free_i32(m3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_flogr(DisasContext *s, DisasOps *o) +static DisasJumpType op_flogr(DisasContext *s, DisasOps *o) { /* We'll use the original input for cc computation, since we get to compare that against 0, which ought to be better than comparing @@ -2330,10 +2331,10 @@ static ExitStatus op_flogr(DisasContext *s, DisasOps *o) tcg_gen_movi_i64(o->out2, 0x8000000000000000ull); tcg_gen_shr_i64(o->out2, o->out2, o->out); tcg_gen_andc_i64(o->out2, cc_dst, o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_icm(DisasContext *s, DisasOps *o) +static DisasJumpType op_icm(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); int pos, len, base = s->insn->data; @@ -2390,18 +2391,18 @@ static ExitStatus op_icm(DisasContext *s, DisasOps *o) tcg_gen_movi_i64(tmp, ccm); gen_op_update2_cc_i64(s, CC_OP_ICM, tmp, o->out); tcg_temp_free_i64(tmp); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_insi(DisasContext *s, DisasOps *o) +static DisasJumpType op_insi(DisasContext *s, DisasOps *o) { int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; tcg_gen_deposit_i64(o->out, o->in1, o->in2, shift, size); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ipm(DisasContext *s, DisasOps *o) +static DisasJumpType op_ipm(DisasContext *s, DisasOps *o) { TCGv_i64 t1; @@ -2417,11 +2418,11 @@ static ExitStatus op_ipm(DisasContext *s, DisasOps *o) tcg_gen_shli_i64(t1, t1, 28); tcg_gen_or_i64(o->out, o->out, t1); tcg_temp_free_i64(t1); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_idte(DisasContext *s, DisasOps *o) +static DisasJumpType op_idte(DisasContext *s, DisasOps *o) { TCGv_i32 m4; @@ -2433,10 +2434,10 @@ static ExitStatus op_idte(DisasContext *s, DisasOps *o) } gen_helper_idte(cpu_env, o->in1, o->in2, m4); tcg_temp_free_i32(m4); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ipte(DisasContext *s, DisasOps *o) +static DisasJumpType op_ipte(DisasContext *s, DisasOps *o) { TCGv_i32 m4; @@ -2448,18 +2449,18 @@ static ExitStatus op_ipte(DisasContext *s, DisasOps *o) } gen_helper_ipte(cpu_env, o->in1, o->in2, m4); tcg_temp_free_i32(m4); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_iske(DisasContext *s, DisasOps *o) +static DisasJumpType op_iske(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_iske(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_msa(DisasContext *s, DisasOps *o) +static DisasJumpType op_msa(DisasContext *s, DisasOps *o) { int r1 = have_field(s->fields, r1) ? get_field(s->fields, r1) : 0; int r2 = have_field(s->fields, r2) ? get_field(s->fields, r2) : 0; @@ -2470,7 +2471,7 @@ static ExitStatus op_msa(DisasContext *s, DisasOps *o) case S390_FEAT_TYPE_KMCTR: if (r3 & 1 || !r3) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } /* FALL THROUGH */ case S390_FEAT_TYPE_PPNO: @@ -2480,7 +2481,7 @@ static ExitStatus op_msa(DisasContext *s, DisasOps *o) case S390_FEAT_TYPE_KM: if (r1 & 1 || !r1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } /* FALL THROUGH */ case S390_FEAT_TYPE_KMAC: @@ -2488,7 +2489,7 @@ static ExitStatus op_msa(DisasContext *s, DisasOps *o) case S390_FEAT_TYPE_KLMD: if (r2 & 1 || !r2) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } /* FALL THROUGH */ case S390_FEAT_TYPE_PCKMO: @@ -2508,31 +2509,31 @@ static ExitStatus op_msa(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t_r2); tcg_temp_free_i32(t_r3); tcg_temp_free_i32(type); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_keb(DisasContext *s, DisasOps *o) +static DisasJumpType op_keb(DisasContext *s, DisasOps *o) { gen_helper_keb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_kdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_kdb(DisasContext *s, DisasOps *o) { gen_helper_kdb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_kxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_kxb(DisasContext *s, DisasOps *o) { gen_helper_kxb(cc_op, cpu_env, o->out, o->out2, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_laa(DisasContext *s, DisasOps *o) +static DisasJumpType op_laa(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ @@ -2540,10 +2541,10 @@ static ExitStatus op_laa(DisasContext *s, DisasOps *o) s->insn->data | MO_ALIGN); /* However, we need to recompute the addition for setting CC. */ tcg_gen_add_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lan(DisasContext *s, DisasOps *o) +static DisasJumpType op_lan(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ @@ -2551,10 +2552,10 @@ static ExitStatus op_lan(DisasContext *s, DisasOps *o) s->insn->data | MO_ALIGN); /* However, we need to recompute the operation for setting CC. */ tcg_gen_and_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lao(DisasContext *s, DisasOps *o) +static DisasJumpType op_lao(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ @@ -2562,10 +2563,10 @@ static ExitStatus op_lao(DisasContext *s, DisasOps *o) s->insn->data | MO_ALIGN); /* However, we need to recompute the operation for setting CC. */ tcg_gen_or_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lax(DisasContext *s, DisasOps *o) +static DisasJumpType op_lax(DisasContext *s, DisasOps *o) { /* The real output is indeed the original value in memory; recompute the addition for the computation of CC. */ @@ -2573,96 +2574,96 @@ static ExitStatus op_lax(DisasContext *s, DisasOps *o) s->insn->data | MO_ALIGN); /* However, we need to recompute the operation for setting CC. */ tcg_gen_xor_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ldeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ldeb(DisasContext *s, DisasOps *o) { gen_helper_ldeb(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ledb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ledb(DisasContext *s, DisasOps *o) { gen_helper_ledb(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ldxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ldxb(DisasContext *s, DisasOps *o) { gen_helper_ldxb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lexb(DisasContext *s, DisasOps *o) +static DisasJumpType op_lexb(DisasContext *s, DisasOps *o) { gen_helper_lexb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lxdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_lxdb(DisasContext *s, DisasOps *o) { gen_helper_lxdb(o->out, cpu_env, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lxeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_lxeb(DisasContext *s, DisasOps *o) { gen_helper_lxeb(o->out, cpu_env, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_llgt(DisasContext *s, DisasOps *o) +static DisasJumpType op_llgt(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld8s(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld8s(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld8s(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld8u(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld8u(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld8u(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld16s(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld16s(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld16s(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld16u(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld16u(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld16u(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld32s(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld32s(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld32s(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld32u(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld32u(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ld64(DisasContext *s, DisasOps *o) +static DisasJumpType op_ld64(DisasContext *s, DisasOps *o) { tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lat(DisasContext *s, DisasOps *o) +static DisasJumpType op_lat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); store_reg32_i64(get_field(s->fields, r1), o->in2); @@ -2670,10 +2671,10 @@ static ExitStatus op_lat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lgat(DisasContext *s, DisasOps *o) +static DisasJumpType op_lgat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); tcg_gen_qemu_ld64(o->out, o->in2, get_mem_index(s)); @@ -2681,10 +2682,10 @@ static ExitStatus op_lgat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lfhat(DisasContext *s, DisasOps *o) +static DisasJumpType op_lfhat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); store_reg32h_i64(get_field(s->fields, r1), o->in2); @@ -2692,10 +2693,10 @@ static ExitStatus op_lfhat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->in2, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_llgfat(DisasContext *s, DisasOps *o) +static DisasJumpType op_llgfat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); tcg_gen_qemu_ld32u(o->out, o->in2, get_mem_index(s)); @@ -2703,10 +2704,10 @@ static ExitStatus op_llgfat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_llgtat(DisasContext *s, DisasOps *o) +static DisasJumpType op_llgtat(DisasContext *s, DisasOps *o) { TCGLabel *lab = gen_new_label(); tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff); @@ -2714,10 +2715,10 @@ static ExitStatus op_llgtat(DisasContext *s, DisasOps *o) tcg_gen_brcondi_i64(TCG_COND_NE, o->out, 0, lab); gen_trap(s); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_loc(DisasContext *s, DisasOps *o) +static DisasJumpType op_loc(DisasContext *s, DisasOps *o) { DisasCompare c; @@ -2744,11 +2745,11 @@ static ExitStatus op_loc(DisasContext *s, DisasOps *o) tcg_temp_free_i64(z); } - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_lctl(DisasContext *s, DisasOps *o) +static DisasJumpType op_lctl(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -2757,10 +2758,10 @@ static ExitStatus op_lctl(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return EXIT_PC_STALE_NOCHAIN; + return DISAS_PC_STALE_NOCHAIN; } -static ExitStatus op_lctlg(DisasContext *s, DisasOps *o) +static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -2769,26 +2770,26 @@ static ExitStatus op_lctlg(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return EXIT_PC_STALE_NOCHAIN; + return DISAS_PC_STALE_NOCHAIN; } -static ExitStatus op_lra(DisasContext *s, DisasOps *o) +static DisasJumpType op_lra(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_lra(o->out, cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lpp(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpp(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_st_i64(o->in2, cpu_env, offsetof(CPUS390XState, pp)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lpsw(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpsw(DisasContext *s, DisasOps *o) { TCGv_i64 t1, t2; @@ -2805,10 +2806,10 @@ static ExitStatus op_lpsw(DisasContext *s, DisasOps *o) gen_helper_load_psw(cpu_env, t1, t2); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); - return EXIT_NORETURN; + return DISAS_NORETURN; } -static ExitStatus op_lpswe(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpswe(DisasContext *s, DisasOps *o) { TCGv_i64 t1, t2; @@ -2823,21 +2824,21 @@ static ExitStatus op_lpswe(DisasContext *s, DisasOps *o) gen_helper_load_psw(cpu_env, t1, t2); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); - return EXIT_NORETURN; + return DISAS_NORETURN; } #endif -static ExitStatus op_lam(DisasContext *s, DisasOps *o) +static DisasJumpType op_lam(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); gen_helper_lam(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lm32(DisasContext *s, DisasOps *o) +static DisasJumpType op_lm32(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -2849,7 +2850,7 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); store_reg32_i64(r1, t1); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* First load the values of the first and last registers to trigger @@ -2865,7 +2866,7 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o) if (((r1 + 1) & 15) == r3) { tcg_temp_free(t2); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* Then load the remaining registers. Page fault can't occur. */ @@ -2880,10 +2881,10 @@ static ExitStatus op_lm32(DisasContext *s, DisasOps *o) tcg_temp_free(t2); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lmh(DisasContext *s, DisasOps *o) +static DisasJumpType op_lmh(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -2895,7 +2896,7 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o) tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); store_reg32h_i64(r1, t1); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* First load the values of the first and last registers to trigger @@ -2911,7 +2912,7 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o) if (((r1 + 1) & 15) == r3) { tcg_temp_free(t2); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* Then load the remaining registers. Page fault can't occur. */ @@ -2926,10 +2927,10 @@ static ExitStatus op_lmh(DisasContext *s, DisasOps *o) tcg_temp_free(t2); tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lm64(DisasContext *s, DisasOps *o) +static DisasJumpType op_lm64(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -2938,7 +2939,7 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o) /* Only one register to read. */ if (unlikely(r1 == r3)) { tcg_gen_qemu_ld64(regs[r1], o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } /* First load the values of the first and last registers to trigger @@ -2954,7 +2955,7 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o) /* Only two registers to read. */ if (((r1 + 1) & 15) == r3) { tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } /* Then load the remaining registers. Page fault can't occur. */ @@ -2967,20 +2968,20 @@ static ExitStatus op_lm64(DisasContext *s, DisasOps *o) } tcg_temp_free(t1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lpd(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpd(DisasContext *s, DisasOps *o) { TCGv_i64 a1, a2; TCGMemOp mop = s->insn->data; /* In a parallel context, stop the world and single step. */ - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { update_psw_addr(s); update_cc_op(s); gen_exception(EXCP_ATOMIC); - return EXIT_NORETURN; + return DISAS_NORETURN; } /* In a serial context, perform the two loads ... */ @@ -2993,52 +2994,52 @@ static ExitStatus op_lpd(DisasContext *s, DisasOps *o) /* ... and indicate that we performed them while interlocked. */ gen_op_movi_cc(s, 0); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lpq(DisasContext *s, DisasOps *o) +static DisasJumpType op_lpq(DisasContext *s, DisasOps *o) { - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_lpq_parallel(o->out, cpu_env, o->in2); } else { gen_helper_lpq(o->out, cpu_env, o->in2); } return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_lura(DisasContext *s, DisasOps *o) +static DisasJumpType op_lura(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_lura(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_lurag(DisasContext *s, DisasOps *o) +static DisasJumpType op_lurag(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_lurag(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_lzrb(DisasContext *s, DisasOps *o) +static DisasJumpType op_lzrb(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in2, -256); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mov2(DisasContext *s, DisasOps *o) +static DisasJumpType op_mov2(DisasContext *s, DisasOps *o) { o->out = o->in2; o->g_out = o->g_in2; o->in2 = NULL; o->g_in2 = false; - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mov2e(DisasContext *s, DisasOps *o) +static DisasJumpType op_mov2e(DisasContext *s, DisasOps *o) { int b2 = get_field(s->fields, b2); TCGv ar1 = tcg_temp_new_i64(); @@ -3048,7 +3049,7 @@ static ExitStatus op_mov2e(DisasContext *s, DisasOps *o) o->in2 = NULL; o->g_in2 = false; - switch (s->tb->flags & FLAG_MASK_ASC) { + switch (s->base.tb->flags & FLAG_MASK_ASC) { case PSW_ASC_PRIMARY >> FLAG_MASK_PSW_SHIFT: tcg_gen_movi_i64(ar1, 0); break; @@ -3070,10 +3071,10 @@ static ExitStatus op_mov2e(DisasContext *s, DisasOps *o) tcg_gen_st32_i64(ar1, cpu_env, offsetof(CPUS390XState, aregs[1])); tcg_temp_free_i64(ar1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_movx(DisasContext *s, DisasOps *o) +static DisasJumpType op_movx(DisasContext *s, DisasOps *o) { o->out = o->in1; o->out2 = o->in2; @@ -3082,26 +3083,26 @@ static ExitStatus op_movx(DisasContext *s, DisasOps *o) o->in1 = NULL; o->in2 = NULL; o->g_in1 = o->g_in2 = false; - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvc(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvc(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvc(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcin(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcin(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvcin(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcl(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcl(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r2 = get_field(s->fields, r2); @@ -3110,7 +3111,7 @@ static ExitStatus op_mvcl(DisasContext *s, DisasOps *o) /* r1 and r2 must be even. */ if (r1 & 1 || r2 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -3119,10 +3120,10 @@ static ExitStatus op_mvcl(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcle(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcle(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -3131,7 +3132,7 @@ static ExitStatus op_mvcle(DisasContext *s, DisasOps *o) /* r1 and r3 must be even. */ if (r1 & 1 || r3 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -3140,10 +3141,10 @@ static ExitStatus op_mvcle(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvclu(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvclu(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -3152,7 +3153,7 @@ static ExitStatus op_mvclu(DisasContext *s, DisasOps *o) /* r1 and r3 must be even. */ if (r1 & 1 || r3 & 1) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } t1 = tcg_const_i32(r1); @@ -3161,151 +3162,151 @@ static ExitStatus op_mvclu(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcos(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcos(DisasContext *s, DisasOps *o) { int r3 = get_field(s->fields, r3); gen_helper_mvcos(cc_op, cpu_env, o->addr1, o->in2, regs[r3]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_mvcp(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcp(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, l1); check_privileged(s); gen_helper_mvcp(cc_op, cpu_env, regs[r1], o->addr1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvcs(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvcs(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, l1); check_privileged(s); gen_helper_mvcs(cc_op, cpu_env, regs[r1], o->addr1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_mvn(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvn(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvn(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvo(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvo(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvo(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvpg(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvpg(DisasContext *s, DisasOps *o) { gen_helper_mvpg(cc_op, cpu_env, regs[0], o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvst(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvst(DisasContext *s, DisasOps *o) { gen_helper_mvst(o->in1, cpu_env, regs[0], o->in1, o->in2); set_cc_static(s); return_low128(o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mvz(DisasContext *s, DisasOps *o) +static DisasJumpType op_mvz(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_mvz(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mul(DisasContext *s, DisasOps *o) +static DisasJumpType op_mul(DisasContext *s, DisasOps *o) { tcg_gen_mul_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mul128(DisasContext *s, DisasOps *o) +static DisasJumpType op_mul128(DisasContext *s, DisasOps *o) { tcg_gen_mulu2_i64(o->out2, o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_meeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_meeb(DisasContext *s, DisasOps *o) { gen_helper_meeb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mdeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mdeb(DisasContext *s, DisasOps *o) { gen_helper_mdeb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mdb(DisasContext *s, DisasOps *o) { gen_helper_mdb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mxb(DisasContext *s, DisasOps *o) { gen_helper_mxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mxdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mxdb(DisasContext *s, DisasOps *o) { gen_helper_mxdb(o->out, cpu_env, o->out, o->out2, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_maeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_maeb(DisasContext *s, DisasOps *o) { TCGv_i64 r3 = load_freg32_i64(get_field(s->fields, r3)); gen_helper_maeb(o->out, cpu_env, o->in1, o->in2, r3); tcg_temp_free_i64(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_madb(DisasContext *s, DisasOps *o) +static DisasJumpType op_madb(DisasContext *s, DisasOps *o) { int r3 = get_field(s->fields, r3); gen_helper_madb(o->out, cpu_env, o->in1, o->in2, fregs[r3]); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mseb(DisasContext *s, DisasOps *o) +static DisasJumpType op_mseb(DisasContext *s, DisasOps *o) { TCGv_i64 r3 = load_freg32_i64(get_field(s->fields, r3)); gen_helper_mseb(o->out, cpu_env, o->in1, o->in2, r3); tcg_temp_free_i64(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_msdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_msdb(DisasContext *s, DisasOps *o) { int r3 = get_field(s->fields, r3); gen_helper_msdb(o->out, cpu_env, o->in1, o->in2, fregs[r3]); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nabs(DisasContext *s, DisasOps *o) +static DisasJumpType op_nabs(DisasContext *s, DisasOps *o) { TCGv_i64 z, n; z = tcg_const_i64(0); @@ -3314,78 +3315,78 @@ static ExitStatus op_nabs(DisasContext *s, DisasOps *o) tcg_gen_movcond_i64(TCG_COND_GE, o->out, o->in2, z, n, o->in2); tcg_temp_free_i64(n); tcg_temp_free_i64(z); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nabsf32(DisasContext *s, DisasOps *o) +static DisasJumpType op_nabsf32(DisasContext *s, DisasOps *o) { tcg_gen_ori_i64(o->out, o->in2, 0x80000000ull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nabsf64(DisasContext *s, DisasOps *o) +static DisasJumpType op_nabsf64(DisasContext *s, DisasOps *o) { tcg_gen_ori_i64(o->out, o->in2, 0x8000000000000000ull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nabsf128(DisasContext *s, DisasOps *o) +static DisasJumpType op_nabsf128(DisasContext *s, DisasOps *o) { tcg_gen_ori_i64(o->out, o->in1, 0x8000000000000000ull); tcg_gen_mov_i64(o->out2, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_nc(DisasContext *s, DisasOps *o) +static DisasJumpType op_nc(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_nc(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_neg(DisasContext *s, DisasOps *o) +static DisasJumpType op_neg(DisasContext *s, DisasOps *o) { tcg_gen_neg_i64(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_negf32(DisasContext *s, DisasOps *o) +static DisasJumpType op_negf32(DisasContext *s, DisasOps *o) { tcg_gen_xori_i64(o->out, o->in2, 0x80000000ull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_negf64(DisasContext *s, DisasOps *o) +static DisasJumpType op_negf64(DisasContext *s, DisasOps *o) { tcg_gen_xori_i64(o->out, o->in2, 0x8000000000000000ull); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_negf128(DisasContext *s, DisasOps *o) +static DisasJumpType op_negf128(DisasContext *s, DisasOps *o) { tcg_gen_xori_i64(o->out, o->in1, 0x8000000000000000ull); tcg_gen_mov_i64(o->out2, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_oc(DisasContext *s, DisasOps *o) +static DisasJumpType op_oc(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_oc(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_or(DisasContext *s, DisasOps *o) +static DisasJumpType op_or(DisasContext *s, DisasOps *o) { tcg_gen_or_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ori(DisasContext *s, DisasOps *o) +static DisasJumpType op_ori(DisasContext *s, DisasOps *o) { int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; @@ -3398,10 +3399,10 @@ static ExitStatus op_ori(DisasContext *s, DisasOps *o) /* Produce the CC from only the bits manipulated. */ tcg_gen_andi_i64(cc_dst, o->out, mask); set_cc_nz_u64(s, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_oi(DisasContext *s, DisasOps *o) +static DisasJumpType op_oi(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); @@ -3419,18 +3420,18 @@ static ExitStatus op_oi(DisasContext *s, DisasOps *o) if (!s390_has_feat(S390_FEAT_INTERLOCKED_ACCESS_2)) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), s->insn->data); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pack(DisasContext *s, DisasOps *o) +static DisasJumpType op_pack(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_pack(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pka(DisasContext *s, DisasOps *o) +static DisasJumpType op_pka(DisasContext *s, DisasOps *o) { int l2 = get_field(s->fields, l2) + 1; TCGv_i32 l; @@ -3438,15 +3439,15 @@ static ExitStatus op_pka(DisasContext *s, DisasOps *o) /* The length must not exceed 32 bytes. */ if (l2 > 32) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } l = tcg_const_i32(l2); gen_helper_pka(cpu_env, o->addr1, o->in2, l); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pku(DisasContext *s, DisasOps *o) +static DisasJumpType op_pku(DisasContext *s, DisasOps *o) { int l2 = get_field(s->fields, l2) + 1; TCGv_i32 l; @@ -3454,30 +3455,30 @@ static ExitStatus op_pku(DisasContext *s, DisasOps *o) /* The length must be even and should not exceed 64 bytes. */ if ((l2 & 1) || (l2 > 64)) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } l = tcg_const_i32(l2); gen_helper_pku(cpu_env, o->addr1, o->in2, l); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_popcnt(DisasContext *s, DisasOps *o) +static DisasJumpType op_popcnt(DisasContext *s, DisasOps *o) { gen_helper_popcnt(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_ptlb(DisasContext *s, DisasOps *o) +static DisasJumpType op_ptlb(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_ptlb(cpu_env); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_risbg(DisasContext *s, DisasOps *o) +static DisasJumpType op_risbg(DisasContext *s, DisasOps *o) { int i3 = get_field(s->fields, i3); int i4 = get_field(s->fields, i4); @@ -3535,7 +3536,7 @@ static ExitStatus op_risbg(DisasContext *s, DisasOps *o) /* In some cases we can implement this with extract. */ if (imask == 0 && pos == 0 && len > 0 && len <= rot) { tcg_gen_extract_i64(o->out, o->in2, 64 - rot, len); - return NO_EXIT; + return DISAS_NEXT; } /* In some cases we can implement this with deposit. */ @@ -3564,10 +3565,10 @@ static ExitStatus op_risbg(DisasContext *s, DisasOps *o) tcg_gen_andi_i64(o->out, o->out, imask); tcg_gen_or_i64(o->out, o->out, o->in2); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rosbg(DisasContext *s, DisasOps *o) +static DisasJumpType op_rosbg(DisasContext *s, DisasOps *o) { int i3 = get_field(s->fields, i3); int i4 = get_field(s->fields, i4); @@ -3617,28 +3618,28 @@ static ExitStatus op_rosbg(DisasContext *s, DisasOps *o) /* Set the CC. */ tcg_gen_andi_i64(cc_dst, o->out, mask); set_cc_nz_u64(s, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rev16(DisasContext *s, DisasOps *o) +static DisasJumpType op_rev16(DisasContext *s, DisasOps *o) { tcg_gen_bswap16_i64(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rev32(DisasContext *s, DisasOps *o) +static DisasJumpType op_rev32(DisasContext *s, DisasOps *o) { tcg_gen_bswap32_i64(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rev64(DisasContext *s, DisasOps *o) +static DisasJumpType op_rev64(DisasContext *s, DisasOps *o) { tcg_gen_bswap64_i64(o->out, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rll32(DisasContext *s, DisasOps *o) +static DisasJumpType op_rll32(DisasContext *s, DisasOps *o) { TCGv_i32 t1 = tcg_temp_new_i32(); TCGv_i32 t2 = tcg_temp_new_i32(); @@ -3650,34 +3651,34 @@ static ExitStatus op_rll32(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t1); tcg_temp_free_i32(t2); tcg_temp_free_i32(to); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rll64(DisasContext *s, DisasOps *o) +static DisasJumpType op_rll64(DisasContext *s, DisasOps *o) { tcg_gen_rotl_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_rrbe(DisasContext *s, DisasOps *o) +static DisasJumpType op_rrbe(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_rrbe(cc_op, cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sacf(DisasContext *s, DisasOps *o) +static DisasJumpType op_sacf(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sacf(cpu_env, o->in2); /* Addressing mode has changed, so end the block. */ - return EXIT_PC_STALE; + return DISAS_PC_STALE; } #endif -static ExitStatus op_sam(DisasContext *s, DisasOps *o) +static DisasJumpType op_sam(DisasContext *s, DisasOps *o) { int sam = s->insn->data; TCGv_i64 tsam; @@ -3698,75 +3699,75 @@ static ExitStatus op_sam(DisasContext *s, DisasOps *o) /* Bizarre but true, we check the address of the current insn for the specification exception, not the next to be executed. Thus the PoO documents that Bad Things Happen two bytes before the end. */ - if (s->pc & ~mask) { + if (s->base.pc_next & ~mask) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } - s->next_pc &= mask; + s->pc_tmp &= mask; tsam = tcg_const_i64(sam); tcg_gen_deposit_i64(psw_mask, psw_mask, tsam, 31, 2); tcg_temp_free_i64(tsam); /* Always exit the TB, since we (may have) changed execution mode. */ - return EXIT_PC_STALE; + return DISAS_PC_STALE; } -static ExitStatus op_sar(DisasContext *s, DisasOps *o) +static DisasJumpType op_sar(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); tcg_gen_st32_i64(o->in2, cpu_env, offsetof(CPUS390XState, aregs[r1])); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_seb(DisasContext *s, DisasOps *o) +static DisasJumpType op_seb(DisasContext *s, DisasOps *o) { gen_helper_seb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sdb(DisasContext *s, DisasOps *o) { gen_helper_sdb(o->out, cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sxb(DisasContext *s, DisasOps *o) { gen_helper_sxb(o->out, cpu_env, o->out, o->out2, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sqeb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sqeb(DisasContext *s, DisasOps *o) { gen_helper_sqeb(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sqdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sqdb(DisasContext *s, DisasOps *o) { gen_helper_sqdb(o->out, cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sqxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_sqxb(DisasContext *s, DisasOps *o) { gen_helper_sqxb(o->out, cpu_env, o->in1, o->in2); return_low128(o->out2); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_servc(DisasContext *s, DisasOps *o) +static DisasJumpType op_servc(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_servc(cc_op, cpu_env, o->in2, o->in1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sigp(DisasContext *s, DisasOps *o) +static DisasJumpType op_sigp(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -3775,11 +3776,11 @@ static ExitStatus op_sigp(DisasContext *s, DisasOps *o) set_cc_static(s); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_soc(DisasContext *s, DisasOps *o) +static DisasJumpType op_soc(DisasContext *s, DisasOps *o) { DisasCompare c; TCGv_i64 a, h; @@ -3821,10 +3822,10 @@ static ExitStatus op_soc(DisasContext *s, DisasOps *o) tcg_temp_free_i64(a); gen_set_label(lab); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sla(DisasContext *s, DisasOps *o) +static DisasJumpType op_sla(DisasContext *s, DisasOps *o) { uint64_t sign = 1ull << s->insn->data; enum cc_op cco = s->insn->data == 31 ? CC_OP_SLA_32 : CC_OP_SLA_64; @@ -3835,40 +3836,40 @@ static ExitStatus op_sla(DisasContext *s, DisasOps *o) tcg_gen_andi_i64(o->out, o->out, ~sign); tcg_gen_andi_i64(o->in1, o->in1, sign); tcg_gen_or_i64(o->out, o->out, o->in1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sll(DisasContext *s, DisasOps *o) +static DisasJumpType op_sll(DisasContext *s, DisasOps *o) { tcg_gen_shl_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sra(DisasContext *s, DisasOps *o) +static DisasJumpType op_sra(DisasContext *s, DisasOps *o) { tcg_gen_sar_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_srl(DisasContext *s, DisasOps *o) +static DisasJumpType op_srl(DisasContext *s, DisasOps *o) { tcg_gen_shr_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sfpc(DisasContext *s, DisasOps *o) +static DisasJumpType op_sfpc(DisasContext *s, DisasOps *o) { gen_helper_sfpc(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sfas(DisasContext *s, DisasOps *o) +static DisasJumpType op_sfas(DisasContext *s, DisasOps *o) { gen_helper_sfas(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_srnm(DisasContext *s, DisasOps *o) +static DisasJumpType op_srnm(DisasContext *s, DisasOps *o) { int b2 = get_field(s->fields, b2); int d2 = get_field(s->fields, d2); @@ -3905,10 +3906,10 @@ static ExitStatus op_srnm(DisasContext *s, DisasOps *o) /* Then install the new FPC to set the rounding mode in fpu_status. */ gen_helper_sfpc(cpu_env, t2); tcg_temp_free_i64(t2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_spm(DisasContext *s, DisasOps *o) +static DisasJumpType op_spm(DisasContext *s, DisasOps *o) { tcg_gen_extrl_i64_i32(cc_op, o->in1); tcg_gen_extract_i32(cc_op, cc_op, 28, 2); @@ -3916,10 +3917,10 @@ static ExitStatus op_spm(DisasContext *s, DisasOps *o) tcg_gen_shri_i64(o->in1, o->in1, 24); tcg_gen_deposit_i64(psw_mask, psw_mask, o->in1, PSW_SHIFT_MASK_PM, 4); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ectg(DisasContext *s, DisasOps *o) +static DisasJumpType op_ectg(DisasContext *s, DisasOps *o) { int b1 = get_field(s->fields, b1); int d1 = get_field(s->fields, d1); @@ -3946,49 +3947,49 @@ static ExitStatus op_ectg(DisasContext *s, DisasOps *o) tcg_gen_mov_i64(regs[1], o->in2); tcg_temp_free_i64(tmp); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_spka(DisasContext *s, DisasOps *o) +static DisasJumpType op_spka(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_shri_i64(o->in2, o->in2, 4); tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, PSW_SHIFT_KEY, 4); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sske(DisasContext *s, DisasOps *o) +static DisasJumpType op_sske(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sske(cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ssm(DisasContext *s, DisasOps *o) +static DisasJumpType op_ssm(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, 56, 8); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return EXIT_PC_STALE_NOCHAIN; + return DISAS_PC_STALE_NOCHAIN; } -static ExitStatus op_stap(DisasContext *s, DisasOps *o) +static DisasJumpType op_stap(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, core_id)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stck(DisasContext *s, DisasOps *o) +static DisasJumpType op_stck(DisasContext *s, DisasOps *o) { gen_helper_stck(o->out, cpu_env); /* ??? We don't implement clock states. */ gen_op_movi_cc(s, 0); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stcke(DisasContext *s, DisasOps *o) +static DisasJumpType op_stcke(DisasContext *s, DisasOps *o) { TCGv_i64 c1 = tcg_temp_new_i64(); TCGv_i64 c2 = tcg_temp_new_i64(); @@ -4012,31 +4013,40 @@ static ExitStatus op_stcke(DisasContext *s, DisasOps *o) tcg_temp_free_i64(todpr); /* ??? We don't implement clock states. */ gen_op_movi_cc(s, 0); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sckc(DisasContext *s, DisasOps *o) +static DisasJumpType op_sck(DisasContext *s, DisasOps *o) +{ + check_privileged(s); + tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN); + gen_helper_sck(cc_op, cpu_env, o->in1); + set_cc_static(s); + return DISAS_NEXT; +} + +static DisasJumpType op_sckc(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sckc(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sckpf(DisasContext *s, DisasOps *o) +static DisasJumpType op_sckpf(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sckpf(cpu_env, regs[0]); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stckc(DisasContext *s, DisasOps *o) +static DisasJumpType op_stckc(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stckc(o->out, cpu_env); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stctg(DisasContext *s, DisasOps *o) +static DisasJumpType op_stctg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -4044,10 +4054,10 @@ static ExitStatus op_stctg(DisasContext *s, DisasOps *o) gen_helper_stctg(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stctl(DisasContext *s, DisasOps *o) +static DisasJumpType op_stctl(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -4055,186 +4065,186 @@ static ExitStatus op_stctl(DisasContext *s, DisasOps *o) gen_helper_stctl(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stidp(DisasContext *s, DisasOps *o) +static DisasJumpType op_stidp(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, cpuid)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_spt(DisasContext *s, DisasOps *o) +static DisasJumpType op_spt(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_spt(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stfl(DisasContext *s, DisasOps *o) +static DisasJumpType op_stfl(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stfl(cpu_env); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stpt(DisasContext *s, DisasOps *o) +static DisasJumpType op_stpt(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stpt(o->out, cpu_env); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stsi(DisasContext *s, DisasOps *o) +static DisasJumpType op_stsi(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stsi(cc_op, cpu_env, o->in2, regs[0], regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_spx(DisasContext *s, DisasOps *o) +static DisasJumpType op_spx(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_spx(cpu_env, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_xsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_xsch(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_csch(DisasContext *s, DisasOps *o) +static DisasJumpType op_csch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_csch(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_hsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_hsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_hsch(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_msch(DisasContext *s, DisasOps *o) +static DisasJumpType op_msch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_msch(cpu_env, regs[1], o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rchp(DisasContext *s, DisasOps *o) +static DisasJumpType op_rchp(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_rchp(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_rsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_rsch(cpu_env, regs[1]); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sal(DisasContext *s, DisasOps *o) +static DisasJumpType op_sal(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sal(cpu_env, regs[1]); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_schm(DisasContext *s, DisasOps *o) +static DisasJumpType op_schm(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_schm(cpu_env, regs[1], regs[2], o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_siga(DisasContext *s, DisasOps *o) +static DisasJumpType op_siga(DisasContext *s, DisasOps *o) { check_privileged(s); /* From KVM code: Not provided, set CC = 3 for subchannel not operational */ gen_op_movi_cc(s, 3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stcps(DisasContext *s, DisasOps *o) +static DisasJumpType op_stcps(DisasContext *s, DisasOps *o) { check_privileged(s); /* The instruction is suppressed if not provided. */ - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ssch(DisasContext *s, DisasOps *o) +static DisasJumpType op_ssch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_ssch(cpu_env, regs[1], o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_stsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stsch(cpu_env, regs[1], o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stcrw(DisasContext *s, DisasOps *o) +static DisasJumpType op_stcrw(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stcrw(cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tpi(DisasContext *s, DisasOps *o) +static DisasJumpType op_tpi(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_tpi(cc_op, cpu_env, o->addr1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tsch(DisasContext *s, DisasOps *o) +static DisasJumpType op_tsch(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_tsch(cpu_env, regs[1], o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_chsc(DisasContext *s, DisasOps *o) +static DisasJumpType op_chsc(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_chsc(cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stpx(DisasContext *s, DisasOps *o) +static DisasJumpType op_stpx(DisasContext *s, DisasOps *o) { check_privileged(s); tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, psa)); tcg_gen_andi_i64(o->out, o->out, 0x7fffe000); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stnosm(DisasContext *s, DisasOps *o) +static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) { uint64_t i2 = get_field(s->fields, i2); TCGv_i64 t; @@ -4257,66 +4267,66 @@ static ExitStatus op_stnosm(DisasContext *s, DisasOps *o) } /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ - return EXIT_PC_STALE_NOCHAIN; + return DISAS_PC_STALE_NOCHAIN; } -static ExitStatus op_stura(DisasContext *s, DisasOps *o) +static DisasJumpType op_stura(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_stura(cpu_env, o->in2, o->in1); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sturg(DisasContext *s, DisasOps *o) +static DisasJumpType op_sturg(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sturg(cpu_env, o->in2, o->in1); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_stfle(DisasContext *s, DisasOps *o) +static DisasJumpType op_stfle(DisasContext *s, DisasOps *o) { gen_helper_stfle(cc_op, cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_st8(DisasContext *s, DisasOps *o) +static DisasJumpType op_st8(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st8(o->in1, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_st16(DisasContext *s, DisasOps *o) +static DisasJumpType op_st16(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st16(o->in1, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_st32(DisasContext *s, DisasOps *o) +static DisasJumpType op_st32(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st32(o->in1, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_st64(DisasContext *s, DisasOps *o) +static DisasJumpType op_st64(DisasContext *s, DisasOps *o) { tcg_gen_qemu_st64(o->in1, o->in2, get_mem_index(s)); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stam(DisasContext *s, DisasOps *o) +static DisasJumpType op_stam(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); gen_helper_stam(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stcm(DisasContext *s, DisasOps *o) +static DisasJumpType op_stcm(DisasContext *s, DisasOps *o) { int m3 = get_field(s->fields, m3); int pos, base = s->insn->data; @@ -4362,10 +4372,10 @@ static ExitStatus op_stcm(DisasContext *s, DisasOps *o) break; } tcg_temp_free_i64(tmp); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stm(DisasContext *s, DisasOps *o) +static DisasJumpType op_stm(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -4386,10 +4396,10 @@ static ExitStatus op_stm(DisasContext *s, DisasOps *o) } tcg_temp_free_i64(tsize); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stmh(DisasContext *s, DisasOps *o) +static DisasJumpType op_stmh(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, r1); int r3 = get_field(s->fields, r3); @@ -4410,20 +4420,20 @@ static ExitStatus op_stmh(DisasContext *s, DisasOps *o) tcg_temp_free_i64(t); tcg_temp_free_i64(t4); tcg_temp_free_i64(t32); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stpq(DisasContext *s, DisasOps *o) +static DisasJumpType op_stpq(DisasContext *s, DisasOps *o) { - if (tb_cflags(s->tb) & CF_PARALLEL) { + if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_stpq_parallel(cpu_env, o->in2, o->out2, o->out); } else { gen_helper_stpq(cpu_env, o->in2, o->out2, o->out); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_srst(DisasContext *s, DisasOps *o) +static DisasJumpType op_srst(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4433,10 +4443,10 @@ static ExitStatus op_srst(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_srstu(DisasContext *s, DisasOps *o) +static DisasJumpType op_srstu(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4446,16 +4456,16 @@ static ExitStatus op_srstu(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sub(DisasContext *s, DisasOps *o) +static DisasJumpType op_sub(DisasContext *s, DisasOps *o) { tcg_gen_sub_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_subb(DisasContext *s, DisasOps *o) +static DisasJumpType op_subb(DisasContext *s, DisasOps *o) { DisasCompare cmp; TCGv_i64 borrow; @@ -4478,10 +4488,10 @@ static ExitStatus op_subb(DisasContext *s, DisasOps *o) tcg_gen_sub_i64(o->out, o->out, borrow); tcg_temp_free_i64(borrow); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_svc(DisasContext *s, DisasOps *o) +static DisasJumpType op_svc(DisasContext *s, DisasOps *o) { TCGv_i32 t; @@ -4497,104 +4507,104 @@ static ExitStatus op_svc(DisasContext *s, DisasOps *o) tcg_temp_free_i32(t); gen_exception(EXCP_SVC); - return EXIT_NORETURN; + return DISAS_NORETURN; } -static ExitStatus op_tam(DisasContext *s, DisasOps *o) +static DisasJumpType op_tam(DisasContext *s, DisasOps *o) { int cc = 0; - cc |= (s->tb->flags & FLAG_MASK_64) ? 2 : 0; - cc |= (s->tb->flags & FLAG_MASK_32) ? 1 : 0; + cc |= (s->base.tb->flags & FLAG_MASK_64) ? 2 : 0; + cc |= (s->base.tb->flags & FLAG_MASK_32) ? 1 : 0; gen_op_movi_cc(s, cc); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tceb(DisasContext *s, DisasOps *o) +static DisasJumpType op_tceb(DisasContext *s, DisasOps *o) { gen_helper_tceb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tcdb(DisasContext *s, DisasOps *o) +static DisasJumpType op_tcdb(DisasContext *s, DisasOps *o) { gen_helper_tcdb(cc_op, cpu_env, o->in1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tcxb(DisasContext *s, DisasOps *o) +static DisasJumpType op_tcxb(DisasContext *s, DisasOps *o) { gen_helper_tcxb(cc_op, cpu_env, o->out, o->out2, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_testblock(DisasContext *s, DisasOps *o) +static DisasJumpType op_testblock(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_testblock(cc_op, cpu_env, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tprot(DisasContext *s, DisasOps *o) +static DisasJumpType op_tprot(DisasContext *s, DisasOps *o) { gen_helper_tprot(cc_op, cpu_env, o->addr1, o->in2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #endif -static ExitStatus op_tp(DisasContext *s, DisasOps *o) +static DisasJumpType op_tp(DisasContext *s, DisasOps *o) { TCGv_i32 l1 = tcg_const_i32(get_field(s->fields, l1) + 1); gen_helper_tp(cc_op, cpu_env, o->addr1, l1); tcg_temp_free_i32(l1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tr(DisasContext *s, DisasOps *o) +static DisasJumpType op_tr(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_tr(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_tre(DisasContext *s, DisasOps *o) +static DisasJumpType op_tre(DisasContext *s, DisasOps *o) { gen_helper_tre(o->out, cpu_env, o->out, o->out2, o->in2); return_low128(o->out2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_trt(DisasContext *s, DisasOps *o) +static DisasJumpType op_trt(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_trt(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_trtr(DisasContext *s, DisasOps *o) +static DisasJumpType op_trtr(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_trtr(cc_op, cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_trXX(DisasContext *s, DisasOps *o) +static DisasJumpType op_trXX(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4622,28 +4632,28 @@ static ExitStatus op_trXX(DisasContext *s, DisasOps *o) tcg_temp_free_i32(sizes); tcg_temp_free_i32(tst); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_ts(DisasContext *s, DisasOps *o) +static DisasJumpType op_ts(DisasContext *s, DisasOps *o) { TCGv_i32 t1 = tcg_const_i32(0xff); tcg_gen_atomic_xchg_i32(t1, o->in2, t1, get_mem_index(s), MO_UB); tcg_gen_extract_i32(cc_op, t1, 7, 1); tcg_temp_free_i32(t1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_unpk(DisasContext *s, DisasOps *o) +static DisasJumpType op_unpk(DisasContext *s, DisasOps *o) { TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1)); gen_helper_unpk(cpu_env, l, o->addr1, o->in2); tcg_temp_free_i32(l); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_unpka(DisasContext *s, DisasOps *o) +static DisasJumpType op_unpka(DisasContext *s, DisasOps *o) { int l1 = get_field(s->fields, l1) + 1; TCGv_i32 l; @@ -4651,16 +4661,16 @@ static ExitStatus op_unpka(DisasContext *s, DisasOps *o) /* The length must not exceed 32 bytes. */ if (l1 > 32) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } l = tcg_const_i32(l1); gen_helper_unpka(cc_op, cpu_env, o->addr1, l, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_unpku(DisasContext *s, DisasOps *o) +static DisasJumpType op_unpku(DisasContext *s, DisasOps *o) { int l1 = get_field(s->fields, l1) + 1; TCGv_i32 l; @@ -4668,17 +4678,17 @@ static ExitStatus op_unpku(DisasContext *s, DisasOps *o) /* The length must be even and should not exceed 64 bytes. */ if ((l1 & 1) || (l1 > 64)) { gen_program_exception(s, PGM_SPECIFICATION); - return EXIT_NORETURN; + return DISAS_NORETURN; } l = tcg_const_i32(l1); gen_helper_unpku(cc_op, cpu_env, o->addr1, l, o->in2); tcg_temp_free_i32(l); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xc(DisasContext *s, DisasOps *o) +static DisasJumpType op_xc(DisasContext *s, DisasOps *o) { int d1 = get_field(s->fields, d1); int d2 = get_field(s->fields, d2); @@ -4719,7 +4729,7 @@ static ExitStatus op_xc(DisasContext *s, DisasOps *o) tcg_gen_qemu_st8(o->in2, o->addr1, get_mem_index(s)); } gen_op_movi_cc(s, 0); - return NO_EXIT; + return DISAS_NEXT; } /* But in general we'll defer to a helper. */ @@ -4728,16 +4738,16 @@ static ExitStatus op_xc(DisasContext *s, DisasOps *o) gen_helper_xc(cc_op, cpu_env, t32, o->addr1, o->in2); tcg_temp_free_i32(t32); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xor(DisasContext *s, DisasOps *o) +static DisasJumpType op_xor(DisasContext *s, DisasOps *o) { tcg_gen_xor_i64(o->out, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xori(DisasContext *s, DisasOps *o) +static DisasJumpType op_xori(DisasContext *s, DisasOps *o) { int shift = s->insn->data & 0xff; int size = s->insn->data >> 8; @@ -4750,10 +4760,10 @@ static ExitStatus op_xori(DisasContext *s, DisasOps *o) /* Produce the CC from only the bits manipulated. */ tcg_gen_andi_i64(cc_dst, o->out, mask); set_cc_nz_u64(s, cc_dst); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_xi(DisasContext *s, DisasOps *o) +static DisasJumpType op_xi(DisasContext *s, DisasOps *o) { o->in1 = tcg_temp_new_i64(); @@ -4771,25 +4781,25 @@ static ExitStatus op_xi(DisasContext *s, DisasOps *o) if (!s390_has_feat(S390_FEAT_INTERLOCKED_ACCESS_2)) { tcg_gen_qemu_st_tl(o->out, o->addr1, get_mem_index(s), s->insn->data); } - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_zero(DisasContext *s, DisasOps *o) +static DisasJumpType op_zero(DisasContext *s, DisasOps *o) { o->out = tcg_const_i64(0); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_zero2(DisasContext *s, DisasOps *o) +static DisasJumpType op_zero2(DisasContext *s, DisasOps *o) { o->out = tcg_const_i64(0); o->out2 = o->out; o->g_out2 = true; - return NO_EXIT; + return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY -static ExitStatus op_clp(DisasContext *s, DisasOps *o) +static DisasJumpType op_clp(DisasContext *s, DisasOps *o) { TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4797,10 +4807,10 @@ static ExitStatus op_clp(DisasContext *s, DisasOps *o) gen_helper_clp(cpu_env, r2); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pcilg(DisasContext *s, DisasOps *o) +static DisasJumpType op_pcilg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4810,10 +4820,10 @@ static ExitStatus op_pcilg(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pcistg(DisasContext *s, DisasOps *o) +static DisasJumpType op_pcistg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4823,10 +4833,10 @@ static ExitStatus op_pcistg(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_stpcifc(DisasContext *s, DisasOps *o) +static DisasJumpType op_stpcifc(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); @@ -4836,17 +4846,17 @@ static ExitStatus op_stpcifc(DisasContext *s, DisasOps *o) tcg_temp_free_i32(ar); tcg_temp_free_i32(r1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_sic(DisasContext *s, DisasOps *o) +static DisasJumpType op_sic(DisasContext *s, DisasOps *o) { check_privileged(s); gen_helper_sic(cpu_env, o->in1, o->in2); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_rpcit(DisasContext *s, DisasOps *o) +static DisasJumpType op_rpcit(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); @@ -4856,10 +4866,10 @@ static ExitStatus op_rpcit(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_pcistb(DisasContext *s, DisasOps *o) +static DisasJumpType op_pcistb(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); @@ -4871,10 +4881,10 @@ static ExitStatus op_pcistb(DisasContext *s, DisasOps *o) tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } -static ExitStatus op_mpcifc(DisasContext *s, DisasOps *o) +static DisasJumpType op_mpcifc(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); @@ -4884,7 +4894,7 @@ static ExitStatus op_mpcifc(DisasContext *s, DisasOps *o) tcg_temp_free_i32(ar); tcg_temp_free_i32(r1); set_cc_static(s); - return NO_EXIT; + return DISAS_NEXT; } #endif @@ -5629,7 +5639,7 @@ static void in2_a2(DisasContext *s, DisasFields *f, DisasOps *o) static void in2_ri2(DisasContext *s, DisasFields *f, DisasOps *o) { - o->in2 = tcg_const_i64(s->pc + (int64_t)get_field(f, i2) * 2); + o->in2 = tcg_const_i64(s->base.pc_next + (int64_t)get_field(f, i2) * 2); } #define SPEC_in2_ri2 0 @@ -5930,7 +5940,7 @@ static void extract_field(DisasFields *o, const DisasField *f, uint64_t insn) static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, DisasFields *f) { - uint64_t insn, pc = s->pc; + uint64_t insn, pc = s->base.pc_next; int op, op2, ilen; const DisasInsn *info; @@ -5962,7 +5972,7 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, g_assert_not_reached(); } } - s->next_pc = s->pc + ilen; + s->pc_tmp = s->base.pc_next + ilen; s->ilen = ilen; /* We can't actually determine the insn format until we've looked up @@ -6028,10 +6038,10 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, return info; } -static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) +static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) { const DisasInsn *insn; - ExitStatus ret = NO_EXIT; + DisasJumpType ret = DISAS_NEXT; DisasFields f; DisasOps o; @@ -6043,12 +6053,12 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) qemu_log_mask(LOG_UNIMP, "unimplemented opcode 0x%02x%02x\n", f.op, f.op2); gen_illegal_opcode(s); - return EXIT_NORETURN; + return DISAS_NORETURN; } #ifndef CONFIG_USER_ONLY - if (s->tb->flags & FLAG_MASK_PER) { - TCGv_i64 addr = tcg_const_i64(s->pc); + if (s->base.tb->flags & FLAG_MASK_PER) { + TCGv_i64 addr = tcg_const_i64(s->base.pc_next); gen_helper_per_ifetch(cpu_env, addr); tcg_temp_free_i64(addr); } @@ -6090,7 +6100,7 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) } if (excp) { gen_program_exception(s, excp); - return EXIT_NORETURN; + return DISAS_NORETURN; } } @@ -6142,10 +6152,10 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) } #ifndef CONFIG_USER_ONLY - if (s->tb->flags & FLAG_MASK_PER) { + if (s->base.tb->flags & FLAG_MASK_PER) { /* An exception might be triggered, save PSW if not already done. */ - if (ret == NO_EXIT || ret == EXIT_PC_STALE) { - tcg_gen_movi_i64(psw_addr, s->next_pc); + if (ret == DISAS_NEXT || ret == DISAS_PC_STALE) { + tcg_gen_movi_i64(psw_addr, s->pc_tmp); } /* Call the helper to check for a possible PER exception. */ @@ -6154,103 +6164,92 @@ static ExitStatus translate_one(CPUS390XState *env, DisasContext *s) #endif /* Advance to the next instruction. */ - s->pc = s->next_pc; + s->base.pc_next = s->pc_tmp; return ret; } -void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +static void s390x_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { - CPUS390XState *env = cs->env_ptr; - DisasContext dc; - target_ulong pc_start; - uint64_t next_page_start; - int num_insns, max_insns; - ExitStatus status; - bool do_debug; - - pc_start = tb->pc; + DisasContext *dc = container_of(dcbase, DisasContext, base); /* 31-bit mode */ - if (!(tb->flags & FLAG_MASK_64)) { - pc_start &= 0x7fffffff; + if (!(dc->base.tb->flags & FLAG_MASK_64)) { + dc->base.pc_first &= 0x7fffffff; + dc->base.pc_next = dc->base.pc_first; } - dc.tb = tb; - dc.pc = pc_start; - dc.cc_op = CC_OP_DYNAMIC; - dc.ex_value = tb->cs_base; - do_debug = dc.singlestep_enabled = cs->singlestep_enabled; + dc->cc_op = CC_OP_DYNAMIC; + dc->ex_value = dc->base.tb->cs_base; + dc->do_debug = dc->base.singlestep_enabled; +} - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; +static void s390x_tr_tb_start(DisasContextBase *db, CPUState *cs) +{ +} - num_insns = 0; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } +static void s390x_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); - gen_tb_start(tb); + tcg_gen_insn_start(dc->base.pc_next, dc->cc_op); +} - do { - tcg_gen_insn_start(dc.pc, dc.cc_op); - num_insns++; +static bool s390x_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); - if (unlikely(cpu_breakpoint_test(cs, dc.pc, BP_ANY))) { - status = EXIT_PC_STALE; - do_debug = true; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - dc.pc += 2; - break; + dc->base.is_jmp = DISAS_PC_STALE; + dc->do_debug = true; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size does the right thing. */ + dc->base.pc_next += 2; + return true; +} + +static void s390x_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + CPUS390XState *env = cs->env_ptr; + DisasContext *dc = container_of(dcbase, DisasContext, base); + + dc->base.is_jmp = translate_one(env, dc); + if (dc->base.is_jmp == DISAS_NEXT) { + uint64_t page_start; + + page_start = dc->base.pc_first & TARGET_PAGE_MASK; + if (dc->base.pc_next - page_start >= TARGET_PAGE_SIZE || dc->ex_value) { + dc->base.is_jmp = DISAS_TOO_MANY; } - - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } - - status = translate_one(env, &dc); - - /* If we reach a page boundary, are single stepping, - or exhaust instruction count, stop generation. */ - if (status == NO_EXIT - && (dc.pc >= next_page_start - || tcg_op_buf_full() - || num_insns >= max_insns - || singlestep - || cs->singlestep_enabled - || dc.ex_value)) { - status = EXIT_PC_STALE; - } - } while (status == NO_EXIT); - - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); } +} - switch (status) { - case EXIT_GOTO_TB: - case EXIT_NORETURN: +static void s390x_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + switch (dc->base.is_jmp) { + case DISAS_GOTO_TB: + case DISAS_NORETURN: break; - case EXIT_PC_STALE: - case EXIT_PC_STALE_NOCHAIN: - update_psw_addr(&dc); + case DISAS_TOO_MANY: + case DISAS_PC_STALE: + case DISAS_PC_STALE_NOCHAIN: + update_psw_addr(dc); /* FALLTHRU */ - case EXIT_PC_UPDATED: + case DISAS_PC_UPDATED: /* Next TB starts off with CC_OP_DYNAMIC, so make sure the cc op type is in env */ - update_cc_op(&dc); + update_cc_op(dc); /* FALLTHRU */ - case EXIT_PC_CC_UPDATED: + case DISAS_PC_CC_UPDATED: /* Exit the TB, either by raising a debug exception or by return. */ - if (do_debug) { + if (dc->do_debug) { gen_exception(EXCP_DEBUG); - } else if (use_exit_tb(&dc) || status == EXIT_PC_STALE_NOCHAIN) { - tcg_gen_exit_tb(0); + } else if (use_exit_tb(dc) || + dc->base.is_jmp == DISAS_PC_STALE_NOCHAIN) { + tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); } @@ -6258,27 +6257,36 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) default: g_assert_not_reached(); } +} - gen_tb_end(tb, num_insns); +static void s390x_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); - tb->size = dc.pc - pc_start; - tb->icount = num_insns; - -#if defined(S390X_DEBUG_DISAS) - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - if (unlikely(dc.ex_value)) { - /* ??? Unfortunately log_target_disas can't use host memory. */ - qemu_log("IN: EXECUTE %016" PRIx64 "\n", dc.ex_value); - } else { - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc.pc - pc_start); - qemu_log("\n"); - } - qemu_log_unlock(); + if (unlikely(dc->ex_value)) { + /* ??? Unfortunately log_target_disas can't use host memory. */ + qemu_log("IN: EXECUTE %016" PRIx64, dc->ex_value); + } else { + qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); + log_target_disas(cs, dc->base.pc_first, dc->base.tb->size); } -#endif +} + +static const TranslatorOps s390x_tr_ops = { + .init_disas_context = s390x_tr_init_disas_context, + .tb_start = s390x_tr_tb_start, + .insn_start = s390x_tr_insn_start, + .breakpoint_check = s390x_tr_breakpoint_check, + .translate_insn = s390x_tr_translate_insn, + .tb_stop = s390x_tr_tb_stop, + .disas_log = s390x_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +{ + DisasContext dc; + + translator_loop(&s390x_tr_ops, &dc.base, cs, tb); } void restore_state_to_opc(CPUS390XState *env, TranslationBlock *tb, diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 541ffc2d97..b9f393b7c7 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -71,7 +71,6 @@ static void superh_cpu_reset(CPUState *s) set_flush_to_zero(1, &env->fp_status); #endif set_default_nan_mode(1, &env->fp_status); - set_snan_bit_is_one(1, &env->fp_status); } static void superh_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 012156b97b..1b9a201d6d 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -242,13 +242,13 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb((uintptr_t)ctx->base.tb + n); + tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_i32(cpu_pc, dest); if (ctx->base.singlestep_enabled) { gen_helper_debug(cpu_env); } else if (use_exit_tb(ctx)) { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); } @@ -266,7 +266,7 @@ static void gen_jump(DisasContext * ctx) if (ctx->base.singlestep_enabled) { gen_helper_debug(cpu_env); } else if (use_exit_tb(ctx)) { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } else { tcg_gen_lookup_and_goto_ptr(); } @@ -1895,35 +1895,18 @@ static void decode_opc(DisasContext * ctx) any sequence via cpu_exec_step_atomic, we can recognize the "normal" sequences and transform them into atomic operations as seen by the host. */ -static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) +static void decode_gusa(DisasContext *ctx, CPUSH4State *env) { uint16_t insns[5]; int ld_adr, ld_dst, ld_mop; int op_dst, op_src, op_opc; int mv_src, mt_dst, st_src, st_mop; TCGv op_arg; - uint32_t pc = ctx->base.pc_next; uint32_t pc_end = ctx->base.tb->cs_base; - int backup = sextract32(ctx->tbflags, GUSA_SHIFT, 8); int max_insns = (pc_end - pc) / 2; int i; - if (pc != pc_end + backup || max_insns < 2) { - /* This is a malformed gUSA region. Don't do anything special, - since the interpreter is likely to get confused. */ - ctx->envflags &= ~GUSA_MASK; - return 0; - } - - if (ctx->tbflags & GUSA_EXCLUSIVE) { - /* Regardless of single-stepping or the end of the page, - we must complete execution of the gUSA region while - holding the exclusive lock. */ - *pmax_insns = max_insns; - return 0; - } - /* The state machine below will consume only a few insns. If there are more than that in a region, fail now. */ if (max_insns > ARRAY_SIZE(insns)) { @@ -2140,7 +2123,6 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) /* * Emit the operation. */ - tcg_gen_insn_start(pc, ctx->envflags); switch (op_opc) { case -1: /* No operation found. Look for exchange pattern. */ @@ -2235,7 +2217,8 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) /* The entire region has been translated. */ ctx->envflags &= ~GUSA_MASK; ctx->base.pc_next = pc_end; - return max_insns; + ctx->base.num_insns += max_insns - 1; + return; fail: qemu_log_mask(LOG_UNIMP, "Unrecognized gUSA sequence %08x-%08x\n", @@ -2243,7 +2226,6 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) /* Restart with the EXCLUSIVE bit set, within a TB run via cpu_exec_step_atomic holding the exclusive lock. */ - tcg_gen_insn_start(pc, ctx->envflags); ctx->envflags |= GUSA_EXCLUSIVE; gen_save_cpu_state(ctx, false); gen_helper_exclusive(cpu_env); @@ -2254,130 +2236,156 @@ static int decode_gusa(DisasContext *ctx, CPUSH4State *env, int *pmax_insns) entire region consumed via ctx->base.pc_next so that it's immediately available in the disassembly dump. */ ctx->base.pc_next = pc_end; - return 1; + ctx->base.num_insns += max_insns - 1; } #endif -void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) +static void sh4_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { + DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUSH4State *env = cs->env_ptr; - DisasContext ctx; - target_ulong pc_start; - int num_insns; - int max_insns; + uint32_t tbflags; + int bound; - pc_start = tb->pc; - ctx.base.pc_next = pc_start; - ctx.tbflags = (uint32_t)tb->flags; - ctx.envflags = tb->flags & TB_FLAG_ENVFLAGS_MASK; - ctx.base.is_jmp = DISAS_NEXT; - ctx.memidx = (ctx.tbflags & (1u << SR_MD)) == 0 ? 1 : 0; + ctx->tbflags = tbflags = ctx->base.tb->flags; + ctx->envflags = tbflags & TB_FLAG_ENVFLAGS_MASK; + ctx->memidx = (tbflags & (1u << SR_MD)) == 0 ? 1 : 0; /* We don't know if the delayed pc came from a dynamic or static branch, so assume it is a dynamic branch. */ - ctx.delayed_pc = -1; /* use delayed pc from env pointer */ - ctx.base.tb = tb; - ctx.base.singlestep_enabled = cs->singlestep_enabled; - ctx.features = env->features; - ctx.has_movcal = (ctx.tbflags & TB_FLAG_PENDING_MOVCA); - ctx.gbank = ((ctx.tbflags & (1 << SR_MD)) && - (ctx.tbflags & (1 << SR_RB))) * 0x10; - ctx.fbank = ctx.tbflags & FPSCR_FR ? 0x10 : 0; + ctx->delayed_pc = -1; /* use delayed pc from env pointer */ + ctx->features = env->features; + ctx->has_movcal = (tbflags & TB_FLAG_PENDING_MOVCA); + ctx->gbank = ((tbflags & (1 << SR_MD)) && + (tbflags & (1 << SR_RB))) * 0x10; + ctx->fbank = tbflags & FPSCR_FR ? 0x10 : 0; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; + if (tbflags & GUSA_MASK) { + uint32_t pc = ctx->base.pc_next; + uint32_t pc_end = ctx->base.tb->cs_base; + int backup = sextract32(ctx->tbflags, GUSA_SHIFT, 8); + int max_insns = (pc_end - pc) / 2; + + if (pc != pc_end + backup || max_insns < 2) { + /* This is a malformed gUSA region. Don't do anything special, + since the interpreter is likely to get confused. */ + ctx->envflags &= ~GUSA_MASK; + } else if (tbflags & GUSA_EXCLUSIVE) { + /* Regardless of single-stepping or the end of the page, + we must complete execution of the gUSA region while + holding the exclusive lock. */ + ctx->base.max_insns = max_insns; + return; + } } - max_insns = MIN(max_insns, TCG_MAX_INSNS); /* Since the ISA is fixed-width, we can bound by the number of instructions remaining on the page. */ - num_insns = -(ctx.base.pc_next | TARGET_PAGE_MASK) / 2; - max_insns = MIN(max_insns, num_insns); + bound = -(ctx->base.pc_next | TARGET_PAGE_MASK) / 2; + ctx->base.max_insns = MIN(ctx->base.max_insns, bound); +} - /* Single stepping means just that. */ - if (ctx.base.singlestep_enabled || singlestep) { - max_insns = 1; - } +static void sh4_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) +{ +} - gen_tb_start(tb); - num_insns = 0; +static void sh4_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(ctx->base.pc_next, ctx->envflags); +} + +static bool sh4_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); + + /* We have hit a breakpoint - make sure PC is up-to-date */ + gen_save_cpu_state(ctx, true); + gen_helper_debug(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + ctx->base.pc_next += 2; + return true; +} + +static void sh4_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + CPUSH4State *env = cs->env_ptr; + DisasContext *ctx = container_of(dcbase, DisasContext, base); #ifdef CONFIG_USER_ONLY - if (ctx.tbflags & GUSA_MASK) { - num_insns = decode_gusa(&ctx, env, &max_insns); + if (unlikely(ctx->envflags & GUSA_MASK) + && !(ctx->envflags & GUSA_EXCLUSIVE)) { + /* We're in an gUSA region, and we have not already fallen + back on using an exclusive region. Attempt to parse the + region into a single supported atomic operation. Failure + is handled within the parser by raising an exception to + retry using an exclusive region. */ + decode_gusa(ctx, env); + return; } #endif - while (ctx.base.is_jmp == DISAS_NEXT - && num_insns < max_insns - && !tcg_op_buf_full()) { - tcg_gen_insn_start(ctx.base.pc_next, ctx.envflags); - num_insns++; + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + decode_opc(ctx); + ctx->base.pc_next += 2; +} - if (unlikely(cpu_breakpoint_test(cs, ctx.base.pc_next, BP_ANY))) { - /* We have hit a breakpoint - make sure PC is up-to-date */ - gen_save_cpu_state(&ctx, true); - gen_helper_debug(cpu_env); - ctx.base.is_jmp = DISAS_NORETURN; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - ctx.base.pc_next += 2; - break; - } +static void sh4_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *ctx = container_of(dcbase, DisasContext, base); - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } - - ctx.opcode = cpu_lduw_code(env, ctx.base.pc_next); - decode_opc(&ctx); - ctx.base.pc_next += 2; - } - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); - } - - if (ctx.tbflags & GUSA_EXCLUSIVE) { + if (ctx->tbflags & GUSA_EXCLUSIVE) { /* Ending the region of exclusivity. Clear the bits. */ - ctx.envflags &= ~GUSA_MASK; + ctx->envflags &= ~GUSA_MASK; } - switch (ctx.base.is_jmp) { + switch (ctx->base.is_jmp) { case DISAS_STOP: - gen_save_cpu_state(&ctx, true); - if (ctx.base.singlestep_enabled) { + gen_save_cpu_state(ctx, true); + if (ctx->base.singlestep_enabled) { gen_helper_debug(cpu_env); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } break; case DISAS_NEXT: - gen_save_cpu_state(&ctx, false); - gen_goto_tb(&ctx, 0, ctx.base.pc_next); + case DISAS_TOO_MANY: + gen_save_cpu_state(ctx, false); + gen_goto_tb(ctx, 0, ctx->base.pc_next); break; case DISAS_NORETURN: break; default: g_assert_not_reached(); } +} - gen_tb_end(tb, num_insns); +static void sh4_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs) +{ + qemu_log("IN:\n"); /* , lookup_symbol(dcbase->pc_first)); */ + log_target_disas(cs, dcbase->pc_first, dcbase->tb->size); +} - tb->size = ctx.base.pc_next - pc_start; - tb->icount = num_insns; +static const TranslatorOps sh4_tr_ops = { + .init_disas_context = sh4_tr_init_disas_context, + .tb_start = sh4_tr_tb_start, + .insn_start = sh4_tr_insn_start, + .breakpoint_check = sh4_tr_breakpoint_check, + .translate_insn = sh4_tr_translate_insn, + .tb_stop = sh4_tr_tb_stop, + .disas_log = sh4_tr_disas_log, +}; -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("IN:\n"); /* , lookup_symbol(pc_start)); */ - log_target_disas(cs, pc_start, ctx.base.pc_next - pc_start); - qemu_log("\n"); - qemu_log_unlock(); - } -#endif +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +{ + DisasContext ctx; + + translator_loop(&sh4_tr_ops, &ctx.base, cs, tb); } void restore_state_to_opc(CPUSH4State *env, TranslationBlock *tb, diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index ff6ed91f9a..0f090ece54 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -647,15 +647,18 @@ void sparc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, } } - for (i = 0; i < TARGET_DPREGS; i++) { - if ((i & 3) == 0) { - cpu_fprintf(f, "%%f%02d: ", i * 2); - } - cpu_fprintf(f, " %016" PRIx64, env->fpr[i].ll); - if ((i & 3) == 3) { - cpu_fprintf(f, "\n"); + if (flags & CPU_DUMP_FPU) { + for (i = 0; i < TARGET_DPREGS; i++) { + if ((i & 3) == 0) { + cpu_fprintf(f, "%%f%02d: ", i * 2); + } + cpu_fprintf(f, " %016" PRIx64, env->fpr[i].ll); + if ((i & 3) == 3) { + cpu_fprintf(f, "\n"); + } } } + #ifdef TARGET_SPARC64 cpu_fprintf(f, "pstate: %08x ccr: %02x (icc: ", env->pstate, (unsigned)cpu_get_ccr(env)); diff --git a/target/sparc/helper.c b/target/sparc/helper.c index 1d854890b4..46232788c8 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -67,7 +67,9 @@ uint64_t helper_tick_get_count(CPUSPARCState *env, void *opaque, int mem_idx) return cpu_tick_get_count(timer); #else - return 0; + /* In user-mode, QEMU_CLOCK_VIRTUAL doesn't exist. + Just pass through the host cpu clock ticks. */ + return cpu_get_host_ticks(); #endif } diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index f8886ae039..135a9c9d9b 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "exec/exec-all.h" #include "trace.h" -#include "exec/address-spaces.h" /* Sparc MMU emulation */ diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 5aa367a182..74315cdf09 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -30,6 +30,7 @@ #include "exec/helper-gen.h" #include "trace-tcg.h" +#include "exec/translator.h" #include "exec/log.h" #include "asi.h" @@ -40,6 +41,8 @@ #define JUMP_PC 2 /* dynamic pc value which takes only two values according to jump_pc[T2] */ +#define DISAS_EXIT DISAS_TARGET_0 + /* global register indexes */ static TCGv_ptr cpu_regwptr; static TCGv cpu_cc_src, cpu_cc_src2, cpu_cc_dst; @@ -66,14 +69,13 @@ static TCGv_i64 cpu_fpr[TARGET_DPREGS]; #include "exec/gen-icount.h" typedef struct DisasContext { + DisasContextBase base; target_ulong pc; /* current Program Counter: integer or DYNAMIC_PC */ target_ulong npc; /* next PC: integer or DYNAMIC_PC or JUMP_PC */ target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */ - int is_br; int mem_idx; bool fpu_enabled; bool address_mask_32bit; - bool singlestep; #ifndef CONFIG_USER_ONLY bool supervisor; #ifdef TARGET_SPARC64 @@ -82,7 +84,6 @@ typedef struct DisasContext { #endif uint32_t cc_op; /* current CC operation */ - struct TranslationBlock *tb; sparc_def_t *def; TCGv_i32 t32[3]; TCGv ttl[5]; @@ -341,13 +342,13 @@ static inline TCGv gen_dest_gpr(DisasContext *dc, int reg) static inline bool use_goto_tb(DisasContext *s, target_ulong pc, target_ulong npc) { - if (unlikely(s->singlestep)) { + if (unlikely(s->base.singlestep_enabled || singlestep)) { return false; } #ifndef CONFIG_USER_ONLY - return (pc & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK) && - (npc & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK); + return (pc & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK) && + (npc & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK); #else return true; #endif @@ -361,12 +362,12 @@ static inline void gen_goto_tb(DisasContext *s, int tb_num, tcg_gen_goto_tb(tb_num); tcg_gen_movi_tl(cpu_pc, pc); tcg_gen_movi_tl(cpu_npc, npc); - tcg_gen_exit_tb((uintptr_t)s->tb + tb_num); + tcg_gen_exit_tb(s->base.tb, tb_num); } else { /* jump to another page: currently not optimized */ tcg_gen_movi_tl(cpu_pc, pc); tcg_gen_movi_tl(cpu_npc, npc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -995,7 +996,7 @@ static void gen_branch_a(DisasContext *dc, target_ulong pc1) gen_set_label(l1); gen_goto_tb(dc, 1, npc + 4, npc + 8); - dc->is_br = 1; + dc->base.is_jmp = DISAS_NORETURN; } static void gen_branch_n(DisasContext *dc, target_ulong pc1) @@ -1078,7 +1079,7 @@ static void gen_exception(DisasContext *dc, int which) t = tcg_const_i32(which); gen_helper_raise_exception(cpu_env, t); tcg_temp_free_i32(t); - dc->is_br = 1; + dc->base.is_jmp = DISAS_NORETURN; } static void gen_check_align(TCGv addr, int mask) @@ -2441,7 +2442,7 @@ static void gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn) default: /* ??? In theory, this should be raise DAE_invalid_asi. But the SS-20 roms do ldstuba [%l0] #ASI_M_CTL, %o1. */ - if (tb_cflags(dc->tb) & CF_PARALLEL) { + if (tb_cflags(dc->base.tb) & CF_PARALLEL) { gen_helper_exit_atomic(cpu_env); } else { TCGv_i32 r_asi = tcg_const_i32(da.asi); @@ -3351,7 +3352,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) if (cond == 8) { /* An unconditional trap ends the TB. */ - dc->is_br = 1; + dc->base.is_jmp = DISAS_NORETURN; goto jmp_insn; } else { /* A conditional trap falls through to the next insn. */ @@ -3401,11 +3402,17 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_const = tcg_const_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, r_const); tcg_temp_free_ptr(r_tickptr); tcg_temp_free_i32(r_const); gen_store_gpr(dc, rd, cpu_dst); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } } break; case 0x5: /* V9 rdpc */ @@ -3448,11 +3455,17 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_const = tcg_const_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_get_count(cpu_dst, cpu_env, r_tickptr, r_const); tcg_temp_free_ptr(r_tickptr); tcg_temp_free_i32(r_const); gen_store_gpr(dc, rd, cpu_dst); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } } break; case 0x19: /* System tick compare */ @@ -3577,10 +3590,16 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_const = tcg_const_i32(dc->mem_idx); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_get_count(cpu_tmp0, cpu_env, r_tickptr, r_const); tcg_temp_free_ptr(r_tickptr); tcg_temp_free_i32(r_const); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } } break; case 5: // tba @@ -4330,8 +4349,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) /* End TB to notice changed ASI. */ save_state(dc); gen_op_next_insn(); - tcg_gen_exit_tb(0); - dc->is_br = 1; + tcg_gen_exit_tb(NULL, 0); + dc->base.is_jmp = DISAS_NORETURN; break; case 0x6: /* V9 wrfprs */ tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); @@ -4339,8 +4358,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) dc->fprs_dirty = 0; save_state(dc); gen_op_next_insn(); - tcg_gen_exit_tb(0); - dc->is_br = 1; + tcg_gen_exit_tb(NULL, 0); + dc->base.is_jmp = DISAS_NORETURN; break; case 0xf: /* V9 sir, nop if user */ #if !defined(CONFIG_USER_ONLY) @@ -4386,9 +4405,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_limit(r_tickptr, cpu_tick_cmpr); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; case 0x18: /* System tick */ @@ -4404,9 +4433,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_count(r_tickptr, cpu_tmp0); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; case 0x19: /* System tick compare */ @@ -4422,9 +4461,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, stick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_limit(r_tickptr, cpu_stick_cmpr); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; @@ -4467,8 +4516,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) dc->cc_op = CC_OP_FLAGS; save_state(dc); gen_op_next_insn(); - tcg_gen_exit_tb(0); - dc->is_br = 1; + tcg_gen_exit_tb(NULL, 0); + dc->base.is_jmp = DISAS_NORETURN; #endif } break; @@ -4532,9 +4581,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, tick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_count(r_tickptr, cpu_tmp0); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; case 5: // tba @@ -4542,7 +4601,13 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) break; case 6: // pstate save_state(dc); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_wrpstate(cpu_env, cpu_tmp0); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } dc->npc = DYNAMIC_PC; break; case 7: // tl @@ -4552,7 +4617,13 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) dc->npc = DYNAMIC_PC; break; case 8: // pil + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_wrpil(cpu_env, cpu_tmp0); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } break; case 9: // cwp gen_helper_wrcwp(cpu_env, cpu_tmp0); @@ -4623,8 +4694,8 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) hpstate)); save_state(dc); gen_op_next_insn(); - tcg_gen_exit_tb(0); - dc->is_br = 1; + tcg_gen_exit_tb(NULL, 0); + dc->base.is_jmp = DISAS_NORETURN; break; case 1: // htstate // XXX gen_op_wrhtstate(); @@ -4643,9 +4714,19 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) r_tickptr = tcg_temp_new_ptr(); tcg_gen_ld_ptr(r_tickptr, cpu_env, offsetof(CPUSPARCState, hstick)); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_tick_set_limit(r_tickptr, cpu_hstick_cmpr); tcg_temp_free_ptr(r_tickptr); + if (tb_cflags(dc->base.tb) & + CF_USE_ICOUNT) { + gen_io_end(); + } + /* End TB to handle timer interrupt */ + dc->base.is_jmp = DISAS_EXIT; } break; case 6: // hver readonly @@ -5266,14 +5347,26 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) goto priv_insn; dc->npc = DYNAMIC_PC; dc->pc = DYNAMIC_PC; + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_done(cpu_env); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } goto jmp_insn; case 1: if (!supervisor(dc)) goto priv_insn; dc->npc = DYNAMIC_PC; dc->pc = DYNAMIC_PC; + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_helper_retry(cpu_env); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } goto jmp_insn; default: goto illegal_insn; @@ -5690,7 +5783,7 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } else if (dc->npc == JUMP_PC) { /* we can do a static jump */ gen_branch2(dc, dc->jump_pc[0], dc->jump_pc[1], cpu_cond); - dc->is_br = 1; + dc->base.is_jmp = DISAS_NORETURN; } else { dc->pc = dc->npc; dc->npc = dc->npc + 4; @@ -5738,99 +5831,94 @@ static void disas_sparc_insn(DisasContext * dc, unsigned int insn) } } -void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) +static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { + DisasContext *dc = container_of(dcbase, DisasContext, base); CPUSPARCState *env = cs->env_ptr; - target_ulong pc_start, last_pc; - DisasContext dc1, *dc = &dc1; - int num_insns; - int max_insns; - unsigned int insn; + int bound; - memset(dc, 0, sizeof(DisasContext)); - dc->tb = tb; - pc_start = tb->pc; - dc->pc = pc_start; - last_pc = dc->pc; - dc->npc = (target_ulong) tb->cs_base; + dc->pc = dc->base.pc_first; + dc->npc = (target_ulong)dc->base.tb->cs_base; dc->cc_op = CC_OP_DYNAMIC; - dc->mem_idx = tb->flags & TB_FLAG_MMU_MASK; + dc->mem_idx = dc->base.tb->flags & TB_FLAG_MMU_MASK; dc->def = &env->def; - dc->fpu_enabled = tb_fpu_enabled(tb->flags); - dc->address_mask_32bit = tb_am_enabled(tb->flags); - dc->singlestep = (cs->singlestep_enabled || singlestep); + dc->fpu_enabled = tb_fpu_enabled(dc->base.tb->flags); + dc->address_mask_32bit = tb_am_enabled(dc->base.tb->flags); #ifndef CONFIG_USER_ONLY - dc->supervisor = (tb->flags & TB_FLAG_SUPER) != 0; + dc->supervisor = (dc->base.tb->flags & TB_FLAG_SUPER) != 0; #endif #ifdef TARGET_SPARC64 dc->fprs_dirty = 0; - dc->asi = (tb->flags >> TB_FLAG_ASI_SHIFT) & 0xff; + dc->asi = (dc->base.tb->flags >> TB_FLAG_ASI_SHIFT) & 0xff; #ifndef CONFIG_USER_ONLY - dc->hypervisor = (tb->flags & TB_FLAG_HYPER) != 0; + dc->hypervisor = (dc->base.tb->flags & TB_FLAG_HYPER) != 0; #endif #endif + /* + * if we reach a page boundary, we stop generation so that the + * PC of a TT_TFAULT exception is always in the right page + */ + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + dc->base.max_insns = MIN(dc->base.max_insns, bound); +} - num_insns = 0; - max_insns = tb_cflags(tb) & CF_COUNT_MASK; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; +static void sparc_tr_tb_start(DisasContextBase *db, CPUState *cs) +{ +} + +static void sparc_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + if (dc->npc & JUMP_PC) { + assert(dc->jump_pc[1] == dc->pc + 4); + tcg_gen_insn_start(dc->pc, dc->jump_pc[0] | JUMP_PC); + } else { + tcg_gen_insn_start(dc->pc, dc->npc); } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; +} + +static bool sparc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + if (dc->pc != dc->base.pc_first) { + save_state(dc); } + gen_helper_debug(cpu_env); + tcg_gen_exit_tb(NULL, 0); + dc->base.is_jmp = DISAS_NORETURN; + /* update pc_next so that the current instruction is included in tb->size */ + dc->base.pc_next += 4; + return true; +} - gen_tb_start(tb); - do { - if (dc->npc & JUMP_PC) { - assert(dc->jump_pc[1] == dc->pc + 4); - tcg_gen_insn_start(dc->pc, dc->jump_pc[0] | JUMP_PC); - } else { - tcg_gen_insn_start(dc->pc, dc->npc); - } - num_insns++; - last_pc = dc->pc; +static void sparc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUSPARCState *env = cs->env_ptr; + unsigned int insn; - if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { - if (dc->pc != pc_start) { - save_state(dc); - } - gen_helper_debug(cpu_env); - tcg_gen_exit_tb(0); - dc->is_br = 1; - goto exit_gen_loop; - } + insn = cpu_ldl_code(env, dc->pc); + dc->base.pc_next += 4; + disas_sparc_insn(dc, insn); - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } - - insn = cpu_ldl_code(env, dc->pc); - - disas_sparc_insn(dc, insn); - - if (dc->is_br) - break; - /* if the next PC is different, we abort now */ - if (dc->pc != (last_pc + 4)) - break; - /* if we reach a page boundary, we stop generation so that the - PC of a TT_TFAULT exception is always in the right page */ - if ((dc->pc & (TARGET_PAGE_SIZE - 1)) == 0) - break; - /* if single step mode, we generate only one instruction and - generate an exception */ - if (dc->singlestep) { - break; - } - } while (!tcg_op_buf_full() && - (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32) && - num_insns < max_insns); - - exit_gen_loop: - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); + if (dc->base.is_jmp == DISAS_NORETURN) { + return; } - if (!dc->is_br) { + if (dc->pc != dc->base.pc_next) { + dc->base.is_jmp = DISAS_TOO_MANY; + } +} + +static void sparc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + switch (dc->base.is_jmp) { + case DISAS_NEXT: + case DISAS_TOO_MANY: if (dc->pc != DYNAMIC_PC && (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) { /* static PC and NPC: we can use direct chaining */ @@ -5840,25 +5928,45 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock * tb) tcg_gen_movi_tl(cpu_pc, dc->pc); } save_npc(dc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } - } - gen_tb_end(tb, num_insns); + break; - tb->size = last_pc + 4 - pc_start; - tb->icount = num_insns; + case DISAS_NORETURN: + break; -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("--------------\n"); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, last_pc + 4 - pc_start); - qemu_log("\n"); - qemu_log_unlock(); + case DISAS_EXIT: + /* Exit TB */ + save_state(dc); + tcg_gen_exit_tb(NULL, 0); + break; + + default: + g_assert_not_reached(); } -#endif +} + +static void sparc_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps sparc_tr_ops = { + .init_disas_context = sparc_tr_init_disas_context, + .tb_start = sparc_tr_tb_start, + .insn_start = sparc_tr_insn_start, + .breakpoint_check = sparc_tr_breakpoint_check, + .translate_insn = sparc_tr_translate_insn, + .tb_stop = sparc_tr_tb_stop, + .disas_log = sparc_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +{ + DisasContext dc = {}; + + translator_loop(&sparc_tr_ops, &dc.base, cs, tb); } void sparc_tcg_init(void) diff --git a/target/tilegx/cpu.c b/target/tilegx/cpu.c index b7451bdcf2..bfe9be59b5 100644 --- a/target/tilegx/cpu.c +++ b/target/tilegx/cpu.c @@ -24,7 +24,6 @@ #include "qemu-common.h" #include "hw/qdev-properties.h" #include "linux-user/syscall_defs.h" -#include "exec/exec-all.h" static void tilegx_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) diff --git a/target/tilegx/translate.c b/target/tilegx/translate.c index d63bf5bba3..f201150fc7 100644 --- a/target/tilegx/translate.c +++ b/target/tilegx/translate.c @@ -2362,7 +2362,7 @@ static void translate_one_bundle(DisasContext *dc, uint64_t bundle) tcg_temp_free_i64(next); } tcg_temp_free_i64(dc->jmp.dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); dc->exit_tb = true; } else if (dc->atomic_excp != TILEGX_EXCP_NONE) { gen_exception(dc, dc->atomic_excp); @@ -2375,7 +2375,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) DisasContext ctx; DisasContext *dc = &ctx; uint64_t pc_start = tb->pc; - uint64_t next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + uint64_t page_start = pc_start & TARGET_PAGE_MASK; int num_insns = 0; int max_insns = tb_cflags(tb) & CF_COUNT_MASK; @@ -2415,11 +2415,11 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) } dc->pc += TILEGX_BUNDLE_SIZE_IN_BYTES; if (num_insns >= max_insns - || dc->pc >= next_page_start + || (dc->pc - page_start >= TARGET_PAGE_SIZE) || tcg_op_buf_full()) { /* Ending the TB due to TB size or page boundary. Set PC. */ tcg_gen_movi_tl(cpu_pc, dc->pc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; } } diff --git a/target/tricore/translate.c b/target/tricore/translate.c index aef0d9cf06..b5ab40d4a2 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -3253,13 +3253,13 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (use_goto_tb(ctx, dest)) { tcg_gen_goto_tb(n); gen_save_pc(dest); - tcg_gen_exit_tb((uintptr_t)ctx->tb + n); + tcg_gen_exit_tb(ctx->tb, n); } else { gen_save_pc(dest); if (ctx->singlestep_enabled) { /* raise exception debug */ } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -3327,7 +3327,7 @@ static void gen_fret(DisasContext *ctx) tcg_gen_qemu_ld_tl(cpu_gpr_a[11], cpu_gpr_a[10], ctx->mem_idx, MO_LESL); tcg_gen_addi_tl(cpu_gpr_a[10], cpu_gpr_a[10], 4); tcg_gen_mov_tl(cpu_PC, temp); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; tcg_temp_free(temp); @@ -3431,12 +3431,12 @@ static void gen_compute_branch(DisasContext *ctx, uint32_t opc, int r1, /* SR-format jumps */ case OPC1_16_SR_JI: tcg_gen_andi_tl(cpu_PC, cpu_gpr_a[r1], 0xfffffffe); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case OPC2_32_SYS_RET: case OPC2_16_SR_RET: gen_helper_ret(cpu_env); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; /* B-format */ case OPC1_32_B_CALLA: @@ -3939,7 +3939,7 @@ static void decode_sr_system(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_16_SR_RFE: gen_helper_rfe(cpu_env); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; break; case OPC2_16_SR_DEBUG: @@ -6578,7 +6578,7 @@ static void decode_rr_idirect(CPUTriCoreState *env, DisasContext *ctx) default: generate_trap(ctx, TRAPC_INSN_ERR, TIN2_IOPC); } - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; } @@ -8398,7 +8398,7 @@ static void decode_sys_interrupts(CPUTriCoreState *env, DisasContext *ctx) break; case OPC2_32_SYS_RFE: gen_helper_rfe(cpu_env); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; break; case OPC2_32_SYS_RFM: @@ -8411,7 +8411,7 @@ static void decode_sys_interrupts(CPUTriCoreState *env, DisasContext *ctx) tcg_gen_brcondi_tl(TCG_COND_NE, tmp, 1, l1); gen_helper_rfm(cpu_env); gen_set_label(l1); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); ctx->bstate = BS_BRANCH; tcg_temp_free(tmp); } else { @@ -8845,7 +8845,7 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) if (num_insns >= max_insns || tcg_op_buf_full()) { gen_save_pc(ctx.next_pc); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; } ctx.pc = ctx.next_pc; diff --git a/target/unicore32/cpu.c b/target/unicore32/cpu.c index 29d160a88d..68f978d80b 100644 --- a/target/unicore32/cpu.c +++ b/target/unicore32/cpu.c @@ -70,7 +70,6 @@ static void unicore_ii_cpu_initfn(Object *obj) set_feature(env, UC32_HWCAP_CMOV); set_feature(env, UC32_HWCAP_UCF64); - set_snan_bit_is_one(1, &env->ucf64.fp_status); } static void uc32_any_cpu_initfn(Object *obj) @@ -83,7 +82,6 @@ static void uc32_any_cpu_initfn(Object *obj) set_feature(env, UC32_HWCAP_CMOV); set_feature(env, UC32_HWCAP_UCF64); - set_snan_bit_is_one(1, &env->ucf64.fp_status); } static void uc32_cpu_realizefn(DeviceState *dev, Error **errp) diff --git a/target/unicore32/translate.c b/target/unicore32/translate.c index 5b51f2166d..002569ff3b 100644 --- a/target/unicore32/translate.c +++ b/target/unicore32/translate.c @@ -1106,10 +1106,10 @@ static inline void gen_goto_tb(DisasContext *s, int n, uint32_t dest) if (use_goto_tb(s, dest)) { tcg_gen_goto_tb(n); gen_set_pc_im(dest); - tcg_gen_exit_tb((uintptr_t)s->tb + n); + tcg_gen_exit_tb(s->tb, n); } else { gen_set_pc_im(dest); - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -1875,7 +1875,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) CPUUniCore32State *env = cs->env_ptr; DisasContext dc1, *dc = &dc1; target_ulong pc_start; - uint32_t next_page_start; + uint32_t page_start; int num_insns; int max_insns; @@ -1894,7 +1894,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) cpu_F1s = tcg_temp_new_i32(); cpu_F0d = tcg_temp_new_i64(); cpu_F1d = tcg_temp_new_i64(); - next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + page_start = pc_start & TARGET_PAGE_MASK; num_insns = 0; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { @@ -1951,7 +1951,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) } while (!dc->is_jmp && !tcg_op_buf_full() && !cs->singlestep_enabled && !singlestep && - dc->pc < next_page_start && + dc->pc - page_start < TARGET_PAGE_SIZE && num_insns < max_insns); if (tb_cflags(tb) & CF_LAST_IO) { @@ -2002,7 +2002,7 @@ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) case DISAS_JUMP: case DISAS_UPDATE: /* indicate that the hash table must be used to find the next TB */ - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: /* nothing more to generate */ @@ -2101,7 +2101,9 @@ void uc32_cpu_dump_state(CPUState *cs, FILE *f, psr & (1 << 28) ? 'V' : '-', cpu_mode_names[psr & 0xf]); - cpu_dump_state_ucf64(env, f, cpu_fprintf, flags); + if (flags & CPU_DUMP_FPU) { + cpu_dump_state_ucf64(env, f, cpu_fprintf, flags); + } } void restore_state_to_opc(CPUUniCore32State *env, TranslationBlock *tb, diff --git a/target/xtensa/core-dc232b.c b/target/xtensa/core-dc232b.c index 7331eeea2f..7131337840 100644 --- a/target/xtensa/core-dc232b.c +++ b/target/xtensa/core-dc232b.c @@ -27,9 +27,9 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu/host-utils.h" +#include "qemu/timer.h" #include "core-dc232b/core-isa.h" #include "overlay_tool.h" diff --git a/target/xtensa/core-dc232b/xtensa-modules.inc.c b/target/xtensa/core-dc232b/xtensa-modules.inc.c index d322c3f52a..164df3b1a4 100644 --- a/target/xtensa/core-dc232b/xtensa-modules.inc.c +++ b/target/xtensa/core-dc232b/xtensa-modules.inc.c @@ -1736,9 +1736,7 @@ Operand_arr_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_arr_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1750,9 +1748,7 @@ Operand_ars_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1764,9 +1760,7 @@ Operand_art_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_art_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1778,9 +1772,7 @@ Operand_ar0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1792,9 +1784,7 @@ Operand_ar4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar4_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1806,9 +1796,7 @@ Operand_ar8_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar8_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1820,9 +1808,7 @@ Operand_ar12_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar12_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1834,9 +1820,7 @@ Operand_ars_entry_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_entry_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -2406,9 +2390,7 @@ Operand_mx_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mx_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2436,9 +2418,7 @@ Operand_mw_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mw_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2450,9 +2430,7 @@ Operand_mr0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2464,9 +2442,7 @@ Operand_mr1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr1_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2478,9 +2454,7 @@ Operand_mr2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr2_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2492,9 +2466,7 @@ Operand_mr3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr3_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int diff --git a/target/xtensa/core-dc233c.c b/target/xtensa/core-dc233c.c index 8296e6fa10..d701e3f5de 100644 --- a/target/xtensa/core-dc233c.c +++ b/target/xtensa/core-dc233c.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/core-dc233c/xtensa-modules.inc.c b/target/xtensa/core-dc233c/xtensa-modules.inc.c index 7c20f82349..0f32f0804a 100644 --- a/target/xtensa/core-dc233c/xtensa-modules.inc.c +++ b/target/xtensa/core-dc233c/xtensa-modules.inc.c @@ -1817,9 +1817,7 @@ Operand_arr_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_arr_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1831,9 +1829,7 @@ Operand_ars_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1845,9 +1841,7 @@ Operand_art_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_art_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1859,9 +1853,7 @@ Operand_ar0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1873,9 +1865,7 @@ Operand_ar4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar4_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1887,9 +1877,7 @@ Operand_ar8_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar8_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1901,9 +1889,7 @@ Operand_ar12_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar12_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -1915,9 +1901,7 @@ Operand_ars_entry_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_entry_encode (uint32 *valp) { - int error; - error = (*valp & ~0x1f) != 0; - return error; + return (*valp & ~0x1f) != 0; } static int @@ -2487,9 +2471,7 @@ Operand_mx_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mx_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2517,9 +2499,7 @@ Operand_mw_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mw_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2531,9 +2511,7 @@ Operand_mr0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2545,9 +2523,7 @@ Operand_mr1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr1_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2559,9 +2535,7 @@ Operand_mr2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr2_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int @@ -2573,9 +2547,7 @@ Operand_mr3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_mr3_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3) != 0; - return error; + return (*valp & ~0x3) != 0; } static int diff --git a/target/xtensa/core-de212.c b/target/xtensa/core-de212.c index 53775a97fa..7322179b56 100644 --- a/target/xtensa/core-de212.c +++ b/target/xtensa/core-de212.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/core-de212/xtensa-modules.inc.c b/target/xtensa/core-de212/xtensa-modules.inc.c index ef7674de3a..480c68d3c6 100644 --- a/target/xtensa/core-de212/xtensa-modules.inc.c +++ b/target/xtensa/core-de212/xtensa-modules.inc.c @@ -1798,9 +1798,7 @@ OperandSem_opnd_sem_AR_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1812,9 +1810,7 @@ OperandSem_opnd_sem_AR_0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_0_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1826,9 +1822,7 @@ OperandSem_opnd_sem_AR_1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_1_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1840,9 +1834,7 @@ OperandSem_opnd_sem_AR_2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_2_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1854,9 +1846,7 @@ OperandSem_opnd_sem_AR_3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_3_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1868,9 +1858,7 @@ OperandSem_opnd_sem_AR_4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_4_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -2464,9 +2452,7 @@ OperandSem_opnd_sem_MR_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2478,9 +2464,7 @@ OperandSem_opnd_sem_MR_1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_1_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2492,9 +2476,7 @@ OperandSem_opnd_sem_MR_2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_2_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2506,9 +2488,7 @@ OperandSem_opnd_sem_MR_3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_3_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2520,9 +2500,7 @@ OperandSem_opnd_sem_MR_4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_4_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int @@ -2534,9 +2512,7 @@ OperandSem_opnd_sem_MR_5_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_MR_5_encode (uint32 *valp) { - int error; - error = (*valp >= 4); - return error; + return (*valp >= 4); } static int diff --git a/target/xtensa/core-fsf.c b/target/xtensa/core-fsf.c index 01932bdc8b..e100e212b9 100644 --- a/target/xtensa/core-fsf.c +++ b/target/xtensa/core-fsf.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/core-fsf/xtensa-modules.inc.c b/target/xtensa/core-fsf/xtensa-modules.inc.c index f7de2dec15..c32683ff77 100644 --- a/target/xtensa/core-fsf/xtensa-modules.inc.c +++ b/target/xtensa/core-fsf/xtensa-modules.inc.c @@ -1379,9 +1379,7 @@ Operand_arr_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_arr_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1393,9 +1391,7 @@ Operand_ars_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1407,9 +1403,7 @@ Operand_art_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_art_encode (uint32 *valp) { - int error; - error = (*valp & ~0xf) != 0; - return error; + return (*valp & ~0xf) != 0; } static int @@ -1421,9 +1415,7 @@ Operand_ar0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar0_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int @@ -1435,9 +1427,7 @@ Operand_ar4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar4_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int @@ -1449,9 +1439,7 @@ Operand_ar8_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar8_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int @@ -1463,9 +1451,7 @@ Operand_ar12_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ar12_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int @@ -1477,9 +1463,7 @@ Operand_ars_entry_decode (uint32 *valp ATTRIBUTE_UNUSED) static int Operand_ars_entry_encode (uint32 *valp) { - int error; - error = (*valp & ~0x3f) != 0; - return error; + return (*valp & ~0x3f) != 0; } static int diff --git a/target/xtensa/core-sample_controller.c b/target/xtensa/core-sample_controller.c index c622335ca5..f433ea8d66 100644 --- a/target/xtensa/core-sample_controller.c +++ b/target/xtensa/core-sample_controller.c @@ -27,7 +27,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/core-sample_controller/xtensa-modules.inc.c b/target/xtensa/core-sample_controller/xtensa-modules.inc.c index fba41b99ae..7e87d216bd 100644 --- a/target/xtensa/core-sample_controller/xtensa-modules.inc.c +++ b/target/xtensa/core-sample_controller/xtensa-modules.inc.c @@ -1570,9 +1570,7 @@ OperandSem_opnd_sem_AR_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1584,9 +1582,7 @@ OperandSem_opnd_sem_AR_0_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_0_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1598,9 +1594,7 @@ OperandSem_opnd_sem_AR_1_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_1_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1612,9 +1606,7 @@ OperandSem_opnd_sem_AR_2_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_2_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1626,9 +1618,7 @@ OperandSem_opnd_sem_AR_3_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_3_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int @@ -1640,9 +1630,7 @@ OperandSem_opnd_sem_AR_4_decode (uint32 *valp ATTRIBUTE_UNUSED) static int OperandSem_opnd_sem_AR_4_encode (uint32 *valp) { - int error; - error = (*valp >= 32); - return error; + return (*valp >= 32); } static int diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 2b5b537222..590813d4f7 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -33,7 +33,6 @@ #include "cpu.h" #include "qemu-common.h" #include "migration/vmstate.h" -#include "exec/exec-all.h" static void xtensa_cpu_set_pc(CPUState *cs, vaddr value) @@ -150,7 +149,7 @@ static void xtensa_cpu_initfn(Object *obj) #ifndef CONFIG_USER_ONLY env->address_space_er = g_malloc(sizeof(*env->address_space_er)); env->system_er = g_malloc(sizeof(*env->system_er)); - memory_region_init_io(env->system_er, NULL, NULL, env, "er", + memory_region_init_io(env->system_er, obj, NULL, env, "er", UINT64_C(0x100000000)); address_space_init(env->address_space_er, env->system_er, "ER"); #endif diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index e9d2e109f7..51b4551464 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -369,6 +369,7 @@ struct XtensaConfig { unsigned nareg; int excm_level; int ndepc; + unsigned inst_fetch_width; uint32_t vecbase; uint32_t exception_vector[EXC_MAX]; unsigned ninterrupt; diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index 34844eead3..c9a6132700 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" @@ -726,10 +727,10 @@ static void dump_tlb(FILE *f, fprintf_function cpu_fprintf, bool print_header = true; if (sz >= 0x100000) { - sz >>= 20; + sz /= MiB; sz_text = "MB"; } else { - sz >>= 10; + sz /= KiB; sz_text = "KB"; } diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh index af6c610479..039406bf28 100755 --- a/target/xtensa/import_core.sh +++ b/target/xtensa/import_core.sh @@ -39,7 +39,6 @@ tar -xf "$OVERLAY" -O binutils/xtensa-modules.c | \ cat < "${TARGET}.c" #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "qemu-common.h" #include "qemu/host-utils.h" diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index e3bcbe10d6..d4c942d879 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -36,11 +36,6 @@ #include "qemu/timer.h" #include "fpu/softfloat.h" -#ifdef CONFIG_USER_ONLY -/* tb_invalidate_phys_range */ -#include "accel/tcg/translate-all.h" -#endif - #ifndef CONFIG_USER_ONLY void xtensa_cpu_do_unaligned_access(CPUState *cs, @@ -105,7 +100,8 @@ static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) int ret = xtensa_get_physical_addr(env, false, vaddr, 2, 0, &paddr, &page_size, &access); if (ret == 0) { - tb_invalidate_phys_addr(&address_space_memory, paddr); + tb_invalidate_phys_addr(&address_space_memory, paddr, + MEMTXATTRS_UNSPECIFIED); } } @@ -113,9 +109,7 @@ static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) { - mmap_lock(); - tb_invalidate_phys_range(vaddr, vaddr + 1); - mmap_unlock(); + tb_invalidate_phys_addr(vaddr); } #endif @@ -464,7 +458,11 @@ void HELPER(check_interrupts)(CPUXtensaState *env) void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr) { - get_page_addr_code(env, vaddr); + /* + * Attempt the memory load; we don't care about the result but + * only the side-effects (ie any MMU or other exception) + */ + cpu_ldub_code_ra(env, vaddr, GETPC()); } /*! diff --git a/target/xtensa/overlay_tool.h b/target/xtensa/overlay_tool.h index b24ad11fec..ee37a04a17 100644 --- a/target/xtensa/overlay_tool.h +++ b/target/xtensa/overlay_tool.h @@ -456,6 +456,7 @@ .options = XTENSA_OPTIONS, \ .nareg = XCHAL_NUM_AREGS, \ .ndepc = (XCHAL_XEA_VERSION >= 2), \ + .inst_fetch_width = XCHAL_INST_FETCH_WIDTH, \ EXCEPTIONS_SECTION, \ INTERRUPTS_SECTION, \ TLB_SECTION, \ diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 4f6d03059f..d22cdcdb16 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -47,20 +47,14 @@ #include "exec/log.h" -/* is_jmp field values */ -#define DISAS_UPDATE DISAS_TARGET_0 /* cpu state was modified dynamically */ - struct DisasContext { + DisasContextBase base; const XtensaConfig *config; - TranslationBlock *tb; uint32_t pc; - uint32_t next_pc; int cring; int ring; uint32_t lbeg; uint32_t lend; - int is_jmp; - int singlestep_enabled; bool sar_5bit; bool sar_m32_5bit; @@ -317,7 +311,7 @@ static void gen_exception_cause(DisasContext *dc, uint32_t cause) tcg_temp_free(tcause); if (cause == ILLEGAL_INSTRUCTION_CAUSE || cause == SYSCALL_CAUSE) { - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; } } @@ -339,7 +333,7 @@ static void gen_debug_exception(DisasContext *dc, uint32_t cause) tcg_temp_free(tpc); tcg_temp_free(tcause); if (cause & (DEBUGCAUSE_IB | DEBUGCAUSE_BI | DEBUGCAUSE_BN)) { - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; } } @@ -351,7 +345,7 @@ static bool gen_check_privilege(DisasContext *dc) } #endif gen_exception_cause(dc, PRIVILEGED_CAUSE); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; return false; } @@ -360,7 +354,7 @@ static bool gen_check_cpenable(DisasContext *dc, unsigned cp) if (option_enabled(dc, XTENSA_OPTION_COPROCESSOR) && !(dc->cpenable & (1 << cp))) { gen_exception_cause(dc, COPROCESSOR0_DISABLED + cp); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; return false; } return true; @@ -372,17 +366,17 @@ static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot) if (dc->icount) { tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount); } - if (dc->singlestep_enabled) { + if (dc->base.singlestep_enabled) { gen_exception(dc, EXCP_DEBUG); } else { if (slot >= 0) { tcg_gen_goto_tb(slot); - tcg_gen_exit_tb((uintptr_t)dc->tb + slot); + tcg_gen_exit_tb(dc->base.tb, slot); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; } static void gen_jump(DisasContext *dc, TCGv dest) @@ -394,7 +388,7 @@ static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot) { TCGv_i32 tmp = tcg_const_i32(dest); #ifndef CONFIG_USER_ONLY - if (((dc->tb->pc ^ dest) & TARGET_PAGE_MASK) != 0) { + if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) { slot = -1; } #endif @@ -411,7 +405,7 @@ static void gen_callw_slot(DisasContext *dc, int callinc, TCGv_i32 dest, tcallinc, PS_CALLINC_SHIFT, PS_CALLINC_LEN); tcg_temp_free(tcallinc); tcg_gen_movi_i32(cpu_R[callinc << 2], - (callinc << 30) | (dc->next_pc & 0x3fffffff)); + (callinc << 30) | (dc->base.pc_next & 0x3fffffff)); gen_jump_slot(dc, dest, slot); } @@ -424,7 +418,7 @@ static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot) { TCGv_i32 tmp = tcg_const_i32(dest); #ifndef CONFIG_USER_ONLY - if (((dc->tb->pc ^ dest) & TARGET_PAGE_MASK) != 0) { + if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) { slot = -1; } #endif @@ -435,15 +429,15 @@ static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot) static bool gen_check_loop_end(DisasContext *dc, int slot) { if (option_enabled(dc, XTENSA_OPTION_LOOP) && - !(dc->tb->flags & XTENSA_TBFLAG_EXCM) && - dc->next_pc == dc->lend) { + !(dc->base.tb->flags & XTENSA_TBFLAG_EXCM) && + dc->base.pc_next == dc->lend) { TCGLabel *label = gen_new_label(); tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_SR[LCOUNT], 0, label); tcg_gen_subi_i32(cpu_SR[LCOUNT], cpu_SR[LCOUNT], 1); gen_jumpi(dc, dc->lbeg, slot); gen_set_label(label); - gen_jumpi(dc, dc->next_pc, -1); + gen_jumpi(dc, dc->base.pc_next, -1); return true; } return false; @@ -452,7 +446,7 @@ static bool gen_check_loop_end(DisasContext *dc, int slot) static void gen_jumpi_check_loop_end(DisasContext *dc, int slot) { if (!gen_check_loop_end(dc, slot)) { - gen_jumpi(dc, dc->next_pc, slot); + gen_jumpi(dc, dc->base.pc_next, slot); } } @@ -503,12 +497,12 @@ static bool gen_check_sr(DisasContext *dc, uint32_t sr, unsigned access) #ifndef CONFIG_USER_ONLY static bool gen_rsr_ccount(DisasContext *dc, TCGv_i32 d, uint32_t sr) { - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_update_ccount(cpu_env); tcg_gen_mov_i32(d, cpu_SR[sr]); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); return true; } @@ -692,11 +686,11 @@ static bool gen_wsr_cpenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) static void gen_check_interrupts(DisasContext *dc) { - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_check_interrupts(cpu_env); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } } @@ -750,11 +744,11 @@ static bool gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) static bool gen_wsr_ccount(DisasContext *dc, uint32_t sr, TCGv_i32 v) { - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_wsr_ccount(cpu_env, v); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); gen_jumpi_check_loop_end(dc, 0); return true; @@ -791,11 +785,11 @@ static bool gen_wsr_ccompare(DisasContext *dc, uint32_t sr, TCGv_i32 v) tcg_gen_mov_i32(cpu_SR[sr], v); tcg_gen_andi_i32(cpu_SR[INTSET], cpu_SR[INTSET], ~int_bit); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_update_ccompare(cpu_env, tmp); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); gen_jumpi_check_loop_end(dc, 0); ret = true; @@ -895,14 +889,14 @@ static void gen_load_store_alignment(DisasContext *dc, int shift, #ifndef CONFIG_USER_ONLY static void gen_waiti(DisasContext *dc, uint32_t imm4) { - TCGv_i32 pc = tcg_const_i32(dc->next_pc); + TCGv_i32 pc = tcg_const_i32(dc->base.pc_next); TCGv_i32 intlevel = tcg_const_i32(imm4); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } gen_helper_waiti(cpu_env, pc, intlevel); - if (tb_cflags(dc->tb) & CF_USE_ICOUNT) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } tcg_temp_free(pc); @@ -918,7 +912,7 @@ static bool gen_window_check1(DisasContext *dc, unsigned r1) TCGv_i32 w = tcg_const_i32(r1 / 4); gen_helper_window_check(cpu_env, pc, w); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; return false; } return true; @@ -969,7 +963,14 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) return; } - dc->next_pc = dc->pc + len; + dc->base.pc_next = dc->pc + len; + if (xtensa_option_enabled(dc->config, XTENSA_OPTION_LOOP) && + dc->lbeg == dc->pc && + ((dc->pc ^ (dc->base.pc_next - 1)) & -dc->config->inst_fetch_width)) { + qemu_log_mask(LOG_GUEST_ERROR, + "unaligned first instruction of a loop (pc = %08x)\n", + dc->pc); + } for (i = 1; i < len; ++i) { b[i] = cpu_ldub_code(env, dc->pc + i); } @@ -1029,10 +1030,10 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) return; } } - if (dc->is_jmp == DISAS_NEXT) { + if (dc->base.is_jmp == DISAS_NEXT) { gen_check_loop_end(dc, 0); } - dc->pc = dc->next_pc; + dc->pc = dc->base.pc_next; } static inline unsigned xtensa_insn_len(CPUXtensaState *env, DisasContext *dc) @@ -1054,149 +1055,163 @@ static void gen_ibreak_check(CPUXtensaState *env, DisasContext *dc) } } -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) +static void xtensa_tr_init_disas_context(DisasContextBase *dcbase, + CPUState *cpu) { - CPUXtensaState *env = cs->env_ptr; - DisasContext dc; - int insn_count = 0; - int max_insns = tb_cflags(tb) & CF_COUNT_MASK; - uint32_t pc_start = tb->pc; - uint32_t next_page_start = - (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUXtensaState *env = cpu->env_ptr; + uint32_t tb_flags = dc->base.tb->flags; - if (max_insns == 0) { - max_insns = CF_COUNT_MASK; - } - if (max_insns > TCG_MAX_INSNS) { - max_insns = TCG_MAX_INSNS; - } - - dc.config = env->config; - dc.singlestep_enabled = cs->singlestep_enabled; - dc.tb = tb; - dc.pc = pc_start; - dc.ring = tb->flags & XTENSA_TBFLAG_RING_MASK; - dc.cring = (tb->flags & XTENSA_TBFLAG_EXCM) ? 0 : dc.ring; - dc.lbeg = env->sregs[LBEG]; - dc.lend = env->sregs[LEND]; - dc.is_jmp = DISAS_NEXT; - dc.debug = tb->flags & XTENSA_TBFLAG_DEBUG; - dc.icount = tb->flags & XTENSA_TBFLAG_ICOUNT; - dc.cpenable = (tb->flags & XTENSA_TBFLAG_CPENABLE_MASK) >> + dc->config = env->config; + dc->pc = dc->base.pc_first; + dc->ring = tb_flags & XTENSA_TBFLAG_RING_MASK; + dc->cring = (tb_flags & XTENSA_TBFLAG_EXCM) ? 0 : dc->ring; + dc->lbeg = env->sregs[LBEG]; + dc->lend = env->sregs[LEND]; + dc->debug = tb_flags & XTENSA_TBFLAG_DEBUG; + dc->icount = tb_flags & XTENSA_TBFLAG_ICOUNT; + dc->cpenable = (tb_flags & XTENSA_TBFLAG_CPENABLE_MASK) >> XTENSA_TBFLAG_CPENABLE_SHIFT; - dc.window = ((tb->flags & XTENSA_TBFLAG_WINDOW_MASK) >> + dc->window = ((tb_flags & XTENSA_TBFLAG_WINDOW_MASK) >> XTENSA_TBFLAG_WINDOW_SHIFT); - if (dc.config->isa) { - dc.insnbuf = xtensa_insnbuf_alloc(dc.config->isa); - dc.slotbuf = xtensa_insnbuf_alloc(dc.config->isa); + if (dc->config->isa) { + dc->insnbuf = xtensa_insnbuf_alloc(dc->config->isa); + dc->slotbuf = xtensa_insnbuf_alloc(dc->config->isa); + } + init_sar_tracker(dc); +} + +static void xtensa_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + if (dc->icount) { + dc->next_icount = tcg_temp_local_new_i32(); + } +} + +static void xtensa_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + tcg_gen_insn_start(dcbase->pc_next); +} + +static bool xtensa_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); + gen_exception(dc, EXCP_DEBUG); + dc->base.is_jmp = DISAS_NORETURN; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->base.pc_next += 2; + return true; +} + +static void xtensa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUXtensaState *env = cpu->env_ptr; + target_ulong page_start; + + /* These two conditions only apply to the first insn in the TB, + but this is the first TranslateOps hook that allows exiting. */ + if ((tb_cflags(dc->base.tb) & CF_USE_ICOUNT) + && (dc->base.tb->flags & XTENSA_TBFLAG_YIELD)) { + gen_exception(dc, EXCP_YIELD); + dc->base.is_jmp = DISAS_NORETURN; + return; + } + if (dc->base.tb->flags & XTENSA_TBFLAG_EXCEPTION) { + gen_exception(dc, EXCP_DEBUG); + dc->base.is_jmp = DISAS_NORETURN; + return; } - init_sar_tracker(&dc); - if (dc.icount) { - dc.next_icount = tcg_temp_local_new_i32(); - } + if (dc->icount) { + TCGLabel *label = gen_new_label(); - gen_tb_start(tb); - - if ((tb_cflags(tb) & CF_USE_ICOUNT) && - (tb->flags & XTENSA_TBFLAG_YIELD)) { - tcg_gen_insn_start(dc.pc); - ++insn_count; - gen_exception(&dc, EXCP_YIELD); - dc.is_jmp = DISAS_UPDATE; - goto done; - } - if (tb->flags & XTENSA_TBFLAG_EXCEPTION) { - tcg_gen_insn_start(dc.pc); - ++insn_count; - gen_exception(&dc, EXCP_DEBUG); - dc.is_jmp = DISAS_UPDATE; - goto done; - } - - do { - tcg_gen_insn_start(dc.pc); - ++insn_count; - - if (unlikely(cpu_breakpoint_test(cs, dc.pc, BP_ANY))) { - tcg_gen_movi_i32(cpu_pc, dc.pc); - gen_exception(&dc, EXCP_DEBUG); - dc.is_jmp = DISAS_UPDATE; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the logic setting tb->size below does the right thing. */ - dc.pc += 2; - break; + tcg_gen_addi_i32(dc->next_icount, cpu_SR[ICOUNT], 1); + tcg_gen_brcondi_i32(TCG_COND_NE, dc->next_icount, 0, label); + tcg_gen_mov_i32(dc->next_icount, cpu_SR[ICOUNT]); + if (dc->debug) { + gen_debug_exception(dc, DEBUGCAUSE_IC); } - - if (insn_count == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } - - if (dc.icount) { - TCGLabel *label = gen_new_label(); - - tcg_gen_addi_i32(dc.next_icount, cpu_SR[ICOUNT], 1); - tcg_gen_brcondi_i32(TCG_COND_NE, dc.next_icount, 0, label); - tcg_gen_mov_i32(dc.next_icount, cpu_SR[ICOUNT]); - if (dc.debug) { - gen_debug_exception(&dc, DEBUGCAUSE_IC); - } - gen_set_label(label); - } - - if (dc.debug) { - gen_ibreak_check(env, &dc); - } - - disas_xtensa_insn(env, &dc); - if (dc.icount) { - tcg_gen_mov_i32(cpu_SR[ICOUNT], dc.next_icount); - } - if (cs->singlestep_enabled) { - tcg_gen_movi_i32(cpu_pc, dc.pc); - gen_exception(&dc, EXCP_DEBUG); - break; - } - } while (dc.is_jmp == DISAS_NEXT && - insn_count < max_insns && - dc.pc < next_page_start && - dc.pc + xtensa_insn_len(env, &dc) <= next_page_start && - !tcg_op_buf_full()); -done: - reset_sar_tracker(&dc); - if (dc.icount) { - tcg_temp_free(dc.next_icount); - } - if (dc.config->isa) { - xtensa_insnbuf_free(dc.config->isa, dc.insnbuf); - xtensa_insnbuf_free(dc.config->isa, dc.slotbuf); + gen_set_label(label); } - if (tb_cflags(tb) & CF_LAST_IO) { - gen_io_end(); + if (dc->debug) { + gen_ibreak_check(env, dc); } - if (dc.is_jmp == DISAS_NEXT) { - gen_jumpi(&dc, dc.pc, 0); - } - gen_tb_end(tb, insn_count); + disas_xtensa_insn(env, dc); -#ifdef DEBUG_DISAS - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - qemu_log_lock(); - qemu_log("----------------\n"); - qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(cs, pc_start, dc.pc - pc_start); - qemu_log("\n"); - qemu_log_unlock(); + if (dc->icount) { + tcg_gen_mov_i32(cpu_SR[ICOUNT], dc->next_icount); } -#endif - tb->size = dc.pc - pc_start; - tb->icount = insn_count; + + /* End the TB if the next insn will cross into the next page. */ + page_start = dc->base.pc_first & TARGET_PAGE_MASK; + if (dc->base.is_jmp == DISAS_NEXT && + (dc->pc - page_start >= TARGET_PAGE_SIZE || + dc->pc - page_start + xtensa_insn_len(env, dc) > TARGET_PAGE_SIZE)) { + dc->base.is_jmp = DISAS_TOO_MANY; + } +} + +static void xtensa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + reset_sar_tracker(dc); + if (dc->config->isa) { + xtensa_insnbuf_free(dc->config->isa, dc->insnbuf); + xtensa_insnbuf_free(dc->config->isa, dc->slotbuf); + } + if (dc->icount) { + tcg_temp_free(dc->next_icount); + } + + switch (dc->base.is_jmp) { + case DISAS_NORETURN: + break; + case DISAS_TOO_MANY: + if (dc->base.singlestep_enabled) { + tcg_gen_movi_i32(cpu_pc, dc->pc); + gen_exception(dc, EXCP_DEBUG); + } else { + gen_jumpi(dc, dc->pc, 0); + } + break; + default: + g_assert_not_reached(); + } +} + +static void xtensa_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps xtensa_translator_ops = { + .init_disas_context = xtensa_tr_init_disas_context, + .tb_start = xtensa_tr_tb_start, + .insn_start = xtensa_tr_insn_start, + .breakpoint_check = xtensa_tr_breakpoint_check, + .translate_insn = xtensa_tr_translate_insn, + .tb_stop = xtensa_tr_tb_stop, + .disas_log = xtensa_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb) +{ + DisasContext dc = {}; + translator_loop(&xtensa_translator_ops, &dc.base, cpu, tb); } void xtensa_cpu_dump_state(CPUState *cs, FILE *f, @@ -1244,7 +1259,8 @@ void xtensa_cpu_dump_state(CPUState *cs, FILE *f, } } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_FP_COPROCESSOR)) { + if ((flags & CPU_DUMP_FPU) && + xtensa_option_enabled(env->config, XTENSA_OPTION_FP_COPROCESSOR)) { cpu_fprintf(f, "\n"); for (i = 0; i < 16; ++i) { @@ -1272,11 +1288,8 @@ XtensaOpcodeOps * xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t, const char *name) { - XtensaOpcodeOps *ops; - - ops = bsearch(name, t->opcode, t->num_opcodes, - sizeof(XtensaOpcodeOps), compare_opcode_ops); - return ops; + return bsearch(name, t->opcode, t->num_opcodes, + sizeof(XtensaOpcodeOps), compare_opcode_ops); } static void translate_abs(DisasContext *dc, const uint32_t arg[], @@ -1484,7 +1497,7 @@ static void translate_break(DisasContext *dc, const uint32_t arg[], static void translate_call0(DisasContext *dc, const uint32_t arg[], const uint32_t par[]) { - tcg_gen_movi_i32(cpu_R[0], dc->next_pc); + tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next); gen_jumpi(dc, arg[0], 0); } @@ -1502,7 +1515,7 @@ static void translate_callx0(DisasContext *dc, const uint32_t arg[], if (gen_window_check1(dc, arg[0])) { TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_mov_i32(tmp, cpu_R[arg[0]]); - tcg_gen_movi_i32(cpu_R[0], dc->next_pc); + tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next); gen_jump(dc, tmp); tcg_temp_free(tmp); } @@ -1527,10 +1540,8 @@ static void translate_clamps(DisasContext *dc, const uint32_t arg[], TCGv_i32 tmp1 = tcg_const_i32(-1u << arg[2]); TCGv_i32 tmp2 = tcg_const_i32((1 << arg[2]) - 1); - tcg_gen_movcond_i32(TCG_COND_GT, tmp1, - cpu_R[arg[1]], tmp1, cpu_R[arg[1]], tmp1); - tcg_gen_movcond_i32(TCG_COND_LT, cpu_R[arg[0]], - tmp1, tmp2, tmp1, tmp2); + tcg_gen_smax_i32(tmp1, tmp1, cpu_R[arg[1]]); + tcg_gen_smin_i32(cpu_R[arg[0]], tmp1, tmp2); tcg_temp_free(tmp1); tcg_temp_free(tmp2); } @@ -1704,7 +1715,7 @@ static void translate_l32r(DisasContext *dc, const uint32_t arg[], if (gen_window_check1(dc, arg[0])) { TCGv_i32 tmp; - if (dc->tb->flags & XTENSA_TBFLAG_LITBASE) { + if (dc->base.tb->flags & XTENSA_TBFLAG_LITBASE) { tmp = tcg_const_i32(dc->raw_arg[1] - 1); tcg_gen_add_i32(tmp, cpu_SR[LITBASE], tmp); } else { @@ -1723,7 +1734,7 @@ static void translate_loop(DisasContext *dc, const uint32_t arg[], TCGv_i32 tmp = tcg_const_i32(lend); tcg_gen_subi_i32(cpu_SR[LCOUNT], cpu_R[arg[0]], 1); - tcg_gen_movi_i32(cpu_SR[LBEG], dc->next_pc); + tcg_gen_movi_i32(cpu_SR[LBEG], dc->base.pc_next); gen_helper_wsr_lend(cpu_env, tmp); tcg_temp_free(tmp); @@ -1734,7 +1745,7 @@ static void translate_loop(DisasContext *dc, const uint32_t arg[], gen_set_label(label); } - gen_jumpi(dc, dc->next_pc, 0); + gen_jumpi(dc, dc->base.pc_next, 0); } } @@ -1855,13 +1866,35 @@ static void translate_memw(DisasContext *dc, const uint32_t arg[], tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); } -static void translate_minmax(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) +static void translate_smin(DisasContext *dc, const uint32_t arg[], + const uint32_t par[]) { if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_movcond_i32(par[0], cpu_R[arg[0]], - cpu_R[arg[1]], cpu_R[arg[2]], - cpu_R[arg[1]], cpu_R[arg[2]]); + tcg_gen_smin_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); + } +} + +static void translate_umin(DisasContext *dc, const uint32_t arg[], + const uint32_t par[]) +{ + if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { + tcg_gen_umin_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); + } +} + +static void translate_smax(DisasContext *dc, const uint32_t arg[], + const uint32_t par[]) +{ + if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { + tcg_gen_smax_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); + } +} + +static void translate_umax(DisasContext *dc, const uint32_t arg[], + const uint32_t par[]) +{ + if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { + tcg_gen_umax_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); } } @@ -2217,7 +2250,7 @@ static void translate_rur(DisasContext *dc, const uint32_t arg[], if (uregnames[par[0]].name) { tcg_gen_mov_i32(cpu_R[arg[0]], cpu_UR[par[0]]); } else { - qemu_log_mask(LOG_UNIMP, "RUR %d not implemented, ", par[0]); + qemu_log_mask(LOG_UNIMP, "RUR %d not implemented\n", par[0]); } } } @@ -2358,7 +2391,7 @@ static void translate_slli(DisasContext *dc, const uint32_t arg[], { if (gen_window_check2(dc, arg[0], arg[1])) { if (arg[2] == 32) { - qemu_log_mask(LOG_GUEST_ERROR, "slli a%d, a%d, 32 is undefined", + qemu_log_mask(LOG_GUEST_ERROR, "slli a%d, a%d, 32 is undefined\n", arg[0], arg[1]); } tcg_gen_shli_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2] & 0x1f); @@ -2554,7 +2587,7 @@ static void translate_wur(DisasContext *dc, const uint32_t arg[], if (uregnames[par[0]].name) { gen_wur(par[0], cpu_R[arg[0]]); } else { - qemu_log_mask(LOG_UNIMP, "WUR %d not implemented, ", par[0]); + qemu_log_mask(LOG_UNIMP, "WUR %d not implemented\n", par[0]); } } } @@ -2984,23 +3017,19 @@ static const XtensaOpcodeOps core_ops[] = { .par = (const uint32_t[]){TCG_COND_NE}, }, { .name = "max", - .translate = translate_minmax, - .par = (const uint32_t[]){TCG_COND_GE}, + .translate = translate_smax, }, { .name = "maxu", - .translate = translate_minmax, - .par = (const uint32_t[]){TCG_COND_GEU}, + .translate = translate_umax, }, { .name = "memw", .translate = translate_memw, }, { .name = "min", - .translate = translate_minmax, - .par = (const uint32_t[]){TCG_COND_LT}, + .translate = translate_smin, }, { .name = "minu", - .translate = translate_minmax, - .par = (const uint32_t[]){TCG_COND_LTU}, + .translate = translate_umin, }, { .name = "mov", .translate = translate_mov, diff --git a/tcg/README b/tcg/README index bb2ea5121b..d22ee084b8 100644 --- a/tcg/README +++ b/tcg/README @@ -431,6 +431,14 @@ double-word product T0. The later is returned in two single-word outputs. Similar to mulu2, except the two inputs T1 and T2 are signed. +* mulsh_i32/i64 t0, t1, t2 +* muluh_i32/i64 t0, t1, t2 + +Provide the high part of a signed or unsigned multiply, respectively. +If mulu2/muls2 are not provided by the backend, the tcg-op generator +can obtain the same results can be obtained by emitting a pair of +opcodes, mul+muluh/mulsh. + ********* Memory Barrier support * mb <$arg> @@ -553,7 +561,7 @@ E.g. VECL=1 -> 64 << 1 -> v128, and VECE=2 -> 1 << 2 -> i32. * orc_vec v0, v1, v2 * not_vec v0, v1 - Similarly, logical operations with and without compliment. + Similarly, logical operations with and without complement. Note that VECE is unused. * shli_vec v0, v1, i2 diff --git a/tcg/aarch64/tcg-target.inc.c b/tcg/aarch64/tcg-target.inc.c index be3192078d..083592a4d7 100644 --- a/tcg/aarch64/tcg-target.inc.c +++ b/tcg/aarch64/tcg-target.inc.c @@ -1733,7 +1733,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_insn(s, 3305, LDR, offset, TCG_REG_TMP); } tcg_out_insn(s, 3207, BR, TCG_REG_TMP); - s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); break; case INDEX_op_goto_ptr: @@ -2219,7 +2219,6 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) switch (opc) { case INDEX_op_add_vec: case INDEX_op_sub_vec: - case INDEX_op_mul_vec: case INDEX_op_and_vec: case INDEX_op_or_vec: case INDEX_op_xor_vec: @@ -2232,6 +2231,8 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_shri_vec: case INDEX_op_sari_vec: return 1; + case INDEX_op_mul_vec: + return vece < MO_64; default: return 0; diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c index dc83f3e5be..e1fbf465cb 100644 --- a/tcg/arm/tcg-target.inc.c +++ b/tcg/arm/tcg-target.inc.c @@ -159,8 +159,8 @@ typedef enum { INSN_STRD_IMM = 0x004000f0, INSN_STRD_REG = 0x000000f0, - INSN_DMB_ISH = 0x5bf07ff5, - INSN_DMB_MCR = 0xba0f07ee, + INSN_DMB_ISH = 0xf57ff05b, + INSN_DMB_MCR = 0xee070fba, /* Architected nop introduced in v6k. */ /* ??? This is an MSR (imm) 0,0,0 insn. Anyone know if this @@ -1822,7 +1822,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_movi32(s, COND_AL, base, ptr - dil); } tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, base, dil); - s->tb_jmp_reset_offset[args[0]] = tcg_current_code_size(s); + set_jmp_reset_offset(s, args[0]); } break; case INDEX_op_goto_ptr: diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index d7e59e79c5..a91e4f1313 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -854,11 +854,11 @@ static void tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, switch (vece) { case MO_8: /* ??? With zero in a register, use PSHUFB. */ - tcg_out_vex_modrm(s, OPC_PUNPCKLBW, r, 0, a); + tcg_out_vex_modrm(s, OPC_PUNPCKLBW, r, a, a); a = r; /* FALLTHRU */ case MO_16: - tcg_out_vex_modrm(s, OPC_PUNPCKLWD, r, 0, a); + tcg_out_vex_modrm(s, OPC_PUNPCKLWD, r, a, a); a = r; /* FALLTHRU */ case MO_32: @@ -867,7 +867,7 @@ static void tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, tcg_out8(s, 0); break; case MO_64: - tcg_out_vex_modrm(s, OPC_PUNPCKLQDQ, r, 0, a); + tcg_out_vex_modrm(s, OPC_PUNPCKLQDQ, r, a, a); break; default: g_assert_not_reached(); @@ -2245,7 +2245,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1, (intptr_t)(s->tb_jmp_target_addr + a0)); } - s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); break; case INDEX_op_goto_ptr: /* jmp to the given host address (could be epilogue) */ @@ -3501,7 +3501,10 @@ static void tcg_target_init(TCGContext *s) sure of not hitting invalid opcode. */ if (c & bit_OSXSAVE) { unsigned xcrl, xcrh; - asm ("xgetbv" : "=a" (xcrl), "=d" (xcrh) : "c" (0)); + /* The xgetbv instruction is not available to older versions of + * the assembler, so we encode the instruction manually. + */ + asm(".byte 0x0f, 0x01, 0xd0" : "=a" (xcrl), "=d" (xcrh) : "c" (0)); if ((xcrl & 6) == 6) { have_avx1 = (c & bit_AVX) != 0; have_avx2 = (b7 & bit_AVX2) != 0; @@ -3529,7 +3532,7 @@ static void tcg_target_init(TCGContext *s) tcg_target_available_regs[TCG_TYPE_V256] = ALL_VECTOR_REGS; } - tcg_target_call_clobber_regs = 0; + tcg_target_call_clobber_regs = ALL_VECTOR_REGS; tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EAX); tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EDX); tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_ECX); diff --git a/tcg/mips/tcg-target.inc.c b/tcg/mips/tcg-target.inc.c index ca5f1d4894..cff525373b 100644 --- a/tcg/mips/tcg-target.inc.c +++ b/tcg/mips/tcg-target.inc.c @@ -1744,7 +1744,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_opc_reg(s, OPC_JR, 0, TCG_TMP0, 0); } tcg_out_nop(s); - s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); break; case INDEX_op_goto_ptr: /* jmp to the given host address (could be epilogue) */ diff --git a/tcg/optimize.c b/tcg/optimize.c index d4ea67e541..5dbe11c3c8 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1094,9 +1094,9 @@ void tcg_optimize(TCGContext *s) tmp = arg_info(op->args[1])->val; tmp = dup_const(TCGOP_VECE(op), tmp); tcg_opt_gen_movi(s, op, op->args[0], tmp); - continue; + break; } - break; + goto do_default; CASE_OP_32_64(not): CASE_OP_32_64(neg): diff --git a/tcg/ppc/tcg-target.inc.c b/tcg/ppc/tcg-target.inc.c index 86f7de5f7e..c2f729ee8f 100644 --- a/tcg/ppc/tcg-target.inc.c +++ b/tcg/ppc/tcg-target.inc.c @@ -2025,10 +2025,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, } tcg_out32(s, MTSPR | RS(TCG_REG_TB) | CTR); tcg_out32(s, BCCTR | BO_ALWAYS); - s->tb_jmp_reset_offset[args[0]] = c = tcg_current_code_size(s); + set_jmp_reset_offset(s, args[0]); if (USE_REG_TB) { /* For the unlinked case, need to reset TCG_REG_TB. */ - c = -c; + c = -tcg_current_code_size(s); assert(c == (int16_t)c); tcg_out32(s, ADDI | TAI(TCG_REG_TB, TCG_REG_TB, c)); } diff --git a/tcg/s390/tcg-target.inc.c b/tcg/s390/tcg-target.inc.c index 9af6dcef05..17c435ade5 100644 --- a/tcg/s390/tcg-target.inc.c +++ b/tcg/s390/tcg-target.inc.c @@ -1783,7 +1783,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, /* and go there */ tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_TB); } - s->tb_jmp_reset_offset[a0] = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); /* For the unlinked path of goto_tb, we need to reset TCG_REG_TB to the beginning of this TB. */ diff --git a/tcg/sparc/tcg-target.inc.c b/tcg/sparc/tcg-target.inc.c index bc673bd8c6..04bdc3df5e 100644 --- a/tcg/sparc/tcg-target.inc.c +++ b/tcg/sparc/tcg-target.inc.c @@ -1388,12 +1388,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, tcg_out_arithi(s, TCG_REG_G0, TCG_REG_TB, 0, JMPL); tcg_out_nop(s); } - s->tb_jmp_reset_offset[a0] = c = tcg_current_code_size(s); + set_jmp_reset_offset(s, a0); /* For the unlinked path of goto_tb, we need to reset TCG_REG_TB to the beginning of this TB. */ if (USE_REG_TB) { - c = -c; + c = -tcg_current_code_size(s); if (check_fit_i32(c, 13)) { tcg_out_arithi(s, TCG_REG_TB, TCG_REG_TB, c, ARITH_ADD); } else { diff --git a/tcg/tcg-ldst.inc.c b/tcg/tcg-ldst.inc.c index 0e14cf4357..47f41b921b 100644 --- a/tcg/tcg-ldst.inc.c +++ b/tcg/tcg-ldst.inc.c @@ -30,7 +30,7 @@ typedef struct TCGLabelQemuLdst { TCGReg datahi_reg; /* reg index for high word to be loaded or stored */ tcg_insn_unit *raddr; /* gen code addr of the next IR of qemu_ld/st IR */ tcg_insn_unit *label_ptr[2]; /* label pointers to be updated */ - struct TCGLabelQemuLdst *next; + QSIMPLEQ_ENTRY(TCGLabelQemuLdst) next; } TCGLabelQemuLdst; @@ -46,7 +46,7 @@ static bool tcg_out_ldst_finalize(TCGContext *s) TCGLabelQemuLdst *lb; /* qemu_ld/st slow paths */ - for (lb = s->ldst_labels; lb != NULL; lb = lb->next) { + QSIMPLEQ_FOREACH(lb, &s->ldst_labels, next) { if (lb->is_ld) { tcg_out_qemu_ld_slow_path(s, lb); } else { @@ -72,7 +72,7 @@ static inline TCGLabelQemuLdst *new_ldst_label(TCGContext *s) { TCGLabelQemuLdst *l = tcg_malloc(sizeof(*l)); - l->next = s->ldst_labels; - s->ldst_labels = l; + QSIMPLEQ_INSERT_TAIL(&s->ldst_labels, l, next); + return l; } diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 22db1590d5..61c25f5784 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -287,8 +287,11 @@ void tcg_gen_gvec_4_ptr(uint32_t dofs, uint32_t aofs, uint32_t bofs, in units of LNSZ. This limits the expansion of inline code. */ static inline bool check_size_impl(uint32_t oprsz, uint32_t lnsz) { - uint32_t lnct = oprsz / lnsz; - return lnct >= 1 && lnct <= MAX_UNROLL; + if (oprsz % lnsz == 0) { + uint32_t lnct = oprsz / lnsz; + return lnct >= 1 && lnct <= MAX_UNROLL; + } + return false; } static void expand_clr(uint32_t dofs, uint32_t maxsz); diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index 70ec889bc1..cefba3d185 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "cpu.h" -#include "exec/exec-all.h" #include "tcg.h" #include "tcg-op.h" #include "tcg-mo.h" @@ -355,8 +354,8 @@ void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, TCGType type = rt->base_type; int can; - tcg_debug_assert(at->base_type == type); - tcg_debug_assert(bt->base_type == type); + tcg_debug_assert(at->base_type >= type); + tcg_debug_assert(bt->base_type >= type); can = tcg_can_emit_vec_op(INDEX_op_cmp_vec, type, vece); if (can > 0) { vec_gen_4(INDEX_op_cmp_vec, type, vece, ri, ai, bi, cond); @@ -377,8 +376,8 @@ void tcg_gen_mul_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) TCGType type = rt->base_type; int can; - tcg_debug_assert(at->base_type == type); - tcg_debug_assert(bt->base_type == type); + tcg_debug_assert(at->base_type >= type); + tcg_debug_assert(bt->base_type >= type); can = tcg_can_emit_vec_op(INDEX_op_mul_vec, type, vece); if (can > 0) { vec_gen_3(INDEX_op_mul_vec, type, vece, ri, ai, bi); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 34b96d68f3..daa416a143 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1033,6 +1033,26 @@ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) } } +void tcg_gen_smin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, a, b); +} + +void tcg_gen_umin_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, a, b); +} + +void tcg_gen_smax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_movcond_i32(TCG_COND_LT, ret, a, b, b, a); +} + +void tcg_gen_umax_i32(TCGv_i32 ret, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_movcond_i32(TCG_COND_LTU, ret, a, b, b, a); +} + /* 64-bit ops */ #if TCG_TARGET_REG_BITS == 32 @@ -2438,6 +2458,26 @@ void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) tcg_temp_free_i64(t2); } +void tcg_gen_smin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, a, b); +} + +void tcg_gen_umin_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, a, b); +} + +void tcg_gen_smax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_movcond_i64(TCG_COND_LT, ret, a, b, b, a); +} + +void tcg_gen_umax_i64(TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_movcond_i64(TCG_COND_LTU, ret, a, b, b, a); +} + /* Size changing operations. */ void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg) @@ -2534,10 +2574,30 @@ void tcg_gen_extr32_i64(TCGv_i64 lo, TCGv_i64 hi, TCGv_i64 arg) /* QEMU specific operations. */ +void tcg_gen_exit_tb(TranslationBlock *tb, unsigned idx) +{ + uintptr_t val = (uintptr_t)tb + idx; + + if (tb == NULL) { + tcg_debug_assert(idx == 0); + } else if (idx <= TB_EXIT_IDXMAX) { +#ifdef CONFIG_DEBUG_TCG + /* This is an exit following a goto_tb. Verify that we have + seen this numbered exit before, via tcg_gen_goto_tb. */ + tcg_debug_assert(tcg_ctx->goto_tb_issue_mask & (1 << idx)); +#endif + } else { + /* This is an exit via the exitreq label. */ + tcg_debug_assert(idx == TB_EXIT_REQUESTED); + } + + tcg_gen_op1i(INDEX_op_exit_tb, val); +} + void tcg_gen_goto_tb(unsigned idx) { /* We only support two chained exits. */ - tcg_debug_assert(idx <= 1); + tcg_debug_assert(idx <= TB_EXIT_IDXMAX); #ifdef CONFIG_DEBUG_TCG /* Verify that we havn't seen this numbered exit before. */ tcg_debug_assert((tcg_ctx->goto_tb_issue_mask & (1 << idx)) == 0); @@ -2554,7 +2614,7 @@ void tcg_gen_lookup_and_goto_ptr(void) tcg_gen_op1i(INDEX_op_goto_ptr, tcgv_ptr_arg(ptr)); tcg_temp_free_ptr(ptr); } else { - tcg_gen_exit_tb(0); + tcg_gen_exit_tb(NULL, 0); } } @@ -3011,11 +3071,19 @@ GEN_ATOMIC_HELPER(fetch_add, add, 0) GEN_ATOMIC_HELPER(fetch_and, and, 0) GEN_ATOMIC_HELPER(fetch_or, or, 0) GEN_ATOMIC_HELPER(fetch_xor, xor, 0) +GEN_ATOMIC_HELPER(fetch_smin, smin, 0) +GEN_ATOMIC_HELPER(fetch_umin, umin, 0) +GEN_ATOMIC_HELPER(fetch_smax, smax, 0) +GEN_ATOMIC_HELPER(fetch_umax, umax, 0) GEN_ATOMIC_HELPER(add_fetch, add, 1) GEN_ATOMIC_HELPER(and_fetch, and, 1) GEN_ATOMIC_HELPER(or_fetch, or, 1) GEN_ATOMIC_HELPER(xor_fetch, xor, 1) +GEN_ATOMIC_HELPER(smin_fetch, smin, 1) +GEN_ATOMIC_HELPER(umin_fetch, umin, 1) +GEN_ATOMIC_HELPER(smax_fetch, smax, 1) +GEN_ATOMIC_HELPER(umax_fetch, umax, 1) static void tcg_gen_mov2_i32(TCGv_i32 r, TCGv_i32 a, TCGv_i32 b) { diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 75bb55aeac..7513c1eb7c 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -324,6 +324,10 @@ void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg); void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg); +void tcg_gen_smin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_smax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_umin_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); +void tcg_gen_umax_i32(TCGv_i32, TCGv_i32 arg1, TCGv_i32 arg2); static inline void tcg_gen_discard_i32(TCGv_i32 arg) { @@ -517,6 +521,10 @@ void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg); void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg); void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg); void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg); +void tcg_gen_smin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_smax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_umin_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); +void tcg_gen_umax_i64(TCGv_i64, TCGv_i64 arg1, TCGv_i64 arg2); #if TCG_TARGET_REG_BITS == 64 static inline void tcg_gen_discard_i64(TCGv_i64 arg) @@ -774,10 +782,19 @@ static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, # error "Unhandled number of operands to insn_start" #endif -static inline void tcg_gen_exit_tb(uintptr_t val) -{ - tcg_gen_op1i(INDEX_op_exit_tb, val); -} +/** + * tcg_gen_exit_tb() - output exit_tb TCG operation + * @tb: The TranslationBlock from which we are exiting + * @idx: Direct jump slot index, or exit request + * + * See tcg/README for more info about this TCG operation. + * See also tcg.h and the block comment above TB_EXIT_MASK. + * + * For a normal exit from the TB, back to the main loop, @tb should + * be NULL and @idx should be 0. Otherwise, @tb should be valid and + * @idx should be one of the TB_EXIT_ values. + */ +void tcg_gen_exit_tb(TranslationBlock *tb, unsigned idx); /** * tcg_gen_goto_tb() - output goto_tb TCG operation @@ -890,6 +907,7 @@ void tcg_gen_atomic_cmpxchg_i64(TCGv_i64, TCGv, TCGv_i64, TCGv_i64, void tcg_gen_atomic_xchg_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_xchg_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); + void tcg_gen_atomic_fetch_add_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_add_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_and_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); @@ -898,6 +916,15 @@ void tcg_gen_atomic_fetch_or_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_or_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_xor_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_fetch_xor_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_smin_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_smin_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_umin_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_umin_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_smax_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_smax_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_umax_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_fetch_umax_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); + void tcg_gen_atomic_add_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_add_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_atomic_and_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); @@ -906,6 +933,14 @@ void tcg_gen_atomic_or_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_or_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_atomic_xor_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); void tcg_gen_atomic_xor_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_smin_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_smin_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_umin_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_umin_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_smax_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_smax_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); +void tcg_gen_atomic_umax_fetch_i32(TCGv_i32, TCGv, TCGv_i32, TCGArg, TCGMemOp); +void tcg_gen_atomic_umax_fetch_i64(TCGv_i64, TCGv, TCGv_i64, TCGArg, TCGMemOp); void tcg_gen_mov_vec(TCGv_vec, TCGv_vec); void tcg_gen_dup_i32_vec(unsigned vece, TCGv_vec, TCGv_i32); @@ -1025,16 +1060,28 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_mulu2_tl tcg_gen_mulu2_i64 #define tcg_gen_muls2_tl tcg_gen_muls2_i64 #define tcg_gen_mulsu2_tl tcg_gen_mulsu2_i64 +#define tcg_gen_smin_tl tcg_gen_smin_i64 +#define tcg_gen_umin_tl tcg_gen_umin_i64 +#define tcg_gen_smax_tl tcg_gen_smax_i64 +#define tcg_gen_umax_tl tcg_gen_umax_i64 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i64 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i64 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i64 #define tcg_gen_atomic_fetch_and_tl tcg_gen_atomic_fetch_and_i64 #define tcg_gen_atomic_fetch_or_tl tcg_gen_atomic_fetch_or_i64 #define tcg_gen_atomic_fetch_xor_tl tcg_gen_atomic_fetch_xor_i64 +#define tcg_gen_atomic_fetch_smin_tl tcg_gen_atomic_fetch_smin_i64 +#define tcg_gen_atomic_fetch_umin_tl tcg_gen_atomic_fetch_umin_i64 +#define tcg_gen_atomic_fetch_smax_tl tcg_gen_atomic_fetch_smax_i64 +#define tcg_gen_atomic_fetch_umax_tl tcg_gen_atomic_fetch_umax_i64 #define tcg_gen_atomic_add_fetch_tl tcg_gen_atomic_add_fetch_i64 #define tcg_gen_atomic_and_fetch_tl tcg_gen_atomic_and_fetch_i64 #define tcg_gen_atomic_or_fetch_tl tcg_gen_atomic_or_fetch_i64 #define tcg_gen_atomic_xor_fetch_tl tcg_gen_atomic_xor_fetch_i64 +#define tcg_gen_atomic_smin_fetch_tl tcg_gen_atomic_smin_fetch_i64 +#define tcg_gen_atomic_umin_fetch_tl tcg_gen_atomic_umin_fetch_i64 +#define tcg_gen_atomic_smax_fetch_tl tcg_gen_atomic_smax_fetch_i64 +#define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i64 #define tcg_gen_dup_tl_vec tcg_gen_dup_i64_vec #else #define tcg_gen_movi_tl tcg_gen_movi_i32 @@ -1123,39 +1170,100 @@ void tcg_gen_stl_vec(TCGv_vec r, TCGv_ptr base, TCGArg offset, TCGType t); #define tcg_gen_mulu2_tl tcg_gen_mulu2_i32 #define tcg_gen_muls2_tl tcg_gen_muls2_i32 #define tcg_gen_mulsu2_tl tcg_gen_mulsu2_i32 +#define tcg_gen_smin_tl tcg_gen_smin_i32 +#define tcg_gen_umin_tl tcg_gen_umin_i32 +#define tcg_gen_smax_tl tcg_gen_smax_i32 +#define tcg_gen_umax_tl tcg_gen_umax_i32 #define tcg_gen_atomic_cmpxchg_tl tcg_gen_atomic_cmpxchg_i32 #define tcg_gen_atomic_xchg_tl tcg_gen_atomic_xchg_i32 #define tcg_gen_atomic_fetch_add_tl tcg_gen_atomic_fetch_add_i32 #define tcg_gen_atomic_fetch_and_tl tcg_gen_atomic_fetch_and_i32 #define tcg_gen_atomic_fetch_or_tl tcg_gen_atomic_fetch_or_i32 #define tcg_gen_atomic_fetch_xor_tl tcg_gen_atomic_fetch_xor_i32 +#define tcg_gen_atomic_fetch_smin_tl tcg_gen_atomic_fetch_smin_i32 +#define tcg_gen_atomic_fetch_umin_tl tcg_gen_atomic_fetch_umin_i32 +#define tcg_gen_atomic_fetch_smax_tl tcg_gen_atomic_fetch_smax_i32 +#define tcg_gen_atomic_fetch_umax_tl tcg_gen_atomic_fetch_umax_i32 #define tcg_gen_atomic_add_fetch_tl tcg_gen_atomic_add_fetch_i32 #define tcg_gen_atomic_and_fetch_tl tcg_gen_atomic_and_fetch_i32 #define tcg_gen_atomic_or_fetch_tl tcg_gen_atomic_or_fetch_i32 #define tcg_gen_atomic_xor_fetch_tl tcg_gen_atomic_xor_fetch_i32 +#define tcg_gen_atomic_smin_fetch_tl tcg_gen_atomic_smin_fetch_i32 +#define tcg_gen_atomic_umin_fetch_tl tcg_gen_atomic_umin_fetch_i32 +#define tcg_gen_atomic_smax_fetch_tl tcg_gen_atomic_smax_fetch_i32 +#define tcg_gen_atomic_umax_fetch_tl tcg_gen_atomic_umax_fetch_i32 #define tcg_gen_dup_tl_vec tcg_gen_dup_i32_vec #endif #if UINTPTR_MAX == UINT32_MAX -# define tcg_gen_ld_ptr(R, A, O) \ - tcg_gen_ld_i32(TCGV_PTR_TO_NAT(R), (A), (O)) -# define tcg_gen_discard_ptr(A) \ - tcg_gen_discard_i32(TCGV_PTR_TO_NAT(A)) -# define tcg_gen_add_ptr(R, A, B) \ - tcg_gen_add_i32(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), TCGV_PTR_TO_NAT(B)) -# define tcg_gen_addi_ptr(R, A, B) \ - tcg_gen_addi_i32(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), (B)) -# define tcg_gen_ext_i32_ptr(R, A) \ - tcg_gen_mov_i32(TCGV_PTR_TO_NAT(R), (A)) +# define PTR i32 +# define NAT TCGv_i32 #else -# define tcg_gen_ld_ptr(R, A, O) \ - tcg_gen_ld_i64(TCGV_PTR_TO_NAT(R), (A), (O)) -# define tcg_gen_discard_ptr(A) \ - tcg_gen_discard_i64(TCGV_PTR_TO_NAT(A)) -# define tcg_gen_add_ptr(R, A, B) \ - tcg_gen_add_i64(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), TCGV_PTR_TO_NAT(B)) -# define tcg_gen_addi_ptr(R, A, B) \ - tcg_gen_addi_i64(TCGV_PTR_TO_NAT(R), TCGV_PTR_TO_NAT(A), (B)) -# define tcg_gen_ext_i32_ptr(R, A) \ - tcg_gen_ext_i32_i64(TCGV_PTR_TO_NAT(R), (A)) -#endif /* UINTPTR_MAX == UINT32_MAX */ +# define PTR i64 +# define NAT TCGv_i64 +#endif + +static inline void tcg_gen_ld_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t o) +{ + glue(tcg_gen_ld_,PTR)((NAT)r, a, o); +} + +static inline void tcg_gen_discard_ptr(TCGv_ptr a) +{ + glue(tcg_gen_discard_,PTR)((NAT)a); +} + +static inline void tcg_gen_add_ptr(TCGv_ptr r, TCGv_ptr a, TCGv_ptr b) +{ + glue(tcg_gen_add_,PTR)((NAT)r, (NAT)a, (NAT)b); +} + +static inline void tcg_gen_addi_ptr(TCGv_ptr r, TCGv_ptr a, intptr_t b) +{ + glue(tcg_gen_addi_,PTR)((NAT)r, (NAT)a, b); +} + +static inline void tcg_gen_brcondi_ptr(TCGCond cond, TCGv_ptr a, + intptr_t b, TCGLabel *label) +{ + glue(tcg_gen_brcondi_,PTR)(cond, (NAT)a, b, label); +} + +static inline void tcg_gen_ext_i32_ptr(TCGv_ptr r, TCGv_i32 a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_mov_i32((NAT)r, a); +#else + tcg_gen_ext_i32_i64((NAT)r, a); +#endif +} + +static inline void tcg_gen_trunc_i64_ptr(TCGv_ptr r, TCGv_i64 a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_extrl_i64_i32((NAT)r, a); +#else + tcg_gen_mov_i64((NAT)r, a); +#endif +} + +static inline void tcg_gen_extu_ptr_i64(TCGv_i64 r, TCGv_ptr a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_extu_i32_i64(r, (NAT)a); +#else + tcg_gen_mov_i64(r, (NAT)a); +#endif +} + +static inline void tcg_gen_trunc_ptr_i32(TCGv_i32 r, TCGv_ptr a) +{ +#if UINTPTR_MAX == UINT32_MAX + tcg_gen_mov_i32(r, (NAT)a); +#else + tcg_gen_extrl_i64_i32(r, (NAT)a); +#endif +} + +#undef PTR +#undef NAT diff --git a/tcg/tcg.c b/tcg/tcg.c index bb24526c93..f27b22bd3c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -135,6 +135,12 @@ static TCGContext **tcg_ctxs; static unsigned int n_tcg_ctxs; TCGv_env cpu_env = 0; +struct tcg_region_tree { + QemuMutex lock; + GTree *tree; + /* padding to avoid false sharing is computed at run-time */ +}; + /* * We divide code_gen_buffer into equally-sized "regions" that TCG threads * dynamically allocate from as demand dictates. Given appropriate region @@ -158,6 +164,13 @@ struct tcg_region_state { }; static struct tcg_region_state region; +/* + * This is an array of struct tcg_region_tree's, with padding. + * We use void * to simplify the computation of region_trees[i]; each + * struct is found every tree_size bytes. + */ +static void *region_trees; +static size_t tree_size; static TCGRegSet tcg_target_available_regs[TCG_TYPE_COUNT]; static TCGRegSet tcg_target_call_clobber_regs; @@ -293,8 +306,190 @@ TCGLabel *gen_new_label(void) return l; } +static void set_jmp_reset_offset(TCGContext *s, int which) +{ + size_t off = tcg_current_code_size(s); + s->tb_jmp_reset_offset[which] = off; + /* Make sure that we didn't overflow the stored offset. */ + assert(s->tb_jmp_reset_offset[which] == off); +} + #include "tcg-target.inc.c" +/* compare a pointer @ptr and a tb_tc @s */ +static int ptr_cmp_tb_tc(const void *ptr, const struct tb_tc *s) +{ + if (ptr >= s->ptr + s->size) { + return 1; + } else if (ptr < s->ptr) { + return -1; + } + return 0; +} + +static gint tb_tc_cmp(gconstpointer ap, gconstpointer bp) +{ + const struct tb_tc *a = ap; + const struct tb_tc *b = bp; + + /* + * When both sizes are set, we know this isn't a lookup. + * This is the most likely case: every TB must be inserted; lookups + * are a lot less frequent. + */ + if (likely(a->size && b->size)) { + if (a->ptr > b->ptr) { + return 1; + } else if (a->ptr < b->ptr) { + return -1; + } + /* a->ptr == b->ptr should happen only on deletions */ + g_assert(a->size == b->size); + return 0; + } + /* + * All lookups have either .size field set to 0. + * From the glib sources we see that @ap is always the lookup key. However + * the docs provide no guarantee, so we just mark this case as likely. + */ + if (likely(a->size == 0)) { + return ptr_cmp_tb_tc(a->ptr, b); + } + return ptr_cmp_tb_tc(b->ptr, a); +} + +static void tcg_region_trees_init(void) +{ + size_t i; + + tree_size = ROUND_UP(sizeof(struct tcg_region_tree), qemu_dcache_linesize); + region_trees = qemu_memalign(qemu_dcache_linesize, region.n * tree_size); + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + qemu_mutex_init(&rt->lock); + rt->tree = g_tree_new(tb_tc_cmp); + } +} + +static struct tcg_region_tree *tc_ptr_to_region_tree(void *p) +{ + size_t region_idx; + + if (p < region.start_aligned) { + region_idx = 0; + } else { + ptrdiff_t offset = p - region.start_aligned; + + if (offset > region.stride * (region.n - 1)) { + region_idx = region.n - 1; + } else { + region_idx = offset / region.stride; + } + } + return region_trees + region_idx * tree_size; +} + +void tcg_tb_insert(TranslationBlock *tb) +{ + struct tcg_region_tree *rt = tc_ptr_to_region_tree(tb->tc.ptr); + + qemu_mutex_lock(&rt->lock); + g_tree_insert(rt->tree, &tb->tc, tb); + qemu_mutex_unlock(&rt->lock); +} + +void tcg_tb_remove(TranslationBlock *tb) +{ + struct tcg_region_tree *rt = tc_ptr_to_region_tree(tb->tc.ptr); + + qemu_mutex_lock(&rt->lock); + g_tree_remove(rt->tree, &tb->tc); + qemu_mutex_unlock(&rt->lock); +} + +/* + * Find the TB 'tb' such that + * tb->tc.ptr <= tc_ptr < tb->tc.ptr + tb->tc.size + * Return NULL if not found. + */ +TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr) +{ + struct tcg_region_tree *rt = tc_ptr_to_region_tree((void *)tc_ptr); + TranslationBlock *tb; + struct tb_tc s = { .ptr = (void *)tc_ptr }; + + qemu_mutex_lock(&rt->lock); + tb = g_tree_lookup(rt->tree, &s); + qemu_mutex_unlock(&rt->lock); + return tb; +} + +static void tcg_region_tree_lock_all(void) +{ + size_t i; + + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + qemu_mutex_lock(&rt->lock); + } +} + +static void tcg_region_tree_unlock_all(void) +{ + size_t i; + + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + qemu_mutex_unlock(&rt->lock); + } +} + +void tcg_tb_foreach(GTraverseFunc func, gpointer user_data) +{ + size_t i; + + tcg_region_tree_lock_all(); + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + g_tree_foreach(rt->tree, func, user_data); + } + tcg_region_tree_unlock_all(); +} + +size_t tcg_nb_tbs(void) +{ + size_t nb_tbs = 0; + size_t i; + + tcg_region_tree_lock_all(); + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + nb_tbs += g_tree_nnodes(rt->tree); + } + tcg_region_tree_unlock_all(); + return nb_tbs; +} + +static void tcg_region_tree_reset_all(void) +{ + size_t i; + + tcg_region_tree_lock_all(); + for (i = 0; i < region.n; i++) { + struct tcg_region_tree *rt = region_trees + i * tree_size; + + /* Increment the refcount first so that destroy acts as a reset */ + g_tree_ref(rt->tree); + g_tree_destroy(rt->tree); + } + tcg_region_tree_unlock_all(); +} + static void tcg_region_bounds(size_t curr_region, void **pstart, void **pend) { void *start, *end; @@ -380,6 +575,8 @@ void tcg_region_reset_all(void) g_assert(!err); } qemu_mutex_unlock(®ion.lock); + + tcg_region_tree_reset_all(); } #ifdef CONFIG_USER_ONLY @@ -496,6 +693,8 @@ void tcg_region_init(void) g_assert(!rc); } + tcg_region_trees_init(); + /* In user-mode we support only one ctx, so do the initial allocation now */ #ifdef CONFIG_USER_ONLY { @@ -600,6 +799,20 @@ size_t tcg_code_capacity(void) return capacity; } +size_t tcg_tb_phys_invalidate_count(void) +{ + unsigned int n_ctxs = atomic_read(&n_tcg_ctxs); + unsigned int i; + size_t total = 0; + + for (i = 0; i < n_ctxs; i++) { + const TCGContext *s = atomic_read(&tcg_ctxs[i]); + + total += atomic_read(&s->tb_phys_invalidate_count); + } + return total; +} + /* pool based memory allocation */ void *tcg_malloc_internal(TCGContext *s, int size) { @@ -866,6 +1079,7 @@ void tcg_func_start(TCGContext *s) /* No temps have been previously allocated for size or locality. */ memset(s->free_temps, 0, sizeof(s->free_temps)); + s->nb_ops = 0; s->nb_labels = 0; s->current_frame_offset = s->frame_start; @@ -980,7 +1194,7 @@ TCGTemp *tcg_global_mem_new_internal(TCGType type, TCGv_ptr base, return ts; } -static TCGTemp *tcg_temp_new_internal(TCGType type, int temp_local) +TCGTemp *tcg_temp_new_internal(TCGType type, bool temp_local) { TCGContext *s = tcg_ctx; TCGTemp *ts; @@ -1025,18 +1239,6 @@ static TCGTemp *tcg_temp_new_internal(TCGType type, int temp_local) return ts; } -TCGv_i32 tcg_temp_new_internal_i32(int temp_local) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, temp_local); - return temp_tcgv_i32(t); -} - -TCGv_i64 tcg_temp_new_internal_i64(int temp_local) -{ - TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, temp_local); - return temp_tcgv_i64(t); -} - TCGv_vec tcg_temp_new_vec(TCGType type) { TCGTemp *t; @@ -1072,7 +1274,7 @@ TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match) return temp_tcgv_vec(t); } -static void tcg_temp_free_internal(TCGTemp *ts) +void tcg_temp_free_internal(TCGTemp *ts) { TCGContext *s = tcg_ctx; int k, idx; @@ -1093,21 +1295,6 @@ static void tcg_temp_free_internal(TCGTemp *ts) set_bit(idx, s->free_temps[k].l); } -void tcg_temp_free_i32(TCGv_i32 arg) -{ - tcg_temp_free_internal(tcgv_i32_temp(arg)); -} - -void tcg_temp_free_i64(TCGv_i64 arg) -{ - tcg_temp_free_internal(tcgv_i64_temp(arg)); -} - -void tcg_temp_free_vec(TCGv_vec arg) -{ - tcg_temp_free_internal(tcgv_vec_temp(arg)); -} - TCGv_i32 tcg_const_i32(int32_t val) { TCGv_i32 t0; @@ -1983,6 +2170,7 @@ void tcg_op_remove(TCGContext *s, TCGOp *op) { QTAILQ_REMOVE(&s->ops, op, link); QTAILQ_INSERT_TAIL(&s->free_ops, op, link); + s->nb_ops--; #ifdef CONFIG_PROFILER atomic_set(&s->prof.del_op_count, s->prof.del_op_count + 1); @@ -2002,6 +2190,7 @@ static TCGOp *tcg_op_alloc(TCGOpcode opc) } memset(op, 0, offsetof(TCGOp, link)); op->opc = opc; + s->nb_ops++; return op; } @@ -3324,7 +3513,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) s->code_ptr = tb->tc.ptr; #ifdef TCG_TARGET_NEED_LDST_LABELS - s->ldst_labels = NULL; + QSIMPLEQ_INIT(&s->ldst_labels); #endif #ifdef TCG_TARGET_NEED_POOL_LABELS s->pool_labels = NULL; @@ -3351,7 +3540,10 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) break; case INDEX_op_insn_start: if (num_insns >= 0) { - s->gen_insn_end_off[num_insns] = tcg_current_code_size(s); + size_t off = tcg_current_code_size(s); + s->gen_insn_end_off[num_insns] = off; + /* Assert that we do not overflow our stored offset. */ + assert(s->gen_insn_end_off[num_insns] == off); } num_insns++; for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { diff --git a/tcg/tcg.h b/tcg/tcg.h index 30896ca304..f9f12378e9 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -655,6 +655,7 @@ struct TCGContext { int nb_globals; int nb_temps; int nb_indirects; + int nb_ops; /* goto_tb support */ tcg_insn_unit *code_buf; @@ -694,12 +695,14 @@ struct TCGContext { /* Threshold to flush the translated code buffer. */ void *code_gen_highwater; + size_t tb_phys_invalidate_count; + /* Track which vCPU triggers events */ CPUState *cpu; /* *_trans */ /* These structures are private to tcg-target.inc.c. */ #ifdef TCG_TARGET_NEED_LDST_LABELS - struct TCGLabelQemuLdst *ldst_labels; + QSIMPLEQ_HEAD(ldst_labels, TCGLabelQemuLdst) ldst_labels; #endif #ifdef TCG_TARGET_NEED_POOL_LABELS struct TCGLabelPoolData *pool_labels; @@ -844,12 +847,19 @@ static inline TCGOp *tcg_last_op(void) /* Test for whether to terminate the TB for using too many opcodes. */ static inline bool tcg_op_buf_full(void) { - return false; + /* This is not a hard limit, it merely stops translation when + * we have produced "enough" opcodes. We want to limit TB size + * such that a RISC host can reasonably use a 16-bit signed + * branch within the TB. We also need to be mindful of the + * 16-bit unsigned offsets, TranslationBlock.jmp_reset_offset[] + * and TCGContext.gen_insn_end_off[]. + */ + return tcg_ctx->nb_ops >= 4000; } /* pool based memory allocation */ -/* user-mode: tb_lock must be held for tcg_malloc_internal. */ +/* user-mode: mmap_lock must be held for tcg_malloc_internal. */ void *tcg_malloc_internal(TCGContext *s, int size); void tcg_pool_reset(TCGContext *s); TranslationBlock *tcg_tb_alloc(TCGContext *s); @@ -860,7 +870,14 @@ void tcg_region_reset_all(void); size_t tcg_code_size(void); size_t tcg_code_capacity(void); -/* user-mode: Called with tb_lock held. */ +void tcg_tb_insert(TranslationBlock *tb); +void tcg_tb_remove(TranslationBlock *tb); +size_t tcg_tb_phys_invalidate_count(void); +TranslationBlock *tcg_tb_lookup(uintptr_t tc_ptr); +void tcg_tb_foreach(GTraverseFunc func, gpointer user_data); +size_t tcg_nb_tbs(void); + +/* user-mode: Called with mmap_lock held. */ static inline void *tcg_malloc(int size) { TCGContext *s = tcg_ctx; @@ -890,15 +907,30 @@ void tcg_set_frame(TCGContext *s, TCGReg reg, intptr_t start, intptr_t size); TCGTemp *tcg_global_mem_new_internal(TCGType, TCGv_ptr, intptr_t, const char *); - -TCGv_i32 tcg_temp_new_internal_i32(int temp_local); -TCGv_i64 tcg_temp_new_internal_i64(int temp_local); +TCGTemp *tcg_temp_new_internal(TCGType, bool); +void tcg_temp_free_internal(TCGTemp *); TCGv_vec tcg_temp_new_vec(TCGType type); TCGv_vec tcg_temp_new_vec_matching(TCGv_vec match); -void tcg_temp_free_i32(TCGv_i32 arg); -void tcg_temp_free_i64(TCGv_i64 arg); -void tcg_temp_free_vec(TCGv_vec arg); +static inline void tcg_temp_free_i32(TCGv_i32 arg) +{ + tcg_temp_free_internal(tcgv_i32_temp(arg)); +} + +static inline void tcg_temp_free_i64(TCGv_i64 arg) +{ + tcg_temp_free_internal(tcgv_i64_temp(arg)); +} + +static inline void tcg_temp_free_ptr(TCGv_ptr arg) +{ + tcg_temp_free_internal(tcgv_ptr_temp(arg)); +} + +static inline void tcg_temp_free_vec(TCGv_vec arg) +{ + tcg_temp_free_internal(tcgv_vec_temp(arg)); +} static inline TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t offset, const char *name) @@ -909,12 +941,14 @@ static inline TCGv_i32 tcg_global_mem_new_i32(TCGv_ptr reg, intptr_t offset, static inline TCGv_i32 tcg_temp_new_i32(void) { - return tcg_temp_new_internal_i32(0); + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, false); + return temp_tcgv_i32(t); } static inline TCGv_i32 tcg_temp_local_new_i32(void) { - return tcg_temp_new_internal_i32(1); + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I32, true); + return temp_tcgv_i32(t); } static inline TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t offset, @@ -926,12 +960,33 @@ static inline TCGv_i64 tcg_global_mem_new_i64(TCGv_ptr reg, intptr_t offset, static inline TCGv_i64 tcg_temp_new_i64(void) { - return tcg_temp_new_internal_i64(0); + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, false); + return temp_tcgv_i64(t); } static inline TCGv_i64 tcg_temp_local_new_i64(void) { - return tcg_temp_new_internal_i64(1); + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_I64, true); + return temp_tcgv_i64(t); +} + +static inline TCGv_ptr tcg_global_mem_new_ptr(TCGv_ptr reg, intptr_t offset, + const char *name) +{ + TCGTemp *t = tcg_global_mem_new_internal(TCG_TYPE_PTR, reg, offset, name); + return temp_tcgv_ptr(t); +} + +static inline TCGv_ptr tcg_temp_new_ptr(void) +{ + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_PTR, false); + return temp_tcgv_ptr(t); +} + +static inline TCGv_ptr tcg_temp_local_new_ptr(void) +{ + TCGTemp *t = tcg_temp_new_internal(TCG_TYPE_PTR, true); + return temp_tcgv_ptr(t); } #if defined(CONFIG_DEBUG_TCG) @@ -1009,26 +1064,6 @@ do {\ abort();\ } while (0) -#if UINTPTR_MAX == UINT32_MAX -static inline TCGv_ptr TCGV_NAT_TO_PTR(TCGv_i32 n) { return (TCGv_ptr)n; } -static inline TCGv_i32 TCGV_PTR_TO_NAT(TCGv_ptr n) { return (TCGv_i32)n; } - -#define tcg_const_ptr(V) TCGV_NAT_TO_PTR(tcg_const_i32((intptr_t)(V))) -#define tcg_global_mem_new_ptr(R, O, N) \ - TCGV_NAT_TO_PTR(tcg_global_mem_new_i32((R), (O), (N))) -#define tcg_temp_new_ptr() TCGV_NAT_TO_PTR(tcg_temp_new_i32()) -#define tcg_temp_free_ptr(T) tcg_temp_free_i32(TCGV_PTR_TO_NAT(T)) -#else -static inline TCGv_ptr TCGV_NAT_TO_PTR(TCGv_i64 n) { return (TCGv_ptr)n; } -static inline TCGv_i64 TCGV_PTR_TO_NAT(TCGv_ptr n) { return (TCGv_i64)n; } - -#define tcg_const_ptr(V) TCGV_NAT_TO_PTR(tcg_const_i64((intptr_t)(V))) -#define tcg_global_mem_new_ptr(R, O, N) \ - TCGV_NAT_TO_PTR(tcg_global_mem_new_i64((R), (O), (N))) -#define tcg_temp_new_ptr() TCGV_NAT_TO_PTR(tcg_temp_new_i64()) -#define tcg_temp_free_ptr(T) tcg_temp_free_i64(TCGV_PTR_TO_NAT(T)) -#endif - bool tcg_op_supported(TCGOpcode op); void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args); @@ -1052,6 +1087,14 @@ TCGv_vec tcg_const_ones_vec(TCGType); TCGv_vec tcg_const_zeros_vec_matching(TCGv_vec); TCGv_vec tcg_const_ones_vec_matching(TCGv_vec); +#if UINTPTR_MAX == UINT32_MAX +# define tcg_const_ptr(x) ((TCGv_ptr)tcg_const_i32((intptr_t)(x))) +# define tcg_const_local_ptr(x) ((TCGv_ptr)tcg_const_local_i32((intptr_t)(x))) +#else +# define tcg_const_ptr(x) ((TCGv_ptr)tcg_const_i64((intptr_t)(x))) +# define tcg_const_local_ptr(x) ((TCGv_ptr)tcg_const_local_i64((intptr_t)(x))) +#endif + TCGLabel *gen_new_label(void); /** @@ -1207,9 +1250,10 @@ static inline unsigned get_mmuidx(TCGMemOpIdx oi) * to this default (which just calls the prologue.code emitted by * tcg_target_qemu_prologue()). */ -#define TB_EXIT_MASK 3 -#define TB_EXIT_IDX0 0 -#define TB_EXIT_IDX1 1 +#define TB_EXIT_MASK 3 +#define TB_EXIT_IDX0 0 +#define TB_EXIT_IDX1 1 +#define TB_EXIT_IDXMAX 1 #define TB_EXIT_REQUESTED 3 #ifdef HAVE_TCG_QEMU_TB_EXEC @@ -1389,12 +1433,20 @@ GEN_ATOMIC_HELPER_ALL(fetch_sub) GEN_ATOMIC_HELPER_ALL(fetch_and) GEN_ATOMIC_HELPER_ALL(fetch_or) GEN_ATOMIC_HELPER_ALL(fetch_xor) +GEN_ATOMIC_HELPER_ALL(fetch_smin) +GEN_ATOMIC_HELPER_ALL(fetch_umin) +GEN_ATOMIC_HELPER_ALL(fetch_smax) +GEN_ATOMIC_HELPER_ALL(fetch_umax) GEN_ATOMIC_HELPER_ALL(add_fetch) GEN_ATOMIC_HELPER_ALL(sub_fetch) GEN_ATOMIC_HELPER_ALL(and_fetch) GEN_ATOMIC_HELPER_ALL(or_fetch) GEN_ATOMIC_HELPER_ALL(xor_fetch) +GEN_ATOMIC_HELPER_ALL(smin_fetch) +GEN_ATOMIC_HELPER_ALL(umin_fetch) +GEN_ATOMIC_HELPER_ALL(smax_fetch) +GEN_ATOMIC_HELPER_ALL(umax_fetch) GEN_ATOMIC_HELPER_ALL(xchg) diff --git a/tcg/tci/tcg-target.inc.c b/tcg/tci/tcg-target.inc.c index cc949bea85..62ed097254 100644 --- a/tcg/tci/tcg-target.inc.c +++ b/tcg/tci/tcg-target.inc.c @@ -574,7 +574,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, /* Indirect jump method. */ TODO(); } - s->tb_jmp_reset_offset[args[0]] = tcg_current_code_size(s); + set_jmp_reset_offset(s, args[0]); break; case INDEX_op_br: tci_out_label(s, arg_label(args[0])); diff --git a/tests/.gitignore b/tests/.gitignore index fb62d2299b..72c18aaab0 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -2,100 +2,19 @@ atomic_add-bench benchmark-crypto-cipher benchmark-crypto-hash benchmark-crypto-hmac -check-qdict -check-qnum -check-qjson -check-qlist -check-qlit -check-qnull -check-qobject -check-qstring -check-qom-interface -check-qom-proplist +check-* +!check-*.c +!check-*.sh qht-bench rcutorture -test-aio -test-aio-multithread -test-arm-mptimer -test-base64 -test-bdrv-drain -test-bitops -test-bitcnt -test-blockjob -test-blockjob-txn -test-bufferiszero -test-char -test-clone-visitor -test-coroutine -test-crypto-afsplit -test-crypto-block -test-crypto-cipher -test-crypto-hash -test-crypto-hmac -test-crypto-ivgen -test-crypto-pbkdf -test-crypto-secret -test-crypto-tlscredsx509 -test-crypto-tlscredsx509-work/ -test-crypto-tlscredsx509-certs/ -test-crypto-tlssession -test-crypto-tlssession-work/ -test-crypto-tlssession-client/ -test-crypto-tlssession-server/ -test-crypto-xts -test-cutils -test-hbitmap -test-hmp -test-int128 -test-iov -test-io-channel-buffer -test-io-channel-command -test-io-channel-command.fifo -test-io-channel-file -test-io-channel-file.txt -test-io-channel-socket -test-io-channel-tls -test-io-task -test-keyval -test-logging -test-mul64 -test-opts-visitor +test-* +!test-*.c +!docker/test-* test-qapi-commands.[ch] test-qapi-events.[ch] test-qapi-types.[ch] -test-qapi-util test-qapi-visit.[ch] -test-qdev-global-props -test-qemu-opts -test-qdist -test-qga -test-qht -test-qht-par -test-qmp-cmds -test-qmp-event -test-qobject-input-strict -test-qobject-input-visitor test-qapi-introspect.[ch] -test-qobject-output-visitor -test-rcu-list -test-replication -test-shift128 -test-string-input-visitor -test-string-output-visitor -test-thread-pool -test-throttle -test-timed-average -test-uuid -test-util-sockets -test-visitor-serialization -test-vmstate -test-write-threshold -test-x86-cpuid -test-x86-cpuid-compat -test-xbzrle -test-netfilter -test-filter-mirror -test-filter-redirector *-test qapi-schema/*.test.* vm/*.img diff --git a/tests/Makefile.include b/tests/Makefile.include index 3b9a5e31a2..a49282704e 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -10,6 +10,7 @@ check-help: @echo " $(MAKE) check-speed Run qobject speed tests" @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" @echo " $(MAKE) check-block Run block tests" + @echo " $(MAKE) check-tcg Run TCG tests" @echo " $(MAKE) check-report.html Generates an HTML test report" @echo " $(MAKE) check-clean Clean the tests" @echo @@ -31,7 +32,6 @@ $(SRC_PATH)/scripts/qapi/types.py \ $(SRC_PATH)/scripts/qapi/visit.py \ $(SRC_PATH)/scripts/qapi/common.py \ $(SRC_PATH)/scripts/qapi/doc.py \ -$(SRC_PATH)/scripts/ordereddict.py \ $(SRC_PATH)/scripts/qapi-gen.py # Get the list of all supported sysemu targets @@ -40,6 +40,8 @@ SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ check-unit-y = tests/check-qdict$(EXESUF) gcov-files-check-qdict-y = qobject/qdict.c +check-unit-y = tests/check-block-qdict$(EXESUF) +gcov-files-check-block-qdict-y = qobject/block-qdict.c check-unit-y += tests/test-char$(EXESUF) gcov-files-check-qdict-y = chardev/char.c check-unit-y += tests/check-qnum$(EXESUF) @@ -179,6 +181,7 @@ check-qtest-generic-y = tests/qmp-test$(EXESUF) gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c check-qtest-generic-y += tests/device-introspect-test$(EXESUF) gcov-files-generic-y = qdev-monitor.c qmp.c +check-qtest-generic-y += tests/cdrom-test$(EXESUF) gcov-files-ipack-y += hw/ipack/ipack.c check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) @@ -297,7 +300,9 @@ check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EX ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),) check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF) endif +check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-swtpm-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-swtpm-test$(EXESUF) check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-test$(EXESUF) check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) @@ -371,6 +376,7 @@ check-qtest-sparc64-y += tests/prom-env-test$(EXESUF) check-qtest-sparc64-y += tests/boot-serial-test$(EXESUF) check-qtest-arm-y = tests/tmp105-test$(EXESUF) +check-qtest-arm-y += tests/pca9552-test$(EXESUF) check-qtest-arm-y += tests/ds1338-test$(EXESUF) check-qtest-arm-y += tests/m25p80-test$(EXESUF) gcov-files-arm-y += hw/misc/tmp105.c @@ -436,6 +442,10 @@ qapi-schema += args-unknown.json qapi-schema += bad-base.json qapi-schema += bad-data.json qapi-schema += bad-ident.json +qapi-schema += bad-if.json +qapi-schema += bad-if-empty.json +qapi-schema += bad-if-empty-list.json +qapi-schema += bad-if-list.json qapi-schema += bad-type-bool.json qapi-schema += bad-type-dict.json qapi-schema += bad-type-int.json @@ -493,7 +503,6 @@ qapi-schema += flat-union-base-any.json qapi-schema += flat-union-base-union.json qapi-schema += flat-union-clash-member.json qapi-schema += flat-union-empty.json -qapi-schema += flat-union-incomplete-branch.json qapi-schema += flat-union-inline.json qapi-schema += flat-union-int-branch.json qapi-schema += flat-union-invalid-branch-key.json @@ -524,6 +533,7 @@ qapi-schema += missing-type.json qapi-schema += nested-struct-data.json qapi-schema += non-objects.json qapi-schema += oob-test.json +qapi-schema += allow-preconfig-test.json qapi-schema += pragma-doc-required-crap.json qapi-schema += pragma-extra-junk.json qapi-schema += pragma-name-case-whitelist-crap.json @@ -580,6 +590,7 @@ GENERATED_FILES += tests/test-qapi-types.h tests/test-qapi-visit.h \ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \ tests/check-qjson.o tests/check-qlit.o \ + tests/check-block-qtest.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \ tests/test-clone-visitor.o \ @@ -610,6 +621,7 @@ test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y) tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) +tests/check-block-qdict$(EXESUF): tests/check-block-qdict.o $(test-util-obj-y) tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y) tests/check-qobject$(EXESUF): tests/check-qobject.o $(test-util-obj-y) @@ -670,13 +682,13 @@ tests/test-qapi-events.c tests/test-qapi-events.h \ tests/test-qapi-introspect.c tests/test-qapi-introspect.h: \ tests/test-qapi-gen-timestamp ; tests/test-qapi-gen-timestamp: $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(qapi-py) - $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o tests -p "test-" $<, \ "GEN","$(@:%-timestamp=%)") @>$@ tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.json $(qapi-py) - $(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o tests/qapi-schema -p "doc-good-" $<, \ "GEN","$@") @mv tests/qapi-schema/doc-good-qapi-doc.texi $@ @@ -715,13 +727,19 @@ tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \ tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS) tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ - tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y) + tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \ + tests/crypto-tls-psk-helpers.o \ + $(test-crypto-obj-y) tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \ tests/socket-helpers.o $(test-util-obj-y) tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y) tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \ tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y) +tests/tpm-crb-swtpm-test$(EXESUF): tests/tpm-crb-swtpm-test.o tests/tpm-emu.o \ + tests/tpm-util.o tests/tpm-tests.o $(test-io-obj-y) tests/tpm-crb-test$(EXESUF): tests/tpm-crb-test.o tests/tpm-emu.o $(test-io-obj-y) +tests/tpm-tis-swtpm-test$(EXESUF): tests/tpm-tis-swtpm-test.o tests/tpm-emu.o \ + tests/tpm-util.o tests/tpm-tests.o $(test-io-obj-y) tests/tpm-tis-test$(EXESUF): tests/tpm-tis-test.o tests/tpm-emu.o $(test-io-obj-y) tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \ tests/io-channel-helpers.o $(test-io-obj-y) @@ -771,6 +789,7 @@ tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \ tests/boot-sector.o tests/acpi-utils.o $(libqos-obj-y) tests/pxe-test$(EXESUF): tests/pxe-test.o tests/boot-sector.o $(libqos-obj-y) tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y) +tests/pca9552-test$(EXESUF): tests/pca9552-test.o $(libqos-omap-obj-y) tests/ds1338-test$(EXESUF): tests/ds1338-test.o $(libqos-imx-obj-y) tests/m25p80-test$(EXESUF): tests/m25p80-test.o tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) @@ -835,6 +854,7 @@ tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) tests/numa-test$(EXESUF): tests/numa-test.o tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y) +tests/cdrom-test$(EXESUF): tests/cdrom-test.o tests/boot-sector.o $(libqos-obj-y) tests/migration/stress$(EXESUF): tests/migration/stress.o $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") @@ -877,26 +897,16 @@ GCOV_OPTIONS = -n $(if $(V),-f,) .PHONY: $(patsubst %, check-qtest-%, $(QTEST_TARGETS)) $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: subdir-%-softmmu $(check-qtest-y) - $(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,) $(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ QTEST_QEMU_IMG=qemu-img$(EXESUF) \ MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} \ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y) $(check-qtest-generic-y),"GTESTER","$@") - $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y) $(gcov-files-generic-y); do \ - echo Gcov report for $$f:;\ - $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \ - done,) .PHONY: $(patsubst %, check-%, $(check-unit-y) $(check-speed-y)) $(patsubst %, check-%, $(check-unit-y) $(check-speed-y)): check-%: % - $(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,) $(call quiet-command, \ MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} \ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER","$*") - $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y) $(gcov-files-generic-y); do \ - echo Gcov report for $$f:;\ - $(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \ - done,) # gtester tests with XML output @@ -916,10 +926,46 @@ check-report.xml: $(patsubst %,check-report-qtest-%.xml, $(QTEST_TARGETS)) check check-report.html: check-report.xml $(call quiet-command,gtester-report $< > $@,"GEN","$@") +# Per guest TCG tests + +LINUX_USER_TARGETS=$(filter %-linux-user,$(TARGET_DIRS)) +BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(LINUX_USER_TARGETS)) +CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(LINUX_USER_TARGETS)) +RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(LINUX_USER_TARGETS)) + +ifeq ($(HAVE_USER_DOCKER),y) +# Probe for the Docker Builds needed for each build +$(foreach PROBE_TARGET,$(TARGET_DIRS), \ + $(eval -include $(SRC_PATH)/tests/tcg/Makefile.probe) \ + $(if $(DOCKER_PREREQ), \ + $(eval build-tcg-tests-$(PROBE_TARGET): $(DOCKER_PREREQ)))) +endif + +build-tcg-tests-%: + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" \ + SKIP_DOCKER_BUILD=1 TARGET_DIR="$*/" guest-tests, \ + "BUILD", "TCG tests for $*") + +run-tcg-tests-%: % build-tcg-tests-% + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" \ + SKIP_DOCKER_BUILD=1 TARGET_DIR="$*/" run-guest-tests, \ + "RUN", "TCG tests for $*") + +clean-tcg-tests-%: + $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" clean-guest-tests,) + +.PHONY: build-tcg +build-tcg: $(BUILD_TCG_TARGET_RULES) + +.PHONY: check-tcg +check-tcg: $(RUN_TCG_TARGET_RULES) + +.PHONY: clean-tcg +clean-tcg: $(CLEAN_TCG_TARGET_RULES) # Other tests -QEMU_IOTESTS_HELPERS-$(CONFIG_LINUX) = tests/qemu-iotests/socket_scm_helper$(EXESUF) +QEMU_IOTESTS_HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = tests/qemu-iotests/socket_scm_helper$(EXESUF) .PHONY: check-tests/qemu-iotests-quick.sh check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) $(QEMU_IOTESTS_HELPERS-y) @@ -928,7 +974,7 @@ check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) .PHONY: $(patsubst %, check-%, $(check-qapi-schema-y)) $(patsubst %, check-%, $(check-qapi-schema-y)): check-%.json: $(SRC_PATH)/%.json $(call quiet-command, PYTHONPATH=$(SRC_PATH)/scripts \ - $(PYTHON_UTF8) $(SRC_PATH)/tests/qapi-schema/test-qapi.py \ + $(PYTHON) $(SRC_PATH)/tests/qapi-schema/test-qapi.py \ $^ >$*.test.out 2>$*.test.err; \ echo $$? >$*.test.exit, \ "TEST","$*.out") @@ -958,7 +1004,6 @@ check-speed: $(patsubst %,check-%, $(check-speed-y)) check-block: $(patsubst %,check-%, $(check-block-y)) check: check-qapi-schema check-unit check-qtest check-decodetree check-clean: - $(MAKE) -C tests/tcg clean rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y) rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y)) rm -f tests/test-qapi-gen-timestamp diff --git a/tests/acceptance/README.rst b/tests/acceptance/README.rst new file mode 100644 index 0000000000..89260faed6 --- /dev/null +++ b/tests/acceptance/README.rst @@ -0,0 +1,10 @@ +============================================ +Acceptance tests using the Avocado Framework +============================================ + +This directory contains functional tests, also known as acceptance +level tests. They're usually higher level, and may interact with +external resources and with various guest operating systems. + +For more information, please refer to ``docs/devel/testing.rst``, +section "Acceptance tests using the Avocado Framework". diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py new file mode 100644 index 0000000000..1e54fd5932 --- /dev/null +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -0,0 +1,54 @@ +# Test class and utilities for functional tests +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# 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 os +import sys + +import avocado + +SRC_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) +SRC_ROOT_DIR = os.path.abspath(os.path.dirname(SRC_ROOT_DIR)) +sys.path.append(os.path.join(SRC_ROOT_DIR, 'scripts')) + +from qemu import QEMUMachine + +def is_readable_executable_file(path): + return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) + + +def pick_default_qemu_bin(): + """ + Picks the path of a QEMU binary, starting either in the current working + directory or in the source tree root directory. + """ + arch = os.uname()[4] + qemu_bin_relative_path = os.path.join("%s-softmmu" % arch, + "qemu-system-%s" % arch) + if is_readable_executable_file(qemu_bin_relative_path): + return qemu_bin_relative_path + + qemu_bin_from_src_dir_path = os.path.join(SRC_ROOT_DIR, + qemu_bin_relative_path) + if is_readable_executable_file(qemu_bin_from_src_dir_path): + return qemu_bin_from_src_dir_path + + +class Test(avocado.Test): + def setUp(self): + self.vm = None + self.qemu_bin = self.params.get('qemu_bin', + default=pick_default_qemu_bin()) + if self.qemu_bin is None: + self.cancel("No QEMU binary defined or found in the source tree") + self.vm = QEMUMachine(self.qemu_bin) + + def tearDown(self): + if self.vm is not None: + self.vm.shutdown() diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py new file mode 100644 index 0000000000..98324f7591 --- /dev/null +++ b/tests/acceptance/boot_linux_console.py @@ -0,0 +1,47 @@ +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# 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 logging + +from avocado_qemu import Test + + +class BootLinuxConsole(Test): + """ + Boots a x86_64 Linux kernel and checks that the console is operational + and the kernel command line is properly passed from QEMU to the kernel + + :avocado: enable + :avocado: tags=x86_64 + """ + + timeout = 60 + + def test(self): + kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/' + 'Everything/x86_64/os/images/pxeboot/vmlinuz') + kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + + self.vm.set_machine('pc') + self.vm.set_console() + kernel_command_line = 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console = self.vm.console_socket.makefile() + console_logger = logging.getLogger('console') + while True: + msg = console.readline() + console_logger.debug(msg.strip()) + if 'Kernel command line: %s' % kernel_command_line in msg: + break + if 'Kernel panic - not syncing' in msg: + self.fail("Kernel panic reached") diff --git a/tests/acceptance/version.py b/tests/acceptance/version.py new file mode 100644 index 0000000000..13b0a7440d --- /dev/null +++ b/tests/acceptance/version.py @@ -0,0 +1,24 @@ +# Version check example test +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +from avocado_qemu import Test + + +class Version(Test): + """ + :avocado: enable + :avocado: tags=quick + """ + def test_qmp_human_info_version(self): + self.vm.launch() + res = self.vm.command('human-monitor-command', + command_line='info version') + self.assertRegexpMatches(res, r'^(\d+\.\d+\.\d)') diff --git a/tests/acceptance/vnc.py b/tests/acceptance/vnc.py new file mode 100644 index 0000000000..b1ef9d71b1 --- /dev/null +++ b/tests/acceptance/vnc.py @@ -0,0 +1,60 @@ +# Simple functional tests for VNC functionality +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Cleber Rosa +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado_qemu import Test + + +class Vnc(Test): + """ + :avocado: enable + :avocado: tags=vnc,quick + """ + def test_no_vnc(self): + self.vm.add_args('-nodefaults', '-S') + self.vm.launch() + self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) + + def test_no_vnc_change_password(self): + self.vm.add_args('-nodefaults', '-S') + self.vm.launch() + self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) + set_password_response = self.vm.qmp('change', + device='vnc', + target='password', + arg='new_password') + self.assertIn('error', set_password_response) + self.assertEqual(set_password_response['error']['class'], + 'GenericError') + self.assertEqual(set_password_response['error']['desc'], + 'Could not set password') + + def test_vnc_change_password_requires_a_password(self): + self.vm.add_args('-nodefaults', '-S', '-vnc', ':0') + self.vm.launch() + self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) + set_password_response = self.vm.qmp('change', + device='vnc', + target='password', + arg='new_password') + self.assertIn('error', set_password_response) + self.assertEqual(set_password_response['error']['class'], + 'GenericError') + self.assertEqual(set_password_response['error']['desc'], + 'Could not set password') + + def test_vnc_change_password(self): + self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password') + self.vm.launch() + self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) + set_password_response = self.vm.qmp('change', + device='vnc', + target='password', + arg='new_password') + self.assertEqual(set_password_response['return'], {}) diff --git a/tests/acpi-test-data/pc/NFIT.dimmpxm b/tests/acpi-test-data/pc/NFIT.dimmpxm index 2bfc6c51f3..598d331b75 100644 Binary files a/tests/acpi-test-data/pc/NFIT.dimmpxm and b/tests/acpi-test-data/pc/NFIT.dimmpxm differ diff --git a/tests/acpi-test-data/pc/SRAT.dimmpxm b/tests/acpi-test-data/pc/SRAT.dimmpxm index 3b10a607d5..5aa6f693ef 100644 Binary files a/tests/acpi-test-data/pc/SRAT.dimmpxm and b/tests/acpi-test-data/pc/SRAT.dimmpxm differ diff --git a/tests/acpi-test-data/pc/SRAT.memhp b/tests/acpi-test-data/pc/SRAT.memhp index e508b4ae3c..5de8a100a4 100644 Binary files a/tests/acpi-test-data/pc/SRAT.memhp and b/tests/acpi-test-data/pc/SRAT.memhp differ diff --git a/tests/acpi-test-data/pc/SRAT.numamem b/tests/acpi-test-data/pc/SRAT.numamem index dbc595d9cb..119922f497 100644 Binary files a/tests/acpi-test-data/pc/SRAT.numamem and b/tests/acpi-test-data/pc/SRAT.numamem differ diff --git a/tests/acpi-test-data/q35/NFIT.dimmpxm b/tests/acpi-test-data/q35/NFIT.dimmpxm index 2bfc6c51f3..598d331b75 100644 Binary files a/tests/acpi-test-data/q35/NFIT.dimmpxm and b/tests/acpi-test-data/q35/NFIT.dimmpxm differ diff --git a/tests/acpi-test-data/q35/SRAT.dimmpxm b/tests/acpi-test-data/q35/SRAT.dimmpxm index 3b10a607d5..5aa6f693ef 100644 Binary files a/tests/acpi-test-data/q35/SRAT.dimmpxm and b/tests/acpi-test-data/q35/SRAT.dimmpxm differ diff --git a/tests/acpi-test-data/q35/SRAT.memhp b/tests/acpi-test-data/q35/SRAT.memhp index e508b4ae3c..5de8a100a4 100644 Binary files a/tests/acpi-test-data/q35/SRAT.memhp and b/tests/acpi-test-data/q35/SRAT.memhp differ diff --git a/tests/acpi-test-data/q35/SRAT.numamem b/tests/acpi-test-data/q35/SRAT.numamem index dbc595d9cb..119922f497 100644 Binary files a/tests/acpi-test-data/q35/SRAT.numamem and b/tests/acpi-test-data/q35/SRAT.numamem differ diff --git a/tests/acpi-test-data/q35/SSDT.dimmpxm b/tests/acpi-test-data/q35/SSDT.dimmpxm index 8ba0e67cb7..2d5b721bcf 100644 Binary files a/tests/acpi-test-data/q35/SSDT.dimmpxm and b/tests/acpi-test-data/q35/SSDT.dimmpxm differ diff --git a/tests/ahci-test.c b/tests/ahci-test.c index fb3cd84d07..1a7b761304 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1566,7 +1566,7 @@ static void atapi_wait_tray(bool open) } else { g_assert(!qdict_get_bool(data, "tray-open")); } - QDECREF(rsp); + qobject_unref(rsp); } static void test_atapi_tray(void) @@ -1596,7 +1596,7 @@ static void test_atapi_tray(void) "'arguments': {'id': 'cd0'}}"); atapi_wait_tray(true); rsp = qmp_receive(); - QDECREF(rsp); + qobject_unref(rsp); qmp_discard_response("{'execute': 'blockdev-remove-medium', " "'arguments': {'id': 'cd0'}}"); @@ -1623,7 +1623,7 @@ static void test_atapi_tray(void) "'arguments': {'id': 'cd0'}}"); atapi_wait_tray(false); rsp = qmp_receive(); - QDECREF(rsp); + qobject_unref(rsp); /* Now, to convince ATAPI we understand the media has changed... */ ahci_atapi_test_ready(ahci, port, false, SENSE_NOT_READY); diff --git a/tests/atomic_add-bench.c b/tests/atomic_add-bench.c index caa1e8e689..f96d448f77 100644 --- a/tests/atomic_add-bench.c +++ b/tests/atomic_add-bench.c @@ -8,6 +8,7 @@ struct thread_info { } QEMU_ALIGNED(64); struct count { + QemuMutex lock; unsigned long val; } QEMU_ALIGNED(64); @@ -18,11 +19,13 @@ static unsigned int n_ready_threads; static struct count *counts; static unsigned int duration = 1; static unsigned int range = 1024; +static bool use_mutex; static bool test_start; static bool test_stop; static const char commands_string[] = " -n = number of threads\n" + " -m = use mutexes instead of atomic increments\n" " -d = duration in seconds\n" " -r = range (will be rounded up to pow2)"; @@ -59,7 +62,13 @@ static void *thread_func(void *arg) info->r = xorshift64star(info->r); index = info->r & (range - 1); - atomic_inc(&counts[index].val); + if (use_mutex) { + qemu_mutex_lock(&counts[index].lock); + counts[index].val += 1; + qemu_mutex_unlock(&counts[index].lock); + } else { + atomic_inc(&counts[index].val); + } } return NULL; } @@ -91,6 +100,9 @@ static void create_threads(void) th_info = g_new(struct thread_info, n_threads); counts = qemu_memalign(64, sizeof(*counts) * range); memset(counts, 0, sizeof(*counts) * range); + for (i = 0; i < range; i++) { + qemu_mutex_init(&counts[i].lock); + } for (i = 0; i < n_threads; i++) { struct thread_info *info = &th_info[i]; @@ -131,7 +143,7 @@ static void parse_args(int argc, char *argv[]) int c; for (;;) { - c = getopt(argc, argv, "hd:n:r:"); + c = getopt(argc, argv, "hd:n:mr:"); if (c < 0) { break; } @@ -145,6 +157,9 @@ static void parse_args(int argc, char *argv[]) case 'n': n_threads = atoi(optarg); break; + case 'm': + use_mutex = true; + break; case 'r': range = pow2ceil(atoi(optarg)); break; diff --git a/tests/benchmark-crypto-cipher.c b/tests/benchmark-crypto-cipher.c index cf98443468..f5a0d0bc32 100644 --- a/tests/benchmark-crypto-cipher.c +++ b/tests/benchmark-crypto-cipher.c @@ -11,6 +11,7 @@ * top-level directory. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "crypto/init.h" #include "crypto/cipher.h" @@ -56,8 +57,7 @@ static void test_cipher_speed(const void *opaque) total += chunk_size; } while (g_test_timer_elapsed() < 5.0); - total /= 1024 * 1024; /* to MB */ - + total /= MiB; g_print("cbc(aes128): "); g_print("Testing chunk_size %zu bytes ", chunk_size); g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); @@ -78,7 +78,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_assert(qcrypto_init(NULL) == 0); - for (i = 512; i <= (64 * 1204); i *= 2) { + for (i = 512; i <= 64 * KiB; i *= 2) { memset(name, 0 , sizeof(name)); snprintf(name, sizeof(name), "/crypto/cipher/speed-%zu", i); g_test_add_data_func(name, (void *)i, test_cipher_speed); diff --git a/tests/benchmark-crypto-hash.c b/tests/benchmark-crypto-hash.c index 122bfb6b85..9b6f7a9155 100644 --- a/tests/benchmark-crypto-hash.c +++ b/tests/benchmark-crypto-hash.c @@ -11,6 +11,7 @@ * top-level directory. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "crypto/init.h" #include "crypto/hash.h" @@ -39,7 +40,7 @@ static void test_hash_speed(const void *opaque) total += chunk_size; } while (g_test_timer_elapsed() < 5.0); - total /= 1024 * 1024; /* to MB */ + total /= MiB; g_print("sha256: "); g_print("Testing chunk_size %zu bytes ", chunk_size); g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); @@ -57,7 +58,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_assert(qcrypto_init(NULL) == 0); - for (i = 512; i <= (64 * 1204); i *= 2) { + for (i = 512; i <= 64 * KiB; i *= 2) { memset(name, 0 , sizeof(name)); snprintf(name, sizeof(name), "/crypto/hash/speed-%zu", i); g_test_add_data_func(name, (void *)i, test_hash_speed); diff --git a/tests/benchmark-crypto-hmac.c b/tests/benchmark-crypto-hmac.c index c30250df3e..f1dfa240cb 100644 --- a/tests/benchmark-crypto-hmac.c +++ b/tests/benchmark-crypto-hmac.c @@ -11,6 +11,7 @@ * top-level directory. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "crypto/init.h" #include "crypto/hmac.h" @@ -53,8 +54,7 @@ static void test_hmac_speed(const void *opaque) total += chunk_size; } while (g_test_timer_elapsed() < 5.0); - total /= 1024 * 1024; /* to MB */ - + total /= MiB; g_print("hmac(sha256): "); g_print("Testing chunk_size %zu bytes ", chunk_size); g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); @@ -72,7 +72,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_assert(qcrypto_init(NULL) == 0); - for (i = 512; i <= (64 * 1204); i *= 2) { + for (i = 512; i <= 64 * KiB; i *= 2) { memset(name, 0 , sizeof(name)); snprintf(name, sizeof(name), "/crypto/hmac/speed-%zu", i); g_test_add_data_func(name, (void *)i, test_hmac_speed); diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index bf3e193ae9..4e24930c4b 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -830,7 +830,7 @@ static void test_acpi_tcg_dimm_pxm(const char *machine) memset(&data, 0, sizeof(data)); data.machine = machine; data.variant = ".dimmpxm"; - test_acpi_one(" -machine nvdimm=on" + test_acpi_one(" -machine nvdimm=on,nvdimm-persistence=cpu" " -smp 4,sockets=4" " -m 128M,slots=3,maxmem=1G" " -numa node,mem=32M,nodeid=0" diff --git a/tests/boot-sector.c b/tests/boot-sector.c index c373f0e715..7824286b9a 100644 --- a/tests/boot-sector.c +++ b/tests/boot-sector.c @@ -68,8 +68,11 @@ static uint8_t x86_boot_sector[512] = { }; /* For s390x, use a mini "kernel" with the appropriate signature */ -static const uint8_t s390x_psw[] = { - 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00 +static const uint8_t s390x_psw_and_magic[] = { + 0x00, 0x08, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, /* Program status word */ + 0x02, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x50, /* Magic: */ + 0x02, 0x00, 0x00, 0x68, 0x60, 0x00, 0x00, 0x50, /* see linux_s390_magic */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* in the s390-ccw bios */ }; static const uint8_t s390x_code[] = { 0xa7, 0xf4, 0x00, 0x0a, /* j 0x10010 */ @@ -110,7 +113,7 @@ int boot_sector_init(char *fname) } else if (g_str_equal(arch, "s390x")) { len = 0x10000 + sizeof(s390x_code); boot_code = g_malloc0(len); - memcpy(boot_code, s390x_psw, sizeof(s390x_psw)); + memcpy(boot_code, s390x_psw_and_magic, sizeof(s390x_psw_and_magic)); memcpy(&boot_code[0x10000], s390x_code, sizeof(s390x_code)); } else { g_assert_not_reached(); diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c index 011525d8cf..952a2e7ead 100644 --- a/tests/boot-serial-test.c +++ b/tests/boot-serial-test.c @@ -96,8 +96,7 @@ static testdef_t tests[] = { { "sparc", "SS-4", "", "MB86904" }, { "sparc", "SS-600MP", "", "TMS390Z55" }, { "sparc64", "sun4u", "", "UltraSPARC" }, - { "s390x", "s390-ccw-virtio", - "-nodefaults -device sclpconsole,chardev=serial0", "virtio device" }, + { "s390x", "s390-ccw-virtio", "", "virtio device" }, { "m68k", "mcf5208evb", "", "TT", sizeof(kernel_mcf5208), kernel_mcf5208 }, { "microblaze", "petalogix-s3adsp1800", "", "TT", sizeof(kernel_pls3adsp1800), kernel_pls3adsp1800 }, @@ -112,9 +111,8 @@ static testdef_t tests[] = { { NULL } }; -static void check_guest_output(const testdef_t *test, int fd) +static bool check_guest_output(const testdef_t *test, int fd) { - bool output_ok = false; int i, nbr = 0, pos = 0, ccnt; char ch; @@ -126,8 +124,7 @@ static void check_guest_output(const testdef_t *test, int fd) pos += 1; if (test->expect[pos] == '\0') { /* We've reached the end of the expected string! */ - output_ok = true; - goto done; + return true; } } else { pos = 0; @@ -137,8 +134,7 @@ static void check_guest_output(const testdef_t *test, int fd) g_usleep(10000); } -done: - g_assert(output_ok); + return false; } static void test_machine(const void *data) @@ -181,12 +177,16 @@ static void test_machine(const void *data) "-no-shutdown -serial chardev:serial0 %s", codeparam, code ? codetmp : "", test->machine, serialtmp, test->extra); - unlink(serialtmp); if (code) { unlink(codetmp); } - check_guest_output(test, ser_fd); + if (!check_guest_output(test, ser_fd)) { + g_error("Failed to find expected string. Please check '%s'", + serialtmp); + } + unlink(serialtmp); + qtest_quit(global_qtest); close(ser_fd); diff --git a/tests/cdrom-test.c b/tests/cdrom-test.c new file mode 100644 index 0000000000..7a1fce5dfb --- /dev/null +++ b/tests/cdrom-test.c @@ -0,0 +1,222 @@ +/* + * Various tests for emulated CD-ROM drives. + * + * Copyright (c) 2018 Red Hat Inc. + * + * Author: + * Thomas Huth + * + * This work is licensed under the terms of the GNU GPL, version 2 + * or later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "boot-sector.h" +#include "qapi/qmp/qdict.h" + +static char isoimage[] = "cdrom-boot-iso-XXXXXX"; + +static int exec_genisoimg(const char **args) +{ + gchar *out_err = NULL; + gint exit_status = -1; + bool success; + + success = g_spawn_sync(NULL, (gchar **)args, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL, + NULL, NULL, NULL, &out_err, &exit_status, NULL); + if (!success) { + return -ENOENT; + } + if (out_err) { + fputs(out_err, stderr); + g_free(out_err); + } + + return exit_status; +} + +static int prepare_image(const char *arch, char *isoimage) +{ + char srcdir[] = "cdrom-test-dir-XXXXXX"; + char *codefile = NULL; + int ifh, ret = -1; + const char *args[] = { + "genisoimage", "-quiet", "-l", "-no-emul-boot", + "-b", NULL, "-o", isoimage, srcdir, NULL + }; + + ifh = mkstemp(isoimage); + if (ifh < 0) { + perror("Error creating temporary iso image file"); + return -1; + } + if (!mkdtemp(srcdir)) { + perror("Error creating temporary directory"); + goto cleanup; + } + + if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || + g_str_equal(arch, "s390x")) { + codefile = g_strdup_printf("%s/bootcode-XXXXXX", srcdir); + ret = boot_sector_init(codefile); + if (ret) { + goto cleanup; + } + } else { + /* Just create a dummy file */ + char txt[] = "empty disc"; + codefile = g_strdup_printf("%s/readme.txt", srcdir); + if (!g_file_set_contents(codefile, txt, sizeof(txt) - 1, NULL)) { + fprintf(stderr, "Failed to create '%s'\n", codefile); + goto cleanup; + } + } + + args[5] = strchr(codefile, '/') + 1; + ret = exec_genisoimg(args); + if (ret) { + fprintf(stderr, "genisoimage failed: %i\n", ret); + } + + unlink(codefile); + +cleanup: + g_free(codefile); + rmdir(srcdir); + close(ifh); + + return ret; +} + +/** + * Check that at least the -cdrom parameter is basically working, i.e. we can + * see the filename of the ISO image in the output of "info block" afterwards + */ +static void test_cdrom_param(gconstpointer data) +{ + QTestState *qts; + char *resp; + + qts = qtest_startf("-M %s -cdrom %s", (const char *)data, isoimage); + resp = qtest_hmp(qts, "info block"); + g_assert(strstr(resp, isoimage) != 0); + g_free(resp); + qtest_quit(qts); +} + +static void add_cdrom_param_tests(const char **machines) +{ + while (*machines) { + char *testname = g_strdup_printf("cdrom/param/%s", *machines); + qtest_add_data_func(testname, *machines, test_cdrom_param); + g_free(testname); + machines++; + } +} + +static void test_cdboot(gconstpointer data) +{ + QTestState *qts; + + qts = qtest_startf("-accel kvm:tcg -no-shutdown %s%s", (const char *)data, + isoimage); + boot_sector_test(qts); + qtest_quit(qts); +} + +static void add_x86_tests(void) +{ + qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); + qtest_add_data_func("cdrom/boot/virtio-scsi", + "-device virtio-scsi -device scsi-cd,drive=cdr " + "-blockdev file,node-name=cdr,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/isapc", "-M isapc " + "-drive if=ide,media=cdrom,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/am53c974", + "-device am53c974 -device scsi-cd,drive=cd1 " + "-drive if=none,id=cd1,format=raw,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/dc390", + "-device dc390 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/lsi53c895a", + "-device lsi53c895a -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/megasas", "-M q35 " + "-device megasas -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); + qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 " + "-device megasas-gen2 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", test_cdboot); +} + +static void add_s390x_tests(void) +{ + qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); + qtest_add_data_func("cdrom/boot/virtio-scsi", + "-device virtio-scsi -device scsi-cd,drive=cdr " + "-blockdev file,node-name=cdr,filename=", test_cdboot); +} + +int main(int argc, char **argv) +{ + int ret; + const char *arch = qtest_get_arch(); + const char *genisocheck[] = { "genisoimage", "-version", NULL }; + + g_test_init(&argc, &argv, NULL); + + if (exec_genisoimg(genisocheck)) { + /* genisoimage not available - so can't run tests */ + return 0; + } + + ret = prepare_image(arch, isoimage); + if (ret) { + return ret; + } + + if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64")) { + add_x86_tests(); + } else if (g_str_equal(arch, "s390x")) { + add_s390x_tests(); + } else if (g_str_equal(arch, "ppc64")) { + const char *ppcmachines[] = { + "pseries", "mac99", "g3beige", "40p", "prep", NULL + }; + add_cdrom_param_tests(ppcmachines); + } else if (g_str_equal(arch, "sparc")) { + const char *sparcmachines[] = { + "LX", "SPARCClassic", "SPARCbook", "SS-10", "SS-20", "SS-4", + "SS-5", "SS-600MP", "Voyager", "leon3_generic", NULL + }; + add_cdrom_param_tests(sparcmachines); + } else if (g_str_equal(arch, "sparc64")) { + const char *sparc64machines[] = { + "niagara", "sun4u", "sun4v", NULL + }; + add_cdrom_param_tests(sparc64machines); + } else if (!strncmp(arch, "mips64", 6)) { + const char *mips64machines[] = { + "magnum", "malta", "mips", "pica61", NULL + }; + add_cdrom_param_tests(mips64machines); + } else if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { + const char *armmachines[] = { + "realview-eb", "realview-eb-mpcore", "realview-pb-a8", + "realview-pbx-a9", "versatileab", "versatilepb", "vexpress-a15", + "vexpress-a9", "virt", NULL + }; + add_cdrom_param_tests(armmachines); + } else { + const char *nonemachine[] = { "none", NULL }; + add_cdrom_param_tests(nonemachine); + } + + ret = g_test_run(); + + unlink(isoimage); + + return ret; +} diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c new file mode 100644 index 0000000000..478807f839 --- /dev/null +++ b/tests/check-block-qdict.c @@ -0,0 +1,723 @@ +/* + * Unit-tests for Block layer QDict extras + * + * Copyright (c) 2013-2018 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "block/qdict.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "qapi/error.h" + +static void qdict_defaults_test(void) +{ + QDict *dict, *copy; + + dict = qdict_new(); + copy = qdict_new(); + + qdict_set_default_str(dict, "foo", "abc"); + qdict_set_default_str(dict, "foo", "def"); + g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc"); + qdict_set_default_str(dict, "bar", "ghi"); + + qdict_copy_default(copy, dict, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc"); + qdict_set_default_str(copy, "bar", "xyz"); + qdict_copy_default(copy, dict, "bar"); + g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); + + qobject_unref(copy); + qobject_unref(dict); +} + +static void qdict_flatten_test(void) +{ + QList *e_1 = qlist_new(); + QList *e = qlist_new(); + QDict *e_1_2 = qdict_new(); + QDict *f = qdict_new(); + QList *y = qlist_new(); + QDict *z = qdict_new(); + QDict *root = qdict_new(); + + /* + * Test the flattening of + * + * { + * "e": [ + * 42, + * [ + * 23, + * 66, + * { + * "a": 0, + * "b": 1 + * } + * ] + * ], + * "f": { + * "c": 2, + * "d": 3, + * }, + * "g": 4, + * "y": [{}], + * "z": {"a": []} + * } + * + * to + * + * { + * "e.0": 42, + * "e.1.0": 23, + * "e.1.1": 66, + * "e.1.2.a": 0, + * "e.1.2.b": 1, + * "f.c": 2, + * "f.d": 3, + * "g": 4, + * "y.0": {}, + * "z.a": [] + * } + */ + + qdict_put_int(e_1_2, "a", 0); + qdict_put_int(e_1_2, "b", 1); + + qlist_append_int(e_1, 23); + qlist_append_int(e_1, 66); + qlist_append(e_1, e_1_2); + qlist_append_int(e, 42); + qlist_append(e, e_1); + + qdict_put_int(f, "c", 2); + qdict_put_int(f, "d", 3); + + qlist_append(y, qdict_new()); + + qdict_put(z, "a", qlist_new()); + + qdict_put(root, "e", e); + qdict_put(root, "f", f); + qdict_put_int(root, "g", 4); + qdict_put(root, "y", y); + qdict_put(root, "z", z); + + qdict_flatten(root); + + g_assert(qdict_get_int(root, "e.0") == 42); + g_assert(qdict_get_int(root, "e.1.0") == 23); + g_assert(qdict_get_int(root, "e.1.1") == 66); + g_assert(qdict_get_int(root, "e.1.2.a") == 0); + g_assert(qdict_get_int(root, "e.1.2.b") == 1); + g_assert(qdict_get_int(root, "f.c") == 2); + g_assert(qdict_get_int(root, "f.d") == 3); + g_assert(qdict_get_int(root, "g") == 4); + g_assert(!qdict_size(qdict_get_qdict(root, "y.0"))); + g_assert(qlist_empty(qdict_get_qlist(root, "z.a"))); + + g_assert(qdict_size(root) == 10); + + qobject_unref(root); +} + +static void qdict_clone_flatten_test(void) +{ + QDict *dict1 = qdict_new(); + QDict *dict2 = qdict_new(); + QDict *cloned_dict1; + + /* + * Test that we can clone and flatten + * { "a": { "b": 42 } } + * without modifying the clone. + */ + + qdict_put_int(dict2, "b", 42); + qdict_put(dict1, "a", dict2); + + cloned_dict1 = qdict_clone_shallow(dict1); + + qdict_flatten(dict1); + + g_assert(qdict_size(dict1) == 1); + g_assert(qdict_get_int(dict1, "a.b") == 42); + + g_assert(qdict_size(cloned_dict1) == 1); + g_assert(qdict_get_qdict(cloned_dict1, "a") == dict2); + + g_assert(qdict_size(dict2) == 1); + g_assert(qdict_get_int(dict2, "b") == 42); + + qobject_unref(dict1); + qobject_unref(cloned_dict1); +} + +static void qdict_array_split_test(void) +{ + QDict *test_dict = qdict_new(); + QDict *dict1, *dict2; + QNum *int1; + QList *test_list; + + /* + * Test the split of + * + * { + * "1.x": 0, + * "4.y": 1, + * "0.a": 42, + * "o.o": 7, + * "0.b": 23, + * "2": 66 + * } + * + * to + * + * [ + * { + * "a": 42, + * "b": 23 + * }, + * { + * "x": 0 + * }, + * 66 + * ] + * + * and + * + * { + * "4.y": 1, + * "o.o": 7 + * } + * + * (remaining in the old QDict) + * + * This example is given in the comment of qdict_array_split(). + */ + + qdict_put_int(test_dict, "1.x", 0); + qdict_put_int(test_dict, "4.y", 1); + qdict_put_int(test_dict, "0.a", 42); + qdict_put_int(test_dict, "o.o", 7); + qdict_put_int(test_dict, "0.b", 23); + qdict_put_int(test_dict, "2", 66); + + qdict_array_split(test_dict, &test_list); + + dict1 = qobject_to(QDict, qlist_pop(test_list)); + dict2 = qobject_to(QDict, qlist_pop(test_list)); + int1 = qobject_to(QNum, qlist_pop(test_list)); + + g_assert(dict1); + g_assert(dict2); + g_assert(int1); + g_assert(qlist_empty(test_list)); + + qobject_unref(test_list); + + g_assert(qdict_get_int(dict1, "a") == 42); + g_assert(qdict_get_int(dict1, "b") == 23); + + g_assert(qdict_size(dict1) == 2); + + qobject_unref(dict1); + + g_assert(qdict_get_int(dict2, "x") == 0); + + g_assert(qdict_size(dict2) == 1); + + qobject_unref(dict2); + + g_assert_cmpint(qnum_get_int(int1), ==, 66); + + qobject_unref(int1); + + g_assert(qdict_get_int(test_dict, "4.y") == 1); + g_assert(qdict_get_int(test_dict, "o.o") == 7); + + g_assert(qdict_size(test_dict) == 2); + + qobject_unref(test_dict); + + /* + * Test the split of + * + * { + * "0": 42, + * "1": 23, + * "1.x": 84 + * } + * + * to + * + * [ + * 42 + * ] + * + * and + * + * { + * "1": 23, + * "1.x": 84 + * } + * + * That is, test whether splitting stops if there is both an entry with key + * of "%u" and other entries with keys prefixed "%u." for the same index. + */ + + test_dict = qdict_new(); + + qdict_put_int(test_dict, "0", 42); + qdict_put_int(test_dict, "1", 23); + qdict_put_int(test_dict, "1.x", 84); + + qdict_array_split(test_dict, &test_list); + + int1 = qobject_to(QNum, qlist_pop(test_list)); + + g_assert(int1); + g_assert(qlist_empty(test_list)); + + qobject_unref(test_list); + + g_assert_cmpint(qnum_get_int(int1), ==, 42); + + qobject_unref(int1); + + g_assert(qdict_get_int(test_dict, "1") == 23); + g_assert(qdict_get_int(test_dict, "1.x") == 84); + + g_assert(qdict_size(test_dict) == 2); + + qobject_unref(test_dict); +} + +static void qdict_array_entries_test(void) +{ + QDict *dict = qdict_new(); + + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); + + qdict_put_int(dict, "bar", 0); + qdict_put_int(dict, "baz.0", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); + + qdict_put_int(dict, "foo.1", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); + qdict_put_int(dict, "foo.0", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); + qdict_put_int(dict, "foo.bar", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); + qdict_del(dict, "foo.bar"); + + qdict_put_int(dict, "foo.2.a", 0); + qdict_put_int(dict, "foo.2.b", 0); + qdict_put_int(dict, "foo.2.c", 0); + g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + + qobject_unref(dict); + + dict = qdict_new(); + qdict_put_int(dict, "1", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + qdict_put_int(dict, "0", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); + qdict_put_int(dict, "bar", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); + qdict_del(dict, "bar"); + + qdict_put_int(dict, "2.a", 0); + qdict_put_int(dict, "2.b", 0); + qdict_put_int(dict, "2.c", 0); + g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); + + qobject_unref(dict); +} + +static void qdict_join_test(void) +{ + QDict *dict1, *dict2; + bool overwrite = false; + int i; + + dict1 = qdict_new(); + dict2 = qdict_new(); + + /* Test everything once without overwrite and once with */ + do { + /* Test empty dicts */ + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 0); + g_assert(qdict_size(dict2) == 0); + + /* First iteration: Test movement */ + /* Second iteration: Test empty source and non-empty destination */ + qdict_put_int(dict2, "foo", 42); + + for (i = 0; i < 2; i++) { + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 1); + g_assert(qdict_size(dict2) == 0); + + g_assert(qdict_get_int(dict1, "foo") == 42); + } + + /* Test non-empty source and destination without conflict */ + qdict_put_int(dict2, "bar", 23); + + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 2); + g_assert(qdict_size(dict2) == 0); + + g_assert(qdict_get_int(dict1, "foo") == 42); + g_assert(qdict_get_int(dict1, "bar") == 23); + + /* Test conflict */ + qdict_put_int(dict2, "foo", 84); + + qdict_join(dict1, dict2, overwrite); + + g_assert(qdict_size(dict1) == 2); + g_assert(qdict_size(dict2) == !overwrite); + + g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); + g_assert(qdict_get_int(dict1, "bar") == 23); + + if (!overwrite) { + g_assert(qdict_get_int(dict2, "foo") == 84); + } + + /* Check the references */ + g_assert(qdict_get(dict1, "foo")->base.refcnt == 1); + g_assert(qdict_get(dict1, "bar")->base.refcnt == 1); + + if (!overwrite) { + g_assert(qdict_get(dict2, "foo")->base.refcnt == 1); + } + + /* Clean up */ + qdict_del(dict1, "foo"); + qdict_del(dict1, "bar"); + + if (!overwrite) { + qdict_del(dict2, "foo"); + } + } while (overwrite ^= true); + + qobject_unref(dict1); + qobject_unref(dict2); +} + +static void qdict_crumple_test_recursive(void) +{ + QDict *src, *dst, *rule, *vnc, *acl, *listen; + QDict *empty, *empty_dict, *empty_list_0; + QList *rules, *empty_list, *empty_dict_a; + + src = qdict_new(); + qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); + qdict_put_str(src, "vnc.listen.port", "5901"); + qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); + qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); + qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); + qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); + qdict_put_str(src, "vnc.acl.default", "deny"); + qdict_put_str(src, "vnc.acl..name", "acl0"); + qdict_put_str(src, "vnc.acl.rule..name", "acl0"); + qdict_put(src, "empty.dict.a", qlist_new()); + qdict_put(src, "empty.list.0", qdict_new()); + + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); + g_assert(dst); + g_assert_cmpint(qdict_size(dst), ==, 2); + + vnc = qdict_get_qdict(dst, "vnc"); + g_assert(vnc); + g_assert_cmpint(qdict_size(vnc), ==, 3); + + listen = qdict_get_qdict(vnc, "listen"); + g_assert(listen); + g_assert_cmpint(qdict_size(listen), ==, 2); + g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); + g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); + + acl = qdict_get_qdict(vnc, "acl"); + g_assert(acl); + g_assert_cmpint(qdict_size(acl), ==, 3); + + rules = qdict_get_qlist(acl, "rules"); + g_assert(rules); + g_assert_cmpint(qlist_size(rules), ==, 2); + + rule = qobject_to(QDict, qlist_pop(rules)); + g_assert(rule); + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); + qobject_unref(rule); + + rule = qobject_to(QDict, qlist_pop(rules)); + g_assert(rule); + g_assert_cmpint(qdict_size(rule), ==, 2); + g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); + g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); + qobject_unref(rule); + + /* With recursive crumpling, we should see all names unescaped */ + g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); + g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); + + empty = qdict_get_qdict(dst, "empty"); + g_assert(empty); + g_assert_cmpint(qdict_size(empty), ==, 2); + empty_dict = qdict_get_qdict(empty, "dict"); + g_assert(empty_dict); + g_assert_cmpint(qdict_size(empty_dict), ==, 1); + empty_dict_a = qdict_get_qlist(empty_dict, "a"); + g_assert(empty_dict_a && qlist_empty(empty_dict_a)); + empty_list = qdict_get_qlist(empty, "list"); + g_assert(empty_list); + g_assert_cmpint(qlist_size(empty_list), ==, 1); + empty_list_0 = qobject_to(QDict, qlist_pop(empty_list)); + g_assert(empty_list_0); + g_assert_cmpint(qdict_size(empty_list_0), ==, 0); + + qobject_unref(src); + qobject_unref(dst); +} + +static void qdict_crumple_test_empty(void) +{ + QDict *src, *dst; + + src = qdict_new(); + + dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); + + g_assert_cmpint(qdict_size(dst), ==, 0); + + qobject_unref(src); + qobject_unref(dst); +} + +static int qdict_count_entries(QDict *dict) +{ + const QDictEntry *e; + int count = 0; + + for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { + count++; + } + + return count; +} + +static void qdict_rename_keys_test(void) +{ + QDict *dict = qdict_new(); + QDict *copy; + QDictRenames *renames; + Error *local_err = NULL; + + qdict_put_str(dict, "abc", "foo"); + qdict_put_str(dict, "abcdef", "bar"); + qdict_put_int(dict, "number", 42); + qdict_put_bool(dict, "flag", true); + qdict_put_null(dict, "nothing"); + + /* Empty rename list */ + renames = (QDictRenames[]) { + { NULL, "this can be anything" } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Simple rename of all entries */ + renames = (QDictRenames[]) { + { "abc", "str1" }, + { "abcdef", "str2" }, + { "number", "int" }, + { "flag", "bool" }, + { "nothing", "null" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert(!qdict_haskey(copy, "abc")); + g_assert(!qdict_haskey(copy, "abcdef")); + g_assert(!qdict_haskey(copy, "number")); + g_assert(!qdict_haskey(copy, "flag")); + g_assert(!qdict_haskey(copy, "nothing")); + + g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); + g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Renames are processed top to bottom */ + renames = (QDictRenames[]) { + { "abc", "tmp" }, + { "abcdef", "abc" }, + { "number", "abcdef" }, + { "flag", "number" }, + { "nothing", "flag" }, + { "tmp", "nothing" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &error_abort); + + g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); + g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); + g_assert(!qdict_haskey(copy, "tmp")); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Conflicting rename */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + copy = qdict_clone_shallow(dict); + qdict_rename_keys(copy, renames, &local_err); + + g_assert(local_err != NULL); + error_free(local_err); + local_err = NULL; + + g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); + g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); + g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); + g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); + g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); + g_assert_cmpint(qdict_count_entries(copy), ==, 5); + + qobject_unref(copy); + + /* Renames in an empty dict */ + renames = (QDictRenames[]) { + { "abcdef", "abc" }, + { NULL , NULL } + }; + + qobject_unref(dict); + dict = qdict_new(); + + qdict_rename_keys(dict, renames, &error_abort); + g_assert(qdict_first(dict) == NULL); + + qobject_unref(dict); +} + +static void qdict_crumple_test_bad_inputs(void) +{ + QDict *src, *nested; + Error *error = NULL; + + src = qdict_new(); + /* rule.0 can't be both a string and a dict */ + qdict_put_str(src, "rule.0", "fred"); + qdict_put_str(src, "rule.0.policy", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); + + src = qdict_new(); + /* rule can't be both a list and a dict */ + qdict_put_str(src, "rule.0", "fred"); + qdict_put_str(src, "rule.a", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); + + src = qdict_new(); + /* The input should be flat, ie no dicts or lists */ + nested = qdict_new(); + qdict_put(nested, "x", qdict_new()); + qdict_put(src, "rule.a", nested); + qdict_put_str(src, "rule.b", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); + + src = qdict_new(); + /* List indexes must not have gaps */ + qdict_put_str(src, "rule.0", "deny"); + qdict_put_str(src, "rule.3", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); + + src = qdict_new(); + /* List indexes must be in %zu format */ + qdict_put_str(src, "rule.0", "deny"); + qdict_put_str(src, "rule.+1", "allow"); + + g_assert(qdict_crumple(src, &error) == NULL); + g_assert(error != NULL); + error_free(error); + error = NULL; + qobject_unref(src); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/public/defaults", qdict_defaults_test); + g_test_add_func("/public/flatten", qdict_flatten_test); + g_test_add_func("/public/clone_flatten", qdict_clone_flatten_test); + g_test_add_func("/public/array_split", qdict_array_split_test); + g_test_add_func("/public/array_entries", qdict_array_entries_test); + g_test_add_func("/public/join", qdict_join_test); + g_test_add_func("/public/crumple/recursive", + qdict_crumple_test_recursive); + g_test_add_func("/public/crumple/empty", + qdict_crumple_test_empty); + g_test_add_func("/public/crumple/bad_inputs", + qdict_crumple_test_bad_inputs); + + g_test_add_func("/public/rename_keys", qdict_rename_keys_test); + + return g_test_run(); +} diff --git a/tests/check-qdict.c b/tests/check-qdict.c index 2e73c2f86e..86e9fe7dc4 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -12,11 +12,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qapi/error.h" -#include "qemu-common.h" /* * Public Interface test-cases @@ -34,7 +29,7 @@ static void qdict_new_test(void) g_assert(qdict->base.refcnt == 1); g_assert(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT); - QDECREF(qdict); + qobject_unref(qdict); } static void qdict_put_obj_test(void) @@ -54,7 +49,7 @@ static void qdict_put_obj_test(void) qn = qobject_to(QNum, ent->value); g_assert_cmpint(qnum_get_int(qn), ==, num); - QDECREF(qdict); + qobject_unref(qdict); } static void qdict_destroy_simple_test(void) @@ -65,7 +60,7 @@ static void qdict_destroy_simple_test(void) qdict_put_int(qdict, "num", 0); qdict_put_str(qdict, "str", "foo"); - QDECREF(qdict); + qobject_unref(qdict); } static void qdict_get_test(void) @@ -84,7 +79,7 @@ static void qdict_get_test(void) qn = qobject_to(QNum, obj); g_assert_cmpint(qnum_get_int(qn), ==, value); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_int_test(void) @@ -99,7 +94,7 @@ static void qdict_get_int_test(void) ret = qdict_get_int(tests_dict, key); g_assert(ret == value); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_try_int_test(void) @@ -121,7 +116,7 @@ static void qdict_get_try_int_test(void) ret = qdict_get_try_int(tests_dict, "string", -42); g_assert_cmpuint(ret, ==, -42); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_str_test(void) @@ -137,7 +132,7 @@ static void qdict_get_str_test(void) g_assert(p != NULL); g_assert(strcmp(p, str) == 0); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_try_str_test(void) @@ -153,29 +148,7 @@ static void qdict_get_try_str_test(void) g_assert(p != NULL); g_assert(strcmp(p, str) == 0); - QDECREF(tests_dict); -} - -static void qdict_defaults_test(void) -{ - QDict *dict, *copy; - - dict = qdict_new(); - copy = qdict_new(); - - qdict_set_default_str(dict, "foo", "abc"); - qdict_set_default_str(dict, "foo", "def"); - g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "abc"); - qdict_set_default_str(dict, "bar", "ghi"); - - qdict_copy_default(copy, dict, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "foo"), ==, "abc"); - qdict_set_default_str(copy, "bar", "xyz"); - qdict_copy_default(copy, dict, "bar"); - g_assert_cmpstr(qdict_get_str(copy, "bar"), ==, "xyz"); - - QDECREF(copy); - QDECREF(dict); + qobject_unref(tests_dict); } static void qdict_haskey_not_test(void) @@ -183,7 +156,7 @@ static void qdict_haskey_not_test(void) QDict *tests_dict = qdict_new(); g_assert(qdict_haskey(tests_dict, "test") == 0); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_haskey_test(void) @@ -194,7 +167,7 @@ static void qdict_haskey_test(void) qdict_put_int(tests_dict, key, 0); g_assert(qdict_haskey(tests_dict, key) == 1); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_del_test(void) @@ -210,7 +183,7 @@ static void qdict_del_test(void) g_assert(qdict_size(tests_dict) == 0); g_assert(qdict_haskey(tests_dict, key) == 0); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qobject_to_qdict_test(void) @@ -218,7 +191,7 @@ static void qobject_to_qdict_test(void) QDict *tests_dict = qdict_new(); g_assert(qobject_to(QDict, QOBJECT(tests_dict)) == tests_dict); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_iterapi_test(void) @@ -250,607 +223,7 @@ static void qdict_iterapi_test(void) g_assert(count == qdict_size(tests_dict)); - QDECREF(tests_dict); -} - -static void qdict_flatten_test(void) -{ - QList *list1 = qlist_new(); - QList *list2 = qlist_new(); - QDict *dict1 = qdict_new(); - QDict *dict2 = qdict_new(); - QDict *dict3 = qdict_new(); - - /* - * Test the flattening of - * - * { - * "e": [ - * 42, - * [ - * 23, - * 66, - * { - * "a": 0, - * "b": 1 - * } - * ] - * ], - * "f": { - * "c": 2, - * "d": 3, - * }, - * "g": 4 - * } - * - * to - * - * { - * "e.0": 42, - * "e.1.0": 23, - * "e.1.1": 66, - * "e.1.2.a": 0, - * "e.1.2.b": 1, - * "f.c": 2, - * "f.d": 3, - * "g": 4 - * } - */ - - qdict_put_int(dict1, "a", 0); - qdict_put_int(dict1, "b", 1); - - qlist_append_int(list1, 23); - qlist_append_int(list1, 66); - qlist_append(list1, dict1); - qlist_append_int(list2, 42); - qlist_append(list2, list1); - - qdict_put_int(dict2, "c", 2); - qdict_put_int(dict2, "d", 3); - qdict_put(dict3, "e", list2); - qdict_put(dict3, "f", dict2); - qdict_put_int(dict3, "g", 4); - - qdict_flatten(dict3); - - g_assert(qdict_get_int(dict3, "e.0") == 42); - g_assert(qdict_get_int(dict3, "e.1.0") == 23); - g_assert(qdict_get_int(dict3, "e.1.1") == 66); - g_assert(qdict_get_int(dict3, "e.1.2.a") == 0); - g_assert(qdict_get_int(dict3, "e.1.2.b") == 1); - g_assert(qdict_get_int(dict3, "f.c") == 2); - g_assert(qdict_get_int(dict3, "f.d") == 3); - g_assert(qdict_get_int(dict3, "g") == 4); - - g_assert(qdict_size(dict3) == 8); - - QDECREF(dict3); -} - -static void qdict_array_split_test(void) -{ - QDict *test_dict = qdict_new(); - QDict *dict1, *dict2; - QNum *int1; - QList *test_list; - - /* - * Test the split of - * - * { - * "1.x": 0, - * "4.y": 1, - * "0.a": 42, - * "o.o": 7, - * "0.b": 23, - * "2": 66 - * } - * - * to - * - * [ - * { - * "a": 42, - * "b": 23 - * }, - * { - * "x": 0 - * }, - * 66 - * ] - * - * and - * - * { - * "4.y": 1, - * "o.o": 7 - * } - * - * (remaining in the old QDict) - * - * This example is given in the comment of qdict_array_split(). - */ - - qdict_put_int(test_dict, "1.x", 0); - qdict_put_int(test_dict, "4.y", 1); - qdict_put_int(test_dict, "0.a", 42); - qdict_put_int(test_dict, "o.o", 7); - qdict_put_int(test_dict, "0.b", 23); - qdict_put_int(test_dict, "2", 66); - - qdict_array_split(test_dict, &test_list); - - dict1 = qobject_to(QDict, qlist_pop(test_list)); - dict2 = qobject_to(QDict, qlist_pop(test_list)); - int1 = qobject_to(QNum, qlist_pop(test_list)); - - g_assert(dict1); - g_assert(dict2); - g_assert(int1); - g_assert(qlist_empty(test_list)); - - QDECREF(test_list); - - g_assert(qdict_get_int(dict1, "a") == 42); - g_assert(qdict_get_int(dict1, "b") == 23); - - g_assert(qdict_size(dict1) == 2); - - QDECREF(dict1); - - g_assert(qdict_get_int(dict2, "x") == 0); - - g_assert(qdict_size(dict2) == 1); - - QDECREF(dict2); - - g_assert_cmpint(qnum_get_int(int1), ==, 66); - - QDECREF(int1); - - g_assert(qdict_get_int(test_dict, "4.y") == 1); - g_assert(qdict_get_int(test_dict, "o.o") == 7); - - g_assert(qdict_size(test_dict) == 2); - - QDECREF(test_dict); - - /* - * Test the split of - * - * { - * "0": 42, - * "1": 23, - * "1.x": 84 - * } - * - * to - * - * [ - * 42 - * ] - * - * and - * - * { - * "1": 23, - * "1.x": 84 - * } - * - * That is, test whether splitting stops if there is both an entry with key - * of "%u" and other entries with keys prefixed "%u." for the same index. - */ - - test_dict = qdict_new(); - - qdict_put_int(test_dict, "0", 42); - qdict_put_int(test_dict, "1", 23); - qdict_put_int(test_dict, "1.x", 84); - - qdict_array_split(test_dict, &test_list); - - int1 = qobject_to(QNum, qlist_pop(test_list)); - - g_assert(int1); - g_assert(qlist_empty(test_list)); - - QDECREF(test_list); - - g_assert_cmpint(qnum_get_int(int1), ==, 42); - - QDECREF(int1); - - g_assert(qdict_get_int(test_dict, "1") == 23); - g_assert(qdict_get_int(test_dict, "1.x") == 84); - - g_assert(qdict_size(test_dict) == 2); - - QDECREF(test_dict); -} - -static void qdict_array_entries_test(void) -{ - QDict *dict = qdict_new(); - - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); - - qdict_put_int(dict, "bar", 0); - qdict_put_int(dict, "baz.0", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 0); - - qdict_put_int(dict, "foo.1", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); - qdict_put_int(dict, "foo.0", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 2); - qdict_put_int(dict, "foo.bar", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, -EINVAL); - qdict_del(dict, "foo.bar"); - - qdict_put_int(dict, "foo.2.a", 0); - qdict_put_int(dict, "foo.2.b", 0); - qdict_put_int(dict, "foo.2.c", 0); - g_assert_cmpint(qdict_array_entries(dict, "foo."), ==, 3); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - - QDECREF(dict); - - dict = qdict_new(); - qdict_put_int(dict, "1", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - qdict_put_int(dict, "0", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, 2); - qdict_put_int(dict, "bar", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, -EINVAL); - qdict_del(dict, "bar"); - - qdict_put_int(dict, "2.a", 0); - qdict_put_int(dict, "2.b", 0); - qdict_put_int(dict, "2.c", 0); - g_assert_cmpint(qdict_array_entries(dict, ""), ==, 3); - - QDECREF(dict); -} - -static void qdict_join_test(void) -{ - QDict *dict1, *dict2; - bool overwrite = false; - int i; - - dict1 = qdict_new(); - dict2 = qdict_new(); - - /* Test everything once without overwrite and once with */ - do - { - /* Test empty dicts */ - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 0); - g_assert(qdict_size(dict2) == 0); - - /* First iteration: Test movement */ - /* Second iteration: Test empty source and non-empty destination */ - qdict_put_int(dict2, "foo", 42); - - for (i = 0; i < 2; i++) { - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 1); - g_assert(qdict_size(dict2) == 0); - - g_assert(qdict_get_int(dict1, "foo") == 42); - } - - /* Test non-empty source and destination without conflict */ - qdict_put_int(dict2, "bar", 23); - - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 2); - g_assert(qdict_size(dict2) == 0); - - g_assert(qdict_get_int(dict1, "foo") == 42); - g_assert(qdict_get_int(dict1, "bar") == 23); - - /* Test conflict */ - qdict_put_int(dict2, "foo", 84); - - qdict_join(dict1, dict2, overwrite); - - g_assert(qdict_size(dict1) == 2); - g_assert(qdict_size(dict2) == !overwrite); - - g_assert(qdict_get_int(dict1, "foo") == (overwrite ? 84 : 42)); - g_assert(qdict_get_int(dict1, "bar") == 23); - - if (!overwrite) { - g_assert(qdict_get_int(dict2, "foo") == 84); - } - - /* Check the references */ - g_assert(qdict_get(dict1, "foo")->refcnt == 1); - g_assert(qdict_get(dict1, "bar")->refcnt == 1); - - if (!overwrite) { - g_assert(qdict_get(dict2, "foo")->refcnt == 1); - } - - /* Clean up */ - qdict_del(dict1, "foo"); - qdict_del(dict1, "bar"); - - if (!overwrite) { - qdict_del(dict2, "foo"); - } - } - while (overwrite ^= true); - - QDECREF(dict1); - QDECREF(dict2); -} - -static void qdict_crumple_test_recursive(void) -{ - QDict *src, *dst, *rule, *vnc, *acl, *listen; - QList *rules; - - src = qdict_new(); - qdict_put_str(src, "vnc.listen.addr", "127.0.0.1"); - qdict_put_str(src, "vnc.listen.port", "5901"); - qdict_put_str(src, "vnc.acl.rules.0.match", "fred"); - qdict_put_str(src, "vnc.acl.rules.0.policy", "allow"); - qdict_put_str(src, "vnc.acl.rules.1.match", "bob"); - qdict_put_str(src, "vnc.acl.rules.1.policy", "deny"); - qdict_put_str(src, "vnc.acl.default", "deny"); - qdict_put_str(src, "vnc.acl..name", "acl0"); - qdict_put_str(src, "vnc.acl.rule..name", "acl0"); - - dst = qobject_to(QDict, qdict_crumple(src, &error_abort)); - g_assert(dst); - g_assert_cmpint(qdict_size(dst), ==, 1); - - vnc = qdict_get_qdict(dst, "vnc"); - g_assert(vnc); - g_assert_cmpint(qdict_size(vnc), ==, 3); - - listen = qdict_get_qdict(vnc, "listen"); - g_assert(listen); - g_assert_cmpint(qdict_size(listen), ==, 2); - g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr")); - g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port")); - - acl = qdict_get_qdict(vnc, "acl"); - g_assert(acl); - g_assert_cmpint(qdict_size(acl), ==, 3); - - rules = qdict_get_qlist(acl, "rules"); - g_assert(rules); - g_assert_cmpint(qlist_size(rules), ==, 2); - - rule = qobject_to(QDict, qlist_pop(rules)); - g_assert(rule); - g_assert_cmpint(qdict_size(rule), ==, 2); - g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match")); - g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy")); - QDECREF(rule); - - rule = qobject_to(QDict, qlist_pop(rules)); - g_assert(rule); - g_assert_cmpint(qdict_size(rule), ==, 2); - g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match")); - g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy")); - QDECREF(rule); - - /* With recursive crumpling, we should see all names unescaped */ - g_assert_cmpstr("acl0", ==, qdict_get_str(vnc, "acl.name")); - g_assert_cmpstr("acl0", ==, qdict_get_str(acl, "rule.name")); - - QDECREF(src); - QDECREF(dst); -} - -static void qdict_crumple_test_empty(void) -{ - QDict *src, *dst; - - src = qdict_new(); - - dst = (QDict *)qdict_crumple(src, &error_abort); - - g_assert_cmpint(qdict_size(dst), ==, 0); - - QDECREF(src); - QDECREF(dst); -} - -static int qdict_count_entries(QDict *dict) -{ - const QDictEntry *e; - int count = 0; - - for (e = qdict_first(dict); e; e = qdict_next(dict, e)) { - count++; - } - - return count; -} - -static void qdict_rename_keys_test(void) -{ - QDict *dict = qdict_new(); - QDict *copy; - QDictRenames *renames; - Error *local_err = NULL; - - qdict_put_str(dict, "abc", "foo"); - qdict_put_str(dict, "abcdef", "bar"); - qdict_put_int(dict, "number", 42); - qdict_put_bool(dict, "flag", true); - qdict_put_null(dict, "nothing"); - - /* Empty rename list */ - renames = (QDictRenames[]) { - { NULL, "this can be anything" } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &error_abort); - - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); - g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - QDECREF(copy); - - /* Simple rename of all entries */ - renames = (QDictRenames[]) { - { "abc", "str1" }, - { "abcdef", "str2" }, - { "number", "int" }, - { "flag", "bool" }, - { "nothing", "null" }, - { NULL , NULL } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &error_abort); - - g_assert(!qdict_haskey(copy, "abc")); - g_assert(!qdict_haskey(copy, "abcdef")); - g_assert(!qdict_haskey(copy, "number")); - g_assert(!qdict_haskey(copy, "flag")); - g_assert(!qdict_haskey(copy, "nothing")); - - g_assert_cmpstr(qdict_get_str(copy, "str1"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "str2"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "int"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "bool"), ==, true); - g_assert(qobject_type(qdict_get(copy, "null")) == QTYPE_QNULL); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - QDECREF(copy); - - /* Renames are processed top to bottom */ - renames = (QDictRenames[]) { - { "abc", "tmp" }, - { "abcdef", "abc" }, - { "number", "abcdef" }, - { "flag", "number" }, - { "nothing", "flag" }, - { "tmp", "nothing" }, - { NULL , NULL } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &error_abort); - - g_assert_cmpstr(qdict_get_str(copy, "nothing"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "abcdef"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "number"), ==, true); - g_assert(qobject_type(qdict_get(copy, "flag")) == QTYPE_QNULL); - g_assert(!qdict_haskey(copy, "tmp")); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - QDECREF(copy); - - /* Conflicting rename */ - renames = (QDictRenames[]) { - { "abcdef", "abc" }, - { NULL , NULL } - }; - copy = qdict_clone_shallow(dict); - qdict_rename_keys(copy, renames, &local_err); - - g_assert(local_err != NULL); - error_free(local_err); - local_err = NULL; - - g_assert_cmpstr(qdict_get_str(copy, "abc"), ==, "foo"); - g_assert_cmpstr(qdict_get_str(copy, "abcdef"), ==, "bar"); - g_assert_cmpint(qdict_get_int(copy, "number"), ==, 42); - g_assert_cmpint(qdict_get_bool(copy, "flag"), ==, true); - g_assert(qobject_type(qdict_get(copy, "nothing")) == QTYPE_QNULL); - g_assert_cmpint(qdict_count_entries(copy), ==, 5); - - QDECREF(copy); - - /* Renames in an empty dict */ - renames = (QDictRenames[]) { - { "abcdef", "abc" }, - { NULL , NULL } - }; - - QDECREF(dict); - dict = qdict_new(); - - qdict_rename_keys(dict, renames, &error_abort); - g_assert(qdict_first(dict) == NULL); - - QDECREF(dict); -} - -static void qdict_crumple_test_bad_inputs(void) -{ - QDict *src; - Error *error = NULL; - - src = qdict_new(); - /* rule.0 can't be both a string and a dict */ - qdict_put_str(src, "rule.0", "fred"); - qdict_put_str(src, "rule.0.policy", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - QDECREF(src); - - src = qdict_new(); - /* rule can't be both a list and a dict */ - qdict_put_str(src, "rule.0", "fred"); - qdict_put_str(src, "rule.a", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - QDECREF(src); - - src = qdict_new(); - /* The input should be flat, ie no dicts or lists */ - qdict_put(src, "rule.a", qdict_new()); - qdict_put_str(src, "rule.b", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - QDECREF(src); - - src = qdict_new(); - /* List indexes must not have gaps */ - qdict_put_str(src, "rule.0", "deny"); - qdict_put_str(src, "rule.3", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - QDECREF(src); - - src = qdict_new(); - /* List indexes must be in %zu format */ - qdict_put_str(src, "rule.0", "deny"); - qdict_put_str(src, "rule.+1", "allow"); - - g_assert(qdict_crumple(src, &error) == NULL); - g_assert(error != NULL); - error_free(error); - error = NULL; - QDECREF(src); + qobject_unref(tests_dict); } /* @@ -871,7 +244,7 @@ static void qdict_put_exists_test(void) g_assert(qdict_size(tests_dict) == 1); - QDECREF(tests_dict); + qobject_unref(tests_dict); } static void qdict_get_not_exists_test(void) @@ -879,7 +252,7 @@ static void qdict_get_not_exists_test(void) QDict *tests_dict = qdict_new(); g_assert(qdict_get(tests_dict, "foo") == NULL); - QDECREF(tests_dict); + qobject_unref(tests_dict); } /* @@ -951,7 +324,7 @@ static void qdict_stress_test(void) g_assert(strcmp(str1, str2) == 0); - QDECREF(value); + qobject_unref(value); } // Delete everything @@ -962,14 +335,14 @@ static void qdict_stress_test(void) break; qdict_del(qdict, key); - QDECREF(value); + qobject_unref(value); g_assert(qdict_haskey(qdict, key) == 0); } fclose(test_file); g_assert(qdict_size(qdict) == 0); - QDECREF(qdict); + qobject_unref(qdict); } int main(int argc, char **argv) @@ -986,29 +359,15 @@ int main(int argc, char **argv) g_test_add_func("/public/get_try_int", qdict_get_try_int_test); g_test_add_func("/public/get_str", qdict_get_str_test); g_test_add_func("/public/get_try_str", qdict_get_try_str_test); - g_test_add_func("/public/defaults", qdict_defaults_test); g_test_add_func("/public/haskey_not", qdict_haskey_not_test); g_test_add_func("/public/haskey", qdict_haskey_test); g_test_add_func("/public/del", qdict_del_test); g_test_add_func("/public/to_qdict", qobject_to_qdict_test); g_test_add_func("/public/iterapi", qdict_iterapi_test); - g_test_add_func("/public/flatten", qdict_flatten_test); - g_test_add_func("/public/array_split", qdict_array_split_test); - g_test_add_func("/public/array_entries", qdict_array_entries_test); - g_test_add_func("/public/join", qdict_join_test); g_test_add_func("/errors/put_exists", qdict_put_exists_test); g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test); - g_test_add_func("/public/crumple/recursive", - qdict_crumple_test_recursive); - g_test_add_func("/public/crumple/empty", - qdict_crumple_test_empty); - g_test_add_func("/public/crumple/bad_inputs", - qdict_crumple_test_bad_inputs); - - g_test_add_func("/public/rename_keys", qdict_rename_keys_test); - /* The Big one */ if (g_test_slow()) { g_test_add_func("/stress/test", qdict_stress_test); diff --git a/tests/check-qjson.c b/tests/check-qjson.c index 997f4d3d2c..da582df3e9 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -67,10 +67,10 @@ static void escaped_string(void) if (test_cases[i].skip == 0) { str = qobject_to_json(obj); g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded); - qobject_decref(obj); + qobject_unref(obj); } - QDECREF(str); + qobject_unref(str); } } @@ -99,9 +99,9 @@ static void simple_string(void) str = qobject_to_json(obj); g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); - qobject_decref(obj); + qobject_unref(obj); - QDECREF(str); + qobject_unref(str); } } @@ -127,7 +127,7 @@ static void single_quote_string(void) g_assert(str); g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - QDECREF(str); + qobject_unref(str); } } @@ -823,7 +823,7 @@ static void utf8_string(void) } else { g_assert(!obj); } - qobject_decref(obj); + qobject_unref(obj); obj = QOBJECT(qstring_from_str(utf8_in)); str = qobject_to_json(obj); @@ -833,8 +833,8 @@ static void utf8_string(void) } else { g_assert(!str); } - QDECREF(str); - qobject_decref(obj); + qobject_unref(str); + qobject_unref(obj); /* * Disabled, because qobject_from_json() is buggy, and I can't @@ -869,7 +869,7 @@ static void vararg_string(void) g_assert(str); g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - QDECREF(str); + qobject_unref(str); } } @@ -904,10 +904,10 @@ static void simple_number(void) str = qobject_to_json(QOBJECT(qnum)); g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); - QDECREF(str); + qobject_unref(str); } - QDECREF(qnum); + qobject_unref(qnum); } } @@ -928,8 +928,8 @@ static void large_number(void) str = qobject_to_json(QOBJECT(qnum)); g_assert_cmpstr(qstring_get_str(str), ==, maxu64); - QDECREF(str); - QDECREF(qnum); + qobject_unref(str); + qobject_unref(qnum); qnum = qobject_to(QNum, qobject_from_json(gtu64, &error_abort)); g_assert(qnum); @@ -939,8 +939,8 @@ static void large_number(void) str = qobject_to_json(QOBJECT(qnum)); g_assert_cmpstr(qstring_get_str(str), ==, gtu64); - QDECREF(str); - QDECREF(qnum); + qobject_unref(str); + qobject_unref(qnum); qnum = qobject_to(QNum, qobject_from_json(lti64, &error_abort)); g_assert(qnum); @@ -950,8 +950,8 @@ static void large_number(void) str = qobject_to_json(QOBJECT(qnum)); g_assert_cmpstr(qstring_get_str(str), ==, "-9223372036854775808"); - QDECREF(str); - QDECREF(qnum); + qobject_unref(str); + qobject_unref(qnum); } static void float_number(void) @@ -983,10 +983,10 @@ static void float_number(void) str = qobject_to_json(obj); g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); - QDECREF(str); + qobject_unref(str); } - QDECREF(qnum); + qobject_unref(qnum); } } @@ -1001,16 +1001,16 @@ static void vararg_number(void) qnum = qobject_to(QNum, qobject_from_jsonf("%d", value)); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, value); - QDECREF(qnum); + qobject_unref(qnum); qnum = qobject_to(QNum, qobject_from_jsonf("%lld", value_ll)); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, value_ll); - QDECREF(qnum); + qobject_unref(qnum); qnum = qobject_to(QNum, qobject_from_jsonf("%f", valuef)); g_assert(qnum_get_double(qnum) == valuef); - QDECREF(qnum); + qobject_unref(qnum); } static void keyword_literal(void) @@ -1027,9 +1027,9 @@ static void keyword_literal(void) str = qobject_to_json(obj); g_assert(strcmp(qstring_get_str(str), "true") == 0); - QDECREF(str); + qobject_unref(str); - QDECREF(qbool); + qobject_unref(qbool); obj = qobject_from_json("false", &error_abort); qbool = qobject_to(QBool, obj); @@ -1038,20 +1038,20 @@ static void keyword_literal(void) str = qobject_to_json(obj); g_assert(strcmp(qstring_get_str(str), "false") == 0); - QDECREF(str); + qobject_unref(str); - QDECREF(qbool); + qobject_unref(qbool); qbool = qobject_to(QBool, qobject_from_jsonf("%i", false)); g_assert(qbool); g_assert(qbool_get_bool(qbool) == false); - QDECREF(qbool); + qobject_unref(qbool); /* Test that non-zero values other than 1 get collapsed to true */ qbool = qobject_to(QBool, qobject_from_jsonf("%i", 2)); g_assert(qbool); g_assert(qbool_get_bool(qbool) == true); - QDECREF(qbool); + qobject_unref(qbool); obj = qobject_from_json("null", &error_abort); g_assert(obj != NULL); @@ -1060,8 +1060,8 @@ static void keyword_literal(void) null = qnull(); g_assert(QOBJECT(null) == obj); - qobject_decref(obj); - QDECREF(null); + qobject_unref(obj); + qobject_unref(null); } static void simple_dict(void) @@ -1101,12 +1101,12 @@ static void simple_dict(void) g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); str = qobject_to_json(obj); - qobject_decref(obj); + qobject_unref(obj); obj = qobject_from_json(qstring_get_str(str), &error_abort); g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - qobject_decref(obj); - QDECREF(str); + qobject_unref(obj); + qobject_unref(str); } } @@ -1158,7 +1158,7 @@ static void large_dict(void) obj = qobject_from_json(gstr->str, &error_abort); g_assert(obj != NULL); - qobject_decref(obj); + qobject_unref(obj); g_string_free(gstr, true); } @@ -1210,12 +1210,12 @@ static void simple_list(void) g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); str = qobject_to_json(obj); - qobject_decref(obj); + qobject_unref(obj); obj = qobject_from_json(qstring_get_str(str), &error_abort); g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - qobject_decref(obj); - QDECREF(str); + qobject_unref(obj); + qobject_unref(str); } } @@ -1272,13 +1272,13 @@ static void simple_whitespace(void) g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); str = qobject_to_json(obj); - qobject_decref(obj); + qobject_unref(obj); obj = qobject_from_json(qstring_get_str(str), &error_abort); g_assert(qlit_equal_qobject(&test_cases[i].decoded, obj)); - qobject_decref(obj); - QDECREF(str); + qobject_unref(obj); + qobject_unref(str); } } @@ -1301,7 +1301,7 @@ static void simple_varargs(void) obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj); g_assert(qlit_equal_qobject(&decoded, obj)); - qobject_decref(obj); + qobject_unref(obj); } static void empty_input(void) @@ -1410,7 +1410,7 @@ static void limits_nesting(void) obj = qobject_from_json(make_nest(buf, max_nesting), &error_abort); g_assert(obj != NULL); - qobject_decref(obj); + qobject_unref(obj); obj = qobject_from_json(make_nest(buf, max_nesting + 1), &err); error_free_or_abort(&err); diff --git a/tests/check-qlist.c b/tests/check-qlist.c index a1c69ed648..ece83e293d 100644 --- a/tests/check-qlist.c +++ b/tests/check-qlist.c @@ -29,7 +29,7 @@ static void qlist_new_test(void) g_assert(qlist->base.refcnt == 1); g_assert(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST); - QDECREF(qlist); + qobject_unref(qlist); } static void qlist_append_test(void) @@ -47,7 +47,7 @@ static void qlist_append_test(void) g_assert(entry != NULL); g_assert(entry->value == QOBJECT(qi)); - QDECREF(qlist); + qobject_unref(qlist); } static void qobject_to_qlist_test(void) @@ -58,7 +58,7 @@ static void qobject_to_qlist_test(void) g_assert(qobject_to(QList, QOBJECT(qlist)) == qlist); - QDECREF(qlist); + qobject_unref(qlist); } static int iter_called; @@ -96,7 +96,7 @@ static void qlist_iter_test(void) g_assert(iter_called == iter_max); - QDECREF(qlist); + qobject_unref(qlist); } int main(int argc, char **argv) diff --git a/tests/check-qlit.c b/tests/check-qlit.c index 96bbb06f2c..bd6798d912 100644 --- a/tests/check-qlit.c +++ b/tests/check-qlit.c @@ -62,7 +62,7 @@ static void qlit_equal_qobject_test(void) qdict_put(qobject_to(QDict, qobj), "bee", qlist_new()); g_assert(!qlit_equal_qobject(&qlit, qobj)); - qobject_decref(qobj); + qobject_unref(qobj); } static void qobject_from_qlit_test(void) @@ -79,15 +79,15 @@ static void qobject_from_qlit_test(void) bee = qdict_get_qlist(qdict, "bee"); obj = qlist_pop(bee); g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 43); - qobject_decref(obj); + qobject_unref(obj); obj = qlist_pop(bee); g_assert_cmpint(qnum_get_int(qobject_to(QNum, obj)), ==, 44); - qobject_decref(obj); + qobject_unref(obj); obj = qlist_pop(bee); g_assert(qbool_get_bool(qobject_to(QBool, obj))); - qobject_decref(obj); + qobject_unref(obj); - qobject_decref(qobj); + qobject_unref(qobj); } int main(int argc, char **argv) diff --git a/tests/check-qnull.c b/tests/check-qnull.c index afa4400da1..ebf21db83c 100644 --- a/tests/check-qnull.c +++ b/tests/check-qnull.c @@ -30,7 +30,7 @@ static void qnull_ref_test(void) g_assert(obj == QOBJECT(&qnull_)); g_assert(qnull_.base.refcnt == 2); g_assert(qobject_type(obj) == QTYPE_QNULL); - qobject_decref(obj); + qobject_unref(obj); g_assert(qnull_.base.refcnt == 1); } @@ -49,10 +49,10 @@ static void qnull_visit_test(void) g_assert(qnull_.base.refcnt == 1); obj = QOBJECT(qnull()); v = qobject_input_visitor_new(obj); - qobject_decref(obj); + qobject_unref(obj); visit_type_null(v, NULL, &null, &error_abort); g_assert(obj == QOBJECT(&qnull_)); - QDECREF(null); + qobject_unref(null); visit_free(v); null = NULL; @@ -60,8 +60,8 @@ static void qnull_visit_test(void) visit_type_null(v, NULL, &null, &error_abort); visit_complete(v, &obj); g_assert(obj == QOBJECT(&qnull_)); - QDECREF(null); - qobject_decref(obj); + qobject_unref(null); + qobject_unref(obj); visit_free(v); g_assert(qnull_.base.refcnt == 1); diff --git a/tests/check-qnum.c b/tests/check-qnum.c index 9187da734b..4105015872 100644 --- a/tests/check-qnum.c +++ b/tests/check-qnum.c @@ -35,7 +35,7 @@ static void qnum_from_int_test(void) g_assert_cmpint(qn->base.refcnt, ==, 1); g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); - QDECREF(qn); + qobject_unref(qn); } static void qnum_from_uint_test(void) @@ -50,7 +50,7 @@ static void qnum_from_uint_test(void) g_assert(qn->base.refcnt == 1); g_assert(qobject_type(QOBJECT(qn)) == QTYPE_QNUM); - QDECREF(qn); + qobject_unref(qn); } static void qnum_from_double_test(void) @@ -65,7 +65,7 @@ static void qnum_from_double_test(void) g_assert_cmpint(qn->base.refcnt, ==, 1); g_assert_cmpint(qobject_type(QOBJECT(qn)), ==, QTYPE_QNUM); - QDECREF(qn); + qobject_unref(qn); } static void qnum_from_int64_test(void) @@ -76,7 +76,7 @@ static void qnum_from_int64_test(void) qn = qnum_from_int(value); g_assert_cmpint((int64_t) qn->u.i64, ==, value); - QDECREF(qn); + qobject_unref(qn); } static void qnum_get_int_test(void) @@ -87,7 +87,7 @@ static void qnum_get_int_test(void) qn = qnum_from_int(value); g_assert_cmpint(qnum_get_int(qn), ==, value); - QDECREF(qn); + qobject_unref(qn); } static void qnum_get_uint_test(void) @@ -100,25 +100,25 @@ static void qnum_get_uint_test(void) qn = qnum_from_uint(value); g_assert(qnum_get_try_uint(qn, &val)); g_assert_cmpuint(val, ==, value); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_int(value); g_assert(qnum_get_try_uint(qn, &val)); g_assert_cmpuint(val, ==, value); - QDECREF(qn); + qobject_unref(qn); /* invalid cases */ qn = qnum_from_int(-1); g_assert(!qnum_get_try_uint(qn, &val)); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_uint(-1ULL); g_assert(!qnum_get_try_int(qn, &ival)); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_double(0.42); g_assert(!qnum_get_try_uint(qn, &val)); - QDECREF(qn); + qobject_unref(qn); } static void qobject_to_qnum_test(void) @@ -127,11 +127,11 @@ static void qobject_to_qnum_test(void) qn = qnum_from_int(0); g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_double(0); g_assert(qobject_to(QNum, QOBJECT(qn)) == qn); - QDECREF(qn); + qobject_unref(qn); } static void qnum_to_string_test(void) @@ -143,13 +143,13 @@ static void qnum_to_string_test(void) tmp = qnum_to_string(qn); g_assert_cmpstr(tmp, ==, "123456"); g_free(tmp); - QDECREF(qn); + qobject_unref(qn); qn = qnum_from_double(0.42); tmp = qnum_to_string(qn); g_assert_cmpstr(tmp, ==, "0.42"); g_free(tmp); - QDECREF(qn); + qobject_unref(qn); } int main(int argc, char **argv) diff --git a/tests/check-qobject.c b/tests/check-qobject.c index 7629b8071b..593c3a0618 100644 --- a/tests/check-qobject.c +++ b/tests/check-qobject.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "block/qdict.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" @@ -80,7 +81,7 @@ static void do_free_all(int _, ...) va_start(ap, _); while ((obj = va_arg(ap, QObject *)) != NULL) { - qobject_decref(obj); + qobject_unref(obj); } va_end(ap); } @@ -153,7 +154,7 @@ static void qobject_is_equal_string_test(void) str_case = qstring_from_str("Foo"); /* Should yield "foo" */ - str_built = qstring_from_substr("form", 0, 1); + str_built = qstring_from_substr("form", 0, 2); qstring_append_chr(str_built, 'o'); check_unequal(str_base, str_whitespace_0, str_whitespace_1, diff --git a/tests/check-qstring.c b/tests/check-qstring.c index 9c4dd3f94f..2d079921e3 100644 --- a/tests/check-qstring.c +++ b/tests/check-qstring.c @@ -31,7 +31,7 @@ static void qstring_from_str_test(void) g_assert(strcmp(str, qstring->string) == 0); g_assert(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING); - QDECREF(qstring); + qobject_unref(qstring); } static void qstring_get_str_test(void) @@ -44,7 +44,7 @@ static void qstring_get_str_test(void) ret_str = qstring_get_str(qstring); g_assert(strcmp(ret_str, str) == 0); - QDECREF(qstring); + qobject_unref(qstring); } static void qstring_append_chr_test(void) @@ -59,18 +59,18 @@ static void qstring_append_chr_test(void) qstring_append_chr(qstring, str[i]); g_assert(strcmp(str, qstring_get_str(qstring)) == 0); - QDECREF(qstring); + qobject_unref(qstring); } static void qstring_from_substr_test(void) { QString *qs; - qs = qstring_from_substr("virtualization", 3, 9); + qs = qstring_from_substr("virtualization", 3, 10); g_assert(qs != NULL); g_assert(strcmp(qstring_get_str(qs), "tualiza") == 0); - QDECREF(qs); + qobject_unref(qs); } @@ -81,7 +81,7 @@ static void qobject_to_qstring_test(void) qstring = qstring_from_str("foo"); g_assert(qobject_to(QString, QOBJECT(qstring)) == qstring); - QDECREF(qstring); + qobject_unref(qstring); } int main(int argc, char **argv) diff --git a/tests/cpu-plug-test.c b/tests/cpu-plug-test.c index 8b5ab1fd02..5f39ba0df3 100644 --- a/tests/cpu-plug-test.c +++ b/tests/cpu-plug-test.c @@ -42,7 +42,7 @@ static void test_plug_with_cpu_add(gconstpointer data) " 'arguments': { 'id': %d } }", i); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } qtest_end(); @@ -66,7 +66,7 @@ static void test_plug_without_cpu_add(gconstpointer data) s->sockets * s->cores * s->threads); g_assert(response); g_assert(qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); qtest_end(); g_free(args); diff --git a/tests/crypto-tls-psk-helpers.c b/tests/crypto-tls-psk-helpers.c new file mode 100644 index 0000000000..a8395477c3 --- /dev/null +++ b/tests/crypto-tls-psk-helpers.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Richard W.M. Jones + */ + +#include "qemu/osdep.h" + +/* Include this first because it defines QCRYPTO_HAVE_TLS_TEST_SUPPORT */ +#include "crypto-tls-x509-helpers.h" + +#include "crypto-tls-psk-helpers.h" +#include "qemu/sockets.h" + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT + +void test_tls_psk_init(const char *pskfile) +{ + FILE *fp; + + fp = fopen(pskfile, "w"); + if (fp == NULL) { + g_critical("Failed to create pskfile %s", pskfile); + abort(); + } + /* Don't do this in real applications! Use psktool. */ + fprintf(fp, "qemu:009d5638c40fde0c\n"); + fclose(fp); +} + +void test_tls_psk_cleanup(const char *pskfile) +{ + unlink(pskfile); +} + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/crypto-tls-psk-helpers.h b/tests/crypto-tls-psk-helpers.h new file mode 100644 index 0000000000..9aec29f1a0 --- /dev/null +++ b/tests/crypto-tls-psk-helpers.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2015-2018 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Richard W.M. Jones + */ + +#include + +#ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT +# include "qemu-common.h" + +void test_tls_psk_init(const char *keyfile); +void test_tls_psk_cleanup(const char *keyfile); + +#endif /* QCRYPTO_HAVE_TLS_TEST_SUPPORT */ diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c index 173d4e28fb..9b669c2a4b 100644 --- a/tests/crypto-tls-x509-helpers.c +++ b/tests/crypto-tls-x509-helpers.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "crypto-tls-x509-helpers.h" +#include "crypto/init.h" #include "qemu/sockets.h" #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT @@ -95,7 +96,7 @@ static gnutls_x509_privkey_t test_tls_load_key(void) void test_tls_init(const char *keyfile) { - gnutls_global_init(); + qcrypto_init(&error_abort); if (asn1_array2tree(pkix_asn1_tab, &pkix_asn1, NULL) != ASN1_SUCCESS) { abort(); diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index a01321aced..0b4f221c29 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -40,8 +40,8 @@ static QList *qom_list_types(const char *implements, bool abstract) " 'arguments': %p }", args); g_assert(qdict_haskey(resp, "return")); ret = qdict_get_qlist(resp, "return"); - QINCREF(ret); - QDECREF(resp); + qobject_ref(ret); + qobject_unref(resp); return ret; } @@ -54,7 +54,7 @@ static QDict *qom_type_index(QList *types) QLIST_FOREACH_ENTRY(types, e) { QDict *d = qobject_to(QDict, qlist_entry_obj(e)); const char *name = qdict_get_str(d, "name"); - QINCREF(d); + qobject_ref(d); qdict_put(index, name, d); } return index; @@ -108,7 +108,7 @@ static void test_one_device(const char *type) resp = qmp("{'execute': 'device-list-properties'," " 'arguments': {'typename': %s}}", type); - QDECREF(resp); + qobject_unref(resp); help = hmp("device_add \"%s,help\"", type); g_free(help); @@ -129,7 +129,7 @@ static void test_device_intro_list(void) qtest_start(common_args); types = device_type_list(true); - QDECREF(types); + qobject_unref(types); help = hmp("device_add help"); g_free(help); @@ -157,8 +157,8 @@ static void test_qom_list_parents(const char *parent) g_assert(qom_has_parent(index, name, parent)); } - QDECREF(types); - QDECREF(index); + qobject_unref(types); + qobject_unref(index); } static void test_qom_list_fields(void) @@ -187,8 +187,8 @@ static void test_qom_list_fields(void) test_qom_list_parents("device"); test_qom_list_parents("sys-bus-device"); - QDECREF(all_types); - QDECREF(non_abstract); + qobject_unref(all_types); + qobject_unref(non_abstract); qtest_end(); } @@ -222,7 +222,7 @@ static void test_device_intro_concrete(void) test_one_device(type); } - QDECREF(types); + qobject_unref(types); qtest_end(); } @@ -255,8 +255,8 @@ static void test_abstract_interfaces(void) g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract")); } - QDECREF(all_types); - QDECREF(index); + qobject_unref(all_types); + qobject_unref(index); qtest_end(); } diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index de87341528..1aaa795743 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -4,7 +4,10 @@ DOCKER_SUFFIX := .docker DOCKER_FILES_DIR := $(SRC_PATH)/tests/docker/dockerfiles -DOCKER_IMAGES := $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))) +DOCKER_DEPRECATED_IMAGES := debian +# we don't run tests on intermediate images (used as base by another image) +DOCKER_PARTIAL_IMAGES := debian debian8 debian9 debian8-mxe debian-ports debian-sid debian-bootstrap +DOCKER_IMAGES := $(filter-out $(DOCKER_DEPRECATED_IMAGES),$(sort $(notdir $(basename $(wildcard $(DOCKER_FILES_DIR)/*.docker))))) DOCKER_TARGETS := $(patsubst %,docker-image-%,$(DOCKER_IMAGES)) # Use a global constant ccache directory to speed up repetitive builds DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache @@ -14,6 +17,8 @@ DOCKER_TESTS := $(notdir $(shell \ DOCKER_TOOLS := travis +DOCKER_SCRIPT=$(SRC_PATH)/tests/docker/docker.py + TESTS ?= % IMAGES ?= % @@ -31,22 +36,50 @@ docker-qemu-src: $(DOCKER_SRC_COPY) docker-image: ${DOCKER_TARGETS} -# General rule for building docker images +# General rule for building docker images. If we are a sub-make +# invoked with SKIP_DOCKER_BUILD we still check the image is upto date +# though +ifdef SKIP_DOCKER_BUILD +docker-image-%: $(DOCKER_FILES_DIR)/%.docker + $(call quiet-command, \ + $(DOCKER_SCRIPT) check --quiet qemu:$* $<, \ + "CHECK", "$*") +else docker-image-%: $(DOCKER_FILES_DIR)/%.docker - @if test "$@" = docker-image-debian-bootstrap -a -z "$(EXECUTABLE)"; then \ - echo WARNING: EXECUTABLE is not set, debootstrap may fail. 2>&1 ; \ - fi $(call quiet-command,\ - $(SRC_PATH)/tests/docker/docker.py build qemu:$* $< \ + $(DOCKER_SCRIPT) build qemu:$* $< \ $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \ $(if $(NOUSER),,--add-current-user) \ $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES))\ $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)),\ "BUILD","$*") -docker-image-debian-powerpc-cross: EXTRA_FILES:=$(SRC_PATH)/tests/docker/dockerfiles/debian-apt-fake.sh +# Special rule for debootstraped binfmt linux-user images +docker-binfmt-image-debian-%: $(DOCKER_FILES_DIR)/debian-bootstrap.docker + $(if $(EXECUTABLE),,\ + $(error EXECUTABLE not set, debootstrap of debian-$* would fail)) + $(if $(DEB_ARCH),,\ + $(error DEB_ARCH not set, debootstrap of debian-$* would fail)) + $(if $(DEB_TYPE),,\ + $(error DEB_TYPE not set, debootstrap of debian-$* would fail)) + $(if $(wildcard $(EXECUTABLE)), \ + $(call quiet-command, \ + DEB_ARCH=$(DEB_ARCH) \ + DEB_TYPE=$(DEB_TYPE) \ + $(DOCKER_SCRIPT) build qemu:debian-$* $< \ + $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \ + $(if $(NOUSER),,--add-current-user) \ + $(if $(EXTRA_FILES),--extra-files $(EXTRA_FILES)) \ + $(if $(EXECUTABLE),--include-executable=$(EXECUTABLE)), \ + "BUILD","binfmt debian-$* (debootstrapped)"), \ + $(call quiet-command, \ + $(DOCKER_SCRIPT) check --quiet qemu:debian-$* $< || \ + { echo "You will need to build $(EXECUTABLE)"; exit 1;},\ + "CHECK", "debian-$* exists")) -# Enforce dependancies for composite images +endif + +# Enforce dependencies for composite images docker-image-debian: docker-image-debian9 docker-image-debian8-mxe: docker-image-debian8 docker-image-debian-amd64: docker-image-debian9 @@ -54,22 +87,67 @@ docker-image-debian-armel-cross: docker-image-debian9 docker-image-debian-armhf-cross: docker-image-debian9 docker-image-debian-arm64-cross: docker-image-debian9 docker-image-debian-mips-cross: docker-image-debian9 +docker-image-debian-mipsel-cross: docker-image-debian9 docker-image-debian-mips64el-cross: docker-image-debian9 -docker-image-debian-powerpc-cross: docker-image-debian8 docker-image-debian-ppc64el-cross: docker-image-debian9 docker-image-debian-s390x-cross: docker-image-debian9 docker-image-debian-win32-cross: docker-image-debian8-mxe docker-image-debian-win64-cross: docker-image-debian8-mxe + +# Debian SID images - we are tracking a rolling distro so we want to +# force a re-build of the base image if we ever need to build one of +# its children. +ifndef SKIP_DOCKER_BUILD +ifeq ($(HAVE_USER_DOCKER),y) +SID_AGE=$(shell $(DOCKER_SCRIPT) check --checktype=age --olderthan=180 --quiet qemu:debian-sid) +ifeq ($(SID_AGE),) +else +docker-image-debian-sid: NOCACHE=1 +endif +endif +endif + +docker-image-debian-alpha-cross: docker-image-debian-sid +docker-image-debian-hppa-cross: docker-image-debian-sid +docker-image-debian-m68k-cross: docker-image-debian-sid +docker-image-debian-sh4-cross: docker-image-debian-sid +docker-image-debian-sparc64-cross: docker-image-debian-sid +docker-image-debian-mips64-cross: docker-image-debian-sid +docker-image-debian-riscv64-cross: docker-image-debian-sid +docker-image-debian-powerpc-cross: docker-image-debian-sid docker-image-travis: NOUSER=1 +# Specialist build images, sometimes very limited tools +docker-image-tricore-cross: docker-image-debian9 + +# These images may be good enough for building tests but not for test builds +DOCKER_PARTIAL_IMAGES += debian-alpha-cross debian-hppa-cross debian-m68k-cross debian-sh4-cross +DOCKER_PARTIAL_IMAGES += debian-sparc64-cross debian-mips64-cross debian-riscv64-cross +DOCKER_PARTIAL_IMAGES += debian-tricore-cross debian-powerpc-cross fedora-i386-cross + +# Rules for building linux-user powered images +# +# These are slower than using native cross compiler setups but can +# work around issues with poorly working multi-arch systems and broken +# packages. + +# Jessie is the last supported release for powerpc, but multi-arch is +# broken so we need a qemu-linux-user for this target +docker-binfmt-image-debian-powerpc-user: DEB_ARCH = powerpc +docker-binfmt-image-debian-powerpc-user: DEB_TYPE = jessie +docker-binfmt-image-debian-powerpc-user: EXECUTABLE = ${BUILD_DIR}/ppc-linux-user/qemu-ppc +docker-image-debian-powerpc-user-cross: docker-binfmt-image-debian-powerpc-user +DOCKER_USER_IMAGES += debian-powerpc-user + # Expand all the pre-requistes for each docker image and test combination -$(foreach i,$(DOCKER_IMAGES), \ +$(foreach i,$(filter-out $(DOCKER_PARTIAL_IMAGES),$(DOCKER_IMAGES) $(DOCKER_DEPRECATED_IMAGES)), \ $(foreach t,$(DOCKER_TESTS) $(DOCKER_TOOLS), \ $(eval .PHONY: docker-$t@$i) \ $(eval docker-$t@$i: docker-image-$i docker-run-$t@$i) \ ) \ $(foreach t,$(DOCKER_TESTS), \ - $(eval docker-test: docker-$t@$i) \ + $(eval docker-all-tests: docker-$t@$i) \ + $(eval docker-$t: docker-$t@$i) \ ) \ ) @@ -79,7 +157,8 @@ docker: @echo 'Available targets:' @echo @echo ' docker: Print this help.' - @echo ' docker-test: Run all image/test combinations.' + @echo ' docker-all-tests: Run all image/test combinations.' + @echo ' docker-TEST: Run TEST on all image combinations.' @echo ' docker-clean: Kill and remove residual docker testing containers.' @echo ' docker-TEST@IMAGE: Run "TEST" in container "IMAGE".' @echo ' Note: "TEST" is one of the listed test name,' @@ -91,6 +170,11 @@ docker: @echo @echo 'Available container images:' @echo ' $(DOCKER_IMAGES)' +ifneq ($(DOCKER_USER_IMAGES),) + @echo + @echo 'Available linux-user images (docker-binfmt-image-debian-%):' + @echo ' $(DOCKER_USER_IMAGES)' +endif @echo @echo 'Available tests:' @echo ' $(DOCKER_TESTS)' @@ -129,17 +213,17 @@ docker-run: docker-qemu-src fi $(if $(EXECUTABLE), \ $(call quiet-command, \ - $(SRC_PATH)/tests/docker/docker.py update \ + $(DOCKER_SCRIPT) update \ $(IMAGE) $(EXECUTABLE), \ " COPYING $(EXECUTABLE) to $(IMAGE)")) $(call quiet-command, \ - $(SRC_PATH)/tests/docker/docker.py run \ + $(DOCKER_SCRIPT) run \ $(if $(NOUSER),,-u $(shell id -u)) \ --security-opt seccomp=unconfined \ $(if $V,,--rm) \ $(if $(DEBUG),-ti,) \ $(if $(NETWORK),$(if $(subst $(NETWORK),,1),--net=$(NETWORK)),--net=none) \ - -e TARGET_LIST=$(TARGET_LIST) \ + -e TARGET_LIST=$(subst $(SPACE),$(COMMA),$(TARGET_LIST)) \ -e EXTRA_CONFIGURE_OPTS="$(EXTRA_CONFIGURE_OPTS)" \ -e V=$V -e J=$J -e DEBUG=$(DEBUG) \ -e SHOW_ENV=$(SHOW_ENV) \ @@ -163,4 +247,4 @@ docker-run-%: @$(MAKE) docker-run TEST=$(CMD) IMAGE=qemu:$(IMAGE) docker-clean: - $(call quiet-command, $(SRC_PATH)/tests/docker/docker.py clean) + $(call quiet-command, $(DOCKER_SCRIPT) clean) diff --git a/tests/docker/common.rc b/tests/docker/common.rc index 046f8a5921..4011561587 100755 --- a/tests/docker/common.rc +++ b/tests/docker/common.rc @@ -21,7 +21,7 @@ requires() done } -build_qemu() +configure_qemu() { config_opts="--enable-werror \ ${TARGET_LIST:+--target-list=${TARGET_LIST}} \ @@ -32,9 +32,31 @@ build_qemu() echo $config_opts $QEMU_SRC/configure $config_opts || \ { cat config.log && test_fail "Failed to run 'configure'"; } +} + +build_qemu() +{ + configure_qemu $@ make $MAKEFLAGS } +check_qemu() +{ + # default to make check unless the caller specifies + if test -z "$@"; then + INVOCATION="check" + else + INVOCATION="$@" + fi + + if command -v gtester > /dev/null 2>&1 && \ + gtester --version > /dev/null 2>&1; then + make $MAKEFLAGS $INVOCATION + else + echo "No working gtester, skipping make $INVOCATION" + fi +} + test_fail() { echo "$@" diff --git a/tests/docker/docker.py b/tests/docker/docker.py index 1246ba9578..d3006d4dae 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -11,6 +11,7 @@ # or (at your option) any later version. See the COPYING file in # the top-level directory. +from __future__ import print_function import os import sys sys.path.append(os.path.join(os.path.dirname(__file__), @@ -25,9 +26,13 @@ import tempfile import re import signal from tarfile import TarFile, TarInfo -from StringIO import StringIO +try: + from StringIO import StringIO +except ImportError: + from io import StringIO from shutil import copy, rmtree from pwd import getpwuid +from datetime import datetime,timedelta FILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy'] @@ -48,7 +53,9 @@ def _guess_docker_command(): commands = [["docker"], ["sudo", "-n", "docker"]] for cmd in commands: try: - if subprocess.call(cmd + ["images"], + # docker version will return the client details in stdout + # but still report a status of 1 if it can't contact the daemon + if subprocess.call(cmd + ["version"], stdout=DEVNULL, stderr=DEVNULL) == 0: return cmd except OSError: @@ -87,7 +94,7 @@ def _get_so_libs(executable): so_lib = search.groups()[1] libs.append("%s/%s" % (so_path, so_lib)) except subprocess.CalledProcessError: - print "%s had no associated libraries (static build?)" % (executable) + print("%s had no associated libraries (static build?)" % (executable)) return libs @@ -105,7 +112,36 @@ def _copy_binary_with_libs(src, dest_dir): so_path = os.path.dirname(l) _copy_with_mkdir(l , dest_dir, so_path) + +def _check_binfmt_misc(executable): + """Check binfmt_misc has entry for executable in the right place. + + The details of setting up binfmt_misc are outside the scope of + this script but we should at least fail early with a useful + message if it won't work.""" + + binary = os.path.basename(executable) + binfmt_entry = "/proc/sys/fs/binfmt_misc/%s" % (binary) + + if not os.path.exists(binfmt_entry): + print ("No binfmt_misc entry for %s" % (binary)) + return False + + with open(binfmt_entry) as x: entry = x.read() + + qpath = "/usr/bin/%s" % (binary) + if not re.search("interpreter %s\n" % (qpath), entry): + print ("binfmt_misc for %s does not point to %s" % (binary, qpath)) + return False + + return True + + def _read_qemu_dockerfile(img_name): + # special case for Debian linux-user images + if img_name.startswith("debian") and img_name.endswith("user"): + img_name = "debian-bootstrap" + df = os.path.join(os.path.dirname(__file__), "dockerfiles", img_name + ".docker") return open(df, "r").read() @@ -161,7 +197,7 @@ class Docker(object): continue if only_known and instance_uuid not in self._instances: continue - print "Terminating", i + print("Terminating", i) if active: self._do(["kill", i]) self._do(["rm", i]) @@ -178,8 +214,17 @@ class Docker(object): stderr=subprocess.STDOUT, **kwargs) + def inspect_tag(self, tag): + try: + return self._output(["inspect", tag]) + except subprocess.CalledProcessError: + return None + + def get_image_creation_time(self, info): + return json.loads(info)[0]["Created"] + def get_image_dockerfile_checksum(self, tag): - resp = self._output(["inspect", tag]) + resp = self.inspect_tag(tag) labels = json.loads(resp)[0]["Config"].get("Labels", {}) return labels.get("com.qemu.dockerfile-checksum", "") @@ -200,8 +245,10 @@ class Docker(object): tmp_df.write("\n") tmp_df.write("LABEL com.qemu.dockerfile-checksum=%s" % - _text_checksum("\n".join([dockerfile] + - extra_files_cksum))) + _text_checksum(_dockerfile_preprocess(dockerfile))) + for f, c in extra_files_cksum: + tmp_df.write("LABEL com.qemu.%s-checksum=%s" % (f, c)) + tmp_df.flush() self._do_check(["build", "-t", tag, "-f", tmp_df.name] + argv + \ @@ -288,11 +335,16 @@ class BuildCommand(SubCommand): if "--no-cache" not in argv and \ dkr.image_matches_dockerfile(tag, dockerfile): if not args.quiet: - print "Image is up to date." + print("Image is up to date.") else: # Create a docker context directory for the build docker_dir = tempfile.mkdtemp(prefix="docker_build") + # Validate binfmt_misc will work + if args.include_executable: + if not _check_binfmt_misc(args.include_executable): + return 1 + # Is there a .pre file to run in the build context? docker_pre = os.path.splitext(args.dockerfile)[0]+".pre" if os.path.exists(docker_pre): @@ -300,10 +352,10 @@ class BuildCommand(SubCommand): rc = subprocess.call(os.path.realpath(docker_pre), cwd=docker_dir, stdout=stdout) if rc == 3: - print "Skip" + print("Skip") return 0 elif rc != 0: - print "%s exited with code %d" % (docker_pre, rc) + print("%s exited with code %d" % (docker_pre, rc)) return 1 # Copy any extra files into the Docker context. These can be @@ -316,7 +368,7 @@ class BuildCommand(SubCommand): _copy_binary_with_libs(args.include_executable, docker_dir) for filename in args.extra_files or []: _copy_with_mkdir(filename, docker_dir) - cksum += [_file_checksum(filename)] + cksum += [(filename, _file_checksum(filename))] argv += ["--build-arg=" + k.lower() + "=" + v for k, v in os.environ.iteritems() @@ -390,6 +442,112 @@ class ImagesCommand(SubCommand): def run(self, args, argv): return Docker().command("images", argv, args.quiet) + +class ProbeCommand(SubCommand): + """Probe if we can run docker automatically""" + name = "probe" + + def run(self, args, argv): + try: + docker = Docker() + if docker._command[0] == "docker": + print("yes") + elif docker._command[0] == "sudo": + print("sudo") + except Exception: + print("no") + + return + + +class CcCommand(SubCommand): + """Compile sources with cc in images""" + name = "cc" + + def args(self, parser): + parser.add_argument("--image", "-i", required=True, + help="The docker image in which to run cc") + parser.add_argument("--cc", default="cc", + help="The compiler executable to call") + parser.add_argument("--user", + help="The user-id to run under") + parser.add_argument("--source-path", "-s", nargs="*", dest="paths", + help="""Extra paths to (ro) mount into container for + reading sources""") + + def run(self, args, argv): + if argv and argv[0] == "--": + argv = argv[1:] + cwd = os.getcwd() + cmd = ["--rm", "-w", cwd, + "-v", "%s:%s:rw" % (cwd, cwd)] + if args.paths: + for p in args.paths: + cmd += ["-v", "%s:%s:ro,z" % (p, p)] + if args.user: + cmd += ["-u", args.user] + cmd += [args.image, args.cc] + cmd += argv + return Docker().command("run", cmd, args.quiet) + + +class CheckCommand(SubCommand): + """Check if we need to re-build a docker image out of a dockerfile. + Arguments: """ + name = "check" + + def args(self, parser): + parser.add_argument("tag", + help="Image Tag") + parser.add_argument("dockerfile", default=None, + help="Dockerfile name", nargs='?') + parser.add_argument("--checktype", choices=["checksum", "age"], + default="checksum", help="check type") + parser.add_argument("--olderthan", default=60, type=int, + help="number of minutes") + + def run(self, args, argv): + tag = args.tag + + try: + dkr = Docker() + except: + print("Docker not set up") + return 1 + + info = dkr.inspect_tag(tag) + if info is None: + print("Image does not exist") + return 1 + + if args.checktype == "checksum": + if not args.dockerfile: + print("Need a dockerfile for tag:%s" % (tag)) + return 1 + + dockerfile = open(args.dockerfile, "rb").read() + + if dkr.image_matches_dockerfile(tag, dockerfile): + if not args.quiet: + print("Image is up to date") + return 0 + else: + print("Image needs updating") + return 1 + elif args.checktype == "age": + timestr = dkr.get_image_creation_time(info).split(".")[0] + created = datetime.strptime(timestr, "%Y-%m-%dT%H:%M:%S") + past = datetime.now() - timedelta(minutes=args.olderthan) + if created < past: + print ("Image created @ %s more than %d minutes old" % + (timestr, args.olderthan)) + return 1 + else: + if not args.quiet: + print ("Image less than %d minutes old" % (args.olderthan)) + return 0 + + def main(): parser = argparse.ArgumentParser(description="A Docker helper", usage="%s ..." % os.path.basename(sys.argv[0])) diff --git a/tests/docker/dockerfiles/centos6.docker b/tests/docker/dockerfiles/centos6.docker deleted file mode 100644 index ad24319582..0000000000 --- a/tests/docker/dockerfiles/centos6.docker +++ /dev/null @@ -1,30 +0,0 @@ -FROM centos:6 -RUN yum install -y epel-release centos-release-xen -ENV PACKAGES \ - bison \ - bzip2-devel \ - ccache \ - csnappy-devel \ - flex \ - g++ \ - gcc \ - gettext \ - git \ - glib2-devel \ - libepoxy-devel \ - libfdt-devel \ - librdmacm-devel \ - lzo-devel \ - make \ - mesa-libEGL-devel \ - mesa-libgbm-devel \ - pixman-devel \ - SDL-devel \ - spice-glib-devel \ - spice-server-devel \ - tar \ - vte-devel \ - xen-devel \ - zlib-devel -RUN yum install -y $PACKAGES -RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/debian-alpha-cross.docker b/tests/docker/dockerfiles/debian-alpha-cross.docker new file mode 100644 index 0000000000..29a25d0dfd --- /dev/null +++ b/tests/docker/dockerfiles/debian-alpha-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-alpha-linux-gnu \ + libc6.1-dev-alpha-cross || { echo "Failed to build - see debian-sid.docker notes"; exit 1; } diff --git a/tests/docker/dockerfiles/debian-apt-fake.sh b/tests/docker/dockerfiles/debian-apt-fake.sh deleted file mode 100755 index 2ec0fdf47a..0000000000 --- a/tests/docker/dockerfiles/debian-apt-fake.sh +++ /dev/null @@ -1,46 +0,0 @@ -#! /bin/sh -# -# Generate fake debian package to resolve unimportant unmet dependencies held -# by upstream multiarch broken packages. -# -# Copyright (c) 2017 Philippe Mathieu-Daudé -# -# 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. - -test $1 = "install" && shift 1 - -fake_install() -{ - echo "Generating fake $2 $1 $3 ..." - (cd /var/cache/apt/archives - (cat << 'EOF' -Section: misc -Priority: optional -Standards-Version: 3.9.2 - -Package: NAME -Version: VERSION -Maintainer: qemu-devel@nongnu.org -Architecture: any -Multi-Arch: same -Description: fake NAME -EOF - ) | sed s/NAME/$2/g | sed s/VERSION/$3/g > $2.control - equivs-build -a $1 $2.control 1>/dev/null 2>/dev/null - dpkg -i --force-overwrite $2_$3_$1.deb - ) -} - -try_install() -{ - name=$(echo $1|sed "s/\(.*\):\(.*\)=\(.*\)/\1/") - arch=$(echo $1|sed "s/\(.*\):\(.*\)=\(.*\)/\2/") - vers=$(echo $1|sed "s/\(.*\):\(.*\)=\(.*\)/\3/") - apt-get install -q -yy $1 || fake_install $arch $name $vers -} - -for package in $*; do - try_install $package -done diff --git a/tests/docker/dockerfiles/debian-bootstrap.docker b/tests/docker/dockerfiles/debian-bootstrap.docker index 3a9125e497..e13c26a7ed 100644 --- a/tests/docker/dockerfiles/debian-bootstrap.docker +++ b/tests/docker/dockerfiles/debian-bootstrap.docker @@ -9,6 +9,7 @@ FROM scratch ADD . / # Patch all mounts as docker already has stuff set up +# (this is not needed for later debootstraps but is harmless atm) RUN sed -i 's/in_target mount/echo not for docker in_target mount/g' /debootstrap/functions # Run stage 2 @@ -17,5 +18,3 @@ RUN /debootstrap/debootstrap --second-stage # At this point we can install additional packages if we want # Duplicate deb line as deb-src RUN cat /etc/apt/sources.list | sed "s/deb/deb-src/" >> /etc/apt/sources.list -RUN apt-get update -RUN apt-get -y build-dep qemu diff --git a/tests/docker/dockerfiles/debian-bootstrap.pre b/tests/docker/dockerfiles/debian-bootstrap.pre index 7c76dce663..3b0ef95374 100755 --- a/tests/docker/dockerfiles/debian-bootstrap.pre +++ b/tests/docker/dockerfiles/debian-bootstrap.pre @@ -32,6 +32,15 @@ if [ -z "${DEB_TYPE}" ]; then fi +# The following allow finer grain control over the defaults +if [ -z "${DEB_VARIANT}" ]; then + DEB_VARIANT=buildd +fi + +if [ -z "${DEB_URL}" ]; then + DEB_URL="http://httpredir.debian.org/debian" +fi + # We check in order for # # - DEBOOTSTRAP_DIR pointing at a development checkout @@ -47,13 +56,16 @@ if [ -z $DEBOOTSTRAP_DIR ]; then if [ -z $DEBOOTSTRAP ]; then echo "No debootstrap installed, attempting to install from SCM" NEED_DEBOOTSTRAP=true - elif ! (echo "${MIN_DEBOOTSTRAP_VERSION}" ; "${DEBOOTSTRAP}" --version \ - | cut -d ' ' -f 2) | sort -t . -n -k 1,1 -k 2,2 -k 3,3 -c &>/dev/null; then - echo "debootstrap too old, attempting to install from SCM" - NEED_DEBOOTSTRAP=true + else + INSTALLED_VERSION=$(${DEBOOTSTRAP} --version | sed 's/debootstrap \([0-9\.]*\)[^0-9\.]*.*/\1/') + if ! (echo "${MIN_DEBOOTSTRAP_VERSION}" ; echo "${INSTALLED_VERSION}") \ + | sort -t . -n -k 1,1 -k 2,2 -k 3,3 -C ; then + echo "debootstrap too old, attempting to install from SCM" + NEED_DEBOOTSTRAP=true + fi fi if $NEED_DEBOOTSTRAP; then - DEBOOTSTRAP_SOURCE=https://anonscm.debian.org/git/d-i/debootstrap.git + DEBOOTSTRAP_SOURCE=https://salsa.debian.org/installer-team/debootstrap.git git clone ${DEBOOTSTRAP_SOURCE} ./debootstrap.git export DEBOOTSTRAP_DIR=./debootstrap.git DEBOOTSTRAP=./debootstrap.git/debootstrap @@ -107,5 +119,5 @@ fi echo "Building a rootfs using ${FAKEROOT} and ${DEBOOTSTRAP} ${DEB_ARCH}/${DEB_TYPE}" -${FAKEROOT} ${DEBOOTSTRAP} --variant=buildd --foreign --arch=$DEB_ARCH $DEB_TYPE . http://httpredir.debian.org/debian || exit 1 +${FAKEROOT} ${DEBOOTSTRAP} --variant=$DEB_VARIANT --foreign --arch=$DEB_ARCH $DEB_TYPE . $DEB_URL || exit 1 exit 0 diff --git a/tests/docker/dockerfiles/debian-hppa-cross.docker b/tests/docker/dockerfiles/debian-hppa-cross.docker new file mode 100644 index 0000000000..ad443defac --- /dev/null +++ b/tests/docker/dockerfiles/debian-hppa-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-hppa-linux-gnu \ + libc6-dev-hppa-cross diff --git a/tests/docker/dockerfiles/debian-m68k-cross.docker b/tests/docker/dockerfiles/debian-m68k-cross.docker new file mode 100644 index 0000000000..21ba3b0132 --- /dev/null +++ b/tests/docker/dockerfiles/debian-m68k-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-m68k-linux-gnu \ + libc6-dev-m68k-cross diff --git a/tests/docker/dockerfiles/debian-mips64-cross.docker b/tests/docker/dockerfiles/debian-mips64-cross.docker new file mode 100644 index 0000000000..ed1ce0e919 --- /dev/null +++ b/tests/docker/dockerfiles/debian-mips64-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-mips64-linux-gnuabi64 \ + libc6-dev-mips64-cross diff --git a/tests/docker/dockerfiles/debian-powerpc-cross.docker b/tests/docker/dockerfiles/debian-powerpc-cross.docker index a5dd46b4ac..5e62ca0df1 100644 --- a/tests/docker/dockerfiles/debian-powerpc-cross.docker +++ b/tests/docker/dockerfiles/debian-powerpc-cross.docker @@ -1,40 +1,13 @@ # # Docker powerpc cross-compiler target # -# This docker target builds on the debian Jessie base image. +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. The original +# Jessie based no longer builds. # -FROM qemu:debian8 -MAINTAINER Philippe Mathieu-Daudé - -# Add the foreign architecture we want and install dependencies -RUN dpkg --add-architecture powerpc -RUN apt-get update -RUN DEBIAN_FRONTEND=noninteractive eatmydata \ - apt-get install -y --no-install-recommends \ - crossbuild-essential-powerpc - -# to fix "following packages have unmet dependencies" ... -ADD debian-apt-fake.sh /usr/local/bin/apt-fake -RUN apt-get install -y --no-install-recommends \ - equivs \ - pkg-config -RUN apt-fake install \ - pkg-config:powerpc=0.28-1.1-fake && \ - ln -s pkg-config /usr/bin/powerpc-linux-gnu-pkg-config -ENV PKG_CONFIG_PATH /usr/lib/powerpc-linux-gnu/pkgconfig -# - -# Specify the cross prefix for this image (see tests/docker/common.rc) -ENV QEMU_CONFIGURE_OPTS --cross-prefix=powerpc-linux-gnu- +FROM qemu:debian-sid RUN DEBIAN_FRONTEND=noninteractive eatmydata \ - apt-get build-dep -yy -a powerpc qemu -RUN DEBIAN_FRONTEND=noninteractive \ apt-get install -y --no-install-recommends \ - glusterfs-common:powerpc \ - libbz2-dev:powerpc \ - liblzo2-dev:powerpc \ - libncursesw5-dev:powerpc \ - libnfs-dev:powerpc \ - librdmacm-dev:powerpc \ - libsnappy-dev:powerpc + gcc-powerpc-linux-gnu \ + libc6-dev-powerpc-cross || { echo "Failed to build - see debian-sid.docker notes"; exit 1; } diff --git a/tests/docker/dockerfiles/debian-powerpc-user-cross.docker b/tests/docker/dockerfiles/debian-powerpc-user-cross.docker new file mode 100644 index 0000000000..6938a845ee --- /dev/null +++ b/tests/docker/dockerfiles/debian-powerpc-user-cross.docker @@ -0,0 +1,15 @@ +# +# Docker powerpc cross-compiler target for QEMU +# +# We can't use current Debian stable cross-compilers to build powerpc +# as it has been dropped as a release architecture. Using Debian Sid +# is just far too sketchy a build environment. This leaves us the +# final option of using linux-user. This image is based of the +# debootstrapped qemu:debian-powerpc-user but doesn't need any extra +# magic once it is setup. +# +FROM qemu:debian-powerpc-user + +RUN echo man-db man-db/auto-update boolean false | debconf-set-selections +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get build-dep -yy qemu diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker new file mode 100644 index 0000000000..2b2e64cee6 --- /dev/null +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-riscv64-linux-gnu \ + libc6-dev-riscv64-cross diff --git a/tests/docker/dockerfiles/debian-sh4-cross.docker b/tests/docker/dockerfiles/debian-sh4-cross.docker new file mode 100644 index 0000000000..88a2423094 --- /dev/null +++ b/tests/docker/dockerfiles/debian-sh4-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-sh4-linux-gnu \ + libc6-dev-sh4-cross diff --git a/tests/docker/dockerfiles/debian-sid.docker b/tests/docker/dockerfiles/debian-sid.docker new file mode 100644 index 0000000000..9a3d168705 --- /dev/null +++ b/tests/docker/dockerfiles/debian-sid.docker @@ -0,0 +1,32 @@ +# +# Debian Sid Base +# +# A number of our guests exist as ports only. We can either use the +# ports repo or get everything from Sid. However Sid is a rolling +# distro which may be broken at any particular time. If you are +# unlucky and try and build your images while gcc is in the process of +# being uploaded this can fail. Your only recourse is to try again in +# a few hours when the repos have re-synced. Once built however you +# won't be affected by repo changes unless the docker recipies are +# updated and trigger a re-build. +# + +FROM debian:sid-slim + +# Duplicate deb line as deb-src +RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list + +# Install common build utilities +RUN apt update +RUN DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + bison \ + build-essential \ + ca-certificates \ + flex \ + git \ + pkg-config \ + psmisc \ + python \ + texinfo || { echo "Failed to build - see debian-sid.docker notes"; exit 1; } diff --git a/tests/docker/dockerfiles/debian-sparc64-cross.docker b/tests/docker/dockerfiles/debian-sparc64-cross.docker new file mode 100644 index 0000000000..1e2c809274 --- /dev/null +++ b/tests/docker/dockerfiles/debian-sparc64-cross.docker @@ -0,0 +1,12 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian sid base image which +# contains cross compilers for Debian "ports" targets. +# +FROM qemu:debian-sid + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + gcc-sparc64-linux-gnu \ + libc6-dev-sparc64-cross diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker new file mode 100644 index 0000000000..4a0f7706a3 --- /dev/null +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -0,0 +1,23 @@ +# +# Docker TriCore cross-compiler target +# +# This docker target builds on the debian Stretch base image. +# +# Copyright (c) 2018 Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +FROM qemu:debian9 + +MAINTAINER Philippe Mathieu-Daudé + +RUN git clone --single-branch \ + https://github.com/bkoppelmann/tricore-binutils.git \ + /usr/src/binutils && \ + cd /usr/src/binutils && chmod +x missing && \ + CFLAGS=-w ./configure --prefix=/usr --disable-nls --target=tricore && \ + make && make install && \ + rm -rf /usr/src/binutils + +# This image isn't designed for building QEMU but building tests +ENV QEMU_CONFIGURE_OPTS --disable-system --disable-user diff --git a/tests/docker/dockerfiles/debian8-mxe.docker b/tests/docker/dockerfiles/debian8-mxe.docker index 9b8e577b03..2df4cc8c5c 100644 --- a/tests/docker/dockerfiles/debian8-mxe.docker +++ b/tests/docker/dockerfiles/debian8-mxe.docker @@ -14,6 +14,6 @@ RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive eatmydata \ apt-get install -y --no-install-recommends \ libpython2.7-stdlib \ - $(apt-get -s install -y --no-install-recommends gw32.shared-mingw-w64 gw32.shared-mingw-w64 | egrep "^Inst mxe-x86-64-unknown-" | cut -d\ -f2) + $(apt-get -s install -y --no-install-recommends gw32.shared-mingw-w64 | egrep "^Inst mxe-x86-64-unknown-" | cut -d\ -f2) ENV PATH $PATH:/usr/lib/mxe/usr/bin/ diff --git a/tests/docker/dockerfiles/debian8.docker b/tests/docker/dockerfiles/debian8.docker index 1bcf2e3d2f..52945631cd 100644 --- a/tests/docker/dockerfiles/debian8.docker +++ b/tests/docker/dockerfiles/debian8.docker @@ -32,6 +32,3 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ pkg-config \ python-minimal -# Setup Emdebian [emdebian-archive-keyring] -RUN echo "deb http://emdebian.org/tools/debian/ jessie main" > /etc/apt/sources.list.d/emdebian.list && \ - curl http://emdebian.org/tools/debian/emdebian-toolchain-archive.key | apt-key add - diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker new file mode 100644 index 0000000000..8fbef2fa53 --- /dev/null +++ b/tests/docker/dockerfiles/fedora-i386-cross.docker @@ -0,0 +1,14 @@ +FROM fedora:latest +ENV PACKAGES \ + gcc \ + glibc-static.i686 \ + glibc-devel.i686 \ + glib2-devel.i686 \ + zlib-devel.i686 \ + glib2-devel.i686 \ + nettle-devel.i686 \ + pixman-devel.i686 \ + gnutls-devel.i686 + +RUN dnf install -y $PACKAGES +RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index b706f42405..7d1d008002 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,4 +1,4 @@ -FROM fedora:27 +FROM fedora:28 ENV PACKAGES \ ccache gettext git tar PyYAML sparse flex bison python3 bzip2 hostname \ gcc gcc-c++ llvm clang make perl which bc findutils glib2-devel \ @@ -11,12 +11,12 @@ ENV PACKAGES \ numactl-devel SDL2-devel snappy-devel spice-server-devel \ systemtap-sdt-devel usbredir-devel virglrenderer-devel vte3-devel \ xen-devel \ - mingw32-pixman mingw32-glib2 mingw32-gmp mingw32-SDL mingw32-pkg-config \ - mingw32-gtk2 mingw32-gtk3 mingw32-gnutls mingw32-nettle mingw32-libtasn1 \ + mingw32-pixman mingw32-glib2 mingw32-gmp mingw32-SDL2 mingw32-pkg-config \ + mingw32-gtk3 mingw32-gnutls mingw32-nettle mingw32-libtasn1 \ mingw32-libjpeg-turbo mingw32-libpng mingw32-curl mingw32-libssh2 \ mingw32-bzip2 \ - mingw64-pixman mingw64-glib2 mingw64-gmp mingw64-SDL mingw64-pkg-config \ - mingw64-gtk2 mingw64-gtk3 mingw64-gnutls mingw64-nettle mingw64-libtasn1 \ + mingw64-pixman mingw64-glib2 mingw64-gmp mingw64-SDL2 mingw64-pkg-config \ + mingw64-gtk3 mingw64-gnutls mingw64-nettle mingw64-libtasn1 \ mingw64-libjpeg-turbo mingw64-libpng mingw64-curl mingw64-libssh2 \ mingw64-bzip2 ENV QEMU_CONFIGURE_OPTS --python=/usr/bin/python3 diff --git a/tests/docker/dockerfiles/min-glib.docker b/tests/docker/dockerfiles/min-glib.docker deleted file mode 100644 index f2eed97d35..0000000000 --- a/tests/docker/dockerfiles/min-glib.docker +++ /dev/null @@ -1,8 +0,0 @@ -FROM centos:6 -RUN yum install -y \ - tar gettext git make gcc g++ \ - zlib-devel SDL-devel pixman-devel \ - epel-release -RUN yum install -y libfdt-devel ccache -RUN yum downgrade -y http://vault.centos.org/6.0/os/x86_64/Packages/glib2-2.22.5-5.el6.x86_64.rpm -RUN yum install -y http://vault.centos.org/6.0/os/x86_64/Packages/glib2-devel-2.22.5-5.el6.x86_64.rpm diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker index 605b6e429b..03ebfb0ef2 100644 --- a/tests/docker/dockerfiles/travis.docker +++ b/tests/docker/dockerfiles/travis.docker @@ -1,8 +1,13 @@ -FROM quay.io/travisci/travis-ruby +FROM travisci/ci-garnet:packer-1512502276-986baf0 ENV DEBIAN_FRONTEND noninteractive ENV LANG en_US.UTF-8 ENV LC_ALL en_US.UTF-8 +RUN cat /etc/apt/sources.list | sed "s/# deb-src/deb-src/" >> /etc/apt/sources.list RUN apt-get update RUN apt-get -y build-dep qemu -RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools -ENV FEATURES pyyaml +RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools gcovr +# Travis tools require PhantomJS / Neo4j / Maven accessible +# in their PATH (QEMU build won't access them). +ENV PATH /usr/local/phantomjs/bin:/usr/local/phantomjs:/usr/local/neo4j-3.2.7/bin:/usr/local/maven-3.5.2/bin:/usr/local/cmake-3.9.2/bin:/usr/local/clang-5.0.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV FEATURES clang pyyaml +USER travis diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker index dabbf2a8a4..7d724e7f53 100644 --- a/tests/docker/dockerfiles/ubuntu.docker +++ b/tests/docker/dockerfiles/ubuntu.docker @@ -1,18 +1,18 @@ FROM ubuntu:16.04 RUN echo "deb http://archive.ubuntu.com/ubuntu/ trusty universe multiverse" >> \ /etc/apt/sources.list -RUN apt-get update ENV PACKAGES flex bison \ libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev libncursesw5-dev \ libseccomp-dev libgnutls-dev libssh2-1-dev libspice-server-dev \ libspice-protocol-dev libnss3-dev libfdt-dev \ - libgtk-3-dev libvte-2.91-dev libsdl1.2-dev libpng12-dev libpixman-1-dev \ + libgtk-3-dev libvte-2.91-dev libsdl2-dev libpng12-dev libpixman-1-dev \ libvdeplug-dev liblzo2-dev libsnappy-dev libbz2-dev libxen-dev librdmacm-dev libibverbs-dev \ libsasl2-dev libjpeg-turbo8-dev xfslibs-dev libcap-ng-dev libbrlapi-dev libcurl4-gnutls-dev \ libbluetooth-dev librbd-dev libaio-dev glusterfs-common libnuma-dev libepoxy-dev libdrm-dev libgbm-dev \ libjemalloc-dev libcacard-dev libusbredirhost-dev libnfs-dev libcap-dev libattr1-dev \ texinfo \ gettext git make ccache python-yaml gcc clang sparse -RUN apt-get -y install $PACKAGES +RUN apt-get update && \ + apt-get -y install $PACKAGES RUN dpkg -l $PACKAGES | sort > /packages.txt -ENV FEATURES clang pyyaml +ENV FEATURES clang pyyaml sdl2 diff --git a/tests/docker/test-clang b/tests/docker/test-clang index e90a793178..324e341cea 100755 --- a/tests/docker/test-clang +++ b/tests/docker/test-clang @@ -23,5 +23,5 @@ OPTS="--cxx=clang++ --cc=clang --host-cc=clang" #OPTS="$OPTS --extra-cflags=-fsanitize=undefined \ #--extra-cflags=-fno-sanitize=float-divide-by-zero" build_qemu $OPTS -make $MAKEFLAGS check +check_qemu install_qemu diff --git a/tests/docker/test-debug b/tests/docker/test-debug index d3f9f70d01..137f4f2ddc 100755 --- a/tests/docker/test-debug +++ b/tests/docker/test-debug @@ -22,5 +22,5 @@ OPTS="--cxx=clang++ --cc=clang --host-cc=clang" OPTS="--enable-debug --enable-sanitizers $OPTS" build_qemu $OPTS -make $MAKEFLAGS V=1 check +check_qemu check V=1 install_qemu diff --git a/tests/docker/test-full b/tests/docker/test-full index b4e42d25d7..aadc0f00a2 100755 --- a/tests/docker/test-full +++ b/tests/docker/test-full @@ -15,4 +15,4 @@ cd "$BUILD_DIR" -build_qemu && make check $MAKEFLAGS && install_qemu +build_qemu && check_qemu && install_qemu diff --git a/tests/docker/test-mingw b/tests/docker/test-mingw index 503a6bc6f7..7cca7e16a6 100755 --- a/tests/docker/test-mingw +++ b/tests/docker/test-mingw @@ -28,8 +28,8 @@ for prefix in x86_64-w64-mingw32- i686-w64-mingw32-; do --enable-vnc \ --enable-bzip2 \ --enable-guest-agent \ - --with-sdlabi=1.2 \ - --with-gtkabi=2.0 + --with-sdlabi=2.0 \ + --with-gtkabi=3.0 install_qemu make clean diff --git a/tests/docker/test-quick b/tests/docker/test-quick index 3b7bce6105..eee59c55fb 100755 --- a/tests/docker/test-quick +++ b/tests/docker/test-quick @@ -18,5 +18,5 @@ cd "$BUILD_DIR" DEF_TARGET_LIST="x86_64-softmmu,aarch64-softmmu" TARGET_LIST=${TARGET_LIST:-$DEF_TARGET_LIST} \ build_qemu -make check $MAKEFLAGS +check_qemu install_qemu diff --git a/tests/docker/test-unit b/tests/docker/test-unit new file mode 100755 index 0000000000..8905d01150 --- /dev/null +++ b/tests/docker/test-unit @@ -0,0 +1,21 @@ +#!/bin/bash -e +# +# Build and run the unit tests +# +# Copyright (c) 2018 Linaro Ltd. +# +# Authors: +# Alex Bennée +# +# 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. + +. common.rc + +cd "$BUILD_DIR" + +# although we are not building QEMU itself we still need a configured +# build for the unit tests to be built and run +configure_qemu +check_qemu check-unit diff --git a/tests/docker/travis.py b/tests/docker/travis.py index 703a7fde85..ea1ef169e6 100755 --- a/tests/docker/travis.py +++ b/tests/docker/travis.py @@ -11,6 +11,7 @@ # or (at your option) any later version. See the COPYING file in # the top-level directory. +from __future__ import print_function import sys import yaml import itertools @@ -34,14 +35,14 @@ def main(): sys.stderr.write("Usage: %s \n" % sys.argv[0]) return 1 conf = load_yaml(sys.argv[1]) - print "\n".join((": ${%s}" % var for var in conf["env"]["global"])) + print("\n".join((": ${%s}" % var for var in conf["env"]["global"]))) for config in conf_iter(conf): - print "(" - print "\n".join(config["env"]) - print "alias cc=" + config["compiler"] - print "\n".join(conf["before_script"]) - print "\n".join(conf["script"]) - print ")" + print("(") + print("\n".join(config["env"])) + print("alias cc=" + config["compiler"]) + print("\n".join(conf["before_script"])) + print("\n".join(conf["script"])) + print(")") return 0 if __name__ == "__main__": diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c index 313030a14c..852fefc8f3 100644 --- a/tests/drive_del-test.c +++ b/tests/drive_del-test.c @@ -41,7 +41,7 @@ static void device_del(void) response = qmp_receive(); g_assert(response); g_assert(qdict_haskey(response, "return")); - QDECREF(response); + qobject_unref(response); } static void test_drive_without_dev(void) @@ -78,7 +78,7 @@ static void test_after_failed_device_add(void) g_assert(response); error = qdict_get_qdict(response, "error"); g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, "GenericError"); - QDECREF(response); + qobject_unref(response); /* Delete the drive */ drive_del(); diff --git a/tests/guest-debug/test-gdbstub.py b/tests/guest-debug/test-gdbstub.py index 31ba6c943a..474d2c5c65 100644 --- a/tests/guest-debug/test-gdbstub.py +++ b/tests/guest-debug/test-gdbstub.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # This script needs to be run on startup # qemu -kernel ${KERNEL} -s -S diff --git a/tests/image-fuzzer/qcow2/__init__.py b/tests/image-fuzzer/qcow2/__init__.py index e2ebe19311..09ef59821b 100644 --- a/tests/image-fuzzer/qcow2/__init__.py +++ b/tests/image-fuzzer/qcow2/__init__.py @@ -1 +1,2 @@ -from layout import create_image +from __future__ import absolute_import +from .layout import create_image diff --git a/tests/image-fuzzer/qcow2/fuzz.py b/tests/image-fuzzer/qcow2/fuzz.py index 20eba6bc1b..abc4f0635d 100644 --- a/tests/image-fuzzer/qcow2/fuzz.py +++ b/tests/image-fuzzer/qcow2/fuzz.py @@ -17,6 +17,7 @@ # import random +from functools import reduce UINT8 = 0xff UINT16 = 0xffff diff --git a/tests/image-fuzzer/qcow2/layout.py b/tests/image-fuzzer/qcow2/layout.py index 63e801f4e8..675877da96 100644 --- a/tests/image-fuzzer/qcow2/layout.py +++ b/tests/image-fuzzer/qcow2/layout.py @@ -16,9 +16,10 @@ # along with this program. If not, see . # +from __future__ import absolute_import import random import struct -import fuzz +from . import fuzz from math import ceil from os import urandom from itertools import chain diff --git a/tests/image-fuzzer/runner.py b/tests/image-fuzzer/runner.py index 96a1c11b2f..95d84f38f3 100755 --- a/tests/image-fuzzer/runner.py +++ b/tests/image-fuzzer/runner.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # +from __future__ import print_function import sys import os import signal @@ -36,9 +37,8 @@ except ImportError: try: import simplejson as json except ImportError: - print >>sys.stderr, \ - "Warning: Module for JSON processing is not found.\n" \ - "'--config' and '--command' options are not supported." + print("Warning: Module for JSON processing is not found.\n" \ + "'--config' and '--command' options are not supported.", file=sys.stderr) # Backing file sizes in MB MAX_BACKING_FILE_SIZE = 10 @@ -128,7 +128,7 @@ class TestEnv(object): if seed is not None: self.seed = seed else: - self.seed = str(random.randint(0, sys.maxint)) + self.seed = str(random.randint(0, sys.maxsize)) random.seed(self.seed) self.init_path = os.getcwd() @@ -158,9 +158,8 @@ class TestEnv(object): try: os.makedirs(self.current_dir) except OSError as e: - print >>sys.stderr, \ - "Error: The working directory '%s' cannot be used. Reason: %s"\ - % (self.work_dir, e[1]) + print("Error: The working directory '%s' cannot be used. Reason: %s"\ + % (self.work_dir, e[1]), file=sys.stderr) raise TestException self.log = open(os.path.join(self.current_dir, "test.log"), "w") self.parent_log = open(run_log, "a") @@ -277,7 +276,7 @@ class TestEnv(object): if __name__ == '__main__': def usage(): - print """ + print(""" Usage: runner.py [OPTION...] TEST_DIR IMG_GENERATOR Set up test environment in TEST_DIR and run a test in it. A module for @@ -326,7 +325,7 @@ if __name__ == '__main__': If '--config' argument is specified, fields not listed in the configuration array will not be fuzzed. - """ + """) def run_test(test_id, seed, work_dir, run_log, cleanup, log_all, command, fuzz_config): @@ -357,8 +356,7 @@ if __name__ == '__main__': ['command=', 'help', 'seed=', 'config=', 'keep_passed', 'verbose', 'duration=']) except getopt.error as e: - print >>sys.stderr, \ - "Error: %s\n\nTry 'runner.py --help' for more information" % e + print("Error: %s\n\nTry 'runner.py --help' for more information" % e, file=sys.stderr) sys.exit(1) command = None @@ -375,9 +373,8 @@ if __name__ == '__main__': try: command = json.loads(arg) except (TypeError, ValueError, NameError) as e: - print >>sys.stderr, \ - "Error: JSON array of test commands cannot be loaded.\n" \ - "Reason: %s" % e + print("Error: JSON array of test commands cannot be loaded.\n" \ + "Reason: %s" % e, file=sys.stderr) sys.exit(1) elif opt in ('-k', '--keep_passed'): cleanup = False @@ -391,15 +388,13 @@ if __name__ == '__main__': try: config = json.loads(arg) except (TypeError, ValueError, NameError) as e: - print >>sys.stderr, \ - "Error: JSON array with the fuzzer configuration cannot" \ - " be loaded\nReason: %s" % e + print("Error: JSON array with the fuzzer configuration cannot" \ + " be loaded\nReason: %s" % e, file=sys.stderr) sys.exit(1) if not len(args) == 2: - print >>sys.stderr, \ - "Expected two parameters\nTry 'runner.py --help'" \ - " for more information." + print("Expected two parameters\nTry 'runner.py --help'" \ + " for more information.", file=sys.stderr) sys.exit(1) work_dir = os.path.realpath(args[0]) @@ -415,9 +410,8 @@ if __name__ == '__main__': try: image_generator = __import__(generator_name) except ImportError as e: - print >>sys.stderr, \ - "Error: The image generator '%s' cannot be imported.\n" \ - "Reason: %s" % (generator_name, e) + print("Error: The image generator '%s' cannot be imported.\n" \ + "Reason: %s" % (generator_name, e), file=sys.stderr) sys.exit(1) # Enable core dumps @@ -428,7 +422,7 @@ if __name__ == '__main__': test_id = count(1) while should_continue(duration, start_time): try: - run_test(str(test_id.next()), seed, work_dir, run_log, cleanup, + run_test(str(next(test_id)), seed, work_dir, run_log, cleanup, log_all, command, config) except (KeyboardInterrupt, SystemExit): sys.exit(1) diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index 8af16ee79a..9b407a3e42 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -504,12 +504,6 @@ int main(int argc, char **argv) const char *arch = qtest_get_arch(); gchar dir[] = "/tmp/ivshmem-test.XXXXXX"; -#if !GLIB_CHECK_VERSION(2, 31, 0) - if (!g_thread_supported()) { - g_thread_init(NULL); - } -#endif - g_test_init(&argc, &argv, NULL); qtest_add_abrt_handler(abrt_handler, NULL); diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index bc201d762b..42d3f76933 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -90,6 +90,7 @@ struct AHCICommand { uint32_t interrupts; uint64_t xbytes; uint32_t prd_size; + uint32_t sector_size; uint64_t buffer; AHCICommandProp *props; /* Data to be transferred to the guest */ @@ -477,10 +478,10 @@ void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot) g_free(d2h); } -void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, - uint8_t slot, size_t buffsize) +void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd) { PIOSetupFIS *pio = g_malloc0(0x20); + uint8_t port = cmd->port; /* We cannot check the Status or E_Status registers, because * the status may have again changed between the PIO Setup FIS @@ -488,15 +489,22 @@ void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x20, pio, 0x20); g_assert_cmphex(pio->fis_type, ==, 0x5f); - /* BUG: PIO Setup FIS as utilized by QEMU tries to fit the entire - * transfer size in a uint16_t field. The maximum transfer size can - * eclipse this; the field is meant to convey the size of data per - * each Data FIS, not the entire operation as a whole. For now, - * we will sanity check the broken case where applicable. */ - if (buffsize <= UINT16_MAX) { - g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, buffsize); + /* Data transferred by PIO will either be: + * (1) 12 or 16 bytes for an ATAPI command packet (QEMU always uses 12), or + * (2) Actual data from the drive. + * If we do both, (2) winds up erasing any evidence of (1). + */ + if (cmd->props->atapi && (cmd->xbytes == 0 || cmd->props->dma)) { + g_assert(le16_to_cpu(pio->tx_count) == 12 || + le16_to_cpu(pio->tx_count) == 16); + } else { + /* The AHCI test suite here does not test any PIO command that specifies + * a DRQ block larger than one sector (like 0xC4), so this should always + * be one sector or less. */ + size_t pio_len = ((cmd->xbytes % cmd->sector_size) ? + (cmd->xbytes % cmd->sector_size) : cmd->sector_size); + g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, pio_len); } - g_free(pio); } @@ -643,10 +651,7 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, /* Command creation */ if (opts->atapi) { uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE; - cmd = ahci_atapi_command_create(op, bcl); - if (opts->atapi_dma) { - ahci_command_enable_atapi_dma(cmd); - } + cmd = ahci_atapi_command_create(op, bcl, opts->atapi_dma); } else { cmd = ahci_command_create(op); } @@ -796,7 +801,7 @@ static void command_header_init(AHCICommand *cmd) static void command_table_init(AHCICommand *cmd) { RegH2DFIS *fis = &(cmd->fis); - uint16_t sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE); + uint16_t sect_count = (cmd->xbytes / cmd->sector_size); fis->fis_type = REG_H2D_FIS; fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */ @@ -819,7 +824,7 @@ static void command_table_init(AHCICommand *cmd) if (cmd->props->lba28 || cmd->props->lba48) { fis->device = ATA_DEVICE_LBA; } - fis->count = (cmd->xbytes / AHCI_SECTOR_SIZE); + fis->count = (cmd->xbytes / cmd->sector_size); } fis->icc = 0x00; fis->control = 0x00; @@ -831,9 +836,9 @@ void ahci_command_enable_atapi_dma(AHCICommand *cmd) RegH2DFIS *fis = &(cmd->fis); g_assert(cmd->props->atapi); fis->feature_low |= 0x01; - cmd->interrupts &= ~AHCI_PX_IS_PSS; + /* PIO is still used to transfer the ATAPI command */ + g_assert(cmd->props->pio); cmd->props->dma = true; - cmd->props->pio = false; /* BUG: We expect the DMA Setup interrupt for DMA commands */ /* cmd->interrupts |= AHCI_PX_IS_DSS; */ } @@ -845,7 +850,7 @@ AHCICommand *ahci_command_create(uint8_t command_name) g_assert(props); cmd = g_new0(AHCICommand, 1); - g_assert(!(props->dma && props->pio)); + g_assert(!(props->dma && props->pio) || props->atapi); g_assert(!(props->lba28 && props->lba48)); g_assert(!(props->read && props->write)); g_assert(!props->size || props->data); @@ -857,6 +862,7 @@ AHCICommand *ahci_command_create(uint8_t command_name) cmd->xbytes = props->size; cmd->prd_size = 4096; cmd->buffer = 0xabad1dea; + cmd->sector_size = props->atapi ? ATAPI_SECTOR_SIZE : AHCI_SECTOR_SIZE; if (!cmd->props->ncq) { cmd->interrupts = AHCI_PX_IS_DHRS; @@ -865,7 +871,6 @@ AHCICommand *ahci_command_create(uint8_t command_name) /* cmd->interrupts |= props->data ? AHCI_PX_IS_DPS : 0; */ /* BUG: We expect the DMA Setup interrupt for DMA commands */ /* cmd->interrupts |= props->dma ? AHCI_PX_IS_DSS : 0; */ - cmd->interrupts |= props->pio ? AHCI_PX_IS_PSS : 0; cmd->interrupts |= props->ncq ? AHCI_PX_IS_SDBS : 0; command_header_init(cmd); @@ -874,19 +879,24 @@ AHCICommand *ahci_command_create(uint8_t command_name) return cmd; } -AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl) +AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma) { AHCICommand *cmd = ahci_command_create(CMD_PACKET); cmd->atapi_cmd = g_malloc0(16); cmd->atapi_cmd[0] = scsi_cmd; stw_le_p(&cmd->fis.lba_lo[1], bcl); + if (dma) { + ahci_command_enable_atapi_dma(cmd); + } else { + cmd->interrupts |= bcl ? AHCI_PX_IS_PSS : 0; + } return cmd; } void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port, bool ready, uint8_t expected_sense) { - AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0); + AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0, false); ahci_command_set_size(cmd, 0); if (!ready) { cmd->interrupts |= AHCI_PX_IS_TFES; @@ -928,7 +938,7 @@ void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port, void ahci_atapi_eject(AHCIQState *ahci, uint8_t port) { - AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0); + AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false); ahci_command_set_size(cmd, 0); cmd->atapi_cmd[4] = 0x02; /* loej = true */ @@ -940,7 +950,7 @@ void ahci_atapi_eject(AHCIQState *ahci, uint8_t port) void ahci_atapi_load(AHCIQState *ahci, uint8_t port) { - AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0); + AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false); ahci_command_set_size(cmd, 0); cmd->atapi_cmd[4] = 0x03; /* loej,start = true */ @@ -1033,7 +1043,7 @@ void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer) static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes) { unsigned char *cbd = cmd->atapi_cmd; - uint64_t nsectors = xbytes / 2048; + uint64_t nsectors = xbytes / ATAPI_SECTOR_SIZE; uint32_t tmp; g_assert(cbd); @@ -1080,7 +1090,7 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, cmd->prd_size = prd_size; } cmd->xbytes = xbytes; - sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE); + sect_count = (cmd->xbytes / cmd->sector_size); if (cmd->props->ncq) { NCQFIS *nfis = (NCQFIS *)&(cmd->fis); @@ -1089,6 +1099,12 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, } else if (cmd->props->atapi) { ahci_atapi_set_size(cmd, xbytes); } else { + /* For writes, the PIO Setup FIS interrupt only comes from DRQs + * after the first. + */ + if (cmd->props->pio && sect_count > (cmd->props->read ? 0 : 1)) { + cmd->interrupts |= AHCI_PX_IS_PSS; + } cmd->fis.count = sect_count; } cmd->header.prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size); @@ -1216,7 +1232,7 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd) ahci_port_check_d2h_sanity(ahci, port, slot); } if (cmd->props->pio) { - ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes); + ahci_port_check_pio_sanity(ahci, cmd); } } diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index 715ca1e226..f05b3e5fce 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -596,8 +596,7 @@ void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port, uint32_t intr_mask); void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot); void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot); -void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, - uint8_t slot, size_t buffsize); +void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd); void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd); /* Misc */ @@ -623,7 +622,7 @@ void ahci_atapi_load(AHCIQState *ahci, uint8_t port); /* Command: Fine-grained lifecycle */ AHCICommand *ahci_command_create(uint8_t command_name); -AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl); +AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma); void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port); void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd); void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd); diff --git a/tests/libqos/i2c.h b/tests/libqos/i2c.h index eb40b808bd..cc01358a9f 100644 --- a/tests/libqos/i2c.h +++ b/tests/libqos/i2c.h @@ -21,6 +21,8 @@ struct I2CAdapter { QTestState *qts; }; +#define OMAP2_I2C_1_BASE 0x48070000 + void i2c_send(I2CAdapter *i2c, uint8_t addr, const uint8_t *buf, uint16_t len); void i2c_recv(I2CAdapter *i2c, uint8_t addr, diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 5124e982c1..013ca68581 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -100,14 +100,14 @@ void migrate(QOSState *from, QOSState *to, const char *uri) sub = qdict_get_qdict(rsp, "return"); g_assert(qdict_haskey(sub, "running")); running = qdict_get_bool(sub, "running"); - QDECREF(rsp); + qobject_unref(rsp); /* Issue the migrate command. */ rsp = qtest_qmp(from->qts, "{ 'execute': 'migrate', 'arguments': { 'uri': %s }}", uri); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); /* Wait for STOP event, but only if we were running: */ if (running) { @@ -132,12 +132,12 @@ void migrate(QOSState *from, QOSState *to, const char *uri) /* "setup", "active", "completed", "failed", "cancelled" */ if (strcmp(st, "completed") == 0) { - QDECREF(rsp); + qobject_unref(rsp); break; } if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0)) { - QDECREF(rsp); + qobject_unref(rsp); g_usleep(5000); continue; } diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index a2daf6103d..a7803308b7 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -170,7 +170,7 @@ void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) g_free(cmd); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); outb(ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot); diff --git a/tests/libqtest.c b/tests/libqtest.c index 6f33a37667..098af6aec4 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -103,8 +103,15 @@ static int socket_accept(int sock) static void kill_qemu(QTestState *s) { if (s->qemu_pid != -1) { + int wstatus = 0; + pid_t pid; + kill(s->qemu_pid, SIGTERM); - waitpid(s->qemu_pid, NULL, 0); + pid = waitpid(s->qemu_pid, &wstatus, 0); + + if (pid == s->qemu_pid && WIFSIGNALED(wstatus)) { + assert(!WCOREDUMP(wstatus)); + } } } @@ -517,8 +524,8 @@ void qmp_fd_sendv(int fd, const char *fmt, va_list ap) /* Send QMP request */ socket_send(fd, str, qstring_get_length(qstr)); - QDECREF(qstr); - qobject_decref(qobj); + qobject_unref(qstr); + qobject_unref(qobj); } } @@ -585,7 +592,7 @@ void qtest_async_qmp(QTestState *s, const char *fmt, ...) void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap) { QDict *response = qtest_qmpv(s, fmt, ap); - QDECREF(response); + qobject_unref(response); } void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...) @@ -596,7 +603,7 @@ void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...) va_start(ap, fmt); response = qtest_qmpv(s, fmt, ap); va_end(ap); - QDECREF(response); + qobject_unref(response); } QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) @@ -609,7 +616,7 @@ QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) (strcmp(qdict_get_str(response, "event"), event) == 0)) { return response; } - QDECREF(response); + qobject_unref(response); } } @@ -618,7 +625,7 @@ void qtest_qmp_eventwait(QTestState *s, const char *event) QDict *response; response = qtest_qmp_eventwait_ref(s, event); - QDECREF(response); + qobject_unref(response); } char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap) @@ -634,12 +641,12 @@ char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap) ret = g_strdup(qdict_get_try_str(resp, "return")); while (ret == NULL && qdict_get_try_str(resp, "event")) { /* Ignore asynchronous QMP events */ - QDECREF(resp); + qobject_unref(resp); resp = qtest_qmp_receive(s); ret = g_strdup(qdict_get_try_str(resp, "return")); } g_assert(ret); - QDECREF(resp); + qobject_unref(resp); g_free(cmd); return ret; } @@ -1021,7 +1028,7 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine)) } qtest_end(); - QDECREF(response); + qobject_unref(response); } /* @@ -1050,7 +1057,7 @@ void qtest_qmp_device_add(const char *driver, const char *id, const char *fmt, g_assert(response); g_assert(!qdict_haskey(response, "event")); /* We don't expect any events */ g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } /* @@ -1095,6 +1102,13 @@ void qtest_qmp_device_del(const char *id) g_assert(event); g_assert_cmpstr(qdict_get_str(event, "event"), ==, "DEVICE_DELETED"); - QDECREF(response1); - QDECREF(response2); + qobject_unref(response1); + qobject_unref(response2); +} + +bool qmp_rsp_is_err(QDict *rsp) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + qobject_unref(rsp); + return !!error; } diff --git a/tests/libqtest.h b/tests/libqtest.h index cbe8df4473..ac52872cbe 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -972,4 +972,13 @@ void qtest_qmp_device_add(const char *driver, const char *id, const char *fmt, */ void qtest_qmp_device_del(const char *id); +/** + * qmp_rsp_is_err: + * @rsp: QMP response to check for error + * + * Test @rsp for error and discard @rsp. + * Returns 'true' if there is error in @rsp and 'false' otherwise. + */ +bool qmp_rsp_is_err(QDict *rsp); + #endif diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c index 26af7d6e8e..5b695971c7 100644 --- a/tests/m48t59-test.c +++ b/tests/m48t59-test.c @@ -256,8 +256,6 @@ static void base_setup(void) int main(int argc, char **argv) { - int ret; - base_setup(); g_test_init(&argc, &argv, NULL); @@ -267,7 +265,5 @@ int main(int argc, char **argv) qtest_add_func("/rtc/bcd-check-time", bcd_check_time); } qtest_add_func("/rtc/fuzz-registers", fuzz_registers); - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c index efdd4be986..f286557b3e 100644 --- a/tests/machine-none-test.c +++ b/tests/machine-none-test.c @@ -88,7 +88,7 @@ static void test_machine_cpu_cli(void) response = qmp("{ 'execute': 'quit' }"); g_assert(qdict_haskey(response, "return")); - QDECREF(response); + qobject_unref(response); qtest_quit(global_qtest); } diff --git a/tests/migration-test.c b/tests/migration-test.c index 422bf1afdf..e079e0bdb6 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -19,13 +19,11 @@ #include "qemu/sockets.h" #include "chardev/char.h" #include "sysemu/sysemu.h" -#include "hw/nvram/chrp_nvram.h" - -#define MIN_NVRAM_SIZE 8192 /* from spapr_nvram.c */ const unsigned start_address = 1024 * 1024; const unsigned end_address = 100 * 1024 * 1024; bool got_stop; +static bool uffd_feature_thread_id; #if defined(__linux__) #include @@ -55,6 +53,7 @@ static bool ufd_version_check(void) g_test_message("Skipping test: UFFDIO_API failed"); return false; } + uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; ioctl_mask = (__u64)1 << _UFFDIO_REGISTER | (__u64)1 << _UFFDIO_UNREGISTER; @@ -90,36 +89,6 @@ static void init_bootfile_x86(const char *bootpath) fclose(bootfile); } -static void init_bootfile_ppc(const char *bootpath) -{ - FILE *bootfile; - char buf[MIN_NVRAM_SIZE]; - ChrpNvramPartHdr *header = (ChrpNvramPartHdr *)buf; - - memset(buf, 0, MIN_NVRAM_SIZE); - - /* Create a "common" partition in nvram to store boot-command property */ - - header->signature = CHRP_NVPART_SYSTEM; - memcpy(header->name, "common", 6); - chrp_nvram_finish_partition(header, MIN_NVRAM_SIZE); - - /* FW_MAX_SIZE is 4MB, but slof.bin is only 900KB, - * so let's modify memory between 1MB and 100MB - * to do like PC bootsector - */ - - sprintf(buf + 16, - "boot-command=hex .\" _\" begin %x %x do i c@ 1 + i c! 1000 +loop " - ".\" B\" 0 until", end_address, start_address); - - /* Write partition to the NVRAM file */ - - bootfile = fopen(bootpath, "wb"); - g_assert_cmpint(fwrite(buf, MIN_NVRAM_SIZE, 1, bootfile), ==, 1); - fclose(bootfile); -} - /* * Wait for some output in the serial output file, * we get an 'A' followed by an endless string of 'B's @@ -193,12 +162,43 @@ static QDict *wait_command(QTestState *who, const char *command) if (!strcmp(event_string, "STOP")) { got_stop = true; } - QDECREF(response); + qobject_unref(response); response = qtest_qmp_receive(who); } return response; } +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +static QDict *migrate_query(QTestState *who) +{ + QDict *rsp, *rsp_return; + + rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); + rsp_return = qdict_get_qdict(rsp, "return"); + g_assert(rsp_return); + qobject_ref(rsp_return); + qobject_unref(rsp); + + return rsp_return; +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +static gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} /* * It's tricky to use qemu's migration event capability with qtest, @@ -207,11 +207,10 @@ static QDict *wait_command(QTestState *who, const char *command) static uint64_t get_migration_pass(QTestState *who) { - QDict *rsp, *rsp_return, *rsp_ram; + QDict *rsp_return, *rsp_ram; uint64_t result; - rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); + rsp_return = migrate_query(who); if (!qdict_haskey(rsp_return, "ram")) { /* Still in setup */ result = 0; @@ -219,23 +218,30 @@ static uint64_t get_migration_pass(QTestState *who) rsp_ram = qdict_get_qdict(rsp_return, "ram"); result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0); } - QDECREF(rsp); + qobject_unref(rsp_return); return result; } -static void wait_for_migration_complete(QTestState *who) +static void read_blocktime(QTestState *who) +{ + QDict *rsp_return; + + rsp_return = migrate_query(who); + g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); + qobject_unref(rsp_return); +} + +static void wait_for_migration_status(QTestState *who, + const char *goal) { while (true) { - QDict *rsp, *rsp_return; bool completed; - const char *status; + char *status; - rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); - status = qdict_get_str(rsp_return, "status"); - completed = strcmp(status, "completed") == 0; + status = migrate_query_status(who); + completed = strcmp(status, goal) == 0; g_assert_cmpstr(status, !=, "failed"); - QDECREF(rsp); + g_free(status); if (completed) { return; } @@ -243,6 +249,11 @@ static void wait_for_migration_complete(QTestState *who) } } +static void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed"); +} + static void wait_for_migration_pass(QTestState *who) { uint64_t initial_pass = get_migration_pass(who); @@ -289,6 +300,7 @@ static void check_guests_ram(QTestState *who) * to us yet. */ hit_edge = true; + last_byte = b; } else { fprintf(stderr, "Memory content inconsistency at %x" " first_byte = %x last_byte = %x current = %x" @@ -297,7 +309,6 @@ static void check_guests_ram(QTestState *who) bad = true; } } - last_byte = b; } g_assert_false(bad); } @@ -322,7 +333,7 @@ static void migrate_check_parameter(QTestState *who, const char *parameter, qdict_get_try_int(rsp_return, parameter, -1)); g_assert_cmpstr(result, ==, value); g_free(result); - QDECREF(rsp); + qobject_unref(rsp); } static void migrate_set_parameter(QTestState *who, const char *parameter, @@ -337,10 +348,33 @@ static void migrate_set_parameter(QTestState *who, const char *parameter, rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); migrate_check_parameter(who, parameter, value); } +static void migrate_pause(QTestState *who) +{ + QDict *rsp; + + rsp = wait_command(who, "{ 'execute': 'migrate-pause' }"); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +static void migrate_recover(QTestState *who, const char *uri) +{ + QDict *rsp; + gchar *cmd = g_strdup_printf( + "{ 'execute': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': '%s' } }", uri); + + rsp = wait_command(who, cmd); + g_assert(qdict_haskey(rsp, "return")); + g_free(cmd); + qobject_unref(rsp); +} + static void migrate_set_capability(QTestState *who, const char *capability, const char *value) { @@ -355,33 +389,39 @@ static void migrate_set_capability(QTestState *who, const char *capability, rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); } -static void migrate(QTestState *who, const char *uri) +static void migrate(QTestState *who, const char *uri, const char *extra) { QDict *rsp; gchar *cmd; cmd = g_strdup_printf("{ 'execute': 'migrate'," - "'arguments': { 'uri': '%s' } }", - uri); + " 'arguments': { 'uri': '%s' %s } }", + uri, extra ? extra : ""); rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); } -static void migrate_start_postcopy(QTestState *who) +static void migrate_postcopy_start(QTestState *from, QTestState *to) { QDict *rsp; - rsp = wait_command(who, "{ 'execute': 'migrate-start-postcopy' }"); + rsp = wait_command(from, "{ 'execute': 'migrate-start-postcopy' }"); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + + qtest_qmp_eventwait(to, "RESUME"); } -static void test_migrate_start(QTestState **from, QTestState **to, +static int test_migrate_start(QTestState **from, QTestState **to, const char *uri, bool hide_stderr) { gchar *cmd_src, *cmd_dst; @@ -406,16 +446,22 @@ static void test_migrate_start(QTestState **from, QTestState **to, accel, tmpfs, bootpath, uri); } else if (strcmp(arch, "ppc64") == 0) { - /* On ppc64, the test only works with kvm-hv, but not with kvm-pr */ + /* On ppc64, the test only works with kvm-hv, but not with kvm-pr + * and TCG is touchy due to race conditions on dirty bits + * (especially on PPC for some reason) + */ if (access("/sys/module/kvm_hv", F_OK)) { - accel = "tcg"; + g_print("Skipping test: kvm_hv not available "); + return -1; } - init_bootfile_ppc(bootpath); cmd_src = g_strdup_printf("-machine accel=%s -m 256M" " -name source,debug-threads=on" " -serial file:%s/src_serial" - " -drive file=%s,if=pflash,format=raw", - accel, tmpfs, bootpath); + " -prom-env '" + "boot-command=hex .\" _\" begin %x %x " + "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " + "until'", accel, tmpfs, end_address, + start_address); cmd_dst = g_strdup_printf("-machine accel=%s -m 256M" " -name target,debug-threads=on" " -serial file:%s/dest_serial" @@ -443,6 +489,7 @@ static void test_migrate_start(QTestState **from, QTestState **to, *to = qtest_init(cmd_dst); g_free(cmd_dst); + return 0; } static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) @@ -491,7 +538,7 @@ static void deprecated_set_downtime(QTestState *who, const double value) rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); result_int = value * 1000L; expected = g_strdup_printf("%" PRId64, result_int); migrate_check_parameter(who, "downtime-limit", expected); @@ -508,7 +555,7 @@ static void deprecated_set_speed(QTestState *who, const char *value) rsp = qtest_qmp(who, cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); migrate_check_parameter(who, "max-bandwidth", value); } @@ -524,15 +571,20 @@ static void test_deprecated(void) qtest_quit(from); } -static void test_migrate(void) +static int migrate_postcopy_prepare(QTestState **from_ptr, + QTestState **to_ptr, + bool hide_error) { char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - test_migrate_start(&from, &to, uri, false); + if (test_migrate_start(&from, &to, uri, hide_error)) { + return -1; + } migrate_set_capability(from, "postcopy-ram", "true"); migrate_set_capability(to, "postcopy-ram", "true"); + migrate_set_capability(to, "postcopy-blocktime", "true"); /* We want to pick a speed slow enough that the test completes * quickly, but that it doesn't complete precopy even on a slow @@ -544,11 +596,154 @@ static void test_migrate(void) /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri); + migrate(from, uri, NULL); + g_free(uri); wait_for_migration_pass(from); - migrate_start_postcopy(from); + *from_ptr = from; + *to_ptr = to; + + return 0; +} + +static void migrate_postcopy_complete(QTestState *from, QTestState *to) +{ + wait_for_migration_complete(from); + + /* Make sure we get at least one "B" on destination */ + wait_for_serial("dest_serial"); + + if (uffd_feature_thread_id) { + read_blocktime(to); + } + + test_migrate_end(from, to, true); +} + +static void test_postcopy(void) +{ + QTestState *from, *to; + + if (migrate_postcopy_prepare(&from, &to, false)) { + return; + } + migrate_postcopy_start(from, to); + migrate_postcopy_complete(from, to); +} + +static void test_postcopy_recovery(void) +{ + QTestState *from, *to; + char *uri; + + if (migrate_postcopy_prepare(&from, &to, true)) { + return; + } + + /* Turn postcopy speed down, 4K/s is slow enough on any machines */ + migrate_set_parameter(from, "max-postcopy-bandwidth", "4096"); + + /* Now we start the postcopy */ + migrate_postcopy_start(from, to); + + /* + * Wait until postcopy is really started; we can only run the + * migrate-pause command during a postcopy + */ + wait_for_migration_status(from, "postcopy-active"); + + /* + * Manually stop the postcopy migration. This emulates a network + * failure with the migration socket + */ + migrate_pause(from); + + /* + * Wait for destination side to reach postcopy-paused state. The + * migrate-recover command can only succeed if destination machine + * is in the paused state + */ + wait_for_migration_status(to, "postcopy-paused"); + + /* + * Create a new socket to emulate a new channel that is different + * from the broken migration channel; tell the destination to + * listen to the new port + */ + uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); + migrate_recover(to, uri); + + /* + * Try to rebuild the migration channel using the resume flag and + * the newly created channel + */ + wait_for_migration_status(from, "postcopy-paused"); + migrate(from, uri, ", 'resume': true"); + g_free(uri); + + /* Restore the postcopy bandwidth to unlimited */ + migrate_set_parameter(from, "max-postcopy-bandwidth", "0"); + + migrate_postcopy_complete(from, to); +} + +static void test_baddest(void) +{ + QTestState *from, *to; + QDict *rsp, *rsp_return; + char *status; + bool failed; + + if (test_migrate_start(&from, &to, "tcp:0:0", true)) { + return; + } + migrate(from, "tcp:0:0", NULL); + do { + status = migrate_query_status(from); + g_assert(!strcmp(status, "setup") || !(strcmp(status, "failed"))); + failed = !strcmp(status, "failed"); + g_free(status); + } while (!failed); + + /* Is the machine currently running? */ + rsp = wait_command(from, "{ 'execute': 'query-status' }"); + g_assert(qdict_haskey(rsp, "return")); + rsp_return = qdict_get_qdict(rsp, "return"); + g_assert(qdict_haskey(rsp_return, "running")); + g_assert(qdict_get_bool(rsp_return, "running")); + qobject_unref(rsp); + + test_migrate_end(from, to, false); +} + +static void test_precopy_unix(void) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + if (test_migrate_start(&from, &to, uri, false)) { + return; + } + + /* We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge*/ + migrate_set_parameter(from, "downtime-limit", "1"); + /* 1GB/s */ + migrate_set_parameter(from, "max-bandwidth", "1000000000"); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate(from, uri, NULL); + + wait_for_migration_pass(from); + + /* 300 ms should converge */ + migrate_set_parameter(from, "downtime-limit", "300"); if (!got_stop) { qtest_qmp_eventwait(from, "STOP"); @@ -559,40 +754,8 @@ static void test_migrate(void) wait_for_serial("dest_serial"); wait_for_migration_complete(from); - g_free(uri); - test_migrate_end(from, to, true); -} - -static void test_baddest(void) -{ - QTestState *from, *to; - QDict *rsp, *rsp_return; - const char *status; - bool failed; - - test_migrate_start(&from, &to, "tcp:0:0", true); - migrate(from, "tcp:0:0"); - do { - rsp = wait_command(from, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); - - status = qdict_get_str(rsp_return, "status"); - - g_assert(!strcmp(status, "setup") || !(strcmp(status, "failed"))); - failed = !strcmp(status, "failed"); - QDECREF(rsp); - } while (!failed); - - /* Is the machine currently running? */ - rsp = wait_command(from, "{ 'execute': 'query-status' }"); - g_assert(qdict_haskey(rsp, "return")); - rsp_return = qdict_get_qdict(rsp, "return"); - g_assert(qdict_haskey(rsp_return, "running")); - g_assert(qdict_get_bool(rsp_return, "running")); - QDECREF(rsp); - - test_migrate_end(from, to, false); + g_free(uri); } int main(int argc, char **argv) @@ -614,9 +777,11 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); - qtest_add_func("/migration/postcopy/unix", test_migrate); + qtest_add_func("/migration/postcopy/unix", test_postcopy); + qtest_add_func("/migration/postcopy/recovery", test_postcopy_recovery); qtest_add_func("/migration/deprecated", test_deprecated); qtest_add_func("/migration/bad_dest", test_baddest); + qtest_add_func("/migration/precopy/unix", test_precopy_unix); ret = g_test_run(); diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index e14d4320b2..398e3f2706 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # Migration test main engine # @@ -117,7 +118,7 @@ class Engine(object): # XXX how to get dst timings on remote host ? if self._verbose: - print "Sleeping %d seconds for initial guest workload run" % self._sleep + print("Sleeping %d seconds for initial guest workload run" % self._sleep) sleep_secs = self._sleep while sleep_secs > 1: src_qemu_time.append(self._cpu_timing(src_pid)) @@ -126,7 +127,7 @@ class Engine(object): sleep_secs -= 1 if self._verbose: - print "Starting migration" + print("Starting migration") if scenario._auto_converge: resp = src.command("migrate-set-capabilities", capabilities = [ @@ -216,7 +217,7 @@ class Engine(object): if progress._status == "completed": if self._verbose: - print "Sleeping %d seconds for final guest workload run" % self._sleep + print("Sleeping %d seconds for final guest workload run" % self._sleep) sleep_secs = self._sleep while sleep_secs > 1: time.sleep(1) @@ -227,23 +228,23 @@ class Engine(object): return [progress_history, src_qemu_time, src_vcpu_time] if self._verbose and (loop % 20) == 0: - print "Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( + print("Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( progress._ram._iterations, progress._ram._remaining_bytes / (1024 * 1024), progress._ram._total_bytes / (1024 * 1024), progress._ram._transferred_bytes / (1024 * 1024), progress._ram._transfer_rate_mbs, - ) + )) if progress._ram._iterations > scenario._max_iters: if self._verbose: - print "No completion after %d iterations over RAM" % scenario._max_iters + print("No completion after %d iterations over RAM" % scenario._max_iters) src.command("migrate_cancel") continue if time.time() > (start + scenario._max_time): if self._verbose: - print "No completion after %d seconds" % scenario._max_time + print("No completion after %d seconds" % scenario._max_time) src.command("migrate_cancel") continue @@ -251,7 +252,7 @@ class Engine(object): progress._ram._iterations >= scenario._post_copy_iters and not post_copy): if self._verbose: - print "Switching to post-copy after %d iterations" % scenario._post_copy_iters + print("Switching to post-copy after %d iterations" % scenario._post_copy_iters) resp = src.command("migrate-start-postcopy") post_copy = True @@ -259,7 +260,7 @@ class Engine(object): progress._ram._iterations >= scenario._pause_iters and not paused): if self._verbose: - print "Pausing VM after %d iterations" % scenario._pause_iters + print("Pausing VM after %d iterations" % scenario._pause_iters) resp = src.command("stop") paused = True @@ -348,7 +349,7 @@ class Engine(object): if not log: return [] if self._debug: - print log + print(log) regex = r"[^\s]+\s\((\d+)\):\sINFO:\s(\d+)ms\scopied\s\d+\sGB\sin\s(\d+)ms" matcher = re.compile(regex) @@ -407,7 +408,7 @@ class Engine(object): if uri[0:5] == "unix:": os.remove(uri[5:]) if self._verbose: - print "Finished migration" + print("Finished migration") src.shutdown() dst.shutdown() @@ -420,7 +421,7 @@ class Engine(object): self._initrd, self._transport, self._sleep) except Exception as e: if self._debug: - print "Failed: %s" % str(e) + print("Failed: %s" % str(e)) try: src.shutdown() except: @@ -431,7 +432,7 @@ class Engine(object): pass if self._debug: - print src.get_log() - print dst.get_log() + print(src.get_log()) + print(dst.get_log()) raise diff --git a/tests/migration/guestperf/plot.py b/tests/migration/guestperf/plot.py index bc42249e16..aa98912a82 100644 --- a/tests/migration/guestperf/plot.py +++ b/tests/migration/guestperf/plot.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # Migration test graph plotting # @@ -588,7 +589,7 @@ class Plot(object): """ def generate_html(self, fh): - print >>fh, """ + print(""" @@ -601,19 +602,19 @@ class Plot(object):

Migration report

Chart summary

-""" % self._generate_style() - print >>fh, self._generate_chart() - print >>fh, """ +""" % self._generate_style(), file=fh) + print(self._generate_chart(), file=fh) + print("""

Report details

-""" - print >>fh, self._generate_report() - print >>fh, """ +""", file=fh) + print(self._generate_report(), file=fh) + print("""
-""" +""", file=fh) def generate(self, filename): if filename is None: diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py index b272978f47..a6b8cec1e0 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration/guestperf/shell.py @@ -1,3 +1,4 @@ +from __future__ import print_function # # Migration test command line shell integration # @@ -160,13 +161,13 @@ class Shell(BaseShell): try: report = engine.run(hardware, scenario) if args.output is None: - print report.to_json() + print(report.to_json()) else: with open(args.output, "w") as fh: - print >>fh, report.to_json() + print(report.to_json(), file=fh) return 0 except Exception as e: - print >>sys.stderr, "Error: %s" % str(e) + print("Error: %s" % str(e), file=sys.stderr) if args.debug: raise return 1 @@ -199,11 +200,11 @@ class BatchShell(BaseShell): name = os.path.join(comparison._name, scenario._name) if not fnmatch.fnmatch(name, args.filter): if args.verbose: - print "Skipping %s" % name + print("Skipping %s" % name) continue if args.verbose: - print "Running %s" % name + print("Running %s" % name) dirname = os.path.join(args.output, comparison._name) filename = os.path.join(dirname, scenario._name + ".json") @@ -211,9 +212,9 @@ class BatchShell(BaseShell): os.makedirs(dirname) report = engine.run(hardware, scenario) with open(filename, "w") as fh: - print >>fh, report.to_json() + print(report.to_json(), file=fh) except Exception as e: - print >>sys.stderr, "Error: %s" % str(e) + print("Error: %s" % str(e), file=sys.stderr) if args.debug: raise @@ -246,14 +247,14 @@ class PlotShell(object): if len(args.reports) == 0: - print >>sys.stderr, "At least one report required" + print("At least one report required", file=sys.stderr) return 1 if not (args.qemu_cpu or args.vcpu_cpu or args.total_guest_cpu or args.split_guest_cpu): - print >>sys.stderr, "At least one chart type is required" + print("At least one chart type is required", file=sys.stderr) return 1 reports = [] diff --git a/tests/numa-test.c b/tests/numa-test.c index 0f861d8176..893f826acb 100644 --- a/tests/numa-test.c +++ b/tests/numa-test.c @@ -111,10 +111,10 @@ static void test_query_cpus(const void *data) } else { g_assert_cmpint(node, ==, 1); } - qobject_decref(e); + qobject_unref(e); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); g_free(cli); } @@ -164,10 +164,10 @@ static void pc_numa_cpu(const void *data) } else { g_assert(false); } - qobject_decref(e); + qobject_unref(e); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); g_free(cli); } @@ -209,10 +209,10 @@ static void spapr_numa_cpu(const void *data) } else { g_assert(false); } - qobject_decref(e); + qobject_unref(e); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); g_free(cli); } @@ -252,14 +252,74 @@ static void aarch64_numa_cpu(const void *data) } else { g_assert(false); } - qobject_decref(e); + qobject_unref(e); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); g_free(cli); } +static void pc_dynamic_cpu_cfg(const void *data) +{ + QObject *e; + QDict *resp; + QList *cpus; + QTestState *qs; + + qs = qtest_startf("%s %s", data ? (char *)data : "", + "-nodefaults --preconfig -smp 2"); + + /* create 2 numa nodes */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 0 } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'node', 'nodeid': 1 } }"))); + + /* map 2 cpus in non default reverse order + * i.e socket1->node0, socket0->node1 + */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 0, 'socket-id': 1 } }"))); + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," + " 'arguments': { 'type': 'cpu', 'node-id': 1, 'socket-id': 0 } }"))); + + /* let machine initialization to complete and run */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + /* check that CPUs are mapped as expected */ + resp = qtest_qmp(qs, "{ 'execute': 'query-hotpluggable-cpus'}"); + g_assert(qdict_haskey(resp, "return")); + cpus = qdict_get_qlist(resp, "return"); + g_assert(cpus); + while ((e = qlist_pop(cpus))) { + const QDict *cpu, *props; + int64_t socket, node; + + cpu = qobject_to(QDict, e); + g_assert(qdict_haskey(cpu, "props")); + props = qdict_get_qdict(cpu, "props"); + + g_assert(qdict_haskey(props, "node-id")); + node = qdict_get_int(props, "node-id"); + g_assert(qdict_haskey(props, "socket-id")); + socket = qdict_get_int(props, "socket-id"); + + if (socket == 0) { + g_assert_cmpint(node, ==, 1); + } else if (socket == 1) { + g_assert_cmpint(node, ==, 0); + } else { + g_assert(false); + } + qobject_unref(e); + } + qobject_unref(resp); + + qtest_quit(qs); +} + int main(int argc, char **argv) { const char *args = NULL; @@ -278,6 +338,7 @@ int main(int argc, char **argv) if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64")) { qtest_add_data_func("/numa/pc/cpu/explicit", args, pc_numa_cpu); + qtest_add_data_func("/numa/pc/dynamic/cpu", args, pc_dynamic_cpu_cfg); } if (!strcmp(arch, "ppc64")) { diff --git a/tests/pca9552-test.c b/tests/pca9552-test.c new file mode 100644 index 0000000000..5466a67ed7 --- /dev/null +++ b/tests/pca9552-test.c @@ -0,0 +1,116 @@ +/* + * QTest testcase for the PCA9552 LED blinker + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "libqtest.h" +#include "libqos/i2c.h" +#include "hw/misc/pca9552_regs.h" + +#define PCA9552_TEST_ID "pca9552-test" +#define PCA9552_TEST_ADDR 0x60 + +static I2CAdapter *i2c; + +static uint8_t pca9552_get8(I2CAdapter *i2c, uint8_t addr, uint8_t reg) +{ + uint8_t resp[1]; + i2c_send(i2c, addr, ®, 1); + i2c_recv(i2c, addr, resp, 1); + return resp[0]; +} + +static void pca9552_set8(I2CAdapter *i2c, uint8_t addr, uint8_t reg, + uint8_t value) +{ + uint8_t cmd[2]; + uint8_t resp[1]; + + cmd[0] = reg; + cmd[1] = value; + i2c_send(i2c, addr, cmd, 2); + i2c_recv(i2c, addr, resp, 1); + g_assert_cmphex(resp[0], ==, cmd[1]); +} + +static void receive_autoinc(void) +{ + uint8_t resp; + uint8_t reg = PCA9552_LS0 | PCA9552_AUTOINC; + + i2c_send(i2c, PCA9552_TEST_ADDR, ®, 1); + + /* PCA9552_LS0 */ + i2c_recv(i2c, PCA9552_TEST_ADDR, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); + + /* PCA9552_LS1 */ + i2c_recv(i2c, PCA9552_TEST_ADDR, &resp, 1); + g_assert_cmphex(resp, ==, 0x55); + + /* PCA9552_LS2 */ + i2c_recv(i2c, PCA9552_TEST_ADDR, &resp, 1); + g_assert_cmphex(resp, ==, 0x55); + + /* PCA9552_LS3 */ + i2c_recv(i2c, PCA9552_TEST_ADDR, &resp, 1); + g_assert_cmphex(resp, ==, 0x54); +} + +static void send_and_receive(void) +{ + uint8_t value; + + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_LS0); + g_assert_cmphex(value, ==, 0x55); + + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_INPUT0); + g_assert_cmphex(value, ==, 0x0); + + /* Switch on LED 0 */ + pca9552_set8(i2c, PCA9552_TEST_ADDR, PCA9552_LS0, 0x54); + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_LS0); + g_assert_cmphex(value, ==, 0x54); + + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_INPUT0); + g_assert_cmphex(value, ==, 0x01); + + /* Switch on LED 12 */ + pca9552_set8(i2c, PCA9552_TEST_ADDR, PCA9552_LS3, 0x54); + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_LS3); + g_assert_cmphex(value, ==, 0x54); + + value = pca9552_get8(i2c, PCA9552_TEST_ADDR, PCA9552_INPUT1); + g_assert_cmphex(value, ==, 0x10); +} + +int main(int argc, char **argv) +{ + QTestState *s = NULL; + int ret; + + g_test_init(&argc, &argv, NULL); + + s = qtest_start("-machine n800 " + "-device pca9552,bus=i2c-bus.0,id=" PCA9552_TEST_ID + ",address=0x60"); + i2c = omap_i2c_create(s, OMAP2_I2C_1_BASE); + + qtest_add_func("/pca9552/tx-rx", send_and_receive); + qtest_add_func("/pca9552/rx-autoinc", receive_autoinc); + + ret = g_test_run(); + + if (s) { + qtest_quit(s); + } + g_free(i2c); + + return ret; +} diff --git a/tests/ptimer-test.c b/tests/ptimer-test.c index 41488896f7..b30aad0737 100644 --- a/tests/ptimer-test.c +++ b/tests/ptimer-test.c @@ -208,6 +208,7 @@ static void check_periodic(gconstpointer arg) bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; @@ -311,7 +312,7 @@ static void check_periodic(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_immediate_reload ? 0 : 10); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -506,6 +507,7 @@ static void check_run_with_delta_0(gconstpointer arg) bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); bool no_immediate_reload = (*policy & PTIMER_POLICY_NO_IMMEDIATE_RELOAD); bool no_round_down = (*policy & PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; @@ -515,7 +517,7 @@ static void check_run_with_delta_0(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_immediate_reload ? 0 : 99); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -563,7 +565,7 @@ static void check_run_with_delta_0(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, no_immediate_reload ? 0 : 99); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -609,6 +611,7 @@ static void check_periodic_with_load_0(gconstpointer arg) ptimer_state *ptimer = ptimer_init(bh, *policy); bool continuous_trigger = (*policy & PTIMER_POLICY_CONTINUOUS_TRIGGER); bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; @@ -617,7 +620,7 @@ static void check_periodic_with_load_0(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -667,6 +670,7 @@ static void check_oneshot_with_load_0(gconstpointer arg) QEMUBH *bh = qemu_bh_new(ptimer_trigger, NULL); ptimer_state *ptimer = ptimer_init(bh, *policy); bool no_immediate_trigger = (*policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER); + bool trig_only_on_dec = (*policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT); triggered = false; @@ -675,7 +679,7 @@ static void check_oneshot_with_load_0(gconstpointer arg) g_assert_cmpuint(ptimer_get_count(ptimer), ==, 0); - if (no_immediate_trigger) { + if (no_immediate_trigger || trig_only_on_dec) { g_assert_false(triggered); } else { g_assert_true(triggered); @@ -725,6 +729,10 @@ static void add_ptimer_tests(uint8_t policy) g_strlcat(policy_name, "no_counter_rounddown,", 256); } + if (policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) { + g_strlcat(policy_name, "trigger_only_on_decrement,", 256); + } + g_test_add_data_func_full( tmp = g_strdup_printf("/ptimer/set_count policy=%s", policy_name), g_memdup(&policy, 1), check_set_count, g_free); @@ -790,10 +798,15 @@ static void add_ptimer_tests(uint8_t policy) static void add_all_ptimer_policies_comb_tests(void) { - int last_policy = PTIMER_POLICY_NO_COUNTER_ROUND_DOWN; + int last_policy = PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT; int policy = PTIMER_POLICY_DEFAULT; for (; policy < (last_policy << 1); policy++) { + if ((policy & PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT) && + (policy & PTIMER_POLICY_NO_IMMEDIATE_TRIGGER)) { + /* Incompatible policy flag settings -- don't try to test them */ + continue; + } add_ptimer_tests(policy); } } diff --git a/tests/pvpanic-test.c b/tests/pvpanic-test.c index ebdf32c2e2..7461a7254f 100644 --- a/tests/pvpanic-test.c +++ b/tests/pvpanic-test.c @@ -28,7 +28,7 @@ static void test_panic(void) data = qdict_get_qdict(response, "data"); g_assert(qdict_haskey(data, "action")); g_assert_cmpstr(qdict_get_str(data, "action"), ==, "pause"); - QDECREF(response); + qobject_unref(response); } int main(int argc, char **argv) diff --git a/tests/q35-test.c b/tests/q35-test.c index 3eaedf4b24..7ea7acc9d8 100644 --- a/tests/q35-test.c +++ b/tests/q35-test.c @@ -109,7 +109,7 @@ static void test_smram_lock(void) response = qmp("{'execute': 'system_reset', 'arguments': {} }"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); /* check open is settable again */ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); diff --git a/tests/qapi-schema/allow-preconfig-test.err b/tests/qapi-schema/allow-preconfig-test.err new file mode 100644 index 0000000000..700d583306 --- /dev/null +++ b/tests/qapi-schema/allow-preconfig-test.err @@ -0,0 +1 @@ +tests/qapi-schema/allow-preconfig-test.json:2: 'allow-preconfig' of command 'allow-preconfig-test' should only use true value diff --git a/tests/qapi-schema/flat-union-incomplete-branch.exit b/tests/qapi-schema/allow-preconfig-test.exit similarity index 100% rename from tests/qapi-schema/flat-union-incomplete-branch.exit rename to tests/qapi-schema/allow-preconfig-test.exit diff --git a/tests/qapi-schema/allow-preconfig-test.json b/tests/qapi-schema/allow-preconfig-test.json new file mode 100644 index 0000000000..d9f0e914df --- /dev/null +++ b/tests/qapi-schema/allow-preconfig-test.json @@ -0,0 +1,2 @@ +# Check against allow-preconfig illegal value +{ 'command': 'allow-preconfig-test', 'allow-preconfig': 'some-string' } diff --git a/tests/qapi-schema/flat-union-incomplete-branch.out b/tests/qapi-schema/allow-preconfig-test.out similarity index 100% rename from tests/qapi-schema/flat-union-incomplete-branch.out rename to tests/qapi-schema/allow-preconfig-test.out diff --git a/tests/qapi-schema/bad-if-empty-list.err b/tests/qapi-schema/bad-if-empty-list.err new file mode 100644 index 0000000000..75fe6497bc --- /dev/null +++ b/tests/qapi-schema/bad-if-empty-list.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if-empty-list.json:2: 'if' condition [] is useless diff --git a/tests/qapi-schema/bad-if-empty-list.exit b/tests/qapi-schema/bad-if-empty-list.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if-empty-list.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if-empty-list.json b/tests/qapi-schema/bad-if-empty-list.json new file mode 100644 index 0000000000..94f2eb8670 --- /dev/null +++ b/tests/qapi-schema/bad-if-empty-list.json @@ -0,0 +1,3 @@ +# check empty 'if' list +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': [] } diff --git a/tests/qapi-schema/bad-if-empty-list.out b/tests/qapi-schema/bad-if-empty-list.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if-empty.err b/tests/qapi-schema/bad-if-empty.err new file mode 100644 index 0000000000..358bdc3e51 --- /dev/null +++ b/tests/qapi-schema/bad-if-empty.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if-empty.json:2: 'if' condition '' makes no sense diff --git a/tests/qapi-schema/bad-if-empty.exit b/tests/qapi-schema/bad-if-empty.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if-empty.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if-empty.json b/tests/qapi-schema/bad-if-empty.json new file mode 100644 index 0000000000..fe1dd4eca6 --- /dev/null +++ b/tests/qapi-schema/bad-if-empty.json @@ -0,0 +1,3 @@ +# check empty 'if' +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': '' } diff --git a/tests/qapi-schema/bad-if-empty.out b/tests/qapi-schema/bad-if-empty.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if-list.err b/tests/qapi-schema/bad-if-list.err new file mode 100644 index 0000000000..0af6316f78 --- /dev/null +++ b/tests/qapi-schema/bad-if-list.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if-list.json:2: 'if' condition '' makes no sense diff --git a/tests/qapi-schema/bad-if-list.exit b/tests/qapi-schema/bad-if-list.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if-list.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if-list.json b/tests/qapi-schema/bad-if-list.json new file mode 100644 index 0000000000..49ced9b9ca --- /dev/null +++ b/tests/qapi-schema/bad-if-list.json @@ -0,0 +1,3 @@ +# check invalid 'if' content +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': ['foo', ''] } diff --git a/tests/qapi-schema/bad-if-list.out b/tests/qapi-schema/bad-if-list.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if.err b/tests/qapi-schema/bad-if.err new file mode 100644 index 0000000000..c2e3f5f44c --- /dev/null +++ b/tests/qapi-schema/bad-if.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if.json:2: 'if' condition must be a string or a list of strings diff --git a/tests/qapi-schema/bad-if.exit b/tests/qapi-schema/bad-if.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if.json b/tests/qapi-schema/bad-if.json new file mode 100644 index 0000000000..3edd1a0bf2 --- /dev/null +++ b/tests/qapi-schema/bad-if.json @@ -0,0 +1,3 @@ +# check invalid 'if' type +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': { 'value': 'defined(TEST_IF_STRUCT)' } } diff --git a/tests/qapi-schema/bad-if.out b/tests/qapi-schema/bad-if.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 97ab4625ff..984cd8ed06 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -55,7 +55,7 @@ # # @two is undocumented ## -{ 'enum': 'Enum', 'data': [ 'one', 'two' ] } +{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' } ## # @Base: diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 63058b1590..35f3f1164c 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -3,6 +3,7 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE module doc-good.json enum Enum ['one', 'two'] + if ['defined(IFCOND)'] object Base member base1: Enum optional=False object Variant1 @@ -28,9 +29,9 @@ object q_obj_cmd-arg member arg2: str optional=True member arg3: bool optional=False command cmd q_obj_cmd-arg -> Object - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False command cmd-boxed Object -> None - gen=True success_response=True boxed=True oob=False + gen=True success_response=True boxed=True oob=False preconfig=False doc freeform body= = Section diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi index 0aed2300a5..e42eace474 100644 --- a/tests/qapi-schema/doc-good.texi +++ b/tests/qapi-schema/doc-good.texi @@ -89,6 +89,8 @@ Not documented @end table @code{two} is undocumented + +@b{If:} @code{defined(IFCOND)} @end deftp diff --git a/tests/qapi-schema/flat-union-incomplete-branch.err b/tests/qapi-schema/flat-union-incomplete-branch.err deleted file mode 100644 index e826bf0789..0000000000 --- a/tests/qapi-schema/flat-union-incomplete-branch.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/flat-union-incomplete-branch.json:6: Union 'TestUnion' data missing 'value2' branch diff --git a/tests/qapi-schema/flat-union-incomplete-branch.json b/tests/qapi-schema/flat-union-incomplete-branch.json deleted file mode 100644 index 25a411bc83..0000000000 --- a/tests/qapi-schema/flat-union-incomplete-branch.json +++ /dev/null @@ -1,9 +0,0 @@ -# we require all branches of the union to be covered -{ 'enum': 'TestEnum', - 'data': [ 'value1', 'value2' ] } -{ 'struct': 'TestTypeA', - 'data': { 'string': 'str' } } -{ 'union': 'TestUnion', - 'base': { 'type': 'TestEnum' }, - 'discriminator': 'type', - 'data': { 'value1': 'TestTypeA' } } diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out index 82213aa51d..24c976f473 100644 --- a/tests/qapi-schema/ident-with-escape.out +++ b/tests/qapi-schema/ident-with-escape.out @@ -5,4 +5,4 @@ module ident-with-escape.json object q_obj_fooA-arg member bar1: str optional=False command fooA q_obj_fooA-arg -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index 862678f8f4..bd8a48630e 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -3,6 +3,6 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE module indented-expr.json command eins None -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False command zwei None -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 06e30f452e..11aa4c8f8d 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -39,7 +39,7 @@ '*enum1': 'EnumOne' } } # intentional forward reference { 'enum': 'EnumOne', - 'data': [ 'value1', 'value2', 'value3' ] } + 'data': [ 'value1', 'value2', 'value3', 'value4' ] } { 'struct': 'UserDefZero', 'data': { 'integer': 'int' } } @@ -56,6 +56,9 @@ 'data': { 'string0': 'str', 'dict1': 'UserDefTwoDict' } } +{ 'struct': 'UserDefThree', + 'data': { 'string0': 'str' } } + # dummy struct to force generation of array types not otherwise mentioned { 'struct': 'ForceArrays', 'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'], @@ -76,7 +79,9 @@ 'discriminator': 'enum1', 'data': { 'value1' : 'UserDefA', 'value2' : 'UserDefB', - 'value3' : 'UserDefB' } } + 'value3' : 'UserDefB' + # 'value4' defaults to empty + } } { 'struct': 'UserDefUnionBase', 'base': 'UserDefZero', @@ -139,8 +144,8 @@ { 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' } { 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true } -# Smoke test on Out-Of-Band -{ 'command': 'an-oob-command', 'allow-oob': true } +# Smoke test on out-of-band and allow-preconfig-test +{ 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true } # For testing integer range flattening in opts-visitor. The following schema # corresponds to the option format: @@ -191,3 +196,26 @@ 'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'], 'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' }, 'returns': '__org.qemu_x-Union1' } + +# test 'if' condition handling + +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': 'defined(TEST_IF_STRUCT)' } + +{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ], + 'if': 'defined(TEST_IF_ENUM)' } + +{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' }, + 'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' } + +{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' }, + 'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' } + +{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' }, + 'returns': 'UserDefThree', + 'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] } + +{ 'command': 'TestCmdReturnDefThree', 'returns': 'UserDefThree' } + +{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' }, + 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 467577d770..0da92455da 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -16,14 +16,14 @@ object Empty1 object Empty2 base Empty1 command user_def_cmd0 Empty2 -> Empty2 - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False enum QEnumTwo ['value1', 'value2'] prefix QENUM_TWO object UserDefOne base UserDefZero member string: str optional=False member enum1: EnumOne optional=True -enum EnumOne ['value1', 'value2', 'value3'] +enum EnumOne ['value1', 'value2', 'value3', 'value4'] object UserDefZero member integer: int optional=False object UserDefTwoDictDict @@ -36,6 +36,8 @@ object UserDefTwoDict object UserDefTwo member string0: str optional=False member dict1: UserDefTwoDict optional=False +object UserDefThree + member string0: str optional=False object ForceArrays member unused1: UserDefOneList optional=False member unused2: UserDefTwoList optional=False @@ -52,6 +54,7 @@ object UserDefFlatUnion case value1: UserDefA case value2: UserDefB case value3: UserDefB + case value4: q_empty object UserDefUnionBase base UserDefZero member string: str optional=False @@ -143,31 +146,31 @@ object UserDefNativeListUnion case sizes: q_obj_sizeList-wrapper case any: q_obj_anyList-wrapper command user_def_cmd None -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_user_def_cmd1-arg member ud1a: UserDefOne optional=False command user_def_cmd1 q_obj_user_def_cmd1-arg -> None - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_user_def_cmd2-arg member ud1a: UserDefOne optional=False member ud1b: UserDefOne optional=True command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_guest-get-time-arg member a: int optional=False member b: int optional=True command guest-get-time q_obj_guest-get-time-arg -> int - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_guest-sync-arg member arg: any optional=False command guest-sync q_obj_guest-sync-arg -> any - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False command boxed-struct UserDefZero -> None - gen=True success_response=True boxed=True oob=False + gen=True success_response=True boxed=True oob=False preconfig=False command boxed-union UserDefNativeListUnion -> None - gen=True success_response=True boxed=True oob=False -command an-oob-command None -> None - gen=True success_response=True boxed=False oob=True + gen=True success_response=True boxed=True oob=False preconfig=False +command test-flags-command None -> None + gen=True success_response=True boxed=False oob=True preconfig=True object UserDefOptions member i64: intList optional=True member u64: uint64List optional=True @@ -231,4 +234,37 @@ object q_obj___org.qemu_x-command-arg member c: __org.qemu_x-Union2 optional=False member d: __org.qemu_x-Alt optional=False command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1 - gen=True success_response=True boxed=False oob=False + gen=True success_response=True boxed=False oob=False preconfig=False +object TestIfStruct + member foo: int optional=False + if ['defined(TEST_IF_STRUCT)'] +enum TestIfEnum ['foo', 'bar'] + if ['defined(TEST_IF_ENUM)'] +object q_obj_TestStruct-wrapper + member data: TestStruct optional=False +enum TestIfUnionKind ['foo'] + if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'] +object TestIfUnion + member type: TestIfUnionKind optional=False + tag type + case foo: q_obj_TestStruct-wrapper + if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'] +alternate TestIfAlternate + tag type + case foo: int + case bar: TestStruct + if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)'] +object q_obj_TestIfCmd-arg + member foo: TestIfStruct optional=False + if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] +command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree + gen=True success_response=True boxed=False oob=False preconfig=False + if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] +command TestCmdReturnDefThree None -> UserDefThree + gen=True success_response=True boxed=False oob=False preconfig=False +object q_obj_TestIfEvent-arg + member foo: TestIfStruct optional=False + if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] +event TestIfEvent q_obj_TestIfEvent-arg + boxed=False + if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index c1a144ba29..f514fe71e4 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -23,12 +23,13 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): def visit_include(self, name, info): print('include %s' % name) - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): print('enum %s %s' % (name, values)) if prefix: print(' prefix %s' % prefix) + self._print_if(ifcond) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): print('object %s' % name) if base: print(' base %s' % base.name) @@ -36,21 +37,25 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' member %s: %s optional=%s' % \ (m.name, m.type.name, m.optional)) self._print_variants(variants) + self._print_if(ifcond) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): print('alternate %s' % name) self._print_variants(variants) + self._print_if(ifcond) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): print('command %s %s -> %s' % \ (name, arg_type and arg_type.name, ret_type and ret_type.name)) - print(' gen=%s success_response=%s boxed=%s oob=%s' % \ - (gen, success_response, boxed, allow_oob)) + print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' % \ + (gen, success_response, boxed, allow_oob, allow_preconfig)) + self._print_if(ifcond) - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) print(' boxed=%s' % boxed) + self._print_if(ifcond) @staticmethod def _print_variants(variants): @@ -59,6 +64,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): for v in variants.variants: print(' case %s: %s' % (v.name, v.type.name)) + @staticmethod + def _print_if(ifcond, indent=4): + if ifcond: + print('%sif %s' % (' ' * indent, ifcond)) + try: schema = QAPISchema(sys.argv[1]) diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index e0d77ce2f5..4071ed6093 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -29,9 +29,14 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img - rm -f "$TEST_DIR/t.$IMGFMT.base_old" - rm -f "$TEST_DIR/t.$IMGFMT.base_new" + _cleanup_test_img + rm -f "$TEST_DIR/t.$IMGFMT.base_old" + rm -f "$TEST_DIR/t.$IMGFMT.base_new" + + rm -f "$TEST_DIR/subdir/t.$IMGFMT" + rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_old" + rm -f "$TEST_DIR/subdir/t.$IMGFMT.base_new" + rmdir "$TEST_DIR/subdir" 2> /dev/null } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -123,6 +128,77 @@ io_pattern readv $((13 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00 io_pattern readv $((14 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x11 io_pattern readv $((15 * CLUSTER_SIZE)) $CLUSTER_SIZE 0 1 0x00 +echo +echo "=== Test rebase in a subdirectory of the working directory ===" +echo + +# Clean up the old images beforehand so they do not interfere with +# this test +_cleanup + +mkdir "$TEST_DIR/subdir" + +# Relative to the overlay +BASE_OLD_OREL="t.$IMGFMT.base_old" +BASE_NEW_OREL="t.$IMGFMT.base_new" + +# Relative to $TEST_DIR (which is going to be our working directory) +OVERLAY_WREL="subdir/t.$IMGFMT" + +BASE_OLD="$TEST_DIR/subdir/$BASE_OLD_OREL" +BASE_NEW="$TEST_DIR/subdir/$BASE_NEW_OREL" +OVERLAY="$TEST_DIR/$OVERLAY_WREL" + +# Test done here: +# +# Backing (old): 11 11 -- 11 +# Backing (new): -- 22 22 11 +# Overlay: -- -- -- -- +# +# Rebasing works, we have verified that above. Here, we just want to +# see that rebasing is done for the correct target backing file. + +TEST_IMG=$BASE_OLD _make_test_img 1M +TEST_IMG=$BASE_NEW _make_test_img 1M +TEST_IMG=$OVERLAY _make_test_img -b "$BASE_OLD_OREL" 1M + +echo + +$QEMU_IO "$BASE_OLD" \ + -c "write -P 0x11 $((0 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + -c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \ + | _filter_qemu_io + +$QEMU_IO "$BASE_NEW" \ + -c "write -P 0x22 $((1 * CLUSTER_SIZE)) $((2 * CLUSTER_SIZE))" \ + -c "write -P 0x11 $((3 * CLUSTER_SIZE)) $((1 * CLUSTER_SIZE))" \ + | _filter_qemu_io + +echo + +pushd "$TEST_DIR" >/dev/null +$QEMU_IMG rebase -f "$IMGFMT" -b "$BASE_NEW_OREL" "$OVERLAY_WREL" +popd >/dev/null + +# Verify the backing path is correct +TEST_IMG=$OVERLAY _img_info | grep '^backing file' + +echo + +# Verify the data is correct +$QEMU_IO "$OVERLAY" \ + -c "read -P 0x11 $((0 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + -c "read -P 0x11 $((1 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + -c "read -P 0x00 $((2 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + -c "read -P 0x11 $((3 * CLUSTER_SIZE)) $CLUSTER_SIZE" \ + | _filter_qemu_io + +echo + +# Verify that cluster #3 is not allocated (because it is the same in +# $BASE_OLD and $BASE_NEW) +$QEMU_IMG map "$OVERLAY" | _filter_qemu_img_map + # success, all done echo "*** done" diff --git a/tests/qemu-iotests/024.out b/tests/qemu-iotests/024.out index 33cfaf5cfc..024dc786b3 100644 --- a/tests/qemu-iotests/024.out +++ b/tests/qemu-iotests/024.out @@ -141,4 +141,34 @@ read 65536/65536 bytes at offset 917504 === IO: pattern 0x00 read 65536/65536 bytes at offset 983040 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Test rebase in a subdirectory of the working directory === + +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_old', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/subdir/t.IMGFMT.base_new', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/subdir/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=t.IMGFMT.base_old + +wrote 131072/131072 bytes at offset 0 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 196608 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 131072/131072 bytes at offset 65536 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 65536/65536 bytes at offset 196608 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +backing file: t.IMGFMT.base_new (actual path: TEST_DIR/subdir/t.IMGFMT.base_new) + +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 131072 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 65536/65536 bytes at offset 196608 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Offset Length File +0 0x30000 TEST_DIR/subdir/t.IMGFMT +0x30000 0x10000 TEST_DIR/subdir/t.IMGFMT.base_new *** done diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index 7fadfbace5..582d254195 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -200,6 +200,23 @@ done done done +echo +echo === Avoid cluster leaks after temporary failure === +echo + +cat > "$TEST_DIR/blkdebug.conf" <&1 | _filter_qemu_io | _filter_testdir poke_file "$TEST_IMG" "$offset_size" "\x00\x00\x00\x00\x00\x00\x02\x00" poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x01" -{ $QEMU_IMG convert -s foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG convert -l foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir # success, all done diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 640a6dfd10..1dbc2ddc49 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -304,8 +304,7 @@ class TestParallelOps(iotests.QMPTestCase): result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6') self.assert_qmp(result, 'error/class', 'GenericError') - event = self.vm.get_qmp_event(wait=True) - self.assertEqual(event['event'], 'BLOCK_JOB_READY') + event = self.vm.event_wait(name='BLOCK_JOB_READY') self.assert_qmp(event, 'data/device', 'commit-drive0') self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp_absent(event, 'data/error') @@ -565,6 +564,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -596,6 +597,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.image_len) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -637,6 +640,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.image_len) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -663,6 +668,8 @@ class TestEIO(TestErrors): self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -722,6 +729,8 @@ class TestENOSPC(TestErrors): self.assert_qmp(event, 'data/offset', self.image_len) self.assert_qmp(event, 'data/len', self.image_len) completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -751,7 +760,9 @@ class TestStreamStop(iotests.QMPTestCase): time.sleep(0.1) events = self.vm.get_qmp_events(wait=False) - self.assertEqual(events, [], 'unexpected QMP event: %s' % events) + for e in events: + self.assert_qmp(e, 'event', 'JOB_STATUS_CHANGE') + self.assert_qmp(e, 'data/id', 'drive0') self.cancel_and_wait(resume=True) diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 90b5b4f2ad..1beb5e6dab 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -162,6 +162,8 @@ class TestSingleDrive(ImageCommitTestCase): elif event['event'] == 'BLOCK_JOB_CANCELLED': self.assert_qmp(event, 'data/device', 'drive0') cancelled = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') else: self.fail("Unexpected event %s" % (event['event'])) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index a860a31e9a..c20ac7da87 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -445,6 +445,8 @@ new_state = "1" self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/error', 'Input/output error') completed = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') self.assert_no_active_block_jobs() self.vm.shutdown() @@ -457,6 +459,10 @@ new_state = "1" self.assert_qmp(result, 'return', {}) event = self.vm.get_qmp_event(wait=True) + while event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') + event = self.vm.get_qmp_event(wait=True) + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') @@ -478,6 +484,10 @@ new_state = "1" self.assert_qmp(result, 'return', {}) event = self.vm.get_qmp_event(wait=True) + while event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') + event = self.vm.get_qmp_event(wait=True) + self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') @@ -608,7 +618,7 @@ new_state = "1" on_target_error='ignore') self.assert_qmp(result, 'return', {}) - event = self.vm.get_qmp_event(wait=True) + event = self.vm.event_wait(name='BLOCK_JOB_ERROR') self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'write') @@ -784,7 +794,12 @@ class TestGranularity(iotests.QMPTestCase): sync='full', target=target_img, mode='absolute-paths', granularity=8192) self.assert_qmp(result, 'return', {}) + event = self.vm.get_qmp_event(wait=60.0) + while event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', 'drive0') + event = self.vm.get_qmp_event(wait=60.0) + # Failures will manifest as COMPLETED/ERROR. self.assert_qmp(event, 'event', 'BLOCK_JOB_READY') self.complete_and_wait(drive='drive0', wait_ready=False) @@ -1015,9 +1030,9 @@ class TestOrphanedSource(iotests.QMPTestCase): 'read-only': 'on' } self.vm = iotests.VM() - self.vm.add_blockdev(self.qmp_to_opts(blk0)) - self.vm.add_blockdev(self.qmp_to_opts(blk1)) - self.vm.add_blockdev(self.qmp_to_opts(blk2)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk0)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk1)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk2)) self.vm.launch() def tearDown(self): diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index f617e25e24..ee9c820d0f 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -99,6 +99,21 @@ run_qemu -drive file="$TEST_IMG",driver=foo run_qemu -drive file="$TEST_IMG",driver=raw,format=qcow2 run_qemu -drive file="$TEST_IMG",driver=qcow2,format=qcow2 +echo +echo === Node names === +echo + +# Maximum length: 31 characters +run_qemu -drive file="$TEST_IMG",node-name=x123456789012345678901234567890 +run_qemu -drive file="$TEST_IMG",node-name=x1234567890123456789012345678901 + +# First character must be alphabetic +# Following characters alphanumeric or -._ +run_qemu -drive file="$TEST_IMG",node-name=All-Types.of_all0wed_chars +run_qemu -drive file="$TEST_IMG",node-name=123foo +run_qemu -drive file="$TEST_IMG",node-name=_foo +run_qemu -drive file="$TEST_IMG",node-name=foo#12 + echo echo === Device without drive === echo diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index dd9846d1ce..b7273505c7 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -47,6 +47,29 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' +=== Node names === + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) quit + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) quit + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name + + === Device without drive === Testing: -device VIRTIO_SCSI -device scsi-hd diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index b01f9a90d7..e9257fe318 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -47,6 +47,29 @@ Testing: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,driver=qcow2,format=qcow2: Cannot specify both 'driver' and 'format' +=== Node names === + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=x123456789012345678901234567890 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) quit + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=x1234567890123456789012345678901: Node name too long + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=All-Types.of_all0wed_chars +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) quit + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=123foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=123foo: Invalid node name + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=_foo +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=_foo: Invalid node name + +Testing: -drive file=TEST_DIR/t.qcow2,node-name=foo#12 +QEMU_PROG: -drive file=TEST_DIR/t.qcow2,node-name=foo#12: Invalid node name + + === Device without drive === Testing: -device VIRTIO_SCSI -device scsi-hd diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index 6c7407f499..74ad371885 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -33,6 +33,14 @@ _cleanup() } trap "_cleanup; exit \$status" 0 1 2 3 15 +# Sometimes the error line might be dumped before/after an event +# randomly. Mask it out for specific test that may trigger this +# uncertainty for current test for now. +_filter_io_error() +{ + sed '/Input\/output error/d' +} + # get standard environment, filters and checks . ./common.rc . ./common.filter @@ -440,6 +448,36 @@ echo "{'execute': 'qmp_capabilities'} -drive if=none,node-name=drive,file="$TEST_IMG",driver=qcow2 \ | _filter_qmp | _filter_qemu_io +echo +echo "=== Testing incoming inactive corrupted image ===" +echo + +_make_test_img 64M +# Create an unaligned L1 entry, so qemu will signal a corruption when +# reading from the covered area +poke_file "$TEST_IMG" "$l1_offset" "\x00\x00\x00\x00\x2a\x2a\x2a\x2a" + +# Inactive images are effectively read-only images, so this should be a +# non-fatal corruption (which does not modify the image) +echo "{'execute': 'qmp_capabilities'} + {'execute': 'human-monitor-command', + 'arguments': {'command-line': 'qemu-io drive \"read 0 512\"'}} + {'execute': 'quit'}" \ + | $QEMU -qmp stdio -nographic -nodefaults \ + -blockdev "{'node-name': 'drive', + 'driver': 'qcow2', + 'file': { + 'driver': 'file', + 'filename': '$TEST_IMG' + }}" \ + -incoming exec:'cat /dev/null' \ + 2>&1 \ + | _filter_qmp | _filter_qemu_io | _filter_io_error + +echo +# Image should not have been marked corrupt +_img_info --format-specific | grep 'corrupt:' + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index 25d5c3938b..d67c6234a4 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -129,7 +129,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: L2 table offset 0x42a00 unaligned (L1 index: 0); further corruption events will be suppressed -qemu-img: Error while amending options: Input/output error +qemu-img: Failed to turn zero into data clusters: Input/output error === Testing unaligned L2 entry === @@ -145,7 +145,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qcow2: Marking image as corrupt: Cluster allocation offset 0x52a00 unaligned (L2 offset: 0x40000, L2 index: 0); further corruption events will be suppressed -qemu-img: Error while amending options: Input/output error +qemu-img: Failed to turn zero into data clusters: Input/output error === Testing unaligned reftable entry === @@ -420,4 +420,17 @@ write failed: Input/output error {"return": ""} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + +=== Testing incoming inactive corrupted image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +QMP_VERSION +{"return": {}} +qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} +{"return": ""} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + corrupt: false *** done diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index e857ef9a7d..183f7dd690 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -358,18 +358,12 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) -qemu-img: Error while amending options: Invalid argument qemu-img: Lazy refcounts only supported with compatibility level 1.1 and above (use compat=1.1 or greater) -qemu-img: Error while amending options: Invalid argument qemu-img: Unknown compatibility level 0.42 -qemu-img: Error while amending options: Invalid argument qemu-img: Invalid parameter 'foo' qemu-img: Changing the cluster size is not supported -qemu-img: Error while amending options: Operation not supported qemu-img: Changing the encryption flag is not supported -qemu-img: Error while amending options: Operation not supported qemu-img: Cannot change preallocation mode -qemu-img: Error while amending options: Operation not supported === Testing correct handling of unset value === @@ -377,7 +371,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Should work: Should not work: qemu-img: Changing the cluster size is not supported -qemu-img: Error while amending options: Operation not supported === Testing zero expansion on inactive clusters === diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063 index e4f6ea9385..adc037c1f5 100755 --- a/tests/qemu-iotests/063 +++ b/tests/qemu-iotests/063 @@ -91,6 +91,15 @@ if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n "$TEST_IMG.orig" "$TEST_IMG" >/dev exit 1 fi +echo "== Regression testing for copy offloading bug ==" + +_make_test_img 1M +TEST_IMG="$TEST_IMG.target" _make_test_img 1M +$QEMU_IO -c 'write -P 1 0 512k' -c 'write -P 2 512k 512k' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'write -P 4 512k 512k' -c 'write -P 3 0 512k' "$TEST_IMG.target" | _filter_qemu_io +$QEMU_IMG convert -n -O $IMGFMT "$TEST_IMG" "$TEST_IMG.target" +$QEMU_IMG compare "$TEST_IMG" "$TEST_IMG.target" + echo "*** done" rm -f $seq.full status=0 diff --git a/tests/qemu-iotests/063.out b/tests/qemu-iotests/063.out index de1c99afd8..7b691b2c9e 100644 --- a/tests/qemu-iotests/063.out +++ b/tests/qemu-iotests/063.out @@ -7,4 +7,16 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 No errors were found on the image. == Testing conversion to a smaller file fails == Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2097152 +== Regression testing for copy offloading bug == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.target', fmt=IMGFMT size=1048576 +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 524288/524288 bytes at offset 0 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Images are identical. *** done diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 index 4dbe68e950..f0eb42f390 100755 --- a/tests/qemu-iotests/080 +++ b/tests/qemu-iotests/080 @@ -176,7 +176,7 @@ _make_test_img 64M { $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir poke_file "$TEST_IMG" "$offset_snap1_l1_offset" "\x00\x00\x00\x00\x00\x40\x02\x00" -{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir +{ $QEMU_IMG convert -l test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir { $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir { $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir @@ -190,7 +190,7 @@ _make_test_img 64M { $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir { $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00" -{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir +{ $QEMU_IMG convert -l test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir { $QEMU_IMG amend -o compat=0.10 $TEST_IMG; } 2>&1 | _filter_testdir { $QEMU_IO -c "open -o overlap-check.inactive-l2=on $TEST_IMG" \ -c 'write 0 4k'; } 2>&1 | _filter_qemu_io | _filter_testdir diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out index 4e0f7f7b92..281c7e0d1d 100644 --- a/tests/qemu-iotests/080.out +++ b/tests/qemu-iotests/080.out @@ -65,7 +65,7 @@ wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Failed to load snapshot: Snapshot L1 table offset invalid qemu-img: Snapshot L1 table offset invalid -qemu-img: Error while amending options: Invalid argument +qemu-img: Failed to turn zero into data clusters: Invalid argument Failed to flush the refcount block cache: Invalid argument write failed: Invalid argument qemu-img: Snapshot L1 table offset invalid @@ -88,7 +88,7 @@ wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Failed to load snapshot: Snapshot L1 table too large qemu-img: Snapshot L1 table too large -qemu-img: Error while amending options: File too large +qemu-img: Failed to turn zero into data clusters: File too large Failed to flush the refcount block cache: File too large write failed: File too large qemu-img: Snapshot L1 table too large diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082 index d5c83d45ed..3e605d52d1 100755 --- a/tests/qemu-iotests/082 +++ b/tests/qemu-iotests/082 @@ -97,6 +97,9 @@ run_qemu_img create -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_ run_qemu_img create -f $IMGFMT -o help run_qemu_img create -o help +# Try help option for a format that does not support creation +run_qemu_img create -f bochs -o help + echo echo === convert: Options specified more than once === @@ -151,6 +154,17 @@ run_qemu_img convert -O $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST run_qemu_img convert -O $IMGFMT -o help run_qemu_img convert -o help +# Try help option for a format that does not support creation +run_qemu_img convert -O bochs -o help + +echo +echo === convert: -C and other options === + +# Adding the help option to a command without other -o options +run_qemu_img convert -C -S 4k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target +run_qemu_img convert -C -S 8k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target +run_qemu_img convert -C -c -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target + echo echo === amend: Options specified more than once === @@ -201,6 +215,9 @@ run_qemu_img amend -f $IMGFMT -o backing_file="$TEST_IMG" -o ,, -o help "$TEST_I run_qemu_img amend -f $IMGFMT -o help run_qemu_img convert -o help +# Try help option for a format that does not support amendment +run_qemu_img amend -f bochs -o help + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out index 1527fbe1b7..19e9fb13ff 100644 --- a/tests/qemu-iotests/082.out +++ b/tests/qemu-iotests/082.out @@ -249,6 +249,9 @@ Testing: create -o help Supported options: size Virtual disk size +Testing: create -f bochs -o help +qemu-img: Format driver 'bochs' does not support image creation + === convert: Options specified more than once === Testing: create -f qcow2 TEST_DIR/t.qcow2 128M @@ -502,6 +505,20 @@ Testing: convert -o help Supported options: size Virtual disk size +Testing: convert -O bochs -o help +qemu-img: Format driver 'bochs' does not support image creation + +=== convert: -C and other options === + +Testing: convert -C -S 4k -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target +qemu-img: Cannot enable copy offloading when -S is used + +Testing: convert -C -S 8k -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target +qemu-img: Cannot enable copy offloading when -S is used + +Testing: convert -C -c -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target +qemu-img: Cannot enable copy offloading when -c is used + === amend: Options specified more than once === Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2 @@ -546,7 +563,7 @@ cluster_size: 65536 === amend: help for -o === Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -564,10 +581,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -585,10 +603,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -606,10 +625,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -627,10 +647,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -648,10 +669,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -669,10 +691,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -690,10 +713,11 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -711,7 +735,8 @@ cluster_size qcow2 cluster size preallocation Preallocation mode (allowed values: off, metadata, falloc, full) lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + +Note that not all of these options may be amendable. Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 @@ -731,7 +756,7 @@ Testing: amend -f qcow2 -o backing_file=TEST_DIR/t.qcow2 -o ,, -o help TEST_DIR/ qemu-img: Invalid option list: ,, Testing: amend -f qcow2 -o help -Supported options: +Creation options for 'qcow2': size Virtual disk size compat Compatibility level (0.10 or 1.1) backing_file File name of a base image @@ -750,7 +775,12 @@ preallocation Preallocation mode (allowed values: off, metadata, falloc, full lazy_refcounts Postpone refcount updates refcount_bits Width of a reference count entry in bits +Note that not all of these options may be amendable. + Testing: convert -o help Supported options: size Virtual disk size + +Testing: amend -f bochs -o help +qemu-img: Format driver 'bochs' does not support option amendment *** done diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086 index cd4494a660..84e3835071 100755 --- a/tests/qemu-iotests/086 +++ b/tests/qemu-iotests/086 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 raw -_supported_proto file nfs +_supported_proto file _supported_os Linux function run_qemu_img() diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index c3404a3171..68e344f8c1 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -237,7 +237,7 @@ class ThrottleTestGroupNames(iotests.QMPTestCase): if name: self.assertEqual(info["group"], name) else: - self.assertFalse(info.has_key('group')) + self.assertFalse('group' in info) return raise Exception("No group information found for '%s'" % device) diff --git a/tests/qemu-iotests/094.out b/tests/qemu-iotests/094.out index f52baffe70..665b630b08 100644 --- a/tests/qemu-iotests/094.out +++ b/tests/qemu-iotests/094.out @@ -2,10 +2,17 @@ QA output created by 094 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} *** done diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095 index 030adb22e1..72ecc22e1b 100755 --- a/tests/qemu-iotests/095 +++ b/tests/qemu-iotests/095 @@ -72,7 +72,7 @@ _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" _send_qemu_cmd $h "{ 'execute': 'block-commit', 'arguments': { 'device': 'test', - 'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED" + 'top': '"${TEST_IMG}.snp1"' } }" '"status": "null"' _cleanup_qemu diff --git a/tests/qemu-iotests/095.out b/tests/qemu-iotests/095.out index 73875cab40..8c093dfff3 100644 --- a/tests/qemu-iotests/095.out +++ b/tests/qemu-iotests/095.out @@ -11,8 +11,14 @@ virtual size: 5.0M (5242880 bytes) === Running QEMU Live Commit Test === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "test"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "test"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 104857600, "offset": 104857600, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "test"}} === Base image info after commit and resize === image: TEST_DIR/t.IMGFMT.base diff --git a/tests/qemu-iotests/096 b/tests/qemu-iotests/096 index aeeb3753cf..a69439602d 100755 --- a/tests/qemu-iotests/096 +++ b/tests/qemu-iotests/096 @@ -53,9 +53,9 @@ class TestLiveSnapshot(iotests.QMPTestCase): self.assertEqual(r['iops'], self.iops) self.assertEqual(r['iops_size'], self.iops_size) else: - self.assertFalse(r.has_key('group')) + self.assertFalse('group' in r) self.assertEqual(r['iops'], 0) - self.assertFalse(r.has_key('iops_size')) + self.assertFalse('iops_size' in r) def testSnapshot(self): self.checkConfig('base') diff --git a/tests/qemu-iotests/103.out b/tests/qemu-iotests/103.out index bd45d3875a..bd9eec3250 100644 --- a/tests/qemu-iotests/103.out +++ b/tests/qemu-iotests/103.out @@ -5,10 +5,10 @@ wrote 65536/65536 bytes at offset 0 === Testing invalid option combinations === -can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time +can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set at the same time can't open device TEST_DIR/t.IMGFMT: l2-cache-size may not exceed cache-size can't open device TEST_DIR/t.IMGFMT: refcount-cache-size may not exceed cache-size -can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set the same time +can't open device TEST_DIR/t.IMGFMT: cache-size, l2-cache-size and refcount-cache-size may not be set at the same time can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) can't open device TEST_DIR/t.IMGFMT: L2 cache entry size must be a power of two between 512 and the cluster size (65536) diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 index d70b574d88..acbd079136 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -64,7 +64,7 @@ function run_qemu() _send_qemu_cmd $QEMU_HANDLE '' "$qmp_event" if test "$qmp_event" = BLOCK_JOB_ERROR; then - _send_qemu_cmd $QEMU_HANDLE '' "BLOCK_JOB_COMPLETED" + _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"' fi _send_qemu_cmd $QEMU_HANDLE '{"execute":"query-block-jobs"}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index 8a9b93672b..ad0ee6fb48 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -6,23 +6,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -32,23 +44,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 512, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -58,23 +82,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 262144, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -84,23 +120,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -110,23 +158,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -136,23 +196,35 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -161,23 +233,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -186,23 +270,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -211,23 +307,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -236,23 +344,35 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": []} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. @@ -261,23 +381,37 @@ Images are identical. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 {"return": {}} WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw. -Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. -Specify the 'raw' format explicitly to remove the restrictions. + Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted. + Specify the 'raw' format explicitly to remove the restrictions. +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} Warning: Image size mismatch! Images are identical. *** done diff --git a/tests/qemu-iotests/112.out b/tests/qemu-iotests/112.out index 86f041075d..ae0318cabe 100644 --- a/tests/qemu-iotests/112.out +++ b/tests/qemu-iotests/112.out @@ -99,13 +99,11 @@ refcount bits: 64 === Amend to compat=0.10 === qemu-img: compat=0.10 requires refcount_bits=16 -qemu-img: Error while amending options: Operation not supported refcount bits: 64 No errors were found on the image. refcount bits: 16 refcount bits: 16 -qemu-img: Different refcount widths than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) -qemu-img: Error while amending options: Invalid argument +qemu-img: Refcount widths other than 16 bits require compatibility level 1.1 or above (use compat=1.1 or greater) refcount bits: 16 === Amend with snapshot === @@ -113,7 +111,6 @@ refcount bits: 16 wrote 16777216/16777216 bytes at offset 0 16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) qemu-img: Cannot decrease refcount entry width to 1 bits: Cluster at offset 0x50000 has a refcount of 2 -qemu-img: Error while amending options: Invalid argument No errors were found on the image. refcount bits: 16 No errors were found on the image. diff --git a/tests/qemu-iotests/113 b/tests/qemu-iotests/113 index 19b68b2727..4e09810905 100755 --- a/tests/qemu-iotests/113 +++ b/tests/qemu-iotests/113 @@ -38,16 +38,17 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -# We can only test one format here because we need its sample file -_supported_fmt bochs -_supported_proto nbd +# Some of these test cases use bochs, but others do use raw, so this +# is only half a lie. +_supported_fmt raw +_supported_proto file _supported_os Linux echo echo '=== Unsupported image creation in qemu-img create ===' echo -$QEMU_IMG create -f $IMGFMT nbd://example.com 2>&1 64M | _filter_imgfmt +$QEMU_IMG create -f bochs nbd://example.com 2>&1 64M echo echo '=== Unsupported image creation in qemu-img convert ===' @@ -56,17 +57,15 @@ echo # We could use any input image format here, but this is a bochs test, so just # use the bochs image _use_sample_img empty.bochs.bz2 -$QEMU_IMG convert -f $IMGFMT -O $IMGFMT "$TEST_IMG" nbd://example.com 2>&1 \ - | _filter_imgfmt +$QEMU_IMG convert -f bochs -O bochs "$TEST_IMG" nbd://example.com echo echo '=== Unsupported format in qemu-img amend ===' echo -# The protocol does not matter here -_use_sample_img empty.bochs.bz2 -$QEMU_IMG amend -f $IMGFMT -o foo=bar "$TEST_IMG" 2>&1 | _filter_imgfmt - +TEST_IMG="$TEST_DIR/t.$IMGFMT" +_make_test_img 1M +$QEMU_IMG amend -f $IMGFMT -o size=2M "$TEST_IMG" 2>&1 | _filter_imgfmt # success, all done echo diff --git a/tests/qemu-iotests/113.out b/tests/qemu-iotests/113.out index 00bdfd6887..3557e2bbf0 100644 --- a/tests/qemu-iotests/113.out +++ b/tests/qemu-iotests/113.out @@ -2,14 +2,15 @@ QA output created by 113 === Unsupported image creation in qemu-img create === -qemu-img: nbd://example.com: Format driver 'IMGFMT' does not support image creation +qemu-img: nbd://example.com: Format driver 'bochs' does not support image creation === Unsupported image creation in qemu-img convert === -qemu-img: Format driver 'IMGFMT' does not support image creation +qemu-img: Format driver 'bochs' does not support image creation === Unsupported format in qemu-img amend === -qemu-img: Format driver 'IMGFMT' does not support any options to amend +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +qemu-img: Format driver 'IMGFMT' does not support option amendment *** done diff --git a/tests/qemu-iotests/118 b/tests/qemu-iotests/118 index a0469b570e..ff3b2ae3e7 100755 --- a/tests/qemu-iotests/118 +++ b/tests/qemu-iotests/118 @@ -390,14 +390,14 @@ class TestChangeReadOnly(ChangeBaseClass): def tearDown(self): self.vm.shutdown() - os.chmod(old_img, 0666) - os.chmod(new_img, 0666) + os.chmod(old_img, 0o666) + os.chmod(new_img, 0o666) os.remove(old_img) os.remove(new_img) def test_ro_ro_retain(self): - os.chmod(old_img, 0444) - os.chmod(new_img, 0444) + os.chmod(old_img, 0o444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -417,7 +417,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_ro_rw_retain(self): - os.chmod(old_img, 0444) + os.chmod(old_img, 0o444) self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -437,7 +437,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_rw_ro_retain(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -459,7 +459,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) def test_ro_rw(self): - os.chmod(old_img, 0444) + os.chmod(old_img, 0o444) self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -480,7 +480,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_rw_ro(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -521,7 +521,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_make_ro_rw(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -542,7 +542,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) def test_make_rw_ro_by_retain(self): - os.chmod(old_img, 0444) + os.chmod(old_img, 0o444) self.vm.add_drive(old_img, 'media=disk,read-only=on', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -562,7 +562,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', new_img) def test_make_ro_rw_by_retain(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() @@ -582,7 +582,7 @@ class TestChangeReadOnly(ChangeBaseClass): self.assert_qmp(result, 'return[0]/inserted/image/filename', old_img) def test_rw_ro_cycle(self): - os.chmod(new_img, 0444) + os.chmod(new_img, 0o444) self.vm.add_drive(old_img, 'media=disk', 'none') self.vm.add_device('floppy,drive=drive0,id=%s' % self.device_name) self.vm.launch() diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122 index 6cf4fcb866..d8c8ad722d 100755 --- a/tests/qemu-iotests/122 +++ b/tests/qemu-iotests/122 @@ -76,6 +76,48 @@ $QEMU_IMG convert -O $IMGFMT -c -B "$TEST_IMG".base "$TEST_IMG" "$TEST_IMG".orig $QEMU_IO -c "read -P 0 0 3M" "$TEST_IMG".orig 2>&1 | _filter_qemu_io | _filter_testdir +echo +echo "=== Converting to an overlay larger than its backing file ===" +echo + +TEST_IMG="$TEST_IMG".base _make_test_img 256M +# Needs to be at least how much an L2 table covers +# (64 kB/entry * 64 kB / 8 B/entry = 512 MB) +# That way, qcow2 will yield at least two status request responses. +# With just a single response, it would always say "Allocated in the +# backing file", so the optimization qemu-img convert tries to do is +# done automatically. Once it has to be queried twice, however (and +# one of the queries is completely after the end of the backing file), +# the block layer will automatically add a ZERO flag that qemu-img +# convert used to follow up with a zero write to the target. +# We do not want such a zero write, however, because we are past the +# end of the backing file on the target as well, so we do not need to +# write anything there. +_make_test_img -b "$TEST_IMG".base 768M + +# Use compat=0.10 as the output so there is no zero cluster support +$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \ + "$TEST_IMG" "$TEST_IMG".orig +# See that nothing has been allocated past 64M +$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map + +echo + +# Just before the end of the backing file +$QEMU_IO -c 'write -P 0x11 255M 1M' "$TEST_IMG".base 2>&1 | _filter_qemu_io +# Somewhere in the second L2 table +$QEMU_IO -c 'write -P 0x22 600M 1M' "$TEST_IMG" 2>&1 | _filter_qemu_io + +$QEMU_IMG convert -O $IMGFMT -B "$TEST_IMG".base -o compat=0.10 \ + "$TEST_IMG" "$TEST_IMG".orig + +$QEMU_IMG map "$TEST_IMG".orig | _filter_qemu_img_map +$QEMU_IO -c 'read -P 0x11 255M 1M' \ + -c 'read -P 0x22 600M 1M' \ + "$TEST_IMG".orig \ + | _filter_qemu_io + + echo echo "=== Concatenate multiple source images ===" echo @@ -129,53 +171,6 @@ $QEMU_IO -c "read -P 0x44 1023k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _fil $QEMU_IO -c "read -P 0 1024k 1022k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir -echo -echo "=== Corrupted size field in compressed cluster descriptor ===" -echo -# Create an empty image and fill half of it with compressed data. -# 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 -$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 - -# Reduce size of compressed data to 4 sectors: this corrupts the image. -poke_file "$TEST_IMG" $((0x800000)) "\x40\x06" -$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir - -# 'qemu-img check' however doesn't see anything wrong because it -# doesn't try to decompress the data and the refcounts are consistent. -# TODO: update qemu-img so this can be detected. -_check_test_img - -# Increase size of compressed data to the maximum (8192 sectors). -# This makes QEMU read more data (8192 sectors instead of 5, host -# addresses [0xa00000, 0xdfffff]), but the decompression algorithm -# stops once we have enough to restore the uncompressed cluster, so -# the rest of the data is ignored. -poke_file "$TEST_IMG" $((0x800000)) "\x7f\xfe" -# Do it also for the second compressed cluster (L2 entry at 0x800008). -# In this case the compressed data would span 3 host clusters -# (host addresses: [0xa00802, 0xe00801]) -poke_file "$TEST_IMG" $((0x800008)) "\x7f\xfe" - -# Here the image is too small so we're asking QEMU to read beyond the -# end of the image. -$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir -# But if we grow the image we won't be reading beyond its end anymore. -$QEMU_IO -c "write -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir -$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir - -# The refcount data is however wrong because due to the increased size -# of the compressed data it now reaches the following host clusters. -# This can be repaired by qemu-img check by increasing the refcount of -# those clusters. -# TODO: update qemu-img to correct the compressed cluster size instead. -_check_test_img -r all -$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir -$QEMU_IO -c "read -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir - echo echo "=== Full allocation with -S 0 ===" echo diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out index a6b7fe007e..c576705284 100644 --- a/tests/qemu-iotests/122.out +++ b/tests/qemu-iotests/122.out @@ -28,6 +28,24 @@ read 3145728/3145728 bytes at offset 0 read 3145728/3145728 bytes at offset 0 3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +=== Converting to an overlay larger than its backing file === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=268435456 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=805306368 backing_file=TEST_DIR/t.IMGFMT.base +Offset Length File + +wrote 1048576/1048576 bytes at offset 267386880 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1048576/1048576 bytes at offset 629145600 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length File +0xff00000 0x100000 TEST_DIR/t.IMGFMT.base +0x25800000 0x100000 TEST_DIR/t.IMGFMT.orig +read 1048576/1048576 bytes at offset 267386880 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 629145600 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + === Concatenate multiple source images === Formatting 'TEST_DIR/t.IMGFMT.1', fmt=IMGFMT size=4194304 @@ -99,39 +117,6 @@ read 1024/1024 bytes at offset 1047552 read 1046528/1046528 bytes at offset 1048576 1022 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -=== Corrupted size field in compressed cluster descriptor === - -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8388608 -wrote 2097152/2097152 bytes at offset 0 -2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 2097152/2097152 bytes at offset 2097152 -2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read failed: Input/output error -No errors were found on the image. -read 4194304/4194304 bytes at offset 0 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -wrote 4194304/4194304 bytes at offset 4194304 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4194304/4194304 bytes at offset 0 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -ERROR cluster 6 refcount=1 reference=3 -ERROR cluster 7 refcount=1 reference=2 -Repairing cluster 6 refcount=1 reference=3 -Repairing cluster 7 refcount=1 reference=2 -Repairing OFLAG_COPIED data cluster: l2_entry=8000000000c00000 refcount=3 -Repairing OFLAG_COPIED data cluster: l2_entry=8000000000e00000 refcount=2 -The following inconsistencies were found and repaired: - - 0 leaked clusters - 4 corruptions - -Double checking the fixed image now... -No errors were found on the image. -read 4194304/4194304 bytes at offset 0 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -read 4194304/4194304 bytes at offset 4194304 -4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - === Full allocation with -S 0 === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 @@ -209,12 +194,12 @@ wrote 1024/1024 bytes at offset 17408 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) convert -S 4k -[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 1024, "length": 7168, "depth": 0, "zero": true, "data": false}, -{ "start": 8192, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false}, -{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}] +[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 4096, "length": 4096, "depth": 0, "zero": true, "data": false}, +{ "start": 8192, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 12288, "length": 4096, "depth": 0, "zero": true, "data": false}, +{ "start": 16384, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 20480, "length": 67088384, "depth": 0, "zero": true, "data": false}] convert -c -S 4k [{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}, @@ -225,10 +210,8 @@ convert -c -S 4k { "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}] convert -S 8k -[{ "start": 0, "length": 9216, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 9216, "length": 8192, "depth": 0, "zero": true, "data": false}, -{ "start": 17408, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -{ "start": 18432, "length": 67090432, "depth": 0, "zero": true, "data": false}] +[{ "start": 0, "length": 24576, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 24576, "length": 67084288, "depth": 0, "zero": true, "data": false}] convert -c -S 8k [{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true}, diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124 index 8e76e62f93..3ea4ac53f5 100755 --- a/tests/qemu-iotests/124 +++ b/tests/qemu-iotests/124 @@ -151,10 +151,17 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): return self.wait_qmp_backup(kwargs['device'], error) + def ignore_job_status_change_events(self): + while True: + e = self.vm.event_wait(name="JOB_STATUS_CHANGE") + if e['data']['status'] == 'null': + break + def wait_qmp_backup(self, device, error='Input/output error'): event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED", match={'data': {'device': device}}) self.assertNotEqual(event, None) + self.ignore_job_status_change_events() try: failure = self.dictpath(event, 'data/error') @@ -172,6 +179,7 @@ class TestIncrementalBackupBase(iotests.QMPTestCase): event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED', match={'data': {'device': device}}) self.assertNotEqual(event, None) + self.ignore_job_status_change_events() def create_anchor_backup(self, drive=None): diff --git a/tests/qemu-iotests/126.out b/tests/qemu-iotests/126.out index 50d73080fa..17d03d5248 100644 --- a/tests/qemu-iotests/126.out +++ b/tests/qemu-iotests/126.out @@ -3,7 +3,7 @@ QA output created by 126 === Testing plain files === Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864 -Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'file:TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864 === Testing relative backing filename resolution === diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out index 543d075005..83b522d4c2 100644 --- a/tests/qemu-iotests/127.out +++ b/tests/qemu-iotests/127.out @@ -5,10 +5,17 @@ Formatting 'TEST_DIR/t.IMGFMT.overlay1', fmt=IMGFMT size=65536 backing_file=TEST wrote 42/42 bytes at offset 0 42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "mirror"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} *** done diff --git a/tests/qemu-iotests/136 b/tests/qemu-iotests/136 index 88b97ea7c6..a154d8ef9d 100755 --- a/tests/qemu-iotests/136 +++ b/tests/qemu-iotests/136 @@ -203,7 +203,7 @@ sector = "%d" if (self.accounted_ops(read = True, write = True, flush = True) != 0): self.assertLess(0, stats['idle_time_ns']) else: - self.assertFalse(stats.has_key('idle_time_ns')) + self.assertFalse('idle_time_ns' in stats) # This test does not alter these, so they must be all 0 self.assertEqual(0, stats['rd_merged']) diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out index e28e1eadba..6a2ffc71fd 100644 --- a/tests/qemu-iotests/137.out +++ b/tests/qemu-iotests/137.out @@ -16,13 +16,13 @@ read 33554432/33554432 bytes at offset 0 === Try setting some invalid values === Parameter 'lazy-refcounts' expects 'on' or 'off' -cache-size, l2-cache-size and refcount-cache-size may not be set the same time +cache-size, l2-cache-size and refcount-cache-size may not be set at the same time l2-cache-size may not exceed cache-size refcount-cache-size may not exceed cache-size L2 cache size too big L2 cache entry size must be a power of two between 512 and the cluster size (65536) L2 cache entry size must be a power of two between 512 and the cluster size (65536) -L2 cache size too big +Refcount cache size too big Conflicting values for qcow2 options 'overlap-check' ('constant') and 'overlap-check.template' ('all') Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141 index 2f9d7b9bc2..4246d387a1 100755 --- a/tests/qemu-iotests/141 +++ b/tests/qemu-iotests/141 @@ -107,7 +107,7 @@ test_blockjob \ 'format': '$IMGFMT', 'sync': 'none'}}" \ 'return' \ - 'BLOCK_JOB_CANCELLED' + '"status": "null"' echo echo '=== Testing drive-mirror ===' @@ -124,7 +124,7 @@ test_blockjob \ 'format': '$IMGFMT', 'sync': 'none'}}" \ 'BLOCK_JOB_READY' \ - 'BLOCK_JOB_COMPLETED' + '"status": "null"' echo echo '=== Testing active block-commit ===' @@ -138,7 +138,7 @@ test_blockjob \ "{'execute': 'block-commit', 'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \ 'BLOCK_JOB_READY' \ - 'BLOCK_JOB_COMPLETED' + '"status": "null"' echo echo '=== Testing non-active block-commit ===' @@ -157,7 +157,7 @@ test_blockjob \ 'top': '$TEST_DIR/m.$IMGFMT', 'speed': 1}}" \ 'return' \ - 'BLOCK_JOB_CANCELLED' + '"status": "null"' echo echo '=== Testing block-stream ===' @@ -170,8 +170,7 @@ echo $QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io # With some data to stream (and @speed set to 1), block-stream will not complete -# until we send the block-job-cancel command. Therefore, no event other than -# BLOCK_JOB_CANCELLED will be emitted. +# until we send the block-job-cancel command. test_blockjob \ "{'execute': 'block-stream', @@ -179,7 +178,7 @@ test_blockjob \ 'device': 'drv0', 'speed': 1}}" \ 'return' \ - 'BLOCK_JOB_CANCELLED' + '"status": "null"' _cleanup_qemu diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out index 82e763b68d..f252c86875 100644 --- a/tests/qemu-iotests/141.out +++ b/tests/qemu-iotests/141.out @@ -8,31 +8,50 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m. {"return": {}} Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing drive-mirror === {"return": {}} Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing active block-commit === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing non-active block-commit === @@ -40,10 +59,15 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t. wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} === Testing block-stream === @@ -51,9 +75,14 @@ wrote 1048576/1048576 bytes at offset 0 wrote 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} {"return": {}} {"error": {"class": "GenericError", "desc": "Node drv0 is in use"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}} {"return": {}} *** done diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 index 00de3c33cf..4b915718cd 100755 --- a/tests/qemu-iotests/144 +++ b/tests/qemu-iotests/144 @@ -93,7 +93,7 @@ _send_qemu_cmd $h "{ 'execute': 'block-job-complete', 'arguments': { 'device': 'virtio0' } - }" "COMPLETED" + }" '"status": "null"' echo echo === Performing Live Snapshot 2 === diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out index 014b2817ee..55299201e4 100644 --- a/tests/qemu-iotests/144.out +++ b/tests/qemu-iotests/144.out @@ -12,10 +12,17 @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/ === Performing block-commit on active layer === +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} {"return": {}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "virtio0"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "virtio0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "virtio0"}} === Performing Live Snapshot 2 === diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index 223cd68ad5..9e0cad76f9 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149 @@ -20,6 +20,7 @@ # Exercise the QEMU 'luks' block driver to validate interoperability # with the Linux dm-crypt + cryptsetup implementation +from __future__ import print_function import subprocess import os import os.path @@ -258,7 +259,7 @@ def qemu_io_image_args(config, dev=False): if dev: return [ "--image-opts", - "driver=file,filename=%s" % config.device_path()] + "driver=host_device,filename=%s" % config.device_path()] else: return [ "--object", @@ -376,7 +377,7 @@ def test_once(config, qemu_img=False): finally: iotests.log("# Delete image") delete_image(config) - print + print() # Obviously we only work with the luks image format diff --git a/tests/qemu-iotests/149.out b/tests/qemu-iotests/149.out index 5dea00bfa8..1407ce6dad 100644 --- a/tests/qemu-iotests/149.out +++ b/tests/qemu-iotests/149.out @@ -7,13 +7,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -43,13 +43,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -67,13 +67,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha1.img', fmt=luks size=439804651 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -103,13 +103,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -127,13 +127,13 @@ sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 512 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -163,13 +163,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -187,13 +187,13 @@ Formatting 'TEST_DIR/luks-twofish-256-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -223,13 +223,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -247,13 +247,13 @@ sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 512 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -283,13 +283,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -307,13 +307,13 @@ Formatting 'TEST_DIR/luks-serpent-256-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -343,13 +343,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-256-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -367,13 +367,13 @@ sudo cryptsetup -q -v luksFormat --cipher cast5-cbc-plain64 --key-size 128 --has sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -403,13 +403,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -427,13 +427,13 @@ Formatting 'TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -463,13 +463,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-cast5-128-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -488,13 +488,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain --key-size 256 --hash sh sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -524,13 +524,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -548,13 +548,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-plain-sha1.img', fmt=luks size=43980465111 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -584,13 +584,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -608,13 +608,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64 --key-size 256 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -644,13 +644,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -668,13 +668,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha1.img', fmt=luks size=439804651 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -704,13 +704,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -728,13 +728,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 -- sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -764,13 +764,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -788,13 +788,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img', fmt=luks size=4398 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -824,13 +824,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -848,13 +848,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-essiv:sha256 --key-size 512 -- sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -884,13 +884,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -908,13 +908,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img', fmt=luks size=4398 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -944,13 +944,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-essiv-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -968,13 +968,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 256 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1004,13 +1004,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1028,13 +1028,13 @@ Formatting 'TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img', fmt=luks size=43 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1064,13 +1064,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-128-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1088,13 +1088,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 384 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1124,13 +1124,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1148,13 +1148,13 @@ Formatting 'TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img', fmt=luks size=43 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1184,13 +1184,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-192-xts-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1208,13 +1208,13 @@ sudo cryptsetup -q -v luksFormat --cipher twofish-xts-plain64 --key-size 256 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1244,13 +1244,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1268,13 +1268,13 @@ Formatting 'TEST_DIR/luks-twofish-128-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1304,13 +1304,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-twofish-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1329,13 +1329,13 @@ sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 256 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1365,13 +1365,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1389,13 +1389,13 @@ Formatting 'TEST_DIR/luks-serpent-128-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1425,13 +1425,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-128-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1449,13 +1449,13 @@ sudo cryptsetup -q -v luksFormat --cipher serpent-xts-plain64 --key-size 384 --h sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1485,13 +1485,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1509,13 +1509,13 @@ Formatting 'TEST_DIR/luks-serpent-192-xts-plain64-sha1.img', fmt=luks size=43980 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1545,13 +1545,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-serpent-192-xts-plain64-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1571,13 +1571,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1607,13 +1607,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1631,13 +1631,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha224.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1667,13 +1667,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha224 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1691,13 +1691,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1727,13 +1727,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1751,13 +1751,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha256.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1787,13 +1787,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha256 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1811,13 +1811,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1847,13 +1847,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1871,13 +1871,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha384.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1907,13 +1907,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha384 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1931,13 +1931,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1967,13 +1967,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -1991,13 +1991,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha512.img', fmt=luks size=4398046 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2027,13 +2027,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-sha512 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2051,13 +2051,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain64 --key-size 512 --hash sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2087,13 +2087,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2111,13 +2111,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img', fmt=luks size=4398 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2147,13 +2147,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain64-ripemd160 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2171,13 +2171,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-xts-plain --key-size 512 --hash sh sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img qiotest-145-aes-256-xts-plain-sha1-pwslot3 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2207,13 +2207,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwslot3.img qiotest-145-aes-256-xts-plain-sha1-pwslot3 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwslot3 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2245,13 +2245,13 @@ sudo cryptsetup -q -v luksAddKey TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2281,13 +2281,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2305,13 +2305,13 @@ Formatting 'TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img', fmt=luks size= sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2341,13 +2341,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-xts-plain-sha1-pwallslots read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2365,13 +2365,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-essiv:sha256 --key-size 256 -- sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2401,13 +2401,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2425,13 +2425,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img', fmt=luks size=439804 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2461,13 +2461,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-essiv-auto-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2485,13 +2485,13 @@ sudo cryptsetup -q -v luksFormat --cipher aes-cbc-plain64:sha256 --key-size 256 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2521,13 +2521,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2545,13 +2545,13 @@ Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img', fmt=luks size=43 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Write test pattern 0xa7 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c write -P 0xa7 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c write -P 0xa7 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Write test pattern 0x13 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c write -P 0x13 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 wrote 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -2581,13 +2581,13 @@ wrote 10485760/10485760 bytes at offset 3298534883328 sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1 # Read test pattern 0x91 sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c read -P 0x91 100M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c read -P 0x91 100M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 104857600 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) # Read test pattern 0x5e sudo chown UID:GID /dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 -qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=file,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 +qemu-io -c read -P 0x5e 3145728M 10M --image-opts driver=host_device,filename=/dev/mapper/qiotest-145-aes-256-cbc-plain64-sha256-sha1 read 10485760/10485760 bytes at offset 3298534883328 10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151 new file mode 100755 index 0000000000..fe53b9f446 --- /dev/null +++ b/tests/qemu-iotests/151 @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# +# Tests for active mirroring +# +# Copyright (C) 2018 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 . +# + +import os +import iotests +from iotests import qemu_img + +source_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt) +target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt) + +class TestActiveMirror(iotests.QMPTestCase): + image_len = 128 * 1024 * 1024 # MB + potential_writes_in_flight = True + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, source_img, '128M') + qemu_img('create', '-f', iotests.imgfmt, target_img, '128M') + + blk_source = {'id': 'source', + 'if': 'none', + 'node-name': 'source-node', + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', + 'filename': source_img}} + + blk_target = {'node-name': 'target-node', + 'driver': iotests.imgfmt, + 'file': {'driver': 'file', + 'filename': target_img}} + + self.vm = iotests.VM() + self.vm.add_drive_raw(self.vm.qmp_to_opts(blk_source)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blk_target)) + self.vm.add_device('virtio-blk,drive=source') + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + + if not self.potential_writes_in_flight: + self.assertTrue(iotests.compare_images(source_img, target_img), + 'mirror target does not match source') + + os.remove(source_img) + os.remove(target_img) + + def doActiveIO(self, sync_source_and_target): + # Fill the source image + self.vm.hmp_qemu_io('source', + 'write -P 1 0 %i' % self.image_len); + + # Start some background requests + for offset in range(1 * self.image_len / 8, 3 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -P 2 %i 1M' % offset) + for offset in range(2 * self.image_len / 8, 3 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) + + # Start the block job + result = self.vm.qmp('blockdev-mirror', + job_id='mirror', + filter_node_name='mirror-node', + device='source-node', + target='target-node', + sync='full', + copy_mode='write-blocking') + self.assert_qmp(result, 'return', {}) + + # Start some more requests + for offset in range(3 * self.image_len / 8, 5 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) + for offset in range(4 * self.image_len / 8, 5 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) + + # Wait for the READY event + self.wait_ready(drive='mirror') + + # Now start some final requests; all of these (which land on + # the source) should be settled using the active mechanism. + # The mirror code itself asserts that the source BDS's dirty + # bitmap will stay clean between READY and COMPLETED. + for offset in range(5 * self.image_len / 8, 7 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) + for offset in range(6 * self.image_len / 8, 7 * self.image_len / 8, 1024 * 1024): + self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) + + if sync_source_and_target: + # If source and target should be in sync after the mirror, + # we have to flush before completion + self.vm.hmp_qemu_io('source', 'aio_flush') + self.potential_writes_in_flight = False + + self.complete_and_wait(drive='mirror', wait_ready=False) + + def testActiveIO(self): + self.doActiveIO(False) + + def testActiveIOFlushed(self): + self.doActiveIO(True) + + + +if __name__ == '__main__': + iotests.main(supported_fmts=['qcow2', 'raw']) diff --git a/tests/qemu-iotests/151.out b/tests/qemu-iotests/151.out new file mode 100644 index 0000000000..fbc63e62f8 --- /dev/null +++ b/tests/qemu-iotests/151.out @@ -0,0 +1,5 @@ +.. +---------------------------------------------------------------------- +Ran 2 tests + +OK diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index a0fd815483..0daeb1b005 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -137,6 +137,24 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do _run_cmd $QEMU_IMG dd $L if="${TEST_IMG}" of="${TEST_IMG}.convert" bs=512 count=1 _run_cmd $QEMU_IMG bench $L -c 1 "${TEST_IMG}" _run_cmd $QEMU_IMG bench $L -w -c 1 "${TEST_IMG}" + + # qemu-img create does not support -U + if [ -z "$L" ]; then + _run_cmd $QEMU_IMG create -f $IMGFMT "${TEST_IMG}" \ + -b ${TEST_IMG}.base + # Read the file format. It used to be the case that + # file-posix simply truncated the file, but the qcow2 + # driver then failed to format it because it was unable + # to acquire the necessary WRITE permission. However, the + # truncation was already wrong, and the whole process + # resulted in the file being completely empty and thus its + # format would be detected to be raw. + # So we read it here to see that creation either completed + # successfully (thus the format is qcow2) or it aborted + # before the file was changed at all (thus the format stays + # qcow2). + _img_info -U | grep 'file format' + fi done _send_qemu_cmd $h "{ 'execute': 'quit', }" "" echo @@ -144,6 +162,7 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do _cleanup_qemu done +test_opts="read-only=off read-only=on read-only=on,force-share=on" for opt1 in $test_opts; do for opt2 in $test_opts; do echo @@ -152,6 +171,7 @@ for opt1 in $test_opts; do done done +echo echo "== Creating ${TEST_IMG}.[abc] ==" | _filter_testdir ( $QEMU_IMG create -f qcow2 "${TEST_IMG}.a" -b "${TEST_IMG}" @@ -242,6 +262,23 @@ _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' _cleanup_qemu +echo +echo "== Detecting -U and force-share conflicts ==" + +echo +echo 'No conflict:' +$QEMU_IMG info -U --image-opts driver=null-co,force-share=on +echo +echo 'Conflict:' +$QEMU_IMG info -U --image-opts driver=null-co,force-share=off + +echo +echo 'No conflict:' +$QEMU_IO -c 'open -r -U -o driver=null-co,force-share=on' +echo +echo 'Conflict:' +$QEMU_IO -c 'open -r -U -o driver=null-co,force-share=off' + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index bb721cb747..93eaf10486 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -92,6 +92,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock Is another process using the image? +_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base +qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock +Is another process using the image? +file format: IMGFMT + == Running utility commands -U == _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 @@ -209,6 +214,11 @@ _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock Is another process using the image? +_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base +qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock +Is another process using the image? +file format: IMGFMT + == Running utility commands -U == _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 @@ -309,6 +319,9 @@ _qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2 _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 +_qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base +file format: IMGFMT + == Running utility commands -U == _qemu_io_wrapper -U -c read 0 512 TEST_DIR/t.qcow2 @@ -356,6 +369,31 @@ _qemu_img_wrapper bench -U -w -c 1 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': force-share=on can only be used with read-only images Round done + +== Two devices with the same image (read-only=off - read-only=off) == +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock +Is another process using the image? + +== Two devices with the same image (read-only=off - read-only=on) == +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=on: Failed to get shared "write" lock +Is another process using the image? + +== Two devices with the same image (read-only=off - read-only=on,force-share=on) == + +== Two devices with the same image (read-only=on - read-only=off) == +QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock +Is another process using the image? + +== Two devices with the same image (read-only=on - read-only=on) == + +== Two devices with the same image (read-only=on - read-only=on,force-share=on) == + +== Two devices with the same image (read-only=on,force-share=on - read-only=off) == + +== Two devices with the same image (read-only=on,force-share=on - read-only=on) == + +== Two devices with the same image (read-only=on,force-share=on - read-only=on,force-share=on) == + == Creating TEST_DIR/t.qcow2.[abc] == Formatting 'TEST_DIR/t.IMGFMT.a', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT Formatting 'TEST_DIR/t.IMGFMT.b', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t.IMGFMT @@ -399,4 +437,20 @@ Is another process using the image? Closing the other _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 + +== Detecting -U and force-share conflicts == + +No conflict: +image: null-co:// +file format: null-co +virtual size: 1.0G (1073741824 bytes) +disk size: unavailable + +Conflict: +qemu-img: --force-share/-U conflicts with image options + +No conflict: + +Conflict: +-U conflicts with image options *** done diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155 index 42dae04c83..63a5b5e2c0 100755 --- a/tests/qemu-iotests/155 +++ b/tests/qemu-iotests/155 @@ -63,7 +63,7 @@ class BaseClass(iotests.QMPTestCase): 'driver': iotests.imgfmt, 'file': {'driver': 'file', 'filename': source_img}} - self.vm.add_blockdev(self.qmp_to_opts(blockdev)) + self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev)) self.vm.add_device('virtio-blk,id=qdev0,drive=source') self.vm.launch() diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index e75dc4d743..0a9a09802e 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -119,7 +119,7 @@ _send_qemu_cmd $QEMU_HANDLE \ _send_qemu_cmd $QEMU_HANDLE \ '' \ - 'BLOCK_JOB_COMPLETED' + '"status": "null"' # Remove the source images rm -f "$TEST_IMG{,.backing,.overlay}" diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out index f96a564c1d..34c057b626 100644 --- a/tests/qemu-iotests/156.out +++ b/tests/qemu-iotests/156.out @@ -12,13 +12,20 @@ wrote 131072/131072 bytes at offset 131072 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "source"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "source"}} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "source"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "source", "len": 131072, "offset": 131072, "speed": 0, "type": "mirror"}} wrote 65536/65536 bytes at offset 196608 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"return": ""} {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "source"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "source"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "source", "len": 196608, "offset": 196608, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "source"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "source"}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index 2936929627..88f62d3c6d 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -18,6 +18,7 @@ # along with this program. If not, see . # +from __future__ import print_function import os import re import iotests @@ -85,7 +86,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) if log: - print log + print(log) self.vm = self.mkVm() self.vm.launch() diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181 index 5e767c6195..e02979378d 100755 --- a/tests/qemu-iotests/181 +++ b/tests/qemu-iotests/181 @@ -96,6 +96,19 @@ echo # Enable postcopy-ram capability both on source and destination silent=yes _send_qemu_cmd $dest 'migrate_set_capability postcopy-ram on' "(qemu)" + +qemu_error_no_exit=yes success_or_failure=yes \ + _send_qemu_cmd $dest '' "(qemu)" "Postcopy is not supported" +if [ ${QEMU_STATUS[$dest]} -lt 0 ]; then + _send_qemu_cmd $dest '' "(qemu)" + + _send_qemu_cmd $src 'quit' "" + _send_qemu_cmd $dest 'quit' "" + wait=1 _cleanup_qemu + + _notrun 'Postcopy is not supported' +fi + _send_qemu_cmd $src 'migrate_set_speed 4k' "(qemu)" _send_qemu_cmd $src 'migrate_set_capability postcopy-ram on' "(qemu)" _send_qemu_cmd $src "migrate -d unix:${MIG_SOCKET}" "(qemu)" diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 298d88d04e..7dcfdeac60 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -27,8 +27,6 @@ echo "QA output created by $seq" here=`pwd` status=1 # failure is the default! -MIG_SOCKET="${TEST_DIR}/migrate" - _cleanup() { rm -f "${TEST_IMG}.mid" @@ -101,14 +99,11 @@ echo # command to be received (after receiving the command, the rest runs # synchronously, so jobs can arbitrarily continue or complete). # -# Jobs present while QEMU is terminating iterate once more due to -# bdrv_drain_all(). -# # The buffer size for commit and streaming is 512k (waiting for 8 seconds after # the first request), for active commit and mirror it's large enough to cover # the full 4M, and for backup it's the qcow2 cluster size, which we know is # 64k. As all of these are at least as large as the speed, we are sure that the -# offset advances exactly twice before qemu exits. +# offset advances exactly once before qemu exits. _send_qemu_cmd $h \ "{ 'execute': 'block-commit', @@ -118,8 +113,13 @@ _send_qemu_cmd $h \ 'speed': 65536 } }" \ "return" +# If we don't sleep here 'quit' command races with disk I/O +sleep 0.5 + +# Ignore the JOB_STATUS_CHANGE events while shutting down the VM. Depending on +# the timing, jobs may or may not transition through a paused state. _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start active commit job and exit qemu === @@ -137,8 +137,11 @@ _send_qemu_cmd $h \ 'speed': 65536 } }" \ "return" +# If we don't sleep here 'quit' command races with disk I/O +sleep 0.5 + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start mirror job and exit qemu === @@ -163,7 +166,7 @@ _send_qemu_cmd $h \ sleep 0.5 _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start backup job and exit qemu === @@ -183,8 +186,11 @@ _send_qemu_cmd $h \ 'speed': 65536 } }" \ "return" +# If we don't sleep here 'quit' command races with disk I/O +sleep 0.5 + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' echo echo === Start streaming job and exit qemu === @@ -201,8 +207,11 @@ _send_qemu_cmd $h \ 'speed': 65536 } }" \ "return" +# If we don't sleep here 'quit' command races with disk I/O +sleep 0.5 + _send_qemu_cmd $h "{ 'execute': 'quit' }" "return" -wait=1 _cleanup_qemu +wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE' _check_test_img diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 2c4b04de73..4e0ca0dffa 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -17,45 +17,53 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q === Start commit job and exit qemu === +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 1048576, "speed": 65536, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "commit"}} === Start active commit job and exit qemu === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} === Start mirror job and exit qemu === {"return": {}} Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} === Start backup job and exit qemu === {"return": {}} Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16 +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 131072, "speed": 65536, "type": "backup"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 65536, "speed": 65536, "type": "backup"}} === Start streaming job and exit qemu === {"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 1048576, "speed": 65536, "type": "stream"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}} No errors were found on the image. *** done diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 index dfad6555e4..d6860e72f7 100755 --- a/tests/qemu-iotests/191 +++ b/tests/qemu-iotests/191 @@ -27,8 +27,6 @@ echo "QA output created by $seq" here=`pwd` status=1 # failure is the default! -MIG_SOCKET="${TEST_DIR}/migrate" - _cleanup() { rm -f "${TEST_IMG}.mid" @@ -83,7 +81,7 @@ _send_qemu_cmd $h \ 'device': 'top', 'base':'$TEST_IMG.base', 'top': '$TEST_IMG.mid' } }" \ - "BLOCK_JOB_COMPLETED" + '"status": "null"' _send_qemu_cmd $h "" "^}" echo @@ -131,7 +129,7 @@ _send_qemu_cmd $h \ 'device': 'top', 'base':'$TEST_IMG.base', 'top': '$TEST_IMG.mid' } }" \ - "BLOCK_JOB_COMPLETED" + '"status": "null"' _send_qemu_cmd $h "" "^}" echo diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out index 190c5f049a..31a0c7d4c4 100644 --- a/tests/qemu-iotests/191.out +++ b/tests/qemu-iotests/191.out @@ -15,10 +15,54 @@ wrote 65536/65536 bytes at offset 1048576 === Perform commit job === +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "created", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "running", + "id": "commit0" + } +} { "return": { } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "waiting", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "pending", + "id": "commit0" + } +} { "timestamp": { "seconds": TIMESTAMP, @@ -33,6 +77,28 @@ wrote 65536/65536 bytes at offset 1048576 "type": "commit" } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "concluded", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "null", + "id": "commit0" + } +} === Check that both top and top2 point to base now === @@ -355,10 +421,54 @@ wrote 65536/65536 bytes at offset 1048576 === Perform commit job === +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "created", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "running", + "id": "commit0" + } +} { "return": { } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "waiting", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "pending", + "id": "commit0" + } +} { "timestamp": { "seconds": TIMESTAMP, @@ -373,6 +483,28 @@ wrote 65536/65536 bytes at offset 1048576 "type": "commit" } } +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "concluded", + "id": "commit0" + } +} +{ + "timestamp": { + "seconds": TIMESTAMP, + "microseconds": TIMESTAMP + }, + "event": "JOB_STATUS_CHANGE", + "data": { + "status": "null", + "id": "commit0" + } +} === Check that both top and top2 point to base now === diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 index 5e869fe2b7..0369aa5cff 100755 --- a/tests/qemu-iotests/197 +++ b/tests/qemu-iotests/197 @@ -44,6 +44,7 @@ esac _cleanup() { _cleanup_test_img + rm -f "$TEST_WRAP" rm -f "$BLKDBG_CONF" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -108,6 +109,15 @@ $QEMU_IO -f qcow2 -c map "$TEST_WRAP" _check_test_img $QEMU_IMG compare -f $IMGFMT -F qcow2 "$TEST_IMG" "$TEST_WRAP" +echo +echo '=== Partial final cluster ===' +echo + +_make_test_img 1024 +$QEMU_IO -f $IMGFMT -C -c 'read 0 1024' "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -f $IMGFMT -c map "$TEST_IMG" +_check_test_img + # success, all done echo '*** done' status=0 diff --git a/tests/qemu-iotests/197.out b/tests/qemu-iotests/197.out index 52b4137d7b..8febda5dea 100644 --- a/tests/qemu-iotests/197.out +++ b/tests/qemu-iotests/197.out @@ -23,4 +23,12 @@ can't open device TEST_DIR/t.wrap.qcow2: Can't use copy-on-read on read-only dev 1023.938 MiB (0x3fff0000) bytes not allocated at offset 3 GiB (0xc0010000) No errors were found on the image. Images are identical. + +=== Partial final cluster === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1024 +read 1024/1024 bytes at offset 0 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) +No errors were found on the image. *** done diff --git a/tests/qemu-iotests/201 b/tests/qemu-iotests/201 index 11f640f5df..c1a1e00077 100755 --- a/tests/qemu-iotests/201 +++ b/tests/qemu-iotests/201 @@ -82,6 +82,19 @@ echo silent=yes _send_qemu_cmd $dest 'migrate_set_capability postcopy-ram on' "(qemu)" + +qemu_error_no_exit=yes success_or_failure=yes \ + _send_qemu_cmd $dest '' "(qemu)" "Postcopy is not supported" +if [ ${QEMU_STATUS[$dest]} -lt 0 ]; then + _send_qemu_cmd $dest '' "(qemu)" + + _send_qemu_cmd $src 'quit' "" + _send_qemu_cmd $dest 'quit' "" + wait=1 _cleanup_qemu + + _notrun 'Postcopy is not supported' +fi + _send_qemu_cmd $src 'migrate_set_capability postcopy-ram on' "(qemu)" _send_qemu_cmd $src "migrate -d unix:${MIG_SOCKET}" "(qemu)" diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 index 0a18b2b19a..128c334c7c 100755 --- a/tests/qemu-iotests/206 +++ b/tests/qemu-iotests/206 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test qcow2 and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf +# # 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 @@ -18,419 +20,263 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['qcow2']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt qcow2 -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.qcow2') as disk_path, \ + iotests.FilePath('t.qcow2.base') as backing_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + vm.add_object('secret,id=keysec0,data=foo') -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -echo -echo "=== Successful image creation (defaults) ===" -echo + size = 128 * 1024 * 1024 -size=$((128 * 1024 * 1024)) + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -run_qemu < +# # 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 @@ -18,244 +20,198 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +import subprocess +import re -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['raw']) +iotests.verify_protocol(supported=['ssh']) -here=`pwd` -status=1 # failure is the default! +def filter_hash(msg): + return re.sub("'hash': '[0-9a-f]+'", "'hash': HASH", msg) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, + filters=[iotests.filter_testfiles, filter_hash]) -_supported_fmt raw -_supported_proto ssh -_supported_os Linux + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} +with iotests.FilePath('t.img') as disk_path, \ + iotests.VM() as vm: -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + remote_path = iotests.remote_filename(disk_path) -echo -echo "=== Successful image creation (defaults) ===" -echo + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -run_qemu </dev/null | grep -v "\\^#" | ' + + 'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1', + shell=True).rstrip() -key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | - cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1) + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'md5', + 'hash': 'wrong', + } + }, + 'size': 2097152 }) + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'md5', + 'hash': md5_key, + } + }, + 'size': 8388608 }) + vm.shutdown() -run_qemu </dev/null | grep -v "\\^#" | ' + + 'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1', + shell=True).rstrip() + vm.launch() + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'sha1', + 'hash': 'wrong', + } + }, + 'size': 2097152 }) + blockdev_create(vm, { 'driver': 'ssh', + 'location': { + 'path': disk_path, + 'server': { + 'host': '127.0.0.1', + 'port': '22' + }, + 'host-key-check': { + 'mode': 'hash', + 'type': 'sha1', + 'hash': sha1_key, + } + }, + 'size': 4194304 }) + vm.shutdown() -key=$(ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | - cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1) + iotests.img_info_log(remote_path, filter_path=disk_path) -run_qemu < +# # 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 @@ -18,230 +20,165 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['luks']) +iotests.verify_protocol(supported=['file']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt luks -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.luks') as disk_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + vm.add_object('secret,id=keysec0,data=foo') -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -echo -echo "=== Successful image creation (defaults) ===" -echo + size = 128 * 1024 * 1024 -size=$((128 * 1024 * 1024)) + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -run_qemu -object secret,id=keysec0,data="foo" <0 size"}} -{"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} - -image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"} +{'execute': 'block_resize', 'arguments': {'size': 9223372036854775296, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u'The requested file size is too large'}} +{'execute': 'block_resize', 'arguments': {'size': 9223372036854775808L, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} +{'execute': 'block_resize', 'arguments': {'size': 18446744073709551104L, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} +{'execute': 'block_resize', 'arguments': {'size': -9223372036854775808, 'node_name': 'node1'}} +{u'error': {u'class': u'GenericError', u'desc': u"Parameter 'size' expects a >0 size"}} +image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT virtual size: 0 (0 bytes) -*** done +encrypted: yes +Format specific information: + ivgen alg: plain64 + hash alg: sha256 + cipher alg: aes-256 + uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + cipher mode: xts + slots: + [0]: + active: true + iters: XXX + key offset: 4096 + stripes: 4000 + [1]: + active: false + key offset: 262144 + [2]: + active: false + key offset: 520192 + [3]: + active: false + key offset: 778240 + [4]: + active: false + key offset: 1036288 + [5]: + active: false + key offset: 1294336 + [6]: + active: false + key offset: 1552384 + [7]: + active: false + key offset: 1810432 + payload offset: 2068480 + master key iters: XXX + diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 index 1edec26517..7b7985db6c 100755 --- a/tests/qemu-iotests/211 +++ b/tests/qemu-iotests/211 @@ -1,9 +1,11 @@ -#!/bin/bash +#!/usr/bin/env python # # Test VDI and file image creation # # Copyright (C) 2018 Red Hat, Inc. # +# Creator/Owner: Kevin Wolf +# # 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 @@ -18,229 +20,154 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['vdi']) +iotests.verify_protocol(supported=['file']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt vdi -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.vdi') as disk_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + size = 128 * 1024 * 1024 -echo -echo "=== Successful image creation (defaults) ===" -echo + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -size=$((128 * 1024 * 1024)) + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') -run_qemu < +# # 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 @@ -18,309 +20,176 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['parallels']) +iotests.verify_protocol(supported=['file']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt parallels -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.parallels') as disk_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + size = 128 * 1024 * 1024 -echo -echo "=== Successful image creation (defaults) ===" -echo + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -size=$((128 * 1024 * 1024)) + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') -run_qemu < +# # 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 @@ -18,332 +20,190 @@ # along with this program. If not, see . # -# creator -owner=kwolf@redhat.com +import iotests +from iotests import imgfmt -seq=`basename $0` -echo "QA output created by $seq" +iotests.verify_image_format(supported_fmts=['vhdx']) +iotests.verify_protocol(supported=['file']) -here=`pwd` -status=1 # failure is the default! +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options) -# get standard environment, filters and checks -. ./common.rc -. ./common.filter + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") -_supported_fmt vhdx -_supported_proto file -_supported_os Linux +with iotests.FilePath('t.vhdx') as disk_path, \ + iotests.VM() as vm: -function do_run_qemu() -{ - echo Testing: "$@" - $QEMU -nographic -qmp stdio -serial none "$@" - echo -} + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") -function run_qemu() -{ - do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ - | _filter_qemu | _filter_imgfmt \ - | _filter_actual_image_size -} + size = 128 * 1024 * 1024 -echo -echo "=== Successful image creation (defaults) ===" -echo + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) -size=$((128 * 1024 * 1024)) + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile') -run_qemu < +# +# 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 . +# + +seq=$(basename "$0") +echo "QA output created by $seq" + +here=$PWD +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +# Repairing the corrupted image requires qemu-img check to store a +# refcount up to 3, which requires at least two refcount bits. +_unsupported_imgopts 'refcount_bits=1[^0-9]' + + +echo +echo "=== Corrupted size field in compressed cluster descriptor ===" +echo +# Create an empty image and fill half of it with compressed data. +# 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 +$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 + +# Reduce size of compressed data to 4 sectors: this corrupts the image. +poke_file "$TEST_IMG" $((0x800000)) "\x40\x06" +$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +# 'qemu-img check' however doesn't see anything wrong because it +# doesn't try to decompress the data and the refcounts are consistent. +# TODO: update qemu-img so this can be detected. +_check_test_img + +# Increase size of compressed data to the maximum (8192 sectors). +# This makes QEMU read more data (8192 sectors instead of 5, host +# addresses [0xa00000, 0xdfffff]), but the decompression algorithm +# stops once we have enough to restore the uncompressed cluster, so +# the rest of the data is ignored. +poke_file "$TEST_IMG" $((0x800000)) "\x7f\xfe" +# Do it also for the second compressed cluster (L2 entry at 0x800008). +# In this case the compressed data would span 3 host clusters +# (host addresses: [0xa00802, 0xe00801]) +poke_file "$TEST_IMG" $((0x800008)) "\x7f\xfe" + +# Here the image is too small so we're asking QEMU to read beyond the +# end of the image. +$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +# But if we grow the image we won't be reading beyond its end anymore. +$QEMU_IO -c "write -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +# The refcount data is however wrong because due to the increased size +# of the compressed data it now reaches the following host clusters. +# This can be repaired by qemu-img check by increasing the refcount of +# those clusters. +# TODO: update qemu-img to correct the compressed cluster size instead. +_check_test_img -r all +$QEMU_IO -c "read -P 0x11 0 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "read -P 0x22 4M 4M" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/214.out b/tests/qemu-iotests/214.out new file mode 100644 index 0000000000..0fcd8dc051 --- /dev/null +++ b/tests/qemu-iotests/214.out @@ -0,0 +1,35 @@ +QA output created by 214 + +=== Corrupted size field in compressed cluster descriptor === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=8388608 +wrote 2097152/2097152 bytes at offset 0 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read failed: Input/output error +No errors were found on the image. +read 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 4194304/4194304 bytes at offset 4194304 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +ERROR cluster 6 refcount=1 reference=3 +ERROR cluster 7 refcount=1 reference=2 +Repairing cluster 6 refcount=1 reference=3 +Repairing cluster 7 refcount=1 reference=2 +Repairing OFLAG_COPIED data cluster: l2_entry=8000000000c00000 refcount=3 +Repairing OFLAG_COPIED data cluster: l2_entry=8000000000e00000 refcount=2 +The following inconsistencies were found and repaired: + + 0 leaked clusters + 4 corruptions + +Double checking the fixed image now... +No errors were found on the image. +read 4194304/4194304 bytes at offset 0 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4194304/4194304 bytes at offset 4194304 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +*** done diff --git a/tests/qemu-iotests/215 b/tests/qemu-iotests/215 new file mode 100755 index 0000000000..2e616ed659 --- /dev/null +++ b/tests/qemu-iotests/215 @@ -0,0 +1,120 @@ +#!/bin/bash +# +# Test case for copy-on-read into qcow2, using the COR filter driver +# +# Copyright (C) 2018 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 . +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +TEST_WRAP="$TEST_DIR/t.wrap.qcow2" +BLKDBG_CONF="$TEST_DIR/blkdebug.conf" + +# Sanity check: our use of blkdebug fails if $TEST_DIR contains spaces +# or other problems +case "$TEST_DIR" in + *[^-_a-zA-Z0-9/]*) + _notrun "Suspicious TEST_DIR='$TEST_DIR', cowardly refusing to run" ;; +esac + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_WRAP" + rm -f "$BLKDBG_CONF" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# Test is supported for any backing file; but we force qcow2 for our wrapper. +_supported_fmt generic +_supported_proto generic +_supported_os Linux +# LUKS support may be possible, but it complicates things. +_unsupported_fmt luks + +echo +echo '=== Copy-on-read ===' +echo + +# Prep the images +# VPC rounds image sizes to a specific geometry, force a specific size. +if [ "$IMGFMT" = "vpc" ]; then + IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size") +fi +_make_test_img 4G +$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io +IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \ + _make_test_img -F "$IMGFMT" -b "$TEST_IMG" | _filter_img_create +$QEMU_IO -f qcow2 -c "write -z -u 1M 64k" "$TEST_WRAP" | _filter_qemu_io + +# Ensure that a read of two clusters, but where one is already allocated, +# does not re-write the allocated cluster +cat > "$BLKDBG_CONF" <&1 | _filter_qemu_io) +case $output in + *allocate*) + _notrun "Insufficent memory to run test" ;; + *) printf '%s\n' "$output" ;; +esac +$QEMU_IO \ + -c "open -o driver=copy-on-read,file.driver=qcow2 $TEST_WRAP" \ + -c "read -P 0 $((3*1024*1024*1024 + 1024)) 1k" \ + | _filter_qemu_io + +# Copy-on-read is incompatible with read-only +$QEMU_IO \ + -c "open -r -o driver=copy-on-read,file.driver=qcow2 $TEST_WRAP" \ + 2>&1 | _filter_testdir + +# Break the backing chain, and show that images are identical, and that +# we properly copied over explicit zeros. +$QEMU_IMG rebase -u -b "" -f qcow2 "$TEST_WRAP" +$QEMU_IO -f qcow2 -c map "$TEST_WRAP" +_check_test_img +$QEMU_IMG compare -f $IMGFMT -F qcow2 "$TEST_IMG" "$TEST_WRAP" + +# success, all done +echo '*** done' +status=0 diff --git a/tests/qemu-iotests/215.out b/tests/qemu-iotests/215.out new file mode 100644 index 0000000000..70b0f5fb19 --- /dev/null +++ b/tests/qemu-iotests/215.out @@ -0,0 +1,26 @@ +QA output created by 215 + +=== Copy-on-read === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4294967296 +wrote 1024/1024 bytes at offset 3221225472 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Formatting 'TEST_DIR/t.wrap.IMGFMT', fmt=IMGFMT size=4294967296 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT +wrote 65536/65536 bytes at offset 1048576 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 131072/131072 bytes at offset 1048576 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 0/0 bytes at offset 0 +0 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2147483136/2147483136 bytes at offset 1024 +2 GiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1024/1024 bytes at offset 3221226496 +1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +can't open device TEST_DIR/t.wrap.qcow2: Block node is read-only +2 GiB (0x80010000) bytes allocated at offset 0 bytes (0x0) +1023.938 MiB (0x3fff0000) bytes not allocated at offset 2 GiB (0x80010000) +64 KiB (0x10000) bytes allocated at offset 3 GiB (0xc0000000) +1023.938 MiB (0x3fff0000) bytes not allocated at offset 3 GiB (0xc0010000) +No errors were found on the image. +Images are identical. +*** done diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216 new file mode 100755 index 0000000000..3c0ae54b44 --- /dev/null +++ b/tests/qemu-iotests/216 @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# +# Copy-on-read tests using a COR filter node +# +# Copyright (C) 2018 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 . +# +# Creator/Owner: Max Reitz + +import iotests +from iotests import log, qemu_img, qemu_io_silent + +# Need backing file support +iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk']) +iotests.verify_platform(['linux']) + +log('') +log('=== Copy-on-read across nodes ===') +log('') + +# The old copy-on-read mechanism without a filter node cannot request +# WRITE_UNCHANGED permissions for its child. Therefore it just tries +# to sneak its write by the usual permission system and holds its +# fingers crossed. However, that sneaking does not work so well when +# there is a filter node in the way: That will receive the write +# request and re-issue a new one to its child, which this time is a +# proper write request that will make the permission system cough -- +# unless there is someone at the top (like a guest device) that has +# requested write permissions. +# +# A COR filter node, however, can request the proper permissions for +# its child and therefore is not hit by this issue. + +with iotests.FilePath('base.img') as base_img_path, \ + iotests.FilePath('top.img') as top_img_path, \ + iotests.VM() as vm: + + log('--- Setting up images ---') + log('') + + assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 + assert qemu_io_silent(base_img_path, '-c', 'write -P 1 0M 1M') == 0 + assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, + top_img_path) == 0 + assert qemu_io_silent(top_img_path, '-c', 'write -P 2 1M 1M') == 0 + + log('Done') + + log('') + log('--- Doing COR ---') + log('') + + # Compare with e.g. the following: + # vm.add_drive_raw('if=none,node-name=node0,copy-on-read=on,driver=raw,' \ + # 'file.driver=%s,file.file.filename=%s' % + # (iotests.imgfmt, top_img_path)) + # (Remove the blockdev-add instead.) + # ((Not tested here because it hits an assertion in the permission + # system.)) + + vm.launch() + + log(vm.qmp('blockdev-add', + node_name='node0', + driver='copy-on-read', + file={ + 'driver': 'raw', + 'file': { + 'driver': 'copy-on-read', + 'file': { + 'driver': 'raw', + 'file': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': top_img_path + }, + 'backing': { + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': base_img_path + } + } + } + } + } + })) + + # Trigger COR + log(vm.qmp('human-monitor-command', + command_line='qemu-io node0 "read 0 64M"')) + + vm.shutdown() + + log('') + log('--- Checking COR result ---') + log('') + + assert qemu_io_silent(base_img_path, '-c', 'discard 0 64M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'read -P 1 0M 1M') == 0 + assert qemu_io_silent(top_img_path, '-c', 'read -P 2 1M 1M') == 0 + + log('Done') diff --git a/tests/qemu-iotests/216.out b/tests/qemu-iotests/216.out new file mode 100644 index 0000000000..45ea857ee1 --- /dev/null +++ b/tests/qemu-iotests/216.out @@ -0,0 +1,15 @@ + +=== Copy-on-read across nodes === + +--- Setting up images --- + +Done + +--- Doing COR --- + +{u'return': {}} +{u'return': u''} + +--- Checking COR result --- + +Done diff --git a/tests/qemu-iotests/217 b/tests/qemu-iotests/217 new file mode 100755 index 0000000000..d3ab5d72be --- /dev/null +++ b/tests/qemu-iotests/217 @@ -0,0 +1,90 @@ +#!/bin/bash +# +# I/O errors when working with internal qcow2 snapshots, and repairing +# the result +# +# Copyright (C) 2018 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 . + +seq=$(basename $0) +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_DIR/blkdebug.conf" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# This test is specific to qcow2 +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +# This test needs clusters with at least a refcount of 2 so that +# OFLAG_COPIED is not set. refcount_bits=1 is therefore unsupported. +_unsupported_imgopts 'refcount_bits=1[^0-9]' + +echo +echo '=== Simulating an I/O error during snapshot deletion ===' +echo + +_make_test_img 64M +$QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io + +# Create the snapshot +$QEMU_IMG snapshot -c foo "$TEST_IMG" + +# Verify the snapshot is there +echo +_img_info | grep 'Snapshot list' +echo '(Snapshot filtered)' +echo + +# Try to delete the snapshot (with an error happening when freeing the +# then leaked clusters) +cat > "$TEST_DIR/blkdebug.conf" <. +# +# Creator/Owner: Max Reitz + +import iotests +from iotests import log + +iotests.verify_platform(['linux']) + + +# Launches the VM, adds two null-co nodes (source and target), and +# starts a blockdev-mirror job on them. +# +# Either both or none of speed and buf_size must be given. + +def start_mirror(vm, speed=None, buf_size=None): + vm.launch() + + ret = vm.qmp('blockdev-add', + node_name='source', + driver='null-co', + size=1048576) + assert ret['return'] == {} + + ret = vm.qmp('blockdev-add', + node_name='target', + driver='null-co', + size=1048576) + assert ret['return'] == {} + + if speed is not None: + ret = vm.qmp('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + sync='full', + speed=speed, + buf_size=buf_size) + else: + ret = vm.qmp('blockdev-mirror', + job_id='mirror', + device='source', + target='target', + sync='full') + + assert ret['return'] == {} + + +log('') +log('=== Cancel mirror job before convergence ===') +log('') + +log('--- force=false ---') +log('') + +with iotests.VM() as vm: + # Low speed so it does not converge + start_mirror(vm, 65536, 65536) + + log('Cancelling job') + log(vm.qmp('block-job-cancel', device='mirror', force=False)) + + log(vm.event_wait('BLOCK_JOB_CANCELLED'), + filters=[iotests.filter_qmp_event]) + +log('') +log('--- force=true ---') +log('') + +with iotests.VM() as vm: + # Low speed so it does not converge + start_mirror(vm, 65536, 65536) + + log('Cancelling job') + log(vm.qmp('block-job-cancel', device='mirror', force=True)) + + log(vm.event_wait('BLOCK_JOB_CANCELLED'), + filters=[iotests.filter_qmp_event]) + + +log('') +log('=== Cancel mirror job after convergence ===') +log('') + +log('--- force=false ---') +log('') + +with iotests.VM() as vm: + start_mirror(vm) + + log(vm.event_wait('BLOCK_JOB_READY'), + filters=[iotests.filter_qmp_event]) + + log('Cancelling job') + log(vm.qmp('block-job-cancel', device='mirror', force=False)) + + log(vm.event_wait('BLOCK_JOB_COMPLETED'), + filters=[iotests.filter_qmp_event]) + +log('') +log('--- force=true ---') +log('') + +with iotests.VM() as vm: + start_mirror(vm) + + log(vm.event_wait('BLOCK_JOB_READY'), + filters=[iotests.filter_qmp_event]) + + log('Cancelling job') + log(vm.qmp('block-job-cancel', device='mirror', force=True)) + + log(vm.event_wait('BLOCK_JOB_CANCELLED'), + filters=[iotests.filter_qmp_event]) diff --git a/tests/qemu-iotests/218.out b/tests/qemu-iotests/218.out new file mode 100644 index 0000000000..7dbf78e682 --- /dev/null +++ b/tests/qemu-iotests/218.out @@ -0,0 +1,30 @@ + +=== Cancel mirror job before convergence === + +--- force=false --- + +Cancelling job +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} + +--- force=true --- + +Cancelling job +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} + +=== Cancel mirror job after convergence === + +--- force=false --- + +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} +Cancelling job +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_COMPLETED'} + +--- force=true --- + +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} +Cancelling job +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_CANCELLED'} diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219 new file mode 100755 index 0000000000..c03bbdb294 --- /dev/null +++ b/tests/qemu-iotests/219 @@ -0,0 +1,223 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 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 . +# +# Creator/Owner: Kevin Wolf +# +# Check using the job-* QMP commands with block jobs + +import iotests + +iotests.verify_image_format(supported_fmts=['qcow2']) + +def pause_wait(vm, job_id): + with iotests.Timeout(3, "Timeout waiting for job to pause"): + while True: + result = vm.qmp('query-jobs') + for job in result['return']: + if job['id'] == job_id and job['status'] in ['paused', 'standby']: + return job + +# Test that block-job-pause/resume and job-pause/resume can be mixed +def test_pause_resume(vm): + for pause_cmd, pause_arg in [('block-job-pause', 'device'), + ('job-pause', 'id')]: + for resume_cmd, resume_arg in [('block-job-resume', 'device'), + ('job-resume', 'id')]: + iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd)) + + iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'})) + pause_wait(vm, 'job0') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + result = vm.qmp('query-jobs') + iotests.log(result) + + old_progress = result['return'][0]['current-progress'] + total_progress = result['return'][0]['total-progress'] + + iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'})) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + if old_progress < total_progress: + # Wait for the job to advance + while result['return'][0]['current-progress'] == old_progress: + result = vm.qmp('query-jobs') + iotests.log(result) + else: + # Already reached the end, so the job cannot advance + # any further; therefore, the query-jobs result can be + # logged immediately + iotests.log(vm.qmp('query-jobs')) + +def test_job_lifecycle(vm, job, job_args, has_ready=False): + iotests.log('') + iotests.log('') + iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' % + (job, + job_args.get('auto-finalize', True), + job_args.get('auto-dismiss', True))) + iotests.log(vm.qmp(job, job_id='job0', **job_args)) + + # Depending on the storage, the first request may or may not have completed + # yet (and the total progress may not have been fully determined yet), so + # filter out the progress. Later query-job calls don't need the filtering + # because the progress is made deterministic by the block job speed + result = vm.qmp('query-jobs') + for j in result['return']: + j['current-progress'] = 'FILTERED' + j['total-progress'] = 'FILTERED' + iotests.log(result) + + # undefined -> created -> running + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + + # RUNNING state: + # pause/resume should work, complete/finalize/dismiss should error out + iotests.log('') + iotests.log('Pause/resume in RUNNING') + test_pause_resume(vm) + + iotests.log(vm.qmp('job-complete', id='job0')) + iotests.log(vm.qmp('job-finalize', id='job0')) + iotests.log(vm.qmp('job-dismiss', id='job0')) + + iotests.log(vm.qmp('block-job-complete', device='job0')) + iotests.log(vm.qmp('block-job-finalize', id='job0')) + iotests.log(vm.qmp('block-job-dismiss', id='job0')) + + # Let the job complete (or transition to READY if it supports that) + iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0)) + if has_ready: + iotests.log('') + iotests.log('Waiting for READY state...') + vm.event_wait('BLOCK_JOB_READY') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + + # READY state: + # pause/resume/complete should work, finalize/dismiss should error out + iotests.log('') + iotests.log('Pause/resume in READY') + test_pause_resume(vm) + + iotests.log(vm.qmp('job-finalize', id='job0')) + iotests.log(vm.qmp('job-dismiss', id='job0')) + + iotests.log(vm.qmp('block-job-finalize', id='job0')) + iotests.log(vm.qmp('block-job-dismiss', id='job0')) + + # Transition to WAITING + iotests.log(vm.qmp('job-complete', id='job0')) + + # Move to WAITING and PENDING state + iotests.log('') + iotests.log('Waiting for PENDING state...') + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + + if not job_args.get('auto-finalize', True): + # PENDING state: + # finalize should work, pause/complete/dismiss should error out + iotests.log(vm.qmp('query-jobs')) + + iotests.log(vm.qmp('job-pause', id='job0')) + iotests.log(vm.qmp('job-complete', id='job0')) + iotests.log(vm.qmp('job-dismiss', id='job0')) + + iotests.log(vm.qmp('block-job-pause', device='job0')) + iotests.log(vm.qmp('block-job-complete', device='job0')) + iotests.log(vm.qmp('block-job-dismiss', id='job0')) + + # Transition to CONCLUDED + iotests.log(vm.qmp('job-finalize', id='job0')) + + + # Move to CONCLUDED state + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + + if not job_args.get('auto-dismiss', True): + # CONCLUDED state: + # dismiss should work, pause/complete/finalize should error out + iotests.log(vm.qmp('query-jobs')) + + iotests.log(vm.qmp('job-pause', id='job0')) + iotests.log(vm.qmp('job-complete', id='job0')) + iotests.log(vm.qmp('job-finalize', id='job0')) + + iotests.log(vm.qmp('block-job-pause', device='job0')) + iotests.log(vm.qmp('block-job-complete', device='job0')) + iotests.log(vm.qmp('block-job-finalize', id='job0')) + + # Transition to NULL + iotests.log(vm.qmp('job-dismiss', id='job0')) + + # Move to NULL state + iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE'))) + iotests.log(vm.qmp('query-jobs')) + + +with iotests.FilePath('disk.img') as disk_path, \ + iotests.FilePath('copy.img') as copy_path, \ + iotests.VM() as vm: + + img_size = '4M' + iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size) + iotests.qemu_io('-c', 'write 0 %s' % (img_size), + '-f', iotests.imgfmt, disk_path) + + iotests.log('Launching VM...') + vm.add_blockdev(vm.qmp_to_opts({ + 'driver': iotests.imgfmt, + 'node-name': 'drive0-node', + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + })) + vm.launch() + + # In order to keep things deterministic (especially progress in query-job, + # but related to this also automatic state transitions like job + # completion), but still get pause points often enough to avoid making this + # test very slow, it's important to have the right ratio between speed and + # buf_size. + # + # For backup, buf_size is hard-coded to the source image cluster size (64k), + # so we'll pick the same for mirror. The slice time, i.e. the granularity + # of the rate limiting is 100ms. With a speed of 256k per second, we can + # get four pause points per second. This gives us 250ms per iteration, + # which should be enough to stay deterministic. + + test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={ + 'device': 'drive0-node', + 'target': copy_path, + 'sync': 'full', + 'speed': 262144, + 'buf_size': 65536, + }) + + for auto_finalize in [True, False]: + for auto_dismiss in [True, False]: + test_job_lifecycle(vm, 'drive-backup', job_args={ + 'device': 'drive0-node', + 'target': copy_path, + 'sync': 'full', + 'speed': 262144, + 'auto-finalize': auto_finalize, + 'auto-dismiss': auto_dismiss, + }) + + vm.shutdown() diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out new file mode 100644 index 0000000000..6dc07bc41e --- /dev/null +++ b/tests/qemu-iotests/219.out @@ -0,0 +1,327 @@ +Launching VM... + + +Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True) +{u'return': {}} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'mirror'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for READY state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} + +Pause/resume in READY +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True) +{u'return': {}} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False) +{u'return': {}} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True) +{u'return': {}} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} + + +Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False) +{u'return': {}} +{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} + +Pause/resume in RUNNING +=== Testing block-job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing block-job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/block-job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +=== Testing job-pause/job-resume === +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{u'return': {}} + +Waiting for PENDING state... +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{u'return': []} diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221 new file mode 100755 index 0000000000..41c4e4bdf8 --- /dev/null +++ b/tests/qemu-iotests/221 @@ -0,0 +1,60 @@ +#!/bin/bash +# +# Test qemu-img vs. unaligned images +# +# Copyright (C) 2018 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 . +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt raw +_supported_proto file +_supported_os Linux + +echo +echo "=== Check mapping of unaligned raw image ===" +echo + +_make_test_img 43009 # qemu-img create rounds size up +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +truncate --size=43009 "$TEST_IMG" # so we resize it and check again +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +$QEMU_IO -c 'w 43008 1' "$TEST_IMG" | _filter_qemu_io # writing also rounds up +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +truncate --size=43009 "$TEST_IMG" # so we resize it and check again +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out new file mode 100644 index 0000000000..a9c0190aad --- /dev/null +++ b/tests/qemu-iotests/221.out @@ -0,0 +1,16 @@ +QA output created by 221 + +=== Check mapping of unaligned raw image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=43009 +[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 43520, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +wrote 1/1 bytes at offset 43008 +1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, +{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +[{ "start": 0, "length": 40960, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, +{ "start": 40960, "length": 2049, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 43009, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +*** done diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222 new file mode 100644 index 0000000000..0ead56d574 --- /dev/null +++ b/tests/qemu-iotests/222 @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# This test covers the basic fleecing workflow, which provides a +# point-in-time snapshot of a node that can be queried over NBD. +# +# Copyright (C) 2018 Red Hat, Inc. +# John helped, too. +# +# 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 . +# +# Creator/Owner: John Snow + +import iotests +from iotests import log, qemu_img, qemu_io, qemu_io_silent + +iotests.verify_platform(['linux']) +iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', + 'vhdx', 'raw']) + +patterns = [("0x5d", "0", "64k"), + ("0xd5", "1M", "64k"), + ("0xdc", "32M", "64k"), + ("0xcd", "0x3ff0000", "64k")] # 64M - 64K + +overwrite = [("0xab", "0", "64k"), # Full overwrite + ("0xad", "0x00f8000", "64k"), # Partial-left (1M-32K) + ("0x1d", "0x2008000", "64k"), # Partial-right (32M+32K) + ("0xea", "0x3fe0000", "64k")] # Adjacent-left (64M - 128K) + +zeroes = [("0", "0x00f8000", "32k"), # Left-end of partial-left (1M-32K) + ("0", "0x2010000", "32k"), # Right-end of partial-right (32M+64K) + ("0", "0x3fe0000", "64k")] # overwrite[3] + +remainder = [("0xd5", "0x108000", "32k"), # Right-end of partial-left [1] + ("0xdc", "32M", "32k"), # Left-end of partial-right [2] + ("0xcd", "0x3ff0000", "64k")] # patterns[3] + +with iotests.FilePath('base.img') as base_img_path, \ + iotests.FilePath('fleece.img') as fleece_img_path, \ + iotests.FilePath('nbd.sock') as nbd_sock_path, \ + iotests.VM() as vm: + + log('--- Setting up images ---') + log('') + + assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 + assert qemu_img('create', '-f', "qcow2", fleece_img_path, '64M') == 0 + + for p in patterns: + qemu_io('-f', iotests.imgfmt, + '-c', 'write -P%s %s %s' % p, base_img_path) + + log('Done') + + log('') + log('--- Launching VM ---') + log('') + + vm.add_drive(base_img_path) + vm.launch() + log('Done') + + log('') + log('--- Setting up Fleecing Graph ---') + log('') + + src_node = "drive0" + tgt_node = "fleeceNode" + + # create tgt_node backed by src_node + log(vm.qmp("blockdev-add", **{ + "driver": "qcow2", + "node-name": tgt_node, + "file": { + "driver": "file", + "filename": fleece_img_path, + }, + "backing": src_node, + })) + + # Establish COW from source to fleecing node + log(vm.qmp("blockdev-backup", + device=src_node, + target=tgt_node, + sync="none")) + + log('') + log('--- Setting up NBD Export ---') + log('') + + nbd_uri = 'nbd+unix:///%s?socket=%s' % (tgt_node, nbd_sock_path) + log(vm.qmp("nbd-server-start", + **{"addr": { "type": "unix", + "data": { "path": nbd_sock_path } } })) + + log(vm.qmp("nbd-server-add", device=tgt_node)) + + log('') + log('--- Sanity Check ---') + log('') + + for p in (patterns + zeroes): + cmd = "read -P%s %s %s" % p + log(cmd) + assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 + + log('') + log('--- Testing COW ---') + log('') + + for p in overwrite: + cmd = "write -P%s %s %s" % p + log(cmd) + log(vm.hmp_qemu_io(src_node, cmd)) + + log('') + log('--- Verifying Data ---') + log('') + + for p in (patterns + zeroes): + cmd = "read -P%s %s %s" % p + log(cmd) + assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0 + + log('') + log('--- Cleanup ---') + log('') + + log(vm.qmp('block-job-cancel', device=src_node)) + log(vm.event_wait('BLOCK_JOB_CANCELLED'), + filters=[iotests.filter_qmp_event]) + log(vm.qmp('nbd-server-stop')) + log(vm.qmp('blockdev-del', node_name=tgt_node)) + vm.shutdown() + + log('') + log('--- Confirming writes ---') + log('') + + for p in (overwrite + remainder): + cmd = "read -P%s %s %s" % p + log(cmd) + assert qemu_io_silent(base_img_path, '-c', cmd) == 0 + + log('') + log('Done') diff --git a/tests/qemu-iotests/222.out b/tests/qemu-iotests/222.out new file mode 100644 index 0000000000..48f336a02b --- /dev/null +++ b/tests/qemu-iotests/222.out @@ -0,0 +1,67 @@ +--- Setting up images --- + +Done + +--- Launching VM --- + +Done + +--- Setting up Fleecing Graph --- + +{u'return': {}} +{u'return': {}} + +--- Setting up NBD Export --- + +{u'return': {}} +{u'return': {}} + +--- Sanity Check --- + +read -P0x5d 0 64k +read -P0xd5 1M 64k +read -P0xdc 32M 64k +read -P0xcd 0x3ff0000 64k +read -P0 0x00f8000 32k +read -P0 0x2010000 32k +read -P0 0x3fe0000 64k + +--- Testing COW --- + +write -P0xab 0 64k +{u'return': u''} +write -P0xad 0x00f8000 64k +{u'return': u''} +write -P0x1d 0x2008000 64k +{u'return': u''} +write -P0xea 0x3fe0000 64k +{u'return': u''} + +--- Verifying Data --- + +read -P0x5d 0 64k +read -P0xd5 1M 64k +read -P0xdc 32M 64k +read -P0xcd 0x3ff0000 64k +read -P0 0x00f8000 32k +read -P0 0x2010000 32k +read -P0 0x3fe0000 64k + +--- Cleanup --- + +{u'return': {}} +{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'drive0', u'type': u'backup', u'speed': 0, u'len': 67108864, u'offset': 393216}, u'event': u'BLOCK_JOB_CANCELLED'} +{u'return': {}} +{u'return': {}} + +--- Confirming writes --- + +read -P0xab 0 64k +read -P0xad 0x00f8000 64k +read -P0x1d 0x2008000 64k +read -P0xea 0x3fe0000 64k +read -P0xd5 0x108000 32k +read -P0xdc 32M 32k +read -P0xcd 0x3ff0000 64k + +Done diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 new file mode 100755 index 0000000000..8b1859c2dd --- /dev/null +++ b/tests/qemu-iotests/223 @@ -0,0 +1,140 @@ +#!/bin/bash +# +# Test reading dirty bitmap over NBD +# +# Copyright (C) 2018 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 . +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +here="$PWD" +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + _cleanup_qemu + rm -f "$TEST_DIR/nbd" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file # uses NBD as well +_supported_os Linux +# Persistent dirty bitmaps require compat=1.1 +_unsupported_imgopts 'compat=0.10' + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ + | _filter_actual_image_size +} + +echo +echo "=== Create partially sparse image, then add dirty bitmap ===" +echo + +_make_test_img 4M +$QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io +run_qemu < >(_filter_nbd) + +silent= +_send_qemu_cmd $QEMU_HANDLE '{"execute":"qmp_capabilities"}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", + "arguments":{"driver":"qcow2", "node-name":"n", + "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", + "arguments":{"node":"n", "name":"b"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-start", + "arguments":{"addr":{"type":"unix", + "data":{"path":"'"$TEST_DIR/nbd"'"}}}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", + "arguments":{"name":"n", "bitmap":"b"}}' "return" + +echo +echo "=== Contrast normal status with dirty-bitmap status ===" +echo + +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT +IMG="driver=nbd,export=n,server.type=unix,server.path=$TEST_DIR/nbd" +$QEMU_IO -r -c 'r -P 0 0 1m' -c 'r -P 0x11 1m 1m' \ + -c 'r -P 0x22 2m 2m' --image-opts "$IMG" | _filter_qemu_io +$QEMU_IMG map --output=json --image-opts \ + "$IMG" | _filter_qemu_img_map +$QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map + +echo +echo "=== End NBD server ===" +echo + +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + "arguments":{"name":"n"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out new file mode 100644 index 0000000000..33021c8e6a --- /dev/null +++ b/tests/qemu-iotests/223.out @@ -0,0 +1,49 @@ +QA output created by 223 + +=== Create partially sparse image, then add dirty bitmap === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 +wrote 2097152/2097152 bytes at offset 1048576 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Testing: +QMP_VERSION +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} + + +=== Write part of the file under active bitmap === + +wrote 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== End dirty bitmap, and start serving image over NBD === + +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} + +=== Contrast normal status with dirty-bitmap status === + +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false}, +{ "start": 1048576, "length": 3145728, "depth": 0, "zero": false, "data": true}] +[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true}, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] + +=== End NBD server === + +{"return": {}} +{"return": {}} +{"return": {}} +*** done diff --git a/tests/qemu-iotests/225 b/tests/qemu-iotests/225 new file mode 100755 index 0000000000..f2ee715685 --- /dev/null +++ b/tests/qemu-iotests/225 @@ -0,0 +1,132 @@ +#!/bin/bash +# +# Test vmdk backing file correlation +# +# Copyright (C) 2018 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 . +# + +# creator +owner=mreitz@redhat.com + +seq=$(basename $0) +echo "QA output created by $seq" + +here=$PWD +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_IMG.not_base" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +# This tests vmdk-specific low-level functionality +_supported_fmt vmdk +_supported_proto file +_supported_os Linux +_unsupported_imgopts "subformat=monolithicFlat" \ + "subformat=twoGbMaxExtentFlat" \ + "subformat=twoGbMaxExtentSparse" + +TEST_IMG="$TEST_IMG.base" _make_test_img 1M +TEST_IMG="$TEST_IMG.not_base" _make_test_img 1M +_make_test_img -b "$TEST_IMG.base" + +make_opts() +{ + node_name=$1 + filename=$2 + backing=$3 + + if [ -z "$backing" ]; then + backing="null" + else + backing="'$backing'" + fi + + echo "{ 'node-name': '$node_name', + 'driver': 'vmdk', + 'file': { + 'driver': 'file', + 'filename': '$filename' + }, + 'backing': $backing }" +} + +overlay_opts=$(make_opts overlay "$TEST_IMG" backing) +base_opts=$(make_opts backing "$TEST_IMG.base") +not_base_opts=$(make_opts backing "$TEST_IMG.not_base") + +not_vmdk_opts="{ 'node-name': 'backing', 'driver': 'null-co' }" + +echo +echo '=== Testing fitting VMDK backing image ===' +echo + +qemu_comm_method=monitor \ + _launch_qemu -blockdev "$base_opts" -blockdev "$overlay_opts" + +# Should not return an error +_send_qemu_cmd $QEMU_HANDLE 'qemu-io overlay "read 0 512"' 'ops' + +_cleanup_qemu + + +echo +echo '=== Testing unrelated VMDK backing image ===' +echo + +qemu_comm_method=monitor \ + _launch_qemu -blockdev "$not_base_opts" -blockdev "$overlay_opts" + +# Should fail (gracefully) +_send_qemu_cmd $QEMU_HANDLE 'qemu-io overlay "read 0 512"' 'failed' + +_cleanup_qemu + + +echo +echo '=== Testing non-VMDK backing image ===' +echo + +# FIXME: This is the reason why we have to use two -blockdev +# invocations. You can only fully override the backing file options +# if you either specify a node reference (as done here) or the new +# options contain file.filename (which in this case they do not). +# In other cases, file.filename will be set to whatever the image +# header of the overlay contains (which we do not want). I consider +# this a FIXME because with -blockdev, you cannot specify "partial" +# options, so setting file.filename but leaving the rest as specified +# by the user does not make sense. +qemu_comm_method=monitor \ + _launch_qemu -blockdev "$not_vmdk_opts" -blockdev "$overlay_opts" + +# Should fail (gracefully) +_send_qemu_cmd $QEMU_HANDLE 'qemu-io overlay "read 0 512"' 'failed' + +_cleanup_qemu + + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/225.out b/tests/qemu-iotests/225.out new file mode 100644 index 0000000000..4dc8ee282f --- /dev/null +++ b/tests/qemu-iotests/225.out @@ -0,0 +1,24 @@ +QA output created by 225 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.not_base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base + +=== Testing fitting VMDK backing image === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io overlay "read 0 512" +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Testing unrelated VMDK backing image === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io overlay "read 0 512" +read failed: Invalid argument + +=== Testing non-VMDK backing image === + +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) qemu-io overlay "read 0 512" +read failed: Invalid argument +*** done diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 new file mode 100755 index 0000000000..8ec3e612dd --- /dev/null +++ b/tests/qemu-iotests/226 @@ -0,0 +1,69 @@ +#!/bin/bash +# +# This test covers expected filetypes for the file, host_cdrom and +# host_device drivers. +# +# Copyright (C) 2018 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 . +# + +# creator +owner=jsnow@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +status=1 # failure is the default! + +_cleanup() +{ + rmdir "$TEST_IMG" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern + +# Generic format, but tests file-protocol specific error handling +_supported_fmt generic +if [ "$IMGOPTSSYNTAX" = "true" ]; then + _unsupported_fmt $IMGFMT +fi +_supported_proto file +_supported_os Linux + +# Create something decidedly not a file, blockdev or chardev... +mkdir "$TEST_IMG" + +for PROTO in "file" "host_device" "host_cdrom"; do + echo + echo "=== Testing with driver:$PROTO ===" + echo + echo "== Testing RO ==" + $QEMU_IO -c "open -r -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt + $QEMU_IO -c "open -r -o driver=$PROTO,filename=/dev/null,locking=off" 2>&1 | _filter_imgfmt + echo "== Testing RW ==" + $QEMU_IO -c "open -o driver=$PROTO,filename=$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt + $QEMU_IO -c "open -o driver=$PROTO,filename=/dev/null,locking=off" 2>&1 | _filter_imgfmt +done + +# success, all done +echo +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/226.out b/tests/qemu-iotests/226.out new file mode 100644 index 0000000000..8c0d060ffc --- /dev/null +++ b/tests/qemu-iotests/226.out @@ -0,0 +1,26 @@ +QA output created by 226 + +=== Testing with driver:file === + +== Testing RO == +can't open: A regular file was expected by the 'file' driver, but something else was given +warning: Opening a character device as a file using the 'file' driver is deprecated +== Testing RW == +can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory +warning: Opening a character device as a file using the 'file' driver is deprecated + +=== Testing with driver:host_device === + +== Testing RO == +can't open: 'host_device' driver expects either a character or block device +== Testing RW == +can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory + +=== Testing with driver:host_cdrom === + +== Testing RO == +can't open: 'host_cdrom' driver expects either a character or block device +== Testing RW == +can't open: Could not open 'TEST_DIR/t.IMGFMT': Is a directory + +*** done diff --git a/tests/qemu-iotests/227 b/tests/qemu-iotests/227 new file mode 100755 index 0000000000..9a5f7f9f14 --- /dev/null +++ b/tests/qemu-iotests/227 @@ -0,0 +1,101 @@ +#!/bin/bash +# +# Test query-blockstats with different ways to create a BB +# +# Copyright (C) 2018 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 . +# + +# creator +owner=kwolf@redhat.com + +seq=$(basename $0) +echo "QA output created by $seq" + +here=$PWD +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt generic +_supported_proto file +_supported_os Linux + +function do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp-pretty stdio -serial none "$@" + echo +} + +function run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ + | _filter_qemu | _filter_imgfmt \ + | _filter_generated_node_ids +} + +echo +echo '=== blockstats with -drive if=virtio ===' +echo + +run_qemu -drive driver=null-co,if=virtio <&${QEMU_IN[${h}]} if [ -n "${1}" ]; then - qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" + if [ -z "${success_or_failure}" ]; then + qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" + else + qemu_error_no_exit=${use_error} _timed_wait_for ${h} "${1}" "${2}" + fi if [ ${QEMU_STATUS[$h]} -eq 0 ]; then return fi diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 9a65a11026..44bee16a5e 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -147,8 +147,9 @@ else TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "nfs" ]; then - TEST_DIR="nfs://127.0.0.1/$TEST_DIR" - TEST_IMG=$TEST_DIR/t.$IMGFMT + TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT + REMOTE_TEST_DIR="nfs://127.0.0.1$TEST_DIR" + TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "vxhs" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="vxhs://127.0.0.1:9999/t.$IMGFMT" @@ -173,6 +174,10 @@ if [ ! -d "$TEST_DIR" ]; then exit 1 fi +if [ -z "$REMOTE_TEST_DIR" ]; then + REMOTE_TEST_DIR="$TEST_DIR" +fi + if [ ! -d "$SAMPLE_IMG_DIR" ]; then echo "common.config: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory" exit 1 @@ -190,6 +195,16 @@ _use_sample_img() fi } +_stop_nbd_server() +{ + if [ -f "${QEMU_TEST_DIR}/qemu-nbd.pid" ]; then + local QEMU_NBD_PID + read QEMU_NBD_PID < "${QEMU_TEST_DIR}/qemu-nbd.pid" + kill ${QEMU_NBD_PID} + rm -f "${QEMU_TEST_DIR}/qemu-nbd.pid" + fi +} + _make_test_img() { # extra qemu-img options can be added by tests @@ -229,6 +244,10 @@ _make_test_img() extra_img_options="-o $optstr $extra_img_options" fi + if [ $IMGPROTO = "nbd" ]; then + _stop_nbd_server + fi + # XXX(hch): have global image options? ( if [ $use_backing = 1 ]; then @@ -269,12 +288,7 @@ _cleanup_test_img() case "$IMGPROTO" in nbd) - if [ -f "${QEMU_TEST_DIR}/qemu-nbd.pid" ]; then - local QEMU_NBD_PID - read QEMU_NBD_PID < "${QEMU_TEST_DIR}/qemu-nbd.pid" - kill ${QEMU_NBD_PID} - rm -f "${QEMU_TEST_DIR}/qemu-nbd.pid" - fi + _stop_nbd_server rm -f "$TEST_IMG_FILE" ;; vxhs) @@ -333,7 +347,8 @@ _img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' $QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \ - sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ + sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ -e "/^disk size:/ D" \ diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 52a80f3f9e..b973dc842d 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -97,7 +97,7 @@ 088 rw auto quick 089 rw auto quick 090 rw auto quick -091 rw auto +091 rw auto migration 092 rw auto quick 093 auto 094 rw auto quick @@ -157,6 +157,7 @@ 148 rw auto quick 149 rw auto sudo 150 rw auto quick +151 rw auto 152 rw auto quick 153 rw auto quick 154 rw auto backing quick @@ -169,7 +170,7 @@ 162 auto quick 163 rw auto 165 rw auto quick -169 rw auto quick +169 rw auto quick migration 170 rw auto quick 171 rw auto quick 172 auto @@ -194,14 +195,14 @@ 192 rw auto quick 194 rw auto migration quick 195 rw auto quick -196 rw auto quick +196 rw auto quick migration 197 rw auto quick 198 rw auto -199 rw auto +199 rw auto migration 200 rw auto 201 rw auto migration 202 rw auto quick -203 rw auto +203 rw auto migration 204 rw auto quick 205 rw auto quick 206 rw auto @@ -212,3 +213,15 @@ 211 rw auto quick 212 rw auto quick 213 rw auto quick +214 rw auto +215 rw auto quick +216 rw auto quick +217 rw auto quick +218 rw auto quick +219 rw auto +221 rw auto quick +222 rw auto quick +223 rw auto quick +225 rw auto quick +226 auto quick +227 auto quick diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index b25d48a91b..4e67fbbe96 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -1,3 +1,4 @@ +from __future__ import print_function # Common utilities and Python wrappers for qemu-iotests # # Copyright (C) 2012 IBM Corp. @@ -109,6 +110,20 @@ def qemu_img_pipe(*args): sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args)))) return subp.communicate()[0] +def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]): + args = [ 'info' ] + if imgopts: + args.append('--image-opts') + else: + args += [ '-f', imgfmt ] + args += extra_args + args.append(filename) + + output = qemu_img_pipe(*args) + if not filter_path: + filter_path = filename + log(filter_img_info(output, filter_path)) + def qemu_io(*args): '''Run qemu-io and return the stdout data''' args = qemu_io_args + list(args) @@ -119,6 +134,15 @@ def qemu_io(*args): sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args))) return subp.communicate()[0] +def qemu_io_silent(*args): + '''Run qemu-io and return the exit code, suppressing stdout''' + args = qemu_io_args + list(args) + exitcode = subprocess.call(args, stdout=open('/dev/null', 'w')) + if exitcode < 0: + sys.stderr.write('qemu-io received signal %i: %s\n' % + (-exitcode, ' '.join(args))) + return exitcode + class QemuIoInteractive: def __init__(self, *args): @@ -206,10 +230,26 @@ def filter_qmp_event(event): event['timestamp']['microseconds'] = 'USECS' return event +def filter_testfiles(msg): + prefix = os.path.join(test_dir, "%s-" % (os.getpid())) + return msg.replace(prefix, 'TEST_DIR/PID-') + +def filter_img_info(output, filename): + lines = [] + for line in output.split('\n'): + if 'disk size' in line or 'actual-size' in line: + continue + line = line.replace(filename, 'TEST_IMG') \ + .replace(imgfmt, 'IMGFMT') + line = re.sub('iters: [0-9]+', 'iters: XXX', line) + line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line) + lines.append(line) + return '\n'.join(lines) + def log(msg, filters=[]): for flt in filters: msg = flt(msg) - print msg + print(msg) class Timeout: def __init__(self, seconds, errmsg = "Timeout"): @@ -281,6 +321,13 @@ def file_path(*names): return paths[0] if len(paths) == 1 else paths +def remote_filename(path): + if imgproto == 'file': + return path + elif imgproto == 'ssh': + return "ssh://127.0.0.1%s" % (path) + else: + raise Exception("Protocol %s not supported" % (imgproto)) class VM(qtest.QEMUQtestMachine): '''A QEMU VM''' @@ -363,6 +410,58 @@ class VM(qtest.QEMUQtestMachine): return self.qmp('human-monitor-command', command_line='qemu-io %s "%s"' % (drive, cmd)) + def flatten_qmp_object(self, obj, output=None, basestr=''): + if output is None: + output = dict() + if isinstance(obj, list): + for i in range(len(obj)): + self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') + elif isinstance(obj, dict): + for key in obj: + self.flatten_qmp_object(obj[key], output, basestr + key + '.') + else: + output[basestr[:-1]] = obj # Strip trailing '.' + return output + + def qmp_to_opts(self, obj): + obj = self.flatten_qmp_object(obj) + output_list = list() + for key in obj: + output_list += [key + '=' + obj[key]] + return ','.join(output_list) + + def get_qmp_events_filtered(self, wait=True): + result = [] + for ev in self.get_qmp_events(wait=wait): + result.append(filter_qmp_event(ev)) + return result + + def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs): + logmsg = "{'execute': '%s', 'arguments': %s}" % (cmd, kwargs) + log(logmsg, filters) + result = self.qmp(cmd, **kwargs) + log(str(result), filters) + return result + + def run_job(self, job, auto_finalize=True, auto_dismiss=False): + while True: + for ev in self.get_qmp_events_filtered(wait=True): + if ev['event'] == 'JOB_STATUS_CHANGE': + status = ev['data']['status'] + if status == 'aborting': + result = self.qmp('query-jobs') + for j in result['return']: + if j['id'] == job: + log('Job failed: %s' % (j['error'])) + elif status == 'pending' and not auto_finalize: + self.qmp_log('job-finalize', id=job) + elif status == 'concluded' and not auto_dismiss: + self.qmp_log('job-dismiss', id=job) + elif status == 'null': + return + else: + iotests.log(ev) + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') @@ -390,26 +489,6 @@ class QMPTestCase(unittest.TestCase): self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d))) return d - def flatten_qmp_object(self, obj, output=None, basestr=''): - if output is None: - output = dict() - if isinstance(obj, list): - for i in range(len(obj)): - self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.') - elif isinstance(obj, dict): - for key in obj: - self.flatten_qmp_object(obj[key], output, basestr + key + '.') - else: - output[basestr[:-1]] = obj # Strip trailing '.' - return output - - def qmp_to_opts(self, obj): - obj = self.flatten_qmp_object(obj) - output_list = list() - for key in obj: - output_list += [key + '=' + obj[key]] - return ','.join(output_list) - def assert_qmp_absent(self, d, path): try: result = self.dictpath(d, path) @@ -444,8 +523,8 @@ class QMPTestCase(unittest.TestCase): '''Asserts that the given filename is a json: filename and that its content is equal to the given reference object''' self.assertEqual(json_filename[:5], 'json:') - self.assertEqual(self.flatten_qmp_object(json.loads(json_filename[5:])), - self.flatten_qmp_object(reference)) + self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])), + self.vm.flatten_qmp_object(reference)) def cancel_and_wait(self, drive='drive0', force=False, resume=False): '''Cancel a block job and wait for it to finish, returning the event''' @@ -464,6 +543,9 @@ class QMPTestCase(unittest.TestCase): self.assert_qmp(event, 'data/device', drive) result = event cancelled = True + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', drive) + self.assert_no_active_block_jobs() return result @@ -479,6 +561,8 @@ class QMPTestCase(unittest.TestCase): self.assert_qmp(event, 'data/offset', event['data']['len']) self.assert_no_active_block_jobs() return event + elif event['event'] == 'JOB_STATUS_CHANGE': + self.assert_qmp(event, 'data/id', drive) def wait_ready(self, drive='drive0'): '''Wait until a block job BLOCK_JOB_READY event''' @@ -507,9 +591,14 @@ class QMPTestCase(unittest.TestCase): with Timeout(1, "Timeout waiting for job to pause"): while True: result = self.vm.qmp('query-block-jobs') + found = False for job in result['return']: - if job['device'] == job_id and job['paused'] == True and job['busy'] == False: - return job + if job['device'] == job_id: + found = True + if job['paused'] == True and job['busy'] == False: + return job + break + assert found def pause_job(self, job_id='job0', wait=True): result = self.vm.qmp('block-job-pause', device=job_id) @@ -525,7 +614,7 @@ def notrun(reason): seq = os.path.basename(sys.argv[0]) open('%s/%s.notrun' % (output_dir, seq), 'wb').write(reason + '\n') - print '%s not run: %s' % (seq, reason) + print('%s not run: %s' % (seq, reason)) sys.exit(0) def verify_image_format(supported_fmts=[], unsupported_fmts=[]): @@ -542,6 +631,16 @@ def verify_image_format(supported_fmts=[], unsupported_fmts=[]): if not_sup or (imgfmt in unsupported_fmts): notrun('not suitable for this image format: %s' % imgfmt) +def verify_protocol(supported=[], unsupported=[]): + assert not (supported and unsupported) + + if 'generic' in supported: + return + + not_sup = supported and (imgproto not in supported) + if not_sup or (imgproto in unsupported): + notrun('not suitable for this protocol: %s' % imgproto) + def verify_platform(supported_oses=['linux']): if True not in [sys.platform.startswith(x) for x in supported_oses]: notrun('not suitable for this OS: %s' % sys.platform) diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py index 8a04d979aa..f9193c0fae 100755 --- a/tests/qemu-iotests/nbd-fault-injector.py +++ b/tests/qemu-iotests/nbd-fault-injector.py @@ -43,6 +43,7 @@ # 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 __future__ import print_function import sys import socket import struct @@ -110,7 +111,7 @@ class FaultInjectionSocket(object): for rule in self.rules: if rule.match(event, io): if rule.when == 0 or bufsize is None: - print 'Closing connection on rule match %s' % rule.name + print('Closing connection on rule match %s' % rule.name) sys.exit(0) if rule.when != -1: return rule.when @@ -182,7 +183,7 @@ def handle_connection(conn, use_export): elif req.type == NBD_CMD_DISC: break else: - print 'unrecognized command type %#02x' % req.type + print('unrecognized command type %#02x' % req.type) break conn.close() @@ -242,7 +243,7 @@ def open_socket(path): sock = socket.socket(socket.AF_UNIX) sock.bind(path) sock.listen(0) - print 'Listening on %s' % path + print('Listening on %s' % path) sys.stdout.flush() # another process may be waiting, show message now return sock diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py index 9cc4cf7d08..b95a837759 100755 --- a/tests/qemu-iotests/qcow2.py +++ b/tests/qemu-iotests/qcow2.py @@ -1,5 +1,6 @@ #!/usr/bin/env python +from __future__ import print_function import sys import struct import string @@ -129,8 +130,8 @@ class QcowHeader: def dump(self): for f in QcowHeader.fields: - print "%-25s" % f[2], f[1] % self.__dict__[f[2]] - print "" + print("%-25s" % f[2], f[1] % self.__dict__[f[2]]) + print("") def dump_extensions(self): for ex in self.extensions: @@ -141,11 +142,11 @@ class QcowHeader: else: data = "" - print "Header extension:" - print "%-25s %#x" % ("magic", ex.magic) - print "%-25s %d" % ("length", ex.length) - print "%-25s %s" % ("data", data) - print "" + print("Header extension:") + print("%-25s %#x" % ("magic", ex.magic)) + print("%-25s %d" % ("length", ex.length)) + print("%-25s %s" % ("data", data)) + print("") def cmd_dump_header(fd): @@ -157,12 +158,12 @@ def cmd_set_header(fd, name, value): try: value = int(value, 0) except: - print "'%s' is not a valid number" % value + print("'%s' is not a valid number" % value) sys.exit(1) fields = (field[2] for field in QcowHeader.fields) if not name in fields: - print "'%s' is not a known header field" % name + print("'%s' is not a known header field" % name) sys.exit(1) h = QcowHeader(fd) @@ -173,7 +174,7 @@ def cmd_add_header_ext(fd, magic, data): try: magic = int(magic, 0) except: - print "'%s' is not a valid magic number" % magic + print("'%s' is not a valid magic number" % magic) sys.exit(1) h = QcowHeader(fd) @@ -188,7 +189,7 @@ def cmd_del_header_ext(fd, magic): try: magic = int(magic, 0) except: - print "'%s' is not a valid magic number" % magic + print("'%s' is not a valid magic number" % magic) sys.exit(1) h = QcowHeader(fd) @@ -200,7 +201,7 @@ def cmd_del_header_ext(fd, magic): h.extensions.remove(ex) if not found: - print "No such header extension" + print("No such header extension") return h.update(fd) @@ -211,7 +212,7 @@ def cmd_set_feature_bit(fd, group, bit): if bit < 0 or bit >= 64: raise ValueError except: - print "'%s' is not a valid bit number in range [0, 64)" % bit + print("'%s' is not a valid bit number in range [0, 64)" % bit) sys.exit(1) h = QcowHeader(fd) @@ -222,7 +223,7 @@ def cmd_set_feature_bit(fd, group, bit): elif group == 'autoclear': h.autoclear_features |= 1 << bit else: - print "'%s' is not a valid group, try 'incompatible', 'compatible', or 'autoclear'" % group + print("'%s' is not a valid group, try 'incompatible', 'compatible', or 'autoclear'" % group) sys.exit(1) h.update(fd) @@ -248,16 +249,16 @@ def main(filename, cmd, args): else: handler(fd, *args) return - print "Unknown command '%s'" % cmd + print("Unknown command '%s'" % cmd) finally: fd.close() def usage(): - print "Usage: %s [, ...]" % sys.argv[0] - print "" - print "Supported commands:" + print("Usage: %s [, ...]" % sys.argv[0]) + print("") + print("Supported commands:") for name, handler, num_args, desc in cmds: - print " %-20s - %s" % (name, desc) + print(" %-20s - %s" % (name, desc)) if __name__ == '__main__': if len(sys.argv) < 3: diff --git a/tests/qemu-iotests/qed.py b/tests/qemu-iotests/qed.py index 748068d7fe..ea469b9c48 100755 --- a/tests/qemu-iotests/qed.py +++ b/tests/qemu-iotests/qed.py @@ -10,6 +10,7 @@ # 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 __future__ import print_function import sys import struct import random @@ -108,12 +109,12 @@ def corrupt_table_invalidate(qed, table): def cmd_show(qed, *args): '''show [header|l1|l2 ]- Show header or l1/l2 tables''' if not args or args[0] == 'header': - print qed.header + print(qed.header) elif args[0] == 'l1': - print qed.l1_table + print(qed.l1_table) elif len(args) == 2 and args[0] == 'l2': offset = int(args[1]) - print qed.read_table(offset) + print(qed.read_table(offset)) else: err('unrecognized sub-command') @@ -146,7 +147,7 @@ def cmd_invalidate(qed, table_level): def cmd_need_check(qed, *args): '''need-check [on|off] - Test, set, or clear the QED_F_NEED_CHECK header bit''' if not args: - print bool(qed.header['features'] & QED_F_NEED_CHECK) + print(bool(qed.header['features'] & QED_F_NEED_CHECK)) return if args[0] == 'on': @@ -208,11 +209,11 @@ def cmd_copy_metadata(qed, outfile): out.close() def usage(): - print 'Usage: %s [, ...]' % sys.argv[0] - print - print 'Supported commands:' + print('Usage: %s [, ...]' % sys.argv[0]) + print() + print('Supported commands:') for cmd in sorted(x for x in globals() if x.startswith('cmd_')): - print globals()[cmd].__doc__ + print(globals()[cmd].__doc__) sys.exit(1) def main(): diff --git a/tests/qht-bench.c b/tests/qht-bench.c index 4cabdfd62a..f492b3a20a 100644 --- a/tests/qht-bench.c +++ b/tests/qht-bench.c @@ -93,10 +93,10 @@ static void usage_complete(int argc, char *argv[]) exit(-1); } -static bool is_equal(const void *obj, const void *userp) +static bool is_equal(const void *ap, const void *bp) { - const long *a = obj; - const long *b = userp; + const long *a = ap; + const long *b = bp; return *a == *b; } @@ -150,7 +150,7 @@ static void do_rw(struct thread_info *info) p = &keys[info->r & (lookup_range - 1)]; hash = h(*p); - read = qht_lookup(&ht, is_equal, p, hash); + read = qht_lookup(&ht, p, hash); if (read) { stats->rd++; } else { @@ -162,8 +162,8 @@ static void do_rw(struct thread_info *info) if (info->write_op) { bool written = false; - if (qht_lookup(&ht, is_equal, p, hash) == NULL) { - written = qht_insert(&ht, p, hash); + if (qht_lookup(&ht, p, hash) == NULL) { + written = qht_insert(&ht, p, hash, NULL); } if (written) { stats->in++; @@ -173,7 +173,7 @@ static void do_rw(struct thread_info *info) } else { bool removed = false; - if (qht_lookup(&ht, is_equal, p, hash)) { + if (qht_lookup(&ht, p, hash)) { removed = qht_remove(&ht, p, hash); } if (removed) { @@ -308,7 +308,7 @@ static void htable_init(void) } /* initialize the hash table */ - qht_init(&ht, qht_n_elems, qht_mode); + qht_init(&ht, is_equal, qht_n_elems, qht_mode); assert(init_size <= init_range); pr_params(); @@ -322,7 +322,7 @@ static void htable_init(void) r = xorshift64star(r); p = &keys[r & (init_range - 1)]; hash = h(*p); - if (qht_insert(&ht, p, hash)) { + if (qht_insert(&ht, p, hash, NULL)) { break; } retries++; diff --git a/tests/qmp-test.c b/tests/qmp-test.c index 772058fc4c..b9774084f8 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -52,27 +52,27 @@ static void test_malformed(QTestState *qts) /* Not even a dictionary */ resp = qtest_qmp(qts, "null"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); /* No "execute" key */ resp = qtest_qmp(qts, "{}"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); /* "execute" isn't a string */ resp = qtest_qmp(qts, "{ 'execute': true }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); /* "arguments" isn't a dictionary */ resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); /* extra key */ resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - QDECREF(resp); + qobject_unref(resp); } static void test_qmp_protocol(void) @@ -90,12 +90,12 @@ static void test_qmp_protocol(void) test_version(qdict_get(q, "version")); capabilities = qdict_get_qlist(q, "capabilities"); g_assert(capabilities && qlist_empty(capabilities)); - QDECREF(resp); + qobject_unref(resp); /* Test valid command before handshake */ resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); - QDECREF(resp); + qobject_unref(resp); /* Test malformed commands before handshake */ test_malformed(qts); @@ -104,17 +104,17 @@ static void test_qmp_protocol(void) resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); ret = qdict_get_qdict(resp, "return"); g_assert(ret && !qdict_size(ret)); - QDECREF(resp); + qobject_unref(resp); /* Test repeated handshake */ resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); - QDECREF(resp); + qobject_unref(resp); /* Test valid command */ resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); test_version(qdict_get(resp, "return")); - QDECREF(resp); + qobject_unref(resp); /* Test malformed commands */ test_malformed(qts); @@ -124,27 +124,76 @@ static void test_qmp_protocol(void) ret = qdict_get_qdict(resp, "return"); g_assert(ret); g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1"); - QDECREF(resp); + qobject_unref(resp); /* Test command failure with 'id' */ resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); - QDECREF(resp); + qobject_unref(resp); qtest_quit(qts); } -/* Tests for Out-Of-Band support. */ +/* Out-of-band tests */ + +char tmpdir[] = "/tmp/qmp-test-XXXXXX"; +char *fifo_name; + +static void setup_blocking_cmd(void) +{ + if (!mkdtemp(tmpdir)) { + g_error("mkdtemp: %s", strerror(errno)); + } + fifo_name = g_strdup_printf("%s/fifo", tmpdir); + if (mkfifo(fifo_name, 0666)) { + g_error("mkfifo: %s", strerror(errno)); + } +} + +static void cleanup_blocking_cmd(void) +{ + unlink(fifo_name); + rmdir(tmpdir); +} + +static void send_cmd_that_blocks(QTestState *s, const char *id) +{ + qtest_async_qmp(s, "{ 'execute': 'blockdev-add', 'id': %s," + " 'arguments': {" + " 'driver': 'blkdebug', 'node-name': %s," + " 'config': %s," + " 'image': { 'driver': 'null-co' } } }", + id, id, fifo_name); +} + +static void unblock_blocked_cmd(void) +{ + int fd = open(fifo_name, O_WRONLY); + g_assert(fd >= 0); + close(fd); +} + +static void send_oob_cmd_that_fails(QTestState *s, const char *id) +{ + qtest_async_qmp(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id); +} + +static void recv_cmd_id(QTestState *s, const char *id) +{ + QDict *resp = qtest_qmp_receive(s); + + g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id); + qobject_unref(resp); +} + static void test_qmp_oob(void) { QTestState *qts; QDict *resp, *q; - int acks = 0; const QListEntry *entry; QList *capabilities; QString *qstr; - const char *cmd_id; qts = qtest_init_without_qmp_handshake(true, common_args); @@ -159,63 +208,53 @@ static void test_qmp_oob(void) qstr = qobject_to(QString, entry->value); g_assert(qstr); g_assert_cmpstr(qstring_get_str(qstr), ==, "oob"); - QDECREF(resp); + qobject_unref(resp); /* Try a fake capability, it should fail. */ resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities', " " 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }"); g_assert(qdict_haskey(resp, "error")); - QDECREF(resp); + qobject_unref(resp); /* Now, enable OOB in current QMP session, it should succeed. */ resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities', " " 'arguments': { 'enable': [ 'oob' ] } }"); g_assert(qdict_haskey(resp, "return")); - QDECREF(resp); + qobject_unref(resp); /* * Try any command that does not support OOB but with OOB flag. We * should get failure. */ - resp = qtest_qmp(qts, - "{ 'execute': 'query-cpus'," - " 'control': { 'run-oob': true } }"); + resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }"); g_assert(qdict_haskey(resp, "error")); - QDECREF(resp); + qobject_unref(resp); - /* - * First send the "x-oob-test" command with lock=true and - * oob=false, it should hang the dispatcher and main thread; - * later, we send another lock=false with oob=true to continue - * that thread processing. Finally we should receive replies from - * both commands. - */ - qtest_async_qmp(qts, - "{ 'execute': 'x-oob-test'," - " 'arguments': { 'lock': true }, " - " 'id': 'lock-cmd'}"); - qtest_async_qmp(qts, - "{ 'execute': 'x-oob-test', " - " 'arguments': { 'lock': false }, " - " 'control': { 'run-oob': true }, " - " 'id': 'unlock-cmd' }"); + /* OOB command overtakes slow in-band command */ + setup_blocking_cmd(); + send_cmd_that_blocks(qts, "ib-blocks-1"); + qtest_async_qmp(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }"); + send_oob_cmd_that_fails(qts, "oob-1"); + recv_cmd_id(qts, "oob-1"); + unblock_blocked_cmd(); + recv_cmd_id(qts, "ib-blocks-1"); + recv_cmd_id(qts, "ib-quick-1"); - /* Ignore all events. Wait for 2 acks */ - while (acks < 2) { - resp = qtest_qmp_receive(qts); - cmd_id = qdict_get_str(resp, "id"); - if (!g_strcmp0(cmd_id, "lock-cmd") || - !g_strcmp0(cmd_id, "unlock-cmd")) { - acks++; - } - QDECREF(resp); - } + /* Even malformed in-band command fails in-band */ + send_cmd_that_blocks(qts, "blocks-2"); + qtest_async_qmp(qts, "{ 'id': 'err-2' }"); + unblock_blocked_cmd(); + recv_cmd_id(qts, "blocks-2"); + recv_cmd_id(qts, "err-2"); + cleanup_blocking_cmd(); qtest_quit(qts); } +/* Query smoke tests */ + static int query_error_class(const char *cmd) { static struct { @@ -271,7 +310,7 @@ static void test_query(const void *data) -1, &error_abort), ==, expected_error_class); } - QDECREF(resp); + qobject_unref(resp); qtest_end(); } @@ -321,7 +360,7 @@ static void qmp_schema_init(QmpSchema *schema) visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); visit_free(qiv); - QDECREF(resp); + qobject_unref(resp); qtest_end(); schema->hash = g_hash_table_new(g_str_hash, g_str_equal); @@ -392,6 +431,47 @@ static void add_query_tests(QmpSchema *schema) } } +/* Preconfig tests */ + +static void test_qmp_preconfig(void) +{ + QDict *rsp, *ret; + QTestState *qs = qtest_startf("%s --preconfig", common_args); + + /* preconfig state */ + /* enabled commands, no error expected */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }"))); + + /* forbidden commands, expected error */ + g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); + + /* check that query-status returns preconfig state */ + rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); + ret = qdict_get_qdict(rsp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig"); + qobject_unref(rsp); + + /* exit preconfig state */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); + qtest_qmp_eventwait(qs, "RESUME"); + + /* check that query-status returns running state */ + rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }"); + ret = qdict_get_qdict(rsp, "return"); + g_assert(ret); + g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running"); + qobject_unref(rsp); + + /* check that x-exit-preconfig returns error after exiting preconfig */ + g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }"))); + + /* enabled commands, no error expected */ + g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }"))); + + qtest_quit(qs); +} + int main(int argc, char *argv[]) { QmpSchema schema; @@ -403,6 +483,7 @@ int main(int argc, char *argv[]) qtest_add_func("qmp/oob", test_qmp_oob); qmp_schema_init(&schema); add_query_tests(&schema); + qtest_add_func("qmp/preconfig", test_qmp_preconfig); ret = g_test_run(); diff --git a/tests/qom-test.c b/tests/qom-test.c index a34ff6ba53..e6f712cbd3 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -57,7 +57,7 @@ static void test_properties(const char *path, bool recurse) g_assert(response); if (!recurse) { - QDECREF(response); + qobject_unref(response); return; } @@ -82,10 +82,10 @@ static void test_properties(const char *path, bool recurse) path, prop); /* qom-get may fail but should not, e.g., segfault. */ g_assert(tmp); - QDECREF(tmp); + qobject_unref(tmp); } } - QDECREF(response); + qobject_unref(response); } static void test_machine(gconstpointer data) @@ -101,7 +101,7 @@ static void test_machine(gconstpointer data) response = qmp("{ 'execute': 'quit' }"); g_assert(qdict_haskey(response, "return")); - QDECREF(response); + qobject_unref(response); qtest_end(); g_free(args); diff --git a/tests/tcg/Makefile b/tests/tcg/Makefile index 89e3342f3d..bf06415390 100644 --- a/tests/tcg/Makefile +++ b/tests/tcg/Makefile @@ -1,156 +1,101 @@ +# -*- Mode: makefile -*- +# +# TCG tests +# +# These are complicated by the fact we want to build them for guest +# systems. This requires knowing what guests we are building and which +# ones we have cross-compilers for or docker images with +# cross-compilers. +# +# The tests themselves should be as minimal as possible as +# cross-compilers don't always have a large amount of libraries +# available. +# +# We only include the host build system for SRC_PATH and we don't +# bother with the common rules.mk. We expect the following: +# +# CC - the C compiler command +# EXTRA_CFLAGS - any extra CFLAGS +# BUILD_STATIC - are we building static binaries +# +# By default all tests are statically compiled but some host systems +# may not package static libraries by default. If an external +# cross-compiler can only build dynamic libraries the user might need +# to make extra efforts to ensure ld.so can link at runtime when the +# tests are run. +# +# We also accept SPEED=slow to enable slower running tests +# +# We also expect to be in the tests build dir for the FOO-linux-user. +# + -include ../../config-host.mak --include $(SRC_PATH)/rules.mak +-include ../config-target.mak -$(call set-vpath, $(SRC_PATH)/tests/tcg) +quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) -QEMU=../../i386-linux-user/qemu-i386 -QEMU_X86_64=../../x86_64-linux-user/qemu-x86_64 -CC_X86_64=$(CC_I386) -m64 +# $1 = test name, $2 = cmd, $3 = desc +run-test = $(call quiet-command, timeout $(TIMEOUT) $2 > $1.out,"TEST",$3) -QEMU_INCLUDES += -I../.. -CFLAGS=-Wall -O2 -g -fno-strict-aliasing -#CFLAGS+=-msse2 +# $1 = test name, $2 = reference +diff-out = $(call quiet-command, diff -u $1.out $2 | head -n 10,"DIFF","$1.out with $2") + +# $1 = test name, $2 = reason +skip-test = @printf " SKIPPED %s on $(TARGET_NAME) because %s\n" $1 $2 + +# Tests we are building +TESTS= + +# Start with a blank slate, the build targets get to add stuff first +CFLAGS= +QEMU_CFLAGS= LDFLAGS= -# TODO: automatically detect ARM and MIPS compilers, and run those too +# The QEMU for this TARGET +QEMU=../qemu-$(TARGET_NAME) -# runcom maps page 0, so it requires root privileges -# also, pi_10.com runs indefinitely - -I386_TESTS=hello-i386 \ - linux-test \ - testthread \ - sha1-i386 \ - test-i386 \ - test-i386-fprem \ - test-mmap \ - # runcom - -# native i386 compilers sometimes are not biarch. assume cross-compilers are -ifneq ($(ARCH),i386) -I386_TESTS+=run-test-x86_64 +# If TCG debugging is enabled things are a lot slower +ifeq ($(CONFIG_DEBUG_TCG),y) +TIMEOUT=45 +else +TIMEOUT=15 endif -TESTS = test_path -ifneq ($(call find-in-path, $(CC_I386)),) -TESTS += $(I386_TESTS) +# The order we include is important. We include multiarch, base arch +# and finally arch if it's not the same as base arch. +-include $(SRC_PATH)/tests/tcg/multiarch/Makefile.target +-include $(SRC_PATH)/tests/tcg/$(TARGET_BASE_ARCH)/Makefile.target +ifneq ($(TARGET_BASE_ARCH),$(TARGET_NAME)) +-include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.target endif -all: $(patsubst %,run-%,$(TESTS)) -test: all +# Add the common build options +CFLAGS+=-Wall -O0 -g -fno-strict-aliasing +ifeq ($(BUILD_STATIC),y) +LDFLAGS+=-static +endif -# rules to run tests +%: %.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -.PHONY: $(patsubst %,run-%,$(TESTS)) +all: $(TESTS) + +# +# Test Runners +# +# By default we just run the test with the appropriate QEMU for the +# target. More advanced tests may want to override the runner in their +# specific make rules. Additional runners for the same binary should +# be added to EXTRA_RUNS. +# + +RUN_TESTS=$(patsubst %,run-%, $(TESTS)) +RUN_TESTS+=$(EXTRA_RUNS) run-%: % - -$(QEMU) ./$* + $(call run-test, $<, $(QEMU) $<, "$< on $(TARGET_NAME)") -run-hello-i386: hello-i386 -run-linux-test: linux-test -run-testthread: testthread -run-sha1-i386: sha1-i386 +.PHONY: run +run: $(RUN_TESTS) -run-test-i386: test-i386 - ./test-i386 > test-i386.ref - -$(QEMU) test-i386 > test-i386.out - @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi - -run-test-i386-fprem: test-i386-fprem - ./test-i386-fprem > test-i386-fprem.ref - -$(QEMU) test-i386-fprem > test-i386-fprem.out - @if diff -u test-i386-fprem.ref test-i386-fprem.out ; then echo "Auto Test OK"; fi - -run-test-x86_64: test-x86_64 - ./test-x86_64 > test-x86_64.ref - -$(QEMU_X86_64) test-x86_64 > test-x86_64.out - @if diff -u test-x86_64.ref test-x86_64.out ; then echo "Auto Test OK"; fi - -run-test-mmap: test-mmap - -$(QEMU) ./test-mmap - -$(QEMU) -p 8192 ./test-mmap 8192 - -$(QEMU) -p 16384 ./test-mmap 16384 - -$(QEMU) -p 32768 ./test-mmap 32768 - -run-runcom: runcom - -$(QEMU) ./runcom $(SRC_PATH)/tests/pi_10.com - -run-test_path: test_path - ./test_path - -# rules to compile tests - -test_path: test_path.o - -test_path.o: test_path.c - -hello-i386: hello-i386.c - $(CC_I386) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $< - strip $@ - -testthread: testthread.c - $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread - -# i386/x86_64 emulation test (test various opcodes) */ -test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \ - test-i386.h test-i386-shift.h test-i386-muldiv.h - $(CC_I386) $(QEMU_INCLUDES) $(CFLAGS) $(LDFLAGS) -o $@ \ - $( OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0x7f00 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x4170 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bff (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7c00 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0x7e00 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0x7e00 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0x7f00 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -nan / 0x00fffffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -nan / 0x00fffbfc0000000000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -nan / 0x00fff8040000000000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: nan / 0x007ff8040000000000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: nan / 0x007ffbfc0000000000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: nan / 0x007ffffc0000000000 (0 => OK) +### Rounding upwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x401 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4249 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0x7f00 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x401 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x4170 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4249 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bff (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7c00 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0x7e00 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0x7e00 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0x7f00 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -nan / 0x00fffffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -nan / 0x00fffbfc0000000000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -nan / 0x00fff8040000000000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: nan / 0x007ff8040000000000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: nan / 0x007ffbfc0000000000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: nan / 0x007ffffc0000000000 (0 => OK) +### Rounding downwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730512e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0x7f00 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x416f (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bfe (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7c00 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0x7e00 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0x7e00 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0x7f00 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -nan / 0x00fffffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -nan / 0x00fffbfc0000000000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -nan / 0x00fff8040000000000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: nan / 0x007ff8040000000000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: nan / 0x007ffbfc0000000000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: nan / 0x007ffffc0000000000 (0 => OK) +### Rounding to zero +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0x7f00 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x416f (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bfe (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7c00 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0x7e00 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0x7e00 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0x7f00 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -nan / 0x00fffffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -nan / 0x00fffbfc0000000000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -nan / 0x00fff8040000000000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: nan / 0x007ff8040000000000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: nan / 0x007ffbfc0000000000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: nan / 0x007ffffc0000000000 (0 => OK) +Converting single-precision to integer +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 INT64: 1/0x000000000000000001 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 INT64: 2/0x000000000000000002 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 INT64: 65503/0x00000000000000ffdf (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 INT64: 65504/0x00000000000000ffe0 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 INT64: 65505/0x00000000000000ffe1 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 INT64: 131007/0x00000000000001ffbf (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 INT64: 131008/0x00000000000001ffc0 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 INT64: 131009/0x00000000000001ffc1 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 INT64: 0/00000000000000000000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting double-precision to integer +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 INT64: -2/0x00fffffffffffffffe (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 INT64: -1/0x00ffffffffffffffff (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 INT64: 0/00000000000000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 INT64: 1/0x000000000000000001 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 INT64: 1/0x000000000000000001 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 INT64: 2/0x000000000000000002 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 INT64: 65503/0x00000000000000ffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 INT64: 65504/0x00000000000000ffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 INT64: 65505/0x00000000000000ffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 INT64: 131007/0x00000000000001ffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 INT64: 131008/0x00000000000001ffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 INT64: 131009/0x00000000000001ffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 INT64: 2147483647/0x00000000007fffffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 INT64: 0/00000000000000000000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 INT64: 0/00000000000000000000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting half-precision to integer +00 HALF: 0xffff (0 => OK) +00 INT64: 4294959104/0x0000000000ffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 INT64: 4292861952/0x0000000000ffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 INT64: 4290781184/0x0000000000ffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 INT64: 4286578688/0x0000000000ff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 INT64: 3347046400/0x0000000000c77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 INT64: 3221225472/0x0000000000c0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 INT64: 3212836864/0x0000000000bf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 INT64: 3011510272/0x0000000000b3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 INT64: 2147483648/0x000000000080000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 INT64: 864026624/0x000000000033800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 INT64: 1065353216/0x00000000003f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 INT64: 1199562752/0x0000000000477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 INT64: 2139095040/0x00000000007f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 INT64: 2143297536/0x00000000007fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 INT64: 2145378304/0x00000000007fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 INT64: 2147475456/0x00000000007fffe000 (0 => OK) +#### Enabling ARM Alternative Half Precision +### Rounding to nearest +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7fff (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xffff (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xffff (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x4170 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bff (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7fff (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7fff (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7fff (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7fff (0x1 => INVALID) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7fff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7fff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7fff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7fff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -1.31008000000000000000e+05 / 0x00c0fffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -8.18560000000000000000e+04 / 0x00c0f3fc0000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -6.56000000000000000000e+04 / 0x00c0f0040000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -6.55360000000000000000e+04 / 0x00c0f0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 6.55360000000000000000e+04 / 0x0040f0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 6.56000000000000000000e+04 / 0x0040f0040000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 8.18560000000000000000e+04 / 0x0040f3fc0000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +### Rounding upwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x401 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4249 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7c00 (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7fff (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x1 => INVALID) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xffff (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xffff (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x401 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x4170 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4249 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bff (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7c00 (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7fff (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7fff (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7fff (0x1 => INVALID) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7fff (0x1 => INVALID) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7fff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7fff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7fff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7fff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -1.31008000000000000000e+05 / 0x00c0fffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -8.18560000000000000000e+04 / 0x00c0f3fc0000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -6.56000000000000000000e+04 / 0x00c0f0040000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -6.55360000000000000000e+04 / 0x00c0f0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 6.55360000000000000000e+04 / 0x0040f0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 6.56000000000000000000e+04 / 0x0040f0040000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 8.18560000000000000000e+04 / 0x0040f3fc0000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +### Rounding downwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7ffe (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730512e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xffff (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xffff (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x416f (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bfe (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7ffe (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7fff (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7fff (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7fff (0x1 => INVALID) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7fff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7fff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7fff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7fff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -1.31008000000000000000e+05 / 0x00c0fffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -8.18560000000000000000e+04 / 0x00c0f3fc0000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -6.56000000000000000000e+04 / 0x00c0f0040000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -6.55360000000000000000e+04 / 0x00c0f0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 6.55360000000000000000e+04 / 0x0040f0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 6.56000000000000000000e+04 / 0x0040f0040000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 8.18560000000000000000e+04 / 0x0040f3fc0000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +### Rounding to zero +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7ffe (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0xffff (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0xffff (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0xc000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0xbc00 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0x400 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x3c00 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x3c01 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x3c00 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x4000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x416f (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x4248 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0x7bfe (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0x7bff (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0x7bff (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0x7ffe (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0x7fff (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0x7fff (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0x7fff (0x1 => INVALID) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0x7fff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0x7fff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0x7fff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0x7fff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: -1.31008000000000000000e+05 / 0x00c0fffc0000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: -8.18560000000000000000e+04 / 0x00c0f3fc0000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: -6.56000000000000000000e+04 / 0x00c0f0040000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: -6.55360000000000000000e+04 / 0x00c0f0000000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: -6.55040000000000000000e+04 / 0x00c0effc0000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: -5.96046447753906250000e-08 / 0x00be70000000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: -0.00000000000000000000e+00 / 0x008000000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 5.96046447753906250000e-08 / 0x003e70000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 6.55360000000000000000e+04 / 0x0040f0000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 6.56000000000000000000e+04 / 0x0040f0040000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 8.18560000000000000000e+04 / 0x0040f3fc0000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +Converting single-precision to integer +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 INT64: 1/0x000000000000000001 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 INT64: 2/0x000000000000000002 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 INT64: 65503/0x00000000000000ffdf (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 INT64: 65504/0x00000000000000ffe0 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 INT64: 65505/0x00000000000000ffe1 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 INT64: 131007/0x00000000000001ffbf (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 INT64: 131008/0x00000000000001ffc0 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 INT64: 131009/0x00000000000001ffc1 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 INT64: 0/00000000000000000000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting double-precision to integer +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 INT64: -9223372036854775808/0x008000000000000000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 INT64: -2/0x00fffffffffffffffe (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 INT64: -1/0x00ffffffffffffffff (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 INT64: 0/00000000000000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 INT64: 1/0x000000000000000001 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 INT64: 1/0x000000000000000001 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 INT64: 2/0x000000000000000002 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 INT64: 65503/0x00000000000000ffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 INT64: 65504/0x00000000000000ffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 INT64: 65505/0x00000000000000ffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 INT64: 131007/0x00000000000001ffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 INT64: 131008/0x00000000000001ffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 INT64: 131009/0x00000000000001ffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 INT64: 2147483647/0x00000000007fffffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 INT64: 9223372036854775807/0x007fffffffffffffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 INT64: 0/00000000000000000000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 INT64: 0/00000000000000000000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting half-precision to integer +00 HALF: 0xffff (0 => OK) +00 INT64: 3355435008/0x0000000000c7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 INT64: 3349143552/0x0000000000c79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 INT64: 3347062784/0x0000000000c7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 INT64: 3347054592/0x0000000000c7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 INT64: 3347046400/0x0000000000c77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 INT64: 3221225472/0x0000000000c0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 INT64: 3212836864/0x0000000000bf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 INT64: 3011510272/0x0000000000b3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 INT64: 2147483648/0x000000000080000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 INT64: 864026624/0x000000000033800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 INT64: 1065353216/0x00000000003f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 INT64: 1199562752/0x0000000000477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 INT64: 1199570944/0x000000000047800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 INT64: 1199579136/0x000000000047802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 INT64: 1201659904/0x0000000000479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 INT64: 1207951360/0x000000000047ffe000 (0 => OK) diff --git a/tests/tcg/alpha/Makefile b/tests/tcg/alpha/Makefile deleted file mode 100644 index 2b1f03d048..0000000000 --- a/tests/tcg/alpha/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -CROSS=alpha-linux-gnu- -CC=$(CROSS)gcc -AS=$(CROSS)as - -SIM=../../alpha-linux-user/qemu-alpha - -CFLAGS=-O -LINK=$(CC) -o $@ crt.o $< -nostdlib - -TESTS=test-cond test-cmov - -all: hello-alpha $(TESTS) - -hello-alpha: hello-alpha.o crt.o - $(LINK) - -test-cond: test-cond.o crt.o - $(LINK) - -test-cmov.o: test-cond.c - $(CC) -c $(CFLAGS) -DTEST_CMOV -o $@ $< - -test-cmov: test-cmov.o crt.o - $(LINK) - -test-ovf: test-ovf.o crt.o - $(LINK) - -check: $(TESTS) - for f in $(TESTS); do $(SIM) $$f || exit 1; done - -clean: - $(RM) *.o *~ hello-alpha $(TESTS) - -.PHONY: clean all check diff --git a/tests/tcg/alpha/Makefile.include b/tests/tcg/alpha/Makefile.include new file mode 100644 index 0000000000..c7dc48eadb --- /dev/null +++ b/tests/tcg/alpha/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-alpha-cross +DOCKER_CROSS_COMPILER=alpha-linux-gnu-gcc diff --git a/tests/tcg/alpha/Makefile.target b/tests/tcg/alpha/Makefile.target new file mode 100644 index 0000000000..a585080328 --- /dev/null +++ b/tests/tcg/alpha/Makefile.target @@ -0,0 +1,18 @@ +# -*- Mode: makefile -*- +# +# Alpha specific tweaks + +ALPHA_SRC=$(SRC_PATH)/tests/tcg/alpha +VPATH+=$(ALPHA_SRC) + +ALPHA_TESTS=hello-alpha test-cond test-cmov test-ovf +TESTS+=$(ALPHA_TESTS) + +test-cmov: EXTRA_CFLAGS=-DTEST_CMOV +test-cmov: test-cond.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +run-test-cmov: test-cmov + +# On Alpha Linux only supports 8k pages +EXTRA_RUNS+=run-test-mmap-8192 diff --git a/tests/tcg/alpha/crt.s b/tests/tcg/alpha/crt.s deleted file mode 100644 index 31af8825bc..0000000000 --- a/tests/tcg/alpha/crt.s +++ /dev/null @@ -1,26 +0,0 @@ - .text - - .globl _start - .ent _start,0 -_start: - .frame $15,0,$15 - br $29,1f -1: ldgp $29, 0($29) - .prologue 0 - ldq $27,main($29) !literal!1 - jsr $26,($27) - or $0,$0,$16 - .end _start - - .globl _exit -_exit: - lda $0,1 - callsys - - call_pal 0 - - .globl write -write: - lda $0,4 - callsys - ret diff --git a/tests/tcg/alpha/hello-alpha.c b/tests/tcg/alpha/hello-alpha.c index 79892e6522..84e43b2fc4 100644 --- a/tests/tcg/alpha/hello-alpha.c +++ b/tests/tcg/alpha/hello-alpha.c @@ -1,3 +1,5 @@ +#include + int main (void) { write (1, "hello\n", 6); diff --git a/tests/tcg/alpha/test-cond.c b/tests/tcg/alpha/test-cond.c index 74adffaa69..e625313b3e 100644 --- a/tests/tcg/alpha/test-cond.c +++ b/tests/tcg/alpha/test-cond.c @@ -1,3 +1,4 @@ +#include #ifdef TEST_CMOV diff --git a/tests/tcg/alpha/test-ovf.c b/tests/tcg/alpha/test-ovf.c index 01c80e7525..17892f1e89 100644 --- a/tests/tcg/alpha/test-ovf.c +++ b/tests/tcg/alpha/test-ovf.c @@ -1,3 +1,5 @@ +#include + static long test_subqv (long a, long b) { long res; diff --git a/tests/tcg/arm/Makefile.include b/tests/tcg/arm/Makefile.include new file mode 100644 index 0000000000..8e7eac008f --- /dev/null +++ b/tests/tcg/arm/Makefile.include @@ -0,0 +1,8 @@ +# Makefile.include for all ARM targets +# +# We don't have any bigendian build tools so we only use this for armhf + +ifeq ($(TARGET_NAME),arm) +DOCKER_IMAGE=debian-armhf-cross +DOCKER_CROSS_COMPILER=arm-linux-gnueabihf-gcc +endif diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target new file mode 100644 index 0000000000..aa4e4e3782 --- /dev/null +++ b/tests/tcg/arm/Makefile.target @@ -0,0 +1,32 @@ +# -*- Mode: makefile -*- +# +# ARM - included from tests/tcg/Makefile +# + +ARM_SRC=$(SRC_PATH)/tests/tcg/arm + +# Set search path for all sources +VPATH += $(ARM_SRC) + +ARM_TESTS=hello-arm test-arm-iwmmxt + +TESTS += $(ARM_TESTS) fcvt + +hello-arm: CFLAGS+=-marm -ffreestanding +hello-arm: LDFLAGS+=-nostdlib + +test-arm-iwmmxt: CFLAGS+=-marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 +test-arm-iwmmxt: test-arm-iwmmxt.S + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + +ifeq ($(TARGET_NAME), arm) +fcvt: LDFLAGS+=-lm +# fcvt: CFLAGS+=-march=armv8.2-a+fp16 -mfpu=neon-fp-armv8 + +run-fcvt: fcvt + $(call run-test,fcvt,$(QEMU) $<,"$< on $(TARGET_NAME)") + $(call diff-out,fcvt,$(ARM_SRC)/fcvt.ref) +endif + +# On ARM Linux only supports 4k pages +EXTRA_RUNS+=run-test-mmap-4096 diff --git a/tests/tcg/arm/README b/tests/tcg/arm/README new file mode 100644 index 0000000000..e6307116e2 --- /dev/null +++ b/tests/tcg/arm/README @@ -0,0 +1,11 @@ +These are ARM specific guest programs + +hello-arm +--------- + +A very simple inline assembly, write syscall based hello world + +test-arm-iwmmxt +--------------- + +A simple test case for older iwmmxt extended ARMs diff --git a/tests/tcg/arm/fcvt.c b/tests/tcg/arm/fcvt.c new file mode 100644 index 0000000000..617626bc63 --- /dev/null +++ b/tests/tcg/arm/fcvt.c @@ -0,0 +1,458 @@ +/* + * Test Floating Point Conversion + */ + +/* we want additional float type definitions */ +#define __STDC_WANT_IEC_60559_BFP_EXT__ +#define __STDC_WANT_IEC_60559_TYPES_EXT__ + +#include +#include +#include +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static char flag_str[256]; + +static char *get_flag_state(int flags) +{ + if (flags) { + snprintf(flag_str, sizeof(flag_str), "%s %s %s %s %s", + flags & FE_OVERFLOW ? "OVERFLOW" : "", + flags & FE_UNDERFLOW ? "UNDERFLOW" : "", + flags & FE_DIVBYZERO ? "DIV0" : "", + flags & FE_INEXACT ? "INEXACT" : "", + flags & FE_INVALID ? "INVALID" : ""); + } else { + snprintf(flag_str, sizeof(flag_str), "OK"); + } + + return flag_str; +} + +static void print_double_number(int i, double num) +{ + uint64_t double_as_hex = *(uint64_t *) # + int flags = fetestexcept(FE_ALL_EXCEPT); + char *fstr = get_flag_state(flags); + + printf("%02d DOUBLE: %02.20e / %#020" PRIx64 " (%#x => %s)\n", + i, num, double_as_hex, flags, fstr); +} + +static void print_single_number(int i, float num) +{ + uint32_t single_as_hex = *(uint32_t *) # + int flags = fetestexcept(FE_ALL_EXCEPT); + char *fstr = get_flag_state(flags); + + printf("%02d SINGLE: %02.20e / %#010x (%#x => %s)\n", + i, num, single_as_hex, flags, fstr); +} + +static void print_half_number(int i, uint16_t num) +{ + int flags = fetestexcept(FE_ALL_EXCEPT); + char *fstr = get_flag_state(flags); + + printf("%02d HALF: %#04x (%#x => %s)\n", + i, num, flags, fstr); +} + +static void print_int64(int i, int64_t num) +{ + uint64_t int64_as_hex = *(uint64_t *) # + int flags = fetestexcept(FE_ALL_EXCEPT); + char *fstr = get_flag_state(flags); + + printf("%02d INT64: %20" PRId64 "/%#020" PRIx64 " (%#x => %s)\n", + i, num, int64_as_hex, flags, fstr); +} + +#ifndef SNANF +/* Signaling NaN macros, if supported. */ +# if __GNUC_PREREQ(3, 3) +# define SNANF (__builtin_nansf ("")) +# define SNAN (__builtin_nans ("")) +# define SNANL (__builtin_nansl ("")) +# endif +#endif + +float single_numbers[] = { -SNANF, + -NAN, + -INFINITY, + -FLT_MAX, + -1.111E+31, + -1.111E+30, + -1.08700982e-12, + -1.78051176e-20, + -FLT_MIN, + 0.0, + FLT_MIN, + 2.98023224e-08, + 5.96046E-8, /* min positive FP16 subnormal */ + 6.09756E-5, /* max subnormal FP16 */ + 6.10352E-5, /* min positive normal FP16 */ + 1.0, + 1.0009765625, /* smallest float after 1.0 FP16 */ + 2.0, + M_E, M_PI, + 65503.0, + 65504.0, /* max FP16 */ + 65505.0, + 131007.0, + 131008.0, /* max AFP */ + 131009.0, + 1.111E+30, + FLT_MAX, + INFINITY, + NAN, + SNANF }; + +static void convert_single_to_half(void) +{ + int i; + + printf("Converting single-precision to half-precision\n"); + + for (i = 0; i < ARRAY_SIZE(single_numbers); ++i) { + float input = single_numbers[i]; + + feclearexcept(FE_ALL_EXCEPT); + + print_single_number(i, input); +#if defined(__arm__) + uint32_t output; + asm("vcvtb.f16.f32 %0, %1" : "=t" (output) : "x" (input)); +#else + uint16_t output; + asm("fcvt %h0, %s1" : "=w" (output) : "x" (input)); +#endif + print_half_number(i, output); + } +} + +static void convert_single_to_double(void) +{ + int i; + + printf("Converting single-precision to double-precision\n"); + + for (i = 0; i < ARRAY_SIZE(single_numbers); ++i) { + float input = single_numbers[i]; + /* uint64_t output; */ + double output; + + feclearexcept(FE_ALL_EXCEPT); + + print_single_number(i, input); +#if defined(__arm__) + asm("vcvt.f64.f32 %P0, %1" : "=w" (output) : "t" (input)); +#else + asm("fcvt %d0, %s1" : "=w" (output) : "x" (input)); +#endif + print_double_number(i, output); + } +} + +static void convert_single_to_integer(void) +{ + int i; + + printf("Converting single-precision to integer\n"); + + for (i = 0; i < ARRAY_SIZE(single_numbers); ++i) { + float input = single_numbers[i]; + int64_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_single_number(i, input); +#if defined(__arm__) + /* asm("vcvt.s32.f32 %s0, %s1" : "=t" (output) : "t" (input)); */ + output = input; +#else + asm("fcvtzs %0, %s1" : "=r" (output) : "w" (input)); +#endif + print_int64(i, output); + } +} + +/* This allows us to initialise some doubles as pure hex */ +typedef union { + double d; + uint64_t h; +} test_doubles; + +test_doubles double_numbers[] = { + {SNAN}, + {-NAN}, + {-INFINITY}, + {-DBL_MAX}, + {-FLT_MAX-1.0}, + {-FLT_MAX}, + {-1.111E+31}, + {-1.111E+30}, /* half prec */ + {-2.0}, {-1.0}, + {-DBL_MIN}, + {-FLT_MIN}, + {0.0}, + {FLT_MIN}, + {2.98023224e-08}, + {5.96046E-8}, /* min positive FP16 subnormal */ + {6.09756E-5}, /* max subnormal FP16 */ + {6.10352E-5}, /* min positive normal FP16 */ + {1.0}, + {1.0009765625}, /* smallest float after 1.0 FP16 */ + {DBL_MIN}, + {1.3789972848607228e-308}, + {1.4914738736681624e-308}, + {1.0}, {2.0}, + {M_E}, {M_PI}, + {65503.0}, + {65504.0}, /* max FP16 */ + {65505.0}, + {131007.0}, + {131008.0}, /* max AFP */ + {131009.0}, + {.h = 0x41dfffffffc00000 }, /* to int = 0x7fffffff */ + {FLT_MAX}, + {FLT_MAX + 1.0}, + {DBL_MAX}, + {INFINITY}, + {NAN}, + {.h = 0x7ff0000000000001}, /* SNAN */ + {SNAN}, +}; + +static void convert_double_to_half(void) +{ + int i; + + printf("Converting double-precision to half-precision\n"); + + for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) { + double input = double_numbers[i].d; + uint16_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_double_number(i, input); + + /* as we don't have _Float16 support */ +#if defined(__arm__) + /* asm("vcvtb.f16.f64 %0, %P1" : "=t" (output) : "x" (input)); */ + output = input; +#else + asm("fcvt %h0, %d1" : "=w" (output) : "x" (input)); +#endif + print_half_number(i, output); + } +} + +static void convert_double_to_single(void) +{ + int i; + + printf("Converting double-precision to single-precision\n"); + + for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) { + double input = double_numbers[i].d; + uint32_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_double_number(i, input); + +#if defined(__arm__) + asm("vcvt.f32.f64 %0, %P1" : "=w" (output) : "x" (input)); +#else + asm("fcvt %s0, %d1" : "=w" (output) : "x" (input)); +#endif + + print_single_number(i, output); + } +} + +static void convert_double_to_integer(void) +{ + int i; + + printf("Converting double-precision to integer\n"); + + for (i = 0; i < ARRAY_SIZE(double_numbers); ++i) { + double input = double_numbers[i].d; + int64_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_double_number(i, input); +#if defined(__arm__) + /* asm("vcvt.s32.f32 %s0, %s1" : "=t" (output) : "t" (input)); */ + output = input; +#else + asm("fcvtzs %0, %d1" : "=r" (output) : "w" (input)); +#endif + print_int64(i, output); + } +} + +/* no handy defines for these numbers */ +uint16_t half_numbers[] = { + 0xffff, /* -NaN / AHP -Max */ + 0xfcff, /* -NaN / AHP */ + 0xfc01, /* -NaN / AHP */ + 0xfc00, /* -Inf */ + 0xfbff, /* -Max */ + 0xc000, /* -2 */ + 0xbc00, /* -1 */ + 0x8001, /* -MIN subnormal */ + 0x8000, /* -0 */ + 0x0000, /* +0 */ + 0x0001, /* MIN subnormal */ + 0x3c00, /* 1 */ + 0x7bff, /* Max */ + 0x7c00, /* Inf */ + 0x7c01, /* NaN / AHP */ + 0x7cff, /* NaN / AHP */ + 0x7fff, /* NaN / AHP +Max*/ +}; + +static void convert_half_to_double(void) +{ + int i; + + printf("Converting half-precision to double-precision\n"); + + for (i = 0; i < ARRAY_SIZE(half_numbers); ++i) { + uint16_t input = half_numbers[i]; + double output; + + feclearexcept(FE_ALL_EXCEPT); + + print_half_number(i, input); +#if defined(__arm__) + /* asm("vcvtb.f64.f16 %P0, %1" : "=w" (output) : "t" (input)); */ + output = input; +#else + asm("fcvt %d0, %h1" : "=w" (output) : "x" (input)); +#endif + print_double_number(i, output); + } +} + +static void convert_half_to_single(void) +{ + int i; + + printf("Converting half-precision to single-precision\n"); + + for (i = 0; i < ARRAY_SIZE(half_numbers); ++i) { + uint16_t input = half_numbers[i]; + float output; + + feclearexcept(FE_ALL_EXCEPT); + + print_half_number(i, input); +#if defined(__arm__) + asm("vcvtb.f32.f16 %0, %1" : "=w" (output) : "x" ((uint32_t)input)); +#else + asm("fcvt %s0, %h1" : "=w" (output) : "x" (input)); +#endif + print_single_number(i, output); + } +} + +static void convert_half_to_integer(void) +{ + int i; + + printf("Converting half-precision to integer\n"); + + for (i = 0; i < ARRAY_SIZE(half_numbers); ++i) { + uint16_t input = half_numbers[i]; + int64_t output; + + feclearexcept(FE_ALL_EXCEPT); + + print_half_number(i, input); +#if defined(__arm__) + /* asm("vcvt.s32.f16 %0, %1" : "=t" (output) : "t" (input)); v8.2*/ + output = input; +#else + asm("fcvt %s0, %h1" : "=w" (output) : "x" (input)); +#endif + print_int64(i, output); + } +} + +typedef struct { + int flag; + char *desc; +} float_mapping; + +float_mapping round_flags[] = { + { FE_TONEAREST, "to nearest" }, + { FE_UPWARD, "upwards" }, + { FE_DOWNWARD, "downwards" }, + { FE_TOWARDZERO, "to zero" } +}; + +int main(int argc, char *argv[argc]) +{ + int i; + + printf("#### Enabling IEEE Half Precision\n"); + + for (i = 0; i < ARRAY_SIZE(round_flags); ++i) { + fesetround(round_flags[i].flag); + printf("### Rounding %s\n", round_flags[i].desc); + convert_single_to_half(); + convert_single_to_double(); + convert_double_to_half(); + convert_double_to_single(); + convert_half_to_single(); + convert_half_to_double(); + } + + /* convert to integer */ + convert_single_to_integer(); + convert_double_to_integer(); + convert_half_to_integer(); + + /* And now with ARM alternative FP16 */ +#if defined(__arm__) + /* See glibc sysdeps/arm/fpu_control.h */ + asm("mrc p10, 7, r1, cr1, cr0, 0\n\t" + "orr r1, r1, %[flags]\n\t" + "mcr p10, 7, r1, cr1, cr0, 0\n\t" + : /* no output */ : [flags] "n" (1 << 26) : "r1" ); +#else + asm("mrs x1, fpcr\n\t" + "orr x1, x1, %[flags]\n\t" + "msr fpcr, x1\n\t" + : /* no output */ : [flags] "n" (1 << 26) : "x1" ); +#endif + + printf("#### Enabling ARM Alternative Half Precision\n"); + + for (i = 0; i < ARRAY_SIZE(round_flags); ++i) { + fesetround(round_flags[i].flag); + printf("### Rounding %s\n", round_flags[i].desc); + convert_single_to_half(); + convert_single_to_double(); + convert_double_to_half(); + convert_double_to_single(); + convert_half_to_single(); + convert_half_to_double(); + } + + /* convert to integer */ + convert_single_to_integer(); + convert_double_to_integer(); + convert_half_to_integer(); + + return 0; +} diff --git a/tests/tcg/arm/fcvt.ref b/tests/tcg/arm/fcvt.ref new file mode 100644 index 0000000000..f052b6d7e5 --- /dev/null +++ b/tests/tcg/arm/fcvt.ref @@ -0,0 +1,3268 @@ +#### Enabling IEEE Half Precision +### Rounding to nearest +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding upwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x401 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4249 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7c00 (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding downwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfc00 (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730512e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding to zero +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0xff00 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0xfe00 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xfc00 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xfbff (0x14 => OVERFLOW INEXACT ) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7bff (0x14 => OVERFLOW INEXACT ) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7c00 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0x7e00 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0x7f00 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -nan / 0xffffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -nan / 0xffdfe000 (0x1 => INVALID) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -nan / 0xffc02000 (0x1 => INVALID) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -inf / 0xff800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: inf / 0x7f800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: nan / 0x7fc02000 (0x1 => INVALID) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: nan / 0x7fdfe000 (0x1 => INVALID) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: nan / 0x7fffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +Converting single-precision to integer +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 INT64: 1/0x000000000000000001 (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 INT64: 65503/0x00000000000000ffdf (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 INT64: 65504/0x00000000000000ffe0 (0x10 => INEXACT ) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 INT64: 65505/0x00000000000000ffe1 (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 INT64: 131007/0x00000000000001ffbf (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 INT64: 131008/0x00000000000001ffc0 (0x10 => INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 INT64: 131009/0x00000000000001ffc1 (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 INT64: -1/0x00ffffffffffffffff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 INT64: 0/00000000000000000000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting double-precision to integer +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 INT64: 1/0x000000000000000001 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 INT64: -2/0x00fffffffffffffffe (0x10 => INEXACT ) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 INT64: -1/0x00ffffffffffffffff (0x10 => INEXACT ) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 INT64: 0/00000000000000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 INT64: 0/00000000000000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 INT64: 0/00000000000000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 INT64: 65503/0x00000000000000ffdf (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 INT64: 65504/0x00000000000000ffe0 (0x10 => INEXACT ) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 INT64: 65505/0x00000000000000ffe1 (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 INT64: 131007/0x00000000000001ffbf (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 INT64: 131008/0x00000000000001ffc0 (0x10 => INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 INT64: 131009/0x00000000000001ffc1 (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 INT64: 2147483647/0x00000000007fffffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 INT64: -1/0x00ffffffffffffffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 INT64: 0/00000000000000000000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 INT64: 0/00000000000000000000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting half-precision to integer +00 HALF: 0xffff (0 => OK) +00 INT64: 65535/0x00000000000000ffff (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 INT64: 64767/0x00000000000000fcff (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 INT64: 64513/0x00000000000000fc01 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 INT64: 64512/0x00000000000000fc00 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 INT64: 64511/0x00000000000000fbff (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 INT64: 49152/0x00000000000000c000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 INT64: 48128/0x00000000000000bc00 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 INT64: 32769/0x000000000000008001 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 INT64: 32768/0x000000000000008000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 INT64: 1/0x000000000000000001 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 INT64: 15360/0x000000000000003c00 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 INT64: 31743/0x000000000000007bff (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 INT64: 31744/0x000000000000007c00 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 INT64: 31745/0x000000000000007c01 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 INT64: 31999/0x000000000000007cff (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 INT64: 32767/0x000000000000007fff (0 => OK) +#### Enabling ARM Alternative Half Precision +### Rounding to nearest +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7fff (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding upwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0x01 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x400 (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 HALF: 0x401 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x4170 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4249 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bff (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7c00 (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7fff (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x1 => INVALID) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750797e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005935e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005935e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015673e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015673e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851006e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851006e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324219e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324219e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635273e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635273e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859812e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766502400000000000e+09 / 0x4f730c3b (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962457600000000000e+09 / 0x4f71605e (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750797e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013061e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638080000000000000e+08 / 0x4e4c0001 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015662e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026624000000000000e+08 / 0x4e4e0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994299e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896384000000000000e+08 / 0x4e61ff01 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013665e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912768000000000000e+08 / 0x4e620001 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138310e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282844e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238764e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509080e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675456000000000000e+09 / 0x4e805bf1 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311600e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07853004800000000000e+09 / 0x4e809220 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32540006400000000000e+09 / 0x4e9e0000 (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859812e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570815e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding downwards +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8001 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7ffe (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859812e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909791e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909791e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635273e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635273e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289629e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289629e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730512e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730512e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750797e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570815e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859812e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007530e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999085e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138310e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750797e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +### Rounding to zero +Converting single-precision to half-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 HALF: 0x8000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 HALF: 0x8000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 HALF: 0xffff (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 HALF: 0xffff (0x1 => INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 HALF: 0xffff (0x1 => INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 HALF: 0xffff (0x1 => INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 HALF: 0x8000 (0x18 => UNDERFLOW INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 HALF: 0000 (0x18 => UNDERFLOW INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 HALF: 0x3ff (0x18 => UNDERFLOW INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 HALF: 0x400 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 HALF: 0x3c00 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 HALF: 0x3c01 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 HALF: 0x4000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 HALF: 0x416f (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 HALF: 0x4248 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 HALF: 0x7bfe (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 HALF: 0x7bff (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 HALF: 0x7bff (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 HALF: 0x7ffe (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 HALF: 0x7fff (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 HALF: 0x7fff (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 HALF: 0x7fff (0x1 => INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 HALF: 0x7fff (0x1 => INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 HALF: 0x7fff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 HALF: 0000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 HALF: 0000 (0x1 => INVALID) +Converting single-precision to double-precision +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 DOUBLE: -nan / 0x00fffc000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 DOUBLE: -1.11100004769645909790e+31 / 0x00c661874b20000000 (0 => OK) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 DOUBLE: -1.11100003258488635272e+30 / 0x00c62c0bab60000000 (0 => OK) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 DOUBLE: -1.08700982243137289628e-12 / 0x00bd731f7500000000 (0 => OK) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 DOUBLE: -1.78051176151664730511e-20 / 0x00bbd5054440000000 (0 => OK) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 DOUBLE: 2.98023223876953125000e-08 / 0x003e60000000000000 (0 => OK) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 DOUBLE: 5.96045985901128005934e-08 / 0x003e6ffffe60000000 (0 => OK) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 DOUBLE: 6.09755988989491015672e-05 / 0x003f0ff801a0000000 (0 => OK) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 DOUBLE: 6.10351999057456851005e-05 / 0x003f100000c0000000 (0 => OK) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 DOUBLE: 2.71828174591064453125e+00 / 0x004005bf0a80000000 (0 => OK) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 DOUBLE: 3.14159274101257324218e+00 / 0x00400921fb60000000 (0 => OK) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 DOUBLE: 1.11100003258488635272e+30 / 0x00462c0bab60000000 (0 => OK) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 DOUBLE: nan / 0x007ffc000000000000 (0x1 => INVALID) +Converting double-precision to half-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 HALF: 0000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 HALF: 0000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 HALF: 0000 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 HALF: 0000 (0x1 => INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 HALF: 0000 (0x1 => INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 HALF: 0000 (0x1 => INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 HALF: 0000 (0x1 => INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 HALF: 0000 (0x1 => INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 HALF: 0000 (0x1 => INVALID) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 HALF: 0000 (0x1 => INVALID) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 HALF: 0000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 HALF: 0000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 HALF: 0000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 HALF: 0000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 HALF: 0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 HALF: 0000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 HALF: 0000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 HALF: 0000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 HALF: 0x01 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 HALF: 0x01 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 HALF: 0000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 HALF: 0000 (0x10 => INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 HALF: 0000 (0x10 => INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 HALF: 0x01 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 HALF: 0x02 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 HALF: 0x02 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 HALF: 0x03 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 HALF: 0xffdf (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 HALF: 0xffe0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 HALF: 0xffe1 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 HALF: 0xffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 HALF: 0xffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 HALF: 0xffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 HALF: 0xffff (0 => OK) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 HALF: 0xffff (0x1 => INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 HALF: 0xffff (0x1 => INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 HALF: 0xffff (0x1 => INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 HALF: 0xffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 HALF: 0000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 HALF: 0000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 HALF: 0000 (0x1 => INVALID) +Converting double-precision to single-precision +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 SINGLE: 4.29077299200000000000e+09 / 0x4f7fc000 (0 => OK) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 SINGLE: 4.28657868800000000000e+09 / 0x4f7f8000 (0 => OK) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x14 => OVERFLOW INEXACT ) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 SINGLE: 4.28657843200000000000e+09 / 0x4f7f7fff (0x10 => INEXACT ) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 SINGLE: 4.07766476800000000000e+09 / 0x4f730c3a (0x10 => INEXACT ) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 SINGLE: 4.04962432000000000000e+09 / 0x4f71605d (0x10 => INEXACT ) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 SINGLE: 3.22122547200000000000e+09 / 0x4f400000 (0 => OK) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 SINGLE: 3.21283686400000000000e+09 / 0x4f3f8000 (0 => OK) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 SINGLE: 2.14748364800000000000e+09 / 0x4f000000 (0x18 => UNDERFLOW INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 SINGLE: 2.15587225600000000000e+09 / 0x4f008000 (0 => OK) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 SINGLE: 8.38860800000000000000e+06 / 0x4b000000 (0 => OK) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 SINGLE: 8.55638016000000000000e+08 / 0x4e4c0000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 SINGLE: 8.64026560000000000000e+08 / 0x4e4dffff (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 SINGLE: 9.47896320000000000000e+08 / 0x4e61ff00 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 SINGLE: 9.47912704000000000000e+08 / 0x4e620000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 SINGLE: 1.06536140800000000000e+09 / 0x4e7e0080 (0 => OK) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 SINGLE: 1.06535321600000000000e+09 / 0x4e7e0000 (0 => OK) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 SINGLE: 1.07374182400000000000e+09 / 0x4e800000 (0 => OK) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 SINGLE: 1.07675443200000000000e+09 / 0x4e805bf0 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 SINGLE: 1.07852992000000000000e+09 / 0x4e80921f (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 SINGLE: 1.19956249600000000000e+09 / 0x4e8effbe (0 => OK) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 SINGLE: 1.19956275200000000000e+09 / 0x4e8effc0 (0 => OK) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 SINGLE: 1.19956300800000000000e+09 / 0x4e8effc2 (0 => OK) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 SINGLE: 1.20795123200000000000e+09 / 0x4e8fffbf (0 => OK) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 SINGLE: 1.20795136000000000000e+09 / 0x4e8fffc0 (0 => OK) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 SINGLE: 1.20795148800000000000e+09 / 0x4e8fffc1 (0 => OK) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 SINGLE: 1.32539993600000000000e+09 / 0x4e9dffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x10 => INEXACT ) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 SINGLE: 2.13909491200000000000e+09 / 0x4efeffff (0x14 => OVERFLOW INEXACT ) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 SINGLE: 2.13909504000000000000e+09 / 0x4eff0000 (0 => OK) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0 => OK) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 SINGLE: 2.14328934400000000000e+09 / 0x4eff8000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 SINGLE: 2.14538649600000000000e+09 / 0x4effc000 (0x1 => INVALID) +Converting half-precision to single-precision +00 HALF: 0xffff (0 => OK) +00 SINGLE: -1.31008000000000000000e+05 / 0xc7ffe000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 SINGLE: -8.18560000000000000000e+04 / 0xc79fe000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 SINGLE: -6.56000000000000000000e+04 / 0xc7802000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 SINGLE: -6.55360000000000000000e+04 / 0xc7800000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 SINGLE: -6.55040000000000000000e+04 / 0xc77fe000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 SINGLE: -2.00000000000000000000e+00 / 0xc0000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 SINGLE: -1.00000000000000000000e+00 / 0xbf800000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 SINGLE: -5.96046447753906250000e-08 / 0xb3800000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 SINGLE: -0.00000000000000000000e+00 / 0x80000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 SINGLE: 5.96046447753906250000e-08 / 0x33800000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 SINGLE: 6.55360000000000000000e+04 / 0x47800000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 SINGLE: 6.56000000000000000000e+04 / 0x47802000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 SINGLE: 8.18560000000000000000e+04 / 0x479fe000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +Converting half-precision to double-precision +00 HALF: 0xffff (0 => OK) +00 DOUBLE: 6.55350000000000000000e+04 / 0x0040efffe000000000 (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 DOUBLE: 6.47670000000000000000e+04 / 0x0040ef9fe000000000 (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 DOUBLE: 6.45130000000000000000e+04 / 0x0040ef802000000000 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 DOUBLE: 6.45120000000000000000e+04 / 0x0040ef800000000000 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 DOUBLE: 6.45110000000000000000e+04 / 0x0040ef7fe000000000 (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 DOUBLE: 4.91520000000000000000e+04 / 0x0040e8000000000000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 DOUBLE: 4.81280000000000000000e+04 / 0x0040e7800000000000 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 DOUBLE: 3.27690000000000000000e+04 / 0x0040e0002000000000 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 DOUBLE: 3.27680000000000000000e+04 / 0x0040e0000000000000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 DOUBLE: 1.53600000000000000000e+04 / 0x0040ce000000000000 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 DOUBLE: 3.17430000000000000000e+04 / 0x0040deffc000000000 (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 DOUBLE: 3.17440000000000000000e+04 / 0x0040df000000000000 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 DOUBLE: 3.17450000000000000000e+04 / 0x0040df004000000000 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 DOUBLE: 3.19990000000000000000e+04 / 0x0040df3fc000000000 (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 DOUBLE: 3.27670000000000000000e+04 / 0x0040dfffc000000000 (0 => OK) +Converting single-precision to integer +00 SINGLE: -nan / 0xffa00000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 SINGLE: -nan / 0xffc00000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 SINGLE: -inf / 0xff800000 (0 => OK) +02 INT64: 1/0x000000000000000001 (0x1 => INVALID) +03 SINGLE: -3.40282346638528859811e+38 / 0xff7fffff (0 => OK) +03 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +04 SINGLE: -1.11100004769645909790e+31 / 0xf30c3a59 (0 => OK) +04 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +05 SINGLE: -1.11100003258488635272e+30 / 0xf1605d5b (0 => OK) +05 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +06 SINGLE: -1.08700982243137289628e-12 / 0xab98fba8 (0 => OK) +06 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +07 SINGLE: -1.78051176151664730511e-20 / 0x9ea82a22 (0 => OK) +07 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +08 SINGLE: -1.17549435082228750796e-38 / 0x80800000 (0 => OK) +08 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +09 SINGLE: 0.00000000000000000000e+00 / 0000000000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 SINGLE: 1.17549435082228750796e-38 / 0x00800000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 SINGLE: 2.98023223876953125000e-08 / 0x33000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 SINGLE: 5.96045985901128005934e-08 / 0x337ffff3 (0 => OK) +12 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +13 SINGLE: 6.09755988989491015672e-05 / 0x387fc00d (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 SINGLE: 6.10351999057456851005e-05 / 0x38800006 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 SINGLE: 1.00000000000000000000e+00 / 0x3f800000 (0 => OK) +15 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +16 SINGLE: 1.00097656250000000000e+00 / 0x3f802000 (0 => OK) +16 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +17 SINGLE: 2.00000000000000000000e+00 / 0x40000000 (0 => OK) +17 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +18 SINGLE: 2.71828174591064453125e+00 / 0x402df854 (0 => OK) +18 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +19 SINGLE: 3.14159274101257324218e+00 / 0x40490fdb (0 => OK) +19 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +20 SINGLE: 6.55030000000000000000e+04 / 0x477fdf00 (0 => OK) +20 INT64: 65503/0x00000000000000ffdf (0x10 => INEXACT ) +21 SINGLE: 6.55040000000000000000e+04 / 0x477fe000 (0 => OK) +21 INT64: 65504/0x00000000000000ffe0 (0x10 => INEXACT ) +22 SINGLE: 6.55050000000000000000e+04 / 0x477fe100 (0 => OK) +22 INT64: 65505/0x00000000000000ffe1 (0x10 => INEXACT ) +23 SINGLE: 1.31007000000000000000e+05 / 0x47ffdf80 (0 => OK) +23 INT64: 131007/0x00000000000001ffbf (0x10 => INEXACT ) +24 SINGLE: 1.31008000000000000000e+05 / 0x47ffe000 (0 => OK) +24 INT64: 131008/0x00000000000001ffc0 (0x10 => INEXACT ) +25 SINGLE: 1.31009000000000000000e+05 / 0x47ffe080 (0 => OK) +25 INT64: 131009/0x00000000000001ffc1 (0x10 => INEXACT ) +26 SINGLE: 1.11100003258488635272e+30 / 0x71605d5b (0 => OK) +26 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +27 SINGLE: 3.40282346638528859811e+38 / 0x7f7fffff (0 => OK) +27 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +28 SINGLE: inf / 0x7f800000 (0 => OK) +28 INT64: -1/0x00ffffffffffffffff (0x1 => INVALID) +29 SINGLE: nan / 0x7fc00000 (0 => OK) +29 INT64: 0/00000000000000000000 (0x1 => INVALID) +30 SINGLE: nan / 0x7fa00000 (0 => OK) +30 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting double-precision to integer +00 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +00 INT64: 0/00000000000000000000 (0x1 => INVALID) +01 DOUBLE: -nan / 0x00fff8000000000000 (0 => OK) +01 INT64: 0/00000000000000000000 (0x1 => INVALID) +02 DOUBLE: -inf / 0x00fff0000000000000 (0 => OK) +02 INT64: 1/0x000000000000000001 (0x1 => INVALID) +03 DOUBLE: -1.79769313486231570814e+308 / 0x00ffefffffffffffff (0 => OK) +03 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +04 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +04 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +05 DOUBLE: -3.40282346638528859811e+38 / 0x00c7efffffe0000000 (0 => OK) +05 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +06 DOUBLE: -1.11100000000000007529e+31 / 0x00c661874b135ff654 (0 => OK) +06 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +07 DOUBLE: -1.11099999999999999084e+30 / 0x00c62c0bab523323b9 (0 => OK) +07 INT64: 1/0x000000000000000001 (0x11 => INEXACT INVALID) +08 DOUBLE: -2.00000000000000000000e+00 / 0x00c000000000000000 (0 => OK) +08 INT64: -2/0x00fffffffffffffffe (0x10 => INEXACT ) +09 DOUBLE: -1.00000000000000000000e+00 / 0x00bff0000000000000 (0 => OK) +09 INT64: -1/0x00ffffffffffffffff (0x10 => INEXACT ) +10 DOUBLE: -2.22507385850720138309e-308 / 0x008010000000000000 (0 => OK) +10 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +11 DOUBLE: -1.17549435082228750796e-38 / 0x00b810000000000000 (0 => OK) +11 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +12 DOUBLE: 0.00000000000000000000e+00 / 00000000000000000000 (0 => OK) +12 INT64: 0/00000000000000000000 (0 => OK) +13 DOUBLE: 1.17549435082228750796e-38 / 0x003810000000000000 (0 => OK) +13 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +14 DOUBLE: 2.98023224000000013060e-08 / 0x003e600000001c5f68 (0 => OK) +14 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +15 DOUBLE: 5.96046000000000015661e-08 / 0x003e6ffffe6cb2fa82 (0 => OK) +15 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +16 DOUBLE: 6.09755999999999994298e-05 / 0x003f0ff801a9af58a1 (0 => OK) +16 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +17 DOUBLE: 6.10352000000000013664e-05 / 0x003f100000c06a1ef5 (0 => OK) +17 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +18 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +18 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +19 DOUBLE: 1.00097656250000000000e+00 / 0x003ff0040000000000 (0 => OK) +19 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +20 DOUBLE: 2.22507385850720138309e-308 / 0x000010000000000000 (0 => OK) +20 INT64: 0/00000000000000000000 (0x10 => INEXACT ) +21 DOUBLE: 1.37899728486072282843e-308 / 0x000009ea82a2287680 (0 => OK) +21 INT64: 0/00000000000000000000 (0x18 => UNDERFLOW INEXACT ) +22 DOUBLE: 1.49147387366816238763e-308 / 0x00000ab98fba843210 (0 => OK) +22 INT64: 0/00000000000000000000 (0x18 => UNDERFLOW INEXACT ) +23 DOUBLE: 1.00000000000000000000e+00 / 0x003ff0000000000000 (0 => OK) +23 INT64: 1/0x000000000000000001 (0x10 => INEXACT ) +24 DOUBLE: 2.00000000000000000000e+00 / 0x004000000000000000 (0 => OK) +24 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +25 DOUBLE: 2.71828182845904509079e+00 / 0x004005bf0a8b145769 (0 => OK) +25 INT64: 2/0x000000000000000002 (0x10 => INEXACT ) +26 DOUBLE: 3.14159265358979311599e+00 / 0x00400921fb54442d18 (0 => OK) +26 INT64: 3/0x000000000000000003 (0x10 => INEXACT ) +27 DOUBLE: 6.55030000000000000000e+04 / 0x0040effbe000000000 (0 => OK) +27 INT64: 65503/0x00000000000000ffdf (0x10 => INEXACT ) +28 DOUBLE: 6.55040000000000000000e+04 / 0x0040effc0000000000 (0 => OK) +28 INT64: 65504/0x00000000000000ffe0 (0x10 => INEXACT ) +29 DOUBLE: 6.55050000000000000000e+04 / 0x0040effc2000000000 (0 => OK) +29 INT64: 65505/0x00000000000000ffe1 (0x10 => INEXACT ) +30 DOUBLE: 1.31007000000000000000e+05 / 0x0040fffbf000000000 (0 => OK) +30 INT64: 131007/0x00000000000001ffbf (0x10 => INEXACT ) +31 DOUBLE: 1.31008000000000000000e+05 / 0x0040fffc0000000000 (0 => OK) +31 INT64: 131008/0x00000000000001ffc0 (0x10 => INEXACT ) +32 DOUBLE: 1.31009000000000000000e+05 / 0x0040fffc1000000000 (0 => OK) +32 INT64: 131009/0x00000000000001ffc1 (0x10 => INEXACT ) +33 DOUBLE: 2.14748364700000000000e+09 / 0x0041dfffffffc00000 (0 => OK) +33 INT64: 2147483647/0x00000000007fffffff (0x10 => INEXACT ) +34 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +34 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +35 DOUBLE: 3.40282346638528859811e+38 / 0x0047efffffe0000000 (0 => OK) +35 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +36 DOUBLE: 1.79769313486231570814e+308 / 0x007fefffffffffffff (0 => OK) +36 INT64: -1/0x00ffffffffffffffff (0x11 => INEXACT INVALID) +37 DOUBLE: inf / 0x007ff0000000000000 (0 => OK) +37 INT64: -1/0x00ffffffffffffffff (0x1 => INVALID) +38 DOUBLE: nan / 0x007ff8000000000000 (0 => OK) +38 INT64: 0/00000000000000000000 (0x1 => INVALID) +39 DOUBLE: nan / 0x007ff0000000000001 (0 => OK) +39 INT64: 0/00000000000000000000 (0x1 => INVALID) +40 DOUBLE: nan / 0x007ff4000000000000 (0 => OK) +40 INT64: 0/00000000000000000000 (0x1 => INVALID) +Converting half-precision to integer +00 HALF: 0xffff (0 => OK) +00 INT64: 65535/0x00000000000000ffff (0 => OK) +01 HALF: 0xfcff (0 => OK) +01 INT64: 64767/0x00000000000000fcff (0 => OK) +02 HALF: 0xfc01 (0 => OK) +02 INT64: 64513/0x00000000000000fc01 (0 => OK) +03 HALF: 0xfc00 (0 => OK) +03 INT64: 64512/0x00000000000000fc00 (0 => OK) +04 HALF: 0xfbff (0 => OK) +04 INT64: 64511/0x00000000000000fbff (0 => OK) +05 HALF: 0xc000 (0 => OK) +05 INT64: 49152/0x00000000000000c000 (0 => OK) +06 HALF: 0xbc00 (0 => OK) +06 INT64: 48128/0x00000000000000bc00 (0 => OK) +07 HALF: 0x8001 (0 => OK) +07 INT64: 32769/0x000000000000008001 (0 => OK) +08 HALF: 0x8000 (0 => OK) +08 INT64: 32768/0x000000000000008000 (0 => OK) +09 HALF: 0000 (0 => OK) +09 INT64: 0/00000000000000000000 (0 => OK) +10 HALF: 0x01 (0 => OK) +10 INT64: 1/0x000000000000000001 (0 => OK) +11 HALF: 0x3c00 (0 => OK) +11 INT64: 15360/0x000000000000003c00 (0 => OK) +12 HALF: 0x7bff (0 => OK) +12 INT64: 31743/0x000000000000007bff (0 => OK) +13 HALF: 0x7c00 (0 => OK) +13 INT64: 31744/0x000000000000007c00 (0 => OK) +14 HALF: 0x7c01 (0 => OK) +14 INT64: 31745/0x000000000000007c01 (0 => OK) +15 HALF: 0x7cff (0 => OK) +15 INT64: 31999/0x000000000000007cff (0 => OK) +16 HALF: 0x7fff (0 => OK) +16 INT64: 32767/0x000000000000007fff (0 => OK) diff --git a/tests/tcg/hello-arm.c b/tests/tcg/arm/hello-arm.c similarity index 100% rename from tests/tcg/hello-arm.c rename to tests/tcg/arm/hello-arm.c diff --git a/tests/tcg/test-arm-iwmmxt.s b/tests/tcg/arm/test-arm-iwmmxt.S similarity index 100% rename from tests/tcg/test-arm-iwmmxt.s rename to tests/tcg/arm/test-arm-iwmmxt.S diff --git a/tests/tcg/hppa/Makefile.include b/tests/tcg/hppa/Makefile.include new file mode 100644 index 0000000000..da2353430e --- /dev/null +++ b/tests/tcg/hppa/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-hppa-cross +DOCKER_CROSS_COMPILER=hppa-linux-gnu-gcc diff --git a/tests/tcg/hppa/Makefile.target b/tests/tcg/hppa/Makefile.target new file mode 100644 index 0000000000..8bf01966bd --- /dev/null +++ b/tests/tcg/hppa/Makefile.target @@ -0,0 +1,6 @@ +# -*- Mode: makefile -*- +# +# HPPA specific tweaks - specifically masking out broken tests + +# On parisc Linux supports 4K/16K/64K (but currently only 4k works) +EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-16384 run-test-mmap-65536 diff --git a/tests/tcg/i386/Makefile.include b/tests/tcg/i386/Makefile.include new file mode 100644 index 0000000000..be1c3008dd --- /dev/null +++ b/tests/tcg/i386/Makefile.include @@ -0,0 +1,9 @@ +# +# Makefile.include for all i386 +# +# There is enough brokeness in x86_64 compilers that we don't default +# to using the x86_64 system compiler for i386 binaries. +# + +DOCKER_IMAGE=fedora-i386-cross +DOCKER_CROSS_COMPILER=gcc diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target new file mode 100644 index 0000000000..b4033ba3d1 --- /dev/null +++ b/tests/tcg/i386/Makefile.target @@ -0,0 +1,47 @@ +# i386 cross compile notes + +I386_SRC=$(SRC_PATH)/tests/tcg/i386 + +# Set search path for all sources +VPATH += $(I386_SRC) + +I386_SRCS=$(notdir $(wildcard $(I386_SRC)/*.c)) +I386_TESTS=$(I386_SRCS:.c=) +I386_ONLY_TESTS=$(filter-out test-i386-ssse3, $(I386_TESTS)) +# Update TESTS +TESTS+=$(I386_ONLY_TESTS) + +ifneq ($(TARGET_NAME),x86_64) +CFLAGS+=-m32 +endif + +# +# hello-i386 is a barebones app +# +hello-i386: CFLAGS+=-ffreestanding +hello-i386: LDFLAGS+=-nostdlib + +# +# test-386 includes a couple of additional objects that need to be linked together +# + +test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S test-i386.h test-i386-shift.h test-i386-muldiv.h + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ + $( $@,"GENREF","generating $@") + +run-test-i386-fprem: TIMEOUT=60 +run-test-i386-fprem: test-i386-fprem + $(call run-test,test-i386-fprem, $(QEMU) $<,"$< on $(TARGET_NAME)") + $(call diff-out,test-i386-fprem, $(I386_SRC)/$<.ref) +else +run-test-i386-fprem: test-i386-fprem + $(call skip-test, $<, "SLOW") +endif + +# On i386 and x86_64 Linux only supports 4k pages (large pages are a different hack) +EXTRA_RUNS+=run-test-mmap-4096 diff --git a/tests/tcg/i386/README b/tests/tcg/i386/README new file mode 100644 index 0000000000..09e88f30dc --- /dev/null +++ b/tests/tcg/i386/README @@ -0,0 +1,35 @@ +These are i386 specific guest programs + +test-i386 +--------- + +This program executes most of the 16 bit and 32 bit x86 instructions and +generates a text output, for comparison with the output obtained with +a real CPU or another emulator. + +The Linux system call modify_ldt() is used to create x86 selectors +to test some 16 bit addressing and 32 bit with segmentation cases. + +The Linux system call vm86() is used to test vm86 emulation. + +Various exceptions are raised to test most of the x86 user space +exception reporting. + +linux-test +---------- + +This program tests various Linux system calls. It is used to verify +that the system call parameters are correctly converted between target +and host CPUs. + +test-i386-fprem +--------------- + +test-mmap +--------- + +sha1 +---- + +hello-i386 +---------- diff --git a/tests/tcg/hello-i386.c b/tests/tcg/i386/hello-i386.c similarity index 96% rename from tests/tcg/hello-i386.c rename to tests/tcg/i386/hello-i386.c index fa00380de2..cfeb24b2f5 100644 --- a/tests/tcg/hello-i386.c +++ b/tests/tcg/i386/hello-i386.c @@ -20,6 +20,7 @@ static inline int write(int fd, const char * buf, int len) return status; } +void _start(void); void _start(void) { write(1, "Hello World\n", 12); diff --git a/tests/tcg/test-i386-code16.S b/tests/tcg/i386/test-i386-code16.S similarity index 100% rename from tests/tcg/test-i386-code16.S rename to tests/tcg/i386/test-i386-code16.S diff --git a/tests/tcg/test-i386-fprem.c b/tests/tcg/i386/test-i386-fprem.c similarity index 97% rename from tests/tcg/test-i386-fprem.c rename to tests/tcg/i386/test-i386-fprem.c index 1a71623204..66f5a9657d 100644 --- a/tests/tcg/test-i386-fprem.c +++ b/tests/tcg/i386/test-i386-fprem.c @@ -23,7 +23,10 @@ * along with this program; if not, see . */ -#include "qemu/osdep.h" +#include +#include + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) /* * Inspired by 's union ieee854_long_double, but with single @@ -39,7 +42,7 @@ union float80u { unsigned int exponent:15; unsigned int negative:1; unsigned int empty:16; - } QEMU_PACKED ieee; + } __attribute__((packed)) ieee; /* This is for NaNs in the IEEE 854 double-extended-precision format. */ struct { @@ -49,7 +52,7 @@ union float80u { unsigned int exponent:15; unsigned int negative:1; unsigned int empty:16; - } QEMU_PACKED ieee_nan; + } __attribute__((packed)) ieee_nan; }; #define IEEE854_LONG_DOUBLE_BIAS 0x3fff @@ -229,6 +232,7 @@ static void test_fprem_cases(void) do_fprem_stack_underflow(); printf("= invalid operation =\n"); + do_fprem(q_nan.d, 1.0); do_fprem(s_nan.d, 1.0); do_fprem(1.0, 0.0); do_fprem(pos_inf.d, 1.0); @@ -238,6 +242,8 @@ static void test_fprem_cases(void) do_fprem(pos_denorm.d, 1.0); do_fprem(1.0, pos_denorm.d); + do_fprem(smallest_positive_norm.d, smallest_positive_norm.d); + /* printf("= underflow =\n"); */ /* TODO: Is there a case where FPREM raises underflow? */ } diff --git a/tests/tcg/test-i386-muldiv.h b/tests/tcg/i386/test-i386-muldiv.h similarity index 100% rename from tests/tcg/test-i386-muldiv.h rename to tests/tcg/i386/test-i386-muldiv.h diff --git a/tests/tcg/test-i386-shift.h b/tests/tcg/i386/test-i386-shift.h similarity index 100% rename from tests/tcg/test-i386-shift.h rename to tests/tcg/i386/test-i386-shift.h diff --git a/tests/tcg/test-i386-ssse3.c b/tests/tcg/i386/test-i386-ssse3.c similarity index 100% rename from tests/tcg/test-i386-ssse3.c rename to tests/tcg/i386/test-i386-ssse3.c diff --git a/tests/tcg/test-i386-vm86.S b/tests/tcg/i386/test-i386-vm86.S similarity index 100% rename from tests/tcg/test-i386-vm86.S rename to tests/tcg/i386/test-i386-vm86.S diff --git a/tests/tcg/test-i386.c b/tests/tcg/i386/test-i386.c similarity index 99% rename from tests/tcg/test-i386.c rename to tests/tcg/i386/test-i386.c index 9599204895..a29b41e764 100644 --- a/tests/tcg/test-i386.c +++ b/tests/tcg/i386/test-i386.c @@ -17,7 +17,6 @@ * along with this program; if not, see . */ #define _GNU_SOURCE -#include "qemu/compiler.h" #include #include #include @@ -2107,8 +2106,8 @@ static void test_enter(void) #ifdef TEST_SSE -typedef int __m64 __attribute__ ((__mode__ (__V2SI__))); -typedef float __m128 __attribute__ ((__mode__(__V4SF__))); +typedef int __m64 __attribute__ ((vector_size(8))); +typedef float __m128 __attribute__ ((vector_size(16))); typedef union { double d[2]; @@ -2259,7 +2258,7 @@ SSE_OP(a ## sd); "pop %0\n"\ : "=rm" (eflags)\ : "x" (a.dq), "x" (b.dq));\ - printf("%-9s: a=%f b=%f cc=%04x\n",\ + printf("%-9s: a=%f b=%f cc=%04lx\n",\ #op, a1, b1,\ eflags & (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A));\ } diff --git a/tests/tcg/test-i386.h b/tests/tcg/i386/test-i386.h similarity index 100% rename from tests/tcg/test-i386.h rename to tests/tcg/i386/test-i386.h diff --git a/tests/tcg/m68k/Makefile.include b/tests/tcg/m68k/Makefile.include new file mode 100644 index 0000000000..cd7c6bf50d --- /dev/null +++ b/tests/tcg/m68k/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-m68k-cross +DOCKER_CROSS_COMPILER=m68k-linux-gnu-gcc diff --git a/tests/tcg/m68k/Makefile.target b/tests/tcg/m68k/Makefile.target new file mode 100644 index 0000000000..62f109eef4 --- /dev/null +++ b/tests/tcg/m68k/Makefile.target @@ -0,0 +1,7 @@ +# -*- Mode: makefile -*- +# +# m68k specific tweaks - specifically masking out broken tests +# + +# On m68k Linux supports 4k and 8k pages (but 8k is currently broken) +EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192 diff --git a/tests/tcg/mips/Makefile.include b/tests/tcg/mips/Makefile.include new file mode 100644 index 0000000000..4a14fc078d --- /dev/null +++ b/tests/tcg/mips/Makefile.include @@ -0,0 +1,20 @@ +# +# Makefile.include for all MIPs targets +# +# As Debian doesn't support mip64 in big endian mode the only way to +# build BE is to pass a working cross compiler to ./configure +# + +ifeq ($(TARGET_NAME),mips64el) +DOCKER_IMAGE=debian-mips64el-cross +DOCKER_CROSS_COMPILER=mips64el-linux-gnuabi64-gcc +else ifeq ($(TARGET_NAME),mips64) +DOCKER_IMAGE=debian-mips64-cross +DOCKER_CROSS_COMPILER=mips64-linux-gnuabi64-gcc +else ifeq ($(TARGET_NAME),mipsel) +DOCKER_IMAGE=debian-mipsel-cross +DOCKER_CROSS_COMPILER=mipsel-linux-gnu-gcc +else ifeq ($(TARGET_NAME),mips) +DOCKER_IMAGE=debian-mips-cross +DOCKER_CROSS_COMPILER=mips-linux-gnu-gcc +endif diff --git a/tests/tcg/mips/Makefile.target b/tests/tcg/mips/Makefile.target new file mode 100644 index 0000000000..086625f533 --- /dev/null +++ b/tests/tcg/mips/Makefile.target @@ -0,0 +1,22 @@ +# -*- Mode: makefile -*- +# +# MIPS - included from tests/tcg/Makefile.target +# + +MIPS_SRC=$(SRC_PATH)/tests/tcg/mips + +# Set search path for all sources +VPATH += $(MIPS_SRC) + +MIPS_TESTS=hello-mips + +TESTS += $(MIPS_TESTS) + +hello-mips: CFLAGS+=-ffreestanding +hello-mips: LDFLAGS+=-nostdlib + +# For MIPS32 and 64 we have a bunch of extra tests in sub-directories +# however they are intended for system tests. + +run-hello-mips: hello-mips + $(call skip-test, $<, "BROKEN") diff --git a/tests/tcg/mips/README b/tests/tcg/mips/README new file mode 100644 index 0000000000..e5bbc58ec5 --- /dev/null +++ b/tests/tcg/mips/README @@ -0,0 +1,7 @@ +MIPS +==== + +hello-mips +---------- + +A very simple inline assembly, write syscall based hello world diff --git a/tests/tcg/hello-mips.c b/tests/tcg/mips/hello-mips.c similarity index 100% rename from tests/tcg/hello-mips.c rename to tests/tcg/mips/hello-mips.c diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target new file mode 100644 index 0000000000..b77084c146 --- /dev/null +++ b/tests/tcg/multiarch/Makefile.target @@ -0,0 +1,36 @@ +# -*- Mode: makefile -*- +# +# Multiarch Tests - included from tests/tcg/Makefile.target +# +# These tests are plain C and built without any architecture specific code. +# + +MULTIARCH_SRC=$(SRC_PATH)/tests/tcg/multiarch + +# Set search path for all sources +VPATH += $(MULTIARCH_SRC) +MULTIARCH_SRCS =$(notdir $(wildcard $(MULTIARCH_SRC)/*.c)) +MULTIARCH_TESTS =$(MULTIARCH_SRCS:.c=) + +# Update TESTS +TESTS +=$(MULTIARCH_TESTS) + +# +# The following are any additional rules needed to build things +# + +testthread: LDFLAGS+=-lpthread + +# We define the runner for test-mmap after the individual +# architectures have defined their supported pages sizes. If no +# additional page sizes are defined we only run the default test. + +# default case (host page size) +run-test-mmap: test-mmap + $(call run-test, test-mmap, $(QEMU) $<, \ + "$< (default) on $(TARGET_NAME)") + +# additional page sizes (defined by each architecture adding to EXTRA_RUNS) +run-test-mmap-%: test-mmap + $(call run-test, test-mmap-$*, $(QEMU) -p $* $<,\ + "$< ($* byte pages) on $(TARGET_NAME)") diff --git a/tests/tcg/multiarch/README b/tests/tcg/multiarch/README new file mode 100644 index 0000000000..522c9d2ea3 --- /dev/null +++ b/tests/tcg/multiarch/README @@ -0,0 +1 @@ +Multi-architecture linux-user tests diff --git a/tests/tcg/linux-test.c b/tests/tcg/multiarch/linux-test.c similarity index 81% rename from tests/tcg/linux-test.c rename to tests/tcg/multiarch/linux-test.c index 5070d31446..e80eccc0ce 100644 --- a/tests/tcg/linux-test.c +++ b/tests/tcg/multiarch/linux-test.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -39,13 +40,11 @@ #include #include #include -#include "qemu/cutils.h" +#include -#define TESTPATH "/tmp/linux-test.tmp" -#define TESTPORT 7654 #define STACK_SIZE 16384 -void error1(const char *filename, int line, const char *fmt, ...) +static void error1(const char *filename, int line, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -56,11 +55,11 @@ void error1(const char *filename, int line, const char *fmt, ...) exit(1); } -int __chk_error(const char *filename, int line, int ret) +static int __chk_error(const char *filename, int line, int ret) { if (ret < 0) { - error1(filename, line, "%m (ret=%d, errno=%d)", - ret, errno); + error1(filename, line, "%m (ret=%d, errno=%d/%s)", + ret, errno, strerror(errno)); } return ret; } @@ -73,7 +72,7 @@ int __chk_error(const char *filename, int line, int ret) #define FILE_BUF_SIZE 300 -void test_file(void) +static void test_file(void) { int fd, i, len, ret; uint8_t buf[FILE_BUF_SIZE]; @@ -85,19 +84,16 @@ void test_file(void) struct iovec vecs[2]; DIR *dir; struct dirent *de; + /* TODO: make common tempdir creation for tcg tests */ + char template[] = "/tmp/linux-test-XXXXXX"; + char *tmpdir = mkdtemp(template); - /* clean up, just in case */ - unlink(TESTPATH "/file1"); - unlink(TESTPATH "/file2"); - unlink(TESTPATH "/file3"); - rmdir(TESTPATH); + assert(tmpdir); if (getcwd(cur_dir, sizeof(cur_dir)) == NULL) error("getcwd"); - chk_error(mkdir(TESTPATH, 0755)); - - chk_error(chdir(TESTPATH)); + chk_error(chdir(tmpdir)); /* open/read/write/close/readv/writev/lseek */ @@ -163,7 +159,7 @@ void test_file(void) st.st_mtime != 1000) error("stat time"); - chk_error(stat(TESTPATH, &st)); + chk_error(stat(tmpdir, &st)); if (!S_ISDIR(st.st_mode)) error("stat mode"); @@ -185,7 +181,7 @@ void test_file(void) error("stat mode"); /* getdents */ - dir = opendir(TESTPATH); + dir = opendir(tmpdir); if (!dir) error("opendir"); len = 0; @@ -207,16 +203,17 @@ void test_file(void) chk_error(unlink("file3")); chk_error(unlink("file2")); chk_error(chdir(cur_dir)); - chk_error(rmdir(TESTPATH)); + chk_error(rmdir(tmpdir)); } -void test_fork(void) +static void test_fork(void) { int pid, status; pid = chk_error(fork()); if (pid == 0) { /* child */ + sleep(2); exit(2); } chk_error(waitpid(pid, &status, 0)); @@ -224,7 +221,7 @@ void test_fork(void) error("waitpid status=0x%x", status); } -void test_time(void) +static void test_time(void) { struct timeval tv, tv2; struct timespec ts, rem; @@ -251,34 +248,7 @@ void test_time(void) error("getrusage"); } -void pstrcpy(char *buf, int buf_size, const char *str) -{ - int c; - char *q = buf; - - if (buf_size <= 0) - return; - - for(;;) { - c = *str++; - if (c == 0 || q >= buf + buf_size - 1) - break; - *q++ = c; - } - *q = '\0'; -} - -/* strcat and truncate. */ -char *pstrcat(char *buf, int buf_size, const char *s) -{ - int len; - len = strlen(buf); - if (len < buf_size) - pstrcpy(buf + len, buf_size - len, s); - return buf; -} - -int server_socket(void) +static int server_socket(void) { int val, fd; struct sockaddr_in sockaddr; @@ -290,7 +260,7 @@ int server_socket(void) chk_error(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))); sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(TESTPORT); + sockaddr.sin_port = htons(0); /* choose random ephemeral port) */ sockaddr.sin_addr.s_addr = 0; chk_error(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))); chk_error(listen(fd, 0)); @@ -298,7 +268,7 @@ int server_socket(void) } -int client_socket(void) +static int client_socket(uint16_t port) { int fd; struct sockaddr_in sockaddr; @@ -306,22 +276,29 @@ int client_socket(void) /* server socket */ fd = chk_error(socket(PF_INET, SOCK_STREAM, 0)); sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons(TESTPORT); + sockaddr.sin_port = htons(port); inet_aton("127.0.0.1", &sockaddr.sin_addr); chk_error(connect(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))); return fd; } -const char socket_msg[] = "hello socket\n"; +static const char socket_msg[] = "hello socket\n"; -void test_socket(void) +static void test_socket(void) { int server_fd, client_fd, fd, pid, ret, val; struct sockaddr_in sockaddr; - socklen_t len; + struct sockaddr_in server_addr; + socklen_t len, socklen; + uint16_t server_port; char buf[512]; server_fd = server_socket(); + /* find out what port we got */ + socklen = sizeof(server_addr); + ret = getsockname(server_fd, &server_addr, &socklen); + chk_error(ret); + server_port = ntohs(server_addr.sin_port); /* test a few socket options */ len = sizeof(val); @@ -331,7 +308,7 @@ void test_socket(void) pid = chk_error(fork()); if (pid == 0) { - client_fd = client_socket(); + client_fd = client_socket(server_port); send(client_fd, socket_msg, sizeof(socket_msg), 0); close(client_fd); exit(0); @@ -350,7 +327,7 @@ void test_socket(void) #define WCOUNT_MAX 512 -void test_pipe(void) +static void test_pipe(void) { fd_set rfds, wfds; int fds[2], fd_max, ret; @@ -382,7 +359,7 @@ void test_pipe(void) } if (FD_ISSET(fds[1], &wfds)) { ch = 'a'; - chk_error(write(fds[0], &ch, 1)); + chk_error(write(fds[1], &ch, 1)); wcount++; } } @@ -391,10 +368,10 @@ void test_pipe(void) chk_error(close(fds[1])); } -int thread1_res; -int thread2_res; +static int thread1_res; +static int thread2_res; -int thread1_func(void *arg) +static int thread1_func(void *arg) { int i; for(i=0;i<5;i++) { @@ -404,7 +381,7 @@ int thread1_func(void *arg) return 0; } -int thread2_func(void *arg) +static int thread2_func(void *arg) { int i; for(i=0;i<6;i++) { @@ -414,23 +391,37 @@ int thread2_func(void *arg) return 0; } -void test_clone(void) +static void wait_for_child(pid_t pid) +{ + int status; + chk_error(waitpid(pid, &status, 0)); +} + +/* For test_clone we must match the clone flags used by glibc, see + * CLONE_THREAD_FLAGS in the QEMU source code. + */ +static void test_clone(void) { uint8_t *stack1, *stack2; - int pid1, pid2, status1, status2; + pid_t pid1, pid2; stack1 = malloc(STACK_SIZE); pid1 = chk_error(clone(thread1_func, stack1 + STACK_SIZE, - CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1")); + CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM, + "hello1")); stack2 = malloc(STACK_SIZE); pid2 = chk_error(clone(thread2_func, stack2 + STACK_SIZE, - CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2")); + CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM, + "hello2")); - while (waitpid(pid1, &status1, 0) != pid1); + wait_for_child(pid1); free(stack1); - while (waitpid(pid2, &status2, 0) != pid2); + wait_for_child(pid2); free(stack2); + if (thread1_res != 5 || thread2_res != 6) error("clone"); @@ -441,21 +432,21 @@ void test_clone(void) volatile int alarm_count; jmp_buf jmp_env; -void sig_alarm(int sig) +static void sig_alarm(int sig) { if (sig != SIGALRM) error("signal"); alarm_count++; } -void sig_segv(int sig, siginfo_t *info, void *puc) +static void sig_segv(int sig, siginfo_t *info, void *puc) { if (sig != SIGSEGV) error("signal"); longjmp(jmp_env, 1); } -void test_signal(void) +static void test_signal(void) { struct sigaction act; struct itimerval it, oit; @@ -475,12 +466,10 @@ void test_signal(void) it.it_value.tv_usec = 10 * 1000; chk_error(setitimer(ITIMER_REAL, &it, NULL)); chk_error(getitimer(ITIMER_REAL, &oit)); - if (oit.it_value.tv_sec != it.it_value.tv_sec || - oit.it_value.tv_usec != it.it_value.tv_usec) - error("itimer"); while (alarm_count < 5) { usleep(10 * 1000); + getitimer(ITIMER_REAL, &oit); } it.it_interval.tv_sec = 0; @@ -489,9 +478,6 @@ void test_signal(void) it.it_value.tv_usec = 0; memset(&oit, 0xff, sizeof(oit)); chk_error(setitimer(ITIMER_REAL, &it, &oit)); - if (oit.it_value.tv_sec != 0 || - oit.it_value.tv_usec != 10 * 1000) - error("setitimer"); /* SIGSEGV test */ act.sa_sigaction = sig_segv; @@ -510,7 +496,7 @@ void test_signal(void) #define SHM_SIZE 32768 -void test_shm(void) +static void test_shm(void) { void *ptr; int shmid; @@ -529,10 +515,16 @@ void test_shm(void) int main(int argc, char **argv) { test_file(); + test_pipe(); test_fork(); test_time(); test_socket(); - // test_clone(); + + if (argc > 1) { + printf("test_clone still considered buggy\n"); + test_clone(); + } + test_signal(); test_shm(); return 0; diff --git a/tests/tcg/sha1.c b/tests/tcg/multiarch/sha1.c similarity index 100% rename from tests/tcg/sha1.c rename to tests/tcg/multiarch/sha1.c diff --git a/tests/tcg/test-mmap.c b/tests/tcg/multiarch/test-mmap.c similarity index 89% rename from tests/tcg/test-mmap.c rename to tests/tcg/multiarch/test-mmap.c index cdefadfa4c..11d0e777b1 100644 --- a/tests/tcg/test-mmap.c +++ b/tests/tcg/multiarch/test-mmap.c @@ -27,7 +27,7 @@ #include #include #include - +#include #include #define D(x) @@ -36,7 +36,7 @@ do \ { \ if (!(x)) { \ - fprintf (stderr, "FAILED at %s:%d\n", __FILE__, __LINE__); \ + fprintf(stderr, "FAILED at %s:%d\n", __FILE__, __LINE__); \ exit (EXIT_FAILURE); \ } \ } while (0) @@ -57,7 +57,7 @@ void check_aligned_anonymous_unfixed_mmaps(void) uintptr_t p; int i; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); for (i = 0; i < 0x1fff; i++) { size_t len; @@ -106,7 +106,7 @@ void check_aligned_anonymous_unfixed_mmaps(void) munmap (p4, len); munmap (p5, len); } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_large_anonymous_unfixed_mmap(void) @@ -115,7 +115,7 @@ void check_large_anonymous_unfixed_mmap(void) uintptr_t p; size_t len; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); len = 0x02000000; p1 = mmap(NULL, len, PROT_READ, @@ -130,7 +130,7 @@ void check_large_anonymous_unfixed_mmap(void) /* Make sure we can read from the entire area. */ memcpy (dummybuf, p1, pagesize); munmap (p1, len); - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_aligned_anonymous_unfixed_colliding_mmaps(void) @@ -141,7 +141,7 @@ void check_aligned_anonymous_unfixed_colliding_mmaps(void) uintptr_t p; int i; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); for (i = 0; i < 0x2fff; i++) { int nlen; @@ -180,7 +180,7 @@ void check_aligned_anonymous_unfixed_colliding_mmaps(void) munmap (p2, pagesize); munmap (p3, nlen); } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_aligned_anonymous_fixed_mmaps(void) @@ -194,7 +194,7 @@ void check_aligned_anonymous_fixed_mmaps(void) addr = mmap(NULL, pagesize * 40, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - fprintf (stderr, "%s addr=%p", __func__, addr); + fprintf(stdout, "%s addr=%p", __func__, addr); fail_unless (addr != MAP_FAILED); for (i = 0; i < 40; i++) @@ -212,7 +212,7 @@ void check_aligned_anonymous_fixed_mmaps(void) munmap (p1, pagesize); addr += pagesize; } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_aligned_anonymous_fixed_mmaps_collide_with_host(void) @@ -225,8 +225,8 @@ void check_aligned_anonymous_fixed_mmaps_collide_with_host(void) /* Find a suitable address to start with. Right were the x86 hosts stack is. */ addr = ((void *)0x80000000); - fprintf (stderr, "%s addr=%p", __func__, addr); - fprintf (stderr, "FIXME: QEMU fails to track pages used by the host."); + fprintf(stdout, "%s addr=%p", __func__, addr); + fprintf(stdout, "FIXME: QEMU fails to track pages used by the host."); for (i = 0; i < 20; i++) { @@ -243,7 +243,7 @@ void check_aligned_anonymous_fixed_mmaps_collide_with_host(void) munmap (p1, pagesize); addr += pagesize; } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_file_unfixed_mmaps(void) @@ -252,7 +252,7 @@ void check_file_unfixed_mmaps(void) uintptr_t p; int i; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); for (i = 0; i < 0x10; i++) { size_t len; @@ -294,7 +294,7 @@ void check_file_unfixed_mmaps(void) munmap (p2, len); munmap (p3, len); } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_file_unfixed_eof_mmaps(void) @@ -304,7 +304,7 @@ void check_file_unfixed_eof_mmaps(void) uintptr_t p; int i; - fprintf (stderr, "%s", __func__); + fprintf(stdout, "%s", __func__); for (i = 0; i < 0x10; i++) { p1 = mmap(NULL, pagesize, PROT_READ, @@ -327,7 +327,7 @@ void check_file_unfixed_eof_mmaps(void) fail_unless (cp[pagesize - 4] == 0); munmap (p1, pagesize); } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_file_fixed_eof_mmaps(void) @@ -343,7 +343,7 @@ void check_file_fixed_eof_mmaps(void) MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - fprintf (stderr, "%s addr=%p", __func__, (void *)addr); + fprintf(stdout, "%s addr=%p", __func__, (void *)addr); fail_unless (addr != MAP_FAILED); for (i = 0; i < 0x10; i++) @@ -371,7 +371,7 @@ void check_file_fixed_eof_mmaps(void) munmap (p1, pagesize); addr += pagesize; } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void check_file_fixed_mmaps(void) @@ -384,7 +384,7 @@ void check_file_fixed_mmaps(void) addr = mmap(NULL, pagesize * 40 * 4, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - fprintf (stderr, "%s addr=%p", __func__, (void *)addr); + fprintf(stdout, "%s addr=%p", __func__, (void *)addr); fail_unless (addr != MAP_FAILED); for (i = 0; i < 40; i++) @@ -426,7 +426,7 @@ void check_file_fixed_mmaps(void) munmap (p4, pagesize); addr += pagesize * 4; } - fprintf (stderr, " passed\n"); + fprintf(stdout, " passed\n"); } void checked_write(int fd, const void *buf, size_t count) @@ -435,6 +435,25 @@ void checked_write(int fd, const void *buf, size_t count) fail_unless(rc == count); } +void check_invalid_mmaps(void) +{ + unsigned char *addr; + + /* Attempt to map a zero length page. */ + addr = mmap(NULL, 0, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + fprintf(stdout, "%s addr=%p", __func__, (void *)addr); + fail_unless(addr == MAP_FAILED); + fail_unless(errno == EINVAL); + + /* Attempt to map a over length page. */ + addr = mmap(NULL, -4, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + fprintf(stdout, "%s addr=%p", __func__, (void *)addr); + fail_unless(addr == MAP_FAILED); + fail_unless(errno == ENOMEM); + + fprintf(stdout, " passed\n"); +} + int main(int argc, char **argv) { char tempname[] = "/tmp/.cmmapXXXXXX"; @@ -476,6 +495,7 @@ int main(int argc, char **argv) check_file_fixed_mmaps(); check_file_fixed_eof_mmaps(); check_file_unfixed_eof_mmaps(); + check_invalid_mmaps(); /* Fails at the moment. */ /* check_aligned_anonymous_fixed_mmaps_collide_with_host(); */ diff --git a/tests/tcg/testthread.c b/tests/tcg/multiarch/testthread.c similarity index 100% rename from tests/tcg/testthread.c rename to tests/tcg/multiarch/testthread.c diff --git a/tests/tcg/pi_10.com b/tests/tcg/pi_10.com deleted file mode 100644 index 8993ba1a51..0000000000 Binary files a/tests/tcg/pi_10.com and /dev/null differ diff --git a/tests/tcg/ppc/Makefile.include b/tests/tcg/ppc/Makefile.include new file mode 100644 index 0000000000..b062c30dd3 --- /dev/null +++ b/tests/tcg/ppc/Makefile.include @@ -0,0 +1,7 @@ +ifeq ($(TARGET_NAME),ppc) +DOCKER_IMAGE=debian-powerpc-cross +DOCKER_CROSS_COMPILER=powerpc-linux-gnu-gcc +else ifeq ($(TARGET_NAME),ppc64le) +DOCKER_IMAGE=debian-ppc64el-cross +DOCKER_CROSS_COMPILER=powerpc64le-linux-gnu-gcc +endif diff --git a/tests/tcg/ppc/Makefile.target b/tests/tcg/ppc/Makefile.target new file mode 100644 index 0000000000..f5e08c7376 --- /dev/null +++ b/tests/tcg/ppc/Makefile.target @@ -0,0 +1,12 @@ +# -*- Mode: makefile -*- +# +# PPC - included from tests/tcg/Makefile +# + +ifneq (,$(findstring 64,$(TARGET_NAME))) +# On PPC64 Linux can be configured with 4k (default) or 64k pages (currently broken) +EXTRA_RUNS+=run-test-mmap-4096 #run-test-mmap-65536 +else +# On PPC32 Linux supports 4K/16K/64K/256K (but currently only 4k works) +EXTRA_RUNS+=run-test-mmap-4096 #run-test-mmap-16384 run-test-mmap-65536 run-test-mmap-262144 +endif diff --git a/tests/tcg/riscv/Makefile.include b/tests/tcg/riscv/Makefile.include new file mode 100644 index 0000000000..d92ac6c89f --- /dev/null +++ b/tests/tcg/riscv/Makefile.include @@ -0,0 +1,10 @@ +# +# Makefile.include for all RISCV targets +# +# Debian only really cares about 64 bit going forward +# + +ifeq ($(TARGET_NAME),riscv64) +DOCKER_IMAGE=debian-riscv64-cross +DOCKER_CROSS_COMPILER=riscv64-linux-gnu-gcc +endif diff --git a/tests/tcg/runcom.c b/tests/tcg/runcom.c deleted file mode 100644 index d60342bfc6..0000000000 --- a/tests/tcg/runcom.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Simple example of use of vm86: launch a basic .com DOS executable - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -extern int vm86 (unsigned long int subfunction, - struct vm86plus_struct *info); - -#define VIF_MASK 0x00080000 - -//#define SIGTEST - -#define COM_BASE_ADDR 0x10100 - -static void usage(void) -{ - printf("runcom version 0.1 (c) 2003 Fabrice Bellard\n" - "usage: runcom file.com\n" - "VM86 Run simple .com DOS executables (linux vm86 test mode)\n"); - exit(1); -} - -static inline void set_bit(uint8_t *a, unsigned int bit) -{ - a[bit / 8] |= (1 << (bit % 8)); -} - -static inline uint8_t *seg_to_linear(unsigned int seg, unsigned int reg) -{ - return (uint8_t *)((seg << 4) + (reg & 0xffff)); -} - -static inline void pushw(struct vm86_regs *r, int val) -{ - r->esp = (r->esp & ~0xffff) | ((r->esp - 2) & 0xffff); - *(uint16_t *)seg_to_linear(r->ss, r->esp) = val; -} - -void dump_regs(struct vm86_regs *r) -{ - fprintf(stderr, - "EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n" - "ESI=%08lx EDI=%08lx EBP=%08lx ESP=%08lx\n" - "EIP=%08lx EFL=%08lx\n" - "CS=%04x DS=%04x ES=%04x SS=%04x FS=%04x GS=%04x\n", - r->eax, r->ebx, r->ecx, r->edx, r->esi, r->edi, r->ebp, r->esp, - r->eip, r->eflags, - r->cs, r->ds, r->es, r->ss, r->fs, r->gs); -} - -#ifdef SIGTEST -void alarm_handler(int sig) -{ - fprintf(stderr, "alarm signal=%d\n", sig); - alarm(1); -} -#endif - -int main(int argc, char **argv) -{ - uint8_t *vm86_mem; - const char *filename; - int fd, ret, seg; - struct vm86plus_struct ctx; - struct vm86_regs *r; - - if (argc != 2) - usage(); - filename = argv[1]; - - vm86_mem = mmap((void *)0x00000000, 0x110000, - PROT_WRITE | PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); - if (vm86_mem == MAP_FAILED) { - perror("mmap"); - exit(1); - } -#ifdef SIGTEST - { - struct sigaction act; - - act.sa_handler = alarm_handler; - sigemptyset(&act.sa_mask); - act.sa_flags = 0; - sigaction(SIGALRM, &act, NULL); - alarm(1); - } -#endif - - /* load the MSDOS .com executable */ - fd = open(filename, O_RDONLY); - if (fd < 0) { - perror(filename); - exit(1); - } - ret = read(fd, vm86_mem + COM_BASE_ADDR, 65536 - 256); - if (ret < 0) { - perror("read"); - exit(1); - } - close(fd); - - memset(&ctx, 0, sizeof(ctx)); - /* init basic registers */ - r = &ctx.regs; - r->eip = 0x100; - r->esp = 0xfffe; - seg = (COM_BASE_ADDR - 0x100) >> 4; - r->cs = seg; - r->ss = seg; - r->ds = seg; - r->es = seg; - r->fs = seg; - r->gs = seg; - r->eflags = VIF_MASK; - - /* put return code */ - set_bit((uint8_t *)&ctx.int_revectored, 0x21); - *seg_to_linear(r->cs, 0) = 0xb4; /* mov ah, $0 */ - *seg_to_linear(r->cs, 1) = 0x00; - *seg_to_linear(r->cs, 2) = 0xcd; /* int $0x21 */ - *seg_to_linear(r->cs, 3) = 0x21; - pushw(&ctx.regs, 0x0000); - - /* the value of these registers seem to be assumed by pi_10.com */ - r->esi = 0x100; - r->ecx = 0xff; - r->ebp = 0x0900; - r->edi = 0xfffe; - - for(;;) { - ret = vm86(VM86_ENTER, &ctx); - switch(VM86_TYPE(ret)) { - case VM86_INTx: - { - int int_num, ah; - - int_num = VM86_ARG(ret); - if (int_num != 0x21) - goto unknown_int; - ah = (r->eax >> 8) & 0xff; - switch(ah) { - case 0x00: /* exit */ - exit(0); - case 0x02: /* write char */ - { - uint8_t c = r->edx; - write(1, &c, 1); - } - break; - case 0x09: /* write string */ - { - uint8_t c; - for(;;) { - c = *seg_to_linear(r->ds, r->edx); - if (c == '$') - break; - write(1, &c, 1); - } - r->eax = (r->eax & ~0xff) | '$'; - } - break; - default: - unknown_int: - fprintf(stderr, "unsupported int 0x%02x\n", int_num); - dump_regs(&ctx.regs); - // exit(1); - } - } - break; - case VM86_SIGNAL: - /* a signal came, we just ignore that */ - break; - case VM86_STI: - break; - default: - fprintf(stderr, "unhandled vm86 return code (0x%x)\n", ret); - dump_regs(&ctx.regs); - exit(1); - } - } -} diff --git a/tests/tcg/s390x/Makefile.include b/tests/tcg/s390x/Makefile.include new file mode 100644 index 0000000000..1f58115d96 --- /dev/null +++ b/tests/tcg/s390x/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-s390x-cross +DOCKER_CROSS_COMPILER=s390x-linux-gnu-gcc diff --git a/tests/tcg/sh4/Makefile.include b/tests/tcg/sh4/Makefile.include new file mode 100644 index 0000000000..ad21594d9d --- /dev/null +++ b/tests/tcg/sh4/Makefile.include @@ -0,0 +1,4 @@ +ifneq ($(TARGET_NAME), sh4eb) +DOCKER_IMAGE=debian-sh4-cross +DOCKER_CROSS_COMPILER=sh4-linux-gnu-gcc +endif diff --git a/tests/tcg/sh4/Makefile.target b/tests/tcg/sh4/Makefile.target new file mode 100644 index 0000000000..9d18d44612 --- /dev/null +++ b/tests/tcg/sh4/Makefile.target @@ -0,0 +1,7 @@ +# -*- Mode: makefile -*- +# +# SuperH specific tweaks +# + +# On sh Linux supports 4k, 8k, 16k and 64k pages (but only 4k currently works) +EXTRA_RUNS+=run-test-mmap-4096 # run-test-mmap-8192 run-test-mmap-16384 run-test-mmap-65536 diff --git a/tests/tcg/sparc64/Makefile.include b/tests/tcg/sparc64/Makefile.include new file mode 100644 index 0000000000..95fc8dee9f --- /dev/null +++ b/tests/tcg/sparc64/Makefile.include @@ -0,0 +1,2 @@ +DOCKER_IMAGE=debian-sparc64-cross +DOCKER_CROSS_COMPILER=sparc64-linux-gnu-gcc diff --git a/tests/tcg/sparc64/Makefile.target b/tests/tcg/sparc64/Makefile.target new file mode 100644 index 0000000000..5bd7f90583 --- /dev/null +++ b/tests/tcg/sparc64/Makefile.target @@ -0,0 +1,11 @@ +# -*- Mode: makefile -*- +# +# sparc specific tweaks and masking out broken tests + +# different from the other hangs: +# tests/tcg/multiarch/linux-test.c:264: Value too large for defined data type (ret=-1, errno=92/Value too large for defined data type) +run-linux-test: linux-test + $(call skip-test, $<, "BROKEN") + +# On Sparc64 Linux support 8k pages +EXTRA_RUNS+=run-test-mmap-8192 diff --git a/tests/tcg/test_path.c b/tests/tcg/test_path.c deleted file mode 100644 index 1c29bce263..0000000000 --- a/tests/tcg/test_path.c +++ /dev/null @@ -1,157 +0,0 @@ -/* Test path override code */ -#include "config-host.h" -#include "util/cutils.c" -#include "util/hexdump.c" -#include "util/iov.c" -#include "util/path.c" -#include "util/qemu-timer-common.c" -#include -#include -#include - -void qemu_log(const char *fmt, ...); - -/* Any log message kills the test. */ -void qemu_log(const char *fmt, ...) -{ - va_list ap; - - fprintf(stderr, "FATAL: "); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - exit(1); -} - -#define NO_CHANGE(_path) \ - do { \ - if (strcmp(path(_path), _path) != 0) return __LINE__; \ - } while(0) - -#define CHANGE_TO(_path, _newpath) \ - do { \ - if (strcmp(path(_path), _newpath) != 0) return __LINE__; \ - } while(0) - -static void cleanup(void) -{ - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE"); - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE2"); - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE3"); - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE4"); - unlink("/tmp/qemu-test_path/DIR1/DIR2/FILE5"); - rmdir("/tmp/qemu-test_path/DIR1/DIR2"); - rmdir("/tmp/qemu-test_path/DIR1/DIR3"); - rmdir("/tmp/qemu-test_path/DIR1"); - rmdir("/tmp/qemu-test_path"); -} - -static unsigned int do_test(void) -{ - if (mkdir("/tmp/qemu-test_path", 0700) != 0) - return __LINE__; - - if (mkdir("/tmp/qemu-test_path/DIR1", 0700) != 0) - return __LINE__; - - if (mkdir("/tmp/qemu-test_path/DIR1/DIR2", 0700) != 0) - return __LINE__; - - if (mkdir("/tmp/qemu-test_path/DIR1/DIR3", 0700) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE", 0600)) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE2", 0600)) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE3", 0600)) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE4", 0600)) != 0) - return __LINE__; - - if (close(creat("/tmp/qemu-test_path/DIR1/DIR2/FILE5", 0600)) != 0) - return __LINE__; - - init_paths("/tmp/qemu-test_path"); - - NO_CHANGE("/tmp"); - NO_CHANGE("/tmp/"); - NO_CHANGE("/tmp/qemu-test_path"); - NO_CHANGE("/tmp/qemu-test_path/"); - NO_CHANGE("/tmp/qemu-test_path/D"); - NO_CHANGE("/tmp/qemu-test_path/DI"); - NO_CHANGE("/tmp/qemu-test_path/DIR"); - NO_CHANGE("/tmp/qemu-test_path/DIR1"); - NO_CHANGE("/tmp/qemu-test_path/DIR1/"); - - NO_CHANGE("/D"); - NO_CHANGE("/DI"); - NO_CHANGE("/DIR"); - NO_CHANGE("/DIR2"); - NO_CHANGE("/DIR1."); - - CHANGE_TO("/DIR1", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/DIR1/", "/tmp/qemu-test_path/DIR1"); - - NO_CHANGE("/DIR1/D"); - NO_CHANGE("/DIR1/DI"); - NO_CHANGE("/DIR1/DIR"); - NO_CHANGE("/DIR1/DIR1"); - - CHANGE_TO("/DIR1/DIR2", "/tmp/qemu-test_path/DIR1/DIR2"); - CHANGE_TO("/DIR1/DIR2/", "/tmp/qemu-test_path/DIR1/DIR2"); - - CHANGE_TO("/DIR1/DIR3", "/tmp/qemu-test_path/DIR1/DIR3"); - CHANGE_TO("/DIR1/DIR3/", "/tmp/qemu-test_path/DIR1/DIR3"); - - NO_CHANGE("/DIR1/DIR2/F"); - NO_CHANGE("/DIR1/DIR2/FI"); - NO_CHANGE("/DIR1/DIR2/FIL"); - NO_CHANGE("/DIR1/DIR2/FIL."); - - CHANGE_TO("/DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/DIR2/FILE2", "/tmp/qemu-test_path/DIR1/DIR2/FILE2"); - CHANGE_TO("/DIR1/DIR2/FILE3", "/tmp/qemu-test_path/DIR1/DIR2/FILE3"); - CHANGE_TO("/DIR1/DIR2/FILE4", "/tmp/qemu-test_path/DIR1/DIR2/FILE4"); - CHANGE_TO("/DIR1/DIR2/FILE5", "/tmp/qemu-test_path/DIR1/DIR2/FILE5"); - - NO_CHANGE("/DIR1/DIR2/FILE6"); - NO_CHANGE("/DIR1/DIR2/FILE/X"); - - CHANGE_TO("/DIR1/../DIR1", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/DIR1/../DIR1/", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/../DIR1", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/../DIR1/", "/tmp/qemu-test_path/DIR1"); - CHANGE_TO("/DIR1/DIR2/../DIR2", "/tmp/qemu-test_path/DIR1/DIR2"); - CHANGE_TO("/DIR1/DIR2/../DIR2/../../DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/DIR2/../DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - - NO_CHANGE("/DIR1/DIR2/../DIR1"); - NO_CHANGE("/DIR1/DIR2/../FILE"); - - CHANGE_TO("/./DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/././DIR1/DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/./DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/././DIR2/FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/DIR1/DIR2/././FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - CHANGE_TO("/./DIR1/./DIR2/./FILE", "/tmp/qemu-test_path/DIR1/DIR2/FILE"); - - return 0; -} - -int main(int argc, char *argv[]) -{ - int ret; - - ret = do_test(); - cleanup(); - if (ret) { - fprintf(stderr, "test_path: failed on line %i\n", ret); - return 1; - } - return 0; -} diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target new file mode 100644 index 0000000000..74f170b9ed --- /dev/null +++ b/tests/tcg/x86_64/Makefile.target @@ -0,0 +1,15 @@ +# -*- Mode: makefile -*- +# +# x86_64 tests - included from tests/tcg/Makefile.target +# +# Currently we only build test-x86_64 and test-i386-ssse3 from +# $(SRC)/tests/tcg/i386/ +# + +X86_64_TESTS=$(filter-out $(I386_ONLY_TESTS), $(TESTS)) +X86_64_TESTS+=test-x86_64 +TESTS:=$(X86_64_TESTS) + +test-x86_64: LDFLAGS+=-lm -lc +test-x86_64: test-i386.c test-i386.h test-i386-shift.h test-i386-muldiv.h + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) diff --git a/tests/tco-test.c b/tests/tco-test.c index aee17af3c1..9945fb8469 100644 --- a/tests/tco-test.c +++ b/tests/tco-test.c @@ -241,8 +241,8 @@ static QDict *get_watchdog_action(void) QDict *data; data = qdict_get_qdict(ev, "data"); - QINCREF(data); - QDECREF(ev); + qobject_ref(data); + qobject_unref(ev); return data; } @@ -265,7 +265,7 @@ static void test_tco_second_timeout_pause(void) clock_step(ticks * TCO_TICK_NSEC * 2); ad = get_watchdog_action(); g_assert(!strcmp(qdict_get_str(ad, "action"), "pause")); - QDECREF(ad); + qobject_unref(ad); stop_tco(&td); test_end(&td); @@ -290,7 +290,7 @@ static void test_tco_second_timeout_reset(void) clock_step(ticks * TCO_TICK_NSEC * 2); ad = get_watchdog_action(); g_assert(!strcmp(qdict_get_str(ad, "action"), "reset")); - QDECREF(ad); + qobject_unref(ad); stop_tco(&td); test_end(&td); @@ -315,7 +315,7 @@ static void test_tco_second_timeout_shutdown(void) clock_step(ticks * TCO_TICK_NSEC * 2); ad = get_watchdog_action(); g_assert(!strcmp(qdict_get_str(ad, "action"), "shutdown")); - QDECREF(ad); + qobject_unref(ad); stop_tco(&td); test_end(&td); @@ -340,7 +340,7 @@ static void test_tco_second_timeout_none(void) clock_step(ticks * TCO_TICK_NSEC * 2); ad = get_watchdog_action(); g_assert(!strcmp(qdict_get_str(ad, "action"), "none")); - QDECREF(ad); + qobject_unref(ad); stop_tco(&td); test_end(&td); diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 7673de1062..17bb8508ae 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -27,15 +27,23 @@ #include "block/blockjob_int.h" #include "sysemu/block-backend.h" #include "qapi/error.h" +#include "iothread.h" + +static QemuEvent done_event; typedef struct BDRVTestState { int drain_count; + AioContext *bh_indirection_ctx; + bool sleep_in_drain_begin; } BDRVTestState; static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs) { BDRVTestState *s = bs->opaque; s->drain_count++; + if (s->sleep_in_drain_begin) { + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + } } static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs) @@ -50,19 +58,48 @@ static void bdrv_test_close(BlockDriverState *bs) g_assert_cmpint(s->drain_count, >, 0); } +static void co_reenter_bh(void *opaque) +{ + aio_co_wake(opaque); +} + static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { + BDRVTestState *s = bs->opaque; + /* We want this request to stay until the polling loop in drain waits for * it to complete. We need to sleep a while as bdrv_drain_invoke() comes * first and polls its result, too, but it shouldn't accidentally complete * this request yet. */ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + if (s->bh_indirection_ctx) { + aio_bh_schedule_oneshot(s->bh_indirection_ctx, co_reenter_bh, + qemu_coroutine_self()); + qemu_coroutine_yield(); + } + return 0; } +static void bdrv_test_child_perm(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + /* bdrv_format_default_perms() accepts only these two, so disguise + * detach_by_driver_cb_role as one of them. */ + if (role != &child_file && role != &child_backing) { + role = &child_file; + } + + bdrv_format_default_perms(bs, c, role, reopen_queue, perm, shared, + nperm, nshared); +} + static BlockDriver bdrv_test = { .format_name = "test", .instance_size = sizeof(BDRVTestState), @@ -73,7 +110,7 @@ static BlockDriver bdrv_test = { .bdrv_co_drain_begin = bdrv_test_co_drain_begin, .bdrv_co_drain_end = bdrv_test_co_drain_end, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_test_child_perm, }; static void aio_ret_cb(void *opaque, int ret) @@ -216,6 +253,11 @@ static void test_drv_cb_drain_subtree(void) test_drv_cb_common(BDRV_SUBTREE_DRAIN, true); } +static void test_drv_cb_co_drain_all(void) +{ + call_in_coroutine(test_drv_cb_drain_all); +} + static void test_drv_cb_co_drain(void) { call_in_coroutine(test_drv_cb_drain); @@ -259,8 +301,7 @@ static void test_quiesce_common(enum drain_type drain_type, bool recursive) static void test_quiesce_drain_all(void) { - // XXX drain_all doesn't quiesce - //test_quiesce_common(BDRV_DRAIN_ALL, true); + test_quiesce_common(BDRV_DRAIN_ALL, true); } static void test_quiesce_drain(void) @@ -273,6 +314,11 @@ static void test_quiesce_drain_subtree(void) test_quiesce_common(BDRV_SUBTREE_DRAIN, true); } +static void test_quiesce_co_drain_all(void) +{ + call_in_coroutine(test_quiesce_drain_all); +} + static void test_quiesce_co_drain(void) { call_in_coroutine(test_quiesce_drain); @@ -302,12 +348,7 @@ static void test_nested(void) for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) { for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) { - /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */ - int bs_quiesce = (outer != BDRV_DRAIN_ALL) + - (inner != BDRV_DRAIN_ALL); - int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) + - (inner == BDRV_SUBTREE_DRAIN); - int backing_cb_cnt = (outer != BDRV_DRAIN) + + int backing_quiesce = (outer != BDRV_DRAIN) + (inner != BDRV_DRAIN); g_assert_cmpint(bs->quiesce_counter, ==, 0); @@ -318,10 +359,10 @@ static void test_nested(void) do_drain_begin(outer, bs); do_drain_begin(inner, bs); - g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce); + g_assert_cmpint(bs->quiesce_counter, ==, 2); g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce); g_assert_cmpint(s->drain_count, ==, 2); - g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt); + g_assert_cmpint(backing_s->drain_count, ==, backing_quiesce); do_drain_end(inner, bs); do_drain_end(outer, bs); @@ -411,7 +452,7 @@ static void test_multiparent(void) blk_unref(blk_b); } -static void test_graph_change(void) +static void test_graph_change_drain_subtree(void) { BlockBackend *blk_a, *blk_b; BlockDriverState *bs_a, *bs_b, *backing; @@ -490,39 +531,263 @@ static void test_graph_change(void) blk_unref(blk_b); } +static void test_graph_change_drain_all(void) +{ + BlockBackend *blk_a, *blk_b; + BlockDriverState *bs_a, *bs_b; + BDRVTestState *a_s, *b_s; + + /* Create node A with a BlockBackend */ + blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR, + &error_abort); + a_s = bs_a->opaque; + blk_insert_bs(blk_a, bs_a, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 0); + g_assert_cmpint(a_s->drain_count, ==, 0); + + /* Call bdrv_drain_all_begin() */ + bdrv_drain_all_begin(); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + + /* Create node B with a BlockBackend */ + blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR, + &error_abort); + b_s = bs_b->opaque; + blk_insert_bs(blk_b, bs_b, &error_abort); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + + /* Unref and finally delete node A */ + blk_unref(blk_a); + + g_assert_cmpint(bs_a->quiesce_counter, ==, 1); + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(a_s->drain_count, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + + bdrv_unref(bs_a); + + g_assert_cmpint(bs_b->quiesce_counter, ==, 1); + g_assert_cmpint(b_s->drain_count, ==, 1); + + /* End the drained section */ + bdrv_drain_all_end(); + + g_assert_cmpint(bs_b->quiesce_counter, ==, 0); + g_assert_cmpint(b_s->drain_count, ==, 0); + + bdrv_unref(bs_b); + blk_unref(blk_b); +} + +struct test_iothread_data { + BlockDriverState *bs; + enum drain_type drain_type; + int *aio_ret; +}; + +static void test_iothread_drain_entry(void *opaque) +{ + struct test_iothread_data *data = opaque; + + aio_context_acquire(bdrv_get_aio_context(data->bs)); + do_drain_begin(data->drain_type, data->bs); + g_assert_cmpint(*data->aio_ret, ==, 0); + do_drain_end(data->drain_type, data->bs); + aio_context_release(bdrv_get_aio_context(data->bs)); + + qemu_event_set(&done_event); +} + +static void test_iothread_aio_cb(void *opaque, int ret) +{ + int *aio_ret = opaque; + *aio_ret = ret; + qemu_event_set(&done_event); +} + +/* + * Starts an AIO request on a BDS that runs in the AioContext of iothread 1. + * The request involves a BH on iothread 2 before it can complete. + * + * @drain_thread = 0 means that do_drain_begin/end are called from the main + * thread, @drain_thread = 1 means that they are called from iothread 1. Drain + * for this BDS cannot be called from iothread 2 because only the main thread + * may do cross-AioContext polling. + */ +static void test_iothread_common(enum drain_type drain_type, int drain_thread) +{ + BlockBackend *blk; + BlockDriverState *bs; + BDRVTestState *s; + BlockAIOCB *acb; + int aio_ret; + struct test_iothread_data data; + + IOThread *a = iothread_new(); + IOThread *b = iothread_new(); + AioContext *ctx_a = iothread_get_aio_context(a); + AioContext *ctx_b = iothread_get_aio_context(b); + + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = NULL, + .iov_len = 0, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + /* bdrv_drain_all() may only be called from the main loop thread */ + if (drain_type == BDRV_DRAIN_ALL && drain_thread != 0) { + goto out; + } + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + s = bs->opaque; + blk_insert_bs(blk, bs, &error_abort); + + blk_set_aio_context(blk, ctx_a); + aio_context_acquire(ctx_a); + + s->bh_indirection_ctx = ctx_b; + + aio_ret = -EINPROGRESS; + if (drain_thread == 0) { + acb = blk_aio_preadv(blk, 0, &qiov, 0, test_iothread_aio_cb, &aio_ret); + } else { + acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret); + } + g_assert(acb != NULL); + g_assert_cmpint(aio_ret, ==, -EINPROGRESS); + + aio_context_release(ctx_a); + + data = (struct test_iothread_data) { + .bs = bs, + .drain_type = drain_type, + .aio_ret = &aio_ret, + }; + + switch (drain_thread) { + case 0: + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(ctx_a); + } + + /* The request is running on the IOThread a. Draining its block device + * will make sure that it has completed as far as the BDS is concerned, + * but the drain in this thread can continue immediately after + * bdrv_dec_in_flight() and aio_ret might be assigned only slightly + * later. */ + qemu_event_reset(&done_event); + do_drain_begin(drain_type, bs); + g_assert_cmpint(bs->in_flight, ==, 0); + + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(ctx_a); + } + qemu_event_wait(&done_event); + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(ctx_a); + } + + g_assert_cmpint(aio_ret, ==, 0); + do_drain_end(drain_type, bs); + + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(ctx_a); + } + break; + case 1: + qemu_event_reset(&done_event); + aio_bh_schedule_oneshot(ctx_a, test_iothread_drain_entry, &data); + qemu_event_wait(&done_event); + break; + default: + g_assert_not_reached(); + } + + aio_context_acquire(ctx_a); + blk_set_aio_context(blk, qemu_get_aio_context()); + aio_context_release(ctx_a); + + bdrv_unref(bs); + blk_unref(blk); + +out: + iothread_join(a); + iothread_join(b); +} + +static void test_iothread_drain_all(void) +{ + test_iothread_common(BDRV_DRAIN_ALL, 0); + test_iothread_common(BDRV_DRAIN_ALL, 1); +} + +static void test_iothread_drain(void) +{ + test_iothread_common(BDRV_DRAIN, 0); + test_iothread_common(BDRV_DRAIN, 1); +} + +static void test_iothread_drain_subtree(void) +{ + test_iothread_common(BDRV_SUBTREE_DRAIN, 0); + test_iothread_common(BDRV_SUBTREE_DRAIN, 1); +} + typedef struct TestBlockJob { BlockJob common; bool should_complete; } TestBlockJob; -static void test_job_completed(BlockJob *job, void *opaque) +static void test_job_completed(Job *job, void *opaque) { - block_job_completed(job, 0); + job_completed(job, 0, NULL); } static void coroutine_fn test_job_start(void *opaque) { TestBlockJob *s = opaque; - block_job_event_ready(&s->common); + job_transition_to_ready(&s->common.job); while (!s->should_complete) { - block_job_sleep_ns(&s->common, 100000); + /* Avoid block_job_sleep_ns() because it marks the job as !busy. We + * want to emulate some actual activity (probably some I/O) here so + * that drain has to wait for this acitivity to stop. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + job_pause_point(&s->common.job); } - block_job_defer_to_main_loop(&s->common, test_job_completed, NULL); + job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); } -static void test_job_complete(BlockJob *job, Error **errp) +static void test_job_complete(Job *job, Error **errp) { - TestBlockJob *s = container_of(job, TestBlockJob, common); + TestBlockJob *s = container_of(job, TestBlockJob, common.job); s->should_complete = true; } BlockJobDriver test_job_driver = { - .instance_size = sizeof(TestBlockJob), - .start = test_job_start, - .complete = test_job_complete, + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = test_job_start, + .complete = test_job_complete, + }, }; static void test_blockjob_common(enum drain_type drain_type) @@ -545,49 +810,47 @@ static void test_blockjob_common(enum drain_type drain_type) job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); - block_job_start(job); + job_start(&job->job); - g_assert_cmpint(job->pause_count, ==, 0); - g_assert_false(job->paused); - g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ do_drain_begin(drain_type, src); if (drain_type == BDRV_DRAIN_ALL) { /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->pause_count, ==, 2); + g_assert_cmpint(job->job.pause_count, ==, 2); } else { - g_assert_cmpint(job->pause_count, ==, 1); + g_assert_cmpint(job->job.pause_count, ==, 1); } - /* XXX We don't wait until the job is actually paused. Is this okay? */ - /* g_assert_true(job->paused); */ - g_assert_false(job->busy); /* The job is paused */ + g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ do_drain_end(drain_type, src); - g_assert_cmpint(job->pause_count, ==, 0); - g_assert_false(job->paused); - g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ do_drain_begin(drain_type, target); if (drain_type == BDRV_DRAIN_ALL) { /* bdrv_drain_all() drains both src and target */ - g_assert_cmpint(job->pause_count, ==, 2); + g_assert_cmpint(job->job.pause_count, ==, 2); } else { - g_assert_cmpint(job->pause_count, ==, 1); + g_assert_cmpint(job->job.pause_count, ==, 1); } - /* XXX We don't wait until the job is actually paused. Is this okay? */ - /* g_assert_true(job->paused); */ - g_assert_false(job->busy); /* The job is paused */ + g_assert_true(job->job.paused); + g_assert_false(job->job.busy); /* The job is paused */ do_drain_end(drain_type, target); - g_assert_cmpint(job->pause_count, ==, 0); - g_assert_false(job->paused); - g_assert_false(job->busy); /* We're in block_job_sleep_ns() */ + g_assert_cmpint(job->job.pause_count, ==, 0); + g_assert_false(job->job.paused); + g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ - ret = block_job_complete_sync(job, &error_abort); + ret = job_complete_sync(&job->job, &error_abort); g_assert_cmpint(ret, ==, 0); blk_unref(blk_src); @@ -611,19 +874,440 @@ static void test_blockjob_drain_subtree(void) test_blockjob_common(BDRV_SUBTREE_DRAIN); } + +typedef struct BDRVTestTopState { + BdrvChild *wait_child; +} BDRVTestTopState; + +static void bdrv_test_top_close(BlockDriverState *bs) +{ + BdrvChild *c, *next_c; + QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { + bdrv_unref_child(bs, c); + } +} + +static int coroutine_fn bdrv_test_top_co_preadv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + BDRVTestTopState *tts = bs->opaque; + return bdrv_co_preadv(tts->wait_child, offset, bytes, qiov, flags); +} + +static BlockDriver bdrv_test_top_driver = { + .format_name = "test_top_driver", + .instance_size = sizeof(BDRVTestTopState), + + .bdrv_close = bdrv_test_top_close, + .bdrv_co_preadv = bdrv_test_top_co_preadv, + + .bdrv_child_perm = bdrv_format_default_perms, +}; + +typedef struct TestCoDeleteByDrainData { + BlockBackend *blk; + bool detach_instead_of_delete; + bool done; +} TestCoDeleteByDrainData; + +static void coroutine_fn test_co_delete_by_drain(void *opaque) +{ + TestCoDeleteByDrainData *dbdd = opaque; + BlockBackend *blk = dbdd->blk; + BlockDriverState *bs = blk_bs(blk); + BDRVTestTopState *tts = bs->opaque; + void *buffer = g_malloc(65536); + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = buffer, + .iov_len = 65536, + }; + + qemu_iovec_init_external(&qiov, &iov, 1); + + /* Pretend some internal write operation from parent to child. + * Important: We have to read from the child, not from the parent! + * Draining works by first propagating it all up the tree to the + * root and then waiting for drainage from root to the leaves + * (protocol nodes). If we have a request waiting on the root, + * everything will be drained before we go back down the tree, but + * we do not want that. We want to be in the middle of draining + * when this following requests returns. */ + bdrv_co_preadv(tts->wait_child, 0, 65536, &qiov, 0); + + g_assert_cmpint(bs->refcnt, ==, 1); + + if (!dbdd->detach_instead_of_delete) { + blk_unref(blk); + } else { + BdrvChild *c, *next_c; + QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { + bdrv_unref_child(bs, c); + } + } + + dbdd->done = true; +} + +/** + * Test what happens when some BDS has some children, you drain one of + * them and this results in the BDS being deleted. + * + * If @detach_instead_of_delete is set, the BDS is not going to be + * deleted but will only detach all of its children. + */ +static void do_test_delete_by_drain(bool detach_instead_of_delete, + enum drain_type drain_type) +{ + BlockBackend *blk; + BlockDriverState *bs, *child_bs, *null_bs; + BDRVTestTopState *tts; + TestCoDeleteByDrainData dbdd; + Coroutine *co; + + bs = bdrv_new_open_driver(&bdrv_test_top_driver, "top", BDRV_O_RDWR, + &error_abort); + bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; + tts = bs->opaque; + + null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort); + + /* This child will be the one to pass to requests through to, and + * it will stall until a drain occurs */ + child_bs = bdrv_new_open_driver(&bdrv_test, "child", BDRV_O_RDWR, + &error_abort); + child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; + /* Takes our reference to child_bs */ + tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", &child_file, + &error_abort); + + /* This child is just there to be deleted + * (for detach_instead_of_delete == true) */ + null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, + &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort); + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk, bs, &error_abort); + + /* Referenced by blk now */ + bdrv_unref(bs); + + g_assert_cmpint(bs->refcnt, ==, 1); + g_assert_cmpint(child_bs->refcnt, ==, 1); + g_assert_cmpint(null_bs->refcnt, ==, 1); + + + dbdd = (TestCoDeleteByDrainData){ + .blk = blk, + .detach_instead_of_delete = detach_instead_of_delete, + .done = false, + }; + co = qemu_coroutine_create(test_co_delete_by_drain, &dbdd); + qemu_coroutine_enter(co); + + /* Drain the child while the read operation is still pending. + * This should result in the operation finishing and + * test_co_delete_by_drain() resuming. Thus, @bs will be deleted + * and the coroutine will exit while this drain operation is still + * in progress. */ + switch (drain_type) { + case BDRV_DRAIN: + bdrv_ref(child_bs); + bdrv_drain(child_bs); + bdrv_unref(child_bs); + break; + case BDRV_SUBTREE_DRAIN: + /* Would have to ref/unref bs here for !detach_instead_of_delete, but + * then the whole test becomes pointless because the graph changes + * don't occur during the drain any more. */ + assert(detach_instead_of_delete); + bdrv_subtree_drained_begin(bs); + bdrv_subtree_drained_end(bs); + break; + case BDRV_DRAIN_ALL: + bdrv_drain_all_begin(); + bdrv_drain_all_end(); + break; + default: + g_assert_not_reached(); + } + + while (!dbdd.done) { + aio_poll(qemu_get_aio_context(), true); + } + + if (detach_instead_of_delete) { + /* Here, the reference has not passed over to the coroutine, + * so we have to delete the BB ourselves */ + blk_unref(blk); + } +} + +static void test_delete_by_drain(void) +{ + do_test_delete_by_drain(false, BDRV_DRAIN); +} + +static void test_detach_by_drain_all(void) +{ + do_test_delete_by_drain(true, BDRV_DRAIN_ALL); +} + +static void test_detach_by_drain(void) +{ + do_test_delete_by_drain(true, BDRV_DRAIN); +} + +static void test_detach_by_drain_subtree(void) +{ + do_test_delete_by_drain(true, BDRV_SUBTREE_DRAIN); +} + + +struct detach_by_parent_data { + BlockDriverState *parent_b; + BdrvChild *child_b; + BlockDriverState *c; + BdrvChild *child_c; + bool by_parent_cb; +}; +static struct detach_by_parent_data detach_by_parent_data; + +static void detach_indirect_bh(void *opaque) +{ + struct detach_by_parent_data *data = opaque; + + bdrv_unref_child(data->parent_b, data->child_b); + + bdrv_ref(data->c); + data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", + &child_file, &error_abort); +} + +static void detach_by_parent_aio_cb(void *opaque, int ret) +{ + struct detach_by_parent_data *data = &detach_by_parent_data; + + g_assert_cmpint(ret, ==, 0); + if (data->by_parent_cb) { + detach_indirect_bh(data); + } +} + +static void detach_by_driver_cb_drained_begin(BdrvChild *child) +{ + aio_bh_schedule_oneshot(qemu_get_current_aio_context(), + detach_indirect_bh, &detach_by_parent_data); + child_file.drained_begin(child); +} + +static BdrvChildRole detach_by_driver_cb_role; + +/* + * Initial graph: + * + * PA PB + * \ / \ + * A B C + * + * by_parent_cb == true: Test that parent callbacks don't poll + * + * PA has a pending write request whose callback changes the child nodes of + * PB: It removes B and adds C instead. The subtree of PB is drained, which + * will indirectly drain the write request, too. + * + * by_parent_cb == false: Test that bdrv_drain_invoke() doesn't poll + * + * PA's BdrvChildRole has a .drained_begin callback that schedules a BH + * that does the same graph change. If bdrv_drain_invoke() calls it, the + * state is messed up, but if it is only polled in the single + * BDRV_POLL_WHILE() at the end of the drain, this should work fine. + */ +static void test_detach_indirect(bool by_parent_cb) +{ + BlockBackend *blk; + BlockDriverState *parent_a, *parent_b, *a, *b, *c; + BdrvChild *child_a, *child_b; + BlockAIOCB *acb; + + QEMUIOVector qiov; + struct iovec iov = { + .iov_base = NULL, + .iov_len = 0, + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + if (!by_parent_cb) { + detach_by_driver_cb_role = child_file; + detach_by_driver_cb_role.drained_begin = + detach_by_driver_cb_drained_begin; + } + + /* Create all involved nodes */ + parent_a = bdrv_new_open_driver(&bdrv_test, "parent-a", BDRV_O_RDWR, + &error_abort); + parent_b = bdrv_new_open_driver(&bdrv_test, "parent-b", 0, + &error_abort); + + a = bdrv_new_open_driver(&bdrv_test, "a", BDRV_O_RDWR, &error_abort); + b = bdrv_new_open_driver(&bdrv_test, "b", BDRV_O_RDWR, &error_abort); + c = bdrv_new_open_driver(&bdrv_test, "c", BDRV_O_RDWR, &error_abort); + + /* blk is a BB for parent-a */ + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + blk_insert_bs(blk, parent_a, &error_abort); + bdrv_unref(parent_a); + + /* If we want to get bdrv_drain_invoke() to call aio_poll(), the driver + * callback must not return immediately. */ + if (!by_parent_cb) { + BDRVTestState *s = parent_a->opaque; + s->sleep_in_drain_begin = true; + } + + /* Set child relationships */ + bdrv_ref(b); + bdrv_ref(a); + child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_file, &error_abort); + child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_backing, &error_abort); + + bdrv_ref(a); + bdrv_attach_child(parent_a, a, "PA-A", + by_parent_cb ? &child_file : &detach_by_driver_cb_role, + &error_abort); + + g_assert_cmpint(parent_a->refcnt, ==, 1); + g_assert_cmpint(parent_b->refcnt, ==, 1); + g_assert_cmpint(a->refcnt, ==, 3); + g_assert_cmpint(b->refcnt, ==, 2); + g_assert_cmpint(c->refcnt, ==, 1); + + g_assert(QLIST_FIRST(&parent_b->children) == child_a); + g_assert(QLIST_NEXT(child_a, next) == child_b); + g_assert(QLIST_NEXT(child_b, next) == NULL); + + /* Start the evil write request */ + detach_by_parent_data = (struct detach_by_parent_data) { + .parent_b = parent_b, + .child_b = child_b, + .c = c, + .by_parent_cb = by_parent_cb, + }; + acb = blk_aio_preadv(blk, 0, &qiov, 0, detach_by_parent_aio_cb, NULL); + g_assert(acb != NULL); + + /* Drain and check the expected result */ + bdrv_subtree_drained_begin(parent_b); + + g_assert(detach_by_parent_data.child_c != NULL); + + g_assert_cmpint(parent_a->refcnt, ==, 1); + g_assert_cmpint(parent_b->refcnt, ==, 1); + g_assert_cmpint(a->refcnt, ==, 3); + g_assert_cmpint(b->refcnt, ==, 1); + g_assert_cmpint(c->refcnt, ==, 2); + + g_assert(QLIST_FIRST(&parent_b->children) == detach_by_parent_data.child_c); + g_assert(QLIST_NEXT(detach_by_parent_data.child_c, next) == child_a); + g_assert(QLIST_NEXT(child_a, next) == NULL); + + g_assert_cmpint(parent_a->quiesce_counter, ==, 1); + g_assert_cmpint(parent_b->quiesce_counter, ==, 1); + g_assert_cmpint(a->quiesce_counter, ==, 1); + g_assert_cmpint(b->quiesce_counter, ==, 0); + g_assert_cmpint(c->quiesce_counter, ==, 1); + + bdrv_subtree_drained_end(parent_b); + + bdrv_unref(parent_b); + blk_unref(blk); + + /* XXX Once bdrv_close() unref's children instead of just detaching them, + * this won't be necessary any more. */ + bdrv_unref(a); + bdrv_unref(a); + bdrv_unref(c); + + g_assert_cmpint(a->refcnt, ==, 1); + g_assert_cmpint(b->refcnt, ==, 1); + g_assert_cmpint(c->refcnt, ==, 1); + bdrv_unref(a); + bdrv_unref(b); + bdrv_unref(c); +} + +static void test_detach_by_parent_cb(void) +{ + test_detach_indirect(true); +} + +static void test_detach_by_driver_cb(void) +{ + test_detach_indirect(false); +} + +static void test_append_to_drained(void) +{ + BlockBackend *blk; + BlockDriverState *base, *overlay; + BDRVTestState *base_s, *overlay_s; + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + base = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); + base_s = base->opaque; + blk_insert_bs(blk, base, &error_abort); + + overlay = bdrv_new_open_driver(&bdrv_test, "overlay", BDRV_O_RDWR, + &error_abort); + overlay_s = overlay->opaque; + + do_drain_begin(BDRV_DRAIN, base); + g_assert_cmpint(base->quiesce_counter, ==, 1); + g_assert_cmpint(base_s->drain_count, ==, 1); + g_assert_cmpint(base->in_flight, ==, 0); + + /* Takes ownership of overlay, so we don't have to unref it later */ + bdrv_append(overlay, base, &error_abort); + g_assert_cmpint(base->in_flight, ==, 0); + g_assert_cmpint(overlay->in_flight, ==, 0); + + g_assert_cmpint(base->quiesce_counter, ==, 1); + g_assert_cmpint(base_s->drain_count, ==, 1); + g_assert_cmpint(overlay->quiesce_counter, ==, 1); + g_assert_cmpint(overlay_s->drain_count, ==, 1); + + do_drain_end(BDRV_DRAIN, base); + + g_assert_cmpint(base->quiesce_counter, ==, 0); + g_assert_cmpint(base_s->drain_count, ==, 0); + g_assert_cmpint(overlay->quiesce_counter, ==, 0); + g_assert_cmpint(overlay_s->drain_count, ==, 0); + + bdrv_unref(base); + blk_unref(blk); +} + int main(int argc, char **argv) { + int ret; + bdrv_init(); qemu_init_main_loop(&error_abort); g_test_init(&argc, &argv, NULL); + qemu_event_init(&done_event, false); g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all); g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain); g_test_add_func("/bdrv-drain/driver-cb/drain_subtree", test_drv_cb_drain_subtree); - // XXX bdrv_drain_all() doesn't work in coroutine context + g_test_add_func("/bdrv-drain/driver-cb/co/drain_all", + test_drv_cb_co_drain_all); g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain); g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree", test_drv_cb_co_drain_subtree); @@ -634,19 +1318,40 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/quiesce/drain_subtree", test_quiesce_drain_subtree); - // XXX bdrv_drain_all() doesn't work in coroutine context + g_test_add_func("/bdrv-drain/quiesce/co/drain_all", + test_quiesce_co_drain_all); g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain); g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree", test_quiesce_co_drain_subtree); g_test_add_func("/bdrv-drain/nested", test_nested); g_test_add_func("/bdrv-drain/multiparent", test_multiparent); - g_test_add_func("/bdrv-drain/graph-change", test_graph_change); + + g_test_add_func("/bdrv-drain/graph-change/drain_subtree", + test_graph_change_drain_subtree); + g_test_add_func("/bdrv-drain/graph-change/drain_all", + test_graph_change_drain_all); + + g_test_add_func("/bdrv-drain/iothread/drain_all", test_iothread_drain_all); + g_test_add_func("/bdrv-drain/iothread/drain", test_iothread_drain); + g_test_add_func("/bdrv-drain/iothread/drain_subtree", + test_iothread_drain_subtree); g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all); g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain); g_test_add_func("/bdrv-drain/blockjob/drain_subtree", test_blockjob_drain_subtree); - return g_test_run(); + g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all); + g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); + g_test_add_func("/bdrv-drain/detach/drain_subtree", test_detach_by_drain_subtree); + g_test_add_func("/bdrv-drain/detach/parent_cb", test_detach_by_parent_cb); + g_test_add_func("/bdrv-drain/detach/driver_cb", test_detach_by_driver_cb); + + g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained); + + ret = g_test_run(); + qemu_event_destroy(&done_event); + return ret; } diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 5789893dda..58d9b87fb2 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -24,16 +24,17 @@ typedef struct { int *result; } TestBlockJob; -static void test_block_job_complete(BlockJob *job, void *opaque) +static void test_block_job_complete(Job *job, void *opaque) { - BlockDriverState *bs = blk_bs(job->blk); + BlockJob *bjob = container_of(job, BlockJob, job); + BlockDriverState *bs = blk_bs(bjob->blk); int rc = (intptr_t)opaque; - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(job)) { rc = -ECANCELED; } - block_job_completed(job, rc); + job_completed(job, rc, NULL); bdrv_unref(bs); } @@ -44,18 +45,18 @@ static void coroutine_fn test_block_job_run(void *opaque) while (s->iterations--) { if (s->use_timer) { - block_job_sleep_ns(job, 0); + job_sleep_ns(&job->job, 0); } else { - block_job_yield(job); + job_yield(&job->job); } - if (block_job_is_cancelled(job)) { + if (job_is_cancelled(&job->job)) { break; } } - block_job_defer_to_main_loop(job, test_block_job_complete, - (void *)(intptr_t)s->rc); + job_defer_to_main_loop(&job->job, test_block_job_complete, + (void *)(intptr_t)s->rc); } typedef struct { @@ -66,7 +67,7 @@ typedef struct { static void test_block_job_cb(void *opaque, int ret) { TestBlockJobCBData *data = opaque; - if (!ret && block_job_is_cancelled(&data->job->common)) { + if (!ret && job_is_cancelled(&data->job->common.job)) { ret = -ECANCELED; } *data->result = ret; @@ -74,8 +75,13 @@ static void test_block_job_cb(void *opaque, int ret) } static const BlockJobDriver test_block_job_driver = { - .instance_size = sizeof(TestBlockJob), - .start = test_block_job_run, + .job_driver = { + .instance_size = sizeof(TestBlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = test_block_job_run, + }, }; /* Create a block job that completes with a given return code after a given @@ -87,7 +93,7 @@ static const BlockJobDriver test_block_job_driver = { */ static BlockJob *test_block_job_start(unsigned int iterations, bool use_timer, - int rc, int *result, BlockJobTxn *txn) + int rc, int *result, JobTxn *txn) { BlockDriverState *bs; TestBlockJob *s; @@ -102,7 +108,7 @@ static BlockJob *test_block_job_start(unsigned int iterations, snprintf(job_id, sizeof(job_id), "job%u", counter++); s = block_job_create(job_id, &test_block_job_driver, txn, bs, - 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, + 0, BLK_PERM_ALL, 0, JOB_DEFAULT, test_block_job_cb, data, &error_abort); s->iterations = iterations; s->use_timer = use_timer; @@ -116,15 +122,15 @@ static BlockJob *test_block_job_start(unsigned int iterations, static void test_single_job(int expected) { BlockJob *job; - BlockJobTxn *txn; + JobTxn *txn; int result = -EINPROGRESS; - txn = block_job_txn_new(); + txn = job_txn_new(); job = test_block_job_start(1, true, expected, &result, txn); - block_job_start(job); + job_start(&job->job); if (expected == -ECANCELED) { - block_job_cancel(job, false); + job_cancel(&job->job, false); } while (result == -EINPROGRESS) { @@ -132,7 +138,7 @@ static void test_single_job(int expected) } g_assert_cmpint(result, ==, expected); - block_job_txn_unref(txn); + job_txn_unref(txn); } static void test_single_job_success(void) @@ -154,26 +160,26 @@ static void test_pair_jobs(int expected1, int expected2) { BlockJob *job1; BlockJob *job2; - BlockJobTxn *txn; + JobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; - txn = block_job_txn_new(); + txn = job_txn_new(); job1 = test_block_job_start(1, true, expected1, &result1, txn); job2 = test_block_job_start(2, true, expected2, &result2, txn); - block_job_start(job1); - block_job_start(job2); + job_start(&job1->job); + job_start(&job2->job); /* Release our reference now to trigger as many nice * use-after-free bugs as possible. */ - block_job_txn_unref(txn); + job_txn_unref(txn); if (expected1 == -ECANCELED) { - block_job_cancel(job1, false); + job_cancel(&job1->job, false); } if (expected2 == -ECANCELED) { - block_job_cancel(job2, false); + job_cancel(&job2->job, false); } while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { @@ -216,23 +222,23 @@ static void test_pair_jobs_fail_cancel_race(void) { BlockJob *job1; BlockJob *job2; - BlockJobTxn *txn; + JobTxn *txn; int result1 = -EINPROGRESS; int result2 = -EINPROGRESS; - txn = block_job_txn_new(); + txn = job_txn_new(); job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn); job2 = test_block_job_start(2, false, 0, &result2, txn); - block_job_start(job1); - block_job_start(job2); + job_start(&job1->job); + job_start(&job2->job); - block_job_cancel(job1, false); + job_cancel(&job1->job, false); /* Now make job2 finish before the main loop kicks jobs. This simulates * the race between a pending kick and another job completing. */ - block_job_enter(job2); - block_job_enter(job2); + job_enter(&job2->job); + job_enter(&job2->job); while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) { aio_poll(qemu_get_aio_context(), true); @@ -241,7 +247,7 @@ static void test_pair_jobs_fail_cancel_race(void) g_assert_cmpint(result1, ==, -ECANCELED); g_assert_cmpint(result2, ==, -ECANCELED); - block_job_txn_unref(txn); + job_txn_unref(txn); } int main(int argc, char **argv) diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index 8946bfd37b..cb42f06e61 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -17,7 +17,12 @@ #include "sysemu/block-backend.h" static const BlockJobDriver test_block_job_driver = { - .instance_size = sizeof(BlockJob), + .job_driver = { + .instance_size = sizeof(BlockJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + }, }; static void block_job_cb(void *opaque, int ret) @@ -38,9 +43,9 @@ static BlockJob *mk_job(BlockBackend *blk, const char *id, g_assert_null(errp); g_assert_nonnull(job); if (id) { - g_assert_cmpstr(job->id, ==, id); + g_assert_cmpstr(job->job.id, ==, id); } else { - g_assert_cmpstr(job->id, ==, blk_name(blk)); + g_assert_cmpstr(job->job.id, ==, blk_name(blk)); } } else { g_assert_nonnull(errp); @@ -55,7 +60,7 @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id, bool should_succeed) { return mk_job(blk, id, &test_block_job_driver, - should_succeed, BLOCK_JOB_DEFAULT); + should_succeed, JOB_DEFAULT); } /* This creates a BlockBackend (optionally with a name) with a @@ -124,11 +129,11 @@ static void test_job_ids(void) job[1] = do_test_id(blk[1], "id0", false); /* But once job[0] finishes we can reuse its ID */ - block_job_early_fail(job[0]); + job_early_fail(&job[0]->job); job[1] = do_test_id(blk[1], "id0", true); /* No job ID specified, defaults to the backend name ('drive1') */ - block_job_early_fail(job[1]); + job_early_fail(&job[1]->job); job[1] = do_test_id(blk[1], NULL, true); /* Duplicate job ID */ @@ -141,9 +146,9 @@ static void test_job_ids(void) /* This one is valid */ job[2] = do_test_id(blk[2], "id_2", true); - block_job_early_fail(job[0]); - block_job_early_fail(job[1]); - block_job_early_fail(job[2]); + job_early_fail(&job[0]->job); + job_early_fail(&job[1]->job); + job_early_fail(&job[2]->job); destroy_blk(blk[0]); destroy_blk(blk[1]); @@ -158,16 +163,16 @@ typedef struct CancelJob { bool completed; } CancelJob; -static void cancel_job_completed(BlockJob *job, void *opaque) +static void cancel_job_completed(Job *job, void *opaque) { CancelJob *s = opaque; s->completed = true; - block_job_completed(job, 0); + job_completed(job, 0, NULL); } -static void cancel_job_complete(BlockJob *job, Error **errp) +static void cancel_job_complete(Job *job, Error **errp) { - CancelJob *s = container_of(job, CancelJob, common); + CancelJob *s = container_of(job, CancelJob, common.job); s->should_complete = true; } @@ -176,25 +181,30 @@ static void coroutine_fn cancel_job_start(void *opaque) CancelJob *s = opaque; while (!s->should_complete) { - if (block_job_is_cancelled(&s->common)) { + if (job_is_cancelled(&s->common.job)) { goto defer; } - if (!s->common.ready && s->should_converge) { - block_job_event_ready(&s->common); + if (!job_is_ready(&s->common.job) && s->should_converge) { + job_transition_to_ready(&s->common.job); } - block_job_sleep_ns(&s->common, 100000); + job_sleep_ns(&s->common.job, 100000); } defer: - block_job_defer_to_main_loop(&s->common, cancel_job_completed, s); + job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); } static const BlockJobDriver test_cancel_driver = { - .instance_size = sizeof(CancelJob), - .start = cancel_job_start, - .complete = cancel_job_complete, + .job_driver = { + .instance_size = sizeof(CancelJob), + .free = block_job_free, + .user_resume = block_job_user_resume, + .drain = block_job_drain, + .start = cancel_job_start, + .complete = cancel_job_complete, + }, }; static CancelJob *create_common(BlockJob **pjob) @@ -205,9 +215,9 @@ static CancelJob *create_common(BlockJob **pjob) blk = create_blk(NULL); job = mk_job(blk, "Steve", &test_cancel_driver, true, - BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS); - block_job_ref(job); - assert(job->status == BLOCK_JOB_STATUS_CREATED); + JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); + job_ref(&job->job); + assert(job->job.status == JOB_STATUS_CREATED); s = container_of(job, CancelJob, common); s->blk = blk; @@ -219,16 +229,15 @@ static void cancel_common(CancelJob *s) { BlockJob *job = &s->common; BlockBackend *blk = s->blk; - BlockJobStatus sts = job->status; + JobStatus sts = job->job.status; - block_job_cancel_sync(job); - if ((sts != BLOCK_JOB_STATUS_CREATED) && - (sts != BLOCK_JOB_STATUS_CONCLUDED)) { - BlockJob *dummy = job; - block_job_dismiss(&dummy, &error_abort); + job_cancel_sync(&job->job); + if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { + Job *dummy = &job->job; + job_dismiss(&dummy, &error_abort); } - assert(job->status == BLOCK_JOB_STATUS_NULL); - block_job_unref(job); + assert(job->job.status == JOB_STATUS_NULL); + job_unref(&job->job); destroy_blk(blk); } @@ -248,8 +257,8 @@ static void test_cancel_running(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); cancel_common(s); } @@ -261,12 +270,12 @@ static void test_cancel_paused(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); - block_job_user_pause(job, &error_abort); - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_PAUSED); + job_user_pause(&job->job, &error_abort); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_PAUSED); cancel_common(s); } @@ -278,12 +287,12 @@ static void test_cancel_ready(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); cancel_common(s); } @@ -295,16 +304,16 @@ static void test_cancel_standby(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); - block_job_user_pause(job, &error_abort); - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_STANDBY); + job_user_pause(&job->job, &error_abort); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_STANDBY); cancel_common(s); } @@ -316,19 +325,19 @@ static void test_cancel_pending(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); - block_job_complete(job, &error_abort); - block_job_enter(job); + job_complete(&job->job, &error_abort); + job_enter(&job->job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); } - assert(job->status == BLOCK_JOB_STATUS_PENDING); + assert(job->job.status == JOB_STATUS_PENDING); cancel_common(s); } @@ -340,22 +349,22 @@ static void test_cancel_concluded(void) s = create_common(&job); - block_job_start(job); - assert(job->status == BLOCK_JOB_STATUS_RUNNING); + job_start(&job->job); + assert(job->job.status == JOB_STATUS_RUNNING); s->should_converge = true; - block_job_enter(job); - assert(job->status == BLOCK_JOB_STATUS_READY); + job_enter(&job->job); + assert(job->job.status == JOB_STATUS_READY); - block_job_complete(job, &error_abort); - block_job_enter(job); + job_complete(&job->job, &error_abort); + job_enter(&job->job); while (!s->completed) { aio_poll(qemu_get_aio_context(), true); } - assert(job->status == BLOCK_JOB_STATUS_PENDING); + assert(job->job.status == JOB_STATUS_PENDING); - block_job_finalize(job, &error_abort); - assert(job->status == BLOCK_JOB_STATUS_CONCLUDED); + job_finalize(&job->job, &error_abort); + assert(job->job.status == JOB_STATUS_CONCLUDED); cancel_common(s); } diff --git a/tests/test-char.c b/tests/test-char.c index 306c728335..5905d31441 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -214,6 +214,10 @@ static void char_mux_test(void) g_assert_cmpint(h2.last_event, ==, -1); /* switch focus */ + qemu_chr_be_write(base, (void *)"\1b", 2); + g_assert_cmpint(h1.last_event, ==, 42); + g_assert_cmpint(h2.last_event, ==, CHR_EVENT_BREAK); + qemu_chr_be_write(base, (void *)"\1c", 2); g_assert_cmpint(h1.last_event, ==, CHR_EVENT_MUX_IN); g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); @@ -227,6 +231,10 @@ static void char_mux_test(void) g_assert_cmpstr(h1.read_buf, ==, "hello"); h1.read_count = 0; + qemu_chr_be_write(base, (void *)"\1b", 2); + g_assert_cmpint(h1.last_event, ==, CHR_EVENT_BREAK); + g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); + /* remove first handler */ qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, NULL, true); @@ -322,7 +330,7 @@ static void char_socket_test_common(Chardev *chr) qdict = qobject_to(QDict, addr); port = qdict_get_str(qdict, "port"); tmp = g_strdup_printf("tcp:127.0.0.1:%s", port); - QDECREF(qdict); + qobject_unref(qdict); qemu_chr_fe_init(&be, chr, &error_abort); qemu_chr_fe_set_handlers(&be, socket_can_read, socket_read, diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c index af2f80e89c..30f9ac4bbf 100644 --- a/tests/test-crypto-tlscredsx509.c +++ b/tests/test-crypto-tlscredsx509.c @@ -54,7 +54,7 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, "sanity-check", "yes", NULL); - if (*errp) { + if (!creds) { return NULL; } return QCRYPTO_TLS_CREDS(creds); @@ -74,7 +74,6 @@ static void test_tls_creds(const void *opaque) struct QCryptoTLSCredsTestData *data = (struct QCryptoTLSCredsTestData *)opaque; QCryptoTLSCreds *creds; - Error *err = NULL; #define CERT_DIR "tests/test-crypto-tlscredsx509-certs/" mkdir(CERT_DIR, 0700); @@ -113,17 +112,11 @@ static void test_tls_creds(const void *opaque) QCRYPTO_TLS_CREDS_ENDPOINT_SERVER : QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT), CERT_DIR, - &err); + data->expectFail ? NULL : &error_abort); if (data->expectFail) { - error_free(err); g_assert(creds == NULL); } else { - if (err) { - g_printerr("Failed to generate creds: %s\n", - error_get_pretty(err)); - error_free(err); - } g_assert(creds != NULL); } diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index 82f21c27f2..6fa9950afb 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -21,7 +21,9 @@ #include "qemu/osdep.h" #include "crypto-tls-x509-helpers.h" +#include "crypto-tls-psk-helpers.h" #include "crypto/tlscredsx509.h" +#include "crypto/tlscredspsk.h" #include "crypto/tlssession.h" #include "qom/object_interfaces.h" #include "qapi/error.h" @@ -31,20 +33,9 @@ #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT #define WORKDIR "tests/test-crypto-tlssession-work/" +#define PSKFILE WORKDIR "keys.psk" #define KEYFILE WORKDIR "key-ctx.pem" -struct QCryptoTLSSessionTestData { - const char *servercacrt; - const char *clientcacrt; - const char *servercrt; - const char *clientcrt; - bool expectServerFail; - bool expectClientFail; - const char *hostname; - const char *const *wildcards; -}; - - static ssize_t testWrite(const char *buf, size_t len, void *opaque) { int *fd = opaque; @@ -59,18 +50,149 @@ static ssize_t testRead(char *buf, size_t len, void *opaque) return read(*fd, buf, len); } -static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, - const char *certdir, - Error **errp) +static QCryptoTLSCreds *test_tls_creds_psk_create( + QCryptoTLSCredsEndpoint endpoint, + const char *dir) +{ + Object *parent = object_get_objects_root(); + Object *creds = object_new_with_props( + TYPE_QCRYPTO_TLS_CREDS_PSK, + parent, + (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "testtlscredsserver" : "testtlscredsclient"), + &error_abort, + "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? + "server" : "client"), + "dir", dir, + "priority", "NORMAL", + NULL + ); + return QCRYPTO_TLS_CREDS(creds); +} + + +static void test_crypto_tls_session_psk(void) +{ + QCryptoTLSCreds *clientCreds; + QCryptoTLSCreds *serverCreds; + QCryptoTLSSession *clientSess = NULL; + QCryptoTLSSession *serverSess = NULL; + int channel[2]; + bool clientShake = false; + bool serverShake = false; + int ret; + + /* We'll use this for our fake client-server connection */ + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, channel); + g_assert(ret == 0); + + /* + * We have an evil loop to do the handshake in a single + * thread, so we need these non-blocking to avoid deadlock + * of ourselves + */ + qemu_set_nonblock(channel[0]); + qemu_set_nonblock(channel[1]); + + clientCreds = test_tls_creds_psk_create( + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, + WORKDIR); + g_assert(clientCreds != NULL); + + serverCreds = test_tls_creds_psk_create( + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, + WORKDIR); + g_assert(serverCreds != NULL); + + /* Now the real part of the test, setup the sessions */ + clientSess = qcrypto_tls_session_new( + clientCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); + g_assert(clientSess != NULL); + + serverSess = qcrypto_tls_session_new( + serverCreds, NULL, NULL, + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); + g_assert(serverSess != NULL); + + /* For handshake to work, we need to set the I/O callbacks + * to read/write over the socketpair + */ + qcrypto_tls_session_set_callbacks(serverSess, + testWrite, testRead, + &channel[0]); + qcrypto_tls_session_set_callbacks(clientSess, + testWrite, testRead, + &channel[1]); + + /* + * Finally we loop around & around doing handshake on each + * session until we get an error, or the handshake completes. + * This relies on the socketpair being nonblocking to avoid + * deadlocking ourselves upon handshake + */ + do { + int rv; + if (!serverShake) { + rv = qcrypto_tls_session_handshake(serverSess, + &error_abort); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(serverSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + serverShake = true; + } + } + if (!clientShake) { + rv = qcrypto_tls_session_handshake(clientSess, + &error_abort); + g_assert(rv >= 0); + if (qcrypto_tls_session_get_handshake_status(clientSess) == + QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + clientShake = true; + } + } + } while (!clientShake || !serverShake); + + + /* Finally make sure the server & client validation is successful. */ + g_assert(qcrypto_tls_session_check_credentials(serverSess, + &error_abort) == 0); + g_assert(qcrypto_tls_session_check_credentials(clientSess, + &error_abort) == 0); + + object_unparent(OBJECT(serverCreds)); + object_unparent(OBJECT(clientCreds)); + + qcrypto_tls_session_free(serverSess); + qcrypto_tls_session_free(clientSess); + + close(channel[0]); + close(channel[1]); +} + + +struct QCryptoTLSSessionTestData { + const char *servercacrt; + const char *clientcacrt; + const char *servercrt; + const char *clientcrt; + bool expectServerFail; + bool expectClientFail; + const char *hostname; + const char *const *wildcards; +}; + +static QCryptoTLSCreds *test_tls_creds_x509_create( + QCryptoTLSCredsEndpoint endpoint, + const char *certdir) { - Error *err = NULL; Object *parent = object_get_objects_root(); Object *creds = object_new_with_props( TYPE_QCRYPTO_TLS_CREDS_X509, parent, (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "testtlscredsserver" : "testtlscredsclient"), - &err, + &error_abort, "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "server" : "client"), "dir", certdir, @@ -85,11 +207,6 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, "sanity-check", "no", NULL ); - - if (err) { - error_propagate(errp, err); - return NULL; - } return QCRYPTO_TLS_CREDS(creds); } @@ -104,7 +221,7 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, * initiate a TLS session across them. Finally do * do actual cert validation tests */ -static void test_crypto_tls_session(const void *opaque) +static void test_crypto_tls_session_x509(const void *opaque) { struct QCryptoTLSSessionTestData *data = (struct QCryptoTLSSessionTestData *)opaque; @@ -117,7 +234,6 @@ static void test_crypto_tls_session(const void *opaque) int channel[2]; bool clientShake = false; bool serverShake = false; - Error *err = NULL; int ret; /* We'll use this for our fake client-server connection */ @@ -159,16 +275,14 @@ static void test_crypto_tls_session(const void *opaque) g_assert(link(KEYFILE, CLIENT_CERT_DIR QCRYPTO_TLS_CREDS_X509_CLIENT_KEY) == 0); - clientCreds = test_tls_creds_create( + clientCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - CLIENT_CERT_DIR, - &err); + CLIENT_CERT_DIR); g_assert(clientCreds != NULL); - serverCreds = test_tls_creds_create( + serverCreds = test_tls_creds_x509_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - SERVER_CERT_DIR, - &err); + SERVER_CERT_DIR); g_assert(serverCreds != NULL); acl = qemu_acl_init("tlssessionacl"); @@ -182,13 +296,13 @@ static void test_crypto_tls_session(const void *opaque) /* Now the real part of the test, setup the sessions */ clientSess = qcrypto_tls_session_new( clientCreds, data->hostname, NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &err); + QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, &error_abort); + g_assert(clientSess != NULL); + serverSess = qcrypto_tls_session_new( serverCreds, NULL, data->wildcards ? "tlssessionacl" : NULL, - QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &err); - - g_assert(clientSess != NULL); + QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, &error_abort); g_assert(serverSess != NULL); /* For handshake to work, we need to set the I/O callbacks @@ -211,7 +325,7 @@ static void test_crypto_tls_session(const void *opaque) int rv; if (!serverShake) { rv = qcrypto_tls_session_handshake(serverSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(serverSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { @@ -220,23 +334,22 @@ static void test_crypto_tls_session(const void *opaque) } if (!clientShake) { rv = qcrypto_tls_session_handshake(clientSess, - &err); + &error_abort); g_assert(rv >= 0); if (qcrypto_tls_session_get_handshake_status(clientSess) == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { clientShake = true; } } - } while (!clientShake && !serverShake); + } while (!clientShake || !serverShake); /* Finally make sure the server validation does what * we were expecting */ - if (qcrypto_tls_session_check_credentials(serverSess, &err) < 0) { + if (qcrypto_tls_session_check_credentials( + serverSess, data->expectServerFail ? NULL : &error_abort) < 0) { g_assert(data->expectServerFail); - error_free(err); - err = NULL; } else { g_assert(!data->expectServerFail); } @@ -244,10 +357,9 @@ static void test_crypto_tls_session(const void *opaque) /* * And the same for the client validation check */ - if (qcrypto_tls_session_check_credentials(clientSess, &err) < 0) { + if (qcrypto_tls_session_check_credentials( + clientSess, data->expectClientFail ? NULL : &error_abort) < 0) { g_assert(data->expectClientFail); - error_free(err); - err = NULL; } else { g_assert(!data->expectClientFail); } @@ -285,7 +397,13 @@ int main(int argc, char **argv) mkdir(WORKDIR, 0700); test_tls_init(KEYFILE); + test_tls_psk_init(PSKFILE); + /* Simple initial test using Pre-Shared Keys. */ + g_test_add_func("/qcrypto/tlssession/psk", + test_crypto_tls_session_psk); + + /* More complex tests using X.509 certificates. */ # define TEST_SESS_REG(name, caCrt, \ serverCrt, clientCrt, \ expectServerFail, expectClientFail, \ @@ -296,7 +414,7 @@ int main(int argc, char **argv) hostname, wildcards \ }; \ g_test_add_data_func("/qcrypto/tlssession/" # name, \ - &name, test_crypto_tls_session); \ + &name, test_crypto_tls_session_x509); \ # define TEST_SESS_REG_EXT(name, serverCaCrt, clientCaCrt, \ @@ -309,7 +427,7 @@ int main(int argc, char **argv) hostname, wildcards \ }; \ g_test_add_data_func("/qcrypto/tlssession/" # name, \ - &name, test_crypto_tls_session); \ + &name, test_crypto_tls_session_x509); \ /* A perfect CA, perfect client & perfect server */ @@ -518,6 +636,7 @@ int main(int argc, char **argv) test_tls_discard_cert(&clientcertlevel2breq); unlink(WORKDIR "cacertchain-sess.pem"); + test_tls_psk_cleanup(PSKFILE); test_tls_cleanup(KEYFILE); rmdir(WORKDIR); diff --git a/tests/test-cutils.c b/tests/test-cutils.c index 64a489c2e9..d85c3e0f6d 100644 --- a/tests/test-cutils.c +++ b/tests/test-cutils.c @@ -26,8 +26,9 @@ */ #include "qemu/osdep.h" - +#include "qemu/units.h" #include "qemu/cutils.h" +#include "qemu/units.h" static void test_parse_uint_null(void) { @@ -2022,7 +2023,7 @@ static void test_qemu_strtosz_units(void) /* default is M */ err = qemu_strtosz_MiB(none, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, M_BYTE); + g_assert_cmpint(res, ==, MiB); g_assert(endptr == none + 1); err = qemu_strtosz(b, &endptr, &res); @@ -2032,32 +2033,32 @@ static void test_qemu_strtosz_units(void) err = qemu_strtosz(k, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, K_BYTE); + g_assert_cmpint(res, ==, KiB); g_assert(endptr == k + 2); err = qemu_strtosz(m, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, M_BYTE); + g_assert_cmpint(res, ==, MiB); g_assert(endptr == m + 2); err = qemu_strtosz(g, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, G_BYTE); + g_assert_cmpint(res, ==, GiB); g_assert(endptr == g + 2); err = qemu_strtosz(t, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, T_BYTE); + g_assert_cmpint(res, ==, TiB); g_assert(endptr == t + 2); err = qemu_strtosz(p, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, P_BYTE); + g_assert_cmpint(res, ==, PiB); g_assert(endptr == p + 2); err = qemu_strtosz(e, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, E_BYTE); + g_assert_cmpint(res, ==, EiB); g_assert(endptr == e + 2); } @@ -2070,7 +2071,7 @@ static void test_qemu_strtosz_float(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12.345 * M_BYTE); + g_assert_cmpint(res, ==, 12.345 * MiB); g_assert(endptr == str + 7); } @@ -2106,7 +2107,7 @@ static void test_qemu_strtosz_trailing(void) str = "123xxx"; err = qemu_strtosz_MiB(str, &endptr, &res); - g_assert_cmpint(res, ==, 123 * M_BYTE); + g_assert_cmpint(res, ==, 123 * MiB); g_assert(endptr == str + 3); err = qemu_strtosz(str, NULL, &res); diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index f29631f939..5e67ac1d3a 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -30,6 +30,18 @@ typedef struct TestHBitmapData { } TestHBitmapData; +static int64_t check_hbitmap_iter_next(HBitmapIter *hbi) +{ + int next0, next1; + + next0 = hbitmap_iter_next(hbi, false); + next1 = hbitmap_iter_next(hbi, true); + + g_assert_cmpint(next0, ==, next1); + + return next0; +} + /* Check that the HBitmap and the shadow bitmap contain the same data, * ignoring the same "first" bits. */ @@ -46,7 +58,7 @@ static void hbitmap_test_check(TestHBitmapData *data, i = first; for (;;) { - next = hbitmap_iter_next(&hbi); + next = check_hbitmap_iter_next(&hbi); if (next < 0) { next = data->size; } @@ -435,25 +447,25 @@ static void test_hbitmap_iter_granularity(TestHBitmapData *data, /* Note that hbitmap_test_check has to be invoked manually in this test. */ hbitmap_test_init(data, 131072 << 7, 7); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); hbitmap_test_set(data, (131072 << 7) - 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); } static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) @@ -893,7 +905,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, for (i = 0; i < num_positions; i++) { hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); hbitmap_iter_init(&iter, data->hb, 0); - next = hbitmap_iter_next(&iter); + next = check_hbitmap_iter_next(&iter); if (i == num_positions - 1) { g_assert_cmpint(next, ==, -1); } else { @@ -919,10 +931,10 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); - hbitmap_iter_next(&hbi); + check_hbitmap_iter_next(&hbi); hbitmap_reset_all(data->hb); - hbitmap_iter_next(&hbi); + check_hbitmap_iter_next(&hbi); } static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c index bb88ee870f..4900c6d433 100644 --- a/tests/test-io-channel-tls.c +++ b/tests/test-io-channel-tls.c @@ -30,6 +30,7 @@ #include "crypto/init.h" #include "crypto/tlscredsx509.h" #include "qemu/acl.h" +#include "qapi/error.h" #include "qom/object_interfaces.h" #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT @@ -64,8 +65,7 @@ static void test_tls_handshake_done(QIOTask *task, static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, - const char *certdir, - Error **errp) + const char *certdir) { Object *parent = object_get_objects_root(); Object *creds = object_new_with_props( @@ -73,7 +73,7 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, parent, (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "testtlscredsserver" : "testtlscredsclient"), - errp, + &error_abort, "endpoint", (endpoint == QCRYPTO_TLS_CREDS_ENDPOINT_SERVER ? "server" : "client"), "dir", certdir, @@ -89,9 +89,6 @@ static QCryptoTLSCreds *test_tls_creds_create(QCryptoTLSCredsEndpoint endpoint, NULL ); - if (*errp) { - return NULL; - } return QCRYPTO_TLS_CREDS(creds); } @@ -121,7 +118,6 @@ static void test_io_channel_tls(const void *opaque) int channel[2]; struct QIOChannelTLSHandshakeData clientHandshake = { false, false }; struct QIOChannelTLSHandshakeData serverHandshake = { false, false }; - Error *err = NULL; QIOChannelTest *test; GMainContext *mainloop; @@ -157,14 +153,12 @@ static void test_io_channel_tls(const void *opaque) clientCreds = test_tls_creds_create( QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT, - CLIENT_CERT_DIR, - &err); + CLIENT_CERT_DIR); g_assert(clientCreds != NULL); serverCreds = test_tls_creds_create( QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, - SERVER_CERT_DIR, - &err); + SERVER_CERT_DIR); g_assert(serverCreds != NULL); acl = qemu_acl_init("channeltlsacl"); @@ -176,10 +170,10 @@ static void test_io_channel_tls(const void *opaque) } clientChanSock = qio_channel_socket_new_fd( - channel[0], &err); + channel[0], &error_abort); g_assert(clientChanSock != NULL); serverChanSock = qio_channel_socket_new_fd( - channel[1], &err); + channel[1], &error_abort); g_assert(serverChanSock != NULL); /* @@ -193,12 +187,12 @@ static void test_io_channel_tls(const void *opaque) /* Now the real part of the test, setup the sessions */ clientChanTLS = qio_channel_tls_new_client( QIO_CHANNEL(clientChanSock), clientCreds, - data->hostname, &err); + data->hostname, &error_abort); g_assert(clientChanTLS != NULL); serverChanTLS = qio_channel_tls_new_server( QIO_CHANNEL(serverChanSock), serverCreds, - "channeltlsacl", &err); + "channeltlsacl", &error_abort); g_assert(serverChanTLS != NULL); qio_channel_tls_handshake(clientChanTLS, diff --git a/tests/test-keyval.c b/tests/test-keyval.c index 029f05202a..09b0ae3c68 100644 --- a/tests/test-keyval.c +++ b/tests/test-keyval.c @@ -11,6 +11,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" @@ -30,7 +31,7 @@ static void test_keyval_parse(void) /* Nothing */ qdict = keyval_parse("", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 0); - QDECREF(qdict); + qobject_unref(qdict); /* Empty key (qemu_opts_parse() accepts this) */ qdict = keyval_parse("=val", NULL, &err); @@ -70,7 +71,7 @@ static void test_keyval_parse(void) qdict = keyval_parse(params + 2, NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, long_key + 1), ==, "v"); - QDECREF(qdict); + qobject_unref(qdict); /* Long key fragment */ qdict = keyval_parse(params, NULL, &error_abort); @@ -79,7 +80,7 @@ static void test_keyval_parse(void) g_assert(sub_qdict); g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(sub_qdict, long_key + 1), ==, "v"); - QDECREF(qdict); + qobject_unref(qdict); g_free(params); /* Crap after valid key */ @@ -92,13 +93,13 @@ static void test_keyval_parse(void) g_assert_cmpuint(qdict_size(qdict), ==, 2); g_assert_cmpstr(qdict_get_try_str(qdict, "a"), ==, "3"); g_assert_cmpstr(qdict_get_try_str(qdict, "b"), ==, "2,x"); - QDECREF(qdict); + qobject_unref(qdict); /* Even when it doesn't in qemu_opts_parse() */ qdict = keyval_parse("id=foo,id=bar", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "bar"); - QDECREF(qdict); + qobject_unref(qdict); /* Dotted keys */ qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, &error_abort); @@ -111,7 +112,7 @@ static void test_keyval_parse(void) g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(sub_qdict, "c"), ==, "2"); g_assert_cmpstr(qdict_get_try_str(qdict, "d"), ==, "3"); - QDECREF(qdict); + qobject_unref(qdict); /* Inconsistent dotted keys */ qdict = keyval_parse("a.b=1,a=2", NULL, &err); @@ -125,7 +126,7 @@ static void test_keyval_parse(void) qdict = keyval_parse("x=y,", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, "y"); - QDECREF(qdict); + qobject_unref(qdict); /* Except when it isn't */ qdict = keyval_parse(",", NULL, &err); @@ -136,13 +137,13 @@ static void test_keyval_parse(void) qdict = keyval_parse("x=,,id=bar", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, "x"), ==, ",id=bar"); - QDECREF(qdict); + qobject_unref(qdict); /* Anti-social ID is left to caller (qemu_opts_parse() rejects it) */ qdict = keyval_parse("id=666", NULL, &error_abort); g_assert_cmpuint(qdict_size(qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(qdict, "id"), ==, "666"); - QDECREF(qdict); + qobject_unref(qdict); /* Implied value not supported (unlike qemu_opts_parse()) */ qdict = keyval_parse("an,noaus,noaus=", NULL, &err); @@ -160,7 +161,7 @@ static void test_keyval_parse(void) g_assert_cmpstr(qdict_get_try_str(qdict, "implied"), ==, "an"); g_assert_cmpstr(qdict_get_try_str(qdict, "aus"), ==, "off"); g_assert_cmpstr(qdict_get_try_str(qdict, "noaus"), ==, ""); - QDECREF(qdict); + qobject_unref(qdict); /* Implied dotted key */ qdict = keyval_parse("val", "eins.zwei", &error_abort); @@ -169,7 +170,7 @@ static void test_keyval_parse(void) g_assert(sub_qdict); g_assert_cmpuint(qdict_size(sub_qdict), ==, 1); g_assert_cmpstr(qdict_get_try_str(sub_qdict, "zwei"), ==, "val"); - QDECREF(qdict); + qobject_unref(qdict); /* Implied key with empty value (qemu_opts_parse() accepts this) */ qdict = keyval_parse(",", "implied", &err); @@ -198,7 +199,7 @@ static void check_list012(QList *qlist) qstr = qobject_to(QString, qlist_pop(qlist)); g_assert(qstr); g_assert_cmpstr(qstring_get_str(qstr), ==, expected[i]); - QDECREF(qstr); + qobject_unref(qstr); } g_assert(qlist_empty(qlist)); } @@ -218,14 +219,14 @@ static void test_keyval_parse_list(void) NULL, &error_abort); g_assert_cmpint(qdict_size(qdict), ==, 1); check_list012(qdict_get_qlist(qdict, "list")); - QDECREF(qdict); + qobject_unref(qdict); /* Multiple indexes, last one wins */ qdict = keyval_parse("list.1=goner,list.0=null,list.01=eins,list.2=zwei", NULL, &error_abort); g_assert_cmpint(qdict_size(qdict), ==, 1); check_list012(qdict_get_qlist(qdict, "list")); - QDECREF(qdict); + qobject_unref(qdict); /* List at deeper nesting */ qdict = keyval_parse("a.list.1=eins,a.list.00=null,a.list.2=zwei", @@ -234,7 +235,7 @@ static void test_keyval_parse_list(void) sub_qdict = qdict_get_qdict(qdict, "a"); g_assert_cmpint(qdict_size(sub_qdict), ==, 1); check_list012(qdict_get_qlist(sub_qdict, "list")); - QDECREF(qdict); + qobject_unref(qdict); /* Inconsistent dotted keys: both list and dictionary */ qdict = keyval_parse("a.b.c=1,a.b.0=2", NULL, &err); @@ -262,7 +263,7 @@ static void test_keyval_visit_bool(void) qdict = keyval_parse("bool1=on,bool2=off", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_bool(v, "bool1", &b, &error_abort); g_assert(b); @@ -274,7 +275,7 @@ static void test_keyval_visit_bool(void) qdict = keyval_parse("bool1=offer", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_bool(v, "bool1", &b, &err); error_free_or_abort(&err); @@ -292,7 +293,7 @@ static void test_keyval_visit_number(void) /* Lower limit zero */ qdict = keyval_parse("number1=0", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &error_abort); g_assert_cmpuint(u, ==, 0); @@ -304,7 +305,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=18446744073709551615,number2=-1", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &error_abort); g_assert_cmphex(u, ==, UINT64_MAX); @@ -318,7 +319,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=18446744073709551616", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &err); error_free_or_abort(&err); @@ -329,7 +330,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=-18446744073709551616", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &err); error_free_or_abort(&err); @@ -340,7 +341,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=0x2a,number2=052", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &error_abort); g_assert_cmpuint(u, ==, 42); @@ -354,7 +355,7 @@ static void test_keyval_visit_number(void) qdict = keyval_parse("number1=3.14,number2=08", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_uint64(v, "number1", &u, &err); error_free_or_abort(&err); @@ -374,7 +375,7 @@ static void test_keyval_visit_size(void) /* Lower limit zero */ qdict = keyval_parse("sz1=0", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmpuint(sz, ==, 0); @@ -390,7 +391,7 @@ static void test_keyval_visit_size(void) "sz3=9007199254740993", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmphex(sz, ==, 0x1fffffffffffff); @@ -407,7 +408,7 @@ static void test_keyval_visit_size(void) "sz2=9223372036854775295", /* 7ffffffffffffdff */ NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmphex(sz, ==, 0x7ffffffffffffc00); @@ -422,7 +423,7 @@ static void test_keyval_visit_size(void) "sz2=18446744073709550591", /* fffffffffffffbff */ NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmphex(sz, ==, 0xfffffffffffff800); @@ -437,7 +438,7 @@ static void test_keyval_visit_size(void) "sz2=18446744073709550592", /* fffffffffffffc00 */ NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &err); error_free_or_abort(&err); @@ -450,18 +451,18 @@ static void test_keyval_visit_size(void) qdict = keyval_parse("sz1=8b,sz2=1.5k,sz3=2M,sz4=0.1G,sz5=16777215T", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &error_abort); g_assert_cmpuint(sz, ==, 8); visit_type_size(v, "sz2", &sz, &error_abort); g_assert_cmpuint(sz, ==, 1536); visit_type_size(v, "sz3", &sz, &error_abort); - g_assert_cmphex(sz, ==, 2 * M_BYTE); + g_assert_cmphex(sz, ==, 2 * MiB); visit_type_size(v, "sz4", &sz, &error_abort); - g_assert_cmphex(sz, ==, G_BYTE / 10); + g_assert_cmphex(sz, ==, GiB / 10); visit_type_size(v, "sz5", &sz, &error_abort); - g_assert_cmphex(sz, ==, 16777215 * T_BYTE); + g_assert_cmphex(sz, ==, 16777215ULL * TiB); visit_check_struct(v, &error_abort); visit_end_struct(v, NULL); visit_free(v); @@ -469,7 +470,7 @@ static void test_keyval_visit_size(void) /* Beyond limit with suffix */ qdict = keyval_parse("sz1=16777216T", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &err); error_free_or_abort(&err); @@ -479,7 +480,7 @@ static void test_keyval_visit_size(void) /* Trailing crap */ qdict = keyval_parse("sz1=16E,sz2=16Gi", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_size(v, "sz1", &sz, &err); error_free_or_abort(&err); @@ -498,7 +499,7 @@ static void test_keyval_visit_dict(void) qdict = keyval_parse("a.b.c=1,a.b.c=2,d=3", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_start_struct(v, "a", NULL, 0, &error_abort); visit_start_struct(v, "b", NULL, 0, &error_abort); @@ -516,7 +517,7 @@ static void test_keyval_visit_dict(void) qdict = keyval_parse("a.b=", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_start_struct(v, "a", NULL, 0, &error_abort); visit_type_int(v, "c", &i, &err); /* a.c missing */ @@ -539,7 +540,7 @@ static void test_keyval_visit_list(void) qdict = keyval_parse("a.0=,a.1=I,a.2.0=II", NULL, &error_abort); /* TODO empty list */ v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_start_list(v, "a", NULL, 0, &error_abort); visit_type_str(v, NULL, &s, &error_abort); @@ -562,7 +563,7 @@ static void test_keyval_visit_list(void) qdict = keyval_parse("a.0=,b.0.0=head", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_start_list(v, "a", NULL, 0, &error_abort); visit_check_list(v, &err); /* a[0] unexpected */ @@ -591,7 +592,7 @@ static void test_keyval_visit_optional(void) qdict = keyval_parse("a.b=1", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_optional(v, "b", &present); g_assert(!present); /* b missing */ @@ -627,7 +628,7 @@ static void test_keyval_visit_alternate(void) */ qdict = keyval_parse("a=1,b=2,c=on", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_AltStrObj(v, "a", &aso, &error_abort); g_assert_cmpint(aso->type, ==, QTYPE_QSTRING); @@ -651,19 +652,19 @@ static void test_keyval_visit_any(void) qdict = keyval_parse("a.0=null,a.1=1", NULL, &error_abort); v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); - QDECREF(qdict); + qobject_unref(qdict); visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_any(v, "a", &any, &error_abort); qlist = qobject_to(QList, any); g_assert(qlist); qstr = qobject_to(QString, qlist_pop(qlist)); g_assert_cmpstr(qstring_get_str(qstr), ==, "null"); - QDECREF(qstr); + qobject_unref(qstr); qstr = qobject_to(QString, qlist_pop(qlist)); g_assert_cmpstr(qstring_get_str(qstr), ==, "1"); g_assert(qlist_empty(qlist)); - QDECREF(qstr); - qobject_decref(any); + qobject_unref(qstr); + qobject_unref(any); visit_check_struct(v, &error_abort); visit_end_struct(v, NULL); visit_free(v); diff --git a/tests/test-netfilter.c b/tests/test-netfilter.c index 95f7839aef..e47075dd06 100644 --- a/tests/test-netfilter.c +++ b/tests/test-netfilter.c @@ -29,7 +29,7 @@ static void add_one_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-del'," " 'arguments': {" @@ -37,7 +37,7 @@ static void add_one_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } /* add a netfilter to a netdev and then remove the netdev */ @@ -57,7 +57,7 @@ static void remove_netdev_with_one_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'netdev_del'," " 'arguments': {" @@ -65,7 +65,7 @@ static void remove_netdev_with_one_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); /* add back the netdev */ response = qmp("{'execute': 'netdev_add'," @@ -75,7 +75,7 @@ static void remove_netdev_with_one_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } /* add multi(2) netfilters to a netdev and then remove them */ @@ -95,7 +95,7 @@ static void add_multi_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-add'," " 'arguments': {" @@ -109,7 +109,7 @@ static void add_multi_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-del'," " 'arguments': {" @@ -117,7 +117,7 @@ static void add_multi_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-del'," " 'arguments': {" @@ -125,7 +125,7 @@ static void add_multi_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } /* add multi(2) netfilters to a netdev and then remove the netdev */ @@ -145,7 +145,7 @@ static void remove_netdev_with_multi_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'object-add'," " 'arguments': {" @@ -159,7 +159,7 @@ static void remove_netdev_with_multi_netfilter(void) g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); response = qmp("{'execute': 'netdev_del'," " 'arguments': {" @@ -167,7 +167,7 @@ static void remove_netdev_with_multi_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); /* add back the netdev */ response = qmp("{'execute': 'netdev_add'," @@ -177,7 +177,7 @@ static void remove_netdev_with_multi_netfilter(void) "}}"); g_assert(response); g_assert(!qdict_haskey(response, "error")); - QDECREF(response); + qobject_unref(response); } int main(int argc, char **argv) diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index 2c422abcd4..ef96e84aed 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "qemu/cutils.h" +#include "qemu/units.h" #include "qemu/option.h" #include "qemu/option_int.h" #include "qapi/error.h" @@ -459,8 +459,6 @@ static void test_opts_parse(void) { Error *err = NULL; QemuOpts *opts; - char long_key[129]; - char *params; /* Nothing */ opts = qemu_opts_parse(&opts_list_03, "", false, &error_abort); @@ -471,22 +469,6 @@ static void test_opts_parse(void) g_assert_cmpuint(opts_count(opts), ==, 1); g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); - /* Long key */ - memset(long_key, 'a', 127); - long_key[127] = 'z'; - long_key[128] = 0; - params = g_strdup_printf("%s=v", long_key); - opts = qemu_opts_parse(&opts_list_03, params + 1, NULL, &error_abort); - g_assert_cmpuint(opts_count(opts), ==, 1); - g_assert_cmpstr(qemu_opt_get(opts, long_key + 1), ==, "v"); - - /* Overlong key gets truncated */ - opts = qemu_opts_parse(&opts_list_03, params, NULL, &error_abort); - g_assert(opts_count(opts) == 1); - long_key[127] = 0; - g_assert_cmpstr(qemu_opt_get(opts, long_key), ==, "v"); - g_free(params); - /* Multiple keys, last one wins */ opts = qemu_opts_parse(&opts_list_03, "a=1,b=2,,x,a=3", false, &error_abort); @@ -722,13 +704,12 @@ static void test_opts_parse_size(void) g_assert_cmpuint(opts_count(opts), ==, 3); g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, 8); g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 1536); - g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * M_BYTE); + g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * MiB); opts = qemu_opts_parse(&opts_list_02, "size1=0.1G,size2=16777215T", false, &error_abort); g_assert_cmpuint(opts_count(opts), ==, 2); - g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, G_BYTE / 10); - g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), - ==, 16777215 * T_BYTE); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, GiB / 10); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 16777215ULL * TiB); /* Beyond limit with suffix */ opts = qemu_opts_parse(&opts_list_02, "size1=16777216T", @@ -887,7 +868,7 @@ static void test_opts_to_qdict_basic(void) g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); g_assert_false(qdict_haskey(dict, "number2")); - QDECREF(dict); + qobject_unref(dict); qemu_opts_del(opts); } @@ -914,7 +895,7 @@ static void test_opts_to_qdict_filtered(void) g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); g_assert_false(qdict_haskey(dict, "number2")); g_assert_false(qdict_haskey(dict, "bool1")); - QDECREF(dict); + qobject_unref(dict); dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, false); g_assert(dict != NULL); @@ -924,7 +905,7 @@ static void test_opts_to_qdict_filtered(void) g_assert_false(qdict_haskey(dict, "str3")); g_assert_false(qdict_haskey(dict, "number1")); g_assert_false(qdict_haskey(dict, "number2")); - QDECREF(dict); + qobject_unref(dict); /* Now delete converted options from opts */ dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_01, true); @@ -935,7 +916,7 @@ static void test_opts_to_qdict_filtered(void) g_assert_cmpstr(qdict_get_str(dict, "number1"), ==, "42"); g_assert_false(qdict_haskey(dict, "number2")); g_assert_false(qdict_haskey(dict, "bool1")); - QDECREF(dict); + qobject_unref(dict); dict = qemu_opts_to_qdict_filtered(opts, NULL, &opts_list_02, true); g_assert(dict != NULL); @@ -945,7 +926,7 @@ static void test_opts_to_qdict_filtered(void) g_assert_false(qdict_haskey(dict, "str3")); g_assert_false(qdict_haskey(dict, "number1")); g_assert_false(qdict_haskey(dict, "number2")); - QDECREF(dict); + qobject_unref(dict); g_assert_true(QTAILQ_EMPTY(&opts->head)); @@ -978,13 +959,13 @@ static void test_opts_to_qdict_duplicates(void) dict = qemu_opts_to_qdict(opts, NULL); g_assert(dict != NULL); g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); - QDECREF(dict); + qobject_unref(dict); /* The last one still wins if entries are deleted, and both are deleted */ dict = qemu_opts_to_qdict_filtered(opts, NULL, NULL, true); g_assert(dict != NULL); g_assert_cmpstr(qdict_get_str(dict, "foo"), ==, "b"); - QDECREF(dict); + qobject_unref(dict); g_assert_true(QTAILQ_EMPTY(&opts->head)); diff --git a/tests/test-qga.c b/tests/test-qga.c index e6ab788f31..d638b1571a 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -180,7 +180,7 @@ static void test_qga_sync_delimited(gconstpointer fix) v = qdict_get_int(ret, "return"); g_assert_cmpint(r, ==, v); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_sync(gconstpointer fix) @@ -212,7 +212,7 @@ static void test_qga_sync(gconstpointer fix) v = qdict_get_int(ret, "return"); g_assert_cmpint(r, ==, v); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_ping(gconstpointer fix) @@ -224,7 +224,39 @@ static void test_qga_ping(gconstpointer fix) g_assert_nonnull(ret); qmp_assert_no_error(ret); - QDECREF(ret); + qobject_unref(ret); +} + +static void test_qga_invalid_id(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *error; + const char *class; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}"); + g_assert_nonnull(ret); + + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + g_assert_cmpstr(class, ==, "GenericError"); + + qobject_unref(ret); +} + +static void test_qga_invalid_oob(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *error; + const char *class; + + ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}"); + g_assert_nonnull(ret); + + error = qdict_get_qdict(ret, "error"); + class = qdict_get_try_str(error, "class"); + g_assert_cmpstr(class, ==, "GenericError"); + + qobject_unref(ret); } static void test_qga_invalid_args(gconstpointer fix) @@ -244,7 +276,7 @@ static void test_qga_invalid_args(gconstpointer fix) g_assert_cmpstr(class, ==, "GenericError"); g_assert_cmpstr(desc, ==, "Parameter 'foo' is unexpected"); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_invalid_cmd(gconstpointer fix) @@ -263,7 +295,7 @@ static void test_qga_invalid_cmd(gconstpointer fix) g_assert_cmpstr(class, ==, "CommandNotFound"); g_assert_cmpint(strlen(desc), >, 0); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_info(gconstpointer fix) @@ -280,7 +312,7 @@ static void test_qga_info(gconstpointer fix) version = qdict_get_try_str(val, "version"); g_assert_cmpstr(version, ==, QEMU_VERSION); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_get_vcpus(gconstpointer fix) @@ -300,7 +332,7 @@ static void test_qga_get_vcpus(gconstpointer fix) g_assert(qdict_haskey(qobject_to(QDict, entry->value), "online")); g_assert(qdict_haskey(qobject_to(QDict, entry->value), "logical-id")); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_get_fsinfo(gconstpointer fix) @@ -324,7 +356,7 @@ static void test_qga_get_fsinfo(gconstpointer fix) g_assert(qdict_haskey(qobject_to(QDict, entry->value), "disk")); } - QDECREF(ret); + qobject_unref(ret); } static void test_qga_get_memory_block_info(gconstpointer fix) @@ -344,7 +376,7 @@ static void test_qga_get_memory_block_info(gconstpointer fix) g_assert_cmpint(size, >, 0); } - QDECREF(ret); + qobject_unref(ret); } static void test_qga_get_memory_blocks(gconstpointer fix) @@ -369,7 +401,7 @@ static void test_qga_get_memory_blocks(gconstpointer fix) } } - QDECREF(ret); + qobject_unref(ret); } static void test_qga_network_get_interfaces(gconstpointer fix) @@ -388,7 +420,7 @@ static void test_qga_network_get_interfaces(gconstpointer fix) entry = qlist_first(list); g_assert(qdict_haskey(qobject_to(QDict, entry->value), "name")); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_file_ops(gconstpointer fix) @@ -410,7 +442,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_nonnull(ret); qmp_assert_no_error(ret); id = qdict_get_int(ret, "return"); - QDECREF(ret); + qobject_unref(ret); enc = g_base64_encode(helloworld, sizeof(helloworld)); /* write */ @@ -426,7 +458,7 @@ static void test_qga_file_ops(gconstpointer fix) eof = qdict_get_bool(val, "eof"); g_assert_cmpint(count, ==, sizeof(helloworld)); g_assert_cmpint(eof, ==, 0); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* flush */ @@ -434,7 +466,7 @@ static void test_qga_file_ops(gconstpointer fix) " 'arguments': {'handle': %" PRId64 "} }", id); ret = qmp_fd(fixture->fd, cmd); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* close */ @@ -442,7 +474,7 @@ static void test_qga_file_ops(gconstpointer fix) " 'arguments': {'handle': %" PRId64 "} }", id); ret = qmp_fd(fixture->fd, cmd); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* check content */ @@ -462,7 +494,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_nonnull(ret); qmp_assert_no_error(ret); id = qdict_get_int(ret, "return"); - QDECREF(ret); + qobject_unref(ret); /* read */ cmd = g_strdup_printf("{'execute': 'guest-file-read'," @@ -477,7 +509,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert(eof); g_assert_cmpstr(b64, ==, enc); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); g_free(enc); @@ -493,7 +525,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_cmpint(count, ==, 0); g_assert(eof); g_assert_cmpstr(b64, ==, ""); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* seek */ @@ -508,7 +540,7 @@ static void test_qga_file_ops(gconstpointer fix) eof = qdict_get_bool(val, "eof"); g_assert_cmpint(count, ==, 6); g_assert(!eof); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* partial read */ @@ -527,7 +559,7 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_cmpmem(dec, count, helloworld + 6, sizeof(helloworld) - 6); g_free(dec); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* close */ @@ -535,7 +567,7 @@ static void test_qga_file_ops(gconstpointer fix) " 'arguments': {'handle': %" PRId64 "} }", id); ret = qmp_fd(fixture->fd, cmd); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); } @@ -555,7 +587,7 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert_nonnull(ret); qmp_assert_no_error(ret); id = qdict_get_int(ret, "return"); - QDECREF(ret); + qobject_unref(ret); enc = g_base64_encode(helloworld, sizeof(helloworld)); /* write */ @@ -571,7 +603,7 @@ static void test_qga_file_write_read(gconstpointer fix) eof = qdict_get_bool(val, "eof"); g_assert_cmpint(count, ==, sizeof(helloworld)); g_assert_cmpint(eof, ==, 0); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* read (check implicit flush) */ @@ -586,7 +618,7 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert_cmpint(count, ==, 0); g_assert(eof); g_assert_cmpstr(b64, ==, ""); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* seek to 0 */ @@ -601,7 +633,7 @@ static void test_qga_file_write_read(gconstpointer fix) eof = qdict_get_bool(val, "eof"); g_assert_cmpint(count, ==, 0); g_assert(!eof); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); /* read */ @@ -616,7 +648,7 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert_cmpint(count, ==, sizeof(helloworld)); g_assert(eof); g_assert_cmpstr(b64, ==, enc); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); g_free(enc); @@ -625,7 +657,7 @@ static void test_qga_file_write_read(gconstpointer fix) " 'arguments': {'handle': %" PRId64 "} }", id); ret = qmp_fd(fixture->fd, cmd); - QDECREF(ret); + qobject_unref(ret); g_free(cmd); } @@ -642,7 +674,7 @@ static void test_qga_get_time(gconstpointer fix) time = qdict_get_int(ret, "return"); g_assert_cmpint(time, >, 0); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_blacklist(gconstpointer data) @@ -661,7 +693,7 @@ static void test_qga_blacklist(gconstpointer data) desc = qdict_get_try_str(error, "desc"); g_assert_cmpstr(class, ==, "GenericError"); g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); - QDECREF(ret); + qobject_unref(ret); ret = qmp_fd(fix.fd, "{'execute': 'guest-get-time'}"); g_assert_nonnull(ret); @@ -670,12 +702,12 @@ static void test_qga_blacklist(gconstpointer data) desc = qdict_get_try_str(error, "desc"); g_assert_cmpstr(class, ==, "GenericError"); g_assert_nonnull(g_strstr_len(desc, -1, "has been disabled")); - QDECREF(ret); + qobject_unref(ret); /* check something work */ ret = qmp_fd(fix.fd, "{'execute': 'guest-get-fsinfo'}"); qmp_assert_no_error(ret); - QDECREF(ret); + qobject_unref(ret); fixture_tear_down(&fix, NULL); } @@ -744,12 +776,10 @@ static void test_qga_config(gconstpointer data) strv = g_key_file_get_string_list(kf, "general", "blacklist", &n, &error); g_assert_cmpint(n, ==, 2); -#if GLIB_CHECK_VERSION(2, 44, 0) g_assert_true(g_strv_contains((const char * const *)strv, "guest-ping")); g_assert_true(g_strv_contains((const char * const *)strv, "guest-get-time")); -#endif g_assert_no_error(error); g_strfreev(strv); @@ -772,7 +802,7 @@ static void test_qga_fsfreeze_status(gconstpointer fix) status = qdict_get_try_str(ret, "return"); g_assert_cmpstr(status, ==, "thawed"); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_guest_exec(gconstpointer fix) @@ -795,7 +825,7 @@ static void test_qga_guest_exec(gconstpointer fix) val = qdict_get_qdict(ret, "return"); pid = qdict_get_int(val, "pid"); g_assert_cmpint(pid, >, 0); - QDECREF(ret); + qobject_unref(ret); /* wait for completion */ now = g_get_monotonic_time(); @@ -807,7 +837,7 @@ static void test_qga_guest_exec(gconstpointer fix) val = qdict_get_qdict(ret, "return"); exited = qdict_get_bool(val, "exited"); if (!exited) { - QDECREF(ret); + qobject_unref(ret); } } while (!exited && g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); @@ -822,7 +852,7 @@ static void test_qga_guest_exec(gconstpointer fix) g_assert_cmpint(len, ==, 12); g_assert_cmpstr((char *)decoded, ==, "\" test_str \""); g_free(decoded); - QDECREF(ret); + qobject_unref(ret); } static void test_qga_guest_exec_invalid(gconstpointer fix) @@ -841,7 +871,7 @@ static void test_qga_guest_exec_invalid(gconstpointer fix) desc = qdict_get_str(error, "desc"); g_assert_cmpstr(class, ==, "GenericError"); g_assert_cmpint(strlen(desc), >, 0); - QDECREF(ret); + qobject_unref(ret); /* invalid pid */ ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec-status'," @@ -853,7 +883,55 @@ static void test_qga_guest_exec_invalid(gconstpointer fix) desc = qdict_get_str(error, "desc"); g_assert_cmpstr(class, ==, "GenericError"); g_assert_cmpint(strlen(desc), >, 0); - QDECREF(ret); + qobject_unref(ret); +} + +static void test_qga_guest_get_host_name(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-host-name'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + val = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(val, "host-name")); + + qobject_unref(ret); +} + +static void test_qga_guest_get_timezone(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret, *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-timezone'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* Make sure there's at least offset */ + val = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(val, "offset")); + + qobject_unref(ret); +} + +static void test_qga_guest_get_users(gconstpointer fix) +{ + const TestFixture *fixture = fix; + QDict *ret; + QList *val; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-users'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + /* There is not much to test here */ + val = qdict_get_qlist(ret, "return"); + g_assert_nonnull(val); + + qobject_unref(ret); } static void test_qga_guest_get_osinfo(gconstpointer data) @@ -905,7 +983,7 @@ static void test_qga_guest_get_osinfo(gconstpointer data) g_assert_nonnull(str); g_assert_cmpstr(str, ==, "unit-test"); - QDECREF(ret); + qobject_unref(ret); g_free(env[0]); fixture_tear_down(&fixture, NULL); } @@ -936,6 +1014,8 @@ int main(int argc, char **argv) g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops); g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read); g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time); + g_test_add_data_func("/qga/invalid-id", &fix, test_qga_invalid_id); + g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); g_test_add_data_func("/qga/fsfreeze-status", &fix, @@ -948,6 +1028,12 @@ int main(int argc, char **argv) test_qga_guest_exec_invalid); g_test_add_data_func("/qga/guest-get-osinfo", &fix, test_qga_guest_get_osinfo); + g_test_add_data_func("/qga/guest-get-host-name", &fix, + test_qga_guest_get_host_name); + g_test_add_data_func("/qga/guest-get-timezone", &fix, + test_qga_guest_get_timezone); + g_test_add_data_func("/qga/guest-get-users", &fix, + test_qga_guest_get_users); ret = g_test_run(); diff --git a/tests/test-qht.c b/tests/test-qht.c index 9b7423abb6..dda6a067be 100644 --- a/tests/test-qht.c +++ b/tests/test-qht.c @@ -13,10 +13,10 @@ static struct qht ht; static int32_t arr[N * 2]; -static bool is_equal(const void *obj, const void *userp) +static bool is_equal(const void *ap, const void *bp) { - const int32_t *a = obj; - const int32_t *b = userp; + const int32_t *a = ap; + const int32_t *b = bp; return *a == *b; } @@ -27,11 +27,17 @@ static void insert(int a, int b) for (i = a; i < b; i++) { uint32_t hash; + void *existing; + bool inserted; arr[i] = i; hash = i; - qht_insert(&ht, &arr[i], hash); + inserted = qht_insert(&ht, &arr[i], hash, NULL); + g_assert_true(inserted); + inserted = qht_insert(&ht, &arr[i], hash, &existing); + g_assert_false(inserted); + g_assert_true(existing == &arr[i]); } } @@ -60,7 +66,12 @@ static void check(int a, int b, bool expected) val = i; hash = i; - p = qht_lookup(&ht, is_equal, &val, hash); + /* test both lookup variants; results should be the same */ + if (i % 2) { + p = qht_lookup(&ht, &val, hash); + } else { + p = qht_lookup_custom(&ht, &val, hash, is_equal); + } g_assert_true(!!p == expected); } rcu_read_unlock(); @@ -102,7 +113,7 @@ static void qht_do_test(unsigned int mode, size_t init_entries) /* under KVM we might fetch stats from an uninitialized qht */ check_n(0); - qht_init(&ht, 0, mode); + qht_init(&ht, is_equal, 0, mode); check_n(0); insert(0, N); diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index db690cc5ae..ba41a6161e 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -12,11 +12,23 @@ static QmpCommandList qmp_commands; +#if defined(TEST_IF_STRUCT) && defined(TEST_IF_CMD) +UserDefThree *qmp_TestIfCmd(TestIfStruct *foo, Error **errp) +{ + return NULL; +} +#endif + +UserDefThree *qmp_TestCmdReturnDefThree(Error **errp) +{ + return NULL; +} + void qmp_user_def_cmd(Error **errp) { } -void qmp_an_oob_command(Error **errp) +void qmp_test_flags_command(Error **errp) { } @@ -98,16 +110,16 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a, static void test_dispatch_cmd(void) { QDict *req = qdict_new(); - QObject *resp; + QDict *resp; qdict_put_str(req, "execute", "user_def_cmd"); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req)); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); - assert(!qdict_haskey(qobject_to(QDict, resp), "error")); + assert(!qdict_haskey(resp, "error")); - qobject_decref(resp); - QDECREF(req); + qobject_unref(resp); + qobject_unref(req); } /* test commands that return an error due to invalid parameters */ @@ -115,16 +127,16 @@ static void test_dispatch_cmd_failure(void) { QDict *req = qdict_new(); QDict *args = qdict_new(); - QObject *resp; + QDict *resp; qdict_put_str(req, "execute", "user_def_cmd2"); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req)); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); - assert(qdict_haskey(qobject_to(QDict, resp), "error")); + assert(qdict_haskey(resp, "error")); - qobject_decref(resp); - QDECREF(req); + qobject_unref(resp); + qobject_unref(req); /* check that with extra arguments it throws an error */ req = qdict_new(); @@ -133,28 +145,25 @@ static void test_dispatch_cmd_failure(void) qdict_put_str(req, "execute", "user_def_cmd"); - resp = qmp_dispatch(&qmp_commands, QOBJECT(req)); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp != NULL); - assert(qdict_haskey(qobject_to(QDict, resp), "error")); + assert(qdict_haskey(resp, "error")); - qobject_decref(resp); - QDECREF(req); + qobject_unref(resp); + qobject_unref(req); } static QObject *test_qmp_dispatch(QDict *req) { - QObject *resp_obj; QDict *resp; QObject *ret; - resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req)); - assert(resp_obj); - resp = qobject_to(QDict, resp_obj); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); assert(resp && !qdict_haskey(resp, "error")); ret = qdict_get(resp, "return"); assert(ret); - qobject_incref(ret); - qobject_decref(resp_obj); + qobject_ref(ret); + qobject_unref(resp); return ret; } @@ -195,7 +204,7 @@ static void test_dispatch_cmd_io(void) assert(qdict_get_int(ret_dict_dict2_userdef, "integer") == 422); assert(!strcmp(qdict_get_str(ret_dict_dict2_userdef, "string"), "hello2")); assert(!strcmp(qdict_get_str(ret_dict_dict2, "string"), "blah4")); - QDECREF(ret); + qobject_unref(ret); qdict_put_int(args3, "a", 66); qdict_put(req, "arguments", args3); @@ -204,9 +213,9 @@ static void test_dispatch_cmd_io(void) ret3 = qobject_to(QNum, test_qmp_dispatch(req)); g_assert(qnum_get_try_int(ret3, &val)); g_assert_cmpint(val, ==, 66); - QDECREF(ret3); + qobject_unref(ret3); - QDECREF(req); + qobject_unref(req); } /* test generated dealloc functions for generated types */ @@ -257,7 +266,7 @@ static void test_dealloc_partial(void) v = qobject_input_visitor_new(QOBJECT(ud2_dict)); visit_type_UserDefTwo(v, NULL, &ud2, &err); visit_free(v); - QDECREF(ud2_dict); + qobject_unref(ud2_dict); } /* verify that visit_type_XXX() cleans up properly on error */ diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index bb1036615f..8677094ad1 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -32,7 +32,7 @@ typedef struct QDictCmpData { } QDictCmpData; TestEventData *test_event_data; -static CompatGMutex test_event_lock; +static GMutex test_event_lock; /* Only compares bool, int, string */ static @@ -133,7 +133,7 @@ static void event_prepare(TestEventData *data, static void event_teardown(TestEventData *data, const void *unused) { - QDECREF(data->expect); + qobject_unref(data->expect); test_event_data = NULL; g_mutex_unlock(&test_event_lock); @@ -242,12 +242,6 @@ static void test_event_d(TestEventData *data, int main(int argc, char **argv) { -#if !GLIB_CHECK_VERSION(2, 31, 0) - if (!g_thread_supported()) { - g_thread_init(NULL); - } -#endif - qmp_event_set_func_emit(event_test_emit); g_test_init(&argc, &argv, NULL); diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c index 6dc59c6211..0f4d234c3f 100644 --- a/tests/test-qobject-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -35,7 +35,7 @@ typedef struct TestInputVisitorData { static void visitor_input_teardown(TestInputVisitorData *data, const void *unused) { - qobject_decref(data->obj); + qobject_unref(data->obj); data->obj = NULL; if (data->qiv) { @@ -483,7 +483,7 @@ static void test_visitor_in_any(TestInputVisitorData *data, g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, -42); - qobject_decref(res); + qobject_unref(res); v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }"); visit_type_any(v, NULL, &res, &error_abort); @@ -505,7 +505,7 @@ static void test_visitor_in_any(TestInputVisitorData *data, qstring = qobject_to(QString, qobj); g_assert(qstring); g_assert_cmpstr(qstring_get_str(qstring), ==, "foo"); - qobject_decref(res); + qobject_unref(res); } static void test_visitor_in_null(TestInputVisitorData *data, @@ -530,7 +530,7 @@ static void test_visitor_in_null(TestInputVisitorData *data, visit_start_struct(v, NULL, NULL, 0, &error_abort); visit_type_null(v, "a", &null, &error_abort); g_assert(qobject_type(QOBJECT(null)) == QTYPE_QNULL); - QDECREF(null); + qobject_unref(null); visit_type_null(v, "b", &null, &err); error_free_or_abort(&err); g_assert(!null); @@ -1262,7 +1262,7 @@ static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data, g_assert(schema); qapi_free_SchemaInfoList(schema); - qobject_decref(obj); + qobject_unref(obj); visit_free(v); } diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c index ecf21c0f31..be635854b4 100644 --- a/tests/test-qobject-output-visitor.c +++ b/tests/test-qobject-output-visitor.c @@ -40,7 +40,7 @@ static void visitor_output_teardown(TestOutputVisitorData *data, { visit_free(data->ov); data->ov = NULL; - qobject_decref(data->obj); + qobject_unref(data->obj); data->obj = NULL; } @@ -346,7 +346,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, g_assert(qnum); g_assert(qnum_get_try_int(qnum, &val)); g_assert_cmpint(val, ==, -42); - qobject_decref(qobj); + qobject_unref(qobj); visitor_reset(data); qdict = qdict_new(); @@ -355,7 +355,7 @@ static void test_visitor_out_any(TestOutputVisitorData *data, qdict_put_str(qdict, "string", "foo"); qobj = QOBJECT(qdict); visit_type_any(data->ov, NULL, &qobj, &error_abort); - qobject_decref(qobj); + qobject_unref(qobj); qdict = qobject_to(QDict, visitor_get(data)); g_assert(qdict); qnum = qobject_to(QNum, qdict_get(qdict, "integer")); @@ -630,7 +630,7 @@ static void check_native_list(QObject *qobj, qvalue = qobject_to(QNum, tmp); g_assert(qnum_get_try_uint(qvalue, &val)); g_assert_cmpint(val, ==, i); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); } break; @@ -654,7 +654,7 @@ static void check_native_list(QObject *qobj, qvalue = qobject_to(QNum, tmp); g_assert(qnum_get_try_int(qvalue, &val)); g_assert_cmpint(val, ==, i); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); } break; case USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN: @@ -665,7 +665,7 @@ static void check_native_list(QObject *qobj, g_assert(tmp); qvalue = qobject_to(QBool, tmp); g_assert_cmpint(qbool_get_bool(qvalue), ==, i % 3 == 0); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); } break; case USER_DEF_NATIVE_LIST_UNION_KIND_STRING: @@ -678,7 +678,7 @@ static void check_native_list(QObject *qobj, qvalue = qobject_to(QString, tmp); sprintf(str, "%d", i); g_assert_cmpstr(qstring_get_str(qvalue), ==, str); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); } break; case USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER: @@ -695,7 +695,7 @@ static void check_native_list(QObject *qobj, g_string_printf(double_actual, "%.6f", qnum_get_double(qvalue)); g_assert_cmpstr(double_actual->str, ==, double_expected->str); - qobject_decref(qlist_pop(qlist)); + qobject_unref(qlist_pop(qlist)); g_string_free(double_expected, true); g_string_free(double_actual, true); } @@ -703,7 +703,7 @@ static void check_native_list(QObject *qobj, default: g_assert_not_reached(); } - QDECREF(qlist); + qobject_unref(qlist); } static void test_native_list(TestOutputVisitorData *data, diff --git a/tests/test-replication.c b/tests/test-replication.c index 68c0d04f2a..c8165ae954 100644 --- a/tests/test-replication.c +++ b/tests/test-replication.c @@ -15,6 +15,7 @@ #include "qemu/option.h" #include "replication.h" #include "block/block_int.h" +#include "block/qdict.h" #include "sysemu/block-backend.h" #define IMG_SIZE (64 * 1024 * 1024) diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c index 91b4ec5524..9cdccb3a47 100644 --- a/tests/test-thread-pool.c +++ b/tests/test-thread-pool.c @@ -224,8 +224,6 @@ static void test_cancel_async(void) int main(int argc, char **argv) { - int ret; - qemu_init_main_loop(&error_abort); ctx = qemu_get_current_aio_context(); pool = aio_get_thread_pool(ctx); @@ -238,7 +236,5 @@ int main(int argc, char **argv) g_test_add_func("/thread-pool/cancel", test_cancel); g_test_add_func("/thread-pool/cancel-async", test_cancel_async); - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/tests/test-util-sockets.c b/tests/test-util-sockets.c index acadd85e8f..6195a3ac36 100644 --- a/tests/test-util-sockets.c +++ b/tests/test-util-sockets.c @@ -69,7 +69,7 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) * stubs/monitor.c is defined, to make sure monitor.o is discarded * otherwise we get duplicate syms at link time. */ -Monitor *cur_mon; +__thread Monitor *cur_mon; void monitor_init(Chardev *chr, int flags) {} diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index d18d90db2c..1c5a8b94ea 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -1036,10 +1036,10 @@ static void qmp_deserialize(void **native_out, void *datap, output_json = qobject_to_json(obj_orig); obj = qobject_from_json(qstring_get_str(output_json), &error_abort); - QDECREF(output_json); + qobject_unref(output_json); d->qiv = qobject_input_visitor_new(obj); - qobject_decref(obj_orig); - qobject_decref(obj); + qobject_unref(obj_orig); + qobject_unref(obj); visit(d->qiv, native_out, errp); } diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index 087844b6c8..37a7a93784 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -859,6 +859,8 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); + setenv("QTEST_SILENT_ERRORS", "1", 1); + g_test_init(&argc, &argv, NULL); g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c index 02e41843fc..84ce9c71ae 100644 --- a/tests/test-x86-cpuid-compat.c +++ b/tests/test-x86-cpuid-compat.c @@ -19,7 +19,7 @@ static char *get_cpu0_qom_path(void) cpu0 = qobject_to(QDict, qlist_peek(ret)); path = g_strdup(qdict_get_str(cpu0, "qom_path")); - QDECREF(resp); + qobject_unref(resp); return path; } @@ -30,8 +30,8 @@ static QObject *qom_get(const char *path, const char *prop) " 'property': %s } }", path, prop); QObject *ret = qdict_get(resp, "return"); - qobject_incref(ret); - QDECREF(resp); + qobject_ref(ret); + qobject_unref(resp); return ret; } @@ -41,7 +41,7 @@ static bool qom_get_bool(const char *path, const char *prop) QBool *value = qobject_to(QBool, qom_get(path, prop)); bool b = qbool_get_bool(value); - QDECREF(value); + qobject_unref(value); return b; } #endif @@ -66,7 +66,7 @@ static void test_cpuid_prop(const void *data) g_assert_cmpint(val, ==, args->expected_value); qtest_end(); - QDECREF(value); + qobject_unref(value); g_free(path); } @@ -142,8 +142,8 @@ static void test_feature_flag(const void *data) g_assert(!!(value & (1U << args->bitnr)) == args->expected_value); - QDECREF(present); - QDECREF(filtered); + qobject_unref(present); + qobject_unref(filtered); g_free(path); } diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c index 66c7a0147f..34cae7a582 100644 --- a/tests/tmp105-test.c +++ b/tests/tmp105-test.c @@ -14,8 +14,6 @@ #include "qapi/qmp/qdict.h" #include "hw/misc/tmp105_regs.h" -#define OMAP2_I2C_1_BASE 0x48070000 - #define TMP105_TEST_ID "tmp105-test" #define TMP105_TEST_ADDR 0x49 @@ -74,7 +72,7 @@ static int qmp_tmp105_get_temperature(const char *id) "'property': 'temperature' } }", id); g_assert(qdict_haskey(response, "return")); ret = qdict_get_int(response, "return"); - QDECREF(response); + qobject_unref(response); return ret; } @@ -85,7 +83,7 @@ static void qmp_tmp105_set_temperature(const char *id, int value) response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, " "'property': 'temperature', 'value': %d } }", id, value); g_assert(qdict_haskey(response, "return")); - QDECREF(response); + qobject_unref(response); } #define TMP105_PRECISION (1000/16) diff --git a/tests/tpm-crb-swtpm-test.c b/tests/tpm-crb-swtpm-test.c new file mode 100644 index 0000000000..8c0a55f3ca --- /dev/null +++ b/tests/tpm-crb-swtpm-test.c @@ -0,0 +1,66 @@ +/* + * QTest testcase for TPM CRB talking to external swtpm and swtpm migration + * + * Copyright (c) 2018 IBM Corporation + * with parts borrowed from migration-test.c that is: + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "libqtest.h" +#include "tpm-tests.h" + +typedef struct TestState { + char *src_tpm_path; + char *dst_tpm_path; + char *uri; +} TestState; + +static void tpm_crb_swtpm_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_crb_transfer, "tpm-crb"); +} + +static void tpm_crb_swtpm_migration_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, + tpm_util_crb_transfer, "tpm-crb"); +} + +int main(int argc, char **argv) +{ + int ret; + TestState ts = { 0 }; + + ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); + ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); + ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test); + qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts, + tpm_crb_swtpm_migration_test); + ret = g_test_run(); + + g_rmdir(ts.dst_tpm_path); + g_free(ts.dst_tpm_path); + g_rmdir(ts.src_tpm_path); + g_free(ts.src_tpm_path); + g_free(ts.uri); + + return ret; +} diff --git a/tests/tpm-emu.c b/tests/tpm-emu.c index 4dada76834..8c2bd53cad 100644 --- a/tests/tpm-emu.c +++ b/tests/tpm-emu.c @@ -125,7 +125,7 @@ void *tpm_emu_ctrl_thread(void *data) case CMD_SHUTDOWN: { ptm_res res = 0; qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); - qio_channel_close(s->tpm_ioc, &error_abort); + /* the tpm data thread is expected to finish now */ g_thread_join(s->emu_tpm_thread); break; } diff --git a/tests/tpm-emu.h b/tests/tpm-emu.h index ef4bfa8800..08f902485e 100644 --- a/tests/tpm-emu.h +++ b/tests/tpm-emu.h @@ -24,8 +24,8 @@ struct tpm_hdr { } QEMU_PACKED; typedef struct TestState { - CompatGMutex data_mutex; - CompatGCond data_cond; + GMutex data_mutex; + GCond data_cond; SocketAddress *addr; QIOChannel *tpm_ioc; GThread *emu_tpm_thread; diff --git a/tests/tpm-tests.c b/tests/tpm-tests.c new file mode 100644 index 0000000000..10c6592aac --- /dev/null +++ b/tests/tpm-tests.c @@ -0,0 +1,127 @@ +/* + * QTest TPM commont test code + * + * Copyright (c) 2018 IBM Corporation + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Stefan Berger + * Marc-André Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "libqtest.h" +#include "tpm-tests.h" + +void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, + const char *ifmodel) +{ + char *args = NULL; + QTestState *s; + SocketAddress *addr = NULL; + gboolean succ; + GPid swtpm_pid; + GError *error = NULL; + + succ = tpm_util_swtpm_start(src_tpm_path, &swtpm_pid, &addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + return; + } + + args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device %s,tpmdev=dev", + addr->u.q_unix.path, ifmodel); + + s = qtest_start(args); + g_free(args); + + tpm_util_startup(s, tx); + tpm_util_pcrextend(s, tx); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(s, tx, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_end(); + tpm_util_swtpm_kill(swtpm_pid); + + if (addr) { + g_unlink(addr->u.q_unix.path); + qapi_free_SocketAddress(addr); + } +} + +void tpm_test_swtpm_migration_test(const char *src_tpm_path, + const char *dst_tpm_path, + const char *uri, tx_func *tx, + const char *ifmodel) +{ + gboolean succ; + GPid src_tpm_pid, dst_tpm_pid; + SocketAddress *src_tpm_addr = NULL, *dst_tpm_addr = NULL; + GError *error = NULL; + QTestState *src_qemu, *dst_qemu; + + succ = tpm_util_swtpm_start(src_tpm_path, &src_tpm_pid, + &src_tpm_addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + return; + } + + succ = tpm_util_swtpm_start(dst_tpm_path, &dst_tpm_pid, + &dst_tpm_addr, &error); + /* succ may be false if swtpm is not available */ + if (!succ) { + goto err_src_tpm_kill; + } + + tpm_util_migration_start_qemu(&src_qemu, &dst_qemu, + src_tpm_addr, dst_tpm_addr, uri, + ifmodel); + + tpm_util_startup(src_qemu, tx); + tpm_util_pcrextend(src_qemu, tx); + + unsigned char tpm_pcrread_resp[] = + "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" + "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" + "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" + "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; + tpm_util_pcrread(src_qemu, tx, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + tpm_util_migrate(src_qemu, uri); + tpm_util_wait_for_migration_complete(src_qemu); + + tpm_util_pcrread(dst_qemu, tx, tpm_pcrread_resp, + sizeof(tpm_pcrread_resp)); + + qtest_quit(dst_qemu); + qtest_quit(src_qemu); + + tpm_util_swtpm_kill(dst_tpm_pid); + if (dst_tpm_addr) { + g_unlink(dst_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(dst_tpm_addr); + } + +err_src_tpm_kill: + tpm_util_swtpm_kill(src_tpm_pid); + if (src_tpm_addr) { + g_unlink(src_tpm_addr->u.q_unix.path); + qapi_free_SocketAddress(src_tpm_addr); + } +} diff --git a/tests/tpm-tests.h b/tests/tpm-tests.h new file mode 100644 index 0000000000..b97688fe75 --- /dev/null +++ b/tests/tpm-tests.h @@ -0,0 +1,26 @@ +/* + * QTest TPM commont test code + * + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Stefan Berger + * + * 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 TESTS_TPM_TESTS_H +#define TESTS_TPM_TESTS_H + +#include "tpm-util.h" + +void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, + const char *ifmodel); + +void tpm_test_swtpm_migration_test(const char *src_tpm_path, + const char *dst_tpm_path, + const char *uri, tx_func *tx, + const char *ifmodel); + +#endif /* TESTS_TPM_TESTS_H */ diff --git a/tests/tpm-tis-swtpm-test.c b/tests/tpm-tis-swtpm-test.c new file mode 100644 index 0000000000..7dcd1d3912 --- /dev/null +++ b/tests/tpm-tis-swtpm-test.c @@ -0,0 +1,66 @@ +/* + * QTest testcase for TPM TIS talking to external swtpm and swtpm migration + * + * Copyright (c) 2018 IBM Corporation + * with parts borrowed from migration-test.c that is: + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Stefan Berger + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include + +#include "libqtest.h" +#include "tpm-tests.h" + +typedef struct TestState { + char *src_tpm_path; + char *dst_tpm_path; + char *uri; +} TestState; + +static void tpm_tis_swtpm_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_test(ts->src_tpm_path, tpm_util_tis_transfer, "tpm-tis"); +} + +static void tpm_tis_swtpm_migration_test(const void *data) +{ + const TestState *ts = data; + + tpm_test_swtpm_migration_test(ts->src_tpm_path, ts->dst_tpm_path, ts->uri, + tpm_util_tis_transfer, "tpm-tis"); +} + +int main(int argc, char **argv) +{ + int ret; + TestState ts = { 0 }; + + ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-tis-swtpm-test.XXXXXX", NULL); + ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-tis-swtpm-test.XXXXXX", NULL); + ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); + + module_call_init(MODULE_INIT_QOM); + g_test_init(&argc, &argv, NULL); + + qtest_add_data_func("/tpm/tis-swtpm/test", &ts, tpm_tis_swtpm_test); + qtest_add_data_func("/tpm/tis-swtpm-migration/test", &ts, + tpm_tis_swtpm_migration_test); + ret = g_test_run(); + + g_rmdir(ts.dst_tpm_path); + g_free(ts.dst_tpm_path); + g_rmdir(ts.src_tpm_path); + g_free(ts.src_tpm_path); + g_free(ts.uri); + + return ret; +} diff --git a/tests/tpm-util.c b/tests/tpm-util.c new file mode 100644 index 0000000000..672cedf905 --- /dev/null +++ b/tests/tpm-util.c @@ -0,0 +1,324 @@ +/* + * QTest TPM utilities + * + * Copyright (c) 2018 IBM Corporation + * Copyright (c) 2018 Red Hat, Inc. + * + * Authors: + * Stefan Berger + * Marc-André Lureau + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" + +#include "hw/acpi/tpm.h" +#include "libqtest.h" +#include "tpm-util.h" +#include "qapi/qmp/qdict.h" + +#define TIS_REG(LOCTY, REG) \ + (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) + +static bool got_stop; + +void tpm_util_crb_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size) +{ + uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); + uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); + + qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); + + qtest_memwrite(s, caddr, req, req_size); + + uint32_t sts, start = 1; + uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); + while (true) { + start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + if ((start & 1) == 0) { + break; + } + if (g_get_monotonic_time() >= end_time) { + break; + } + }; + start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); + g_assert_cmpint(start & 1, ==, 0); + sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); + g_assert_cmpint(sts & 1, ==, 0); + + qtest_memread(s, raddr, rsp, rsp_size); +} + +void tpm_util_tis_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size) +{ + uint32_t sts; + uint16_t bcount; + size_t i; + + /* request use of locality 0 */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); + qtest_writel(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); + + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + bcount = (sts >> 8) & 0xffff; + g_assert_cmpint(bcount, >=, req_size); + + /* transmit command */ + for (i = 0; i < req_size; i++) { + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO), req[i]); + } + + /* start processing */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); + + uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; + do { + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { + break; + } + } while (g_get_monotonic_time() < end_time); + + sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); + bcount = (sts >> 8) & 0xffff; + + memset(rsp, 0, rsp_size); + for (i = 0; i < bcount; i++) { + rsp[i] = qtest_readb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); + } + + /* relinquish use of locality 0 */ + qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), + TPM_TIS_ACCESS_ACTIVE_LOCALITY); +} + +void tpm_util_startup(QTestState *s, tx_func *tx) +{ + unsigned char buffer[1024]; + unsigned char tpm_startup[] = + "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; + unsigned char tpm_startup_resp[] = + "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00"; + + tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, sizeof(tpm_startup_resp), + tpm_startup_resp, sizeof(tpm_startup_resp)); +} + +void tpm_util_pcrextend(QTestState *s, tx_func *tx) +{ + unsigned char buffer[1024]; + unsigned char tpm_pcrextend[] = + "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00" + "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" + "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00"; + + unsigned char tpm_pcrextend_resp[] = + "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x01\x00\x00"; + + tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp), + tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp)); +} + +void tpm_util_pcrread(QTestState *s, tx_func *tx, + const unsigned char *exp_resp, size_t exp_resp_size) +{ + unsigned char buffer[1024]; + unsigned char tpm_pcrread[] = + "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b" + "\x03\x00\x04\x00"; + + tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer)); + + g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size); +} + +static gboolean tpm_util_swtpm_has_tpm2(void) +{ + gint mystdout; + gboolean succ; + unsigned i; + char buffer[10240]; + ssize_t n; + gchar *swtpm_argv[] = { + g_strdup("swtpm"), g_strdup("socket"), g_strdup("--help"), NULL + }; + + succ = g_spawn_async_with_pipes(NULL, swtpm_argv, NULL, + G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, + NULL, &mystdout, NULL, NULL); + if (!succ) { + goto cleanup; + } + + n = read(mystdout, buffer, sizeof(buffer) - 1); + if (n < 0) { + goto cleanup; + } + buffer[n] = 0; + if (!strstr(buffer, "--tpm2")) { + succ = false; + } + + cleanup: + for (i = 0; swtpm_argv[i]; i++) { + g_free(swtpm_argv[i]); + } + + return succ; +} + +gboolean tpm_util_swtpm_start(const char *path, GPid *pid, + SocketAddress **addr, GError **error) +{ + char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path); + char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock", + path); + gchar *swtpm_argv[] = { + g_strdup("swtpm"), g_strdup("socket"), + g_strdup("--tpmstate"), swtpm_argv_tpmstate, + g_strdup("--ctrl"), swtpm_argv_ctrl, + g_strdup("--tpm2"), + NULL + }; + gboolean succ; + unsigned i; + + succ = tpm_util_swtpm_has_tpm2(); + if (!succ) { + goto cleanup; + } + + *addr = g_new0(SocketAddress, 1); + (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX; + (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL); + + succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH, + NULL, NULL, pid, error); + +cleanup: + for (i = 0; swtpm_argv[i]; i++) { + g_free(swtpm_argv[i]); + } + + return succ; +} + +void tpm_util_swtpm_kill(GPid pid) +{ + int n; + + if (!pid) { + return; + } + + g_spawn_close_pid(pid); + + n = kill(pid, 0); + if (n < 0) { + return; + } + + kill(pid, SIGKILL); +} + +void tpm_util_migrate(QTestState *who, const char *uri) +{ + QDict *rsp; + gchar *cmd; + + cmd = g_strdup_printf("{ 'execute': 'migrate'," + "'arguments': { 'uri': '%s' } }", + uri); + rsp = qtest_qmp(who, cmd); + g_free(cmd); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); +} + +/* + * Events can get in the way of responses we are actually waiting for. + */ +static QDict *tpm_util_wait_command(QTestState *who, const char *command) +{ + const char *event_string; + QDict *response; + + response = qtest_qmp(who, command); + + while (qdict_haskey(response, "event")) { + /* OK, it was an event */ + event_string = qdict_get_str(response, "event"); + if (!strcmp(event_string, "STOP")) { + got_stop = true; + } + qobject_unref(response); + response = qtest_qmp_receive(who); + } + return response; +} + +void tpm_util_wait_for_migration_complete(QTestState *who) +{ + while (true) { + QDict *rsp, *rsp_return; + bool completed; + const char *status; + + rsp = tpm_util_wait_command(who, "{ 'execute': 'query-migrate' }"); + rsp_return = qdict_get_qdict(rsp, "return"); + status = qdict_get_str(rsp_return, "status"); + completed = strcmp(status, "completed") == 0; + g_assert_cmpstr(status, !=, "failed"); + qobject_unref(rsp); + if (completed) { + return; + } + usleep(1000); + } +} + +void tpm_util_migration_start_qemu(QTestState **src_qemu, + QTestState **dst_qemu, + SocketAddress *src_tpm_addr, + SocketAddress *dst_tpm_addr, + const char *miguri, + const char *ifmodel) +{ + char *src_qemu_args, *dst_qemu_args; + + src_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device %s,tpmdev=dev ", + src_tpm_addr->u.q_unix.path, ifmodel); + + *src_qemu = qtest_init(src_qemu_args); + + dst_qemu_args = g_strdup_printf( + "-chardev socket,id=chr,path=%s " + "-tpmdev emulator,id=dev,chardev=chr " + "-device %s,tpmdev=dev " + "-incoming %s", + dst_tpm_addr->u.q_unix.path, + ifmodel, miguri); + + *dst_qemu = qtest_init(dst_qemu_args); + + free(src_qemu_args); + free(dst_qemu_args); +} diff --git a/tests/tpm-util.h b/tests/tpm-util.h new file mode 100644 index 0000000000..330b9657fe --- /dev/null +++ b/tests/tpm-util.h @@ -0,0 +1,50 @@ +/* + * QTest TPM utilities + * + * Copyright (c) 2018 IBM Corporation + * + * Authors: + * Stefan Berger + * + * 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 TESTS_TPM_UTIL_H +#define TESTS_TPM_UTIL_H + +#include "qemu/osdep.h" +#include "io/channel-socket.h" + +typedef void (tx_func)(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); + +void tpm_util_crb_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); +void tpm_util_tis_transfer(QTestState *s, + const unsigned char *req, size_t req_size, + unsigned char *rsp, size_t rsp_size); + +void tpm_util_startup(QTestState *s, tx_func *tx); +void tpm_util_pcrextend(QTestState *s, tx_func *tx); +void tpm_util_pcrread(QTestState *s, tx_func *tx, + const unsigned char *exp_resp, size_t exp_resp_size); + +gboolean tpm_util_swtpm_start(const char *path, GPid *pid, + SocketAddress **addr, GError **error); +void tpm_util_swtpm_kill(GPid pid); + +void tpm_util_migrate(QTestState *who, const char *uri); + +void tpm_util_migration_start_qemu(QTestState **src_qemu, + QTestState **dst_qemu, + SocketAddress *src_tpm_addr, + SocketAddress *dst_tpm_addr, + const char *miguri, + const char *ifmodel); + +void tpm_util_wait_for_migration_complete(QTestState *who); + +#endif /* TESTS_TPM_UTIL_H */ diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c index 9c14e3053a..5b1b681bf2 100644 --- a/tests/usb-hcd-xhci-test.c +++ b/tests/usb-hcd-xhci-test.c @@ -35,6 +35,15 @@ static void test_usb_uas_hotplug(void) qtest_qmp_device_del("uas"); } +static void test_usb_ccid_hotplug(void) +{ + qtest_qmp_device_add("usb-ccid", "ccid", NULL); + qtest_qmp_device_del("ccid"); + /* check the device can be added again */ + qtest_qmp_device_add("usb-ccid", "ccid", NULL); + qtest_qmp_device_del("ccid"); +} + int main(int argc, char **argv) { int ret; @@ -44,6 +53,7 @@ int main(int argc, char **argv) qtest_add_func("/xhci/pci/init", test_xhci_init); qtest_add_func("/xhci/pci/hotplug", test_xhci_hotplug); qtest_add_func("/xhci/pci/hotplug/usb-uas", test_usb_uas_hotplug); + qtest_add_func("/xhci/pci/hotplug/usb-ccid", test_usb_ccid_hotplug); qtest_start("-device nec-usb-xhci,id=xhci" " -drive id=drive0,if=none,file=null-co://,format=raw"); diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index e0605a529e..0884294141 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -29,6 +29,7 @@ #define _FILE_OFFSET_BITS 64 +#include "qemu/atomic.h" #include "qemu/osdep.h" #include "qemu/iov.h" #include "standard-headers/linux/virtio_net.h" @@ -65,6 +66,11 @@ typedef struct VubrDev { int sock; int ready; int quit; + struct { + int fd; + void *addr; + pthread_t thread; + } notifier; } VubrDev; static void @@ -445,14 +451,22 @@ static uint64_t vubr_get_features(VuDev *dev) { return 1ULL << VIRTIO_NET_F_GUEST_ANNOUNCE | - 1ULL << VIRTIO_NET_F_MRG_RXBUF; + 1ULL << VIRTIO_NET_F_MRG_RXBUF | + 1ULL << VIRTIO_F_VERSION_1; } static void vubr_queue_set_started(VuDev *dev, int qidx, bool started) { + VubrDev *vubr = container_of(dev, VubrDev, vudev); VuVirtq *vq = vu_get_queue(dev, qidx); + if (started && vubr->notifier.fd >= 0) { + vu_set_queue_host_notifier(dev, vq, vubr->notifier.fd, + getpagesize(), + qidx * getpagesize()); + } + if (qidx % 2 == 1) { vu_set_queue_handler(dev, vq, started ? vubr_handle_tx : NULL); } @@ -522,6 +536,8 @@ vubr_new(const char *path, bool client) vubr_die("socket"); } + dev->notifier.fd = -1; + un.sun_family = AF_UNIX; strcpy(un.sun_path, path); len = sizeof(un.sun_family) + strlen(path); @@ -559,6 +575,73 @@ vubr_new(const char *path, bool client) return dev; } +static void *notifier_thread(void *arg) +{ + VuDev *dev = (VuDev *)arg; + VubrDev *vubr = container_of(dev, VubrDev, vudev); + int pagesize = getpagesize(); + int qidx; + + while (true) { + for (qidx = 0; qidx < VHOST_MAX_NR_VIRTQUEUE; qidx++) { + uint16_t *n = vubr->notifier.addr + pagesize * qidx; + + if (*n == qidx) { + *n = 0xffff; + /* We won't miss notifications if we reset + * the memory first. */ + smp_mb(); + + DPRINT("Got a notification for queue%d via host notifier.\n", + qidx); + + if (qidx % 2 == 1) { + vubr_handle_tx(dev, qidx); + } + } + usleep(1000); + } + } + + return NULL; +} + +static void +vubr_host_notifier_setup(VubrDev *dev) +{ + char template[] = "/tmp/vubr-XXXXXX"; + pthread_t thread; + size_t length; + void *addr; + int fd; + + length = getpagesize() * VHOST_MAX_NR_VIRTQUEUE; + + fd = mkstemp(template); + if (fd < 0) { + vubr_die("mkstemp()"); + } + + if (posix_fallocate(fd, 0, length) != 0) { + vubr_die("posix_fallocate()"); + } + + addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + vubr_die("mmap()"); + } + + memset(addr, 0xff, length); + + if (pthread_create(&thread, NULL, notifier_thread, &dev->vudev) != 0) { + vubr_die("pthread_create()"); + } + + dev->notifier.fd = fd; + dev->notifier.addr = addr; + dev->notifier.thread = thread; +} + static void vubr_set_host(struct sockaddr_in *saddr, const char *host) { @@ -673,8 +756,9 @@ main(int argc, char *argv[]) VubrDev *dev; int opt; bool client = false; + bool host_notifier = false; - while ((opt = getopt(argc, argv, "l:r:u:c")) != -1) { + while ((opt = getopt(argc, argv, "l:r:u:cH")) != -1) { switch (opt) { case 'l': @@ -693,6 +777,9 @@ main(int argc, char *argv[]) case 'c': client = true; break; + case 'H': + host_notifier = true; + break; default: goto out; } @@ -708,6 +795,10 @@ main(int argc, char *argv[]) return 1; } + if (host_notifier) { + vubr_host_notifier_setup(dev); + } + vubr_backend_udp_setup(dev, lhost, lport, rhost, rport); vubr_run(dev); @@ -717,7 +808,7 @@ main(int argc, char *argv[]) out: fprintf(stderr, "Usage: %s ", argv[0]); - fprintf(stderr, "[-c] [-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n"); + fprintf(stderr, "[-c] [-H] [-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n"); fprintf(stderr, "\t-u path to unix doman socket. default: %s\n", DEFAULT_UD_SOCKET); fprintf(stderr, "\t-l local host and port. default: %s:%s\n", @@ -725,6 +816,7 @@ out: fprintf(stderr, "\t-r remote host and port. default: %s:%s\n", DEFAULT_RHOST, DEFAULT_RPORT); fprintf(stderr, "\t-c client mode\n"); + fprintf(stderr, "\t-H use host notifier\n"); return 1; } diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 61d997253c..fecc832d99 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -32,14 +32,6 @@ #include #include -/* GLIB version compatibility flags */ -#if !GLIB_CHECK_VERSION(2, 26, 0) -#define G_TIME_SPAN_SECOND (G_GINT64_CONSTANT(1000000)) -#endif - -#if GLIB_CHECK_VERSION(2, 28, 0) -#define HAVE_MONOTONIC_TIME -#endif #define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM," \ "mem-path=%s,share=on -numa node,memdev=mem" @@ -150,8 +142,8 @@ typedef struct TestServer { int fds_num; int fds[VHOST_MEMORY_MAX_NREGIONS]; VhostUserMemory memory; - CompatGMutex data_mutex; - CompatGCond data_cond; + GMutex data_mutex; + GCond data_cond; int log_fd; uint64_t rings; bool test_fail; @@ -545,6 +537,7 @@ static gboolean _test_server_free(TestServer *server) g_free(server->mig_path); g_free(server->chr_name); + g_assert(server->bus); qpci_free_pc(server->bus); g_free(server); @@ -642,21 +635,7 @@ test_migrate_source_check(GSource *source) return FALSE; } -#if !GLIB_CHECK_VERSION(2,36,0) -/* this callback is unnecessary with glib >2.36, the default - * prepare for the source does the same */ -static gboolean -test_migrate_source_prepare(GSource *source, gint *timeout) -{ - *timeout = -1; - return FALSE; -} -#endif - GSourceFuncs test_migrate_source_funcs = { -#if !GLIB_CHECK_VERSION(2,36,0) - .prepare = test_migrate_source_prepare, -#endif .check = test_migrate_source_check, }; @@ -706,6 +685,7 @@ static void test_migrate(void) g_free(cmd); init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC); + init_virtio_dev(dest, 1u << VIRTIO_NET_F_MAC); wait_for_fds(s); size = get_log_size(s); g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); @@ -727,7 +707,7 @@ static void test_migrate(void) rsp = qmp("{ 'execute': 'migrate_set_speed'," "'arguments': { 'value': 10 } }"); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); cmd = g_strdup_printf("{ 'execute': 'migrate'," "'arguments': { 'uri': '%s' } }", @@ -735,7 +715,7 @@ static void test_migrate(void) rsp = qmp(cmd); g_free(cmd); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); wait_for_log_fd(s); @@ -751,7 +731,7 @@ static void test_migrate(void) rsp = qmp("{ 'execute': 'migrate_set_speed'," "'arguments': { 'value': 0 } }"); g_assert(qdict_haskey(rsp, "return")); - QDECREF(rsp); + qobject_unref(rsp); qmp_eventwait("STOP"); @@ -761,6 +741,7 @@ static void test_migrate(void) read_guest_mem_server(dest); uninit_virtio_dev(s); + uninit_virtio_dev(dest); g_source_destroy(source); g_source_unref(source); diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index 0a3c5dd257..b285a262e9 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -173,7 +173,7 @@ static void rx_stop_cont_test(QVirtioDevice *dev, qvirtqueue_kick(dev, vq, free_head); rsp = qmp("{ 'execute' : 'stop'}"); - QDECREF(rsp); + qobject_unref(rsp); ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); @@ -182,9 +182,9 @@ static void rx_stop_cont_test(QVirtioDevice *dev, * ensure the packet data gets queued in QEMU, before we do 'cont'. */ rsp = qmp("{ 'execute' : 'query-status'}"); - QDECREF(rsp); + qobject_unref(rsp); rsp = qmp("{ 'execute' : 'cont'}"); - QDECREF(rsp); + qobject_unref(rsp); qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_NET_TIMEOUT_US); memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 3a2d508c35..3643117816 100755 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -11,6 +11,7 @@ # the COPYING file in the top-level directory. # +from __future__ import print_function import os import sys import logging @@ -222,7 +223,7 @@ def main(vmcls): try: args, argv = parse_args(vmcls.name) if not argv and not args.build_qemu and not args.build_image: - print "Nothing to do?" + print("Nothing to do?") return 1 logging.basicConfig(level=(logging.DEBUG if args.debug else logging.WARN)) diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c index 2ec274e37c..8d915c610c 100644 --- a/tests/vmgenid-test.c +++ b/tests/vmgenid-test.c @@ -125,7 +125,7 @@ static void read_guid_from_monitor(QemuUUID *guid) guid_str = qdict_get_str(rsp_ret, "guid"); g_assert(qemu_uuid_parse(guid_str, guid) == 0); } - QDECREF(rsp); + qobject_unref(rsp); } static char disk[] = "tests/vmgenid-test-disk-XXXXXX"; diff --git a/tests/wdt_ib700-test.c b/tests/wdt_ib700-test.c index 3b5bbcf007..797288d939 100644 --- a/tests/wdt_ib700-test.c +++ b/tests/wdt_ib700-test.c @@ -16,7 +16,7 @@ static void qmp_check_no_event(QTestState *s) { QDict *resp = qtest_qmp(s, "{'execute':'query-status'}"); g_assert(qdict_haskey(resp, "return")); - QDECREF(resp); + qobject_unref(resp); } static QDict *ib700_program_and_wait(QTestState *s) @@ -48,8 +48,8 @@ static QDict *ib700_program_and_wait(QTestState *s) qtest_clock_step(s, 2 * NANOSECONDS_PER_SECOND); event = qtest_qmp_eventwait_ref(s, "WATCHDOG"); data = qdict_get_qdict(event, "data"); - QINCREF(data); - QDECREF(event); + qobject_ref(data); + qobject_unref(event); return data; } @@ -62,7 +62,7 @@ static void ib700_pause(void) qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "pause")); - QDECREF(d); + qobject_unref(d); qtest_qmp_eventwait(s, "STOP"); qtest_quit(s); } @@ -75,7 +75,7 @@ static void ib700_reset(void) qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); - QDECREF(d); + qobject_unref(d); qtest_qmp_eventwait(s, "RESET"); qtest_quit(s); } @@ -89,7 +89,7 @@ static void ib700_shutdown(void) qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "reset")); - QDECREF(d); + qobject_unref(d); qtest_qmp_eventwait(s, "SHUTDOWN"); qtest_quit(s); } @@ -102,7 +102,7 @@ static void ib700_none(void) qtest_irq_intercept_in(s, "ioapic"); d = ib700_program_and_wait(s); g_assert(!strcmp(qdict_get_str(d, "action"), "none")); - QDECREF(d); + qobject_unref(d); qtest_quit(s); } diff --git a/trace-events b/trace-events index ed71f44649..c445f54773 100644 --- a/trace-events +++ b/trace-events @@ -104,6 +104,20 @@ gdbstub_err_invalid_rle(void) "got invalid RLE sequence" gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x" gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x" +# job.c +job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" +job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" +job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" + +# job-qmp.c +qmp_job_cancel(void *job) "job %p" +qmp_job_pause(void *job) "job %p" +qmp_job_resume(void *job) "job %p" +qmp_job_complete(void *job) "job %p" +qmp_job_finalize(void *job) "job %p" +qmp_job_dismiss(void *job) "job %p" + + ### Guest events, keep at bottom diff --git a/trace/control-target.c b/trace/control-target.c index 706b2cee9d..ceb55c70ce 100644 --- a/trace/control-target.c +++ b/trace/control-target.c @@ -11,7 +11,6 @@ #include "cpu.h" #include "trace-root.h" #include "trace/control.h" -#include "translate-all.h" void trace_event_set_state_dynamic_init(TraceEvent *ev, bool state) diff --git a/trace/control.c b/trace/control.c index e40cfca775..43fb7868db 100644 --- a/trace/control.c +++ b/trace/control.c @@ -253,7 +253,7 @@ void trace_init_file(const char *file) #ifdef CONFIG_TRACE_SIMPLE st_set_trace_file(file); #elif defined CONFIG_TRACE_LOG - /* If both the simple and the log backends are enabled, "-trace file" + /* If both the simple and the log backends are enabled, "--trace file" * only applies to the simple backend; use "-D" for the log backend. */ if (file) { @@ -261,7 +261,7 @@ void trace_init_file(const char *file) } #else if (file) { - fprintf(stderr, "error: -trace file=...: " + fprintf(stderr, "error: --trace file=...: " "option not supported by the selected tracing backends\n"); exit(1); } diff --git a/trace/control.h b/trace/control.h index 1903e22975..0716f90f45 100644 --- a/trace/control.h +++ b/trace/control.h @@ -193,7 +193,7 @@ void trace_event_set_vcpu_state_dynamic(CPUState *vcpu, /** * trace_init_backends: * @file: Name of trace output file; may be NULL. - * Corresponds to commandline option "-trace file=...". + * Corresponds to commandline option "--trace file=...". * * Initialize the tracing backend. * @@ -204,7 +204,7 @@ bool trace_init_backends(void); /** * trace_init_file: * @file: Name of trace output file; may be NULL. - * Corresponds to commandline option "-trace file=...". + * Corresponds to commandline option "--trace file=...". * * Record the name of the output file for the tracing backend. * Exits if no selected backend does not support specifying the @@ -267,6 +267,6 @@ char *trace_opt_parse(const char *optarg); uint32_t trace_get_vcpu_event_count(void); -#include "trace/control-internal.h" +#include "control-internal.h" #endif /* TRACE__CONTROL_H */ diff --git a/trace/mem-internal.h b/trace/mem-internal.h index ddda934253..f6efaf6d6b 100644 --- a/trace/mem-internal.h +++ b/trace/mem-internal.h @@ -10,37 +10,45 @@ #ifndef TRACE__MEM_INTERNAL_H #define TRACE__MEM_INTERNAL_H -static inline uint8_t trace_mem_get_info(TCGMemOp op, bool store) +#define TRACE_MEM_SZ_SHIFT_MASK 0x7 /* size shift mask */ +#define TRACE_MEM_SE (1ULL << 3) /* sign extended (y/n) */ +#define TRACE_MEM_BE (1ULL << 4) /* big endian (y/n) */ +#define TRACE_MEM_ST (1ULL << 5) /* store (y/n) */ + +static inline uint8_t trace_mem_build_info( + int size_shift, bool sign_extend, TCGMemOp endianness, bool store) { - uint8_t res = op; - bool be = (op & MO_BSWAP) == MO_BE; + uint8_t res; - /* remove untraced fields */ - res &= (1ULL << 4) - 1; - /* make endianness absolute */ - res &= ~MO_BSWAP; - if (be) { - res |= 1ULL << 3; + res = size_shift & TRACE_MEM_SZ_SHIFT_MASK; + if (sign_extend) { + res |= TRACE_MEM_SE; + } + if (endianness == MO_BE) { + res |= TRACE_MEM_BE; } - /* add fields */ if (store) { - res |= 1ULL << 4; + res |= TRACE_MEM_ST; } - return res; } -static inline uint8_t trace_mem_build_info( - TCGMemOp size, bool sign_extend, TCGMemOp endianness, bool store) +static inline uint8_t trace_mem_get_info(TCGMemOp op, bool store) { - uint8_t res = 0; - res |= size; - res |= (sign_extend << 2); - if (endianness == MO_BE) { - res |= (1ULL << 3); - } - res |= (store << 4); - return res; + return trace_mem_build_info(op & MO_SIZE, !!(op & MO_SIGN), + op & MO_BSWAP, store); +} + +static inline +uint8_t trace_mem_build_info_no_se_be(int size_shift, bool store) +{ + return trace_mem_build_info(size_shift, false, MO_BE, store); +} + +static inline +uint8_t trace_mem_build_info_no_se_le(int size_shift, bool store) +{ + return trace_mem_build_info(size_shift, false, MO_LE, store); } #endif /* TRACE__MEM_INTERNAL_H */ diff --git a/trace/mem.h b/trace/mem.h index 9c88bcb4e6..2b58196e53 100644 --- a/trace/mem.h +++ b/trace/mem.h @@ -25,7 +25,7 @@ static uint8_t trace_mem_get_info(TCGMemOp op, bool store); * * Return a value for the 'info' argument in guest memory access traces. */ -static uint8_t trace_mem_build_info(TCGMemOp size, bool sign_extend, +static uint8_t trace_mem_build_info(int size_shift, bool sign_extend, TCGMemOp endianness, bool store); diff --git a/trace/qmp.c b/trace/qmp.c index 756086c79f..ea99b00956 100644 --- a/trace/qmp.c +++ b/trace/qmp.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-commands-trace.h" -#include "trace/control.h" +#include "control.h" static CPUState *get_cpu(bool has_vcpu, int vcpu, Error **errp) diff --git a/trace/simple.c b/trace/simple.c index e82018d923..701dec639c 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -36,9 +36,9 @@ * Trace records are written out by a dedicated thread. The thread waits for * records to become available, writes them out, and then waits again. */ -static CompatGMutex trace_lock; -static CompatGCond trace_available_cond; -static CompatGCond trace_empty_cond; +static GMutex trace_lock; +static GCond trace_available_cond; +static GCond trace_empty_cond; static bool trace_available; static bool trace_writeout_enabled; diff --git a/ui/Makefile.objs b/ui/Makefile.objs index cc784346cb..00f6976c30 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -15,10 +15,6 @@ common-obj-$(CONFIG_COCOA) += cocoa.o common-obj-$(CONFIG_VNC) += $(vnc-obj-y) common-obj-$(call lnot,$(CONFIG_VNC)) += vnc-stubs.o -common-obj-$(CONFIG_X11) += x_keymap.o -x_keymap.o-cflags := $(X11_CFLAGS) -x_keymap.o-libs := $(X11_LIBS) - # ui-sdl module common-obj-$(CONFIG_SDL) += sdl.mo ifeq ($(CONFIG_SDLABI),1.2) @@ -46,6 +42,13 @@ gtk.mo-objs += gtk-gl-area.o endif endif +ifeq ($(CONFIG_X11),y) +sdl.mo-objs += x_keymap.o +gtk.mo-objs += x_keymap.o +x_keymap.o-cflags := $(X11_CFLAGS) +x_keymap.o-libs := $(X11_LIBS) +endif + common-obj-$(CONFIG_CURSES) += curses.mo curses.mo-objs := curses.o curses.mo-cflags := $(CURSES_CFLAGS) diff --git a/ui/cocoa.m b/ui/cocoa.m index 110b393e4e..ecf12bfc2e 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -44,6 +44,9 @@ #ifndef MAC_OS_X_VERSION_10_6 #define MAC_OS_X_VERSION_10_6 1060 #endif +#ifndef MAC_OS_X_VERSION_10_9 +#define MAC_OS_X_VERSION_10_9 1090 +#endif #ifndef MAC_OS_X_VERSION_10_10 #define MAC_OS_X_VERSION_10_10 101000 #endif @@ -79,6 +82,13 @@ #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask #define NSWindowStyleMaskTitled NSTitledWindowMask #endif +/* 10.13 deprecates NSFileHandlingPanelOKButton in favour of + * NSModalResponseOK, which was introduced in 10.9. Define + * it for older versions. + */ +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 +#define NSModalResponseOK NSFileHandlingPanelOKButton +#endif //#define DEBUG @@ -627,6 +637,7 @@ QemuCocoaView *cocoaView; int buttons = 0; int keycode = 0; bool mouse_event = false; + static bool switched_to_fullscreen = false; NSPoint p = [event locationInWindow]; switch ([event type]) { @@ -671,7 +682,11 @@ QemuCocoaView *cocoaView; keycode == Q_KEY_CODE_NUM_LOCK) { [self toggleStatefulModifier:keycode]; } else if (qemu_console_is_graphic(NULL)) { - [self toggleModifier:keycode]; + if (switched_to_fullscreen) { + switched_to_fullscreen = false; + } else { + [self toggleModifier:keycode]; + } } } @@ -681,6 +696,13 @@ QemuCocoaView *cocoaView; // forward command key combos to the host UI unless the mouse is grabbed if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) { + /* + * Prevent the command key from being stuck down in the guest + * when using Command-F to switch to full screen mode. + */ + if (keycode == Q_KEY_CODE_F) { + switched_to_fullscreen = true; + } [NSApp sendEvent:event]; return; } @@ -792,14 +814,20 @@ QemuCocoaView *cocoaView; * This is in-line with standard Mac OS X UI behaviour. */ + /* + * 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. + */ + if ([event deltaY] != 0) { /* Determine if this is a scroll up or scroll down event */ - buttons = ([event scrollingDeltaY] > 0) ? - INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; - qemu_input_queue_btn(dcl->con, buttons, true); - qemu_input_event_sync(); - qemu_input_queue_btn(dcl->con, buttons, false); - qemu_input_event_sync(); - + buttons = ([event deltaY] > 0) ? + INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN; + 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 * movement code from executing. @@ -1218,7 +1246,7 @@ QemuCocoaView *cocoaView; [openPanel setCanChooseFiles: YES]; [openPanel setAllowsMultipleSelection: NO]; [openPanel setAllowedFileTypes: supportedImageFileTypes]; - if([openPanel runModal] == NSFileHandlingPanelOKButton) { + if([openPanel runModal] == NSModalResponseOK) { NSString * file = [[[openPanel URLs] objectAtIndex: 0] path]; if(file == nil) { NSBeep(); diff --git a/ui/console.c b/ui/console.c index 3fb2f4e09f..bc58458ee8 100644 --- a/ui/console.c +++ b/ui/console.c @@ -165,6 +165,8 @@ struct QemuConsole { QEMUFIFO out_fifo; uint8_t out_fifo_buf[16]; QEMUTimer *kbd_timer; + + QTAILQ_ENTRY(QemuConsole) next; }; struct DisplayState { @@ -180,8 +182,8 @@ struct DisplayState { static DisplayState *display_state; static QemuConsole *active_console; -static QemuConsole **consoles; -static int nb_consoles = 0; +static QTAILQ_HEAD(consoles_head, QemuConsole) consoles = + QTAILQ_HEAD_INITIALIZER(consoles); static bool cursor_visible_phase; static QEMUTimer *cursor_timer; @@ -197,7 +199,7 @@ static void gui_update(void *opaque) uint64_t dcl_interval; DisplayState *ds = opaque; DisplayChangeListener *dcl; - int i; + QemuConsole *con; ds->refreshing = true; dpy_refresh(ds); @@ -212,9 +214,9 @@ static void gui_update(void *opaque) } if (ds->update_interval != interval) { ds->update_interval = interval; - for (i = 0; i < nb_consoles; i++) { - if (consoles[i]->hw_ops->update_interval) { - consoles[i]->hw_ops->update_interval(consoles[i]->hw, interval); + QTAILQ_FOREACH(con, &consoles, next) { + if (con->hw_ops->update_interval) { + con->hw_ops->update_interval(con->hw, interval); } } trace_console_refresh(interval); @@ -370,6 +372,11 @@ void qmp_screendump(const char *filename, bool has_device, const char *device, graphic_hw_update(con); surface = qemu_console_surface(con); + if (!surface) { + error_setg(errp, "no surface"); + return; + } + ppm_save(filename, surface, errp); } @@ -1280,7 +1287,7 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, object_property_add_link(obj, "device", TYPE_DEVICE, (Object **)&s->device, object_property_allow_set_link, - OBJ_PROP_LINK_UNREF_ON_RELEASE, + OBJ_PROP_LINK_STRONG, &error_abort); object_property_add_uint32_ptr(obj, "head", &s->head, &error_abort); @@ -1292,10 +1299,13 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, s->ds = ds; s->console_type = console_type; - consoles = g_realloc(consoles, sizeof(*consoles) * (nb_consoles+1)); - if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) { - s->index = nb_consoles; - consoles[nb_consoles++] = s; + if (QTAILQ_EMPTY(&consoles)) { + s->index = 0; + QTAILQ_INSERT_TAIL(&consoles, s, next); + } else if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) { + QemuConsole *last = QTAILQ_LAST(&consoles, consoles_head); + s->index = last->index + 1; + QTAILQ_INSERT_TAIL(&consoles, s, next); } else { /* * HACK: Put graphical consoles before text consoles. @@ -1303,15 +1313,24 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, * Only do that for coldplugged devices. After initial device * initialization we will not renumber the consoles any more. */ - for (i = nb_consoles; i > 0; i--) { - if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE) - break; - consoles[i] = consoles[i - 1]; - consoles[i]->index = i; + QemuConsole *c = QTAILQ_FIRST(&consoles); + + while (QTAILQ_NEXT(c, next) != NULL && + c->console_type == GRAPHIC_CONSOLE) { + c = QTAILQ_NEXT(c, next); + } + if (c->console_type == GRAPHIC_CONSOLE) { + /* have no text consoles */ + s->index = c->index + 1; + QTAILQ_INSERT_AFTER(&consoles, c, s, next); + } else { + s->index = c->index; + QTAILQ_INSERT_BEFORE(c, s, next); + /* renumber text consoles */ + for (i = s->index + 1; c != NULL; c = QTAILQ_NEXT(c, next), i++) { + c->index = i; + } } - s->index = i; - consoles[i] = s; - nb_consoles++; } return s; } @@ -1574,6 +1593,16 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h) } } +void dpy_gfx_update_full(QemuConsole *con) +{ + if (!con->surface) { + return; + } + dpy_gfx_update(con, 0, 0, + surface_width(con->surface), + surface_height(con->surface)); +} + void dpy_gfx_replace_surface(QemuConsole *con, DisplaySurface *surface) { @@ -1851,21 +1880,21 @@ static DisplayState *get_alloc_displaystate(void) DisplayState *init_displaystate(void) { gchar *name; - int i; + QemuConsole *con; get_alloc_displaystate(); - for (i = 0; i < nb_consoles; i++) { - if (consoles[i]->console_type != GRAPHIC_CONSOLE && - consoles[i]->ds == NULL) { - text_console_do_init(consoles[i]->chr, display_state); + QTAILQ_FOREACH(con, &consoles, next) { + if (con->console_type != GRAPHIC_CONSOLE && + con->ds == NULL) { + text_console_do_init(con->chr, display_state); } /* Hook up into the qom tree here (not in new_console()), once * all QemuConsoles are created and the order / numbering * doesn't change any more */ - name = g_strdup_printf("console[%d]", i); + name = g_strdup_printf("console[%d]", con->index); object_property_add_child(container_get(object_get_root(), "/backend"), - name, OBJECT(consoles[i]), &error_abort); + name, OBJECT(con), &error_abort); g_free(name); } @@ -1947,33 +1976,34 @@ void graphic_console_close(QemuConsole *con) QemuConsole *qemu_console_lookup_by_index(unsigned int index) { - if (index >= nb_consoles) { - return NULL; + QemuConsole *con; + + QTAILQ_FOREACH(con, &consoles, next) { + if (con->index == index) { + return con; + } } - return consoles[index]; + return NULL; } QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head) { + QemuConsole *con; Object *obj; uint32_t h; - int i; - for (i = 0; i < nb_consoles; i++) { - if (!consoles[i]) { - continue; - } - obj = object_property_get_link(OBJECT(consoles[i]), + QTAILQ_FOREACH(con, &consoles, next) { + obj = object_property_get_link(OBJECT(con), "device", &error_abort); if (DEVICE(obj) != dev) { continue; } - h = object_property_get_uint(OBJECT(consoles[i]), + h = object_property_get_uint(OBJECT(con), "head", &error_abort); if (h != head) { continue; } - return consoles[i]; + return con; } return NULL; } @@ -2003,22 +2033,19 @@ QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, QemuConsole *qemu_console_lookup_unused(void) { + QemuConsole *con; Object *obj; - int i; - for (i = 0; i < nb_consoles; i++) { - if (!consoles[i]) { + QTAILQ_FOREACH(con, &consoles, next) { + if (con->hw_ops != &unused_ops) { continue; } - if (consoles[i]->hw_ops != &unused_ops) { - continue; - } - obj = object_property_get_link(OBJECT(consoles[i]), + obj = object_property_get_link(OBJECT(con), "device", &error_abort); if (obj != NULL) { continue; } - return consoles[i]; + return con; } return NULL; } @@ -2120,12 +2147,11 @@ static void text_console_update_cursor_timer(void) static void text_console_update_cursor(void *opaque) { QemuConsole *s; - int i, count = 0; + int count = 0; cursor_visible_phase = !cursor_visible_phase; - for (i = 0; i < nb_consoles; i++) { - s = consoles[i]; + QTAILQ_FOREACH(s, &consoles, next) { if (qemu_console_is_graphic(s) || !qemu_console_is_visible(s)) { continue; diff --git a/ui/egl-context.c b/ui/egl-context.c index 2161969abe..78e6c7ab7c 100644 --- a/ui/egl-context.c +++ b/ui/egl-context.c @@ -6,15 +6,22 @@ QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl, QEMUGLParams *params) { EGLContext ctx; - EGLint ctx_att[] = { + EGLint ctx_att_core[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_CONTEXT_CLIENT_VERSION, params->major_ver, EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver, EGL_NONE }; + EGLint ctx_att_gles[] = { + EGL_CONTEXT_CLIENT_VERSION, params->major_ver, + EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver, + EGL_NONE + }; + bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES); ctx = eglCreateContext(qemu_egl_display, qemu_egl_config, - eglGetCurrentContext(), ctx_att); + eglGetCurrentContext(), + gles ? ctx_att_gles : ctx_att_core); return ctx; } diff --git a/ui/egl-headless.c b/ui/egl-headless.c index 7c877122d3..42a41310b0 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -171,11 +171,12 @@ static void early_egl_headless_init(DisplayOptions *opts) static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) { + DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON; QemuConsole *con; egl_dpy *edpy; int idx; - if (egl_rendernode_init(NULL) < 0) { + if (egl_rendernode_init(NULL, mode) < 0) { error_report("egl: render node init failed"); exit(1); } diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 16dc3ded36..71b6a97bd1 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -24,6 +24,7 @@ EGLDisplay *qemu_egl_display; EGLConfig qemu_egl_config; +DisplayGLMode qemu_egl_mode; /* ------------------------------------------------------------------ */ @@ -191,7 +192,7 @@ static int qemu_egl_rendernode_open(const char *rendernode) return fd; } -int egl_rendernode_init(const char *rendernode) +int egl_rendernode_init(const char *rendernode, DisplayGLMode mode) { qemu_egl_rn_fd = -1; int rc; @@ -208,7 +209,8 @@ int egl_rendernode_init(const char *rendernode) goto err; } - rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev); + rc = qemu_egl_init_dpy_mesa((EGLNativeDisplayType)qemu_egl_rn_gbm_dev, + mode); if (rc != 0) { /* qemu_egl_init_dpy_mesa reports error */ goto err; @@ -392,9 +394,10 @@ static EGLDisplay qemu_egl_get_display(EGLNativeDisplayType native, } static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, - EGLenum platform) + EGLenum platform, + DisplayGLMode mode) { - static const EGLint conf_att_gl[] = { + static const EGLint conf_att_core[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_RED_SIZE, 5, @@ -403,9 +406,19 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, EGL_ALPHA_SIZE, 0, EGL_NONE, }; + static const EGLint conf_att_gles[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 5, + EGL_BLUE_SIZE, 5, + EGL_ALPHA_SIZE, 0, + EGL_NONE, + }; EGLint major, minor; EGLBoolean b; EGLint n; + bool gles = (mode == DISPLAYGL_MODE_ES); qemu_egl_display = qemu_egl_get_display(dpy, platform); if (qemu_egl_display == EGL_NO_DISPLAY) { @@ -419,50 +432,60 @@ static int qemu_egl_init_dpy(EGLNativeDisplayType dpy, return -1; } - b = eglBindAPI(EGL_OPENGL_API); + b = eglBindAPI(gles ? EGL_OPENGL_ES_API : EGL_OPENGL_API); if (b == EGL_FALSE) { - error_report("egl: eglBindAPI failed"); + error_report("egl: eglBindAPI failed (%s mode)", + gles ? "gles" : "core"); return -1; } - b = eglChooseConfig(qemu_egl_display, conf_att_gl, + b = eglChooseConfig(qemu_egl_display, + gles ? conf_att_gles : conf_att_core, &qemu_egl_config, 1, &n); if (b == EGL_FALSE || n != 1) { - error_report("egl: eglChooseConfig failed"); + error_report("egl: eglChooseConfig failed (%s mode)", + gles ? "gles" : "core"); return -1; } + + qemu_egl_mode = gles ? DISPLAYGL_MODE_ES : DISPLAYGL_MODE_CORE; return 0; } -int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy) +int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode) { #ifdef EGL_KHR_platform_x11 - return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR); + return qemu_egl_init_dpy(dpy, EGL_PLATFORM_X11_KHR, mode); #else - return qemu_egl_init_dpy(dpy, 0); + return qemu_egl_init_dpy(dpy, 0, mode); #endif } -int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy) +int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode) { #ifdef EGL_MESA_platform_gbm - return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA); + return qemu_egl_init_dpy(dpy, EGL_PLATFORM_GBM_MESA, mode); #else - return qemu_egl_init_dpy(dpy, 0); + return qemu_egl_init_dpy(dpy, 0, mode); #endif } EGLContext qemu_egl_init_ctx(void) { - static const EGLint ctx_att_gl[] = { + static const EGLint ctx_att_core[] = { EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, EGL_NONE }; + static const EGLint ctx_att_gles[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + bool gles = (qemu_egl_mode == DISPLAYGL_MODE_ES); EGLContext ectx; EGLBoolean b; ectx = eglCreateContext(qemu_egl_display, qemu_egl_config, EGL_NO_CONTEXT, - ctx_att_gl); + gles ? ctx_att_gles : ctx_att_core); if (ectx == EGL_NO_CONTEXT) { error_report("egl: eglCreateContext failed"); return NULL; diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 9390c6762e..fb00ad12ec 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -280,12 +280,12 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); } -void gtk_egl_init(void) +void gtk_egl_init(DisplayGLMode mode) { GdkDisplay *gdk_display = gdk_display_get_default(); Display *x11_display = gdk_x11_display_get_xdisplay(gdk_display); - if (qemu_egl_init_dpy_x11(x11_display) < 0) { + if (qemu_egl_init_dpy_x11(x11_display, mode) < 0) { return; } diff --git a/ui/gtk.c b/ui/gtk.c index bb3214cffb..5cce6ed42d 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -66,7 +66,7 @@ #define VC_SCALE_STEP 0.25 #ifdef GDK_WINDOWING_X11 -#include "ui/x_keymap.h" +#include "x_keymap.h" /* Gtk2 compat */ #ifndef GDK_IS_X11_DISPLAY @@ -145,6 +145,7 @@ #define GDK_KEY_2 GDK_2 #define GDK_KEY_f GDK_f #define GDK_KEY_g GDK_g +#define GDK_KEY_m GDK_m #define GDK_KEY_q GDK_q #define GDK_KEY_plus GDK_plus #define GDK_KEY_equal GDK_equal @@ -208,6 +209,7 @@ struct GtkDisplayState { GtkWidget *show_tabs_item; GtkWidget *untabify_item; + GtkWidget *show_menubar_item; GtkWidget *vbox; GtkWidget *notebook; @@ -1387,6 +1389,30 @@ static void gd_menu_untabify(GtkMenuItem *item, void *opaque) } } +static void gd_menu_show_menubar(GtkMenuItem *item, void *opaque) +{ + GtkDisplayState *s = opaque; + VirtualConsole *vc = gd_vc_find_current(s); + + if (s->full_screen) { + return; + } + + if (gtk_check_menu_item_get_active( + GTK_CHECK_MENU_ITEM(s->show_menubar_item))) { + gtk_widget_show(s->menu_bar); + } else { + gtk_widget_hide(s->menu_bar); + } + gd_update_windowsize(vc); +} + +static void gd_accel_show_menubar(void *opaque) +{ + GtkDisplayState *s = opaque; + gtk_menu_item_activate(GTK_MENU_ITEM(s->show_menubar_item)); +} + static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) { GtkDisplayState *s = opaque; @@ -1403,7 +1429,10 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) } else { gtk_window_unfullscreen(GTK_WINDOW(s->window)); gd_menu_show_tabs(GTK_MENU_ITEM(s->show_tabs_item), s); - gtk_widget_show(s->menu_bar); + if (gtk_check_menu_item_get_active( + GTK_CHECK_MENU_ITEM(s->show_menubar_item))) { + gtk_widget_show(s->menu_bar); + } s->full_screen = FALSE; if (vc->type == GD_VC_GFX) { vc->gfx.scale_x = 1.0; @@ -2036,6 +2065,8 @@ static void gd_connect_signals(GtkDisplayState *s) G_CALLBACK(gd_menu_show_tabs), s); g_signal_connect(s->untabify_item, "activate", G_CALLBACK(gd_menu_untabify), s); + g_signal_connect(s->show_menubar_item, "activate", + G_CALLBACK(gd_menu_show_menubar), s); g_signal_connect(s->window, "delete-event", G_CALLBACK(gd_window_close), s); @@ -2272,11 +2303,26 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s) s->untabify_item = gtk_menu_item_new_with_mnemonic(_("Detach Tab")); gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->untabify_item); + s->show_menubar_item = gtk_check_menu_item_new_with_mnemonic( + _("Show Menubar")); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(s->show_menubar_item), + TRUE); + gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0, + g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL)); +#if GTK_CHECK_VERSION(3, 8, 0) + gtk_accel_label_set_accel( + GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))), + GDK_KEY_m, HOTKEY_MODIFIERS); +#endif + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item); + return view_menu; } static void gd_create_menus(GtkDisplayState *s) { + GtkSettings *settings; + s->accel_group = gtk_accel_group_new(); s->machine_menu = gd_create_menu_machine(s); s->view_menu = gd_create_menu_view(s); @@ -2292,6 +2338,10 @@ static void gd_create_menus(GtkDisplayState *s) g_object_set_data(G_OBJECT(s->window), "accel_group", s->accel_group); gtk_window_add_accel_group(GTK_WINDOW(s->window), s->accel_group); + + /* Disable the default "F10" menu shortcut. */ + settings = gtk_widget_get_settings(s->window); + g_object_set(G_OBJECT(settings), "gtk-menu-bar-accel", "", NULL); } @@ -2434,16 +2484,17 @@ static void early_gtk_display_init(DisplayOptions *opts) } assert(opts->type == DISPLAY_TYPE_GTK); - if (opts->has_gl && opts->gl) { + if (opts->has_gl && opts->gl != DISPLAYGL_MODE_OFF) { #if defined(CONFIG_OPENGL) #if defined(CONFIG_GTK_GL) && defined(GDK_WINDOWING_WAYLAND) if (GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) { gtk_use_gl_area = true; gtk_gl_area_init(); - } + } else #endif { - gtk_egl_init(); + DisplayGLMode mode = opts->has_gl ? opts->gl : DISPLAYGL_MODE_ON; + gtk_egl_init(mode); } #endif } diff --git a/ui/input-keymap.c b/ui/input-keymap.c index 3d4e66bab5..db5ccff5ad 100644 --- a/ui/input-keymap.c +++ b/ui/input-keymap.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "sysemu/sysemu.h" -#include "ui/keymaps.h" +#include "keymaps.h" #include "ui/input.h" #include "standard-headers/linux/input.h" @@ -21,6 +21,7 @@ #include "ui/input-keymap-xorgkbd-to-qcode.c" #include "ui/input-keymap-xorgxquartz-to-qcode.c" #include "ui/input-keymap-xorgxwin-to-qcode.c" +#include "ui/input-keymap-osx-to-qcode.c" int qemu_input_linux_to_qcode(unsigned int lnx) { diff --git a/ui/input-legacy.c b/ui/input-legacy.c index e5d4db1d97..549654e26a 100644 --- a/ui/input-legacy.c +++ b/ui/input-legacy.c @@ -26,7 +26,7 @@ #include "qapi/qapi-commands-ui.h" #include "sysemu/sysemu.h" #include "ui/console.h" -#include "ui/keymaps.h" +#include "keymaps.h" #include "ui/input.h" struct QEMUPutMouseEntry { diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c index 1f34817bae..85484407be 100644 --- a/ui/sdl2-2d.c +++ b/ui/sdl2-2d.c @@ -36,9 +36,7 @@ void sdl2_2d_update(DisplayChangeListener *dcl, struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); DisplaySurface *surf = qemu_console_surface(dcl->con); SDL_Rect rect; - size_t surface_data_offset = surface_bytes_per_pixel(surf) * x + - surface_stride(surf) * y; - + size_t surface_data_offset; assert(!scon->opengl); if (!surf) { @@ -48,6 +46,8 @@ void sdl2_2d_update(DisplayChangeListener *dcl, return; } + surface_data_offset = surface_bytes_per_pixel(surf) * x + + surface_stride(surf) * y; rect.x = x; rect.y = y; rect.w = w; diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index c3683e6b65..83b71853d1 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -140,12 +140,27 @@ QEMUGLContext sdl2_gl_create_context(DisplayChangeListener *dcl, SDL_GL_MakeCurrent(scon->real_window, scon->winctx); SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, - SDL_GL_CONTEXT_PROFILE_CORE); + if (scon->opts->gl == DISPLAYGL_MODE_ON || + scon->opts->gl == DISPLAYGL_MODE_CORE) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_CORE); + } else if (scon->opts->gl == DISPLAYGL_MODE_ES) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_ES); + } SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, params->major_ver); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, params->minor_ver); ctx = SDL_GL_CreateContext(scon->real_window); + + /* If SDL fail to create a GL context and we use the "on" flag, + * then try to fallback to GLES. + */ + if (!ctx && scon->opts->gl == DISPLAYGL_MODE_ON) { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_ES); + ctx = SDL_GL_CreateContext(scon->real_window); + } return (QEMUGLContext)ctx; } diff --git a/ui/sdl2.c b/ui/sdl2.c index 9b7c6ab434..ab95d0e1de 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -33,7 +33,6 @@ static int sdl2_num_outputs; static struct sdl2_console *sdl2_console; -static DisplayOptions *opts; static SDL_Surface *guest_sprite_surface; static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ @@ -379,8 +378,7 @@ static void handle_keydown(SDL_Event *ev) } break; case SDL_SCANCODE_U: - sdl2_window_destroy(scon); - sdl2_window_create(scon); + sdl2_window_resize(scon); if (!scon->opengl) { /* re-create scon->texture */ sdl2_2d_switch(&scon->dcl, scon->surface); @@ -433,7 +431,7 @@ static void handle_keyup(SDL_Event *ev) static void handle_textinput(SDL_Event *ev) { - struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->text.windowID); QemuConsole *con = scon ? scon->dcl.con : NULL; if (qemu_console_is_graphic(con)) { @@ -445,9 +443,9 @@ static void handle_textinput(SDL_Event *ev) static void handle_mousemotion(SDL_Event *ev) { int max_x, max_y; - struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->motion.windowID); - if (!qemu_console_is_graphic(scon->dcl.con)) { + if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } @@ -477,9 +475,9 @@ static void handle_mousebutton(SDL_Event *ev) { int buttonstate = SDL_GetMouseState(NULL, NULL); SDL_MouseButtonEvent *bev; - struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->button.windowID); - if (!qemu_console_is_graphic(scon->dcl.con)) { + if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } @@ -501,11 +499,11 @@ static void handle_mousebutton(SDL_Event *ev) static void handle_mousewheel(SDL_Event *ev) { - struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); + struct sdl2_console *scon = get_scon_from_window(ev->wheel.windowID); SDL_MouseWheelEvent *wev = &ev->wheel; InputButton btn; - if (!qemu_console_is_graphic(scon->dcl.con)) { + if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } @@ -573,7 +571,7 @@ static void handle_windowevent(SDL_Event *ev) break; case SDL_WINDOWEVENT_CLOSE: if (qemu_console_is_graphic(scon->dcl.con)) { - if (opts->has_window_close && !opts->window_close) { + if (scon->opts->has_window_close && !scon->opts->window_close) { allow_close = false; } if (allow_close) { @@ -620,7 +618,7 @@ void sdl2_poll_events(struct sdl2_console *scon) handle_textinput(ev); break; case SDL_QUIT: - if (opts->has_window_close && !opts->window_close) { + if (scon->opts->has_window_close && !scon->opts->window_close) { allow_close = false; } if (allow_close) { @@ -777,7 +775,6 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) SDL_SysWMinfo info; assert(o->type == DISPLAY_TYPE_SDL); - opts = o; #ifdef __linux__ /* on Linux, SDL may use fbcon|directfb|svgalib when run without @@ -820,6 +817,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) sdl2_console[i].hidden = true; } sdl2_console[i].idx = i; + sdl2_console[i].opts = o; #ifdef CONFIG_OPENGL sdl2_console[i].opengl = display_opengl; sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops; @@ -853,7 +851,8 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) g_free(filename); } - if (opts->has_full_screen && opts->full_screen) { + if (sdl2_console->opts->has_full_screen && + sdl2_console->opts->full_screen) { gui_fullscreen = 1; sdl_grab_start(0); } diff --git a/ui/spice-core.c b/ui/spice-core.c index ae8921a201..f8c0878529 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -829,7 +829,8 @@ void qemu_spice_init(void) "incompatible with -spice port/tls-port"); exit(1); } - if (egl_rendernode_init(qemu_opt_get(opts, "rendernode")) != 0) { + if (egl_rendernode_init(qemu_opt_get(opts, "rendernode"), + DISPLAYGL_MODE_ON) != 0) { error_report("Failed to initialize EGL render node for SPICE GL"); exit(1); } diff --git a/ui/spice-input.c b/ui/spice-input.c index 3d41aa1831..a426c03b5e 100644 --- a/ui/spice-input.c +++ b/ui/spice-input.c @@ -23,7 +23,7 @@ #include "qemu-common.h" #include "ui/qemu-spice.h" #include "ui/console.h" -#include "ui/keymaps.h" +#include "keymaps.h" #include "ui/input.h" /* keyboard bits */ diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c index fd63d4f688..7493a84723 100644 --- a/ui/vnc-enc-zrle.c +++ b/ui/vnc-enc-zrle.c @@ -199,56 +199,56 @@ static void zrle_write_u8(VncState *vs, uint8_t value) #define ZRLE_BPP 8 #define ZYWRLE_ENDIAN ENDIAN_NO -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_BPP #define ZRLE_BPP 15 #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_BPP #define ZRLE_BPP 16 #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_BPP #define ZRLE_BPP 32 #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #define ZRLE_COMPACT_PIXEL 24a #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_COMPACT_PIXEL #define ZRLE_COMPACT_PIXEL 24b #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_LITTLE -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZYWRLE_ENDIAN #define ZYWRLE_ENDIAN ENDIAN_BIG -#include "vnc-enc-zrle-template.c" +#include "vnc-enc-zrle.inc.c" #undef ZRLE_COMPACT_PIXEL #undef ZRLE_BPP diff --git a/ui/vnc-enc-zrle-template.c b/ui/vnc-enc-zrle.inc.c similarity index 100% rename from ui/vnc-enc-zrle-template.c rename to ui/vnc-enc-zrle.inc.c diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 868dddef4b..b0b15d42a8 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -82,6 +82,7 @@ VncJob *vnc_job_new(VncState *vs) { VncJob *job = g_new0(VncJob, 1); + assert(vs->magic == VNC_MAGIC); job->vs = vs; vnc_lock_queue(queue); QLIST_INIT(&job->rectangles); @@ -214,6 +215,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) /* Here job can only be NULL if queue->exit is true */ job = QTAILQ_FIRST(&queue->jobs); vnc_unlock_queue(queue); + assert(job->vs->magic == VNC_MAGIC); if (queue->exit) { return -1; @@ -236,6 +238,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) /* Make a local copy of vs and switch output buffers */ vnc_async_encoding_start(job->vs, &vs); + vs.magic = VNC_MAGIC; /* Start sending rectangles */ n_rectangles = 0; @@ -289,6 +292,7 @@ disconnected: vnc_unlock_queue(queue); qemu_cond_broadcast(&queue->cond); g_free(job); + vs.magic = 0; return 0; } diff --git a/ui/vnc.c b/ui/vnc.c index e164eb798c..359693238b 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -1138,6 +1138,7 @@ static void audio_capture_notify(void *opaque, audcnotification_e cmd) { VncState *vs = opaque; + assert(vs->magic == VNC_MAGIC); switch (cmd) { case AUD_CNOTIFY_DISABLE: vnc_lock_output(vs); @@ -1167,6 +1168,7 @@ static void audio_capture(void *opaque, void *buf, int size) { VncState *vs = opaque; + assert(vs->magic == VNC_MAGIC); vnc_lock_output(vs); if (vs->output.offset < vs->throttle_output_offset) { vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); @@ -1275,6 +1277,7 @@ void vnc_disconnect_finish(VncState *vs) vs->ioc = NULL; object_unref(OBJECT(vs->sioc)); vs->sioc = NULL; + vs->magic = 0; g_free(vs); } @@ -1414,7 +1417,7 @@ static void vnc_client_write_locked(VncState *vs) static void vnc_client_write(VncState *vs) { - + assert(vs->magic == VNC_MAGIC); vnc_lock_output(vs); if (vs->output.offset) { vnc_client_write_locked(vs); @@ -1487,6 +1490,7 @@ static void vnc_jobs_bh(void *opaque) { VncState *vs = opaque; + assert(vs->magic == VNC_MAGIC); vnc_jobs_consume_buffer(vs); } @@ -1537,15 +1541,18 @@ gboolean vnc_client_io(QIOChannel *ioc G_GNUC_UNUSED, GIOCondition condition, void *opaque) { VncState *vs = opaque; + + assert(vs->magic == VNC_MAGIC); if (condition & G_IO_IN) { if (vnc_client_read(vs) < 0) { - goto end; + /* vs is free()ed here */ + return TRUE; } } if (condition & G_IO_OUT) { vnc_client_write(vs); } -end: + if (vs->disconnecting) { if (vs->ioc_tag != 0) { g_source_remove(vs->ioc_tag); @@ -1567,6 +1574,7 @@ end: void vnc_write(VncState *vs, const void *data, size_t len) { + assert(vs->magic == VNC_MAGIC); if (vs->disconnecting) { return; } @@ -3063,6 +3071,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, int i; trace_vnc_client_connect(vs, sioc); + vs->magic = VNC_MAGIC; vs->sioc = sioc; object_ref(OBJECT(vs->sioc)); vs->ioc = QIO_CHANNEL(sioc); diff --git a/ui/vnc.h b/ui/vnc.h index 7b29def77d..a86e0610e8 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -255,8 +255,11 @@ typedef enum { VNC_STATE_UPDATE_FORCE, } VncStateUpdate; +#define VNC_MAGIC ((uint64_t)0x05b3f069b3d204bb) + struct VncState { + uint64_t magic; QIOChannelSocket *sioc; /* The underlying socket */ QIOChannel *ioc; /* The channel currently used for I/O */ guint ioc_tag; @@ -294,7 +297,9 @@ struct VncState bool encode_ws; bool websocket; +#ifdef CONFIG_VNC VncClientInfo *info; +#endif /* Job thread bottom half has put data for a forced update * into the output buffer. This offset points to the end of diff --git a/util/Makefile.objs b/util/Makefile.objs index 728c3541db..e1c3fed4dc 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -47,4 +47,5 @@ util-obj-y += qht.o util-obj-y += range.o util-obj-y += stats64.o util-obj-y += systemd.o +util-obj-y += iova-tree.o util-obj-$(CONFIG_LINUX) += vfio-helpers.o diff --git a/util/aio-posix.c b/util/aio-posix.c index d8f0cb4af8..118bf5784b 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -45,11 +45,11 @@ struct AioHandler static void aio_epoll_disable(AioContext *ctx) { - ctx->epoll_available = false; - if (!ctx->epoll_enabled) { + ctx->epoll_enabled = false; + if (!ctx->epoll_available) { return; } - ctx->epoll_enabled = false; + ctx->epoll_available = false; close(ctx->epollfd); } @@ -713,6 +713,13 @@ void aio_context_setup(AioContext *ctx) #endif } +void aio_context_destroy(AioContext *ctx) +{ +#ifdef CONFIG_EPOLL_CREATE1 + aio_epoll_disable(ctx); +#endif +} + void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, int64_t grow, int64_t shrink, Error **errp) { diff --git a/util/aio-win32.c b/util/aio-win32.c index a67b00c6ad..e676a8d9b2 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -407,6 +407,10 @@ void aio_context_setup(AioContext *ctx) { } +void aio_context_destroy(AioContext *ctx) +{ +} + void aio_context_set_poll_params(AioContext *ctx, int64_t max_ns, int64_t grow, int64_t shrink, Error **errp) { diff --git a/util/async.c b/util/async.c index 4dd9d95a9e..05979f8014 100644 --- a/util/async.c +++ b/util/async.c @@ -298,6 +298,7 @@ aio_ctx_finalize(GSource *source) qemu_rec_mutex_destroy(&ctx->lock); qemu_lockcnt_destroy(&ctx->list_lock); timerlistgroup_deinit(&ctx->tlg); + aio_context_destroy(ctx); } static GSourceFuncs aio_source_funcs = { @@ -322,14 +323,22 @@ ThreadPool *aio_get_thread_pool(AioContext *ctx) } #ifdef CONFIG_LINUX_AIO -LinuxAioState *aio_get_linux_aio(AioContext *ctx) +LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp) { if (!ctx->linux_aio) { - ctx->linux_aio = laio_init(); - laio_attach_aio_context(ctx->linux_aio, ctx); + ctx->linux_aio = laio_init(errp); + if (ctx->linux_aio) { + laio_attach_aio_context(ctx->linux_aio, ctx); + } } return ctx->linux_aio; } + +LinuxAioState *aio_get_linux_aio(AioContext *ctx) +{ + assert(ctx->linux_aio); + return ctx->linux_aio; +} #endif void aio_notify(AioContext *ctx) diff --git a/util/cutils.c b/util/cutils.c index 0de69e6db4..9205e09031 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -544,6 +544,21 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, return check_strtox_error(nptr, ep, endptr, errno); } +/** + * Searches for the first occurrence of 'c' in 's', and returns a pointer + * to the trailing null byte if none was found. + */ +#ifndef HAVE_STRCHRNUL +const char *qemu_strchrnul(const char *s, int c) +{ + const char *e = strchr(s, c); + if (!e) { + e = s + strlen(s); + } + return e; +} +#endif + /** * parse_uint: * diff --git a/util/hbitmap.c b/util/hbitmap.c index 58a2c93842..bcd304041a 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -141,7 +141,7 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) return cur; } -int64_t hbitmap_iter_next(HBitmapIter *hbi) +int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance) { unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1] & hbi->hb->levels[HBITMAP_LEVELS - 1][hbi->pos]; @@ -154,8 +154,12 @@ int64_t hbitmap_iter_next(HBitmapIter *hbi) } } - /* The next call will resume work from the next bit. */ - hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); + if (advance) { + /* The next call will resume work from the next bit. */ + hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); + } else { + hbi->cur[HBITMAP_LEVELS - 1] = cur; + } item = ((uint64_t)hbi->pos << BITS_PER_LEVEL) + ctzl(cur); return item << hbi->granularity; diff --git a/util/iova-tree.c b/util/iova-tree.c new file mode 100644 index 0000000000..7990692cbd --- /dev/null +++ b/util/iova-tree.c @@ -0,0 +1,114 @@ +/* + * IOVA tree implementation based on GTree. + * + * Copyright 2018 Red Hat, Inc. + * + * Authors: + * Peter Xu + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + */ + +#include "qemu/osdep.h" +#include "qemu/iova-tree.h" + +struct IOVATree { + GTree *tree; +}; + +static int iova_tree_compare(gconstpointer a, gconstpointer b, gpointer data) +{ + const DMAMap *m1 = a, *m2 = b; + + if (m1->iova > m2->iova + m2->size) { + return 1; + } + + if (m1->iova + m1->size < m2->iova) { + return -1; + } + + /* Overlapped */ + return 0; +} + +IOVATree *iova_tree_new(void) +{ + IOVATree *iova_tree = g_new0(IOVATree, 1); + + /* We don't have values actually, no need to free */ + iova_tree->tree = g_tree_new_full(iova_tree_compare, NULL, g_free, NULL); + + return iova_tree; +} + +DMAMap *iova_tree_find(IOVATree *tree, DMAMap *map) +{ + return g_tree_lookup(tree->tree, map); +} + +DMAMap *iova_tree_find_address(IOVATree *tree, hwaddr iova) +{ + DMAMap map = { .iova = iova, .size = 0 }; + + return iova_tree_find(tree, &map); +} + +static inline void iova_tree_insert_internal(GTree *gtree, DMAMap *range) +{ + /* Key and value are sharing the same range data */ + g_tree_insert(gtree, range, range); +} + +int iova_tree_insert(IOVATree *tree, DMAMap *map) +{ + DMAMap *new; + + if (map->iova + map->size < map->iova || map->perm == IOMMU_NONE) { + return IOVA_ERR_INVALID; + } + + /* We don't allow to insert range that overlaps with existings */ + if (iova_tree_find(tree, map)) { + return IOVA_ERR_OVERLAP; + } + + new = g_new0(DMAMap, 1); + memcpy(new, map, sizeof(*new)); + iova_tree_insert_internal(tree->tree, new); + + return IOVA_OK; +} + +static gboolean iova_tree_traverse(gpointer key, gpointer value, + gpointer data) +{ + iova_tree_iterator iterator = data; + DMAMap *map = key; + + g_assert(key == value); + + return iterator(map); +} + +void iova_tree_foreach(IOVATree *tree, iova_tree_iterator iterator) +{ + g_tree_foreach(tree->tree, iova_tree_traverse, iterator); +} + +int iova_tree_remove(IOVATree *tree, DMAMap *map) +{ + DMAMap *overlap; + + while ((overlap = iova_tree_find(tree, map))) { + g_tree_remove(tree->tree, overlap); + } + + return IOVA_OK; +} + +void iova_tree_destroy(IOVATree *tree) +{ + g_tree_destroy(tree->tree); + g_free(tree); +} diff --git a/util/keyval.c b/util/keyval.c index 1c7351a233..13def4af54 100644 --- a/util/keyval.c +++ b/util/keyval.c @@ -126,7 +126,7 @@ static int key_to_index(const char *key, const char **end) * Else, fail because we have conflicting needs on how to map * @key_in_cur. * In any case, take over the reference to @value, i.e. if the caller - * wants to hold on to a reference, it needs to QINCREF(). + * wants to hold on to a reference, it needs to qobject_ref(). * Use @key up to @key_cursor to identify the key in error messages. * On success, return the mapped value. * On failure, store an error through @errp and return NULL. @@ -143,7 +143,7 @@ static QObject *keyval_parse_put(QDict *cur, if (qobject_type(old) != (value ? QTYPE_QSTRING : QTYPE_QDICT)) { error_setg(errp, "Parameters '%.*s.*' used inconsistently", (int)(key_cursor - key), key); - QDECREF(value); + qobject_unref(value); return NULL; } if (!value) { @@ -375,10 +375,10 @@ static QObject *keyval_listify(QDict *cur, GSList *key_of_cur, Error **errp) error_setg(errp, "Parameter '%s%d' missing", key, i); g_free(key); g_free(elt); - QDECREF(list); + qobject_unref(list); return NULL; } - qobject_incref(elt[i]); + qobject_ref(elt[i]); qlist_append_obj(list, elt[i]); } @@ -404,7 +404,7 @@ QDict *keyval_parse(const char *params, const char *implied_key, while (*s) { s = keyval_parse_one(qdict, s, implied_key, errp); if (!s) { - QDECREF(qdict); + qobject_unref(qdict); return NULL; } implied_key = NULL; @@ -412,7 +412,7 @@ QDict *keyval_parse(const char *params, const char *implied_key, listified = keyval_listify(qdict, NULL, errp); if (!listified) { - QDECREF(qdict); + qobject_unref(qdict); return NULL; } assert(listified == QOBJECT(qdict)); diff --git a/util/log.c b/util/log.c index 96f30dd21a..c0dbbd4700 100644 --- a/util/log.c +++ b/util/log.c @@ -256,6 +256,8 @@ const QEMULogItem qemu_log_items[] = { "show trace before each executed TB (lots of logs)" }, { CPU_LOG_TB_CPU, "cpu", "show CPU registers before entering a TB (lots of logs)" }, + { CPU_LOG_TB_FPU, "fpu", + "include FPU registers in the 'cpu' logging" }, { CPU_LOG_MMU, "mmu", "log MMU-related activities" }, { CPU_LOG_PCALL, "pcall", diff --git a/util/main-loop.c b/util/main-loop.c index 992f9b0f34..affe0403c5 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -222,36 +222,11 @@ static int os_host_main_loop_wait(int64_t timeout) { GMainContext *context = g_main_context_default(); int ret; - static int spin_counter; g_main_context_acquire(context); glib_pollfds_fill(&timeout); - /* If the I/O thread is very busy or we are incorrectly busy waiting in - * the I/O thread, this can lead to starvation of the BQL such that the - * VCPU threads never run. To make sure we can detect the later case, - * print a message to the screen. If we run into this condition, create - * a fake timeout in order to give the VCPU threads a chance to run. - */ - if (!timeout && (spin_counter > MAX_MAIN_LOOP_SPIN)) { - static bool notified; - - if (!notified && !qtest_enabled() && !qtest_driver()) { - warn_report("I/O thread spun for %d iterations", - MAX_MAIN_LOOP_SPIN); - notified = true; - } - - timeout = SCALE_MS; - } - - - if (timeout) { - spin_counter = 0; - } else { - spin_counter++; - } qemu_mutex_unlock_iothread(); replay_mutex_unlock(); diff --git a/util/memfd.c b/util/memfd.c index b3ecbac19e..d248a53c3c 100644 --- a/util/memfd.c +++ b/util/memfd.c @@ -66,7 +66,7 @@ int qemu_memfd_create(const char *name, size_t size, bool hugetlb, { int htsize = hugetlbsize ? ctz64(hugetlbsize) : 0; - if (htsize && 1 << htsize != hugetlbsize) { + if (htsize && 1ULL << htsize != hugetlbsize) { error_setg(errp, "Hugepage size must be a power of 2"); return -1; } diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 2fd8cbcc6f..fd329eccd8 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -50,19 +50,21 @@ size_t qemu_mempath_getpagesize(const char *mem_path) struct statfs fs; int ret; - do { - ret = statfs(mem_path, &fs); - } while (ret != 0 && errno == EINTR); + if (mem_path) { + do { + ret = statfs(mem_path, &fs); + } while (ret != 0 && errno == EINTR); - if (ret != 0) { - fprintf(stderr, "Couldn't statfs() memory path: %s\n", - strerror(errno)); - exit(1); - } + if (ret != 0) { + fprintf(stderr, "Couldn't statfs() memory path: %s\n", + strerror(errno)); + exit(1); + } - if (fs.f_type == HUGETLBFS_MAGIC) { - /* It's hugepage, return the huge page size */ - return fs.f_bsize; + if (fs.f_type == HUGETLBFS_MAGIC) { + /* It's hugepage, return the huge page size */ + return fs.f_bsize; + } } #ifdef __sparc__ /* SPARC Linux needs greater alignment than the pagesize */ diff --git a/util/osdep.c b/util/osdep.c index a73de0e1ba..1c8d1e2ee0 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -302,7 +302,8 @@ int qemu_open(const char *name, int flags, ...) } fd = monitor_fdset_get_fd(fdset_id, flags); - if (fd == -1) { + if (fd < 0) { + errno = -fd; return -1; } @@ -503,20 +504,6 @@ int socket_init(void) return 0; } -#if !GLIB_CHECK_VERSION(2, 31, 0) -/* Ensure that glib is running in multi-threaded mode - * Old versions of glib require explicit initialization. Failure to do - * this results in the single-threaded code paths being taken inside - * glib. For example, the g_slice allocator will not be thread-safe - * and cause crashes. - */ -static void __attribute__((constructor)) thread_init(void) -{ - if (!g_thread_supported()) { - g_thread_init(NULL); - } -} -#endif #ifndef CONFIG_IOVEC /* helper function for iov_send_recv() */ diff --git a/util/qemu-config.c b/util/qemu-config.c index 20f7d1429d..9d2e278e29 100644 --- a/util/qemu-config.c +++ b/util/qemu-config.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "block/qdict.h" /* for qdict_extract_subqdict() */ #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qmp/qdict.h" @@ -562,8 +563,8 @@ static void config_parse_qdict_section(QDict *options, QemuOptsList *opts, } out: - QDECREF(subqdict); - QDECREF(list); + qobject_unref(subqdict); + qobject_unref(list); } void qemu_config_parse_qdict(QDict *options, QemuOptsList **lists, diff --git a/util/qemu-option.c b/util/qemu-option.c index d0756fda58..01886efe90 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -43,26 +43,23 @@ * first byte of the option name) * * The option name is delimited by delim (usually , or =) or the string end - * and is copied into buf. If the option name is longer than buf_size, it is - * truncated. buf is always zero terminated. + * and is copied into option. The caller is responsible for free'ing option + * when no longer required. * * The return value is the position of the delimiter/zero byte after the option * name in p. */ -const char *get_opt_name(char *buf, int buf_size, const char *p, char delim) +static const char *get_opt_name(const char *p, char **option, char delim) { - char *q; + char *offset = strchr(p, delim); - q = buf; - while (*p != '\0' && *p != delim) { - if (q && (q - buf) < buf_size - 1) - *q++ = *p; - p++; + if (offset) { + *option = g_strndup(p, offset - p); + return offset; + } else { + *option = g_strdup(p); + return p + strlen(p); } - if (q) - *q = '\0'; - - return p; } /* @@ -73,25 +70,31 @@ const char *get_opt_name(char *buf, int buf_size, const char *p, char delim) * delimiter is fixed to be comma which starts a new option. To specify an * option value that contains commas, double each comma. */ -const char *get_opt_value(char *buf, int buf_size, const char *p) +const char *get_opt_value(const char *p, char **value) { - char *q; + size_t capacity = 0, length; + const char *offset; - q = buf; - while (*p != '\0') { - if (*p == ',') { - if (*(p + 1) != ',') - break; - p++; + *value = NULL; + while (1) { + offset = qemu_strchrnul(p, ','); + length = offset - p; + if (*offset != '\0' && *(offset + 1) == ',') { + length++; + } + *value = g_renew(char, *value, capacity + length + 1); + strncpy(*value + capacity, p, length); + (*value)[capacity + length] = '\0'; + capacity += length; + if (*offset == '\0' || + *(offset + 1) != ',') { + break; } - if (q && (q - buf) < buf_size - 1) - *q++ = *p; - p++; - } - if (q) - *q = '\0'; - return p; + p += (offset - p) + 2; + } + + return offset; } static void parse_option_bool(const char *name, const char *value, bool *ret, @@ -165,50 +168,43 @@ void parse_option_size(const char *name, const char *value, bool has_help_option(const char *param) { - size_t buflen = strlen(param) + 1; - char *buf = g_malloc(buflen); const char *p = param; bool result = false; - while (*p) { - p = get_opt_value(buf, buflen, p); + while (*p && !result) { + char *value; + + p = get_opt_value(p, &value); if (*p) { p++; } - if (is_help_option(buf)) { - result = true; - goto out; - } + result = is_help_option(value); + g_free(value); } -out: - g_free(buf); return result; } -bool is_valid_option_list(const char *param) +bool is_valid_option_list(const char *p) { - size_t buflen = strlen(param) + 1; - char *buf = g_malloc(buflen); - const char *p = param; - bool result = true; + char *value = NULL; + bool result = false; while (*p) { - p = get_opt_value(buf, buflen, p); - if (*p && !*++p) { - result = false; + p = get_opt_value(p, &value); + if ((*p && !*++p) || + (!*value || *value == ',')) { goto out; } - if (!*buf || *buf == ',') { - result = false; - goto out; - } + g_free(value); + value = NULL; } + result = true; out: - g_free(buf); + g_free(value); return result; } @@ -218,7 +214,6 @@ void qemu_opts_print_help(QemuOptsList *list) assert(list); desc = list->desc; - printf("Supported options:\n"); while (desc && desc->name) { printf("%-16s %s\n", desc->name, desc->help ? desc->help : "No description available"); @@ -490,7 +485,7 @@ int qemu_opt_unset(QemuOpts *opts, const char *name) } } -static void opt_set(QemuOpts *opts, const char *name, const char *value, +static void opt_set(QemuOpts *opts, const char *name, char *value, bool prepend, Error **errp) { QemuOpt *opt; @@ -499,6 +494,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, desc = find_desc_by_name(opts->list->desc, name); if (!desc && !opts_accepts_any(opts)) { + g_free(value); error_setg(errp, QERR_INVALID_PARAMETER, name); return; } @@ -512,8 +508,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, QTAILQ_INSERT_TAIL(&opts->head, opt, next); } opt->desc = desc; - opt->str = g_strdup(value); - assert(opt->str); + opt->str = value; qemu_opt_parse(opt, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -524,7 +519,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, void qemu_opt_set(QemuOpts *opts, const char *name, const char *value, Error **errp) { - opt_set(opts, name, value, false, errp); + opt_set(opts, name, g_strdup(value), false, errp); } void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, @@ -757,7 +752,8 @@ void qemu_opts_print(QemuOpts *opts, const char *separator) static void opts_do_parse(QemuOpts *opts, const char *params, const char *firstname, bool prepend, Error **errp) { - char option[128], value[1024]; + char *option = NULL; + char *value = NULL; const char *p,*pe,*pc; Error *local_err = NULL; @@ -768,39 +764,45 @@ static void opts_do_parse(QemuOpts *opts, const char *params, /* found "foo,more" */ if (p == params && firstname) { /* implicitly named first option */ - pstrcpy(option, sizeof(option), firstname); - p = get_opt_value(value, sizeof(value), p); + option = g_strdup(firstname); + p = get_opt_value(p, &value); } else { /* option without value, probably a flag */ - p = get_opt_name(option, sizeof(option), p, ','); + p = get_opt_name(p, &option, ','); if (strncmp(option, "no", 2) == 0) { memmove(option, option+2, strlen(option+2)+1); - pstrcpy(value, sizeof(value), "off"); + value = g_strdup("off"); } else { - pstrcpy(value, sizeof(value), "on"); + value = g_strdup("on"); } } } else { /* found "foo=bar,more" */ - p = get_opt_name(option, sizeof(option), p, '='); - if (*p != '=') { - break; - } + p = get_opt_name(p, &option, '='); + assert(*p == '='); p++; - p = get_opt_value(value, sizeof(value), p); + p = get_opt_value(p, &value); } if (strcmp(option, "id") != 0) { /* store and parse */ opt_set(opts, option, value, prepend, &local_err); + value = NULL; if (local_err) { error_propagate(errp, local_err); - return; + goto cleanup; } } if (*p != ',') { break; } + g_free(option); + g_free(value); + option = value = NULL; } + + cleanup: + g_free(option); + g_free(value); } /** @@ -819,7 +821,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, bool permit_abbrev, bool defaults, Error **errp) { const char *firstname; - char value[1024], *id = NULL; + char *id = NULL; const char *p; QemuOpts *opts; Error *local_err = NULL; @@ -828,11 +830,9 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, firstname = permit_abbrev ? list->implied_opt_name : NULL; if (strncmp(params, "id=", 3) == 0) { - get_opt_value(value, sizeof(value), params+3); - id = value; + get_opt_value(params + 3, &id); } else if ((p = strstr(params, ",id=")) != NULL) { - get_opt_value(value, sizeof(value), p+4); - id = value; + get_opt_value(p + 4, &id); } /* @@ -844,6 +844,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, */ assert(!defaults || list->merge_lists); opts = qemu_opts_create(list, id, !defaults, &local_err); + g_free(id); if (opts == NULL) { error_propagate(errp, local_err); return NULL; diff --git a/util/qemu-thread-common.h b/util/qemu-thread-common.h new file mode 100644 index 0000000000..a0ea7c0d92 --- /dev/null +++ b/util/qemu-thread-common.h @@ -0,0 +1,55 @@ +/* + * Common qemu-thread implementation header file. + * + * Copyright Red Hat, Inc. 2018 + * + * Authors: + * Peter Xu , + * + * 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 QEMU_THREAD_COMMON_H +#define QEMU_THREAD_COMMON_H + +#include "qemu/typedefs.h" +#include "qemu/thread.h" +#include "trace.h" + +static inline void qemu_mutex_post_init(QemuMutex *mutex) +{ +#ifdef CONFIG_DEBUG_MUTEX + mutex->file = NULL; + mutex->line = 0; +#endif + mutex->initialized = true; +} + +static inline void qemu_mutex_pre_lock(QemuMutex *mutex, + const char *file, int line) +{ + trace_qemu_mutex_lock(mutex, file, line); +} + +static inline void qemu_mutex_post_lock(QemuMutex *mutex, + const char *file, int line) +{ +#ifdef CONFIG_DEBUG_MUTEX + mutex->file = file; + mutex->line = line; +#endif + trace_qemu_mutex_locked(mutex, file, line); +} + +static inline void qemu_mutex_pre_unlock(QemuMutex *mutex, + const char *file, int line) +{ +#ifdef CONFIG_DEBUG_MUTEX + mutex->file = NULL; + mutex->line = 0; +#endif + trace_qemu_mutex_unlock(mutex, file, line); +} + +#endif diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index b789cf32e9..dfa66ff2fb 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -14,7 +14,7 @@ #include "qemu/thread.h" #include "qemu/atomic.h" #include "qemu/notify.h" -#include "trace.h" +#include "qemu-thread-common.h" static bool name_threads; @@ -43,7 +43,7 @@ void qemu_mutex_init(QemuMutex *mutex) err = pthread_mutex_init(&mutex->lock, NULL); if (err) error_exit(err, __func__); - mutex->initialized = true; + qemu_mutex_post_init(mutex); } void qemu_mutex_destroy(QemuMutex *mutex) @@ -62,13 +62,11 @@ void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line) int err; assert(mutex->initialized); - trace_qemu_mutex_lock(mutex, file, line); - + qemu_mutex_pre_lock(mutex, file, line); err = pthread_mutex_lock(&mutex->lock); if (err) error_exit(err, __func__); - - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); } int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) @@ -78,7 +76,7 @@ int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) assert(mutex->initialized); err = pthread_mutex_trylock(&mutex->lock); if (err == 0) { - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); return 0; } if (err != EBUSY) { @@ -92,11 +90,10 @@ void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line) int err; assert(mutex->initialized); + qemu_mutex_pre_unlock(mutex, file, line); err = pthread_mutex_unlock(&mutex->lock); if (err) error_exit(err, __func__); - - trace_qemu_mutex_unlock(mutex, file, line); } void qemu_rec_mutex_init(QemuRecMutex *mutex) @@ -160,9 +157,9 @@ void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, con int err; assert(cond->initialized); - trace_qemu_mutex_unlock(mutex, file, line); + qemu_mutex_pre_unlock(mutex, file, line); err = pthread_cond_wait(&cond->cond, &mutex->lock); - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); if (err) error_exit(err, __func__); } @@ -482,7 +479,6 @@ static void __attribute__((constructor)) qemu_thread_atexit_init(void) } -#ifdef CONFIG_PTHREAD_SETNAME_NP typedef struct { void *(*start_routine)(void *); void *arg; @@ -495,16 +491,18 @@ static void *qemu_thread_start(void *args) void *(*start_routine)(void *) = qemu_thread_args->start_routine; void *arg = qemu_thread_args->arg; +#ifdef CONFIG_PTHREAD_SETNAME_NP /* Attempt to set the threads name; note that this is for debug, so * we're not going to fail if we can't set it. */ - pthread_setname_np(pthread_self(), qemu_thread_args->name); + if (name_threads && qemu_thread_args->name) { + pthread_setname_np(pthread_self(), qemu_thread_args->name); + } +#endif g_free(qemu_thread_args->name); g_free(qemu_thread_args); return start_routine(arg); } -#endif - void qemu_thread_create(QemuThread *thread, const char *name, void *(*start_routine)(void*), @@ -513,6 +511,7 @@ void qemu_thread_create(QemuThread *thread, const char *name, sigset_t set, oldset; int err; pthread_attr_t attr; + QemuThreadArgs *qemu_thread_args; err = pthread_attr_init(&attr); if (err) { @@ -527,22 +526,13 @@ void qemu_thread_create(QemuThread *thread, const char *name, sigfillset(&set); pthread_sigmask(SIG_SETMASK, &set, &oldset); -#ifdef CONFIG_PTHREAD_SETNAME_NP - if (name_threads) { - QemuThreadArgs *qemu_thread_args; - qemu_thread_args = g_new0(QemuThreadArgs, 1); - qemu_thread_args->name = g_strdup(name); - qemu_thread_args->start_routine = start_routine; - qemu_thread_args->arg = arg; + qemu_thread_args = g_new0(QemuThreadArgs, 1); + qemu_thread_args->name = g_strdup(name); + qemu_thread_args->start_routine = start_routine; + qemu_thread_args->arg = arg; - err = pthread_create(&thread->thread, &attr, - qemu_thread_start, qemu_thread_args); - } else -#endif - { - err = pthread_create(&thread->thread, &attr, - start_routine, arg); - } + err = pthread_create(&thread->thread, &attr, + qemu_thread_start, qemu_thread_args); if (err) error_exit(err, __func__); diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index ab60c0d557..b303188a36 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -19,7 +19,7 @@ #include "qemu-common.h" #include "qemu/thread.h" #include "qemu/notify.h" -#include "trace.h" +#include "qemu-thread-common.h" #include static bool name_threads; @@ -46,7 +46,7 @@ static void error_exit(int err, const char *msg) void qemu_mutex_init(QemuMutex *mutex) { InitializeSRWLock(&mutex->lock); - mutex->initialized = true; + qemu_mutex_post_init(mutex); } void qemu_mutex_destroy(QemuMutex *mutex) @@ -59,10 +59,9 @@ void qemu_mutex_destroy(QemuMutex *mutex) void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line) { assert(mutex->initialized); - trace_qemu_mutex_lock(mutex, file, line); - + qemu_mutex_pre_lock(mutex, file, line); AcquireSRWLockExclusive(&mutex->lock); - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); } int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) @@ -72,7 +71,7 @@ int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) assert(mutex->initialized); owned = TryAcquireSRWLockExclusive(&mutex->lock); if (owned) { - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); return 0; } return -EBUSY; @@ -81,7 +80,7 @@ int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line) void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line) { assert(mutex->initialized); - trace_qemu_mutex_unlock(mutex, file, line); + qemu_mutex_pre_unlock(mutex, file, line); ReleaseSRWLockExclusive(&mutex->lock); } @@ -145,9 +144,9 @@ void qemu_cond_broadcast(QemuCond *cond) void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, const int line) { assert(cond->initialized); - trace_qemu_mutex_unlock(mutex, file, line); + qemu_mutex_pre_unlock(mutex, file, line); SleepConditionVariableSRW(&cond->var, &mutex->lock, INFINITE, 0); - trace_qemu_mutex_locked(mutex, file, line); + qemu_mutex_post_lock(mutex, file, line); } void qemu_sem_init(QemuSemaphore *sem, int init) diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 2ed1bf2778..86bfe84037 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -578,17 +578,10 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg) { int64_t deadline = -1; QEMUClockType type; - bool play = replay_mode == REPLAY_MODE_PLAY; for (type = 0; type < QEMU_CLOCK_MAX; type++) { if (qemu_clock_use_for_deadline(type)) { - if (!play || type == QEMU_CLOCK_REALTIME) { - deadline = qemu_soonest_timeout(deadline, - timerlist_deadline_ns(tlg->tl[type])); - } else { - /* Read clock from the replay file and - do not calculate the deadline, based on virtual clock. */ - qemu_clock_get_ns(type); - } + deadline = qemu_soonest_timeout(deadline, + timerlist_deadline_ns(tlg->tl[type])); } } return deadline; diff --git a/util/qht.c b/util/qht.c index ff4d2e6974..c138777a9c 100644 --- a/util/qht.c +++ b/util/qht.c @@ -351,11 +351,14 @@ static struct qht_map *qht_map_create(size_t n_buckets) return map; } -void qht_init(struct qht *ht, size_t n_elems, unsigned int mode) +void qht_init(struct qht *ht, qht_cmp_func_t cmp, size_t n_elems, + unsigned int mode) { struct qht_map *map; size_t n_buckets = qht_elems_to_buckets(n_elems); + g_assert(cmp); + ht->cmp = cmp; ht->mode = mode; qemu_mutex_init(&ht->lock); map = qht_map_create(n_buckets); @@ -479,8 +482,8 @@ void *qht_lookup__slowpath(struct qht_bucket *b, qht_lookup_func_t func, return ret; } -void *qht_lookup(struct qht *ht, qht_lookup_func_t func, const void *userp, - uint32_t hash) +void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash, + qht_lookup_func_t func) { struct qht_bucket *b; struct qht_map *map; @@ -502,10 +505,15 @@ void *qht_lookup(struct qht *ht, qht_lookup_func_t func, const void *userp, return qht_lookup__slowpath(b, func, userp, hash); } +void *qht_lookup(struct qht *ht, const void *userp, uint32_t hash) +{ + return qht_lookup_custom(ht, userp, hash, ht->cmp); +} + /* call with head->lock held */ -static bool qht_insert__locked(struct qht *ht, struct qht_map *map, - struct qht_bucket *head, void *p, uint32_t hash, - bool *needs_resize) +static void *qht_insert__locked(struct qht *ht, struct qht_map *map, + struct qht_bucket *head, void *p, uint32_t hash, + bool *needs_resize) { struct qht_bucket *b = head; struct qht_bucket *prev = NULL; @@ -515,8 +523,9 @@ static bool qht_insert__locked(struct qht *ht, struct qht_map *map, do { for (i = 0; i < QHT_BUCKET_ENTRIES; i++) { if (b->pointers[i]) { - if (unlikely(b->pointers[i] == p)) { - return false; + if (unlikely(b->hashes[i] == hash && + ht->cmp(b->pointers[i], p))) { + return b->pointers[i]; } } else { goto found; @@ -545,7 +554,7 @@ static bool qht_insert__locked(struct qht *ht, struct qht_map *map, atomic_set(&b->hashes[i], hash); atomic_set(&b->pointers[i], p); seqlock_write_end(&head->sequence); - return true; + return NULL; } static __attribute__((noinline)) void qht_grow_maybe(struct qht *ht) @@ -569,25 +578,31 @@ static __attribute__((noinline)) void qht_grow_maybe(struct qht *ht) qemu_mutex_unlock(&ht->lock); } -bool qht_insert(struct qht *ht, void *p, uint32_t hash) +bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing) { struct qht_bucket *b; struct qht_map *map; bool needs_resize = false; - bool ret; + void *prev; /* NULL pointers are not supported */ qht_debug_assert(p); b = qht_bucket_lock__no_stale(ht, hash, &map); - ret = qht_insert__locked(ht, map, b, p, hash, &needs_resize); + prev = qht_insert__locked(ht, map, b, p, hash, &needs_resize); qht_bucket_debug__locked(b); qemu_spin_unlock(&b->lock); if (unlikely(needs_resize) && ht->mode & QHT_MODE_AUTO_RESIZE) { qht_grow_maybe(ht); } - return ret; + if (likely(prev == NULL)) { + return true; + } + if (existing) { + *existing = prev; + } + return false; } static inline bool qht_entry_is_last(struct qht_bucket *b, int pos) @@ -759,7 +774,7 @@ static void qht_do_resize_reset(struct qht *ht, struct qht_map *new, bool reset) return; } - g_assert_cmpuint(new->n_buckets, !=, old->n_buckets); + g_assert(new->n_buckets != old->n_buckets); qht_map_iter__all_locked(ht, old, qht_map_copy, new); qht_map_debug__all_locked(new); diff --git a/util/uri.c b/util/uri.c index 93ecefdaaf..8bdef84120 100644 --- a/util/uri.c +++ b/util/uri.c @@ -52,6 +52,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu/uri.h" @@ -1065,10 +1066,7 @@ URI *uri_parse_raw(const char *str, int raw) */ URI *uri_new(void) { - URI *ret; - - ret = g_new0(URI, 1); - return ret; + return g_new0(URI, 1); } /** @@ -2269,10 +2267,7 @@ struct QueryParams *query_params_parse(const char *query) /* Find the next separator, or end of the string. */ end = strchr(query, '&'); if (!end) { - end = strchr(query, ';'); - } - if (!end) { - end = query + strlen(query); + end = qemu_strchrnul(query, ';'); } /* Find the first '=' character between here and end. */ diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index 006674c916..1d9272efa4 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -522,8 +522,7 @@ static IOVAMapping *qemu_vfio_add_mapping(QEMUVFIOState *s, assert(index >= 0); s->nr_mappings++; - s->mappings = g_realloc_n(s->mappings, sizeof(s->mappings[0]), - s->nr_mappings); + s->mappings = g_renew(IOVAMapping, s->mappings, s->nr_mappings); insert = &s->mappings[index]; shift = s->nr_mappings - index - 1; if (shift) { @@ -577,8 +576,7 @@ static void qemu_vfio_undo_mapping(QEMUVFIOState *s, IOVAMapping *mapping, memmove(mapping, &s->mappings[index + 1], sizeof(s->mappings[0]) * (s->nr_mappings - index - 1)); s->nr_mappings--; - s->mappings = g_realloc_n(s->mappings, sizeof(s->mappings[0]), - s->nr_mappings); + s->mappings = g_renew(IOVAMapping, s->mappings, s->nr_mappings); } /* Check if the mapping list is (ascending) ordered. */ diff --git a/vl.c b/vl.c index fce1fd12d8..16b913f9d5 100644 --- a/vl.c +++ b/vl.c @@ -23,16 +23,13 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "qemu-version.h" #include "qemu/cutils.h" #include "qemu/help_option.h" #include "qemu/uuid.h" - -#ifdef CONFIG_SECCOMP -#include #include "sysemu/seccomp.h" -#endif #ifdef CONFIG_SDL #if defined(__APPLE__) || defined(main) @@ -120,12 +117,14 @@ int main(int argc, char **argv) #include "ui/qemu-spice.h" #include "qapi/string-input-visitor.h" #include "qapi/opts-visitor.h" +#include "qapi/clone-visitor.h" #include "qom/object_interfaces.h" #include "exec/semihost.h" #include "crypto/init.h" #include "sysemu/replay.h" #include "qapi/qapi-events-run-state.h" #include "qapi/qapi-visit-block-core.h" +#include "qapi/qapi-visit-ui.h" #include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-run-state.h" @@ -133,7 +132,6 @@ int main(int argc, char **argv) #include "sysemu/iothread.h" #define MAX_VIRTIO_CONSOLES 1 -#define MAX_SCLP_CONSOLES 1 static const char *data_dir[16]; static int data_dir_idx; @@ -145,6 +143,7 @@ ram_addr_t ram_size; const char *mem_path = NULL; int mem_prealloc = 0; /* force preallocation of physical target memory */ bool enable_mlock = false; +bool enable_cpu_pm = false; int nb_nics; NICInfo nd_table[MAX_NICS]; int autostart; @@ -154,10 +153,10 @@ QEMUClockType rtc_clock; int vga_interface_type = VGA_NONE; static DisplayOptions dpy; int no_frame; -Chardev *serial_hds[MAX_SERIAL_PORTS]; +static int num_serial_hds; +static Chardev **serial_hds; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES]; -Chardev *sclp_hds[MAX_SCLP_CONSOLES]; int win2k_install_hack = 0; int singlestep = 0; int smp_cpus; @@ -209,7 +208,6 @@ static int has_defaults = 1; static int default_serial = 1; static int default_parallel = 1; static int default_virtcon = 1; -static int default_sclp = 1; static int default_monitor = 1; static int default_floppy = 1; static int default_cdrom = 1; @@ -259,35 +257,6 @@ static QemuOptsList qemu_rtc_opts = { }, }; -static QemuOptsList qemu_sandbox_opts = { - .name = "sandbox", - .implied_opt_name = "enable", - .head = QTAILQ_HEAD_INITIALIZER(qemu_sandbox_opts.head), - .desc = { - { - .name = "enable", - .type = QEMU_OPT_BOOL, - }, - { - .name = "obsolete", - .type = QEMU_OPT_STRING, - }, - { - .name = "elevateprivileges", - .type = QEMU_OPT_STRING, - }, - { - .name = "spawn", - .type = QEMU_OPT_STRING, - }, - { - .name = "resourcecontrol", - .type = QEMU_OPT_STRING, - }, - { /* end of list */ } - }, -}; - static QemuOptsList qemu_option_rom_opts = { .name = "option-rom", .implied_opt_name = "romfile", @@ -423,6 +392,22 @@ static QemuOptsList qemu_realtime_opts = { }, }; +static QemuOptsList qemu_overcommit_opts = { + .name = "overcommit", + .head = QTAILQ_HEAD_INITIALIZER(qemu_overcommit_opts.head), + .desc = { + { + .name = "mem-lock", + .type = QEMU_OPT_BOOL, + }, + { + .name = "cpu-pm", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; + static QemuOptsList qemu_msg_opts = { .name = "msg", .head = QTAILQ_HEAD_INITIALIZER(qemu_msg_opts.head), @@ -594,7 +579,7 @@ static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp) /***********************************************************/ /* QEMU state */ -static RunState current_run_state = RUN_STATE_PRELAUNCH; +static RunState current_run_state = RUN_STATE_PRECONFIG; /* We use RUN_STATE__MAX but any invalid value will do */ static RunState vmstop_requested = RUN_STATE__MAX; @@ -607,6 +592,13 @@ typedef struct { static const RunStateTransition runstate_transitions_def[] = { /* from -> to */ + { RUN_STATE_PRECONFIG, RUN_STATE_PRELAUNCH }, + /* Early switch to inmigrate state to allow -incoming CLI option work + * as it used to. TODO: delay actual switching to inmigrate state to + * the point after machine is built and remove this hack. + */ + { RUN_STATE_PRECONFIG, RUN_STATE_INMIGRATE }, + { RUN_STATE_DEBUG, RUN_STATE_RUNNING }, { RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_DEBUG, RUN_STATE_PRELAUNCH }, @@ -1043,88 +1035,6 @@ static int bt_parse(const char *opt) return 1; } -static int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) -{ - if (qemu_opt_get_bool(opts, "enable", false)) { -#ifdef CONFIG_SECCOMP - uint32_t seccomp_opts = QEMU_SECCOMP_SET_DEFAULT - | QEMU_SECCOMP_SET_OBSOLETE; - const char *value = NULL; - - value = qemu_opt_get(opts, "obsolete"); - if (value) { - if (g_str_equal(value, "allow")) { - seccomp_opts &= ~QEMU_SECCOMP_SET_OBSOLETE; - } else if (g_str_equal(value, "deny")) { - /* this is the default option, this if is here - * to provide a little bit of consistency for - * the command line */ - } else { - error_report("invalid argument for obsolete"); - return -1; - } - } - - value = qemu_opt_get(opts, "elevateprivileges"); - if (value) { - if (g_str_equal(value, "deny")) { - seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; - } else if (g_str_equal(value, "children")) { - seccomp_opts |= QEMU_SECCOMP_SET_PRIVILEGED; - - /* calling prctl directly because we're - * not sure if host has CAP_SYS_ADMIN set*/ - if (prctl(PR_SET_NO_NEW_PRIVS, 1)) { - error_report("failed to set no_new_privs " - "aborting"); - return -1; - } - } else if (g_str_equal(value, "allow")) { - /* default value */ - } else { - error_report("invalid argument for elevateprivileges"); - return -1; - } - } - - value = qemu_opt_get(opts, "spawn"); - if (value) { - if (g_str_equal(value, "deny")) { - seccomp_opts |= QEMU_SECCOMP_SET_SPAWN; - } else if (g_str_equal(value, "allow")) { - /* default value */ - } else { - error_report("invalid argument for spawn"); - return -1; - } - } - - value = qemu_opt_get(opts, "resourcecontrol"); - if (value) { - if (g_str_equal(value, "deny")) { - seccomp_opts |= QEMU_SECCOMP_SET_RESOURCECTL; - } else if (g_str_equal(value, "allow")) { - /* default value */ - } else { - error_report("invalid argument for resourcecontrol"); - return -1; - } - } - - if (seccomp_start(seccomp_opts) < 0) { - error_report("failed to install seccomp syscall filter " - "in the kernel"); - return -1; - } -#else - error_report("seccomp support is disabled"); - return -1; -#endif - } - - return 0; -} - static int parse_name(void *opaque, QemuOpts *opts, Error **errp) { const char *proc_name; @@ -1630,6 +1540,7 @@ static pid_t shutdown_pid; static int powerdown_requested; static int debug_requested; static int suspend_requested; +static bool preconfig_exit_requested = true; static WakeupReason wakeup_reason; static NotifierList powerdown_notifiers = NOTIFIER_LIST_INITIALIZER(powerdown_notifiers); @@ -1714,6 +1625,11 @@ static int qemu_debug_requested(void) return r; } +void qemu_exit_preconfig_request(void) +{ + preconfig_exit_requested = true; +} + /* * Reset the VM. Issue an event unless @reason is SHUTDOWN_CAUSE_NONE. */ @@ -1730,7 +1646,7 @@ void qemu_system_reset(ShutdownCause reason) } else { qemu_devices_reset(); } - if (reason) { + if (reason != SHUTDOWN_CAUSE_SUBSYSTEM_RESET) { qapi_event_send_reset(shutdown_caused_by_guest(reason), &error_abort); } @@ -1776,7 +1692,7 @@ void qemu_system_guest_panicked(GuestPanicInformation *info) void qemu_system_reset_request(ShutdownCause reason) { - if (no_reboot) { + if (no_reboot && reason != SHUTDOWN_CAUSE_SUBSYSTEM_RESET) { shutdown_requested = reason; } else { reset_requested = reason; @@ -1887,6 +1803,13 @@ static bool main_loop_should_exit(void) RunState r; ShutdownCause request; + if (preconfig_exit_requested) { + if (runstate_check(RUN_STATE_PRECONFIG)) { + runstate_set(RUN_STATE_PRELAUNCH); + } + preconfig_exit_requested = false; + return true; + } if (qemu_debug_requested()) { vm_stop(RUN_STATE_DEBUG); } @@ -1936,7 +1859,7 @@ static void main_loop(void) #ifdef CONFIG_PROFILER int64_t ti; #endif - do { + while (!main_loop_should_exit()) { #ifdef CONFIG_PROFILER ti = profile_getclock(); #endif @@ -1944,7 +1867,7 @@ static void main_loop(void) #ifdef CONFIG_PROFILER dev_time += profile_getclock() - ti; #endif - } while (!main_loop_should_exit()); + } } static void version(void) @@ -2090,11 +2013,40 @@ static void select_vgahw(const char *p) } } +static void parse_display_qapi(const char *optarg) +{ + Error *err = NULL; + DisplayOptions *opts; + Visitor *v; + + v = qobject_input_visitor_new_str(optarg, "type", &err); + if (!v) { + error_report_err(err); + exit(1); + } + + visit_type_DisplayOptions(v, NULL, &opts, &error_fatal); + QAPI_CLONE_MEMBERS(DisplayOptions, &dpy, opts); + + qapi_free_DisplayOptions(opts); + visit_free(v); +} + static void parse_display(const char *p) { const char *opts; if (strstart(p, "sdl", &opts)) { + /* + * sdl DisplayType needs hand-crafted parser instead of + * parse_display_qapi() due to some options not in + * DisplayOptions, specifically: + * - frame + * Already deprecated. + * - ctrl_grab + alt_grab + * Not clear yet what happens to them long-term. Should + * replaced by something better or deprecated and dropped. + */ dpy.type = DISPLAY_TYPE_SDL; while (*opts) { const char *nextopt; @@ -2142,9 +2094,13 @@ static void parse_display(const char *p) opts = nextopt; dpy.has_gl = true; if (strstart(opts, "on", &nextopt)) { - dpy.gl = true; + dpy.gl = DISPLAYGL_MODE_ON; + } else if (strstart(opts, "core", &nextopt)) { + dpy.gl = DISPLAYGL_MODE_CORE; + } else if (strstart(opts, "es", &nextopt)) { + dpy.gl = DISPLAYGL_MODE_ES; } else if (strstart(opts, "off", &nextopt)) { - dpy.gl = false; + dpy.gl = DISPLAYGL_MODE_OFF; } else { goto invalid_sdl_args; } @@ -2156,53 +2112,18 @@ static void parse_display(const char *p) opts = nextopt; } } else if (strstart(p, "vnc", &opts)) { + /* + * vnc isn't a (local) DisplayType but a protocol for remote + * display access. + */ if (*opts == '=') { vnc_parse(opts + 1, &error_fatal); } else { error_report("VNC requires a display argument vnc="); exit(1); } - } else if (strstart(p, "egl-headless", &opts)) { - dpy.type = DISPLAY_TYPE_EGL_HEADLESS; - } else if (strstart(p, "curses", &opts)) { - dpy.type = DISPLAY_TYPE_CURSES; - } else if (strstart(p, "gtk", &opts)) { - dpy.type = DISPLAY_TYPE_GTK; - while (*opts) { - const char *nextopt; - - if (strstart(opts, ",grab_on_hover=", &nextopt)) { - opts = nextopt; - dpy.u.gtk.has_grab_on_hover = true; - if (strstart(opts, "on", &nextopt)) { - dpy.u.gtk.grab_on_hover = true; - } else if (strstart(opts, "off", &nextopt)) { - dpy.u.gtk.grab_on_hover = false; - } else { - goto invalid_gtk_args; - } - } else if (strstart(opts, ",gl=", &nextopt)) { - opts = nextopt; - dpy.has_gl = true; - if (strstart(opts, "on", &nextopt)) { - dpy.gl = true; - } else if (strstart(opts, "off", &nextopt)) { - dpy.gl = false; - } else { - goto invalid_gtk_args; - } - } else { - invalid_gtk_args: - error_report("invalid GTK option string"); - exit(1); - } - opts = nextopt; - } - } else if (strstart(p, "none", &opts)) { - dpy.type = DISPLAY_TYPE_NONE; } else { - error_report("unknown display type"); - exit(1); + parse_display_qapi(p); } } @@ -2496,26 +2417,38 @@ static int foreach_device_config(int type, int (*func)(const char *cmdline)) static int serial_parse(const char *devname) { - static int index = 0; + int index = num_serial_hds; char label[32]; if (strcmp(devname, "none") == 0) return 0; - if (index == MAX_SERIAL_PORTS) { - error_report("too many serial ports"); - exit(1); - } snprintf(label, sizeof(label), "serial%d", index); + serial_hds = g_renew(Chardev *, serial_hds, index + 1); + serial_hds[index] = qemu_chr_new(label, devname); if (!serial_hds[index]) { error_report("could not connect serial device" " to character backend '%s'", devname); return -1; } - index++; + num_serial_hds++; return 0; } +Chardev *serial_hd(int i) +{ + assert(i >= 0); + if (i < num_serial_hds) { + return serial_hds[i]; + } + return NULL; +} + +int serial_max_hds(void) +{ + return num_serial_hds; +} + static int parallel_parse(const char *devname) { static int index = 0; @@ -2571,39 +2504,6 @@ static int virtcon_parse(const char *devname) return 0; } -static int sclp_parse(const char *devname) -{ - QemuOptsList *device = qemu_find_opts("device"); - static int index = 0; - char label[32]; - QemuOpts *dev_opts; - - if (strcmp(devname, "none") == 0) { - return 0; - } - if (index == MAX_SCLP_CONSOLES) { - error_report("too many sclp consoles"); - exit(1); - } - - assert(arch_type == QEMU_ARCH_S390X); - - dev_opts = qemu_opts_create(device, NULL, 0, NULL); - qemu_opt_set(dev_opts, "driver", "sclpconsole", &error_abort); - - snprintf(label, sizeof(label), "sclpcon%d", index); - sclp_hds[index] = qemu_chr_new(label, devname); - if (!sclp_hds[index]) { - error_report("could not connect sclp console" - " to character backend '%s'", devname); - return -1; - } - qemu_opt_set(dev_opts, "chardev", label, &error_abort); - - index++; - return 0; -} - static int debugcon_parse(const char *devname) { QemuOpts *opts; @@ -2678,8 +2578,9 @@ static gint machine_class_cmp(gconstpointer a, gconstpointer b) if (mc->alias) { printf("%-20s %s (alias of %s)\n", mc->alias, mc->desc, mc->name); } - printf("%-20s %s%s\n", mc->name, mc->desc, - mc->is_default ? " (default)" : ""); + printf("%-20s %s%s%s\n", mc->name, mc->desc, + mc->is_default ? " (default)" : "", + mc->deprecation_reason ? " (deprecated)" : ""); } } @@ -2887,7 +2788,6 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, { uint64_t sz; const char *mem_str; - const char *maxmem_str, *slots_str; const ram_addr_t default_ram_size = mc->default_ram_size; QemuOpts *opts = qemu_find_opts_singleton("memory"); Location loc; @@ -2909,8 +2809,8 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) { uint64_t overflow_check = sz; - sz <<= 20; - if ((sz >> 20) != overflow_check) { + sz *= MiB; + if (sz / MiB != overflow_check) { error_report("too large 'size' option value"); exit(EXIT_FAILURE); } @@ -2933,9 +2833,7 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, qemu_opt_set_number(opts, "size", ram_size, &error_abort); *maxram_size = ram_size; - maxmem_str = qemu_opt_get(opts, "maxmem"); - slots_str = qemu_opt_get(opts, "slots"); - if (maxmem_str && slots_str) { + if (qemu_opt_get(opts, "maxmem")) { uint64_t slots; sz = qemu_opt_get_size(opts, "maxmem", 0); @@ -2946,13 +2844,7 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, "the initial memory size (0x" RAM_ADDR_FMT ")", sz, ram_size); exit(EXIT_FAILURE); - } else if (sz > ram_size) { - if (!slots) { - error_report("invalid value of -m option: maxmem was " - "specified, but no hotplug slots were specified"); - exit(EXIT_FAILURE); - } - } else if (slots) { + } else if (slots && sz == ram_size) { error_report("invalid value of -m option maxmem: " "memory slots were specified but maximum memory size " "(0x%" PRIx64 ") is equal to the initial memory size " @@ -2962,10 +2854,8 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, *maxram_size = sz; *ram_slots = slots; - } else if ((!maxmem_str && slots_str) || - (maxmem_str && !slots_str)) { - error_report("invalid -m option value: missing " - "'%s' option", slots_str ? "maxmem" : "slots"); + } else if (qemu_opt_get(opts, "slots")) { + error_report("invalid -m option value: missing 'maxmem' option"); exit(EXIT_FAILURE); } @@ -3093,7 +2983,6 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_mem_opts); qemu_add_opts(&qemu_smp_opts); qemu_add_opts(&qemu_boot_opts); - qemu_add_opts(&qemu_sandbox_opts); qemu_add_opts(&qemu_add_fd_opts); qemu_add_opts(&qemu_object_opts); qemu_add_opts(&qemu_tpmdev_opts); @@ -3108,6 +2997,7 @@ int main(int argc, char **argv, char **envp) runstate_init(); postcopy_infrastructure_init(); + monitor_init_globals(); if (qcrypto_init(&err) < 0) { error_reportf_err(err, "cannot initialize crypto: "); @@ -3168,11 +3058,6 @@ int main(int argc, char **argv, char **envp) exit(1); } switch(popt->index) { - case QEMU_OPTION_no_kvm_irqchip: { - olist = qemu_find_opts("machine"); - qemu_opts_parse_noisily(olist, "kernel_irqchip=off", false); - break; - } case QEMU_OPTION_cpu: /* hw initialization will check this */ cpu_model = optarg; @@ -3617,6 +3502,8 @@ int main(int argc, char **argv, char **envp) } break; case QEMU_OPTION_virtiocon: + warn_report("This option is deprecated, " + "use '-device virtconsole' instead"); add_device_config(DEV_VIRTCON, optarg); default_virtcon = 0; if (strncmp(optarg, "mon:", 4) == 0) { @@ -3704,11 +3591,15 @@ int main(int argc, char **argv, char **envp) exit(1); } break; + case QEMU_OPTION_preconfig: + preconfig_exit_requested = false; + break; case QEMU_OPTION_enable_kvm: olist = qemu_find_opts("machine"); qemu_opts_parse_noisily(olist, "accel=kvm", false); break; case QEMU_OPTION_enable_hax: + warn_report("Option is deprecated, use '-accel hax' instead"); olist = qemu_find_opts("machine"); qemu_opts_parse_noisily(olist, "accel=hax", false); break; @@ -3724,18 +3615,6 @@ int main(int argc, char **argv, char **envp) olist = qemu_find_opts("machine"); qemu_opts_parse_noisily(olist, "accel=tcg", false); break; - case QEMU_OPTION_no_kvm_pit_reinjection: { - static GlobalProperty kvm_pit_lost_tick_policy = { - .driver = "kvm-pit", - .property = "lost_tick_policy", - .value = "discard", - }; - - warn_report("deprecated, replaced by " - "-global kvm-pit.lost_tick_policy=discard"); - qdev_prop_register_global(&kvm_pit_lost_tick_policy); - break; - } case QEMU_OPTION_accel: accel_opts = qemu_opts_parse_noisily(qemu_find_opts("accel"), optarg, true); @@ -3878,6 +3757,7 @@ int main(int argc, char **argv, char **envp) /* Clock options no longer exist. Keep this option for * backward compatibility. */ + warn_report("This option is ignored and will be removed soon"); break; case QEMU_OPTION_startdate: warn_report("This option is deprecated, use '-rtc base=' instead."); @@ -4006,11 +3886,17 @@ int main(int argc, char **argv, char **envp) qtest_log = optarg; break; case QEMU_OPTION_sandbox: +#ifdef CONFIG_SECCOMP opts = qemu_opts_parse_noisily(qemu_find_opts("sandbox"), optarg, true); if (!opts) { exit(1); } +#else + error_report("-sandbox support is not enabled " + "in this QEMU binary"); + exit(1); +#endif break; case QEMU_OPTION_add_fd: #ifndef _WIN32 @@ -4038,7 +3924,20 @@ int main(int argc, char **argv, char **envp) if (!opts) { exit(1); } - enable_mlock = qemu_opt_get_bool(opts, "mlock", true); + /* Don't override the -overcommit option if set */ + enable_mlock = enable_mlock || + qemu_opt_get_bool(opts, "mlock", true); + break; + case QEMU_OPTION_overcommit: + opts = qemu_opts_parse_noisily(qemu_find_opts("overcommit"), + optarg, false); + if (!opts) { + exit(1); + } + /* Don't override the -realtime option if set */ + enable_mlock = enable_mlock || + qemu_opt_get_bool(opts, "mem-lock", false); + enable_cpu_pm = qemu_opt_get_bool(opts, "cpu-pm", false); break; case QEMU_OPTION_msg: opts = qemu_opts_parse_noisily(qemu_find_opts("msg"), optarg, @@ -4060,8 +3959,15 @@ int main(int argc, char **argv, char **envp) exit(1); } break; + case QEMU_OPTION_nodefconfig: + case QEMU_OPTION_nouserconfig: + /* Nothing to be parsed here. Especially, do not error out below. */ + break; default: - os_parse_cmd_args(popt->index, optarg); + if (os_parse_cmd_args(popt->index, optarg)) { + error_report("Option not supported in this build"); + exit(1); + } } } } @@ -4073,6 +3979,12 @@ int main(int argc, char **argv, char **envp) replay_configure(icount_opts); + if (incoming && !preconfig_exit_requested) { + error_report("'preconfig' and 'incoming' options are " + "mutually exclusive"); + exit(EXIT_FAILURE); + } + machine_class = select_machine(); set_memory_options(&ram_slots, &maxram_size, machine_class); @@ -4090,10 +4002,12 @@ int main(int argc, char **argv, char **envp) exit(1); } +#ifdef CONFIG_SECCOMP if (qemu_opts_foreach(qemu_find_opts("sandbox"), parse_sandbox, NULL, NULL)) { exit(1); } +#endif if (qemu_opts_foreach(qemu_find_opts("name"), parse_name, NULL, NULL)) { @@ -4237,9 +4151,6 @@ int main(int argc, char **argv, char **envp) if (!has_defaults || !machine_class->use_virtcon) { default_virtcon = 0; } - if (!has_defaults || !machine_class->use_sclp) { - default_sclp = 0; - } if (!has_defaults || machine_class->no_floppy) { default_floppy = 0; } @@ -4256,6 +4167,12 @@ int main(int argc, char **argv, char **envp) } if (is_daemonized()) { + if (!preconfig_exit_requested) { + error_report("'preconfig' and 'daemonize' options are " + "mutually exclusive"); + exit(EXIT_FAILURE); + } + /* According to documentation and historically, -nographic redirects * serial port, parallel port and monitor to stdio, which does not work * with -daemonize. We can redirect these to null instead, but since @@ -4286,16 +4203,11 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_SERIAL, "mon:stdio"); } else if (default_virtcon && default_monitor) { add_device_config(DEV_VIRTCON, "mon:stdio"); - } else if (default_sclp && default_monitor) { - add_device_config(DEV_SCLP, "mon:stdio"); } else { if (default_serial) add_device_config(DEV_SERIAL, "stdio"); if (default_virtcon) add_device_config(DEV_VIRTCON, "stdio"); - if (default_sclp) { - add_device_config(DEV_SCLP, "stdio"); - } if (default_monitor) monitor_parse("stdio", "readline", false); } @@ -4308,9 +4220,6 @@ int main(int argc, char **argv, char **envp) monitor_parse("vc:80Cx24C", "readline", false); if (default_virtcon) add_device_config(DEV_VIRTCON, "vc:80Cx24C"); - if (default_sclp) { - add_device_config(DEV_SCLP, "vc:80Cx24C"); - } } #if defined(CONFIG_VNC) @@ -4343,7 +4252,7 @@ int main(int argc, char **argv, char **envp) qemu_display_early_init(&dpy); qemu_console_early_init(); - if (dpy.has_gl && dpy.gl && display_opengl == 0) { + if (dpy.has_gl && dpy.gl != DISPLAYGL_MODE_OFF && display_opengl == 0) { #if defined(CONFIG_OPENGL) error_report("OpenGL is not supported by the display"); #else @@ -4387,6 +4296,11 @@ int main(int argc, char **argv, char **envp) configure_accelerator(current_machine); + if (!qtest_enabled() && machine_class->deprecation_reason) { + error_report("Machine type '%s' is deprecated: %s", + machine_class->name, machine_class->deprecation_reason); + } + /* * Register all the global properties, including accel properties, * machine properties, and user-specified ones. @@ -4543,12 +4457,6 @@ int main(int argc, char **argv, char **envp) default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); - /* - * Note: qtest_enabled() (which is used in monitor_qapi_event_init()) - * depends on configure_accelerator() above. - */ - monitor_init_globals(); - if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, NULL)) { exit(1); @@ -4560,9 +4468,6 @@ int main(int argc, char **argv, char **envp) exit(1); if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0) exit(1); - if (foreach_device_config(DEV_SCLP, sclp_parse) < 0) { - exit(1); - } if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) exit(1); @@ -4604,6 +4509,10 @@ int main(int argc, char **argv, char **envp) } parse_numa_opts(current_machine); + /* do monitor/qmp handling at preconfig state if requested */ + main_loop(); + + /* from here on runstate is RUN_STATE_PRELAUNCH */ machine_run_board_init(current_machine); realtime_init(); @@ -4729,6 +4638,7 @@ int main(int argc, char **argv, char **envp) vm_start(); } + accel_setup_post(current_machine); os_setup_post(); main_loop(); @@ -4738,6 +4648,7 @@ int main(int argc, char **argv, char **envp) /* No more vcpu or device emulation activity beyond this point */ vm_shutdown(); + job_cancel_sync_all(); bdrv_close_all(); res_free(); diff --git a/win_dump.c b/win_dump.c new file mode 100644 index 0000000000..b15c191ad7 --- /dev/null +++ b/win_dump.c @@ -0,0 +1,385 @@ +/* + * Windows crashdump + * + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "elf.h" +#include "cpu.h" +#include "exec/hwaddr.h" +#include "monitor/monitor.h" +#include "sysemu/kvm.h" +#include "sysemu/dump.h" +#include "sysemu/sysemu.h" +#include "sysemu/memory_mapping.h" +#include "sysemu/cpus.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qemu/error-report.h" +#include "hw/misc/vmcoreinfo.h" +#include "win_dump.h" + +static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp) +{ + void *buf; + uint64_t addr = run->BasePage << TARGET_PAGE_BITS; + uint64_t size = run->PageCount << TARGET_PAGE_BITS; + uint64_t len = size; + + buf = cpu_physical_memory_map(addr, &len, false); + if (!buf) { + error_setg(errp, "win-dump: failed to map run"); + return 0; + } + if (len != size) { + error_setg(errp, "win-dump: failed to map entire run"); + len = 0; + goto out_unmap; + } + + len = qemu_write_full(fd, buf, len); + if (len != size) { + error_setg(errp, QERR_IO_ERROR); + } + +out_unmap: + cpu_physical_memory_unmap(buf, addr, false, len); + + return len; +} + +static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp) +{ + WinDumpPhyMemDesc64 *desc = &h->PhysicalMemoryBlock; + WinDumpPhyMemRun64 *run = desc->Run; + Error *local_err = NULL; + int i; + + for (i = 0; i < desc->NumberOfRuns; i++) { + s->written_size += write_run(run + i, s->fd, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } +} + +static void patch_mm_pfn_database(WinDumpHeader64 *h, Error **errp) +{ + if (cpu_memory_rw_debug(first_cpu, + h->KdDebuggerDataBlock + KDBG_MM_PFN_DATABASE_OFFSET64, + (uint8_t *)&h->PfnDatabase, sizeof(h->PfnDatabase), 0)) { + error_setg(errp, "win-dump: failed to read MmPfnDatabase"); + return; + } +} + +static void patch_bugcheck_data(WinDumpHeader64 *h, Error **errp) +{ + uint64_t KiBugcheckData; + + if (cpu_memory_rw_debug(first_cpu, + h->KdDebuggerDataBlock + KDBG_KI_BUGCHECK_DATA_OFFSET64, + (uint8_t *)&KiBugcheckData, sizeof(KiBugcheckData), 0)) { + error_setg(errp, "win-dump: failed to read KiBugcheckData"); + return; + } + + if (cpu_memory_rw_debug(first_cpu, + KiBugcheckData, + h->BugcheckData, sizeof(h->BugcheckData), 0)) { + error_setg(errp, "win-dump: failed to read bugcheck data"); + return; + } + + /* + * If BugcheckCode wasn't saved, we consider guest OS as alive. + */ + + if (!h->BugcheckCode) { + h->BugcheckCode = LIVE_SYSTEM_DUMP; + } +} + +/* + * This routine tries to correct mistakes in crashdump header. + */ +static void patch_header(WinDumpHeader64 *h) +{ + Error *local_err = NULL; + + h->RequiredDumpSpace = sizeof(WinDumpHeader64) + + (h->PhysicalMemoryBlock.NumberOfPages << TARGET_PAGE_BITS); + h->PhysicalMemoryBlock.unused = 0; + h->unused1 = 0; + + patch_mm_pfn_database(h, &local_err); + if (local_err) { + warn_report_err(local_err); + local_err = NULL; + } + patch_bugcheck_data(h, &local_err); + if (local_err) { + warn_report_err(local_err); + } +} + +static void check_header(WinDumpHeader64 *h, Error **errp) +{ + const char Signature[] = "PAGE"; + const char ValidDump[] = "DU64"; + + if (memcmp(h->Signature, Signature, sizeof(h->Signature))) { + error_setg(errp, "win-dump: invalid header, expected '%.4s'," + " got '%.4s'", Signature, h->Signature); + return; + } + + if (memcmp(h->ValidDump, ValidDump, sizeof(h->ValidDump))) { + error_setg(errp, "win-dump: invalid header, expected '%.4s'," + " got '%.4s'", ValidDump, h->ValidDump); + return; + } +} + +static void check_kdbg(WinDumpHeader64 *h, Error **errp) +{ + const char OwnerTag[] = "KDBG"; + char read_OwnerTag[4]; + uint64_t KdDebuggerDataBlock = h->KdDebuggerDataBlock; + bool try_fallback = true; + +try_again: + if (cpu_memory_rw_debug(first_cpu, + KdDebuggerDataBlock + KDBG_OWNER_TAG_OFFSET64, + (uint8_t *)&read_OwnerTag, sizeof(read_OwnerTag), 0)) { + error_setg(errp, "win-dump: failed to read OwnerTag"); + return; + } + + if (memcmp(read_OwnerTag, OwnerTag, sizeof(read_OwnerTag))) { + if (try_fallback) { + /* + * If attempt to use original KDBG failed + * (most likely because of its encryption), + * we try to use KDBG obtained by guest driver. + */ + + KdDebuggerDataBlock = h->BugcheckParameter1; + try_fallback = false; + goto try_again; + } else { + error_setg(errp, "win-dump: invalid KDBG OwnerTag," + " expected '%.4s', got '%.4s'", + OwnerTag, read_OwnerTag); + return; + } + } + + h->KdDebuggerDataBlock = KdDebuggerDataBlock; +} + +struct saved_context { + WinContext ctx; + uint64_t addr; +}; + +static void patch_and_save_context(WinDumpHeader64 *h, + struct saved_context *saved_ctx, + Error **errp) +{ + uint64_t KiProcessorBlock; + uint16_t OffsetPrcbContext; + CPUState *cpu; + int i = 0; + + if (cpu_memory_rw_debug(first_cpu, + h->KdDebuggerDataBlock + KDBG_KI_PROCESSOR_BLOCK_OFFSET64, + (uint8_t *)&KiProcessorBlock, sizeof(KiProcessorBlock), 0)) { + error_setg(errp, "win-dump: failed to read KiProcessorBlock"); + return; + } + + if (cpu_memory_rw_debug(first_cpu, + h->KdDebuggerDataBlock + KDBG_OFFSET_PRCB_CONTEXT_OFFSET64, + (uint8_t *)&OffsetPrcbContext, sizeof(OffsetPrcbContext), 0)) { + error_setg(errp, "win-dump: failed to read OffsetPrcbContext"); + return; + } + + CPU_FOREACH(cpu) { + X86CPU *x86_cpu = X86_CPU(cpu); + CPUX86State *env = &x86_cpu->env; + uint64_t Prcb; + uint64_t Context; + WinContext ctx; + + if (cpu_memory_rw_debug(first_cpu, + KiProcessorBlock + i * sizeof(uint64_t), + (uint8_t *)&Prcb, sizeof(Prcb), 0)) { + error_setg(errp, "win-dump: failed to read" + " CPU #%d PRCB location", i); + return; + } + + if (cpu_memory_rw_debug(first_cpu, + Prcb + OffsetPrcbContext, + (uint8_t *)&Context, sizeof(Context), 0)) { + error_setg(errp, "win-dump: failed to read" + " CPU #%d ContextFrame location", i); + return; + } + + saved_ctx[i].addr = Context; + + ctx = (WinContext){ + .ContextFlags = WIN_CTX_ALL, + .MxCsr = env->mxcsr, + + .SegEs = env->segs[0].selector, + .SegCs = env->segs[1].selector, + .SegSs = env->segs[2].selector, + .SegDs = env->segs[3].selector, + .SegFs = env->segs[4].selector, + .SegGs = env->segs[5].selector, + .EFlags = cpu_compute_eflags(env), + + .Dr0 = env->dr[0], + .Dr1 = env->dr[1], + .Dr2 = env->dr[2], + .Dr3 = env->dr[3], + .Dr6 = env->dr[6], + .Dr7 = env->dr[7], + + .Rax = env->regs[R_EAX], + .Rbx = env->regs[R_EBX], + .Rcx = env->regs[R_ECX], + .Rdx = env->regs[R_EDX], + .Rsp = env->regs[R_ESP], + .Rbp = env->regs[R_EBP], + .Rsi = env->regs[R_ESI], + .Rdi = env->regs[R_EDI], + .R8 = env->regs[8], + .R9 = env->regs[9], + .R10 = env->regs[10], + .R11 = env->regs[11], + .R12 = env->regs[12], + .R13 = env->regs[13], + .R14 = env->regs[14], + .R15 = env->regs[15], + + .Rip = env->eip, + .FltSave = { + .MxCsr = env->mxcsr, + }, + }; + + if (cpu_memory_rw_debug(first_cpu, Context, + (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 0)) { + error_setg(errp, "win-dump: failed to save CPU #%d context", i); + return; + } + + if (cpu_memory_rw_debug(first_cpu, Context, + (uint8_t *)&ctx, sizeof(WinContext), 1)) { + error_setg(errp, "win-dump: failed to write CPU #%d context", i); + return; + } + + i++; + } +} + +static void restore_context(WinDumpHeader64 *h, + struct saved_context *saved_ctx) +{ + int i; + Error *err = NULL; + + for (i = 0; i < h->NumberProcessors; i++) { + if (cpu_memory_rw_debug(first_cpu, saved_ctx[i].addr, + (uint8_t *)&saved_ctx[i].ctx, sizeof(WinContext), 1)) { + error_setg(&err, "win-dump: failed to restore CPU #%d context", i); + warn_report_err(err); + } + } +} + +void create_win_dump(DumpState *s, Error **errp) +{ + WinDumpHeader64 *h = (WinDumpHeader64 *)(s->guest_note + + VMCOREINFO_ELF_NOTE_HDR_SIZE); + X86CPU *first_x86_cpu = X86_CPU(first_cpu); + uint64_t saved_cr3 = first_x86_cpu->env.cr[3]; + struct saved_context *saved_ctx = NULL; + Error *local_err = NULL; + + if (s->guest_note_size != sizeof(WinDumpHeader64) + + VMCOREINFO_ELF_NOTE_HDR_SIZE) { + error_setg(errp, "win-dump: invalid vmcoreinfo note size"); + return; + } + + check_header(h, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * Further access to kernel structures by virtual addresses + * should be made from system context. + */ + + first_x86_cpu->env.cr[3] = h->DirectoryTableBase; + + check_kdbg(h, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out_cr3; + } + + patch_header(h); + + saved_ctx = g_new(struct saved_context, h->NumberProcessors); + + /* + * Always patch context because there is no way + * to determine if the system-saved context is valid + */ + + patch_and_save_context(h, saved_ctx, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out_free; + } + + s->total_size = h->RequiredDumpSpace; + + s->written_size = qemu_write_full(s->fd, h, sizeof(*h)); + if (s->written_size != sizeof(*h)) { + error_setg(errp, QERR_IO_ERROR); + goto out_restore; + } + + write_runs(s, h, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out_restore; + } + +out_restore: + restore_context(h, saved_ctx); +out_free: + g_free(saved_ctx); +out_cr3: + first_x86_cpu->env.cr[3] = saved_cr3; + + return; +} diff --git a/win_dump.h b/win_dump.h new file mode 100644 index 0000000000..f9e1faf8eb --- /dev/null +++ b/win_dump.h @@ -0,0 +1,176 @@ +/* + * Windows crashdump + * + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +typedef struct WinDumpPhyMemRun64 { + uint64_t BasePage; + uint64_t PageCount; +} QEMU_PACKED WinDumpPhyMemRun64; + +typedef struct WinDumpPhyMemDesc64 { + uint32_t NumberOfRuns; + uint32_t unused; + uint64_t NumberOfPages; + WinDumpPhyMemRun64 Run[43]; +} QEMU_PACKED WinDumpPhyMemDesc64; + +typedef struct WinDumpExceptionRecord { + uint32_t ExceptionCode; + uint32_t ExceptionFlags; + uint64_t ExceptionRecord; + uint64_t ExceptionAddress; + uint32_t NumberParameters; + uint32_t unused; + uint64_t ExceptionInformation[15]; +} QEMU_PACKED WinDumpExceptionRecord; + +typedef struct WinDumpHeader64 { + char Signature[4]; + char ValidDump[4]; + uint32_t MajorVersion; + uint32_t MinorVersion; + uint64_t DirectoryTableBase; + uint64_t PfnDatabase; + uint64_t PsLoadedModuleList; + uint64_t PsActiveProcessHead; + uint32_t MachineImageType; + uint32_t NumberProcessors; + union { + struct { + uint32_t BugcheckCode; + uint32_t unused0; + uint64_t BugcheckParameter1; + uint64_t BugcheckParameter2; + uint64_t BugcheckParameter3; + uint64_t BugcheckParameter4; + }; + uint8_t BugcheckData[40]; + }; + uint8_t VersionUser[32]; + uint64_t KdDebuggerDataBlock; + union { + WinDumpPhyMemDesc64 PhysicalMemoryBlock; + uint8_t PhysicalMemoryBlockBuffer[704]; + }; + union { + uint8_t ContextBuffer[3000]; + }; + WinDumpExceptionRecord Exception; + uint32_t DumpType; + uint32_t unused1; + uint64_t RequiredDumpSpace; + uint64_t SystemTime; + char Comment[128]; + uint64_t SystemUpTime; + uint32_t MiniDumpFields; + uint32_t SecondaryDataState; + uint32_t ProductType; + uint32_t SuiteMask; + uint32_t WriterStatus; + uint8_t unused2; + uint8_t KdSecondaryVersion; + uint8_t reserved[4018]; +} QEMU_PACKED WinDumpHeader64; + +void create_win_dump(DumpState *s, Error **errp); + +#define KDBG_OWNER_TAG_OFFSET64 0x10 +#define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0 +#define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88 +#define KDBG_KI_PROCESSOR_BLOCK_OFFSET64 0x218 +#define KDBG_OFFSET_PRCB_CONTEXT_OFFSET64 0x338 + +#define VMCOREINFO_ELF_NOTE_HDR_SIZE 24 + +#define WIN_CTX_X64 0x00100000L + +#define WIN_CTX_CTL 0x00000001L +#define WIN_CTX_INT 0x00000002L +#define WIN_CTX_SEG 0x00000004L +#define WIN_CTX_FP 0x00000008L +#define WIN_CTX_DBG 0x00000010L + +#define WIN_CTX_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP) +#define WIN_CTX_ALL (WIN_CTX_FULL | WIN_CTX_SEG | WIN_CTX_DBG) + +#define LIVE_SYSTEM_DUMP 0x00000161 + +typedef struct WinM128A { + uint64_t low; + int64_t high; +} QEMU_ALIGNED(16) WinM128A; + +typedef struct WinContext { + uint64_t PHome[6]; + + uint32_t ContextFlags; + uint32_t MxCsr; + + uint16_t SegCs; + uint16_t SegDs; + uint16_t SegEs; + uint16_t SegFs; + uint16_t SegGs; + uint16_t SegSs; + uint32_t EFlags; + + uint64_t Dr0; + uint64_t Dr1; + uint64_t Dr2; + uint64_t Dr3; + uint64_t Dr6; + uint64_t Dr7; + + uint64_t Rax; + uint64_t Rcx; + uint64_t Rdx; + uint64_t Rbx; + uint64_t Rsp; + uint64_t Rbp; + uint64_t Rsi; + uint64_t Rdi; + uint64_t R8; + uint64_t R9; + uint64_t R10; + uint64_t R11; + uint64_t R12; + uint64_t R13; + uint64_t R14; + uint64_t R15; + + uint64_t Rip; + + struct { + uint16_t ControlWord; + uint16_t StatusWord; + uint8_t TagWord; + uint8_t Reserved1; + uint16_t ErrorOpcode; + uint32_t ErrorOffset; + uint16_t ErrorSelector; + uint16_t Reserved2; + uint32_t DataOffset; + uint16_t DataSelector; + uint16_t Reserved3; + uint32_t MxCsr; + uint32_t MxCsr_Mask; + WinM128A FloatRegisters[8]; + WinM128A XmmRegisters[16]; + uint8_t Reserved4[96]; + } FltSave; + + WinM128A VectorRegister[26]; + uint64_t VectorControl; + + uint64_t DebugControl; + uint64_t LastBranchToRip; + uint64_t LastBranchFromRip; + uint64_t LastExceptionToRip; + uint64_t LastExceptionFromRip; +} QEMU_ALIGNED(16) WinContext;