]> git.proxmox.com Git - mirror_qemu.git/commitdiff
Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-2.12-pull-request' into...
authorPeter Maydell <peter.maydell@linaro.org>
Fri, 9 Mar 2018 18:49:27 +0000 (18:49 +0000)
committerPeter Maydell <peter.maydell@linaro.org>
Fri, 9 Mar 2018 18:49:27 +0000 (18:49 +0000)
# gpg: Signature made Fri 09 Mar 2018 14:54:33 GMT
# gpg:                using RSA key F30C38BD3F2FBE3C
# gpg: Good signature from "Laurent Vivier <lvivier@redhat.com>"
# gpg:                 aka "Laurent Vivier <laurent@vivier.eu>"
# gpg:                 aka "Laurent Vivier (Red Hat) <lvivier@redhat.com>"
# Primary key fingerprint: CD2F 75DD C8E3 A4DC 2E4F  5173 F30C 38BD 3F2F BE3C

* remotes/vivier/tags/m68k-for-2.12-pull-request:
  target/m68k: implement ftentox
  target/m68k: implement ftwotox
  target/m68k: implement fetox
  target/m68k: implement flog2
  target/m68k: implement flog10
  target/m68k: implement flogn
  target/m68k: implement flognp1
  target/m68k: define floatx80_move()

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
206 files changed:
.shippable.yml
MAINTAINERS
Makefile.objs
README
arch_init.c
block.c
block/blkdebug.c
block/block-backend.c
block/commit.c
block/crypto.c
block/file-posix.c
block/file-win32.c
block/gluster.c
block/io.c
block/iscsi.c
block/mirror.c
block/nfs.c
block/null.c
block/nvme.c
block/parallels.c
block/qcow.c
block/qcow2-bitmap.c
block/qcow2-cluster.c
block/qcow2-refcount.c
block/qcow2-snapshot.c
block/qcow2.c
block/qcow2.h
block/qed.c
block/raw-format.c
block/rbd.c
block/sheepdog.c
block/ssh.c
block/throttle.c
block/vdi.c
block/vhdx.c
block/vmdk.c
block/vpc.c
block/vvfat.c
chardev/char-socket.c
configure
cpus.c
default-configs/riscv32-linux-user.mak [new file with mode: 0644]
default-configs/riscv32-softmmu.mak [new file with mode: 0644]
default-configs/riscv64-linux-user.mak [new file with mode: 0644]
default-configs/riscv64-softmmu.mak [new file with mode: 0644]
disas.c
disas/Makefile.objs
disas/riscv.c [new file with mode: 0644]
docs/interop/qcow2.txt
docs/qcow2-cache.txt
exec.c
fpu/softfloat-specialize.h
fpu/softfloat.c
fsdev/virtfs-proxy-helper.c
hw/block/dataplane/virtio-blk.c
hw/char/sclpconsole-lm.c
hw/char/sclpconsole.c
hw/core/loader.c
hw/display/g364fb.c
hw/i386/multiboot.c
hw/i386/pc.c
hw/i386/pc_piix.c
hw/i386/pc_q35.c
hw/ide/core.c
hw/intc/openpic_kvm.c
hw/misc/trace-events
hw/riscv/Makefile.objs [new file with mode: 0644]
hw/riscv/riscv_hart.c [new file with mode: 0644]
hw/riscv/riscv_htif.c [new file with mode: 0644]
hw/riscv/sifive_clint.c [new file with mode: 0644]
hw/riscv/sifive_e.c [new file with mode: 0644]
hw/riscv/sifive_plic.c [new file with mode: 0644]
hw/riscv/sifive_prci.c [new file with mode: 0644]
hw/riscv/sifive_test.c [new file with mode: 0644]
hw/riscv/sifive_u.c [new file with mode: 0644]
hw/riscv/sifive_uart.c [new file with mode: 0644]
hw/riscv/spike.c [new file with mode: 0644]
hw/riscv/virt.c [new file with mode: 0644]
hw/s390x/event-facility.c
hw/s390x/ipl.c
hw/s390x/s390-ccw.c
hw/s390x/s390-virtio-ccw.c
hw/s390x/sclpcpu.c
hw/s390x/sclpquiesce.c
hw/s390x/virtio-ccw.c
hw/s390x/virtio-ccw.h
hw/scsi/lsi53c895a.c
hw/scsi/scsi-bus.c
hw/scsi/spapr_vscsi.c
hw/scsi/virtio-scsi-dataplane.c
hw/sparc/sun4m.c
hw/tpm/tpm_crb.c
hw/tpm/tpm_emulator.c
hw/tpm/tpm_passthrough.c
hw/tpm/tpm_tis.c
hw/tpm/tpm_util.c
hw/tpm/trace-events [new file with mode: 0644]
hw/vfio/ccw.c
hw/vfio/pci.c
hw/vfio/platform.c
include/block/aio-wait.h [new file with mode: 0644]
include/block/aio.h
include/block/block.h
include/block/block_int.h
include/disas/bfd.h
include/elf.h
include/exec/memory-internal.h
include/exec/memory.h
include/hw/elf_ops.h
include/hw/loader.h
include/hw/riscv/riscv_hart.h [new file with mode: 0644]
include/hw/riscv/riscv_htif.h [new file with mode: 0644]
include/hw/riscv/sifive_clint.h [new file with mode: 0644]
include/hw/riscv/sifive_e.h [new file with mode: 0644]
include/hw/riscv/sifive_plic.h [new file with mode: 0644]
include/hw/riscv/sifive_prci.h [new file with mode: 0644]
include/hw/riscv/sifive_test.h [new file with mode: 0644]
include/hw/riscv/sifive_u.h [new file with mode: 0644]
include/hw/riscv/sifive_uart.h [new file with mode: 0644]
include/hw/riscv/spike.h [new file with mode: 0644]
include/hw/riscv/virt.h [new file with mode: 0644]
include/hw/s390x/event-facility.h
include/hw/scsi/scsi.h
include/io/channel-socket.h
include/io/channel-tls.h
include/io/channel.h
include/io/net-listener.h
include/io/task.h
include/qemu/lockable.h
include/qom/object.h
include/sysemu/arch_init.h
include/sysemu/iothread.h
include/sysemu/sysemu.h
io/channel-socket.c
io/channel-tls.c
io/channel.c
io/dns-resolver.c
io/net-listener.c
io/task.c
iothread.c
linux-user/elfload.c
linux-user/main.c
linux-user/riscv/syscall_nr.h [new file with mode: 0644]
linux-user/riscv/target_cpu.h [new file with mode: 0644]
linux-user/riscv/target_elf.h [new file with mode: 0644]
linux-user/riscv/target_signal.h [new file with mode: 0644]
linux-user/riscv/target_structs.h [new file with mode: 0644]
linux-user/riscv/target_syscall.h [new file with mode: 0644]
linux-user/riscv/termbits.h [new file with mode: 0644]
linux-user/signal.c
linux-user/syscall.c
linux-user/syscall_defs.h
memory.c
migration/socket.c
migration/tls.c
nbd/client.c
nbd/server.c
numa.c
pc-bios/s390-ccw/bootmap.c
pc-bios/s390-ccw/bootmap.h
qapi/block-core.json
qapi/misc.json
qdev-monitor.c
qemu-doc.texi
qemu-img.c
qemu-io.c
qemu-options.hx
qga/commands-posix.c
qmp.c
qom/object.c
scripts/checkpatch.pl
scripts/qemu-binfmt-conf.sh
target/i386/whpx-all.c
target/riscv/Makefile.objs [new file with mode: 0644]
target/riscv/cpu.c [new file with mode: 0644]
target/riscv/cpu.h [new file with mode: 0644]
target/riscv/cpu_bits.h [new file with mode: 0644]
target/riscv/cpu_user.h [new file with mode: 0644]
target/riscv/fpu_helper.c [new file with mode: 0644]
target/riscv/gdbstub.c [new file with mode: 0644]
target/riscv/helper.c [new file with mode: 0644]
target/riscv/helper.h [new file with mode: 0644]
target/riscv/instmap.h [new file with mode: 0644]
target/riscv/op_helper.c [new file with mode: 0644]
target/riscv/pmp.c [new file with mode: 0644]
target/riscv/pmp.h [new file with mode: 0644]
target/riscv/translate.c [new file with mode: 0644]
target/s390x/cpu.h
target/s390x/mem_helper.c
target/s390x/mmu_helper.c
target/sparc/translate.c
tests/Makefile.include
tests/ahci-test.c
tests/qemu-iotests/033
tests/qemu-iotests/033.out
tests/qemu-iotests/051
tests/sdhci-test.c
tests/test-block-backend.c [new file with mode: 0644]
tests/test-io-channel-socket.c
tests/test-io-channel-tls.c
tests/test-io-task.c
ui/vnc-auth-vencrypt.c
ui/vnc-ws.c
util/Makefile.objs
util/aio-wait.c [new file with mode: 0644]
vl.c

index dd4bbc84b1ea15545d18e4a9c63aa84e95b90da8..60f2ce92181588a69c8998c6b213a7bea770fdb7 100644 (file)
@@ -23,8 +23,6 @@ env:
       TARGET_LIST=mips-softmmu,mipsel-linux-user
     - IMAGE=debian-mips64el-cross
       TARGET_LIST=mips64el-softmmu,mips64el-linux-user
-    - IMAGE=debian-powerpc-cross
-      TARGET_LIST=ppc-softmmu,ppcemb-softmmu,ppc-linux-user
     - IMAGE=debian-ppc64el-cross
       TARGET_LIST=ppc64-softmmu,ppc64-linux-user,ppc64abi32-linux-user
 build:
index b7c41303884e8b1516f263e05c2946bbb6d0d98f..f409f3b158fad32454b6b2a22fe68b862bef6b45 100644 (file)
@@ -233,6 +233,17 @@ F: hw/ppc/
 F: include/hw/ppc/
 F: disas/ppc.c
 
+RISC-V
+M: Michael Clark <mjc@sifive.com>
+M: Palmer Dabbelt <palmer@sifive.com>
+M: Sagar Karandikar <sagark@eecs.berkeley.edu>
+M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
+S: Maintained
+F: target/riscv/
+F: hw/riscv/
+F: include/hw/riscv/
+F: disas/riscv.c
+
 S390
 M: Richard Henderson <rth@twiddle.net>
 M: Alexander Graf <agraf@suse.de>
index 69413d33b175d3cba7540d94b6e1d92a151523a5..d8b44a2d3c28a7d3e9be19c03a2cc6b323f533e0 100644 (file)
@@ -233,6 +233,7 @@ trace-events-subdirs += hw/alpha
 trace-events-subdirs += hw/hppa
 trace-events-subdirs += hw/xen
 trace-events-subdirs += hw/ide
+trace-events-subdirs += hw/tpm
 trace-events-subdirs += ui
 trace-events-subdirs += audio
 trace-events-subdirs += net
diff --git a/README b/README
index 7833b9736523c72c86cf782d15d23bf92d66c387..49a9fd09cd5ffdc3c9b21b0c861441920e2a611e 100644 (file)
--- a/README
+++ b/README
@@ -73,7 +73,7 @@ The QEMU website is also maintained under source control.
   git clone git://git.qemu.org/qemu-web.git
   https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
 
-A 'git-profile' utility was created to make above process less
+A 'git-publish' utility was created to make above process less
 cumbersome, and is highly recommended for making regular contributions,
 or even just for sending consecutive patch series revisions. It also
 requires a working 'git send-email' setup, and by default doesn't
index 46d03f550dad741c3e39d37dd82b5d706a309f07..6ee07478bd117a7cef52dd3aed1250eb27603755 100644 (file)
@@ -71,6 +71,8 @@ int graphic_depth = 32;
 #define QEMU_ARCH QEMU_ARCH_OPENRISC
 #elif defined(TARGET_PPC)
 #define QEMU_ARCH QEMU_ARCH_PPC
+#elif defined(TARGET_RISCV)
+#define QEMU_ARCH QEMU_ARCH_RISCV
 #elif defined(TARGET_S390X)
 #define QEMU_ARCH QEMU_ARCH_S390X
 #elif defined(TARGET_SH4)
diff --git a/block.c b/block.c
index c205320a50bb9ed01b843f416dca552032d1c219..4f76714f6b846f655c40061c6968a52e39331103 100644 (file)
--- a/block.c
+++ b/block.c
@@ -418,7 +418,7 @@ static void coroutine_fn bdrv_create_co_entry(void *opaque)
     CreateCo *cco = opaque;
     assert(cco->drv);
 
-    ret = cco->drv->bdrv_create(cco->filename, cco->opts, &local_err);
+    ret = cco->drv->bdrv_co_create_opts(cco->filename, cco->opts, &local_err);
     error_propagate(&cco->err, local_err);
     cco->ret = ret;
 }
@@ -437,7 +437,7 @@ int bdrv_create(BlockDriver *drv, const char* filename,
         .err = NULL,
     };
 
-    if (!drv->bdrv_create) {
+    if (!drv->bdrv_co_create_opts) {
         error_setg(errp, "Driver '%s' does not support image creation", drv->format_name);
         ret = -ENOTSUP;
         goto out;
@@ -4711,7 +4711,12 @@ out:
 
 AioContext *bdrv_get_aio_context(BlockDriverState *bs)
 {
-    return bs->aio_context;
+    return bs ? bs->aio_context : qemu_get_aio_context();
+}
+
+AioWait *bdrv_get_aio_wait(BlockDriverState *bs)
+{
+    return bs ? &bs->wait : NULL;
 }
 
 void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co)
index d83f23febd7fad1d588a86af42060408100052d5..589712475acf53fa289c9ebfbc489c1a8aecb7f7 100644 (file)
@@ -627,15 +627,17 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
     return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
 }
 
-static int64_t coroutine_fn blkdebug_co_get_block_status(
-    BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
-    BlockDriverState **file)
+static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
+                                                 bool want_zero,
+                                                 int64_t offset,
+                                                 int64_t bytes,
+                                                 int64_t *pnum,
+                                                 int64_t *map,
+                                                 BlockDriverState **file)
 {
-    assert(QEMU_IS_ALIGNED(sector_num | nb_sectors,
-                           DIV_ROUND_UP(bs->bl.request_alignment,
-                                        BDRV_SECTOR_SIZE)));
-    return bdrv_co_get_block_status_from_file(bs, sector_num, nb_sectors,
-                                              pnum, file);
+    assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
+    return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
+                                          pnum, map, file);
 }
 
 static void blkdebug_close(BlockDriverState *bs)
@@ -907,7 +909,7 @@ static BlockDriver bdrv_blkdebug = {
     .bdrv_co_flush_to_disk  = blkdebug_co_flush,
     .bdrv_co_pwrite_zeroes  = blkdebug_co_pwrite_zeroes,
     .bdrv_co_pdiscard       = blkdebug_co_pdiscard,
-    .bdrv_co_get_block_status = blkdebug_co_get_block_status,
+    .bdrv_co_block_status   = blkdebug_co_block_status,
 
     .bdrv_debug_event           = blkdebug_debug_event,
     .bdrv_debug_breakpoint      = blkdebug_debug_breakpoint,
index 94ffbb6a60b0f58e3396ff27d57bdb52693449dc..f2e0a855ff7c03c16595a4159ffbe4c863a1f103 100644 (file)
@@ -73,6 +73,14 @@ struct BlockBackend {
     int quiesce_counter;
     VMChangeStateEntry *vmsh;
     bool force_allow_inactivate;
+
+    /* Number of in-flight aio requests.  BlockDriverState also counts
+     * in-flight requests but aio requests can exist even when blk->root is
+     * NULL, so we cannot rely on its counter for that case.
+     * Accessed with atomic ops.
+     */
+    unsigned int in_flight;
+    AioWait wait;
 };
 
 typedef struct BlockBackendAIOCB {
@@ -1142,7 +1150,7 @@ int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
 typedef struct BlkRwCo {
     BlockBackend *blk;
     int64_t offset;
-    QEMUIOVector *qiov;
+    void *iobuf;
     int ret;
     BdrvRequestFlags flags;
 } BlkRwCo;
@@ -1150,17 +1158,19 @@ typedef struct BlkRwCo {
 static void blk_read_entry(void *opaque)
 {
     BlkRwCo *rwco = opaque;
+    QEMUIOVector *qiov = rwco->iobuf;
 
-    rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, rwco->qiov->size,
-                              rwco->qiov, rwco->flags);
+    rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
+                              qiov, rwco->flags);
 }
 
 static void blk_write_entry(void *opaque)
 {
     BlkRwCo *rwco = opaque;
+    QEMUIOVector *qiov = rwco->iobuf;
 
-    rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, rwco->qiov->size,
-                               rwco->qiov, rwco->flags);
+    rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
+                               qiov, rwco->flags);
 }
 
 static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
@@ -1180,7 +1190,7 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
     rwco = (BlkRwCo) {
         .blk    = blk,
         .offset = offset,
-        .qiov   = &qiov,
+        .iobuf  = &qiov,
         .flags  = flags,
         .ret    = NOT_DONE,
     };
@@ -1225,11 +1235,22 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags)
     return bdrv_make_zero(blk->root, flags);
 }
 
+static void blk_inc_in_flight(BlockBackend *blk)
+{
+    atomic_inc(&blk->in_flight);
+}
+
+static void blk_dec_in_flight(BlockBackend *blk)
+{
+    atomic_dec(&blk->in_flight);
+    aio_wait_kick(&blk->wait);
+}
+
 static void error_callback_bh(void *opaque)
 {
     struct BlockBackendAIOCB *acb = opaque;
 
-    bdrv_dec_in_flight(acb->common.bs);
+    blk_dec_in_flight(acb->blk);
     acb->common.cb(acb->common.opaque, acb->ret);
     qemu_aio_unref(acb);
 }
@@ -1240,7 +1261,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk,
 {
     struct BlockBackendAIOCB *acb;
 
-    bdrv_inc_in_flight(blk_bs(blk));
+    blk_inc_in_flight(blk);
     acb = blk_aio_get(&block_backend_aiocb_info, blk, cb, opaque);
     acb->blk = blk;
     acb->ret = ret;
@@ -1263,7 +1284,7 @@ static const AIOCBInfo blk_aio_em_aiocb_info = {
 static void blk_aio_complete(BlkAioEmAIOCB *acb)
 {
     if (acb->has_returned) {
-        bdrv_dec_in_flight(acb->common.bs);
+        blk_dec_in_flight(acb->rwco.blk);
         acb->common.cb(acb->common.opaque, acb->rwco.ret);
         qemu_aio_unref(acb);
     }
@@ -1277,19 +1298,19 @@ static void blk_aio_complete_bh(void *opaque)
 }
 
 static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
-                                QEMUIOVector *qiov, CoroutineEntry co_entry,
+                                void *iobuf, CoroutineEntry co_entry,
                                 BdrvRequestFlags flags,
                                 BlockCompletionFunc *cb, void *opaque)
 {
     BlkAioEmAIOCB *acb;
     Coroutine *co;
 
-    bdrv_inc_in_flight(blk_bs(blk));
+    blk_inc_in_flight(blk);
     acb = blk_aio_get(&blk_aio_em_aiocb_info, blk, cb, opaque);
     acb->rwco = (BlkRwCo) {
         .blk    = blk,
         .offset = offset,
-        .qiov   = qiov,
+        .iobuf  = iobuf,
         .flags  = flags,
         .ret    = NOT_DONE,
     };
@@ -1312,10 +1333,11 @@ static void blk_aio_read_entry(void *opaque)
 {
     BlkAioEmAIOCB *acb = opaque;
     BlkRwCo *rwco = &acb->rwco;
+    QEMUIOVector *qiov = rwco->iobuf;
 
-    assert(rwco->qiov->size == acb->bytes);
+    assert(qiov->size == acb->bytes);
     rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, acb->bytes,
-                              rwco->qiov, rwco->flags);
+                              qiov, rwco->flags);
     blk_aio_complete(acb);
 }
 
@@ -1323,10 +1345,11 @@ static void blk_aio_write_entry(void *opaque)
 {
     BlkAioEmAIOCB *acb = opaque;
     BlkRwCo *rwco = &acb->rwco;
+    QEMUIOVector *qiov = rwco->iobuf;
 
-    assert(!rwco->qiov || rwco->qiov->size == acb->bytes);
+    assert(!qiov || qiov->size == acb->bytes);
     rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, acb->bytes,
-                               rwco->qiov, rwco->flags);
+                               qiov, rwco->flags);
     blk_aio_complete(acb);
 }
 
@@ -1455,8 +1478,10 @@ int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
 static void blk_ioctl_entry(void *opaque)
 {
     BlkRwCo *rwco = opaque;
+    QEMUIOVector *qiov = rwco->iobuf;
+
     rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
-                             rwco->qiov->iov[0].iov_base);
+                             qiov->iov[0].iov_base);
 }
 
 int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
@@ -1469,24 +1494,15 @@ static void blk_aio_ioctl_entry(void *opaque)
     BlkAioEmAIOCB *acb = opaque;
     BlkRwCo *rwco = &acb->rwco;
 
-    rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
-                             rwco->qiov->iov[0].iov_base);
+    rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, rwco->iobuf);
+
     blk_aio_complete(acb);
 }
 
 BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
                           BlockCompletionFunc *cb, void *opaque)
 {
-    QEMUIOVector qiov;
-    struct iovec iov;
-
-    iov = (struct iovec) {
-        .iov_base = buf,
-        .iov_len = 0,
-    };
-    qemu_iovec_init_external(&qiov, &iov, 1);
-
-    return blk_aio_prwv(blk, req, 0, &qiov, blk_aio_ioctl_entry, 0, cb, opaque);
+    return blk_aio_prwv(blk, req, 0, buf, blk_aio_ioctl_entry, 0, cb, opaque);
 }
 
 int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
@@ -1521,14 +1537,41 @@ int blk_flush(BlockBackend *blk)
 
 void blk_drain(BlockBackend *blk)
 {
-    if (blk_bs(blk)) {
-        bdrv_drain(blk_bs(blk));
+    BlockDriverState *bs = blk_bs(blk);
+
+    if (bs) {
+        bdrv_drained_begin(bs);
+    }
+
+    /* We may have -ENOMEDIUM completions in flight */
+    AIO_WAIT_WHILE(&blk->wait,
+            blk_get_aio_context(blk),
+            atomic_mb_read(&blk->in_flight) > 0);
+
+    if (bs) {
+        bdrv_drained_end(bs);
     }
 }
 
 void blk_drain_all(void)
 {
-    bdrv_drain_all();
+    BlockBackend *blk = NULL;
+
+    bdrv_drain_all_begin();
+
+    while ((blk = blk_all_next(blk)) != NULL) {
+        AioContext *ctx = blk_get_aio_context(blk);
+
+        aio_context_acquire(ctx);
+
+        /* We may have -ENOMEDIUM completions in flight */
+        AIO_WAIT_WHILE(&blk->wait, ctx,
+                atomic_mb_read(&blk->in_flight) > 0);
+
+        aio_context_release(ctx);
+    }
+
+    bdrv_drain_all_end();
 }
 
 void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error,
@@ -1569,10 +1612,11 @@ static void send_qmp_error_event(BlockBackend *blk,
                                  bool is_read, int error)
 {
     IoOperationType optype;
+    BlockDriverState *bs = blk_bs(blk);
 
     optype = is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE;
-    qapi_event_send_block_io_error(blk_name(blk),
-                                   bdrv_get_node_name(blk_bs(blk)), optype,
+    qapi_event_send_block_io_error(blk_name(blk), !!bs,
+                                   bs ? bdrv_get_node_name(bs) : NULL, optype,
                                    action, blk_iostatus_is_enabled(blk),
                                    error == ENOSPC, strerror(error),
                                    &error_abort);
@@ -1902,7 +1946,9 @@ int blk_truncate(BlockBackend *blk, int64_t offset, PreallocMode prealloc,
 static void blk_pdiscard_entry(void *opaque)
 {
     BlkRwCo *rwco = opaque;
-    rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, rwco->qiov->size);
+    QEMUIOVector *qiov = rwco->iobuf;
+
+    rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
 }
 
 int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
index bb6c904704d35565b213624b9158389ed4ca9d65..1943c9c3e166194ab0f5100d4d53db147cf522fe 100644 (file)
@@ -265,7 +265,7 @@ static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
 static BlockDriver bdrv_commit_top = {
     .format_name                = "commit_top",
     .bdrv_co_preadv             = bdrv_commit_top_preadv,
-    .bdrv_co_get_block_status   = bdrv_co_get_block_status_from_backing,
+    .bdrv_co_block_status       = bdrv_co_block_status_from_backing,
     .bdrv_refresh_filename      = bdrv_commit_top_refresh_filename,
     .bdrv_close                 = bdrv_commit_top_close,
     .bdrv_child_perm            = bdrv_commit_top_child_perm,
index aeac482e7bbcf7f13bdf48493013578dea4ee122..17b5c0abad6f46966f4a2f79d4083b0a784863a7 100644 (file)
@@ -556,9 +556,9 @@ static int block_crypto_open_luks(BlockDriverState *bs,
                                      bs, options, flags, errp);
 }
 
-static int block_crypto_create_luks(const char *filename,
-                                    QemuOpts *opts,
-                                    Error **errp)
+static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
+                                                         QemuOpts *opts,
+                                                         Error **errp)
 {
     return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
                                        filename, opts, errp);
@@ -617,7 +617,7 @@ BlockDriver bdrv_crypto_luks = {
     .bdrv_open          = block_crypto_open_luks,
     .bdrv_close         = block_crypto_close,
     .bdrv_child_perm    = bdrv_format_default_perms,
-    .bdrv_create        = block_crypto_create_luks,
+    .bdrv_co_create_opts = block_crypto_co_create_opts_luks,
     .bdrv_truncate      = block_crypto_truncate,
     .create_opts        = &block_crypto_create_opts_luks,
 
index ca49c1a98ae1d2a0ca5f59070c709cd84dd70afe..7f2cc63c60b866dee6a7d0ced2c860560fe37345 100644 (file)
@@ -1982,7 +1982,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
     return (int64_t)st.st_blocks * 512;
 }
 
-static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
+                                           Error **errp)
 {
     int fd;
     int result = 0;
@@ -2131,25 +2132,24 @@ static int find_allocation(BlockDriverState *bs, off_t start,
 }
 
 /*
- * Returns the allocation status of the specified sectors.
+ * Returns the allocation status of the specified offset.
  *
- * If 'sector_num' is beyond the end of the disk image the return value is 0
- * and 'pnum' is set to 0.
+ * The block layer guarantees 'offset' and 'bytes' are within bounds.
  *
- * 'pnum' is set to the number of sectors (including and immediately following
- * the specified sector) that are known to be in the same
+ * 'pnum' is set to the number of bytes (including and immediately following
+ * the specified offset) that are known to be in the same
  * allocated/unallocated state.
  *
- * 'nb_sectors' is the max value 'pnum' should be set to.  If nb_sectors goes
- * beyond the end of the disk image it will be clamped.
+ * 'bytes' is the max value 'pnum' should be set to.
  */
-static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
-                                                    int64_t sector_num,
-                                                    int nb_sectors, int *pnum,
-                                                    BlockDriverState **file)
-{
-    off_t start, data = 0, hole = 0;
-    int64_t total_size;
+static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
+                                            bool want_zero,
+                                            int64_t offset,
+                                            int64_t bytes, int64_t *pnum,
+                                            int64_t *map,
+                                            BlockDriverState **file)
+{
+    off_t data = 0, hole = 0;
     int ret;
 
     ret = fd_open(bs);
@@ -2157,39 +2157,36 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
         return ret;
     }
 
-    start = sector_num * BDRV_SECTOR_SIZE;
-    total_size = bdrv_getlength(bs);
-    if (total_size < 0) {
-        return total_size;
-    } else if (start >= total_size) {
-        *pnum = 0;
-        return 0;
-    } else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) {
-        nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE);
+    if (!want_zero) {
+        *pnum = bytes;
+        *map = offset;
+        *file = bs;
+        return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
     }
 
-    ret = find_allocation(bs, start, &data, &hole);
+    ret = find_allocation(bs, offset, &data, &hole);
     if (ret == -ENXIO) {
         /* Trailing hole */
-        *pnum = nb_sectors;
+        *pnum = bytes;
         ret = BDRV_BLOCK_ZERO;
     } else if (ret < 0) {
         /* No info available, so pretend there are no holes */
-        *pnum = nb_sectors;
+        *pnum = bytes;
         ret = BDRV_BLOCK_DATA;
-    } else if (data == start) {
-        /* On a data extent, compute sectors to the end of the extent,
+    } else if (data == offset) {
+        /* On a data extent, compute bytes to the end of the extent,
          * possibly including a partial sector at EOF. */
-        *pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE));
+        *pnum = MIN(bytes, hole - offset);
         ret = BDRV_BLOCK_DATA;
     } else {
-        /* On a hole, compute sectors to the beginning of the next extent.  */
-        assert(hole == start);
-        *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
+        /* On a hole, compute bytes to the beginning of the next extent.  */
+        assert(hole == offset);
+        *pnum = MIN(bytes, data - offset);
         ret = BDRV_BLOCK_ZERO;
     }
+    *map = offset;
     *file = bs;
-    return ret | BDRV_BLOCK_OFFSET_VALID | start;
+    return ret | BDRV_BLOCK_OFFSET_VALID;
 }
 
 static coroutine_fn BlockAIOCB *raw_aio_pdiscard(BlockDriverState *bs,
@@ -2280,9 +2277,9 @@ BlockDriver bdrv_file = {
     .bdrv_reopen_commit = raw_reopen_commit,
     .bdrv_reopen_abort = raw_reopen_abort,
     .bdrv_close = raw_close,
-    .bdrv_create = raw_create,
+    .bdrv_co_create_opts = raw_co_create_opts,
     .bdrv_has_zero_init = bdrv_has_zero_init_1,
-    .bdrv_co_get_block_status = raw_co_get_block_status,
+    .bdrv_co_block_status = raw_co_block_status,
     .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes,
 
     .bdrv_co_preadv         = raw_co_preadv,
@@ -2684,8 +2681,8 @@ static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs,
     return -ENOTSUP;
 }
 
-static int hdev_create(const char *filename, QemuOpts *opts,
-                       Error **errp)
+static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts,
+                                            Error **errp)
 {
     int fd;
     int ret = 0;
@@ -2758,7 +2755,7 @@ static BlockDriver bdrv_host_device = {
     .bdrv_reopen_prepare = raw_reopen_prepare,
     .bdrv_reopen_commit  = raw_reopen_commit,
     .bdrv_reopen_abort   = raw_reopen_abort,
-    .bdrv_create         = hdev_create,
+    .bdrv_co_create_opts = hdev_co_create_opts,
     .create_opts         = &raw_create_opts,
     .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes,
 
@@ -2880,7 +2877,7 @@ static BlockDriver bdrv_host_cdrom = {
     .bdrv_reopen_prepare = raw_reopen_prepare,
     .bdrv_reopen_commit  = raw_reopen_commit,
     .bdrv_reopen_abort   = raw_reopen_abort,
-    .bdrv_create         = hdev_create,
+    .bdrv_co_create_opts = hdev_co_create_opts,
     .create_opts         = &raw_create_opts,
 
 
@@ -3011,7 +3008,7 @@ static BlockDriver bdrv_host_cdrom = {
     .bdrv_reopen_prepare = raw_reopen_prepare,
     .bdrv_reopen_commit  = raw_reopen_commit,
     .bdrv_reopen_abort   = raw_reopen_abort,
-    .bdrv_create        = hdev_create,
+    .bdrv_co_create_opts = hdev_co_create_opts,
     .create_opts        = &raw_create_opts,
 
     .bdrv_co_preadv         = raw_co_preadv,
index f24c7bb92c6434e0db52b469a6f285c588943c7b..4a430d45f1d1e734a6871ffee4989213136b621d 100644 (file)
@@ -553,7 +553,8 @@ static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
     return st.st_size;
 }
 
-static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
+                                           Error **errp)
 {
     int fd;
     int64_t total_size = 0;
@@ -599,7 +600,7 @@ BlockDriver bdrv_file = {
     .bdrv_file_open     = raw_open,
     .bdrv_refresh_limits = raw_probe_alignment,
     .bdrv_close         = raw_close,
-    .bdrv_create        = raw_create,
+    .bdrv_co_create_opts = raw_co_create_opts,
     .bdrv_has_zero_init = bdrv_has_zero_init_1,
 
     .bdrv_aio_readv     = raw_aio_readv,
index 3f17b7819d232dfd00c105aae86ea21e206293ef..79b4cfdf74ed4d61b76cac23100e09cfa4907445 100644 (file)
@@ -1021,8 +1021,9 @@ static int qemu_gluster_do_truncate(struct glfs_fd *fd, int64_t offset,
     return 0;
 }
 
-static int qemu_gluster_create(const char *filename,
-                               QemuOpts *opts, Error **errp)
+static int coroutine_fn qemu_gluster_co_create_opts(const char *filename,
+                                                    QemuOpts *opts,
+                                                    Error **errp)
 {
     BlockdevOptionsGluster *gconf;
     struct glfs *glfs;
@@ -1362,68 +1363,66 @@ exit:
 }
 
 /*
- * Returns the allocation status of the specified sectors.
+ * Returns the allocation status of the specified offset.
  *
- * If 'sector_num' is beyond the end of the disk image the return value is 0
- * and 'pnum' is set to 0.
+ * The block layer guarantees 'offset' and 'bytes' are within bounds.
  *
- * 'pnum' is set to the number of sectors (including and immediately following
- * the specified sector) that are known to be in the same
+ * 'pnum' is set to the number of bytes (including and immediately following
+ * the specified offset) that are known to be in the same
  * allocated/unallocated state.
  *
- * 'nb_sectors' is the max value 'pnum' should be set to.  If nb_sectors goes
- * beyond the end of the disk image it will be clamped.
+ * 'bytes' is the max value 'pnum' should be set to.
  *
- * (Based on raw_co_get_block_status() from file-posix.c.)
+ * (Based on raw_co_block_status() from file-posix.c.)
  */
-static int64_t coroutine_fn qemu_gluster_co_get_block_status(
-        BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
-        BlockDriverState **file)
+static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
+                                                     bool want_zero,
+                                                     int64_t offset,
+                                                     int64_t bytes,
+                                                     int64_t *pnum,
+                                                     int64_t *map,
+                                                     BlockDriverState **file)
 {
     BDRVGlusterState *s = bs->opaque;
-    off_t start, data = 0, hole = 0;
-    int64_t total_size;
+    off_t data = 0, hole = 0;
     int ret = -EINVAL;
 
     if (!s->fd) {
         return ret;
     }
 
-    start = sector_num * BDRV_SECTOR_SIZE;
-    total_size = bdrv_getlength(bs);
-    if (total_size < 0) {
-        return total_size;
-    } else if (start >= total_size) {
-        *pnum = 0;
-        return 0;
-    } else if (start + nb_sectors * BDRV_SECTOR_SIZE > total_size) {
-        nb_sectors = DIV_ROUND_UP(total_size - start, BDRV_SECTOR_SIZE);
+    if (!want_zero) {
+        *pnum = bytes;
+        *map = offset;
+        *file = bs;
+        return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
     }
 
-    ret = find_allocation(bs, start, &data, &hole);
+    ret = find_allocation(bs, offset, &data, &hole);
     if (ret == -ENXIO) {
         /* Trailing hole */
-        *pnum = nb_sectors;
+        *pnum = bytes;
         ret = BDRV_BLOCK_ZERO;
     } else if (ret < 0) {
         /* No info available, so pretend there are no holes */
-        *pnum = nb_sectors;
+        *pnum = bytes;
         ret = BDRV_BLOCK_DATA;
-    } else if (data == start) {
-        /* On a data extent, compute sectors to the end of the extent,
+    } else if (data == offset) {
+        /* On a data extent, compute bytes to the end of the extent,
          * possibly including a partial sector at EOF. */
-        *pnum = MIN(nb_sectors, DIV_ROUND_UP(hole - start, BDRV_SECTOR_SIZE));
+        *pnum = MIN(bytes, hole - offset);
         ret = BDRV_BLOCK_DATA;
     } else {
-        /* On a hole, compute sectors to the beginning of the next extent.  */
-        assert(hole == start);
-        *pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
+        /* On a hole, compute bytes to the beginning of the next extent.  */
+        assert(hole == offset);
+        *pnum = MIN(bytes, data - offset);
         ret = BDRV_BLOCK_ZERO;
     }
 
+    *map = offset;
     *file = bs;
 
-    return ret | BDRV_BLOCK_OFFSET_VALID | start;
+    return ret | BDRV_BLOCK_OFFSET_VALID;
 }
 
 
@@ -1437,7 +1436,7 @@ static BlockDriver bdrv_gluster = {
     .bdrv_reopen_commit           = qemu_gluster_reopen_commit,
     .bdrv_reopen_abort            = qemu_gluster_reopen_abort,
     .bdrv_close                   = qemu_gluster_close,
-    .bdrv_create                  = qemu_gluster_create,
+    .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,
@@ -1451,7 +1450,7 @@ static BlockDriver bdrv_gluster = {
 #ifdef CONFIG_GLUSTERFS_ZEROFILL
     .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes,
 #endif
-    .bdrv_co_get_block_status     = qemu_gluster_co_get_block_status,
+    .bdrv_co_block_status         = qemu_gluster_co_block_status,
     .create_opts                  = &qemu_gluster_create_opts,
 };
 
@@ -1465,7 +1464,7 @@ static BlockDriver bdrv_gluster_tcp = {
     .bdrv_reopen_commit           = qemu_gluster_reopen_commit,
     .bdrv_reopen_abort            = qemu_gluster_reopen_abort,
     .bdrv_close                   = qemu_gluster_close,
-    .bdrv_create                  = qemu_gluster_create,
+    .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,
@@ -1479,7 +1478,7 @@ static BlockDriver bdrv_gluster_tcp = {
 #ifdef CONFIG_GLUSTERFS_ZEROFILL
     .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes,
 #endif
-    .bdrv_co_get_block_status     = qemu_gluster_co_get_block_status,
+    .bdrv_co_block_status         = qemu_gluster_co_block_status,
     .create_opts                  = &qemu_gluster_create_opts,
 };
 
@@ -1493,7 +1492,7 @@ static BlockDriver bdrv_gluster_unix = {
     .bdrv_reopen_commit           = qemu_gluster_reopen_commit,
     .bdrv_reopen_abort            = qemu_gluster_reopen_abort,
     .bdrv_close                   = qemu_gluster_close,
-    .bdrv_create                  = qemu_gluster_create,
+    .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,
@@ -1507,7 +1506,7 @@ static BlockDriver bdrv_gluster_unix = {
 #ifdef CONFIG_GLUSTERFS_ZEROFILL
     .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes,
 #endif
-    .bdrv_co_get_block_status     = qemu_gluster_co_get_block_status,
+    .bdrv_co_block_status         = qemu_gluster_co_block_status,
     .create_opts                  = &qemu_gluster_create_opts,
 };
 
@@ -1527,7 +1526,7 @@ static BlockDriver bdrv_gluster_rdma = {
     .bdrv_reopen_commit           = qemu_gluster_reopen_commit,
     .bdrv_reopen_abort            = qemu_gluster_reopen_abort,
     .bdrv_close                   = qemu_gluster_close,
-    .bdrv_create                  = qemu_gluster_create,
+    .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,
@@ -1541,7 +1540,7 @@ static BlockDriver bdrv_gluster_rdma = {
 #ifdef CONFIG_GLUSTERFS_ZEROFILL
     .bdrv_co_pwrite_zeroes        = qemu_gluster_co_pwrite_zeroes,
 #endif
-    .bdrv_co_get_block_status     = qemu_gluster_co_get_block_status,
+    .bdrv_co_block_status         = qemu_gluster_co_block_status,
     .create_opts                  = &qemu_gluster_create_opts,
 };
 
index 89d0745e952f39d431d9df794af62766d90f3323..2b09c656d0899307329ffe1b7a9c5c1b319f5b0d 100644 (file)
@@ -25,6 +25,7 @@
 #include "qemu/osdep.h"
 #include "trace.h"
 #include "sysemu/block-backend.h"
+#include "block/aio-wait.h"
 #include "block/blockjob.h"
 #include "block/blockjob_int.h"
 #include "block/block_int.h"
@@ -587,16 +588,9 @@ void bdrv_inc_in_flight(BlockDriverState *bs)
     atomic_inc(&bs->in_flight);
 }
 
-static void dummy_bh_cb(void *opaque)
-{
-}
-
 void bdrv_wakeup(BlockDriverState *bs)
 {
-    /* The barrier (or an atomic op) is in the caller.  */
-    if (atomic_read(&bs->wakeup)) {
-        aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL);
-    }
+    aio_wait_kick(bdrv_get_aio_wait(bs));
 }
 
 void bdrv_dec_in_flight(BlockDriverState *bs)
@@ -1701,7 +1695,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
      */
     tracked_request_begin(&req, bs, offset, bytes, BDRV_TRACKED_WRITE);
 
-    if (!qiov) {
+    if (flags & BDRV_REQ_ZERO_WRITE) {
         ret = bdrv_co_do_zero_pwritev(child, offset, bytes, flags, &req);
         goto out;
     }
@@ -1868,30 +1862,34 @@ typedef struct BdrvCoBlockStatusData {
     bool done;
 } BdrvCoBlockStatusData;
 
-int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs,
-                                                        int64_t sector_num,
-                                                        int nb_sectors,
-                                                        int *pnum,
-                                                        BlockDriverState **file)
+int coroutine_fn bdrv_co_block_status_from_file(BlockDriverState *bs,
+                                                bool want_zero,
+                                                int64_t offset,
+                                                int64_t bytes,
+                                                int64_t *pnum,
+                                                int64_t *map,
+                                                BlockDriverState **file)
 {
     assert(bs->file && bs->file->bs);
-    *pnum = nb_sectors;
+    *pnum = bytes;
+    *map = offset;
     *file = bs->file->bs;
-    return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
-           (sector_num << BDRV_SECTOR_BITS);
+    return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID;
 }
 
-int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs,
-                                                           int64_t sector_num,
-                                                           int nb_sectors,
-                                                           int *pnum,
-                                                           BlockDriverState **file)
+int coroutine_fn bdrv_co_block_status_from_backing(BlockDriverState *bs,
+                                                   bool want_zero,
+                                                   int64_t offset,
+                                                   int64_t bytes,
+                                                   int64_t *pnum,
+                                                   int64_t *map,
+                                                   BlockDriverState **file)
 {
     assert(bs->backing && bs->backing->bs);
-    *pnum = nb_sectors;
+    *pnum = bytes;
+    *map = offset;
     *file = bs->backing->bs;
-    return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
-           (sector_num << BDRV_SECTOR_BITS);
+    return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID;
 }
 
 /*
@@ -1899,10 +1897,10 @@ int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs,
  * Drivers not implementing the functionality are assumed to not support
  * backing files, hence all their sectors are reported as allocated.
  *
- * If 'want_zero' is true, the caller is querying for mapping purposes,
- * and the result should include BDRV_BLOCK_OFFSET_VALID and
- * BDRV_BLOCK_ZERO where possible; otherwise, the result may omit those
- * bits particularly if it allows for a larger value in 'pnum'.
+ * If 'want_zero' is true, the caller is querying for mapping
+ * purposes, with a focus on valid BDRV_BLOCK_OFFSET_VALID, _DATA, and
+ * _ZERO where possible; otherwise, the result favors larger 'pnum',
+ * with a focus on accurate BDRV_BLOCK_ALLOCATED.
  *
  * If 'offset' is beyond the end of the disk image the return value is
  * BDRV_BLOCK_EOF and 'pnum' is set to 0.
@@ -1959,7 +1957,7 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs,
 
     /* Must be non-NULL or bdrv_getlength() would have failed */
     assert(bs->drv);
-    if (!bs->drv->bdrv_co_get_block_status) {
+    if (!bs->drv->bdrv_co_block_status) {
         *pnum = bytes;
         ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED;
         if (offset + bytes == total_size) {
@@ -1976,44 +1974,24 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs,
     bdrv_inc_in_flight(bs);
 
     /* Round out to request_alignment boundaries */
-    /* TODO: until we have a byte-based driver callback, we also have to
-     * round out to sectors, even if that is bigger than request_alignment */
-    align = MAX(bs->bl.request_alignment, BDRV_SECTOR_SIZE);
+    align = bs->bl.request_alignment;
     aligned_offset = QEMU_ALIGN_DOWN(offset, align);
     aligned_bytes = ROUND_UP(offset + bytes, align) - aligned_offset;
 
-    {
-        int count; /* sectors */
-        int64_t longret;
-
-        assert(QEMU_IS_ALIGNED(aligned_offset | aligned_bytes,
-                               BDRV_SECTOR_SIZE));
-        /*
-         * The contract allows us to return pnum smaller than bytes, even
-         * if the next query would see the same status; we truncate the
-         * request to avoid overflowing the driver's 32-bit interface.
-         */
-        longret = bs->drv->bdrv_co_get_block_status(
-            bs, aligned_offset >> BDRV_SECTOR_BITS,
-            MIN(INT_MAX, aligned_bytes) >> BDRV_SECTOR_BITS, &count,
-            &local_file);
-        if (longret < 0) {
-            assert(INT_MIN <= longret);
-            ret = longret;
-            goto out;
-        }
-        if (longret & BDRV_BLOCK_OFFSET_VALID) {
-            local_map = longret & BDRV_BLOCK_OFFSET_MASK;
-        }
-        ret = longret & ~BDRV_BLOCK_OFFSET_MASK;
-        *pnum = count * BDRV_SECTOR_SIZE;
+    ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset,
+                                        aligned_bytes, pnum, &local_map,
+                                        &local_file);
+    if (ret < 0) {
+        *pnum = 0;
+        goto out;
     }
 
     /*
-     * The driver's result must be a multiple of request_alignment.
+     * The driver's result must be a non-zero multiple of request_alignment.
      * Clamp pnum and adjust map to original request.
      */
-    assert(QEMU_IS_ALIGNED(*pnum, align) && align > offset - aligned_offset);
+    assert(*pnum && QEMU_IS_ALIGNED(*pnum, align) &&
+           align > offset - aligned_offset);
     *pnum -= offset - aligned_offset;
     if (*pnum > bytes) {
         *pnum = bytes;
index d2b320ea41ed1146b8473cfc8bc2c14ae767f746..8bf0e8724408cbdb55bafe30f1795104e21e041e 100644 (file)
@@ -86,7 +86,7 @@ typedef struct IscsiLun {
     unsigned long *allocmap;
     unsigned long *allocmap_valid;
     long allocmap_size;
-    int cluster_sectors;
+    int cluster_size;
     bool use_16_for_rw;
     bool write_protected;
     bool lbpme;
@@ -430,9 +430,10 @@ static int iscsi_allocmap_init(IscsiLun *iscsilun, int open_flags)
 {
     iscsi_allocmap_free(iscsilun);
 
+    assert(iscsilun->cluster_size);
     iscsilun->allocmap_size =
-        DIV_ROUND_UP(sector_lun2qemu(iscsilun->num_blocks, iscsilun),
-                     iscsilun->cluster_sectors);
+        DIV_ROUND_UP(iscsilun->num_blocks * iscsilun->block_size,
+                     iscsilun->cluster_size);
 
     iscsilun->allocmap = bitmap_try_new(iscsilun->allocmap_size);
     if (!iscsilun->allocmap) {
@@ -440,7 +441,7 @@ static int iscsi_allocmap_init(IscsiLun *iscsilun, int open_flags)
     }
 
     if (open_flags & BDRV_O_NOCACHE) {
-        /* in case that cache.direct = on all allocmap entries are
+        /* when cache.direct = on all allocmap entries are
          * treated as invalid to force a relookup of the block
          * status on every read request */
         return 0;
@@ -457,8 +458,8 @@ static int iscsi_allocmap_init(IscsiLun *iscsilun, int open_flags)
 }
 
 static void
-iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num,
-                      int nb_sectors, bool allocated, bool valid)
+iscsi_allocmap_update(IscsiLun *iscsilun, int64_t offset,
+                      int64_t bytes, bool allocated, bool valid)
 {
     int64_t cl_num_expanded, nb_cls_expanded, cl_num_shrunk, nb_cls_shrunk;
 
@@ -466,13 +467,13 @@ iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num,
         return;
     }
     /* expand to entirely contain all affected clusters */
-    cl_num_expanded = sector_num / iscsilun->cluster_sectors;
-    nb_cls_expanded = DIV_ROUND_UP(sector_num + nb_sectors,
-                                   iscsilun->cluster_sectors) - cl_num_expanded;
+    assert(iscsilun->cluster_size);
+    cl_num_expanded = offset / iscsilun->cluster_size;
+    nb_cls_expanded = DIV_ROUND_UP(offset + bytes,
+                                   iscsilun->cluster_size) - cl_num_expanded;
     /* shrink to touch only completely contained clusters */
-    cl_num_shrunk = DIV_ROUND_UP(sector_num, iscsilun->cluster_sectors);
-    nb_cls_shrunk = (sector_num + nb_sectors) / iscsilun->cluster_sectors
-                      - cl_num_shrunk;
+    cl_num_shrunk = DIV_ROUND_UP(offset, iscsilun->cluster_size);
+    nb_cls_shrunk = (offset + bytes) / iscsilun->cluster_size - cl_num_shrunk;
     if (allocated) {
         bitmap_set(iscsilun->allocmap, cl_num_expanded, nb_cls_expanded);
     } else {
@@ -495,26 +496,26 @@ iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num,
 }
 
 static void
-iscsi_allocmap_set_allocated(IscsiLun *iscsilun, int64_t sector_num,
-                             int nb_sectors)
+iscsi_allocmap_set_allocated(IscsiLun *iscsilun, int64_t offset,
+                             int64_t bytes)
 {
-    iscsi_allocmap_update(iscsilun, sector_num, nb_sectors, true, true);
+    iscsi_allocmap_update(iscsilun, offset, bytes, true, true);
 }
 
 static void
-iscsi_allocmap_set_unallocated(IscsiLun *iscsilun, int64_t sector_num,
-                               int nb_sectors)
+iscsi_allocmap_set_unallocated(IscsiLun *iscsilun, int64_t offset,
+                               int64_t bytes)
 {
     /* Note: if cache.direct=on the fifth argument to iscsi_allocmap_update
      * is ignored, so this will in effect be an iscsi_allocmap_set_invalid.
      */
-    iscsi_allocmap_update(iscsilun, sector_num, nb_sectors, false, true);
+    iscsi_allocmap_update(iscsilun, offset, bytes, false, true);
 }
 
-static void iscsi_allocmap_set_invalid(IscsiLun *iscsilun, int64_t sector_num,
-                                       int nb_sectors)
+static void iscsi_allocmap_set_invalid(IscsiLun *iscsilun, int64_t offset,
+                                       int64_t bytes)
 {
-    iscsi_allocmap_update(iscsilun, sector_num, nb_sectors, false, false);
+    iscsi_allocmap_update(iscsilun, offset, bytes, false, false);
 }
 
 static void iscsi_allocmap_invalidate(IscsiLun *iscsilun)
@@ -528,28 +529,30 @@ static void iscsi_allocmap_invalidate(IscsiLun *iscsilun)
 }
 
 static inline bool
-iscsi_allocmap_is_allocated(IscsiLun *iscsilun, int64_t sector_num,
-                            int nb_sectors)
+iscsi_allocmap_is_allocated(IscsiLun *iscsilun, int64_t offset,
+                            int64_t bytes)
 {
     unsigned long size;
     if (iscsilun->allocmap == NULL) {
         return true;
     }
-    size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors);
+    assert(iscsilun->cluster_size);
+    size = DIV_ROUND_UP(offset + bytes, iscsilun->cluster_size);
     return !(find_next_bit(iscsilun->allocmap, size,
-                           sector_num / iscsilun->cluster_sectors) == size);
+                           offset / iscsilun->cluster_size) == size);
 }
 
 static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun,
-                                           int64_t sector_num, int nb_sectors)
+                                           int64_t offset, int64_t bytes)
 {
     unsigned long size;
     if (iscsilun->allocmap_valid == NULL) {
         return false;
     }
-    size = DIV_ROUND_UP(sector_num + nb_sectors, iscsilun->cluster_sectors);
+    assert(iscsilun->cluster_size);
+    size = DIV_ROUND_UP(offset + bytes, iscsilun->cluster_size);
     return (find_next_zero_bit(iscsilun->allocmap_valid, size,
-                               sector_num / iscsilun->cluster_sectors) == size);
+                               offset / iscsilun->cluster_size) == size);
 }
 
 static int coroutine_fn
@@ -631,14 +634,16 @@ retry:
     }
 
     if (iTask.status != SCSI_STATUS_GOOD) {
-        iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors);
+        iscsi_allocmap_set_invalid(iscsilun, sector_num * BDRV_SECTOR_SIZE,
+                                   nb_sectors * BDRV_SECTOR_SIZE);
         error_report("iSCSI WRITE10/16 failed at lba %" PRIu64 ": %s", lba,
                      iTask.err_str);
         r = iTask.err_code;
         goto out_unlock;
     }
 
-    iscsi_allocmap_set_allocated(iscsilun, sector_num, nb_sectors);
+    iscsi_allocmap_set_allocated(iscsilun, sector_num * BDRV_SECTOR_SIZE,
+                                 nb_sectors * BDRV_SECTOR_SIZE);
 
 out_unlock:
     qemu_mutex_unlock(&iscsilun->mutex);
@@ -648,36 +653,36 @@ out_unlock:
 
 
 
-static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
-                                                  int64_t sector_num,
-                                                  int nb_sectors, int *pnum,
-                                                  BlockDriverState **file)
+static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs,
+                                              bool want_zero, int64_t offset,
+                                              int64_t bytes, int64_t *pnum,
+                                              int64_t *map,
+                                              BlockDriverState **file)
 {
     IscsiLun *iscsilun = bs->opaque;
     struct scsi_get_lba_status *lbas = NULL;
     struct scsi_lba_status_descriptor *lbasd = NULL;
     struct IscsiTask iTask;
     uint64_t lba;
-    int64_t ret;
+    int ret;
 
     iscsi_co_init_iscsitask(iscsilun, &iTask);
 
-    if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
-        ret = -EINVAL;
-        goto out;
-    }
+    assert(QEMU_IS_ALIGNED(offset | bytes, iscsilun->block_size));
 
     /* default to all sectors allocated */
-    ret = BDRV_BLOCK_DATA;
-    ret |= (sector_num << BDRV_SECTOR_BITS) | BDRV_BLOCK_OFFSET_VALID;
-    *pnum = nb_sectors;
+    ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
+    if (map) {
+        *map = offset;
+    }
+    *pnum = bytes;
 
     /* LUN does not support logical block provisioning */
     if (!iscsilun->lbpme) {
         goto out;
     }
 
-    lba = sector_qemu2lun(sector_num, iscsilun);
+    lba = offset / iscsilun->block_size;
 
     qemu_mutex_lock(&iscsilun->mutex);
 retry:
@@ -722,12 +727,12 @@ retry:
 
     lbasd = &lbas->descriptors[0];
 
-    if (sector_qemu2lun(sector_num, iscsilun) != lbasd->lba) {
+    if (lba != lbasd->lba) {
         ret = -EIO;
         goto out_unlock;
     }
 
-    *pnum = sector_lun2qemu(lbasd->num_blocks, iscsilun);
+    *pnum = lbasd->num_blocks * iscsilun->block_size;
 
     if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
         lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
@@ -738,13 +743,13 @@ retry:
     }
 
     if (ret & BDRV_BLOCK_ZERO) {
-        iscsi_allocmap_set_unallocated(iscsilun, sector_num, *pnum);
+        iscsi_allocmap_set_unallocated(iscsilun, offset, *pnum);
     } else {
-        iscsi_allocmap_set_allocated(iscsilun, sector_num, *pnum);
+        iscsi_allocmap_set_allocated(iscsilun, offset, *pnum);
     }
 
-    if (*pnum > nb_sectors) {
-        *pnum = nb_sectors;
+    if (*pnum > bytes) {
+        *pnum = bytes;
     }
 out_unlock:
     qemu_mutex_unlock(&iscsilun->mutex);
@@ -753,7 +758,7 @@ out:
     if (iTask.task != NULL) {
         scsi_free_scsi_task(iTask.task);
     }
-    if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) {
+    if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID && file) {
         *file = bs;
     }
     return ret;
@@ -780,29 +785,37 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
     /* if cache.direct is off and we have a valid entry in our allocation map
      * we can skip checking the block status and directly return zeroes if
      * the request falls within an unallocated area */
-    if (iscsi_allocmap_is_valid(iscsilun, sector_num, nb_sectors) &&
-        !iscsi_allocmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
+    if (iscsi_allocmap_is_valid(iscsilun, sector_num * BDRV_SECTOR_SIZE,
+                                nb_sectors * BDRV_SECTOR_SIZE) &&
+        !iscsi_allocmap_is_allocated(iscsilun, sector_num * BDRV_SECTOR_SIZE,
+                                     nb_sectors * BDRV_SECTOR_SIZE)) {
             qemu_iovec_memset(iov, 0, 0x00, iov->size);
             return 0;
     }
 
     if (nb_sectors >= ISCSI_CHECKALLOC_THRES &&
-        !iscsi_allocmap_is_valid(iscsilun, sector_num, nb_sectors) &&
-        !iscsi_allocmap_is_allocated(iscsilun, sector_num, nb_sectors)) {
-        int pnum;
-        BlockDriverState *file;
+        !iscsi_allocmap_is_valid(iscsilun, sector_num * BDRV_SECTOR_SIZE,
+                                 nb_sectors * BDRV_SECTOR_SIZE) &&
+        !iscsi_allocmap_is_allocated(iscsilun, sector_num * BDRV_SECTOR_SIZE,
+                                     nb_sectors * BDRV_SECTOR_SIZE)) {
+        int64_t pnum;
         /* check the block status from the beginning of the cluster
          * containing the start sector */
-        int64_t ret = iscsi_co_get_block_status(bs,
-                          sector_num - sector_num % iscsilun->cluster_sectors,
-                          BDRV_REQUEST_MAX_SECTORS, &pnum, &file);
+        int64_t head;
+        int ret;
+
+        assert(iscsilun->cluster_size);
+        head = (sector_num * BDRV_SECTOR_SIZE) % iscsilun->cluster_size;
+        ret = iscsi_co_block_status(bs, true,
+                                    sector_num * BDRV_SECTOR_SIZE - head,
+                                    BDRV_REQUEST_MAX_BYTES, &pnum, NULL, NULL);
         if (ret < 0) {
             return ret;
         }
         /* if the whole request falls into an unallocated area we can avoid
-         * to read and directly return zeroes instead */
+         * reading and directly return zeroes instead */
         if (ret & BDRV_BLOCK_ZERO &&
-            pnum >= nb_sectors + sector_num % iscsilun->cluster_sectors) {
+            pnum >= nb_sectors * BDRV_SECTOR_SIZE + head) {
             qemu_iovec_memset(iov, 0, 0x00, iov->size);
             return 0;
         }
@@ -1146,8 +1159,7 @@ retry:
         goto retry;
     }
 
-    iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
-                               bytes >> BDRV_SECTOR_BITS);
+    iscsi_allocmap_set_invalid(iscsilun, offset, bytes);
 
     if (iTask.status == SCSI_STATUS_CHECK_CONDITION) {
         /* the target might fail with a check condition if it
@@ -1260,8 +1272,7 @@ retry:
     }
 
     if (iTask.status != SCSI_STATUS_GOOD) {
-        iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
-                                   bytes >> BDRV_SECTOR_BITS);
+        iscsi_allocmap_set_invalid(iscsilun, offset, bytes);
         error_report("iSCSI WRITESAME10/16 failed at lba %" PRIu64 ": %s",
                      lba, iTask.err_str);
         r = iTask.err_code;
@@ -1269,11 +1280,9 @@ retry:
     }
 
     if (flags & BDRV_REQ_MAY_UNMAP) {
-        iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
-                                   bytes >> BDRV_SECTOR_BITS);
+        iscsi_allocmap_set_invalid(iscsilun, offset, bytes);
     } else {
-        iscsi_allocmap_set_allocated(iscsilun, offset >> BDRV_SECTOR_BITS,
-                                     bytes >> BDRV_SECTOR_BITS);
+        iscsi_allocmap_set_allocated(iscsilun, offset, bytes);
     }
 
 out_unlock:
@@ -1953,8 +1962,8 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
      * reasonable size */
     if (iscsilun->bl.opt_unmap_gran * iscsilun->block_size >= 4 * 1024 &&
         iscsilun->bl.opt_unmap_gran * iscsilun->block_size <= 16 * 1024 * 1024) {
-        iscsilun->cluster_sectors = (iscsilun->bl.opt_unmap_gran *
-                                     iscsilun->block_size) >> BDRV_SECTOR_BITS;
+        iscsilun->cluster_size = iscsilun->bl.opt_unmap_gran *
+            iscsilun->block_size;
         if (iscsilun->lbprz) {
             ret = iscsi_allocmap_init(iscsilun, bs->open_flags);
         }
@@ -2108,7 +2117,8 @@ static int iscsi_truncate(BlockDriverState *bs, int64_t offset,
     return 0;
 }
 
-static int iscsi_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opts,
+                                             Error **errp)
 {
     int ret = 0;
     int64_t total_size = 0;
@@ -2163,7 +2173,7 @@ static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
 {
     IscsiLun *iscsilun = bs->opaque;
     bdi->unallocated_blocks_are_zero = iscsilun->lbprz;
-    bdi->cluster_size = iscsilun->cluster_sectors * BDRV_SECTOR_SIZE;
+    bdi->cluster_size = iscsilun->cluster_size;
     return 0;
 }
 
@@ -2195,7 +2205,7 @@ static BlockDriver bdrv_iscsi = {
     .bdrv_parse_filename    = iscsi_parse_filename,
     .bdrv_file_open         = iscsi_open,
     .bdrv_close             = iscsi_close,
-    .bdrv_create            = iscsi_create,
+    .bdrv_co_create_opts    = iscsi_co_create_opts,
     .create_opts            = &iscsi_create_opts,
     .bdrv_reopen_prepare    = iscsi_reopen_prepare,
     .bdrv_reopen_commit     = iscsi_reopen_commit,
@@ -2206,7 +2216,7 @@ static BlockDriver bdrv_iscsi = {
     .bdrv_truncate   = iscsi_truncate,
     .bdrv_refresh_limits = iscsi_refresh_limits,
 
-    .bdrv_co_get_block_status = iscsi_co_get_block_status,
+    .bdrv_co_block_status  = iscsi_co_block_status,
     .bdrv_co_pdiscard      = iscsi_co_pdiscard,
     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
     .bdrv_co_readv         = iscsi_co_readv,
@@ -2230,7 +2240,7 @@ static BlockDriver bdrv_iser = {
     .bdrv_parse_filename    = iscsi_parse_filename,
     .bdrv_file_open         = iscsi_open,
     .bdrv_close             = iscsi_close,
-    .bdrv_create            = iscsi_create,
+    .bdrv_co_create_opts    = iscsi_co_create_opts,
     .create_opts            = &iscsi_create_opts,
     .bdrv_reopen_prepare    = iscsi_reopen_prepare,
     .bdrv_reopen_commit     = iscsi_reopen_commit,
@@ -2241,7 +2251,7 @@ static BlockDriver bdrv_iser = {
     .bdrv_truncate   = iscsi_truncate,
     .bdrv_refresh_limits = iscsi_refresh_limits,
 
-    .bdrv_co_get_block_status = iscsi_co_get_block_status,
+    .bdrv_co_block_status  = iscsi_co_block_status,
     .bdrv_co_pdiscard      = iscsi_co_pdiscard,
     .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
     .bdrv_co_readv         = iscsi_co_readv,
index c9badc1203b5ecbedf69b73cfbe53cc51db0df47..f5bf620942f5483403257009c4bafe892eb24b51 100644 (file)
@@ -1094,7 +1094,7 @@ static BlockDriver bdrv_mirror_top = {
     .bdrv_co_pwrite_zeroes      = bdrv_mirror_top_pwrite_zeroes,
     .bdrv_co_pdiscard           = bdrv_mirror_top_pdiscard,
     .bdrv_co_flush              = bdrv_mirror_top_flush,
-    .bdrv_co_get_block_status   = bdrv_co_get_block_status_from_backing,
+    .bdrv_co_block_status       = bdrv_co_block_status_from_backing,
     .bdrv_refresh_filename      = bdrv_mirror_top_refresh_filename,
     .bdrv_close                 = bdrv_mirror_top_close,
     .bdrv_child_perm            = bdrv_mirror_top_child_perm,
index bbdb4fadad0081954433ee33a6081972740045f2..1d82ff5042f712112a2e867cc6d0e19f3b59a9f4 100644 (file)
@@ -684,7 +684,8 @@ static QemuOptsList nfs_create_opts = {
     }
 };
 
-static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp)
+static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
+                                                Error **errp)
 {
     int64_t ret, total_size;
     NFSClient *client = g_new0(NFSClient, 1);
@@ -897,7 +898,7 @@ static BlockDriver bdrv_nfs = {
 
     .bdrv_file_open                 = nfs_file_open,
     .bdrv_close                     = nfs_file_close,
-    .bdrv_create                    = nfs_file_create,
+    .bdrv_co_create_opts            = nfs_file_co_create_opts,
     .bdrv_reopen_prepare            = nfs_reopen_prepare,
 
     .bdrv_co_preadv                 = nfs_co_preadv,
index 214d394fff42e27909b3bca9cf0fc1b73c0adf78..806a8631e4d245ce77e676194a3e33d6e0667981 100644 (file)
@@ -223,22 +223,23 @@ static int null_reopen_prepare(BDRVReopenState *reopen_state,
     return 0;
 }
 
-static int64_t coroutine_fn null_co_get_block_status(BlockDriverState *bs,
-                                                     int64_t sector_num,
-                                                     int nb_sectors, int *pnum,
-                                                     BlockDriverState **file)
+static int coroutine_fn null_co_block_status(BlockDriverState *bs,
+                                             bool want_zero, int64_t offset,
+                                             int64_t bytes, int64_t *pnum,
+                                             int64_t *map,
+                                             BlockDriverState **file)
 {
     BDRVNullState *s = bs->opaque;
-    off_t start = sector_num * BDRV_SECTOR_SIZE;
+    int ret = BDRV_BLOCK_OFFSET_VALID;
 
-    *pnum = nb_sectors;
+    *pnum = bytes;
+    *map = offset;
     *file = bs;
 
     if (s->read_zeroes) {
-        return BDRV_BLOCK_OFFSET_VALID | start | BDRV_BLOCK_ZERO;
-    } else {
-        return BDRV_BLOCK_OFFSET_VALID | start;
+        ret |= BDRV_BLOCK_ZERO;
     }
+    return ret;
 }
 
 static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
@@ -270,7 +271,7 @@ static BlockDriver bdrv_null_co = {
     .bdrv_co_flush_to_disk  = null_co_flush,
     .bdrv_reopen_prepare    = null_reopen_prepare,
 
-    .bdrv_co_get_block_status   = null_co_get_block_status,
+    .bdrv_co_block_status   = null_co_block_status,
 
     .bdrv_refresh_filename  = null_refresh_filename,
 };
@@ -290,7 +291,7 @@ static BlockDriver bdrv_null_aio = {
     .bdrv_aio_flush         = null_aio_flush,
     .bdrv_reopen_prepare    = null_reopen_prepare,
 
-    .bdrv_co_get_block_status   = null_co_get_block_status,
+    .bdrv_co_block_status   = null_co_block_status,
 
     .bdrv_refresh_filename  = null_refresh_filename,
 };
index 75078022f6e3460fe43faa1879313d54787a028c..8bca57aae695bff1a1a883f7368a2eba8b15df7c 100644 (file)
@@ -1072,18 +1072,6 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
     return 0;
 }
 
-static int64_t coroutine_fn nvme_co_get_block_status(BlockDriverState *bs,
-                                                     int64_t sector_num,
-                                                     int nb_sectors, int *pnum,
-                                                     BlockDriverState **file)
-{
-    *pnum = nb_sectors;
-    *file = bs;
-
-    return BDRV_BLOCK_ALLOCATED | BDRV_BLOCK_OFFSET_VALID |
-           (sector_num << BDRV_SECTOR_BITS);
-}
-
 static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
 {
     QINCREF(opts);
@@ -1183,8 +1171,6 @@ static BlockDriver bdrv_nvme = {
     .bdrv_co_flush_to_disk    = nvme_co_flush,
     .bdrv_reopen_prepare      = nvme_reopen_prepare,
 
-    .bdrv_co_get_block_status = nvme_co_get_block_status,
-
     .bdrv_refresh_filename    = nvme_refresh_filename,
     .bdrv_refresh_limits      = nvme_refresh_limits,
 
index e1e3d80c8875de17e3e3337c483e7f4f134a70f8..81085795c239c4fde5c8caa5078e650c03a50732 100644 (file)
@@ -261,23 +261,31 @@ static coroutine_fn int parallels_co_flush_to_os(BlockDriverState *bs)
 }
 
 
-static int64_t coroutine_fn parallels_co_get_block_status(BlockDriverState *bs,
-        int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
+static int coroutine_fn parallels_co_block_status(BlockDriverState *bs,
+                                                  bool want_zero,
+                                                  int64_t offset,
+                                                  int64_t bytes,
+                                                  int64_t *pnum,
+                                                  int64_t *map,
+                                                  BlockDriverState **file)
 {
     BDRVParallelsState *s = bs->opaque;
-    int64_t offset;
+    int count;
 
+    assert(QEMU_IS_ALIGNED(offset | bytes, BDRV_SECTOR_SIZE));
     qemu_co_mutex_lock(&s->lock);
-    offset = block_status(s, sector_num, nb_sectors, pnum);
+    offset = block_status(s, offset >> BDRV_SECTOR_BITS,
+                          bytes >> BDRV_SECTOR_BITS, &count);
     qemu_co_mutex_unlock(&s->lock);
 
+    *pnum = count * BDRV_SECTOR_SIZE;
     if (offset < 0) {
         return 0;
     }
 
+    *map = offset * BDRV_SECTOR_SIZE;
     *file = bs->file->bs;
-    return (offset << BDRV_SECTOR_BITS) |
-        BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
+    return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
 }
 
 static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
@@ -467,7 +475,9 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
 }
 
 
-static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn parallels_co_create_opts(const char *filename,
+                                                 QemuOpts *opts,
+                                                 Error **errp)
 {
     int64_t total_size, cl_size;
     uint8_t tmp[BDRV_SECTOR_SIZE];
@@ -782,13 +792,13 @@ static BlockDriver bdrv_parallels = {
     .bdrv_open         = parallels_open,
     .bdrv_close                = parallels_close,
     .bdrv_child_perm          = bdrv_format_default_perms,
-    .bdrv_co_get_block_status = parallels_co_get_block_status,
+    .bdrv_co_block_status     = parallels_co_block_status,
     .bdrv_has_zero_init       = bdrv_has_zero_init_1,
     .bdrv_co_flush_to_os      = parallels_co_flush_to_os,
     .bdrv_co_readv  = parallels_co_readv,
     .bdrv_co_writev = parallels_co_writev,
     .supports_backing = true,
-    .bdrv_create    = parallels_create,
+    .bdrv_co_create_opts = parallels_co_create_opts,
     .bdrv_check     = parallels_check,
     .create_opts    = &parallels_create_opts,
 };
index 8631155ac8121b92af0de9424900c5788d84cfb0..47a18d9a3a3cf4a20c3418967ad71cd7e41ec052 100644 (file)
@@ -524,23 +524,28 @@ static int get_cluster_offset(BlockDriverState *bs,
     return 1;
 }
 
-static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
-        int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
+static int coroutine_fn qcow_co_block_status(BlockDriverState *bs,
+                                             bool want_zero,
+                                             int64_t offset, int64_t bytes,
+                                             int64_t *pnum, int64_t *map,
+                                             BlockDriverState **file)
 {
     BDRVQcowState *s = bs->opaque;
-    int index_in_cluster, n, ret;
+    int index_in_cluster, ret;
+    int64_t n;
     uint64_t cluster_offset;
 
     qemu_co_mutex_lock(&s->lock);
-    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);
     qemu_co_mutex_unlock(&s->lock);
     if (ret < 0) {
         return ret;
     }
-    index_in_cluster = sector_num & (s->cluster_sectors - 1);
-    n = s->cluster_sectors - index_in_cluster;
-    if (n > nb_sectors)
-        n = nb_sectors;
+    index_in_cluster = offset & (s->cluster_size - 1);
+    n = s->cluster_size - index_in_cluster;
+    if (n > bytes) {
+        n = bytes;
+    }
     *pnum = n;
     if (!cluster_offset) {
         return 0;
@@ -548,9 +553,9 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
     if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypto) {
         return BDRV_BLOCK_DATA;
     }
-    cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
+    *map = cluster_offset | index_in_cluster;
     *file = bs->file->bs;
-    return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset;
+    return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
 }
 
 static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
@@ -805,7 +810,8 @@ static void qcow_close(BlockDriverState *bs)
     error_free(s->migration_blocker);
 }
 
-static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts,
+                                            Error **errp)
 {
     int header_size, backing_filename_len, l1_size, shift, i;
     QCowHeader header;
@@ -1122,13 +1128,13 @@ static BlockDriver bdrv_qcow = {
     .bdrv_close                = qcow_close,
     .bdrv_child_perm        = bdrv_format_default_perms,
     .bdrv_reopen_prepare    = qcow_reopen_prepare,
-    .bdrv_create            = qcow_create,
+    .bdrv_co_create_opts    = qcow_co_create_opts,
     .bdrv_has_zero_init     = bdrv_has_zero_init_1,
     .supports_backing       = true,
 
     .bdrv_co_readv          = qcow_co_readv,
     .bdrv_co_writev         = qcow_co_writev,
-    .bdrv_co_get_block_status   = qcow_co_get_block_status,
+    .bdrv_co_block_status   = qcow_co_block_status,
 
     .bdrv_make_empty        = qcow_make_empty,
     .bdrv_co_pwritev_compressed = qcow_co_pwritev_compressed,
index 4f6fd863eabb1fa42e8ea1b9c9050162eda1522b..5127276f90b89d65b439baed7f096ff55879368a 100644 (file)
@@ -413,8 +413,8 @@ static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry)
 
 static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size)
 {
-    return align_offset(sizeof(Qcow2BitmapDirEntry) +
-                        name_size + extra_data_size, 8);
+    int size = sizeof(Qcow2BitmapDirEntry) + name_size + extra_data_size;
+    return ROUND_UP(size, 8);
 }
 
 static inline int dir_entry_size(Qcow2BitmapDirEntry *entry)
index e406b0f3b9ead9417aa67146ca0203130bcfffa1..98908c426402ab63754401d8089f0212840256b0 100644 (file)
@@ -126,11 +126,11 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
 
     new_l1_size2 = sizeof(uint64_t) * new_l1_size;
     new_l1_table = qemu_try_blockalign(bs->file->bs,
-                                       align_offset(new_l1_size2, 512));
+                                       ROUND_UP(new_l1_size2, 512));
     if (new_l1_table == NULL) {
         return -ENOMEM;
     }
-    memset(new_l1_table, 0, align_offset(new_l1_size2, 512));
+    memset(new_l1_table, 0, ROUND_UP(new_l1_size2, 512));
 
     if (s->l1_size) {
         memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t));
index d46b69d7f348f7492448fb3590ef6c955291b153..126cca3276c43dd17b539b4f3f97279fa3a6c665 100644 (file)
@@ -1204,7 +1204,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
      * l1_table_offset when it is the current s->l1_table_offset! Be careful
      * when changing this! */
     if (l1_table_offset != s->l1_table_offset) {
-        l1_table = g_try_malloc0(align_offset(l1_size2, 512));
+        l1_table = g_try_malloc0(ROUND_UP(l1_size2, 512));
         if (l1_size2 && l1_table == NULL) {
             ret = -ENOMEM;
             goto fail;
@@ -2553,7 +2553,7 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
     }
 
     /* align range to test to cluster boundaries */
-    size = align_offset(offset_into_cluster(s, offset) + size, s->cluster_size);
+    size = ROUND_UP(offset_into_cluster(s, offset) + size, s->cluster_size);
     offset = start_of_cluster(s, offset);
 
     if ((chk & QCOW2_OL_ACTIVE_L1) && s->l1_size) {
index 44243e0e95bfbfb432bb315a94d4dab36d197c73..cee25f582b98b105f3a2f42388be9d6d0da29744 100644 (file)
@@ -66,7 +66,7 @@ int qcow2_read_snapshots(BlockDriverState *bs)
 
     for(i = 0; i < s->nb_snapshots; i++) {
         /* Read statically sized part of the snapshot header */
-        offset = align_offset(offset, 8);
+        offset = ROUND_UP(offset, 8);
         ret = bdrv_pread(bs->file, offset, &h, sizeof(h));
         if (ret < 0) {
             goto fail;
@@ -155,7 +155,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
     offset = 0;
     for(i = 0; i < s->nb_snapshots; i++) {
         sn = s->snapshots + i;
-        offset = align_offset(offset, 8);
+        offset = ROUND_UP(offset, 8);
         offset += sizeof(h);
         offset += sizeof(extra);
         offset += strlen(sn->id_str);
@@ -215,7 +215,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
         assert(id_str_size <= UINT16_MAX && name_size <= UINT16_MAX);
         h.id_str_size = cpu_to_be16(id_str_size);
         h.name_size = cpu_to_be16(name_size);
-        offset = align_offset(offset, 8);
+        offset = ROUND_UP(offset, 8);
 
         ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h));
         if (ret < 0) {
@@ -441,7 +441,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
     /* The VM state isn't needed any more in the active L1 table; in fact, it
      * hurts by causing expensive COW for the next snapshot. */
     qcow2_cluster_discard(bs, qcow2_vm_state_offset(s),
-                          align_offset(sn->vm_state_size, s->cluster_size),
+                          ROUND_UP(sn->vm_state_size, s->cluster_size),
                           QCOW2_DISCARD_NEVER, false);
 
 #ifdef DEBUG_ALLOC
@@ -710,7 +710,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs,
     }
     new_l1_bytes = sn->l1_size * sizeof(uint64_t);
     new_l1_table = qemu_try_blockalign(bs->file->bs,
-                                       align_offset(new_l1_bytes, 512));
+                                       ROUND_UP(new_l1_bytes, 512));
     if (new_l1_table == NULL) {
         return -ENOMEM;
     }
index 3dd098b74f8f569bd07f3257abc85af0a1e5bd32..071dc4d608f6e118eed480ee3ee59d79bf430192 100644 (file)
@@ -1377,7 +1377,7 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
 
     if (s->l1_size > 0) {
         s->l1_table = qemu_try_blockalign(bs->file->bs,
-            align_offset(s->l1_size * sizeof(uint64_t), 512));
+            ROUND_UP(s->l1_size * sizeof(uint64_t), 512));
         if (s->l1_table == NULL) {
             error_setg(errp, "Could not allocate L1 table");
             ret = -ENOMEM;
@@ -1668,32 +1668,34 @@ static void qcow2_join_options(QDict *options, QDict *old_options)
     }
 }
 
-static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
-        int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
+static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
+                                              bool want_zero,
+                                              int64_t offset, int64_t count,
+                                              int64_t *pnum, int64_t *map,
+                                              BlockDriverState **file)
 {
     BDRVQcow2State *s = bs->opaque;
     uint64_t cluster_offset;
     int index_in_cluster, ret;
     unsigned int bytes;
-    int64_t status = 0;
+    int status = 0;
 
-    bytes = MIN(INT_MAX, nb_sectors * BDRV_SECTOR_SIZE);
+    bytes = MIN(INT_MAX, count);
     qemu_co_mutex_lock(&s->lock);
-    ret = qcow2_get_cluster_offset(bs, sector_num << BDRV_SECTOR_BITS, &bytes,
-                                   &cluster_offset);
+    ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset);
     qemu_co_mutex_unlock(&s->lock);
     if (ret < 0) {
         return ret;
     }
 
-    *pnum = bytes >> BDRV_SECTOR_BITS;
+    *pnum = bytes;
 
     if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
         !s->crypto) {
-        index_in_cluster = sector_num & (s->cluster_sectors - 1);
-        cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
+        index_in_cluster = offset & (s->cluster_size - 1);
+        *map = cluster_offset | index_in_cluster;
         *file = bs->file->bs;
-        status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
+        status |= BDRV_BLOCK_OFFSET_VALID;
     }
     if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) {
         status |= BDRV_BLOCK_ZERO;
@@ -2638,19 +2640,19 @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size,
 {
     int64_t meta_size = 0;
     uint64_t nl1e, nl2e;
-    int64_t aligned_total_size = align_offset(total_size, cluster_size);
+    int64_t aligned_total_size = ROUND_UP(total_size, cluster_size);
 
     /* header: 1 cluster */
     meta_size += cluster_size;
 
     /* total size of L2 tables */
     nl2e = aligned_total_size / cluster_size;
-    nl2e = align_offset(nl2e, cluster_size / sizeof(uint64_t));
+    nl2e = ROUND_UP(nl2e, cluster_size / sizeof(uint64_t));
     meta_size += nl2e * sizeof(uint64_t);
 
     /* total size of L1 tables */
     nl1e = nl2e * sizeof(uint64_t) / cluster_size;
-    nl1e = align_offset(nl1e, cluster_size / sizeof(uint64_t));
+    nl1e = ROUND_UP(nl1e, cluster_size / sizeof(uint64_t));
     meta_size += nl1e * sizeof(uint64_t);
 
     /* total size of refcount table and blocks */
@@ -2721,11 +2723,12 @@ static uint64_t qcow2_opt_get_refcount_bits_del(QemuOpts *opts, int version,
     return refcount_bits;
 }
 
-static int qcow2_create2(const char *filename, int64_t total_size,
-                         const char *backing_file, const char *backing_format,
-                         int flags, size_t cluster_size, PreallocMode prealloc,
-                         QemuOpts *opts, int version, int refcount_order,
-                         const char *encryptfmt, Error **errp)
+static int coroutine_fn
+qcow2_co_create2(const char *filename, int64_t total_size,
+                 const char *backing_file, const char *backing_format,
+                 int flags, size_t cluster_size, PreallocMode prealloc,
+                 QemuOpts *opts, int version, int refcount_order,
+                 const char *encryptfmt, Error **errp)
 {
     QDict *options;
 
@@ -2912,7 +2915,8 @@ out:
     return ret;
 }
 
-static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opts,
+                                             Error **errp)
 {
     char *backing_file = NULL;
     char *backing_fmt = NULL;
@@ -2993,9 +2997,9 @@ static int qcow2_create(const char *filename, QemuOpts *opts, Error **errp)
 
     refcount_order = ctz32(refcount_bits);
 
-    ret = qcow2_create2(filename, size, backing_file, backing_fmt, flags,
-                        cluster_size, prealloc, opts, version, refcount_order,
-                        encryptfmt, &local_err);
+    ret = qcow2_co_create2(filename, size, backing_file, backing_fmt, flags,
+                           cluster_size, prealloc, opts, version, refcount_order,
+                           encryptfmt, &local_err);
     error_propagate(errp, local_err);
 
 finish:
@@ -3704,8 +3708,8 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
     has_backing_file = !!optstr;
     g_free(optstr);
 
-    virtual_size = align_offset(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
-                                cluster_size);
+    virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
+    virtual_size = ROUND_UP(virtual_size, cluster_size);
 
     /* Check that virtual disk size is valid */
     l2_tables = DIV_ROUND_UP(virtual_size / cluster_size,
@@ -3725,7 +3729,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs,
             goto err;
         }
 
-        virtual_size = align_offset(ssize, cluster_size);
+        virtual_size = ROUND_UP(ssize, cluster_size);
 
         if (has_backing_file) {
             /* We don't how much of the backing chain is shared by the input
@@ -4348,9 +4352,9 @@ BlockDriver bdrv_qcow2 = {
     .bdrv_reopen_abort    = qcow2_reopen_abort,
     .bdrv_join_options    = qcow2_join_options,
     .bdrv_child_perm      = bdrv_format_default_perms,
-    .bdrv_create        = qcow2_create,
+    .bdrv_co_create_opts  = qcow2_co_create_opts,
     .bdrv_has_zero_init = bdrv_has_zero_init_1,
-    .bdrv_co_get_block_status = qcow2_co_get_block_status,
+    .bdrv_co_block_status = qcow2_co_block_status,
 
     .bdrv_co_preadv         = qcow2_co_preadv,
     .bdrv_co_pwritev        = qcow2_co_pwritev,
index 883802241fb6f6c7b3ed3bfdf4a5cd43602442a8..1a84cc77b0b03bfab964b39c62539cd84167e761 100644 (file)
@@ -480,12 +480,6 @@ static inline int offset_to_l2_slice_index(BDRVQcow2State *s, int64_t offset)
     return (offset >> s->cluster_bits) & (s->l2_slice_size - 1);
 }
 
-static inline int64_t align_offset(int64_t offset, int n)
-{
-    offset = (offset + n - 1) & ~(n - 1);
-    return offset;
-}
-
 static inline int64_t qcow2_vm_state_offset(BDRVQcow2State *s)
 {
     return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
index c6ff3ab015d2c46f3510b78c7c371ab54ef08a78..72cf2f58ab84d454257aebdd4b4b4494fb2f4cf6 100644 (file)
@@ -638,7 +638,9 @@ out:
     return ret;
 }
 
-static int bdrv_qed_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
+                                                QemuOpts *opts,
+                                                Error **errp)
 {
     uint64_t image_size = 0;
     uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
@@ -688,74 +690,46 @@ finish:
     return ret;
 }
 
-typedef struct {
-    BlockDriverState *bs;
-    Coroutine *co;
-    uint64_t pos;
-    int64_t status;
-    int *pnum;
-    BlockDriverState **file;
-} QEDIsAllocatedCB;
-
-/* Called with table_lock held.  */
-static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len)
+static int coroutine_fn bdrv_qed_co_block_status(BlockDriverState *bs,
+                                                 bool want_zero,
+                                                 int64_t pos, int64_t bytes,
+                                                 int64_t *pnum, int64_t *map,
+                                                 BlockDriverState **file)
 {
-    QEDIsAllocatedCB *cb = opaque;
-    BDRVQEDState *s = cb->bs->opaque;
-    *cb->pnum = len / BDRV_SECTOR_SIZE;
+    BDRVQEDState *s = bs->opaque;
+    size_t len = MIN(bytes, SIZE_MAX);
+    int status;
+    QEDRequest request = { .l2_table = NULL };
+    uint64_t offset;
+    int ret;
+
+    qemu_co_mutex_lock(&s->table_lock);
+    ret = qed_find_cluster(s, &request, pos, &len, &offset);
+
+    *pnum = len;
     switch (ret) {
     case QED_CLUSTER_FOUND:
-        offset |= qed_offset_into_cluster(s, cb->pos);
-        cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
-        *cb->file = cb->bs->file->bs;
+        *map = offset | qed_offset_into_cluster(s, pos);
+        status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
+        *file = bs->file->bs;
         break;
     case QED_CLUSTER_ZERO:
-        cb->status = BDRV_BLOCK_ZERO;
+        status = BDRV_BLOCK_ZERO;
         break;
     case QED_CLUSTER_L2:
     case QED_CLUSTER_L1:
-        cb->status = 0;
+        status = 0;
         break;
     default:
         assert(ret < 0);
-        cb->status = ret;
+        status = ret;
         break;
     }
 
-    if (cb->co) {
-        aio_co_wake(cb->co);
-    }
-}
-
-static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
-                                                 int64_t sector_num,
-                                                 int nb_sectors, int *pnum,
-                                                 BlockDriverState **file)
-{
-    BDRVQEDState *s = bs->opaque;
-    size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE;
-    QEDIsAllocatedCB cb = {
-        .bs = bs,
-        .pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE,
-        .status = BDRV_BLOCK_OFFSET_MASK,
-        .pnum = pnum,
-        .file = file,
-    };
-    QEDRequest request = { .l2_table = NULL };
-    uint64_t offset;
-    int ret;
-
-    qemu_co_mutex_lock(&s->table_lock);
-    ret = qed_find_cluster(s, &request, cb.pos, &len, &offset);
-    qed_is_allocated_cb(&cb, ret, offset, len);
-
-    /* The callback was invoked immediately */
-    assert(cb.status != BDRV_BLOCK_OFFSET_MASK);
-
     qed_unref_l2_cache_entry(request.l2_table);
     qemu_co_mutex_unlock(&s->table_lock);
 
-    return cb.status;
+    return status;
 }
 
 static BDRVQEDState *acb_to_s(QEDAIOCB *acb)
@@ -1592,9 +1566,9 @@ static BlockDriver bdrv_qed = {
     .bdrv_close               = bdrv_qed_close,
     .bdrv_reopen_prepare      = bdrv_qed_reopen_prepare,
     .bdrv_child_perm          = bdrv_format_default_perms,
-    .bdrv_create              = bdrv_qed_create,
+    .bdrv_co_create_opts      = bdrv_qed_co_create_opts,
     .bdrv_has_zero_init       = bdrv_has_zero_init_1,
-    .bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
+    .bdrv_co_block_status     = bdrv_qed_co_block_status,
     .bdrv_co_readv            = bdrv_qed_co_readv,
     .bdrv_co_writev           = bdrv_qed_co_writev,
     .bdrv_co_pwrite_zeroes    = bdrv_qed_co_pwrite_zeroes,
index ab552c095416e8244287994ba63f5160d6d1af14..a378547c9983ae89558cd0a0fb557cb28d44f340 100644 (file)
@@ -250,17 +250,17 @@ fail:
     return ret;
 }
 
-static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
-                                            int64_t sector_num,
-                                            int nb_sectors, int *pnum,
+static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
+                                            bool want_zero, int64_t offset,
+                                            int64_t bytes, int64_t *pnum,
+                                            int64_t *map,
                                             BlockDriverState **file)
 {
     BDRVRawState *s = bs->opaque;
-    *pnum = nb_sectors;
+    *pnum = bytes;
     *file = bs->file->bs;
-    sector_num += s->offset / BDRV_SECTOR_SIZE;
-    return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
-           (sector_num << BDRV_SECTOR_BITS);
+    *map = offset + s->offset;
+    return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID;
 }
 
 static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs,
@@ -396,7 +396,8 @@ static int raw_has_zero_init(BlockDriverState *bs)
     return bdrv_has_zero_init(bs->file->bs);
 }
 
-static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
+                                           Error **errp)
 {
     return bdrv_create_file(filename, opts, errp);
 }
@@ -491,12 +492,12 @@ BlockDriver bdrv_raw = {
     .bdrv_open            = &raw_open,
     .bdrv_close           = &raw_close,
     .bdrv_child_perm      = bdrv_filter_default_perms,
-    .bdrv_create          = &raw_create,
+    .bdrv_co_create_opts  = &raw_co_create_opts,
     .bdrv_co_preadv       = &raw_co_preadv,
     .bdrv_co_pwritev      = &raw_co_pwritev,
     .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
     .bdrv_co_pdiscard     = &raw_co_pdiscard,
-    .bdrv_co_get_block_status = &raw_co_get_block_status,
+    .bdrv_co_block_status = &raw_co_block_status,
     .bdrv_truncate        = &raw_truncate,
     .bdrv_getlength       = &raw_getlength,
     .has_variable_length  = true,
index 8474b0ba117ca3701f86086cae0e9cdac02d99db..c7dd32e213b299c9110df691dbca8153286171ee 100644 (file)
@@ -351,7 +351,9 @@ static QemuOptsList runtime_opts = {
     },
 };
 
-static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
+                                                QemuOpts *opts,
+                                                Error **errp)
 {
     Error *local_err = NULL;
     int64_t bytes = 0;
@@ -1132,7 +1134,7 @@ static BlockDriver bdrv_rbd = {
     .bdrv_file_open         = qemu_rbd_open,
     .bdrv_close             = qemu_rbd_close,
     .bdrv_reopen_prepare    = qemu_rbd_reopen_prepare,
-    .bdrv_create            = qemu_rbd_create,
+    .bdrv_co_create_opts    = qemu_rbd_co_create_opts,
     .bdrv_has_zero_init     = bdrv_has_zero_init_1,
     .bdrv_get_info          = qemu_rbd_getinfo,
     .create_opts            = &qemu_rbd_create_opts,
index 215223053bb08e30b05a89f297c53d5b45c124ad..d8c10b7cac3392461261d336f8655a060a746d44 100644 (file)
@@ -1959,8 +1959,8 @@ static int parse_block_size_shift(BDRVSheepdogState *s, QemuOpts *opt)
     return 0;
 }
 
-static int sd_create(const char *filename, QemuOpts *opts,
-                     Error **errp)
+static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
+                                          Error **errp)
 {
     Error *err = NULL;
     int ret = 0;
@@ -3004,19 +3004,19 @@ static coroutine_fn int sd_co_pdiscard(BlockDriverState *bs, int64_t offset,
     return acb.ret;
 }
 
-static coroutine_fn int64_t
-sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
-                       int *pnum, BlockDriverState **file)
+static coroutine_fn int
+sd_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset,
+                   int64_t bytes, int64_t *pnum, int64_t *map,
+                   BlockDriverState **file)
 {
     BDRVSheepdogState *s = bs->opaque;
     SheepdogInode *inode = &s->inode;
     uint32_t object_size = (UINT32_C(1) << inode->block_size_shift);
-    uint64_t offset = sector_num * BDRV_SECTOR_SIZE;
     unsigned long start = offset / object_size,
-                  end = DIV_ROUND_UP((sector_num + nb_sectors) *
-                                     BDRV_SECTOR_SIZE, object_size);
+                  end = DIV_ROUND_UP(offset + bytes, object_size);
     unsigned long idx;
-    int64_t ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
+    *map = offset;
+    int ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
 
     for (idx = start; idx < end; idx++) {
         if (inode->data_vdi_id[idx] == 0) {
@@ -3033,9 +3033,9 @@ sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
         }
     }
 
-    *pnum = (idx - start) * object_size / BDRV_SECTOR_SIZE;
-    if (*pnum > nb_sectors) {
-        *pnum = nb_sectors;
+    *pnum = (idx - start) * object_size;
+    if (*pnum > bytes) {
+        *pnum = bytes;
     }
     if (ret > 0 && ret & BDRV_BLOCK_OFFSET_VALID) {
         *file = bs;
@@ -3103,7 +3103,7 @@ static BlockDriver bdrv_sheepdog = {
     .bdrv_reopen_commit           = sd_reopen_commit,
     .bdrv_reopen_abort            = sd_reopen_abort,
     .bdrv_close                   = sd_close,
-    .bdrv_create                  = sd_create,
+    .bdrv_co_create_opts          = sd_co_create_opts,
     .bdrv_has_zero_init           = bdrv_has_zero_init_1,
     .bdrv_getlength               = sd_getlength,
     .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
@@ -3113,7 +3113,7 @@ static BlockDriver bdrv_sheepdog = {
     .bdrv_co_writev               = sd_co_writev,
     .bdrv_co_flush_to_disk        = sd_co_flush_to_disk,
     .bdrv_co_pdiscard             = sd_co_pdiscard,
-    .bdrv_co_get_block_status     = sd_co_get_block_status,
+    .bdrv_co_block_status         = sd_co_block_status,
 
     .bdrv_snapshot_create         = sd_snapshot_create,
     .bdrv_snapshot_goto           = sd_snapshot_goto,
@@ -3139,7 +3139,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
     .bdrv_reopen_commit           = sd_reopen_commit,
     .bdrv_reopen_abort            = sd_reopen_abort,
     .bdrv_close                   = sd_close,
-    .bdrv_create                  = sd_create,
+    .bdrv_co_create_opts          = sd_co_create_opts,
     .bdrv_has_zero_init           = bdrv_has_zero_init_1,
     .bdrv_getlength               = sd_getlength,
     .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
@@ -3149,7 +3149,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
     .bdrv_co_writev               = sd_co_writev,
     .bdrv_co_flush_to_disk        = sd_co_flush_to_disk,
     .bdrv_co_pdiscard             = sd_co_pdiscard,
-    .bdrv_co_get_block_status     = sd_co_get_block_status,
+    .bdrv_co_block_status         = sd_co_block_status,
 
     .bdrv_snapshot_create         = sd_snapshot_create,
     .bdrv_snapshot_goto           = sd_snapshot_goto,
@@ -3175,7 +3175,7 @@ static BlockDriver bdrv_sheepdog_unix = {
     .bdrv_reopen_commit           = sd_reopen_commit,
     .bdrv_reopen_abort            = sd_reopen_abort,
     .bdrv_close                   = sd_close,
-    .bdrv_create                  = sd_create,
+    .bdrv_co_create_opts          = sd_co_create_opts,
     .bdrv_has_zero_init           = bdrv_has_zero_init_1,
     .bdrv_getlength               = sd_getlength,
     .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
@@ -3185,7 +3185,7 @@ static BlockDriver bdrv_sheepdog_unix = {
     .bdrv_co_writev               = sd_co_writev,
     .bdrv_co_flush_to_disk        = sd_co_flush_to_disk,
     .bdrv_co_pdiscard             = sd_co_pdiscard,
-    .bdrv_co_get_block_status     = sd_co_get_block_status,
+    .bdrv_co_block_status         = sd_co_block_status,
 
     .bdrv_snapshot_create         = sd_snapshot_create,
     .bdrv_snapshot_goto           = sd_snapshot_goto,
index b11d4c5e862d61c438221ae3824e6f1ae2c98c83..ff9929497d7baa10cc164a18a97e34e0bd698586 100644 (file)
@@ -803,6 +803,33 @@ static int ssh_file_open(BlockDriverState *bs, QDict *options, int bdrv_flags,
     return ret;
 }
 
+/* Note: This is a blocking operation */
+static int ssh_grow_file(BDRVSSHState *s, int64_t offset, Error **errp)
+{
+    ssize_t ret;
+    char c[1] = { '\0' };
+    int was_blocking = libssh2_session_get_blocking(s->session);
+
+    /* offset must be strictly greater than the current size so we do
+     * not overwrite anything */
+    assert(offset > 0 && offset > s->attrs.filesize);
+
+    libssh2_session_set_blocking(s->session, 1);
+
+    libssh2_sftp_seek64(s->sftp_handle, offset - 1);
+    ret = libssh2_sftp_write(s->sftp_handle, c, 1);
+
+    libssh2_session_set_blocking(s->session, was_blocking);
+
+    if (ret < 0) {
+        sftp_error_setg(errp, s, "Failed to grow file");
+        return -EIO;
+    }
+
+    s->attrs.filesize = offset;
+    return 0;
+}
+
 static QemuOptsList ssh_create_opts = {
     .name = "ssh-create-opts",
     .head = QTAILQ_HEAD_INITIALIZER(ssh_create_opts.head),
@@ -816,14 +843,13 @@ static QemuOptsList ssh_create_opts = {
     }
 };
 
-static int ssh_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
+                                           Error **errp)
 {
     int r, ret;
     int64_t total_size = 0;
     QDict *uri_options = NULL;
     BDRVSSHState s;
-    ssize_t r2;
-    char c[1] = { '\0' };
 
     ssh_state_init(&s);
 
@@ -849,14 +875,10 @@ static int ssh_create(const char *filename, QemuOpts *opts, Error **errp)
     }
 
     if (total_size > 0) {
-        libssh2_sftp_seek64(s.sftp_handle, total_size-1);
-        r2 = libssh2_sftp_write(s.sftp_handle, c, 1);
-        if (r2 < 0) {
-            sftp_error_setg(errp, &s, "truncate failed");
-            ret = -EINVAL;
+        ret = ssh_grow_file(&s, total_size, errp);
+        if (ret < 0) {
             goto out;
         }
-        s.attrs.filesize = total_size;
     }
 
     ret = 0;
@@ -1198,18 +1220,42 @@ static int64_t ssh_getlength(BlockDriverState *bs)
     return length;
 }
 
+static int ssh_truncate(BlockDriverState *bs, int64_t offset,
+                        PreallocMode prealloc, Error **errp)
+{
+    BDRVSSHState *s = bs->opaque;
+
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_str(prealloc));
+        return -ENOTSUP;
+    }
+
+    if (offset < s->attrs.filesize) {
+        error_setg(errp, "ssh driver does not support shrinking files");
+        return -ENOTSUP;
+    }
+
+    if (offset == s->attrs.filesize) {
+        return 0;
+    }
+
+    return ssh_grow_file(s, offset, errp);
+}
+
 static BlockDriver bdrv_ssh = {
     .format_name                  = "ssh",
     .protocol_name                = "ssh",
     .instance_size                = sizeof(BDRVSSHState),
     .bdrv_parse_filename          = ssh_parse_filename,
     .bdrv_file_open               = ssh_file_open,
-    .bdrv_create                  = ssh_create,
+    .bdrv_co_create_opts          = ssh_co_create_opts,
     .bdrv_close                   = ssh_close,
     .bdrv_has_zero_init           = ssh_has_zero_init,
     .bdrv_co_readv                = ssh_co_readv,
     .bdrv_co_writev               = ssh_co_writev,
     .bdrv_getlength               = ssh_getlength,
+    .bdrv_truncate                = ssh_truncate,
     .bdrv_co_flush_to_disk        = ssh_co_flush,
     .create_opts                  = &ssh_create_opts,
 };
index 495f88c7521dfa5bb826d76ce9f01e1041ef58f5..5f4d43d0fc57b84515bfb3790c28e9e9ec4ec603 100644 (file)
@@ -240,7 +240,7 @@ static BlockDriver bdrv_throttle = {
     .bdrv_reopen_prepare                =   throttle_reopen_prepare,
     .bdrv_reopen_commit                 =   throttle_reopen_commit,
     .bdrv_reopen_abort                  =   throttle_reopen_abort,
-    .bdrv_co_get_block_status           =   bdrv_co_get_block_status_from_file,
+    .bdrv_co_block_status               =   bdrv_co_block_status_from_file,
 
     .bdrv_co_drain_begin                =   throttle_co_drain_begin,
     .bdrv_co_drain_end                  =   throttle_co_drain_end,
index fc1c614cb122e7fefff0f1b01d941500fa04acb6..68592cc58d5985b2b4293b0b25e79ad9d210f862 100644 (file)
 #define DEFAULT_CLUSTER_SIZE (1 * MiB)
 
 #if defined(CONFIG_VDI_DEBUG)
-#define logout(fmt, ...) \
-                fprintf(stderr, "vdi\t%-24s" fmt, __func__, ##__VA_ARGS__)
+#define VDI_DEBUG 1
 #else
-#define logout(fmt, ...) ((void)0)
+#define VDI_DEBUG 0
 #endif
 
+#define logout(fmt, ...) \
+    do {                                                                \
+        if (VDI_DEBUG) {                                                \
+            fprintf(stderr, "vdi\t%-24s" fmt, __func__, ##__VA_ARGS__); \
+        }                                                               \
+    } while (0)
+
 /* Image signature. */
 #define VDI_SIGNATURE 0xbeda107f
 
@@ -166,8 +172,6 @@ typedef struct {
     uint32_t *bmap;
     /* Size of block (bytes). */
     uint32_t block_size;
-    /* Size of block (sectors). */
-    uint32_t block_sectors;
     /* First sector of block map. */
     uint32_t bmap_sector;
     /* VDI header (converted to host endianness). */
@@ -457,7 +461,6 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
     bs->total_sectors = header.disk_size / SECTOR_SIZE;
 
     s->block_size = header.block_size;
-    s->block_sectors = header.block_size / SECTOR_SIZE;
     s->bmap_sector = header.offset_bmap / SECTOR_SIZE;
     s->header = header;
 
@@ -503,33 +506,29 @@ static int vdi_reopen_prepare(BDRVReopenState *state,
     return 0;
 }
 
-static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs,
-        int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
+static int coroutine_fn vdi_co_block_status(BlockDriverState *bs,
+                                            bool want_zero,
+                                            int64_t offset, int64_t bytes,
+                                            int64_t *pnum, int64_t *map,
+                                            BlockDriverState **file)
 {
-    /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
     BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
-    size_t bmap_index = sector_num / s->block_sectors;
-    size_t sector_in_block = sector_num % s->block_sectors;
-    int n_sectors = s->block_sectors - sector_in_block;
+    size_t bmap_index = offset / s->block_size;
+    size_t index_in_block = offset % s->block_size;
     uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]);
-    uint64_t offset;
     int result;
 
-    logout("%p, %" PRId64 ", %d, %p\n", bs, sector_num, nb_sectors, pnum);
-    if (n_sectors > nb_sectors) {
-        n_sectors = nb_sectors;
-    }
-    *pnum = n_sectors;
+    logout("%p, %" PRId64 ", %" PRId64 ", %p\n", bs, offset, bytes, pnum);
+    *pnum = MIN(s->block_size - index_in_block, bytes);
     result = VDI_IS_ALLOCATED(bmap_entry);
     if (!result) {
         return 0;
     }
 
-    offset = s->header.offset_data +
-                              (uint64_t)bmap_entry * s->block_size +
-                              sector_in_block * SECTOR_SIZE;
+    *map = s->header.offset_data + (uint64_t)bmap_entry * s->block_size +
+        index_in_block;
     *file = bs->file->bs;
-    return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
+    return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
 }
 
 static int coroutine_fn
@@ -717,7 +716,8 @@ nonallocating_write:
     return ret;
 }
 
-static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
+                                           Error **errp)
 {
     int ret = 0;
     uint64_t bytes = 0;
@@ -895,9 +895,9 @@ static BlockDriver bdrv_vdi = {
     .bdrv_close = vdi_close,
     .bdrv_reopen_prepare = vdi_reopen_prepare,
     .bdrv_child_perm          = bdrv_format_default_perms,
-    .bdrv_create = vdi_create,
+    .bdrv_co_create_opts = vdi_co_create_opts,
     .bdrv_has_zero_init = bdrv_has_zero_init_1,
-    .bdrv_co_get_block_status = vdi_co_get_block_status,
+    .bdrv_co_block_status = vdi_co_block_status,
     .bdrv_make_empty = vdi_make_empty,
 
     .bdrv_co_preadv     = vdi_co_preadv,
index c449c5dcfd879675582556c3722e301511ffdd83..3fbff5048b3b1b4fa90ab46feb66f0f147c323ed 100644 (file)
@@ -1792,7 +1792,8 @@ exit:
  *    .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
  *   1MB
  */
-static int vhdx_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts,
+                                            Error **errp)
 {
     int ret = 0;
     uint64_t image_size = (uint64_t) 2 * GiB;
@@ -2003,7 +2004,7 @@ static BlockDriver bdrv_vhdx = {
     .bdrv_child_perm        = bdrv_format_default_perms,
     .bdrv_co_readv          = vhdx_co_readv,
     .bdrv_co_writev         = vhdx_co_writev,
-    .bdrv_create            = vhdx_create,
+    .bdrv_co_create_opts    = vhdx_co_create_opts,
     .bdrv_get_info          = vhdx_get_info,
     .bdrv_check             = vhdx_check,
     .bdrv_has_zero_init     = bdrv_has_zero_init_1,
index ef15ddbfd3d55290022377ac352df355e1696143..67342ed69b601ffe03111b6df3716e81982f8ddb 100644 (file)
@@ -1304,33 +1304,27 @@ static inline uint64_t vmdk_find_offset_in_cluster(VmdkExtent *extent,
     return extent_relative_offset % cluster_size;
 }
 
-static inline uint64_t vmdk_find_index_in_cluster(VmdkExtent *extent,
-                                                  int64_t sector_num)
-{
-    uint64_t offset;
-    offset = vmdk_find_offset_in_cluster(extent, sector_num * BDRV_SECTOR_SIZE);
-    return offset / BDRV_SECTOR_SIZE;
-}
-
-static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
-        int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
+static int coroutine_fn vmdk_co_block_status(BlockDriverState *bs,
+                                             bool want_zero,
+                                             int64_t offset, int64_t bytes,
+                                             int64_t *pnum, int64_t *map,
+                                             BlockDriverState **file)
 {
     BDRVVmdkState *s = bs->opaque;
     int64_t index_in_cluster, n, ret;
-    uint64_t offset;
+    uint64_t cluster_offset;
     VmdkExtent *extent;
 
-    extent = find_extent(s, sector_num, NULL);
+    extent = find_extent(s, offset >> BDRV_SECTOR_BITS, NULL);
     if (!extent) {
-        return 0;
+        return -EIO;
     }
     qemu_co_mutex_lock(&s->lock);
-    ret = get_cluster_offset(bs, extent, NULL,
-                             sector_num * 512, false, &offset,
+    ret = get_cluster_offset(bs, extent, NULL, offset, false, &cluster_offset,
                              0, 0);
     qemu_co_mutex_unlock(&s->lock);
 
-    index_in_cluster = vmdk_find_index_in_cluster(extent, sector_num);
+    index_in_cluster = vmdk_find_offset_in_cluster(extent, offset);
     switch (ret) {
     case VMDK_ERROR:
         ret = -EIO;
@@ -1345,18 +1339,14 @@ static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
         ret = BDRV_BLOCK_DATA;
         if (!extent->compressed) {
             ret |= BDRV_BLOCK_OFFSET_VALID;
-            ret |= (offset + (index_in_cluster << BDRV_SECTOR_BITS))
-                    & BDRV_BLOCK_OFFSET_MASK;
+            *map = cluster_offset + index_in_cluster;
         }
         *file = extent->file->bs;
         break;
     }
 
-    n = extent->cluster_sectors - index_in_cluster;
-    if (n > nb_sectors) {
-        n = nb_sectors;
-    }
-    *pnum = n;
+    n = extent->cluster_sectors * BDRV_SECTOR_SIZE - index_in_cluster;
+    *pnum = MIN(n, bytes);
     return ret;
 }
 
@@ -1892,7 +1882,8 @@ static int filename_decompose(const char *filename, char *path, char *prefix,
     return VMDK_OK;
 }
 
-static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts,
+                                            Error **errp)
 {
     int idx = 0;
     BlockBackend *new_blk = NULL;
@@ -2408,9 +2399,9 @@ static BlockDriver bdrv_vmdk = {
     .bdrv_co_pwritev_compressed   = vmdk_co_pwritev_compressed,
     .bdrv_co_pwrite_zeroes        = vmdk_co_pwrite_zeroes,
     .bdrv_close                   = vmdk_close,
-    .bdrv_create                  = vmdk_create,
+    .bdrv_co_create_opts          = vmdk_co_create_opts,
     .bdrv_co_flush_to_disk        = vmdk_co_flush,
-    .bdrv_co_get_block_status     = vmdk_co_get_block_status,
+    .bdrv_co_block_status         = vmdk_co_block_status,
     .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
     .bdrv_has_zero_init           = vmdk_has_zero_init,
     .bdrv_get_specific_info       = vmdk_get_specific_info,
index cfa5144e86786c399cb57f6b0974736927df6d5d..b2e2b9ebd43a55f959ca3852771654015b53d2f6 100644 (file)
@@ -706,53 +706,54 @@ fail:
     return ret;
 }
 
-static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
-        int64_t sector_num, int nb_sectors, int *pnum, BlockDriverState **file)
+static int coroutine_fn vpc_co_block_status(BlockDriverState *bs,
+                                            bool want_zero,
+                                            int64_t offset, int64_t bytes,
+                                            int64_t *pnum, int64_t *map,
+                                            BlockDriverState **file)
 {
     BDRVVPCState *s = bs->opaque;
     VHDFooter *footer = (VHDFooter*) s->footer_buf;
-    int64_t start, offset;
+    int64_t image_offset;
     bool allocated;
-    int64_t ret;
-    int n;
+    int ret;
+    int64_t n;
 
     if (be32_to_cpu(footer->type) == VHD_FIXED) {
-        *pnum = nb_sectors;
+        *pnum = bytes;
+        *map = offset;
         *file = bs->file->bs;
-        return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
-               (sector_num << BDRV_SECTOR_BITS);
+        return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID;
     }
 
     qemu_co_mutex_lock(&s->lock);
 
-    offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false, NULL);
-    start = offset;
-    allocated = (offset != -1);
+    image_offset = get_image_offset(bs, offset, false, NULL);
+    allocated = (image_offset != -1);
     *pnum = 0;
     ret = 0;
 
     do {
         /* All sectors in a block are contiguous (without using the bitmap) */
-        n = ROUND_UP(sector_num + 1, s->block_size / BDRV_SECTOR_SIZE)
-          - sector_num;
-        n = MIN(n, nb_sectors);
+        n = ROUND_UP(offset + 1, s->block_size) - offset;
+        n = MIN(n, bytes);
 
         *pnum += n;
-        sector_num += n;
-        nb_sectors -= n;
+        offset += n;
+        bytes -= n;
         /* *pnum can't be greater than one block for allocated
          * sectors since there is always a bitmap in between. */
         if (allocated) {
             *file = bs->file->bs;
-            ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
+            *map = image_offset;
+            ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID;
             break;
         }
-        if (nb_sectors == 0) {
+        if (bytes == 0) {
             break;
         }
-        offset = get_image_offset(bs, sector_num << BDRV_SECTOR_BITS, false,
-                                  NULL);
-    } while (offset == -1);
+        image_offset = get_image_offset(bs, offset, false, NULL);
+    } while (image_offset == -1);
 
     qemu_co_mutex_unlock(&s->lock);
     return ret;
@@ -896,7 +897,8 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
     return ret;
 }
 
-static int vpc_create(const char *filename, QemuOpts *opts, Error **errp)
+static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
+                                           Error **errp)
 {
     uint8_t buf[1024];
     VHDFooter *footer = (VHDFooter *) buf;
@@ -1094,11 +1096,11 @@ static BlockDriver bdrv_vpc = {
     .bdrv_close             = vpc_close,
     .bdrv_reopen_prepare    = vpc_reopen_prepare,
     .bdrv_child_perm        = bdrv_format_default_perms,
-    .bdrv_create            = vpc_create,
+    .bdrv_co_create_opts    = vpc_co_create_opts,
 
     .bdrv_co_preadv             = vpc_co_preadv,
     .bdrv_co_pwritev            = vpc_co_pwritev,
-    .bdrv_co_get_block_status   = vpc_co_get_block_status,
+    .bdrv_co_block_status       = vpc_co_block_status,
 
     .bdrv_get_info          = vpc_get_info,
 
index 7e06ebacf6176b660aa07d14f35ec6d68d151268..4a17a49e12823a9a755dcf8aaec186bb18b2eb70 100644 (file)
@@ -3088,15 +3088,13 @@ vvfat_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
     return ret;
 }
 
-static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs,
-        int64_t sector_num, int nb_sectors, int *n, BlockDriverState **file)
+static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs,
+                                              bool want_zero, int64_t offset,
+                                              int64_t bytes, int64_t *n,
+                                              int64_t *map,
+                                              BlockDriverState **file)
 {
-    *n = bs->total_sectors - sector_num;
-    if (*n > nb_sectors) {
-        *n = nb_sectors;
-    } else if (*n < 0) {
-        return 0;
-    }
+    *n = bytes;
     return BDRV_BLOCK_DATA;
 }
 
@@ -3257,7 +3255,7 @@ static BlockDriver bdrv_vvfat = {
 
     .bdrv_co_preadv         = vvfat_co_preadv,
     .bdrv_co_pwritev        = vvfat_co_pwritev,
-    .bdrv_co_get_block_status = vvfat_co_get_block_status,
+    .bdrv_co_block_status   = vvfat_co_block_status,
 };
 
 static void bdrv_vvfat_init(void)
index 22f65971a1d81179010af8fd32c3a3171ad8757c..a220803c012383c52b7c3f1ed3bad9b7b7ae9d92 100644 (file)
@@ -592,19 +592,23 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
             ret = 0;
         } else {
             tcp_chr_disconnect(init->chr);
-            return FALSE;
+            goto end;
         }
     }
     init->buflen -= ret;
 
     if (init->buflen == 0) {
         tcp_chr_connect(init->chr);
-        return FALSE;
+        goto end;
     }
 
     memmove(init->buf, init->buf + ret, init->buflen);
 
-    return TRUE;
+    return G_SOURCE_CONTINUE;
+
+end:
+    g_free(init);
+    return G_SOURCE_REMOVE;
 }
 
 static void tcp_chr_telnet_init(Chardev *chr)
@@ -703,6 +707,7 @@ static void tcp_chr_tls_init(Chardev *chr)
     qio_channel_tls_handshake(tioc,
                               tcp_chr_tls_handshake,
                               chr,
+                              NULL,
                               NULL);
 }
 
@@ -867,7 +872,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
     tcp_chr_set_client_ioc_name(chr, sioc);
     qio_channel_socket_connect_async(sioc, s->addr,
                                      qemu_chr_socket_connected,
-                                     chr, NULL);
+                                     chr, NULL, NULL);
 
     return false;
 }
@@ -951,7 +956,7 @@ static void qmp_chardev_open_socket(Chardev *chr,
         tcp_chr_set_client_ioc_name(chr, sioc);
         qio_channel_socket_connect_async(sioc, s->addr,
                                          qemu_chr_socket_connected,
-                                         chr, NULL);
+                                         chr, NULL, NULL);
     } else {
         if (s->is_listen) {
             char *name;
index 27d3f66bd536363d247eeb1dfd9b3f53346fe11f..f74e1f3b7cce0a5c4f56a48d0a8a7d65c3b55dd7 100755 (executable)
--- a/configure
+++ b/configure
@@ -2486,20 +2486,20 @@ fi
 if test "$whpx" != "no" ; then
     cat > $TMPC << EOF
 #include <windows.h>
-#include <winhvplatform.h>
-#include <winhvemulation.h>
+#include <WinHvPlatform.h>
+#include <WinHvEmulation.h>
 int main(void) {
     WHV_CAPABILITY whpx_cap;
     WHvGetCapability(WHvCapabilityCodeFeatures, &whpx_cap, sizeof(whpx_cap));
     return 0;
 }
 EOF
-    if compile_prog "" "-lwinhvplatform -lwinhvemulation" ; then
-        libs_softmmu="$libs_softmmu -lwinhvplatform -lwinhvemulation"
+    if compile_prog "" "-lWinHvPlatform -lWinHvEmulation" ; then
+        libs_softmmu="$libs_softmmu -lWinHvPlatform -lWinHvEmulation"
         whpx="yes"
     else
         if test "$whpx" = "yes"; then
-            feature_not_found "winhvplatform" "winhvemulation is not installed"
+            feature_not_found "WinHvPlatform" "WinHvEmulation is not installed"
         fi
         whpx="no"
     fi
@@ -5316,25 +5316,27 @@ fi
 ##########################################
 # checks for sanitizers
 
-# we could use a simple skeleton for flags checks, but this also
-# detect the static linking issue of ubsan, see also:
-# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84285
-cat > $TMPC << EOF
-#include <stdint.h>
-int main(void) {
-  return INT32_MIN / -1;
-}
-EOF
-
 have_asan=no
 have_ubsan=no
 have_asan_iface_h=no
 have_asan_iface_fiber=no
 
 if test "$sanitizers" = "yes" ; then
+  write_c_skeleton
   if compile_prog "$CPU_CFLAGS -Werror -fsanitize=address" ""; then
       have_asan=yes
   fi
+
+  # we could use a simple skeleton for flags checks, but this also
+  # detect the static linking issue of ubsan, see also:
+  # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84285
+  cat > $TMPC << EOF
+#include <stdlib.h>
+int main(void) {
+    void *tmp = malloc(10);
+    return *(int *)(tmp + 2);
+}
+EOF
   if compile_prog "$CPU_CFLAGS -Werror -fsanitize=undefined" ""; then
       have_ubsan=yes
   fi
@@ -5366,19 +5368,8 @@ if test "$gcov" = "yes" ; then
   LDFLAGS="-fprofile-arcs -ftest-coverage $LDFLAGS"
 elif test "$fortify_source" = "yes" ; then
   CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS"
-elif test "$debug" = "yes"; then
-  if compile_prog "-Og" ""; then
-      CFLAGS="-Og $CFLAGS"
-  elif compile_prog "-O1" ""; then
-      CFLAGS="-O1 $CFLAGS"
-  fi
-  # Workaround GCC false-positive Wuninitialized bugs with Og or O1:
-  # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=24639
-  if cc_has_warning_flag "-Wno-maybe-uninitialized"; then
-      CFLAGS="-Wno-maybe-uninitialized $CFLAGS"
-  fi
-else
-    CFLAGS="-O2 $CFLAGS"
+elif test "$debug" = "no"; then
+  CFLAGS="-O2 $CFLAGS"
 fi
 
 if test "$have_asan" = "yes"; then
@@ -6806,6 +6797,16 @@ case "$target_name" in
     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"
   ;;
+  riscv32)
+    TARGET_BASE_ARCH=riscv
+    TARGET_ABI_DIR=riscv
+    mttcg=yes
+  ;;
+  riscv64)
+    TARGET_BASE_ARCH=riscv
+    TARGET_ABI_DIR=riscv
+    mttcg=yes
+  ;;
   sh4|sh4eb)
     TARGET_ARCH=sh4
     bflt="yes"
@@ -6975,6 +6976,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
   ppc*)
     disas_config "PPC"
   ;;
+  riscv)
+    disas_config "RISCV"
+  ;;
   s390*)
     disas_config "S390"
   ;;
diff --git a/cpus.c b/cpus.c
index 4f5f88edba9ebaf7fe4ce7c8efe4b3dd3f1f051c..c652da84cf4825225fb51b8bbca924a3ef35c0ef 100644 (file)
--- a/cpus.c
+++ b/cpus.c
@@ -993,7 +993,7 @@ void cpu_synchronize_all_pre_loadvm(void)
     }
 }
 
-static int do_vm_stop(RunState state)
+static int do_vm_stop(RunState state, bool send_stop)
 {
     int ret = 0;
 
@@ -1002,7 +1002,9 @@ static int do_vm_stop(RunState state)
         pause_all_vcpus();
         runstate_set(state);
         vm_state_notify(0, state);
-        qapi_event_send_stop(&error_abort);
+        if (send_stop) {
+            qapi_event_send_stop(&error_abort);
+        }
     }
 
     bdrv_drain_all();
@@ -1012,6 +1014,14 @@ static int do_vm_stop(RunState state)
     return ret;
 }
 
+/* Special vm_stop() variant for terminating the process.  Historically clients
+ * did not expect a QMP STOP event and so we need to retain compatibility.
+ */
+int vm_shutdown(void)
+{
+    return do_vm_stop(RUN_STATE_SHUTDOWN, false);
+}
+
 static bool cpu_can_run(CPUState *cpu)
 {
     if (cpu->stop) {
@@ -1383,11 +1393,9 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
     qemu_mutex_lock_iothread();
     qemu_thread_get_self(cpu->thread);
 
-    CPU_FOREACH(cpu) {
-        cpu->thread_id = qemu_get_thread_id();
-        cpu->created = true;
-        cpu->can_do_io = 1;
-    }
+    cpu->thread_id = qemu_get_thread_id();
+    cpu->created = true;
+    cpu->can_do_io = 1;
     qemu_cond_signal(&qemu_cpu_cond);
 
     /* wait for initial kick-off after machine start */
@@ -1856,13 +1864,13 @@ static void qemu_tcg_init_vcpu(CPUState *cpu)
 #ifdef _WIN32
         cpu->hThread = qemu_thread_get_handle(cpu->thread);
 #endif
-        while (!cpu->created) {
-            qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
-        }
     } else {
         /* For non-MTTCG cases we share the thread */
         cpu->thread = single_tcg_cpu_thread;
         cpu->halt_cond = single_tcg_halt_cond;
+        cpu->thread_id = first_cpu->thread_id;
+        cpu->can_do_io = 1;
+        cpu->created = true;
     }
 }
 
@@ -1881,9 +1889,6 @@ static void qemu_hax_start_vcpu(CPUState *cpu)
 #ifdef _WIN32
     cpu->hThread = qemu_thread_get_handle(cpu->thread);
 #endif
-    while (!cpu->created) {
-        qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
-    }
 }
 
 static void qemu_kvm_start_vcpu(CPUState *cpu)
@@ -1897,9 +1902,6 @@ static void qemu_kvm_start_vcpu(CPUState *cpu)
              cpu->cpu_index);
     qemu_thread_create(cpu->thread, thread_name, qemu_kvm_cpu_thread_fn,
                        cpu, QEMU_THREAD_JOINABLE);
-    while (!cpu->created) {
-        qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
-    }
 }
 
 static void qemu_hvf_start_vcpu(CPUState *cpu)
@@ -1918,9 +1920,6 @@ static void qemu_hvf_start_vcpu(CPUState *cpu)
              cpu->cpu_index);
     qemu_thread_create(cpu->thread, thread_name, qemu_hvf_cpu_thread_fn,
                        cpu, QEMU_THREAD_JOINABLE);
-    while (!cpu->created) {
-        qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
-    }
 }
 
 static void qemu_whpx_start_vcpu(CPUState *cpu)
@@ -1937,9 +1936,6 @@ static void qemu_whpx_start_vcpu(CPUState *cpu)
 #ifdef _WIN32
     cpu->hThread = qemu_thread_get_handle(cpu->thread);
 #endif
-    while (!cpu->created) {
-        qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
-    }
 }
 
 static void qemu_dummy_start_vcpu(CPUState *cpu)
@@ -1953,9 +1949,6 @@ static void qemu_dummy_start_vcpu(CPUState *cpu)
              cpu->cpu_index);
     qemu_thread_create(cpu->thread, thread_name, qemu_dummy_cpu_thread_fn, cpu,
                        QEMU_THREAD_JOINABLE);
-    while (!cpu->created) {
-        qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
-    }
 }
 
 void qemu_init_vcpu(CPUState *cpu)
@@ -1985,6 +1978,10 @@ void qemu_init_vcpu(CPUState *cpu)
     } else {
         qemu_dummy_start_vcpu(cpu);
     }
+
+    while (!cpu->created) {
+        qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
+    }
 }
 
 void cpu_stop_current(void)
@@ -2007,7 +2004,7 @@ int vm_stop(RunState state)
         return 0;
     }
 
-    return do_vm_stop(state);
+    return do_vm_stop(state, true);
 }
 
 /**
@@ -2094,6 +2091,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
 #elif defined(TARGET_SPARC)
         SPARCCPU *sparc_cpu = SPARC_CPU(cpu);
         CPUSPARCState *env = &sparc_cpu->env;
+#elif defined(TARGET_RISCV)
+        RISCVCPU *riscv_cpu = RISCV_CPU(cpu);
+        CPURISCVState *env = &riscv_cpu->env;
 #elif defined(TARGET_MIPS)
         MIPSCPU *mips_cpu = MIPS_CPU(cpu);
         CPUMIPSState *env = &mips_cpu->env;
@@ -2133,6 +2133,9 @@ CpuInfoList *qmp_query_cpus(Error **errp)
 #elif defined(TARGET_S390X)
         info->value->arch = CPU_INFO_ARCH_S390;
         info->value->u.s390.cpu_state = env->cpu_state;
+#elif defined(TARGET_RISCV)
+        info->value->arch = CPU_INFO_ARCH_RISCV;
+        info->value->u.riscv.pc = env->pc;
 #else
         info->value->arch = CPU_INFO_ARCH_OTHER;
 #endif
diff --git a/default-configs/riscv32-linux-user.mak b/default-configs/riscv32-linux-user.mak
new file mode 100644 (file)
index 0000000..865b362
--- /dev/null
@@ -0,0 +1 @@
+# Default configuration for riscv-linux-user
diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak
new file mode 100644 (file)
index 0000000..f9e7421
--- /dev/null
@@ -0,0 +1,4 @@
+# Default configuration for riscv-softmmu
+
+CONFIG_SERIAL=y
+CONFIG_VIRTIO=y
diff --git a/default-configs/riscv64-linux-user.mak b/default-configs/riscv64-linux-user.mak
new file mode 100644 (file)
index 0000000..865b362
--- /dev/null
@@ -0,0 +1 @@
+# Default configuration for riscv-linux-user
diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak
new file mode 100644 (file)
index 0000000..f9e7421
--- /dev/null
@@ -0,0 +1,4 @@
+# Default configuration for riscv-softmmu
+
+CONFIG_SERIAL=y
+CONFIG_VIRTIO=y
diff --git a/disas.c b/disas.c
index d4ad1089efb3b4ae6639d25e0b177f431bc2b106..5325b7e6be67577da9493381a27ea03fcb33c224 100644 (file)
--- a/disas.c
+++ b/disas.c
@@ -522,6 +522,8 @@ void disas(FILE *out, void *code, unsigned long size)
 # ifdef _ARCH_PPC64
     s.info.cap_mode = CS_MODE_64;
 # endif
+#elif defined(__riscv__)
+    print_insn = print_insn_riscv;
 #elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS)
     print_insn = print_insn_arm_a64;
     s.info.cap_arch = CS_ARCH_ARM64;
index 53556f8f5a9ff09596b969218191dcfac6a96e77..213be2fab2035975e72d1629d553e73cf5e08012 100644 (file)
@@ -17,6 +17,7 @@ common-obj-$(CONFIG_MIPS_DIS) += mips.o
 common-obj-$(CONFIG_NIOS2_DIS) += nios2.o
 common-obj-$(CONFIG_MOXIE_DIS) += moxie.o
 common-obj-$(CONFIG_PPC_DIS) += ppc.o
+common-obj-$(CONFIG_RISCV_DIS) += riscv.o
 common-obj-$(CONFIG_S390_DIS) += s390.o
 common-obj-$(CONFIG_SH4_DIS) += sh4.o
 common-obj-$(CONFIG_SPARC_DIS) += sparc.o
diff --git a/disas/riscv.c b/disas/riscv.c
new file mode 100644 (file)
index 0000000..3c17501
--- /dev/null
@@ -0,0 +1,3048 @@
+/*
+ * QEMU RISC-V Disassembler
+ *
+ * Copyright (c) 2016-2017 Michael Clark <michaeljclark@mac.com>
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "disas/bfd.h"
+
+
+/* types */
+
+typedef uint64_t rv_inst;
+typedef uint16_t rv_opcode;
+
+/* enums */
+
+typedef enum {
+    rv32,
+    rv64,
+    rv128
+} rv_isa;
+
+typedef enum {
+    rv_rm_rne = 0,
+    rv_rm_rtz = 1,
+    rv_rm_rdn = 2,
+    rv_rm_rup = 3,
+    rv_rm_rmm = 4,
+    rv_rm_dyn = 7,
+} rv_rm;
+
+typedef enum {
+    rv_fence_i = 8,
+    rv_fence_o = 4,
+    rv_fence_r = 2,
+    rv_fence_w = 1,
+} rv_fence;
+
+typedef enum {
+    rv_ireg_zero,
+    rv_ireg_ra,
+    rv_ireg_sp,
+    rv_ireg_gp,
+    rv_ireg_tp,
+    rv_ireg_t0,
+    rv_ireg_t1,
+    rv_ireg_t2,
+    rv_ireg_s0,
+    rv_ireg_s1,
+    rv_ireg_a0,
+    rv_ireg_a1,
+    rv_ireg_a2,
+    rv_ireg_a3,
+    rv_ireg_a4,
+    rv_ireg_a5,
+    rv_ireg_a6,
+    rv_ireg_a7,
+    rv_ireg_s2,
+    rv_ireg_s3,
+    rv_ireg_s4,
+    rv_ireg_s5,
+    rv_ireg_s6,
+    rv_ireg_s7,
+    rv_ireg_s8,
+    rv_ireg_s9,
+    rv_ireg_s10,
+    rv_ireg_s11,
+    rv_ireg_t3,
+    rv_ireg_t4,
+    rv_ireg_t5,
+    rv_ireg_t6,
+} rv_ireg;
+
+typedef enum {
+    rvc_end,
+    rvc_simm_6,
+    rvc_imm_6,
+    rvc_imm_7,
+    rvc_imm_8,
+    rvc_imm_9,
+    rvc_imm_10,
+    rvc_imm_12,
+    rvc_imm_18,
+    rvc_imm_nz,
+    rvc_imm_x2,
+    rvc_imm_x4,
+    rvc_imm_x8,
+    rvc_imm_x16,
+    rvc_rd_b3,
+    rvc_rs1_b3,
+    rvc_rs2_b3,
+    rvc_rd_eq_rs1,
+    rvc_rd_eq_ra,
+    rvc_rd_eq_sp,
+    rvc_rd_eq_x0,
+    rvc_rs1_eq_sp,
+    rvc_rs1_eq_x0,
+    rvc_rs2_eq_x0,
+    rvc_rd_ne_x0_x2,
+    rvc_rd_ne_x0,
+    rvc_rs1_ne_x0,
+    rvc_rs2_ne_x0,
+    rvc_rs2_eq_rs1,
+    rvc_rs1_eq_ra,
+    rvc_imm_eq_zero,
+    rvc_imm_eq_n1,
+    rvc_imm_eq_p1,
+    rvc_csr_eq_0x001,
+    rvc_csr_eq_0x002,
+    rvc_csr_eq_0x003,
+    rvc_csr_eq_0xc00,
+    rvc_csr_eq_0xc01,
+    rvc_csr_eq_0xc02,
+    rvc_csr_eq_0xc80,
+    rvc_csr_eq_0xc81,
+    rvc_csr_eq_0xc82,
+} rvc_constraint;
+
+typedef enum {
+    rv_codec_illegal,
+    rv_codec_none,
+    rv_codec_u,
+    rv_codec_uj,
+    rv_codec_i,
+    rv_codec_i_sh5,
+    rv_codec_i_sh6,
+    rv_codec_i_sh7,
+    rv_codec_i_csr,
+    rv_codec_s,
+    rv_codec_sb,
+    rv_codec_r,
+    rv_codec_r_m,
+    rv_codec_r4_m,
+    rv_codec_r_a,
+    rv_codec_r_l,
+    rv_codec_r_f,
+    rv_codec_cb,
+    rv_codec_cb_imm,
+    rv_codec_cb_sh5,
+    rv_codec_cb_sh6,
+    rv_codec_ci,
+    rv_codec_ci_sh5,
+    rv_codec_ci_sh6,
+    rv_codec_ci_16sp,
+    rv_codec_ci_lwsp,
+    rv_codec_ci_ldsp,
+    rv_codec_ci_lqsp,
+    rv_codec_ci_li,
+    rv_codec_ci_lui,
+    rv_codec_ci_none,
+    rv_codec_ciw_4spn,
+    rv_codec_cj,
+    rv_codec_cj_jal,
+    rv_codec_cl_lw,
+    rv_codec_cl_ld,
+    rv_codec_cl_lq,
+    rv_codec_cr,
+    rv_codec_cr_mv,
+    rv_codec_cr_jalr,
+    rv_codec_cr_jr,
+    rv_codec_cs,
+    rv_codec_cs_sw,
+    rv_codec_cs_sd,
+    rv_codec_cs_sq,
+    rv_codec_css_swsp,
+    rv_codec_css_sdsp,
+    rv_codec_css_sqsp,
+} rv_codec;
+
+typedef enum {
+    rv_op_illegal = 0,
+    rv_op_lui = 1,
+    rv_op_auipc = 2,
+    rv_op_jal = 3,
+    rv_op_jalr = 4,
+    rv_op_beq = 5,
+    rv_op_bne = 6,
+    rv_op_blt = 7,
+    rv_op_bge = 8,
+    rv_op_bltu = 9,
+    rv_op_bgeu = 10,
+    rv_op_lb = 11,
+    rv_op_lh = 12,
+    rv_op_lw = 13,
+    rv_op_lbu = 14,
+    rv_op_lhu = 15,
+    rv_op_sb = 16,
+    rv_op_sh = 17,
+    rv_op_sw = 18,
+    rv_op_addi = 19,
+    rv_op_slti = 20,
+    rv_op_sltiu = 21,
+    rv_op_xori = 22,
+    rv_op_ori = 23,
+    rv_op_andi = 24,
+    rv_op_slli = 25,
+    rv_op_srli = 26,
+    rv_op_srai = 27,
+    rv_op_add = 28,
+    rv_op_sub = 29,
+    rv_op_sll = 30,
+    rv_op_slt = 31,
+    rv_op_sltu = 32,
+    rv_op_xor = 33,
+    rv_op_srl = 34,
+    rv_op_sra = 35,
+    rv_op_or = 36,
+    rv_op_and = 37,
+    rv_op_fence = 38,
+    rv_op_fence_i = 39,
+    rv_op_lwu = 40,
+    rv_op_ld = 41,
+    rv_op_sd = 42,
+    rv_op_addiw = 43,
+    rv_op_slliw = 44,
+    rv_op_srliw = 45,
+    rv_op_sraiw = 46,
+    rv_op_addw = 47,
+    rv_op_subw = 48,
+    rv_op_sllw = 49,
+    rv_op_srlw = 50,
+    rv_op_sraw = 51,
+    rv_op_ldu = 52,
+    rv_op_lq = 53,
+    rv_op_sq = 54,
+    rv_op_addid = 55,
+    rv_op_sllid = 56,
+    rv_op_srlid = 57,
+    rv_op_sraid = 58,
+    rv_op_addd = 59,
+    rv_op_subd = 60,
+    rv_op_slld = 61,
+    rv_op_srld = 62,
+    rv_op_srad = 63,
+    rv_op_mul = 64,
+    rv_op_mulh = 65,
+    rv_op_mulhsu = 66,
+    rv_op_mulhu = 67,
+    rv_op_div = 68,
+    rv_op_divu = 69,
+    rv_op_rem = 70,
+    rv_op_remu = 71,
+    rv_op_mulw = 72,
+    rv_op_divw = 73,
+    rv_op_divuw = 74,
+    rv_op_remw = 75,
+    rv_op_remuw = 76,
+    rv_op_muld = 77,
+    rv_op_divd = 78,
+    rv_op_divud = 79,
+    rv_op_remd = 80,
+    rv_op_remud = 81,
+    rv_op_lr_w = 82,
+    rv_op_sc_w = 83,
+    rv_op_amoswap_w = 84,
+    rv_op_amoadd_w = 85,
+    rv_op_amoxor_w = 86,
+    rv_op_amoor_w = 87,
+    rv_op_amoand_w = 88,
+    rv_op_amomin_w = 89,
+    rv_op_amomax_w = 90,
+    rv_op_amominu_w = 91,
+    rv_op_amomaxu_w = 92,
+    rv_op_lr_d = 93,
+    rv_op_sc_d = 94,
+    rv_op_amoswap_d = 95,
+    rv_op_amoadd_d = 96,
+    rv_op_amoxor_d = 97,
+    rv_op_amoor_d = 98,
+    rv_op_amoand_d = 99,
+    rv_op_amomin_d = 100,
+    rv_op_amomax_d = 101,
+    rv_op_amominu_d = 102,
+    rv_op_amomaxu_d = 103,
+    rv_op_lr_q = 104,
+    rv_op_sc_q = 105,
+    rv_op_amoswap_q = 106,
+    rv_op_amoadd_q = 107,
+    rv_op_amoxor_q = 108,
+    rv_op_amoor_q = 109,
+    rv_op_amoand_q = 110,
+    rv_op_amomin_q = 111,
+    rv_op_amomax_q = 112,
+    rv_op_amominu_q = 113,
+    rv_op_amomaxu_q = 114,
+    rv_op_ecall = 115,
+    rv_op_ebreak = 116,
+    rv_op_uret = 117,
+    rv_op_sret = 118,
+    rv_op_hret = 119,
+    rv_op_mret = 120,
+    rv_op_dret = 121,
+    rv_op_sfence_vm = 122,
+    rv_op_sfence_vma = 123,
+    rv_op_wfi = 124,
+    rv_op_csrrw = 125,
+    rv_op_csrrs = 126,
+    rv_op_csrrc = 127,
+    rv_op_csrrwi = 128,
+    rv_op_csrrsi = 129,
+    rv_op_csrrci = 130,
+    rv_op_flw = 131,
+    rv_op_fsw = 132,
+    rv_op_fmadd_s = 133,
+    rv_op_fmsub_s = 134,
+    rv_op_fnmsub_s = 135,
+    rv_op_fnmadd_s = 136,
+    rv_op_fadd_s = 137,
+    rv_op_fsub_s = 138,
+    rv_op_fmul_s = 139,
+    rv_op_fdiv_s = 140,
+    rv_op_fsgnj_s = 141,
+    rv_op_fsgnjn_s = 142,
+    rv_op_fsgnjx_s = 143,
+    rv_op_fmin_s = 144,
+    rv_op_fmax_s = 145,
+    rv_op_fsqrt_s = 146,
+    rv_op_fle_s = 147,
+    rv_op_flt_s = 148,
+    rv_op_feq_s = 149,
+    rv_op_fcvt_w_s = 150,
+    rv_op_fcvt_wu_s = 151,
+    rv_op_fcvt_s_w = 152,
+    rv_op_fcvt_s_wu = 153,
+    rv_op_fmv_x_s = 154,
+    rv_op_fclass_s = 155,
+    rv_op_fmv_s_x = 156,
+    rv_op_fcvt_l_s = 157,
+    rv_op_fcvt_lu_s = 158,
+    rv_op_fcvt_s_l = 159,
+    rv_op_fcvt_s_lu = 160,
+    rv_op_fld = 161,
+    rv_op_fsd = 162,
+    rv_op_fmadd_d = 163,
+    rv_op_fmsub_d = 164,
+    rv_op_fnmsub_d = 165,
+    rv_op_fnmadd_d = 166,
+    rv_op_fadd_d = 167,
+    rv_op_fsub_d = 168,
+    rv_op_fmul_d = 169,
+    rv_op_fdiv_d = 170,
+    rv_op_fsgnj_d = 171,
+    rv_op_fsgnjn_d = 172,
+    rv_op_fsgnjx_d = 173,
+    rv_op_fmin_d = 174,
+    rv_op_fmax_d = 175,
+    rv_op_fcvt_s_d = 176,
+    rv_op_fcvt_d_s = 177,
+    rv_op_fsqrt_d = 178,
+    rv_op_fle_d = 179,
+    rv_op_flt_d = 180,
+    rv_op_feq_d = 181,
+    rv_op_fcvt_w_d = 182,
+    rv_op_fcvt_wu_d = 183,
+    rv_op_fcvt_d_w = 184,
+    rv_op_fcvt_d_wu = 185,
+    rv_op_fclass_d = 186,
+    rv_op_fcvt_l_d = 187,
+    rv_op_fcvt_lu_d = 188,
+    rv_op_fmv_x_d = 189,
+    rv_op_fcvt_d_l = 190,
+    rv_op_fcvt_d_lu = 191,
+    rv_op_fmv_d_x = 192,
+    rv_op_flq = 193,
+    rv_op_fsq = 194,
+    rv_op_fmadd_q = 195,
+    rv_op_fmsub_q = 196,
+    rv_op_fnmsub_q = 197,
+    rv_op_fnmadd_q = 198,
+    rv_op_fadd_q = 199,
+    rv_op_fsub_q = 200,
+    rv_op_fmul_q = 201,
+    rv_op_fdiv_q = 202,
+    rv_op_fsgnj_q = 203,
+    rv_op_fsgnjn_q = 204,
+    rv_op_fsgnjx_q = 205,
+    rv_op_fmin_q = 206,
+    rv_op_fmax_q = 207,
+    rv_op_fcvt_s_q = 208,
+    rv_op_fcvt_q_s = 209,
+    rv_op_fcvt_d_q = 210,
+    rv_op_fcvt_q_d = 211,
+    rv_op_fsqrt_q = 212,
+    rv_op_fle_q = 213,
+    rv_op_flt_q = 214,
+    rv_op_feq_q = 215,
+    rv_op_fcvt_w_q = 216,
+    rv_op_fcvt_wu_q = 217,
+    rv_op_fcvt_q_w = 218,
+    rv_op_fcvt_q_wu = 219,
+    rv_op_fclass_q = 220,
+    rv_op_fcvt_l_q = 221,
+    rv_op_fcvt_lu_q = 222,
+    rv_op_fcvt_q_l = 223,
+    rv_op_fcvt_q_lu = 224,
+    rv_op_fmv_x_q = 225,
+    rv_op_fmv_q_x = 226,
+    rv_op_c_addi4spn = 227,
+    rv_op_c_fld = 228,
+    rv_op_c_lw = 229,
+    rv_op_c_flw = 230,
+    rv_op_c_fsd = 231,
+    rv_op_c_sw = 232,
+    rv_op_c_fsw = 233,
+    rv_op_c_nop = 234,
+    rv_op_c_addi = 235,
+    rv_op_c_jal = 236,
+    rv_op_c_li = 237,
+    rv_op_c_addi16sp = 238,
+    rv_op_c_lui = 239,
+    rv_op_c_srli = 240,
+    rv_op_c_srai = 241,
+    rv_op_c_andi = 242,
+    rv_op_c_sub = 243,
+    rv_op_c_xor = 244,
+    rv_op_c_or = 245,
+    rv_op_c_and = 246,
+    rv_op_c_subw = 247,
+    rv_op_c_addw = 248,
+    rv_op_c_j = 249,
+    rv_op_c_beqz = 250,
+    rv_op_c_bnez = 251,
+    rv_op_c_slli = 252,
+    rv_op_c_fldsp = 253,
+    rv_op_c_lwsp = 254,
+    rv_op_c_flwsp = 255,
+    rv_op_c_jr = 256,
+    rv_op_c_mv = 257,
+    rv_op_c_ebreak = 258,
+    rv_op_c_jalr = 259,
+    rv_op_c_add = 260,
+    rv_op_c_fsdsp = 261,
+    rv_op_c_swsp = 262,
+    rv_op_c_fswsp = 263,
+    rv_op_c_ld = 264,
+    rv_op_c_sd = 265,
+    rv_op_c_addiw = 266,
+    rv_op_c_ldsp = 267,
+    rv_op_c_sdsp = 268,
+    rv_op_c_lq = 269,
+    rv_op_c_sq = 270,
+    rv_op_c_lqsp = 271,
+    rv_op_c_sqsp = 272,
+    rv_op_nop = 273,
+    rv_op_mv = 274,
+    rv_op_not = 275,
+    rv_op_neg = 276,
+    rv_op_negw = 277,
+    rv_op_sext_w = 278,
+    rv_op_seqz = 279,
+    rv_op_snez = 280,
+    rv_op_sltz = 281,
+    rv_op_sgtz = 282,
+    rv_op_fmv_s = 283,
+    rv_op_fabs_s = 284,
+    rv_op_fneg_s = 285,
+    rv_op_fmv_d = 286,
+    rv_op_fabs_d = 287,
+    rv_op_fneg_d = 288,
+    rv_op_fmv_q = 289,
+    rv_op_fabs_q = 290,
+    rv_op_fneg_q = 291,
+    rv_op_beqz = 292,
+    rv_op_bnez = 293,
+    rv_op_blez = 294,
+    rv_op_bgez = 295,
+    rv_op_bltz = 296,
+    rv_op_bgtz = 297,
+    rv_op_ble = 298,
+    rv_op_bleu = 299,
+    rv_op_bgt = 300,
+    rv_op_bgtu = 301,
+    rv_op_j = 302,
+    rv_op_ret = 303,
+    rv_op_jr = 304,
+    rv_op_rdcycle = 305,
+    rv_op_rdtime = 306,
+    rv_op_rdinstret = 307,
+    rv_op_rdcycleh = 308,
+    rv_op_rdtimeh = 309,
+    rv_op_rdinstreth = 310,
+    rv_op_frcsr = 311,
+    rv_op_frrm = 312,
+    rv_op_frflags = 313,
+    rv_op_fscsr = 314,
+    rv_op_fsrm = 315,
+    rv_op_fsflags = 316,
+    rv_op_fsrmi = 317,
+    rv_op_fsflagsi = 318,
+} rv_op;
+
+/* structures */
+
+typedef struct {
+    uint64_t  pc;
+    uint64_t  inst;
+    int32_t   imm;
+    uint16_t  op;
+    uint8_t   codec;
+    uint8_t   rd;
+    uint8_t   rs1;
+    uint8_t   rs2;
+    uint8_t   rs3;
+    uint8_t   rm;
+    uint8_t   pred;
+    uint8_t   succ;
+    uint8_t   aq;
+    uint8_t   rl;
+} rv_decode;
+
+typedef struct {
+    const int op;
+    const rvc_constraint *constraints;
+} rv_comp_data;
+
+typedef struct {
+    const char * const name;
+    const rv_codec codec;
+    const char * const format;
+    const rv_comp_data *pseudo;
+    const int decomp_rv32;
+    const int decomp_rv64;
+    const int decomp_rv128;
+} rv_opcode_data;
+
+/* register names */
+
+static const char rv_ireg_name_sym[32][5] = {
+    "zero", "ra",   "sp",   "gp",   "tp",   "t0",   "t1",   "t2",
+    "s0",   "s1",   "a0",   "a1",   "a2",   "a3",   "a4",   "a5",
+    "a6",   "a7",   "s2",   "s3",   "s4",   "s5",   "s6",   "s7",
+    "s8",   "s9",   "s10",  "s11",  "t3",   "t4",   "t5",   "t6",
+};
+
+static const char rv_freg_name_sym[32][5] = {
+    "ft0",  "ft1",  "ft2",  "ft3",  "ft4",  "ft5",  "ft6",  "ft7",
+    "fs0",  "fs1",  "fa0",  "fa1",  "fa2",  "fa3",  "fa4",  "fa5",
+    "fa6",  "fa7",  "fs2",  "fs3",  "fs4",  "fs5",  "fs6",  "fs7",
+    "fs8",  "fs9",  "fs10", "fs11", "ft8",  "ft9",  "ft10", "ft11",
+};
+
+/* instruction formats */
+
+#define rv_fmt_none                   "O\t"
+#define rv_fmt_rs1                    "O\t1"
+#define rv_fmt_offset                 "O\to"
+#define rv_fmt_pred_succ              "O\tp,s"
+#define rv_fmt_rs1_rs2                "O\t1,2"
+#define rv_fmt_rd_imm                 "O\t0,i"
+#define rv_fmt_rd_offset              "O\t0,o"
+#define rv_fmt_rd_rs1_rs2             "O\t0,1,2"
+#define rv_fmt_frd_rs1                "O\t3,1"
+#define rv_fmt_rd_frs1                "O\t0,4"
+#define rv_fmt_rd_frs1_frs2           "O\t0,4,5"
+#define rv_fmt_frd_frs1_frs2          "O\t3,4,5"
+#define rv_fmt_rm_frd_frs1            "O\tr,3,4"
+#define rv_fmt_rm_frd_rs1             "O\tr,3,1"
+#define rv_fmt_rm_rd_frs1             "O\tr,0,4"
+#define rv_fmt_rm_frd_frs1_frs2       "O\tr,3,4,5"
+#define rv_fmt_rm_frd_frs1_frs2_frs3  "O\tr,3,4,5,6"
+#define rv_fmt_rd_rs1_imm             "O\t0,1,i"
+#define rv_fmt_rd_rs1_offset          "O\t0,1,i"
+#define rv_fmt_rd_offset_rs1          "O\t0,i(1)"
+#define rv_fmt_frd_offset_rs1         "O\t3,i(1)"
+#define rv_fmt_rd_csr_rs1             "O\t0,c,1"
+#define rv_fmt_rd_csr_zimm            "O\t0,c,7"
+#define rv_fmt_rs2_offset_rs1         "O\t2,i(1)"
+#define rv_fmt_frs2_offset_rs1        "O\t5,i(1)"
+#define rv_fmt_rs1_rs2_offset         "O\t1,2,o"
+#define rv_fmt_rs2_rs1_offset         "O\t2,1,o"
+#define rv_fmt_aqrl_rd_rs2_rs1        "OAR\t0,2,(1)"
+#define rv_fmt_aqrl_rd_rs1            "OAR\t0,(1)"
+#define rv_fmt_rd                     "O\t0"
+#define rv_fmt_rd_zimm                "O\t0,7"
+#define rv_fmt_rd_rs1                 "O\t0,1"
+#define rv_fmt_rd_rs2                 "O\t0,2"
+#define rv_fmt_rs1_offset             "O\t1,o"
+#define rv_fmt_rs2_offset             "O\t2,o"
+
+/* pseudo-instruction constraints */
+
+static const rvc_constraint rvcc_jal[] = { rvc_rd_eq_ra, rvc_end };
+static const rvc_constraint rvcc_jalr[] = { rvc_rd_eq_ra, rvc_imm_eq_zero, rvc_end };
+static const rvc_constraint rvcc_nop[] = { rvc_rd_eq_x0, rvc_rs1_eq_x0, rvc_imm_eq_zero, rvc_end };
+static const rvc_constraint rvcc_mv[] = { rvc_imm_eq_zero, rvc_end };
+static const rvc_constraint rvcc_not[] = { rvc_imm_eq_n1, rvc_end };
+static const rvc_constraint rvcc_neg[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_negw[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_sext_w[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_seqz[] = { rvc_imm_eq_p1, rvc_end };
+static const rvc_constraint rvcc_snez[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_sltz[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_sgtz[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_fmv_s[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fabs_s[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fneg_s[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fmv_d[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fabs_d[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fneg_d[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fmv_q[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fabs_q[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_fneg_q[] = { rvc_rs2_eq_rs1, rvc_end };
+static const rvc_constraint rvcc_beqz[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_bnez[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_blez[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_bgez[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_bltz[] = { rvc_rs2_eq_x0, rvc_end };
+static const rvc_constraint rvcc_bgtz[] = { rvc_rs1_eq_x0, rvc_end };
+static const rvc_constraint rvcc_ble[] = { rvc_end };
+static const rvc_constraint rvcc_bleu[] = { rvc_end };
+static const rvc_constraint rvcc_bgt[] = { rvc_end };
+static const rvc_constraint rvcc_bgtu[] = { rvc_end };
+static const rvc_constraint rvcc_j[] = { rvc_rd_eq_x0, rvc_end };
+static const rvc_constraint rvcc_ret[] = { rvc_rd_eq_x0, rvc_rs1_eq_ra, rvc_end };
+static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end };
+static const rvc_constraint rvcc_rdcycle[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc00, rvc_end };
+static const rvc_constraint rvcc_rdtime[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc01, rvc_end };
+static const rvc_constraint rvcc_rdinstret[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc02, rvc_end };
+static const rvc_constraint rvcc_rdcycleh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end };
+static const rvc_constraint rvcc_rdtimeh[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc81, rvc_end };
+static const rvc_constraint rvcc_rdinstreth[] = { rvc_rs1_eq_x0, rvc_csr_eq_0xc80, rvc_end };
+static const rvc_constraint rvcc_frcsr[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x003, rvc_end };
+static const rvc_constraint rvcc_frrm[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x002, rvc_end };
+static const rvc_constraint rvcc_frflags[] = { rvc_rs1_eq_x0, rvc_csr_eq_0x001, rvc_end };
+static const rvc_constraint rvcc_fscsr[] = { rvc_csr_eq_0x003, rvc_end };
+static const rvc_constraint rvcc_fsrm[] = { rvc_csr_eq_0x002, rvc_end };
+static const rvc_constraint rvcc_fsflags[] = { rvc_csr_eq_0x001, rvc_end };
+static const rvc_constraint rvcc_fsrmi[] = { rvc_csr_eq_0x002, rvc_end };
+static const rvc_constraint rvcc_fsflagsi[] = { rvc_csr_eq_0x001, rvc_end };
+
+/* pseudo-instruction metadata */
+
+static const rv_comp_data rvcp_jal[] = {
+    { rv_op_j, rvcc_j },
+    { rv_op_jal, rvcc_jal },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_jalr[] = {
+    { rv_op_ret, rvcc_ret },
+    { rv_op_jr, rvcc_jr },
+    { rv_op_jalr, rvcc_jalr },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_beq[] = {
+    { rv_op_beqz, rvcc_beqz },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_bne[] = {
+    { rv_op_bnez, rvcc_bnez },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_blt[] = {
+    { rv_op_bltz, rvcc_bltz },
+    { rv_op_bgtz, rvcc_bgtz },
+    { rv_op_bgt, rvcc_bgt },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_bge[] = {
+    { rv_op_blez, rvcc_blez },
+    { rv_op_bgez, rvcc_bgez },
+    { rv_op_ble, rvcc_ble },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_bltu[] = {
+    { rv_op_bgtu, rvcc_bgtu },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_bgeu[] = {
+    { rv_op_bleu, rvcc_bleu },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_addi[] = {
+    { rv_op_nop, rvcc_nop },
+    { rv_op_mv, rvcc_mv },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_sltiu[] = {
+    { rv_op_seqz, rvcc_seqz },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_xori[] = {
+    { rv_op_not, rvcc_not },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_sub[] = {
+    { rv_op_neg, rvcc_neg },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_slt[] = {
+    { rv_op_sltz, rvcc_sltz },
+    { rv_op_sgtz, rvcc_sgtz },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_sltu[] = {
+    { rv_op_snez, rvcc_snez },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_addiw[] = {
+    { rv_op_sext_w, rvcc_sext_w },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_subw[] = {
+    { rv_op_negw, rvcc_negw },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_csrrw[] = {
+    { rv_op_fscsr, rvcc_fscsr },
+    { rv_op_fsrm, rvcc_fsrm },
+    { rv_op_fsflags, rvcc_fsflags },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_csrrs[] = {
+    { rv_op_rdcycle, rvcc_rdcycle },
+    { rv_op_rdtime, rvcc_rdtime },
+    { rv_op_rdinstret, rvcc_rdinstret },
+    { rv_op_rdcycleh, rvcc_rdcycleh },
+    { rv_op_rdtimeh, rvcc_rdtimeh },
+    { rv_op_rdinstreth, rvcc_rdinstreth },
+    { rv_op_frcsr, rvcc_frcsr },
+    { rv_op_frrm, rvcc_frrm },
+    { rv_op_frflags, rvcc_frflags },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_csrrwi[] = {
+    { rv_op_fsrmi, rvcc_fsrmi },
+    { rv_op_fsflagsi, rvcc_fsflagsi },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnj_s[] = {
+    { rv_op_fmv_s, rvcc_fmv_s },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjn_s[] = {
+    { rv_op_fneg_s, rvcc_fneg_s },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjx_s[] = {
+    { rv_op_fabs_s, rvcc_fabs_s },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnj_d[] = {
+    { rv_op_fmv_d, rvcc_fmv_d },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjn_d[] = {
+    { rv_op_fneg_d, rvcc_fneg_d },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjx_d[] = {
+    { rv_op_fabs_d, rvcc_fabs_d },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnj_q[] = {
+    { rv_op_fmv_q, rvcc_fmv_q },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjn_q[] = {
+    { rv_op_fneg_q, rvcc_fneg_q },
+    { rv_op_illegal, NULL }
+};
+
+static const rv_comp_data rvcp_fsgnjx_q[] = {
+    { rv_op_fabs_q, rvcc_fabs_q },
+    { rv_op_illegal, NULL }
+};
+
+/* instruction metadata */
+
+const rv_opcode_data opcode_data[] = {
+    { "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 },
+    { "lui", rv_codec_u, rv_fmt_rd_imm, NULL, 0, 0, 0 },
+    { "auipc", rv_codec_u, rv_fmt_rd_offset, NULL, 0, 0, 0 },
+    { "jal", rv_codec_uj, rv_fmt_rd_offset, rvcp_jal, 0, 0, 0 },
+    { "jalr", rv_codec_i, rv_fmt_rd_rs1_offset, rvcp_jalr, 0, 0, 0 },
+    { "beq", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_beq, 0, 0, 0 },
+    { "bne", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bne, 0, 0, 0 },
+    { "blt", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_blt, 0, 0, 0 },
+    { "bge", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bge, 0, 0, 0 },
+    { "bltu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bltu, 0, 0, 0 },
+    { "bgeu", rv_codec_sb, rv_fmt_rs1_rs2_offset, rvcp_bgeu, 0, 0, 0 },
+    { "lb", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lh", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lw", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lbu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lhu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "sb", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "sh", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "sw", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "addi", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addi, 0, 0, 0 },
+    { "slti", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "sltiu", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_sltiu, 0, 0, 0 },
+    { "xori", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_xori, 0, 0, 0 },
+    { "ori", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "andi", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "slli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "srli", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "srai", rv_codec_i_sh7, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "add", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "sub", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sub, 0, 0, 0 },
+    { "sll", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "slt", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_slt, 0, 0, 0 },
+    { "sltu", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_sltu, 0, 0, 0 },
+    { "xor", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "srl", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "sra", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "or", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "and", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "fence", rv_codec_r_f, rv_fmt_pred_succ, NULL, 0, 0, 0 },
+    { "fence.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "lwu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "ld", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "sd", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "addiw", rv_codec_i, rv_fmt_rd_rs1_imm, rvcp_addiw, 0, 0, 0 },
+    { "slliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "srliw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "sraiw", rv_codec_i_sh5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "addw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "subw", rv_codec_r, rv_fmt_rd_rs1_rs2, rvcp_subw, 0, 0, 0 },
+    { "sllw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "srlw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "sraw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "ldu", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "lq", rv_codec_i, rv_fmt_rd_offset_rs1, NULL, 0, 0, 0 },
+    { "sq", rv_codec_s, rv_fmt_rs2_offset_rs1, NULL, 0, 0, 0 },
+    { "addid", rv_codec_i, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "sllid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "srlid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "sraid", rv_codec_i_sh6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
+    { "addd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "subd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "slld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "srld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "srad", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mul", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mulh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mulhsu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mulhu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "div", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "rem", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remu", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "mulw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remuw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "muld", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "divud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remd", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "remud", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
+    { "lr.w", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 },
+    { "sc.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoswap.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoadd.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoxor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoor.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoand.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomin.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomax.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amominu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomaxu.w", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "lr.d", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 },
+    { "sc.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoswap.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoadd.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoxor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoor.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoand.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomin.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomax.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amominu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomaxu.d", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "lr.q", rv_codec_r_l, rv_fmt_aqrl_rd_rs1, NULL, 0, 0, 0 },
+    { "sc.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoswap.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoadd.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoxor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoor.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amoand.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomin.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomax.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amominu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "amomaxu.q", rv_codec_r_a, rv_fmt_aqrl_rd_rs2_rs1, NULL, 0, 0, 0 },
+    { "ecall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "ebreak", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "uret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "sret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "hret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "mret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "dret", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "sfence.vm", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
+    { "sfence.vma", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 },
+    { "wfi", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
+    { "csrrw", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrw, 0, 0, 0 },
+    { "csrrs", rv_codec_i_csr, rv_fmt_rd_csr_rs1, rvcp_csrrs, 0, 0, 0 },
+    { "csrrc", rv_codec_i_csr, rv_fmt_rd_csr_rs1, NULL, 0, 0, 0 },
+    { "csrrwi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, rvcp_csrrwi, 0, 0, 0 },
+    { "csrrsi", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 },
+    { "csrrci", rv_codec_i_csr, rv_fmt_rd_csr_zimm, NULL, 0, 0, 0 },
+    { "flw", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 },
+    { "fsw", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 },
+    { "fmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmsub.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmadd.s", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fadd.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsub.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmul.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fdiv.s", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsgnj.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_s, 0, 0, 0 },
+    { "fsgnjn.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_s, 0, 0, 0 },
+    { "fsgnjx.s", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_s, 0, 0, 0 },
+    { "fmin.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmax.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsqrt.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fle.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "flt.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "feq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.w.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.wu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.s.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.s.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fmv.x.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fclass.s", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fmv.s.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.l.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.lu.s", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.s.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.s.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fld", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 },
+    { "fsd", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 },
+    { "fmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmsub.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmadd.d", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fadd.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsub.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmul.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fdiv.d", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsgnj.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_d, 0, 0, 0 },
+    { "fsgnjn.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_d, 0, 0, 0 },
+    { "fsgnjx.d", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_d, 0, 0, 0 },
+    { "fmin.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmax.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.s.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.d.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fsqrt.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fle.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "flt.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "feq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.wu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.d.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.d.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fclass.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.l.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.lu.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fmv.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.d.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.d.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fmv.d.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
+    { "flq", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 },
+    { "fsq", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 },
+    { "fmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmsub.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fnmadd.q", rv_codec_r4_m, rv_fmt_rm_frd_frs1_frs2_frs3, NULL, 0, 0, 0 },
+    { "fadd.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsub.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmul.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fdiv.q", rv_codec_r_m, rv_fmt_rm_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fsgnj.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnj_q, 0, 0, 0 },
+    { "fsgnjn.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjn_q, 0, 0, 0 },
+    { "fsgnjx.q", rv_codec_r, rv_fmt_frd_frs1_frs2, rvcp_fsgnjx_q, 0, 0, 0 },
+    { "fmin.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fmax.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.s.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.q.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.d.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.q.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fsqrt.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
+    { "fle.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "flt.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "feq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
+    { "fcvt.w.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.wu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.q.w", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.q.wu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fclass.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.l.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.lu.q", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
+    { "fcvt.q.l", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fcvt.q.lu", rv_codec_r_m, rv_fmt_rm_frd_rs1, NULL, 0, 0, 0 },
+    { "fmv.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
+    { "fmv.q.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
+    { "c.addi4spn", rv_codec_ciw_4spn, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.fld", rv_codec_cl_ld, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, 0 },
+    { "c.lw", rv_codec_cl_lw, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw },
+    { "c.flw", rv_codec_cl_lw, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 },
+    { "c.fsd", rv_codec_cs_sd, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, 0 },
+    { "c.sw", rv_codec_cs_sw, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw },
+    { "c.fsw", rv_codec_cs_sw, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 },
+    { "c.nop", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.addi", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.jal", rv_codec_cj_jal, rv_fmt_rd_offset, NULL, rv_op_jal, 0, 0 },
+    { "c.li", rv_codec_ci_li, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.addi16sp", rv_codec_ci_16sp, rv_fmt_rd_rs1_imm, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.lui", rv_codec_ci_lui, rv_fmt_rd_imm, NULL, rv_op_lui, rv_op_lui, rv_op_lui },
+    { "c.srli", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srli, rv_op_srli, rv_op_srli },
+    { "c.srai", rv_codec_cb_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_srai, rv_op_srai, rv_op_srai },
+    { "c.andi", rv_codec_cb_imm, rv_fmt_rd_rs1_imm, NULL, rv_op_andi, rv_op_andi, rv_op_andi },
+    { "c.sub", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_sub, rv_op_sub, rv_op_sub },
+    { "c.xor", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_xor, rv_op_xor, rv_op_xor },
+    { "c.or", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_or, rv_op_or, rv_op_or },
+    { "c.and", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_and, rv_op_and, rv_op_and },
+    { "c.subw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_subw, rv_op_subw, rv_op_subw },
+    { "c.addw", rv_codec_cs, rv_fmt_rd_rs1_rs2, NULL, rv_op_addw, rv_op_addw, rv_op_addw },
+    { "c.j", rv_codec_cj, rv_fmt_rd_offset, NULL, rv_op_jal, rv_op_jal, rv_op_jal },
+    { "c.beqz", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_beq, rv_op_beq, rv_op_beq },
+    { "c.bnez", rv_codec_cb, rv_fmt_rs1_rs2_offset, NULL, rv_op_bne, rv_op_bne, rv_op_bne },
+    { "c.slli", rv_codec_ci_sh6, rv_fmt_rd_rs1_imm, NULL, rv_op_slli, rv_op_slli, rv_op_slli },
+    { "c.fldsp", rv_codec_ci_ldsp, rv_fmt_frd_offset_rs1, NULL, rv_op_fld, rv_op_fld, rv_op_fld },
+    { "c.lwsp", rv_codec_ci_lwsp, rv_fmt_rd_offset_rs1, NULL, rv_op_lw, rv_op_lw, rv_op_lw },
+    { "c.flwsp", rv_codec_ci_lwsp, rv_fmt_frd_offset_rs1, NULL, rv_op_flw, 0, 0 },
+    { "c.jr", rv_codec_cr_jr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr },
+    { "c.mv", rv_codec_cr_mv, rv_fmt_rd_rs1_rs2, NULL, rv_op_addi, rv_op_addi, rv_op_addi },
+    { "c.ebreak", rv_codec_ci_none, rv_fmt_none, NULL, rv_op_ebreak, rv_op_ebreak, rv_op_ebreak },
+    { "c.jalr", rv_codec_cr_jalr, rv_fmt_rd_rs1_offset, NULL, rv_op_jalr, rv_op_jalr, rv_op_jalr },
+    { "c.add", rv_codec_cr, rv_fmt_rd_rs1_rs2, NULL, rv_op_add, rv_op_add, rv_op_add },
+    { "c.fsdsp", rv_codec_css_sdsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsd, rv_op_fsd, rv_op_fsd },
+    { "c.swsp", rv_codec_css_swsp, rv_fmt_rs2_offset_rs1, NULL, rv_op_sw, rv_op_sw, rv_op_sw },
+    { "c.fswsp", rv_codec_css_swsp, rv_fmt_frs2_offset_rs1, NULL, rv_op_fsw, 0, 0 },
+    { "c.ld", rv_codec_cl_ld, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld },
+    { "c.sd", rv_codec_cs_sd, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd },
+    { "c.addiw", rv_codec_ci, rv_fmt_rd_rs1_imm, NULL, 0, rv_op_addiw, rv_op_addiw },
+    { "c.ldsp", rv_codec_ci_ldsp, rv_fmt_rd_offset_rs1, NULL, 0, rv_op_ld, rv_op_ld },
+    { "c.sdsp", rv_codec_css_sdsp, rv_fmt_rs2_offset_rs1, NULL, 0, rv_op_sd, rv_op_sd },
+    { "c.lq", rv_codec_cl_lq, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq },
+    { "c.sq", rv_codec_cs_sq, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq },
+    { "c.lqsp", rv_codec_ci_lqsp, rv_fmt_rd_offset_rs1, NULL, 0, 0, rv_op_lq },
+    { "c.sqsp", rv_codec_css_sqsp, rv_fmt_rs2_offset_rs1, NULL, 0, 0, rv_op_sq },
+    { "nop", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 },
+    { "mv", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "not", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "neg", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 },
+    { "negw", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 },
+    { "sext.w", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "seqz", rv_codec_i, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "snez", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 },
+    { "sltz", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "sgtz", rv_codec_r, rv_fmt_rd_rs2, NULL, 0, 0, 0 },
+    { "fmv.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fabs.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fneg.s", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fmv.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fabs.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fneg.d", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fmv.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fabs.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fneg.q", rv_codec_r, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "beqz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 },
+    { "bnez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 },
+    { "blez", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 },
+    { "bgez", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 },
+    { "bltz", rv_codec_sb, rv_fmt_rs1_offset, NULL, 0, 0, 0 },
+    { "bgtz", rv_codec_sb, rv_fmt_rs2_offset, NULL, 0, 0, 0 },
+    { "ble", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 },
+    { "bleu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 },
+    { "bgt", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 },
+    { "bgtu", rv_codec_sb, rv_fmt_rs2_rs1_offset, NULL, 0, 0, 0 },
+    { "j", rv_codec_uj, rv_fmt_offset, NULL, 0, 0, 0 },
+    { "ret", rv_codec_i, rv_fmt_none, NULL, 0, 0, 0 },
+    { "jr", rv_codec_i, rv_fmt_rs1, NULL, 0, 0, 0 },
+    { "rdcycle", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdtime", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdinstret", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdcycleh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdtimeh", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "rdinstreth", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "frcsr", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "frrm", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "frflags", rv_codec_i_csr, rv_fmt_rd, NULL, 0, 0, 0 },
+    { "fscsr", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fsrm", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fsflags", rv_codec_i_csr, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
+    { "fsrmi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 },
+    { "fsflagsi", rv_codec_i_csr, rv_fmt_rd_zimm, NULL, 0, 0, 0 },
+};
+
+/* CSR names */
+
+static const char *csr_name(int csrno)
+{
+    switch (csrno) {
+    case 0x0000: return "ustatus";
+    case 0x0001: return "fflags";
+    case 0x0002: return "frm";
+    case 0x0003: return "fcsr";
+    case 0x0004: return "uie";
+    case 0x0005: return "utvec";
+    case 0x0040: return "uscratch";
+    case 0x0041: return "uepc";
+    case 0x0042: return "ucause";
+    case 0x0043: return "utval";
+    case 0x0044: return "uip";
+    case 0x0100: return "sstatus";
+    case 0x0102: return "sedeleg";
+    case 0x0103: return "sideleg";
+    case 0x0104: return "sie";
+    case 0x0105: return "stvec";
+    case 0x0106: return "scounteren";
+    case 0x0140: return "sscratch";
+    case 0x0141: return "sepc";
+    case 0x0142: return "scause";
+    case 0x0143: return "stval";
+    case 0x0144: return "sip";
+    case 0x0180: return "satp";
+    case 0x0200: return "hstatus";
+    case 0x0202: return "hedeleg";
+    case 0x0203: return "hideleg";
+    case 0x0204: return "hie";
+    case 0x0205: return "htvec";
+    case 0x0240: return "hscratch";
+    case 0x0241: return "hepc";
+    case 0x0242: return "hcause";
+    case 0x0243: return "hbadaddr";
+    case 0x0244: return "hip";
+    case 0x0300: return "mstatus";
+    case 0x0301: return "misa";
+    case 0x0302: return "medeleg";
+    case 0x0303: return "mideleg";
+    case 0x0304: return "mie";
+    case 0x0305: return "mtvec";
+    case 0x0306: return "mcounteren";
+    case 0x0320: return "mucounteren";
+    case 0x0321: return "mscounteren";
+    case 0x0322: return "mhcounteren";
+    case 0x0323: return "mhpmevent3";
+    case 0x0324: return "mhpmevent4";
+    case 0x0325: return "mhpmevent5";
+    case 0x0326: return "mhpmevent6";
+    case 0x0327: return "mhpmevent7";
+    case 0x0328: return "mhpmevent8";
+    case 0x0329: return "mhpmevent9";
+    case 0x032a: return "mhpmevent10";
+    case 0x032b: return "mhpmevent11";
+    case 0x032c: return "mhpmevent12";
+    case 0x032d: return "mhpmevent13";
+    case 0x032e: return "mhpmevent14";
+    case 0x032f: return "mhpmevent15";
+    case 0x0330: return "mhpmevent16";
+    case 0x0331: return "mhpmevent17";
+    case 0x0332: return "mhpmevent18";
+    case 0x0333: return "mhpmevent19";
+    case 0x0334: return "mhpmevent20";
+    case 0x0335: return "mhpmevent21";
+    case 0x0336: return "mhpmevent22";
+    case 0x0337: return "mhpmevent23";
+    case 0x0338: return "mhpmevent24";
+    case 0x0339: return "mhpmevent25";
+    case 0x033a: return "mhpmevent26";
+    case 0x033b: return "mhpmevent27";
+    case 0x033c: return "mhpmevent28";
+    case 0x033d: return "mhpmevent29";
+    case 0x033e: return "mhpmevent30";
+    case 0x033f: return "mhpmevent31";
+    case 0x0340: return "mscratch";
+    case 0x0341: return "mepc";
+    case 0x0342: return "mcause";
+    case 0x0343: return "mtval";
+    case 0x0344: return "mip";
+    case 0x0380: return "mbase";
+    case 0x0381: return "mbound";
+    case 0x0382: return "mibase";
+    case 0x0383: return "mibound";
+    case 0x0384: return "mdbase";
+    case 0x0385: return "mdbound";
+    case 0x03a0: return "pmpcfg3";
+    case 0x03b0: return "pmpaddr0";
+    case 0x03b1: return "pmpaddr1";
+    case 0x03b2: return "pmpaddr2";
+    case 0x03b3: return "pmpaddr3";
+    case 0x03b4: return "pmpaddr4";
+    case 0x03b5: return "pmpaddr5";
+    case 0x03b6: return "pmpaddr6";
+    case 0x03b7: return "pmpaddr7";
+    case 0x03b8: return "pmpaddr8";
+    case 0x03b9: return "pmpaddr9";
+    case 0x03ba: return "pmpaddr10";
+    case 0x03bb: return "pmpaddr11";
+    case 0x03bc: return "pmpaddr12";
+    case 0x03bd: return "pmpaddr14";
+    case 0x03be: return "pmpaddr13";
+    case 0x03bf: return "pmpaddr15";
+    case 0x0780: return "mtohost";
+    case 0x0781: return "mfromhost";
+    case 0x0782: return "mreset";
+    case 0x0783: return "mipi";
+    case 0x0784: return "miobase";
+    case 0x07a0: return "tselect";
+    case 0x07a1: return "tdata1";
+    case 0x07a2: return "tdata2";
+    case 0x07a3: return "tdata3";
+    case 0x07b0: return "dcsr";
+    case 0x07b1: return "dpc";
+    case 0x07b2: return "dscratch";
+    case 0x0b00: return "mcycle";
+    case 0x0b01: return "mtime";
+    case 0x0b02: return "minstret";
+    case 0x0b03: return "mhpmcounter3";
+    case 0x0b04: return "mhpmcounter4";
+    case 0x0b05: return "mhpmcounter5";
+    case 0x0b06: return "mhpmcounter6";
+    case 0x0b07: return "mhpmcounter7";
+    case 0x0b08: return "mhpmcounter8";
+    case 0x0b09: return "mhpmcounter9";
+    case 0x0b0a: return "mhpmcounter10";
+    case 0x0b0b: return "mhpmcounter11";
+    case 0x0b0c: return "mhpmcounter12";
+    case 0x0b0d: return "mhpmcounter13";
+    case 0x0b0e: return "mhpmcounter14";
+    case 0x0b0f: return "mhpmcounter15";
+    case 0x0b10: return "mhpmcounter16";
+    case 0x0b11: return "mhpmcounter17";
+    case 0x0b12: return "mhpmcounter18";
+    case 0x0b13: return "mhpmcounter19";
+    case 0x0b14: return "mhpmcounter20";
+    case 0x0b15: return "mhpmcounter21";
+    case 0x0b16: return "mhpmcounter22";
+    case 0x0b17: return "mhpmcounter23";
+    case 0x0b18: return "mhpmcounter24";
+    case 0x0b19: return "mhpmcounter25";
+    case 0x0b1a: return "mhpmcounter26";
+    case 0x0b1b: return "mhpmcounter27";
+    case 0x0b1c: return "mhpmcounter28";
+    case 0x0b1d: return "mhpmcounter29";
+    case 0x0b1e: return "mhpmcounter30";
+    case 0x0b1f: return "mhpmcounter31";
+    case 0x0b80: return "mcycleh";
+    case 0x0b81: return "mtimeh";
+    case 0x0b82: return "minstreth";
+    case 0x0b83: return "mhpmcounter3h";
+    case 0x0b84: return "mhpmcounter4h";
+    case 0x0b85: return "mhpmcounter5h";
+    case 0x0b86: return "mhpmcounter6h";
+    case 0x0b87: return "mhpmcounter7h";
+    case 0x0b88: return "mhpmcounter8h";
+    case 0x0b89: return "mhpmcounter9h";
+    case 0x0b8a: return "mhpmcounter10h";
+    case 0x0b8b: return "mhpmcounter11h";
+    case 0x0b8c: return "mhpmcounter12h";
+    case 0x0b8d: return "mhpmcounter13h";
+    case 0x0b8e: return "mhpmcounter14h";
+    case 0x0b8f: return "mhpmcounter15h";
+    case 0x0b90: return "mhpmcounter16h";
+    case 0x0b91: return "mhpmcounter17h";
+    case 0x0b92: return "mhpmcounter18h";
+    case 0x0b93: return "mhpmcounter19h";
+    case 0x0b94: return "mhpmcounter20h";
+    case 0x0b95: return "mhpmcounter21h";
+    case 0x0b96: return "mhpmcounter22h";
+    case 0x0b97: return "mhpmcounter23h";
+    case 0x0b98: return "mhpmcounter24h";
+    case 0x0b99: return "mhpmcounter25h";
+    case 0x0b9a: return "mhpmcounter26h";
+    case 0x0b9b: return "mhpmcounter27h";
+    case 0x0b9c: return "mhpmcounter28h";
+    case 0x0b9d: return "mhpmcounter29h";
+    case 0x0b9e: return "mhpmcounter30h";
+    case 0x0b9f: return "mhpmcounter31h";
+    case 0x0c00: return "cycle";
+    case 0x0c01: return "time";
+    case 0x0c02: return "instret";
+    case 0x0c80: return "cycleh";
+    case 0x0c81: return "timeh";
+    case 0x0c82: return "instreth";
+    case 0x0d00: return "scycle";
+    case 0x0d01: return "stime";
+    case 0x0d02: return "sinstret";
+    case 0x0d80: return "scycleh";
+    case 0x0d81: return "stimeh";
+    case 0x0d82: return "sinstreth";
+    case 0x0e00: return "hcycle";
+    case 0x0e01: return "htime";
+    case 0x0e02: return "hinstret";
+    case 0x0e80: return "hcycleh";
+    case 0x0e81: return "htimeh";
+    case 0x0e82: return "hinstreth";
+    case 0x0f11: return "mvendorid";
+    case 0x0f12: return "marchid";
+    case 0x0f13: return "mimpid";
+    case 0x0f14: return "mhartid";
+    default: return NULL;
+    }
+}
+
+/* decode opcode */
+
+static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
+{
+    rv_inst inst = dec->inst;
+    rv_opcode op = rv_op_illegal;
+    switch (((inst >> 0) & 0b11)) {
+    case 0:
+        switch (((inst >> 13) & 0b111)) {
+        case 0: op = rv_op_c_addi4spn; break;
+        case 1:
+            if (isa == rv128) {
+                op = rv_op_c_lq;
+            } else {
+                op = rv_op_c_fld;
+            }
+            break;
+        case 2: op = rv_op_c_lw; break;
+        case 3:
+            if (isa == rv32) {
+                op = rv_op_c_flw;
+            } else {
+                op = rv_op_c_ld;
+            }
+            break;
+        case 5:
+            if (isa == rv128) {
+                op = rv_op_c_sq;
+            } else {
+                op = rv_op_c_fsd;
+            }
+            break;
+        case 6: op = rv_op_c_sw; break;
+        case 7:
+            if (isa == rv32) {
+                op = rv_op_c_fsw;
+            } else {
+                op = rv_op_c_sd;
+            }
+            break;
+        }
+        break;
+    case 1:
+        switch (((inst >> 13) & 0b111)) {
+        case 0:
+            switch (((inst >> 2) & 0b11111111111)) {
+            case 0: op = rv_op_c_nop; break;
+            default: op = rv_op_c_addi; break;
+            }
+            break;
+        case 1:
+            if (isa == rv32) {
+                op = rv_op_c_jal;
+            } else {
+                op = rv_op_c_addiw;
+            }
+            break;
+        case 2: op = rv_op_c_li; break;
+        case 3:
+            switch (((inst >> 7) & 0b11111)) {
+            case 2: op = rv_op_c_addi16sp; break;
+            default: op = rv_op_c_lui; break;
+            }
+            break;
+        case 4:
+            switch (((inst >> 10) & 0b11)) {
+            case 0:
+                op = rv_op_c_srli;
+                break;
+            case 1:
+                op = rv_op_c_srai;
+                break;
+            case 2: op = rv_op_c_andi; break;
+            case 3:
+                switch (((inst >> 10) & 0b100) | ((inst >> 5) & 0b011)) {
+                case 0: op = rv_op_c_sub; break;
+                case 1: op = rv_op_c_xor; break;
+                case 2: op = rv_op_c_or; break;
+                case 3: op = rv_op_c_and; break;
+                case 4: op = rv_op_c_subw; break;
+                case 5: op = rv_op_c_addw; break;
+                }
+                break;
+            }
+            break;
+        case 5: op = rv_op_c_j; break;
+        case 6: op = rv_op_c_beqz; break;
+        case 7: op = rv_op_c_bnez; break;
+        }
+        break;
+    case 2:
+        switch (((inst >> 13) & 0b111)) {
+        case 0:
+            op = rv_op_c_slli;
+            break;
+        case 1:
+            if (isa == rv128) {
+                op = rv_op_c_lqsp;
+            } else {
+                op = rv_op_c_fldsp;
+            }
+            break;
+        case 2: op = rv_op_c_lwsp; break;
+        case 3:
+            if (isa == rv32) {
+                op = rv_op_c_flwsp;
+            } else {
+                op = rv_op_c_ldsp;
+            }
+            break;
+        case 4:
+            switch (((inst >> 12) & 0b1)) {
+            case 0:
+                switch (((inst >> 2) & 0b11111)) {
+                case 0: op = rv_op_c_jr; break;
+                default: op = rv_op_c_mv; break;
+                }
+                break;
+            case 1:
+                switch (((inst >> 2) & 0b11111)) {
+                case 0:
+                    switch (((inst >> 7) & 0b11111)) {
+                    case 0: op = rv_op_c_ebreak; break;
+                    default: op = rv_op_c_jalr; break;
+                    }
+                    break;
+                default: op = rv_op_c_add; break;
+                }
+                break;
+            }
+            break;
+        case 5:
+            if (isa == rv128) {
+                op = rv_op_c_sqsp;
+            } else {
+                op = rv_op_c_fsdsp; break;
+            }
+        case 6: op = rv_op_c_swsp; break;
+        case 7:
+            if (isa == rv32) {
+                op = rv_op_c_fswsp;
+            } else {
+                op = rv_op_c_sdsp;
+            }
+            break;
+        }
+        break;
+    case 3:
+        switch (((inst >> 2) & 0b11111)) {
+        case 0:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_lb; break;
+            case 1: op = rv_op_lh; break;
+            case 2: op = rv_op_lw; break;
+            case 3: op = rv_op_ld; break;
+            case 4: op = rv_op_lbu; break;
+            case 5: op = rv_op_lhu; break;
+            case 6: op = rv_op_lwu; break;
+            case 7: op = rv_op_ldu; break;
+            }
+            break;
+        case 1:
+            switch (((inst >> 12) & 0b111)) {
+            case 2: op = rv_op_flw; break;
+            case 3: op = rv_op_fld; break;
+            case 4: op = rv_op_flq; break;
+            }
+            break;
+        case 3:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_fence; break;
+            case 1: op = rv_op_fence_i; break;
+            case 2: op = rv_op_lq; break;
+            }
+            break;
+        case 4:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_addi; break;
+            case 1:
+                switch (((inst >> 27) & 0b11111)) {
+                case 0: op = rv_op_slli; break;
+                }
+                break;
+            case 2: op = rv_op_slti; break;
+            case 3: op = rv_op_sltiu; break;
+            case 4: op = rv_op_xori; break;
+            case 5:
+                switch (((inst >> 27) & 0b11111)) {
+                case 0: op = rv_op_srli; break;
+                case 8: op = rv_op_srai; break;
+                }
+                break;
+            case 6: op = rv_op_ori; break;
+            case 7: op = rv_op_andi; break;
+            }
+            break;
+        case 5: op = rv_op_auipc; break;
+        case 6:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_addiw; break;
+            case 1:
+                switch (((inst >> 25) & 0b1111111)) {
+                case 0: op = rv_op_slliw; break;
+                }
+                break;
+            case 5:
+                switch (((inst >> 25) & 0b1111111)) {
+                case 0: op = rv_op_srliw; break;
+                case 32: op = rv_op_sraiw; break;
+                }
+                break;
+            }
+            break;
+        case 8:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_sb; break;
+            case 1: op = rv_op_sh; break;
+            case 2: op = rv_op_sw; break;
+            case 3: op = rv_op_sd; break;
+            case 4: op = rv_op_sq; break;
+            }
+            break;
+        case 9:
+            switch (((inst >> 12) & 0b111)) {
+            case 2: op = rv_op_fsw; break;
+            case 3: op = rv_op_fsd; break;
+            case 4: op = rv_op_fsq; break;
+            }
+            break;
+        case 11:
+            switch (((inst >> 24) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+            case 2: op = rv_op_amoadd_w; break;
+            case 3: op = rv_op_amoadd_d; break;
+            case 4: op = rv_op_amoadd_q; break;
+            case 10: op = rv_op_amoswap_w; break;
+            case 11: op = rv_op_amoswap_d; break;
+            case 12: op = rv_op_amoswap_q; break;
+            case 18:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_lr_w; break;
+                }
+                break;
+            case 19:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_lr_d; break;
+                }
+                break;
+            case 20:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_lr_q; break;
+                }
+                break;
+            case 26: op = rv_op_sc_w; break;
+            case 27: op = rv_op_sc_d; break;
+            case 28: op = rv_op_sc_q; break;
+            case 34: op = rv_op_amoxor_w; break;
+            case 35: op = rv_op_amoxor_d; break;
+            case 36: op = rv_op_amoxor_q; break;
+            case 66: op = rv_op_amoor_w; break;
+            case 67: op = rv_op_amoor_d; break;
+            case 68: op = rv_op_amoor_q; break;
+            case 98: op = rv_op_amoand_w; break;
+            case 99: op = rv_op_amoand_d; break;
+            case 100: op = rv_op_amoand_q; break;
+            case 130: op = rv_op_amomin_w; break;
+            case 131: op = rv_op_amomin_d; break;
+            case 132: op = rv_op_amomin_q; break;
+            case 162: op = rv_op_amomax_w; break;
+            case 163: op = rv_op_amomax_d; break;
+            case 164: op = rv_op_amomax_q; break;
+            case 194: op = rv_op_amominu_w; break;
+            case 195: op = rv_op_amominu_d; break;
+            case 196: op = rv_op_amominu_q; break;
+            case 226: op = rv_op_amomaxu_w; break;
+            case 227: op = rv_op_amomaxu_d; break;
+            case 228: op = rv_op_amomaxu_q; break;
+            }
+            break;
+        case 12:
+            switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {
+            case 0: op = rv_op_add; break;
+            case 1: op = rv_op_sll; break;
+            case 2: op = rv_op_slt; break;
+            case 3: op = rv_op_sltu; break;
+            case 4: op = rv_op_xor; break;
+            case 5: op = rv_op_srl; break;
+            case 6: op = rv_op_or; break;
+            case 7: op = rv_op_and; break;
+            case 8: op = rv_op_mul; break;
+            case 9: op = rv_op_mulh; break;
+            case 10: op = rv_op_mulhsu; break;
+            case 11: op = rv_op_mulhu; break;
+            case 12: op = rv_op_div; break;
+            case 13: op = rv_op_divu; break;
+            case 14: op = rv_op_rem; break;
+            case 15: op = rv_op_remu; break;
+            case 256: op = rv_op_sub; break;
+            case 261: op = rv_op_sra; break;
+            }
+            break;
+        case 13: op = rv_op_lui; break;
+        case 14:
+            switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {
+            case 0: op = rv_op_addw; break;
+            case 1: op = rv_op_sllw; break;
+            case 5: op = rv_op_srlw; break;
+            case 8: op = rv_op_mulw; break;
+            case 12: op = rv_op_divw; break;
+            case 13: op = rv_op_divuw; break;
+            case 14: op = rv_op_remw; break;
+            case 15: op = rv_op_remuw; break;
+            case 256: op = rv_op_subw; break;
+            case 261: op = rv_op_sraw; break;
+            }
+            break;
+        case 16:
+            switch (((inst >> 25) & 0b11)) {
+            case 0: op = rv_op_fmadd_s; break;
+            case 1: op = rv_op_fmadd_d; break;
+            case 3: op = rv_op_fmadd_q; break;
+            }
+            break;
+        case 17:
+            switch (((inst >> 25) & 0b11)) {
+            case 0: op = rv_op_fmsub_s; break;
+            case 1: op = rv_op_fmsub_d; break;
+            case 3: op = rv_op_fmsub_q; break;
+            }
+            break;
+        case 18:
+            switch (((inst >> 25) & 0b11)) {
+            case 0: op = rv_op_fnmsub_s; break;
+            case 1: op = rv_op_fnmsub_d; break;
+            case 3: op = rv_op_fnmsub_q; break;
+            }
+            break;
+        case 19:
+            switch (((inst >> 25) & 0b11)) {
+            case 0: op = rv_op_fnmadd_s; break;
+            case 1: op = rv_op_fnmadd_d; break;
+            case 3: op = rv_op_fnmadd_q; break;
+            }
+            break;
+        case 20:
+            switch (((inst >> 25) & 0b1111111)) {
+            case 0: op = rv_op_fadd_s; break;
+            case 1: op = rv_op_fadd_d; break;
+            case 3: op = rv_op_fadd_q; break;
+            case 4: op = rv_op_fsub_s; break;
+            case 5: op = rv_op_fsub_d; break;
+            case 7: op = rv_op_fsub_q; break;
+            case 8: op = rv_op_fmul_s; break;
+            case 9: op = rv_op_fmul_d; break;
+            case 11: op = rv_op_fmul_q; break;
+            case 12: op = rv_op_fdiv_s; break;
+            case 13: op = rv_op_fdiv_d; break;
+            case 15: op = rv_op_fdiv_q; break;
+            case 16:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fsgnj_s; break;
+                case 1: op = rv_op_fsgnjn_s; break;
+                case 2: op = rv_op_fsgnjx_s; break;
+                }
+                break;
+            case 17:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fsgnj_d; break;
+                case 1: op = rv_op_fsgnjn_d; break;
+                case 2: op = rv_op_fsgnjx_d; break;
+                }
+                break;
+            case 19:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fsgnj_q; break;
+                case 1: op = rv_op_fsgnjn_q; break;
+                case 2: op = rv_op_fsgnjx_q; break;
+                }
+                break;
+            case 20:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fmin_s; break;
+                case 1: op = rv_op_fmax_s; break;
+                }
+                break;
+            case 21:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fmin_d; break;
+                case 1: op = rv_op_fmax_d; break;
+                }
+                break;
+            case 23:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fmin_q; break;
+                case 1: op = rv_op_fmax_q; break;
+                }
+                break;
+            case 32:
+                switch (((inst >> 20) & 0b11111)) {
+                case 1: op = rv_op_fcvt_s_d; break;
+                case 3: op = rv_op_fcvt_s_q; break;
+                }
+                break;
+            case 33:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_d_s; break;
+                case 3: op = rv_op_fcvt_d_q; break;
+                }
+                break;
+            case 35:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_q_s; break;
+                case 1: op = rv_op_fcvt_q_d; break;
+                }
+                break;
+            case 44:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fsqrt_s; break;
+                }
+                break;
+            case 45:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fsqrt_d; break;
+                }
+                break;
+            case 47:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fsqrt_q; break;
+                }
+                break;
+            case 80:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fle_s; break;
+                case 1: op = rv_op_flt_s; break;
+                case 2: op = rv_op_feq_s; break;
+                }
+                break;
+            case 81:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fle_d; break;
+                case 1: op = rv_op_flt_d; break;
+                case 2: op = rv_op_feq_d; break;
+                }
+                break;
+            case 83:
+                switch (((inst >> 12) & 0b111)) {
+                case 0: op = rv_op_fle_q; break;
+                case 1: op = rv_op_flt_q; break;
+                case 2: op = rv_op_feq_q; break;
+                }
+                break;
+            case 96:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_w_s; break;
+                case 1: op = rv_op_fcvt_wu_s; break;
+                case 2: op = rv_op_fcvt_l_s; break;
+                case 3: op = rv_op_fcvt_lu_s; break;
+                }
+                break;
+            case 97:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_w_d; break;
+                case 1: op = rv_op_fcvt_wu_d; break;
+                case 2: op = rv_op_fcvt_l_d; break;
+                case 3: op = rv_op_fcvt_lu_d; break;
+                }
+                break;
+            case 99:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_w_q; break;
+                case 1: op = rv_op_fcvt_wu_q; break;
+                case 2: op = rv_op_fcvt_l_q; break;
+                case 3: op = rv_op_fcvt_lu_q; break;
+                }
+                break;
+            case 104:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_s_w; break;
+                case 1: op = rv_op_fcvt_s_wu; break;
+                case 2: op = rv_op_fcvt_s_l; break;
+                case 3: op = rv_op_fcvt_s_lu; break;
+                }
+                break;
+            case 105:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_d_w; break;
+                case 1: op = rv_op_fcvt_d_wu; break;
+                case 2: op = rv_op_fcvt_d_l; break;
+                case 3: op = rv_op_fcvt_d_lu; break;
+                }
+                break;
+            case 107:
+                switch (((inst >> 20) & 0b11111)) {
+                case 0: op = rv_op_fcvt_q_w; break;
+                case 1: op = rv_op_fcvt_q_wu; break;
+                case 2: op = rv_op_fcvt_q_l; break;
+                case 3: op = rv_op_fcvt_q_lu; break;
+                }
+                break;
+            case 112:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_x_s; break;
+                case 1: op = rv_op_fclass_s; break;
+                }
+                break;
+            case 113:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_x_d; break;
+                case 1: op = rv_op_fclass_d; break;
+                }
+                break;
+            case 115:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_x_q; break;
+                case 1: op = rv_op_fclass_q; break;
+                }
+                break;
+            case 120:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_s_x; break;
+                }
+                break;
+            case 121:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_d_x; break;
+                }
+                break;
+            case 123:
+                switch (((inst >> 17) & 0b11111000) | ((inst >> 12) & 0b00000111)) {
+                case 0: op = rv_op_fmv_q_x; break;
+                }
+                break;
+            }
+            break;
+        case 22:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_addid; break;
+            case 1:
+                switch (((inst >> 26) & 0b111111)) {
+                case 0: op = rv_op_sllid; break;
+                }
+                break;
+            case 5:
+                switch (((inst >> 26) & 0b111111)) {
+                case 0: op = rv_op_srlid; break;
+                case 16: op = rv_op_sraid; break;
+                }
+                break;
+            }
+            break;
+        case 24:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_beq; break;
+            case 1: op = rv_op_bne; break;
+            case 4: op = rv_op_blt; break;
+            case 5: op = rv_op_bge; break;
+            case 6: op = rv_op_bltu; break;
+            case 7: op = rv_op_bgeu; break;
+            }
+            break;
+        case 25:
+            switch (((inst >> 12) & 0b111)) {
+            case 0: op = rv_op_jalr; break;
+            }
+            break;
+        case 27: op = rv_op_jal; break;
+        case 28:
+            switch (((inst >> 12) & 0b111)) {
+            case 0:
+                switch (((inst >> 20) & 0b111111100000) | ((inst >> 7) & 0b000000011111)) {
+                case 0:
+                    switch (((inst >> 15) & 0b1111111111)) {
+                    case 0: op = rv_op_ecall; break;
+                    case 32: op = rv_op_ebreak; break;
+                    case 64: op = rv_op_uret; break;
+                    }
+                    break;
+                case 256:
+                    switch (((inst >> 20) & 0b11111)) {
+                    case 2:
+                        switch (((inst >> 15) & 0b11111)) {
+                        case 0: op = rv_op_sret; break;
+                        }
+                        break;
+                    case 4: op = rv_op_sfence_vm; break;
+                    case 5:
+                        switch (((inst >> 15) & 0b11111)) {
+                        case 0: op = rv_op_wfi; break;
+                        }
+                        break;
+                    }
+                    break;
+                case 288: op = rv_op_sfence_vma; break;
+                case 512:
+                    switch (((inst >> 15) & 0b1111111111)) {
+                    case 64: op = rv_op_hret; break;
+                    }
+                    break;
+                case 768:
+                    switch (((inst >> 15) & 0b1111111111)) {
+                    case 64: op = rv_op_mret; break;
+                    }
+                    break;
+                case 1952:
+                    switch (((inst >> 15) & 0b1111111111)) {
+                    case 576: op = rv_op_dret; break;
+                    }
+                    break;
+                }
+                break;
+            case 1: op = rv_op_csrrw; break;
+            case 2: op = rv_op_csrrs; break;
+            case 3: op = rv_op_csrrc; break;
+            case 5: op = rv_op_csrrwi; break;
+            case 6: op = rv_op_csrrsi; break;
+            case 7: op = rv_op_csrrci; break;
+            }
+            break;
+        case 30:
+            switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {
+            case 0: op = rv_op_addd; break;
+            case 1: op = rv_op_slld; break;
+            case 5: op = rv_op_srld; break;
+            case 8: op = rv_op_muld; break;
+            case 12: op = rv_op_divd; break;
+            case 13: op = rv_op_divud; break;
+            case 14: op = rv_op_remd; break;
+            case 15: op = rv_op_remud; break;
+            case 256: op = rv_op_subd; break;
+            case 261: op = rv_op_srad; break;
+            }
+            break;
+        }
+        break;
+    }
+    dec->op = op;
+}
+
+/* operand extractors */
+
+static uint32_t operand_rd(rv_inst inst)
+{
+    return (inst << 52) >> 59;
+}
+
+static uint32_t operand_rs1(rv_inst inst)
+{
+    return (inst << 44) >> 59;
+}
+
+static uint32_t operand_rs2(rv_inst inst)
+{
+    return (inst << 39) >> 59;
+}
+
+static uint32_t operand_rs3(rv_inst inst)
+{
+    return (inst << 32) >> 59;
+}
+
+static uint32_t operand_aq(rv_inst inst)
+{
+    return (inst << 37) >> 63;
+}
+
+static uint32_t operand_rl(rv_inst inst)
+{
+    return (inst << 38) >> 63;
+}
+
+static uint32_t operand_pred(rv_inst inst)
+{
+    return (inst << 36) >> 60;
+}
+
+static uint32_t operand_succ(rv_inst inst)
+{
+    return (inst << 40) >> 60;
+}
+
+static uint32_t operand_rm(rv_inst inst)
+{
+    return (inst << 49) >> 61;
+}
+
+static uint32_t operand_shamt5(rv_inst inst)
+{
+    return (inst << 39) >> 59;
+}
+
+static uint32_t operand_shamt6(rv_inst inst)
+{
+    return (inst << 38) >> 58;
+}
+
+static uint32_t operand_shamt7(rv_inst inst)
+{
+    return (inst << 37) >> 57;
+}
+
+static uint32_t operand_crdq(rv_inst inst)
+{
+    return (inst << 59) >> 61;
+}
+
+static uint32_t operand_crs1q(rv_inst inst)
+{
+    return (inst << 54) >> 61;
+}
+
+static uint32_t operand_crs1rdq(rv_inst inst)
+{
+    return (inst << 54) >> 61;
+}
+
+static uint32_t operand_crs2q(rv_inst inst)
+{
+    return (inst << 59) >> 61;
+}
+
+static uint32_t operand_crd(rv_inst inst)
+{
+    return (inst << 52) >> 59;
+}
+
+static uint32_t operand_crs1(rv_inst inst)
+{
+    return (inst << 52) >> 59;
+}
+
+static uint32_t operand_crs1rd(rv_inst inst)
+{
+    return (inst << 52) >> 59;
+}
+
+static uint32_t operand_crs2(rv_inst inst)
+{
+    return (inst << 57) >> 59;
+}
+
+static uint32_t operand_cimmsh5(rv_inst inst)
+{
+    return (inst << 57) >> 59;
+}
+
+static uint32_t operand_csr12(rv_inst inst)
+{
+    return (inst << 32) >> 52;
+}
+
+static int32_t operand_imm12(rv_inst inst)
+{
+    return ((int64_t)inst << 32) >> 52;
+}
+
+static int32_t operand_imm20(rv_inst inst)
+{
+    return (((int64_t)inst << 32) >> 44) << 12;
+}
+
+static int32_t operand_jimm20(rv_inst inst)
+{
+    return (((int64_t)inst << 32) >> 63) << 20 |
+        ((inst << 33) >> 54) << 1 |
+        ((inst << 43) >> 63) << 11 |
+        ((inst << 44) >> 56) << 12;
+}
+
+static int32_t operand_simm12(rv_inst inst)
+{
+    return (((int64_t)inst << 32) >> 57) << 5 |
+        (inst << 52) >> 59;
+}
+
+static int32_t operand_sbimm12(rv_inst inst)
+{
+    return (((int64_t)inst << 32) >> 63) << 12 |
+        ((inst << 33) >> 58) << 5 |
+        ((inst << 52) >> 60) << 1 |
+        ((inst << 56) >> 63) << 11;
+}
+
+static uint32_t operand_cimmsh6(rv_inst inst)
+{
+    return ((inst << 51) >> 63) << 5 |
+        (inst << 57) >> 59;
+}
+
+static int32_t operand_cimmi(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 5 |
+        (inst << 57) >> 59;
+}
+
+static int32_t operand_cimmui(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 17 |
+        ((inst << 57) >> 59) << 12;
+}
+
+static uint32_t operand_cimmlwsp(rv_inst inst)
+{
+    return ((inst << 51) >> 63) << 5 |
+        ((inst << 57) >> 61) << 2 |
+        ((inst << 60) >> 62) << 6;
+}
+
+static uint32_t operand_cimmldsp(rv_inst inst)
+{
+    return ((inst << 51) >> 63) << 5 |
+        ((inst << 57) >> 62) << 3 |
+        ((inst << 59) >> 61) << 6;
+}
+
+static uint32_t operand_cimmlqsp(rv_inst inst)
+{
+    return ((inst << 51) >> 63) << 5 |
+        ((inst << 57) >> 63) << 4 |
+        ((inst << 58) >> 60) << 6;
+}
+
+static int32_t operand_cimm16sp(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 9 |
+        ((inst << 57) >> 63) << 4 |
+        ((inst << 58) >> 63) << 6 |
+        ((inst << 59) >> 62) << 7 |
+        ((inst << 61) >> 63) << 5;
+}
+
+static int32_t operand_cimmj(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 11 |
+        ((inst << 52) >> 63) << 4 |
+        ((inst << 53) >> 62) << 8 |
+        ((inst << 55) >> 63) << 10 |
+        ((inst << 56) >> 63) << 6 |
+        ((inst << 57) >> 63) << 7 |
+        ((inst << 58) >> 61) << 1 |
+        ((inst << 61) >> 63) << 5;
+}
+
+static int32_t operand_cimmb(rv_inst inst)
+{
+    return (((int64_t)inst << 51) >> 63) << 8 |
+        ((inst << 52) >> 62) << 3 |
+        ((inst << 57) >> 62) << 6 |
+        ((inst << 59) >> 62) << 1 |
+        ((inst << 61) >> 63) << 5;
+}
+
+static uint32_t operand_cimmswsp(rv_inst inst)
+{
+    return ((inst << 51) >> 60) << 2 |
+        ((inst << 55) >> 62) << 6;
+}
+
+static uint32_t operand_cimmsdsp(rv_inst inst)
+{
+    return ((inst << 51) >> 61) << 3 |
+        ((inst << 54) >> 61) << 6;
+}
+
+static uint32_t operand_cimmsqsp(rv_inst inst)
+{
+    return ((inst << 51) >> 62) << 4 |
+        ((inst << 53) >> 60) << 6;
+}
+
+static uint32_t operand_cimm4spn(rv_inst inst)
+{
+    return ((inst << 51) >> 62) << 4 |
+        ((inst << 53) >> 60) << 6 |
+        ((inst << 57) >> 63) << 2 |
+        ((inst << 58) >> 63) << 3;
+}
+
+static uint32_t operand_cimmw(rv_inst inst)
+{
+    return ((inst << 51) >> 61) << 3 |
+        ((inst << 57) >> 63) << 2 |
+        ((inst << 58) >> 63) << 6;
+}
+
+static uint32_t operand_cimmd(rv_inst inst)
+{
+    return ((inst << 51) >> 61) << 3 |
+        ((inst << 57) >> 62) << 6;
+}
+
+static uint32_t operand_cimmq(rv_inst inst)
+{
+    return ((inst << 51) >> 62) << 4 |
+        ((inst << 53) >> 63) << 8 |
+        ((inst << 57) >> 62) << 6;
+}
+
+/* decode operands */
+
+static void decode_inst_operands(rv_decode *dec)
+{
+    rv_inst inst = dec->inst;
+    dec->codec = opcode_data[dec->op].codec;
+    switch (dec->codec) {
+    case rv_codec_none:
+        dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_u:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_imm20(inst);
+        break;
+    case rv_codec_uj:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_jimm20(inst);
+        break;
+    case rv_codec_i:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_imm12(inst);
+        break;
+    case rv_codec_i_sh5:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_shamt5(inst);
+        break;
+    case rv_codec_i_sh6:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_shamt6(inst);
+        break;
+    case rv_codec_i_sh7:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_shamt7(inst);
+        break;
+    case rv_codec_i_csr:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_csr12(inst);
+        break;
+    case rv_codec_s:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = operand_simm12(inst);
+        break;
+    case rv_codec_sb:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = operand_sbimm12(inst);
+        break;
+    case rv_codec_r:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = 0;
+        break;
+    case rv_codec_r_m:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = 0;
+        dec->rm = operand_rm(inst);
+        break;
+    case rv_codec_r4_m:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->rs3 = operand_rs3(inst);
+        dec->imm = 0;
+        dec->rm = operand_rm(inst);
+        break;
+    case rv_codec_r_a:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = operand_rs2(inst);
+        dec->imm = 0;
+        dec->aq = operand_aq(inst);
+        dec->rl = operand_rl(inst);
+        break;
+    case rv_codec_r_l:
+        dec->rd = operand_rd(inst);
+        dec->rs1 = operand_rs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        dec->aq = operand_aq(inst);
+        dec->rl = operand_rl(inst);
+        break;
+    case rv_codec_r_f:
+        dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->pred = operand_pred(inst);
+        dec->succ = operand_succ(inst);
+        dec->imm = 0;
+        break;
+    case rv_codec_cb:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmb(inst);
+        break;
+    case rv_codec_cb_imm:
+        dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmi(inst);
+        break;
+    case rv_codec_cb_sh5:
+        dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmsh5(inst);
+        break;
+    case rv_codec_cb_sh6:
+        dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmsh6(inst);
+        break;
+    case rv_codec_ci:
+        dec->rd = dec->rs1 = operand_crs1rd(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmi(inst);
+        break;
+    case rv_codec_ci_sh5:
+        dec->rd = dec->rs1 = operand_crs1rd(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmsh5(inst);
+        break;
+    case rv_codec_ci_sh6:
+        dec->rd = dec->rs1 = operand_crs1rd(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmsh6(inst);
+        break;
+    case rv_codec_ci_16sp:
+        dec->rd = rv_ireg_sp;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimm16sp(inst);
+        break;
+    case rv_codec_ci_lwsp:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmlwsp(inst);
+        break;
+    case rv_codec_ci_ldsp:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmldsp(inst);
+        break;
+    case rv_codec_ci_lqsp:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmlqsp(inst);
+        break;
+    case rv_codec_ci_li:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_zero;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmi(inst);
+        break;
+    case rv_codec_ci_lui:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = rv_ireg_zero;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmui(inst);
+        break;
+    case rv_codec_ci_none:
+        dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_ciw_4spn:
+        dec->rd = operand_crdq(inst) + 8;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimm4spn(inst);
+        break;
+    case rv_codec_cj:
+        dec->rd = dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmj(inst);
+        break;
+    case rv_codec_cj_jal:
+        dec->rd = rv_ireg_ra;
+        dec->rs1 = dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmj(inst);
+        break;
+    case rv_codec_cl_lw:
+        dec->rd = operand_crdq(inst) + 8;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmw(inst);
+        break;
+    case rv_codec_cl_ld:
+        dec->rd = operand_crdq(inst) + 8;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmd(inst);
+        break;
+    case rv_codec_cl_lq:
+        dec->rd = operand_crdq(inst) + 8;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = operand_cimmq(inst);
+        break;
+    case rv_codec_cr:
+        dec->rd = dec->rs1 = operand_crs1rd(inst);
+        dec->rs2 = operand_crs2(inst);
+        dec->imm = 0;
+        break;
+    case rv_codec_cr_mv:
+        dec->rd = operand_crd(inst);
+        dec->rs1 = operand_crs2(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_cr_jalr:
+        dec->rd = rv_ireg_ra;
+        dec->rs1 = operand_crs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_cr_jr:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1(inst);
+        dec->rs2 = rv_ireg_zero;
+        dec->imm = 0;
+        break;
+    case rv_codec_cs:
+        dec->rd = dec->rs1 = operand_crs1rdq(inst) + 8;
+        dec->rs2 = operand_crs2q(inst) + 8;
+        dec->imm = 0;
+        break;
+    case rv_codec_cs_sw:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = operand_crs2q(inst) + 8;
+        dec->imm = operand_cimmw(inst);
+        break;
+    case rv_codec_cs_sd:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = operand_crs2q(inst) + 8;
+        dec->imm = operand_cimmd(inst);
+        break;
+    case rv_codec_cs_sq:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = operand_crs1q(inst) + 8;
+        dec->rs2 = operand_crs2q(inst) + 8;
+        dec->imm = operand_cimmq(inst);
+        break;
+    case rv_codec_css_swsp:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = operand_crs2(inst);
+        dec->imm = operand_cimmswsp(inst);
+        break;
+    case rv_codec_css_sdsp:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = operand_crs2(inst);
+        dec->imm = operand_cimmsdsp(inst);
+        break;
+    case rv_codec_css_sqsp:
+        dec->rd = rv_ireg_zero;
+        dec->rs1 = rv_ireg_sp;
+        dec->rs2 = operand_crs2(inst);
+        dec->imm = operand_cimmsqsp(inst);
+        break;
+    };
+}
+
+/* check constraint */
+
+static bool check_constraints(rv_decode *dec, const rvc_constraint *c)
+{
+    int32_t imm = dec->imm;
+    uint8_t rd = dec->rd, rs1 = dec->rs1, rs2 = dec->rs2;
+    while (*c != rvc_end) {
+        switch (*c) {
+        case rvc_simm_6:
+            if (!(imm >= -32 && imm < 32)) {
+                return false;
+            }
+            break;
+        case rvc_imm_6:
+            if (!(imm <= 63)) {
+                return false;
+            }
+            break;
+        case rvc_imm_7:
+            if (!(imm <= 127)) {
+                return false;
+            }
+            break;
+        case rvc_imm_8:
+            if (!(imm <= 255)) {
+                return false;
+            }
+            break;
+        case rvc_imm_9:
+            if (!(imm <= 511)) {
+                return false;
+            }
+            break;
+        case rvc_imm_10:
+            if (!(imm <= 1023)) {
+                return false;
+            }
+            break;
+        case rvc_imm_12:
+            if (!(imm <= 4095)) {
+                return false;
+            }
+            break;
+        case rvc_imm_18:
+            if (!(imm <= 262143)) {
+                return false;
+            }
+            break;
+        case rvc_imm_nz:
+            if (!(imm != 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_x2:
+            if (!((imm & 0b1) == 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_x4:
+            if (!((imm & 0b11) == 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_x8:
+            if (!((imm & 0b111) == 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_x16:
+            if (!((imm & 0b1111) == 0)) {
+                return false;
+            }
+            break;
+        case rvc_rd_b3:
+            if (!(rd  >= 8 && rd  <= 15)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_b3:
+            if (!(rs1 >= 8 && rs1 <= 15)) {
+                return false;
+            }
+            break;
+        case rvc_rs2_b3:
+            if (!(rs2 >= 8 && rs2 <= 15)) {
+                return false;
+            }
+            break;
+        case rvc_rd_eq_rs1:
+            if (!(rd == rs1)) {
+                return false;
+            }
+            break;
+        case rvc_rd_eq_ra:
+            if (!(rd == 1)) {
+                return false;
+            }
+            break;
+        case rvc_rd_eq_sp:
+            if (!(rd == 2)) {
+                return false;
+            }
+            break;
+        case rvc_rd_eq_x0:
+            if (!(rd == 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_eq_sp:
+            if (!(rs1 == 2)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_eq_x0:
+            if (!(rs1 == 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs2_eq_x0:
+            if (!(rs2 == 0)) {
+                return false;
+            }
+            break;
+        case rvc_rd_ne_x0_x2:
+            if (!(rd != 0 && rd != 2)) {
+                return false;
+            }
+            break;
+        case rvc_rd_ne_x0:
+            if (!(rd != 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_ne_x0:
+            if (!(rs1 != 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs2_ne_x0:
+            if (!(rs2 != 0)) {
+                return false;
+            }
+            break;
+        case rvc_rs2_eq_rs1:
+            if (!(rs2 == rs1)) {
+                return false;
+            }
+            break;
+        case rvc_rs1_eq_ra:
+            if (!(rs1 == 1)) {
+                return false;
+            }
+            break;
+        case rvc_imm_eq_zero:
+            if (!(imm == 0)) {
+                return false;
+            }
+            break;
+        case rvc_imm_eq_n1:
+            if (!(imm == -1)) {
+                return false;
+            }
+            break;
+        case rvc_imm_eq_p1:
+            if (!(imm == 1)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0x001:
+            if (!(imm == 0x001)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0x002:
+            if (!(imm == 0x002)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0x003:
+            if (!(imm == 0x003)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc00:
+            if (!(imm == 0xc00)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc01:
+            if (!(imm == 0xc01)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc02:
+            if (!(imm == 0xc02)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc80:
+            if (!(imm == 0xc80)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc81:
+            if (!(imm == 0xc81)) {
+                return false;
+            }
+            break;
+        case rvc_csr_eq_0xc82:
+            if (!(imm == 0xc82)) {
+                return false;
+            }
+            break;
+        default: break;
+        }
+        c++;
+    }
+    return true;
+}
+
+/* instruction length */
+
+static size_t inst_length(rv_inst inst)
+{
+    /* NOTE: supports maximum instruction size of 64-bits */
+
+    /* instruction length coding
+     *
+     *      aa - 16 bit aa != 11
+     *   bbb11 - 32 bit bbb != 111
+     *  011111 - 48 bit
+     * 0111111 - 64 bit
+     */
+
+    return (inst &      0b11) != 0b11      ? 2
+         : (inst &   0b11100) != 0b11100   ? 4
+         : (inst &  0b111111) == 0b011111  ? 6
+         : (inst & 0b1111111) == 0b0111111 ? 8
+         : 0;
+}
+
+/* format instruction */
+
+static void append(char *s1, const char *s2, size_t n)
+{
+    size_t l1 = strlen(s1);
+    if (n - l1 - 1 > 0) {
+        strncat(s1, s2, n - l1);
+    }
+}
+
+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) {
+        case 'O':
+            append(buf, opcode_data[dec->op].name, buflen);
+            break;
+        case '(':
+            append(buf, "(", buflen);
+            break;
+        case ',':
+            append(buf, ",", buflen);
+            break;
+        case ')':
+            append(buf, ")", buflen);
+            break;
+        case '0':
+            append(buf, rv_ireg_name_sym[dec->rd], buflen);
+            break;
+        case '1':
+            append(buf, rv_ireg_name_sym[dec->rs1], buflen);
+            break;
+        case '2':
+            append(buf, rv_ireg_name_sym[dec->rs2], buflen);
+            break;
+        case '3':
+            append(buf, rv_freg_name_sym[dec->rd], buflen);
+            break;
+        case '4':
+            append(buf, rv_freg_name_sym[dec->rs1], buflen);
+            break;
+        case '5':
+            append(buf, rv_freg_name_sym[dec->rs2], buflen);
+            break;
+        case '6':
+            append(buf, rv_freg_name_sym[dec->rs3], buflen);
+            break;
+        case '7':
+            snprintf(tmp, sizeof(tmp), "%d", dec->rs1);
+            append(buf, tmp, buflen);
+            break;
+        case 'i':
+            snprintf(tmp, sizeof(tmp), "%d", dec->imm);
+            append(buf, tmp, buflen);
+            break;
+        case 'o':
+            snprintf(tmp, sizeof(tmp), "%d", dec->imm);
+            append(buf, tmp, buflen);
+            while (strlen(buf) < tab * 2) {
+                append(buf, " ", buflen);
+            }
+            snprintf(tmp, sizeof(tmp), "# 0x%" PRIx64,
+                dec->pc + dec->imm);
+            append(buf, tmp, buflen);
+            break;
+        case 'c': {
+            const char *name = csr_name(dec->imm & 0xfff);
+            if (name) {
+                append(buf, name, buflen);
+            } else {
+                snprintf(tmp, sizeof(tmp), "0x%03x", dec->imm & 0xfff);
+                append(buf, tmp, buflen);
+            }
+            break;
+        }
+        case 'r':
+            switch (dec->rm) {
+            case rv_rm_rne:
+                append(buf, "rne", buflen);
+                break;
+            case rv_rm_rtz:
+                append(buf, "rtz", buflen);
+                break;
+            case rv_rm_rdn:
+                append(buf, "rdn", buflen);
+                break;
+            case rv_rm_rup:
+                append(buf, "rup", buflen);
+                break;
+            case rv_rm_rmm:
+                append(buf, "rmm", buflen);
+                break;
+            case rv_rm_dyn:
+                append(buf, "dyn", buflen);
+                break;
+            default:
+                append(buf, "inv", buflen);
+                break;
+            }
+            break;
+        case 'p':
+            if (dec->pred & rv_fence_i) {
+                append(buf, "i", buflen);
+            }
+            if (dec->pred & rv_fence_o) {
+                append(buf, "o", buflen);
+            }
+            if (dec->pred & rv_fence_r) {
+                append(buf, "r", buflen);
+            }
+            if (dec->pred & rv_fence_w) {
+                append(buf, "w", buflen);
+            }
+            break;
+        case 's':
+            if (dec->succ & rv_fence_i) {
+                append(buf, "i", buflen);
+            }
+            if (dec->succ & rv_fence_o) {
+                append(buf, "o", buflen);
+            }
+            if (dec->succ & rv_fence_r) {
+                append(buf, "r", buflen);
+            }
+            if (dec->succ & rv_fence_w) {
+                append(buf, "w", buflen);
+            }
+            break;
+        case '\t':
+            while (strlen(buf) < tab) {
+                append(buf, " ", buflen);
+            }
+            break;
+        case 'A':
+            if (dec->aq) {
+                append(buf, ".aq", buflen);
+            }
+            break;
+        case 'R':
+            if (dec->rl) {
+                append(buf, ".rl", buflen);
+            }
+            break;
+        default:
+            break;
+        }
+        fmt++;
+    }
+}
+
+/* lift instruction to pseudo-instruction */
+
+static void decode_inst_lift_pseudo(rv_decode *dec)
+{
+    const rv_comp_data *comp_data = opcode_data[dec->op].pseudo;
+    if (!comp_data) {
+        return;
+    }
+    while (comp_data->constraints) {
+        if (check_constraints(dec, comp_data->constraints)) {
+            dec->op = comp_data->op;
+            dec->codec = opcode_data[dec->op].codec;
+            return;
+        }
+        comp_data++;
+    }
+}
+
+/* decompress instruction */
+
+static void decode_inst_decompress_rv32(rv_decode *dec)
+{
+    int decomp_op = opcode_data[dec->op].decomp_rv32;
+    if (decomp_op != rv_op_illegal) {
+        dec->op = decomp_op;
+        dec->codec = opcode_data[decomp_op].codec;
+    }
+}
+
+static void decode_inst_decompress_rv64(rv_decode *dec)
+{
+    int decomp_op = opcode_data[dec->op].decomp_rv64;
+    if (decomp_op != rv_op_illegal) {
+        dec->op = decomp_op;
+        dec->codec = opcode_data[decomp_op].codec;
+    }
+}
+
+static void decode_inst_decompress_rv128(rv_decode *dec)
+{
+    int decomp_op = opcode_data[dec->op].decomp_rv128;
+    if (decomp_op != rv_op_illegal) {
+        dec->op = decomp_op;
+        dec->codec = opcode_data[decomp_op].codec;
+    }
+}
+
+static void decode_inst_decompress(rv_decode *dec, rv_isa isa)
+{
+    switch (isa) {
+    case rv32:
+        decode_inst_decompress_rv32(dec);
+        break;
+    case rv64:
+        decode_inst_decompress_rv64(dec);
+        break;
+    case rv128:
+        decode_inst_decompress_rv128(dec);
+        break;
+    }
+}
+
+/* disassemble instruction */
+
+static void
+disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst)
+{
+    rv_decode dec = { 0 };
+    dec.pc = pc;
+    dec.inst = inst;
+    decode_inst_opcode(&dec, isa);
+    decode_inst_operands(&dec);
+    decode_inst_decompress(&dec, isa);
+    decode_inst_lift_pseudo(&dec);
+    format_inst(buf, buflen, 16, &dec);
+}
+
+static int
+print_insn_riscv(bfd_vma memaddr, struct disassemble_info *info, rv_isa isa)
+{
+    char buf[128] = { 0 };
+    bfd_byte packet[2];
+    rv_inst inst = 0;
+    size_t len = 2;
+    bfd_vma n;
+    int status;
+
+    /* Instructions are made of 2-byte packets in little-endian order */
+    for (n = 0; n < len; n += 2) {
+        status = (*info->read_memory_func)(memaddr + n, packet, 2, info);
+        if (status != 0) {
+            /* Don't fail just because we fell off the end.  */
+            if (n > 0) {
+                break;
+            }
+            (*info->memory_error_func)(status, memaddr, info);
+            return status;
+        }
+        inst |= ((rv_inst) bfd_getl16(packet)) << (8 * n);
+        if (n == 0) {
+            len = inst_length(inst);
+        }
+    }
+
+    disasm_inst(buf, sizeof(buf), isa, memaddr, inst);
+    (*info->fprintf_func)(info->stream, "%s", buf);
+
+    return len;
+}
+
+int print_insn_riscv32(bfd_vma memaddr, struct disassemble_info *info)
+{
+    return print_insn_riscv(memaddr, info, rv32);
+}
+
+int print_insn_riscv64(bfd_vma memaddr, struct disassemble_info *info)
+{
+    return print_insn_riscv(memaddr, info, rv64);
+}
index d7fdb1fee313c33d2f0d9e9b467d64b970aa4e8a..feb711fb6a88187c36716518e0ed1954a6fc6eda 100644 (file)
@@ -426,10 +426,20 @@ Standard Cluster Descriptor:
 
 Compressed Clusters Descriptor (x = 62 - (cluster_bits - 8)):
 
-    Bit  0 -  x:    Host cluster offset. This is usually _not_ aligned to a
-                    cluster boundary!
+    Bit  0 - x-1:   Host cluster offset. This is usually _not_ aligned to a
+                    cluster or sector boundary!
 
-       x+1 - 61:    Compressed size of the images in sectors of 512 bytes
+         x - 61:    Number of additional 512-byte sectors used for the
+                    compressed data, beyond the sector containing the offset
+                    in the previous field. Some of these sectors may reside
+                    in the next contiguous host cluster.
+
+                    Note that the compressed data does not necessarily occupy
+                    all of the bytes in the final sector; rather, decompression
+                    stops when it has produced a cluster of data.
+
+                    Another compressed cluster may map to the tail of the final
+                    sector used by this compressed cluster.
 
 If a cluster is unallocated, read requests shall read the data from the backing
 file (except if bit 0 in the Standard Cluster Descriptor is set). If there is
index b0571de4b826234fc15d56a4074c445e48cb269a..170191a242bf488307e8c5dbf9165140ee28d9da 100644 (file)
@@ -1,6 +1,6 @@
 qcow2 L2/refcount cache configuration
 =====================================
-Copyright (C) 2015 Igalia, S.L.
+Copyright (C) 2015, 2018 Igalia, S.L.
 Author: Alberto Garcia <berto@igalia.com>
 
 This work is licensed under the terms of the GNU GPL, version 2 or
@@ -118,8 +118,8 @@ There are three options available, and all of them take bytes:
 
 There are two things that need to be taken into account:
 
- - Both caches must have a size that is a multiple of the cluster
-   size.
+ - 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
@@ -143,6 +143,46 @@ much less often than the L2 cache, so it's perfectly reasonable to
 keep it small.
 
 
+Using smaller cache entries
+---------------------------
+The qcow2 L2 cache stores complete tables by default. This means that
+if QEMU needs an entry from an L2 table then the whole table is read
+from disk and is kept in the cache. If the cache is full then a
+complete table needs to be evicted first.
+
+This can be inefficient with large cluster sizes since it results in
+more disk I/O and wastes more cache memory.
+
+Since QEMU 2.12 you can change the size of the L2 cache entry and make
+it smaller than the cluster size. This can be configured using the
+"l2-cache-entry-size" parameter:
+
+   -drive file=hd.qcow2,l2-cache-size=2097152,l2-cache-entry-size=4096
+
+Some things to take into account:
+
+ - The L2 cache entry size has the same restrictions as the cluster
+   size (power of two, at least 512 bytes).
+
+ - Smaller entry sizes generally improve the cache efficiency and make
+   disk I/O faster. This is particularly true with solid state drives
+   so it's a good idea to reduce the entry size in those cases. With
+   rotating hard drives the situation is a bit more complicated so you
+   should test it first and stay with the default size if unsure.
+
+ - Try different entry sizes to see which one gives faster performance
+   in your case. The block size of the host filesystem is generally a
+   good default (usually 4096 bytes in the case of ext4).
+
+ - Only the L2 cache can be configured this way. The refcount cache
+   always uses the cluster size as the entry size.
+
+ - If the L2 cache is big enough to hold all of the image's L2 tables
+   (as explained in the "Choosing the right cache sizes" section
+   earlier in this document) then none of this is necessary and you
+   can omit the "l2-cache-entry-size" parameter altogether.
+
+
 Reducing the memory usage
 -------------------------
 It is possible to clean unused cache entries in order to reduce the
diff --git a/exec.c b/exec.c
index 4d8addb263a0b607427ca1f446feda21b984d1dc..a9181e641766172b14696afd625d7001d2566ef8 100644 (file)
--- a/exec.c
+++ b/exec.c
@@ -2616,6 +2616,8 @@ static const MemoryRegionOps watch_mem_ops = {
     },
 };
 
+static MemTxResult flatview_read(FlatView *fv, hwaddr addr,
+                                      MemTxAttrs attrs, uint8_t *buf, int len);
 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,
@@ -3078,6 +3080,7 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr,
     return result;
 }
 
+/* Called from RCU critical section.  */
 static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs,
                                   const uint8_t *buf, int len)
 {
@@ -3086,25 +3089,14 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs,
     MemoryRegion *mr;
     MemTxResult result = MEMTX_OK;
 
-    if (len > 0) {
-        rcu_read_lock();
-        l = len;
-        mr = flatview_translate(fv, addr, &addr1, &l, true);
-        result = flatview_write_continue(fv, addr, attrs, buf, len,
-                                         addr1, l, mr);
-        rcu_read_unlock();
-    }
+    l = len;
+    mr = flatview_translate(fv, addr, &addr1, &l, true);
+    result = flatview_write_continue(fv, addr, attrs, buf, len,
+                                     addr1, l, mr);
 
     return result;
 }
 
-MemTxResult address_space_write(AddressSpace *as, hwaddr addr,
-                                              MemTxAttrs attrs,
-                                              const uint8_t *buf, int len)
-{
-    return flatview_write(address_space_to_flatview(as), addr, attrs, buf, len);
-}
-
 /* Called within RCU critical section.  */
 MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr,
                                    MemTxAttrs attrs, uint8_t *buf,
@@ -3175,42 +3167,61 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr,
     return result;
 }
 
-MemTxResult flatview_read_full(FlatView *fv, hwaddr addr,
-                               MemTxAttrs attrs, uint8_t *buf, int len)
+/* Called from RCU critical section.  */
+static MemTxResult flatview_read(FlatView *fv, hwaddr addr,
+                                 MemTxAttrs attrs, uint8_t *buf, int len)
 {
     hwaddr l;
     hwaddr addr1;
     MemoryRegion *mr;
+
+    l = len;
+    mr = flatview_translate(fv, addr, &addr1, &l, false);
+    return flatview_read_continue(fv, addr, attrs, buf, len,
+                                  addr1, l, mr);
+}
+
+MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
+                                    MemTxAttrs attrs, uint8_t *buf, int len)
+{
     MemTxResult result = MEMTX_OK;
+    FlatView *fv;
 
     if (len > 0) {
         rcu_read_lock();
-        l = len;
-        mr = flatview_translate(fv, addr, &addr1, &l, false);
-        result = flatview_read_continue(fv, addr, attrs, buf, len,
-                                        addr1, l, mr);
+        fv = address_space_to_flatview(as);
+        result = flatview_read(fv, addr, attrs, buf, len);
         rcu_read_unlock();
     }
 
     return result;
 }
 
-static MemTxResult flatview_rw(FlatView *fv, hwaddr addr, MemTxAttrs attrs,
-                               uint8_t *buf, int len, bool is_write)
+MemTxResult address_space_write(AddressSpace *as, hwaddr addr,
+                                MemTxAttrs attrs,
+                                const uint8_t *buf, int len)
 {
-    if (is_write) {
-        return flatview_write(fv, addr, attrs, (uint8_t *)buf, len);
-    } else {
-        return flatview_read(fv, addr, attrs, (uint8_t *)buf, len);
+    MemTxResult result = MEMTX_OK;
+    FlatView *fv;
+
+    if (len > 0) {
+        rcu_read_lock();
+        fv = address_space_to_flatview(as);
+        result = flatview_write(fv, addr, attrs, buf, len);
+        rcu_read_unlock();
     }
+
+    return result;
 }
 
-MemTxResult address_space_rw(AddressSpace *as, hwaddr addr,
-                             MemTxAttrs attrs, uint8_t *buf,
-                             int len, bool is_write)
+MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs,
+                             uint8_t *buf, int len, bool is_write)
 {
-    return flatview_rw(address_space_to_flatview(as),
-                       addr, attrs, buf, len, is_write);
+    if (is_write) {
+        return address_space_write(as, addr, attrs, buf, len);
+    } else {
+        return address_space_read_full(as, addr, attrs, buf, len);
+    }
 }
 
 void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf,
@@ -3376,14 +3387,12 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len,
     MemoryRegion *mr;
     hwaddr l, xlat;
 
-    rcu_read_lock();
     while (len > 0) {
         l = len;
         mr = flatview_translate(fv, addr, &xlat, &l, is_write);
         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)) {
-                rcu_read_unlock();
                 return false;
             }
         }
@@ -3391,15 +3400,20 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len,
         len -= l;
         addr += l;
     }
-    rcu_read_unlock();
     return true;
 }
 
 bool address_space_access_valid(AddressSpace *as, hwaddr addr,
                                 int len, bool is_write)
 {
-    return flatview_access_valid(address_space_to_flatview(as),
-                                 addr, len, is_write);
+    FlatView *fv;
+    bool result;
+
+    rcu_read_lock();
+    fv = address_space_to_flatview(as);
+    result = flatview_access_valid(fv, addr, len, is_write);
+    rcu_read_unlock();
+    return result;
 }
 
 static hwaddr
@@ -3445,7 +3459,7 @@ void *address_space_map(AddressSpace *as,
     hwaddr l, xlat;
     MemoryRegion *mr;
     void *ptr;
-    FlatView *fv = address_space_to_flatview(as);
+    FlatView *fv;
 
     if (len == 0) {
         return NULL;
@@ -3453,6 +3467,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);
 
     if (!memory_access_is_direct(mr, is_write)) {
index 9ccb59422c7f4e77afce58e9cbbf5f5e173791a3..27834af0deb9e1a8cf395f58413f63a4f5e57213 100644 (file)
@@ -114,7 +114,8 @@ float32 float32_default_nan(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_XTENSA) || defined(TARGET_S390X) || \
+      defined(TARGET_TRICORE) || defined(TARGET_RISCV)
     return const_float32(0x7FC00000);
 #elif defined(TARGET_HPPA)
     return const_float32(0x7FA00000);
@@ -139,7 +140,7 @@ float64 float64_default_nan(float_status *status)
 #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_S390X) || defined(TARGET_RISCV)
     return const_float64(LIT64(0x7FF8000000000000));
 #elif defined(TARGET_HPPA)
     return const_float64(LIT64(0x7FF4000000000000));
@@ -203,7 +204,7 @@ float128 float128_default_nan(float_status *status)
         r.high = LIT64(0x7FFF7FFFFFFFFFFF);
     } else {
         r.low = LIT64(0x0000000000000000);
-#if defined(TARGET_S390X) || defined(TARGET_PPC)
+#if defined(TARGET_S390X) || defined(TARGET_PPC) || defined(TARGET_RISCV)
         r.high = LIT64(0x7FFF800000000000);
 #else
         r.high = LIT64(0xFFFF800000000000);
index e124df9f7e795ee35641f4201e45fa31152f0573..6e16284e66c277195e9076953326a4606c0d3209 100644 (file)
@@ -1342,6 +1342,8 @@ 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:
         return max;
     case float_class_inf:
         return p.sign ? min : max;
@@ -1430,6 +1432,8 @@ 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:
index 8e48500dd53a623e47aa27c5b37bdf05cc96fa86..6f132c5ff15a7f77667d32215502188e6f727460 100644 (file)
@@ -55,6 +55,7 @@ static struct option helper_opts[] = {
 
 static bool is_daemon;
 static bool get_version; /* IOC getversion IOCTL supported */
+static char *prog_name;
 
 static void GCC_FMT_ATTR(2, 3) do_log(int loglevel, const char *format, ...)
 {
@@ -785,7 +786,7 @@ error:
     return -1;
 }
 
-static void usage(char *prog)
+static void usage(void)
 {
     fprintf(stderr, "usage: %s\n"
             " -p|--path <path> 9p path to export\n"
@@ -795,7 +796,7 @@ static void usage(char *prog)
             " access to this socket\n"
             " \tNote: -s & -f can not be used together\n"
             " [-n|--nodaemon] Run as a normal program\n",
-            basename(prog));
+            prog_name);
 }
 
 static int process_reply(int sock, int type,
@@ -1045,6 +1046,8 @@ int main(int argc, char **argv)
     struct statfs st_fs;
 #endif
 
+    prog_name = g_path_get_basename(argv[0]);
+
     is_daemon = true;
     sock = -1;
     own_u = own_g = -1;
@@ -1077,7 +1080,7 @@ int main(int argc, char **argv)
         case '?':
         case 'h':
         default:
-            usage(argv[0]);
+            usage();
             exit(EXIT_FAILURE);
         }
     }
@@ -1085,13 +1088,13 @@ int main(int argc, char **argv)
     /* Parameter validation */
     if ((sock_name == NULL && sock == -1) || rpath == NULL) {
         fprintf(stderr, "socket, socket descriptor or path not specified\n");
-        usage(argv[0]);
+        usage();
         return -1;
     }
 
     if (sock_name && sock != -1) {
         fprintf(stderr, "both named socket and socket descriptor specified\n");
-        usage(argv[0]);
+        usage();
         exit(EXIT_FAILURE);
     }
 
@@ -1099,7 +1102,7 @@ int main(int argc, char **argv)
         fprintf(stderr, "owner uid:gid not specified, ");
         fprintf(stderr,
                 "owner uid:gid specifies who can access the socket file\n");
-        usage(argv[0]);
+        usage();
         exit(EXIT_FAILURE);
     }
 
index 2cb990997eab339e0fba07038de69d4992f9b8d7..101f32cf66fad6b435c683f2e1ca7f4ee9669324 100644 (file)
@@ -34,6 +34,7 @@ struct VirtIOBlockDataPlane {
     VirtIODevice *vdev;
     QEMUBH *bh;                     /* bh for guest notification */
     unsigned long *batch_notify_vqs;
+    bool batch_notifications;
 
     /* Note that these EventNotifiers are assigned by value.  This is
      * fine as long as you do not call event_notifier_cleanup on them
@@ -47,8 +48,12 @@ struct VirtIOBlockDataPlane {
 /* Raise an interrupt to signal guest, if necessary */
 void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq)
 {
-    set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs);
-    qemu_bh_schedule(s->bh);
+    if (s->batch_notifications) {
+        set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs);
+        qemu_bh_schedule(s->bh);
+    } else {
+        virtio_notify_irqfd(s->vdev, vq);
+    }
 }
 
 static void notify_guest_bh(void *opaque)
@@ -177,6 +182,12 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev)
 
     s->starting = true;
 
+    if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) {
+        s->batch_notifications = true;
+    } else {
+        s->batch_notifications = false;
+    }
+
     /* Set up guest notifier (irq) */
     r = k->set_guest_notifiers(qbus->parent, nvqs, true);
     if (r != 0) {
@@ -229,6 +240,22 @@ int virtio_blk_data_plane_start(VirtIODevice *vdev)
     return -ENOSYS;
 }
 
+/* Stop notifications for new requests from guest.
+ *
+ * Context: BH in IOThread
+ */
+static void virtio_blk_data_plane_stop_bh(void *opaque)
+{
+    VirtIOBlockDataPlane *s = opaque;
+    unsigned i;
+
+    for (i = 0; i < s->conf->num_queues; i++) {
+        VirtQueue *vq = virtio_get_queue(s->vdev, i);
+
+        virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL);
+    }
+}
+
 /* Context: QEMU global mutex held */
 void virtio_blk_data_plane_stop(VirtIODevice *vdev)
 {
@@ -253,13 +280,7 @@ void virtio_blk_data_plane_stop(VirtIODevice *vdev)
     trace_virtio_blk_data_plane_stop(s);
 
     aio_context_acquire(s->ctx);
-
-    /* Stop notifications for new requests from guest */
-    for (i = 0; i < nvqs; i++) {
-        VirtQueue *vq = virtio_get_queue(s->vdev, i);
-
-        virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL);
-    }
+    aio_wait_bh_oneshot(s->ctx, virtio_blk_data_plane_stop_bh, s);
 
     /* Drain and switch bs back to the QEMU main loop */
     blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
index c500bdaf29313146a2c9c94dcefa8d47a68e909a..dbc91a1e5b674fd098650ded3c1e2084ae1c47c0 100644 (file)
@@ -102,12 +102,12 @@ static bool can_handle_event(uint8_t type)
     return type == SCLP_EVENT_MESSAGE || type == SCLP_EVENT_PMSGCMD;
 }
 
-static unsigned int send_mask(void)
+static sccb_mask_t send_mask(void)
 {
     return SCLP_EVENT_MASK_OP_CMD | SCLP_EVENT_MASK_PMSGCMD;
 }
 
-static unsigned int receive_mask(void)
+static sccb_mask_t receive_mask(void)
 {
     return SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_PMSGCMD;
 }
@@ -318,11 +318,6 @@ static int console_init(SCLPEvent *event)
     return 0;
 }
 
-static int console_exit(SCLPEvent *event)
-{
-    return 0;
-}
-
 static void console_reset(DeviceState *dev)
 {
    SCLPEvent *event = SCLP_EVENT(dev);
@@ -349,7 +344,6 @@ static void console_class_init(ObjectClass *klass, void *data)
     dc->reset = console_reset;
     dc->vmsd = &vmstate_sclplmconsole;
     ec->init = console_init;
-    ec->exit = console_exit;
     ec->get_send_mask = send_mask;
     ec->get_receive_mask = receive_mask;
     ec->can_handle_event = can_handle_event;
index d0265dfa7ad94d2b142106a385dbe95c36a4e1a2..1fa16e90556ac082452fb84f24f413ff1711ef1e 100644 (file)
@@ -83,12 +83,12 @@ static bool can_handle_event(uint8_t type)
     return type == SCLP_EVENT_ASCII_CONSOLE_DATA;
 }
 
-static unsigned int send_mask(void)
+static sccb_mask_t send_mask(void)
 {
     return SCLP_EVENT_MASK_MSG_ASCII;
 }
 
-static unsigned int receive_mask(void)
+static sccb_mask_t receive_mask(void)
 {
     return SCLP_EVENT_MASK_MSG_ASCII;
 }
@@ -246,11 +246,6 @@ static void console_reset(DeviceState *dev)
    scon->notify = false;
 }
 
-static int console_exit(SCLPEvent *event)
-{
-    return 0;
-}
-
 static Property console_properties[] = {
     DEFINE_PROP_CHR("chardev", SCLPConsole, chr),
     DEFINE_PROP_END_OF_LIST(),
@@ -265,7 +260,6 @@ static void console_class_init(ObjectClass *klass, void *data)
     dc->reset = console_reset;
     dc->vmsd = &vmstate_sclpconsole;
     ec->init = console_init;
-    ec->exit = console_exit;
     ec->get_send_mask = send_mask;
     ec->get_receive_mask = receive_mask;
     ec->can_handle_event = can_handle_event;
index 76b244c5086ada8be941bfd85754e40858e47e5c..06bdbca53709050609d6043570810520e2368a58 100644 (file)
@@ -449,6 +449,20 @@ int load_elf_ram(const char *filename,
                  uint64_t *highaddr, int big_endian, int elf_machine,
                  int clear_lsb, int data_swab, AddressSpace *as,
                  bool load_rom)
+{
+    return load_elf_ram_sym(filename, translate_fn, translate_opaque,
+                            pentry, lowaddr, highaddr, big_endian,
+                            elf_machine, clear_lsb, data_swab, as,
+                            load_rom, NULL);
+}
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf_ram_sym(const char *filename,
+                     uint64_t (*translate_fn)(void *, uint64_t),
+                     void *translate_opaque, uint64_t *pentry,
+                     uint64_t *lowaddr, uint64_t *highaddr, int big_endian,
+                     int elf_machine, int clear_lsb, int data_swab,
+                     AddressSpace *as, bool load_rom, symbol_fn_t sym_cb)
 {
     int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
     uint8_t e_ident[EI_NIDENT];
@@ -488,11 +502,11 @@ int load_elf_ram(const char *filename,
     if (e_ident[EI_CLASS] == ELFCLASS64) {
         ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
                          pentry, lowaddr, highaddr, elf_machine, clear_lsb,
-                         data_swab, as, load_rom);
+                         data_swab, as, load_rom, sym_cb);
     } else {
         ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
                          pentry, lowaddr, highaddr, elf_machine, clear_lsb,
-                         data_swab, as, load_rom);
+                         data_swab, as, load_rom, sym_cb);
     }
 
  fail:
index 819f8be05d145d682ff2c5a78c298c22b036fea6..3d75394e7778ad14a86a164712875b6b07b4f4cc 100644 (file)
@@ -207,6 +207,7 @@ done:
     if (xmax || ymax) {
         dpy_gfx_update(s->con, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
     }
+    g_free(snap);
 }
 
 static void g364fb_draw_blank(G364State *s)
index 46d9c68bf5baa912f467c5d89991807c751ed691..b9064264d83564b1a8442523003b9d0f2a35e61b 100644 (file)
 #include "hw/loader.h"
 #include "elf.h"
 #include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
 
 /* Show multiboot debug output */
 //#define DEBUG_MULTIBOOT
 
 #ifdef DEBUG_MULTIBOOT
-#define mb_debug(a...) fprintf(stderr, ## a)
+#define mb_debug(a...) error_report(a)
 #else
 #define mb_debug(a...)
 #endif
@@ -137,7 +138,7 @@ static void mb_add_mod(MultibootState *s,
     stl_p(p + MB_MOD_END,     end);
     stl_p(p + MB_MOD_CMDLINE, cmdline_phys);
 
-    mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx"\n",
+    mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx,
              s->mb_mods_count, start, end);
 
     s->mb_mods_count++;
@@ -179,12 +180,12 @@ int load_multiboot(FWCfgState *fw_cfg,
     if (!is_multiboot)
         return 0; /* no multiboot */
 
-    mb_debug("qemu: I believe we found a multiboot image!\n");
+    mb_debug("qemu: I believe we found a multiboot image!");
     memset(bootinfo, 0, sizeof(bootinfo));
     memset(&mbs, 0, sizeof(mbs));
 
     if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */
-        fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n");
+        error_report("qemu: multiboot knows VBE. we don't.");
     }
     if (!(flags & 0x00010000)) { /* MULTIBOOT_HEADER_HAS_ADDR */
         uint64_t elf_entry;
@@ -193,7 +194,7 @@ int load_multiboot(FWCfgState *fw_cfg,
         fclose(f);
 
         if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) {
-            fprintf(stderr, "Cannot load x86-64 image, give a 32bit one.\n");
+            error_report("Cannot load x86-64 image, give a 32bit one.");
             exit(1);
         }
 
@@ -201,7 +202,7 @@ int load_multiboot(FWCfgState *fw_cfg,
                                &elf_low, &elf_high, 0, I386_ELF_MACHINE,
                                0, 0);
         if (kernel_size < 0) {
-            fprintf(stderr, "Error while loading elf kernel\n");
+            error_report("Error while loading elf kernel");
             exit(1);
         }
         mh_load_addr = elf_low;
@@ -210,12 +211,13 @@ int load_multiboot(FWCfgState *fw_cfg,
 
         mbs.mb_buf = g_malloc(mb_kernel_size);
         if (rom_copy(mbs.mb_buf, mh_load_addr, mb_kernel_size) != mb_kernel_size) {
-            fprintf(stderr, "Error while fetching elf kernel from rom\n");
+            error_report("Error while fetching elf kernel from rom");
             exit(1);
         }
 
-        mb_debug("qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n",
-                  mb_kernel_size, (size_t)mh_entry_addr);
+        mb_debug("qemu: loading multiboot-elf kernel "
+                 "(%#x bytes) with entry %#zx",
+                 mb_kernel_size, (size_t)mh_entry_addr);
     } else {
         /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */
         uint32_t mh_header_addr = ldl_p(header+i+12);
@@ -224,7 +226,7 @@ int load_multiboot(FWCfgState *fw_cfg,
 
         mh_load_addr = ldl_p(header+i+16);
         if (mh_header_addr < mh_load_addr) {
-            fprintf(stderr, "invalid mh_load_addr address\n");
+            error_report("invalid load_addr address");
             exit(1);
         }
 
@@ -233,43 +235,39 @@ int load_multiboot(FWCfgState *fw_cfg,
         mh_entry_addr = ldl_p(header+i+28);
 
         if (mh_load_end_addr) {
-            if (mh_bss_end_addr < mh_load_addr) {
-                fprintf(stderr, "invalid mh_bss_end_addr address\n");
-                exit(1);
-            }
-            mb_kernel_size = mh_bss_end_addr - mh_load_addr;
-
             if (mh_load_end_addr < mh_load_addr) {
-                fprintf(stderr, "invalid mh_load_end_addr address\n");
+                error_report("invalid load_end_addr address");
                 exit(1);
             }
             mb_load_size = mh_load_end_addr - mh_load_addr;
         } else {
             if (kernel_file_size < mb_kernel_text_offset) {
-                fprintf(stderr, "invalid kernel_file_size\n");
+                error_report("invalid kernel_file_size");
                 exit(1);
             }
-            mb_kernel_size = kernel_file_size - mb_kernel_text_offset;
-            mb_load_size = mb_kernel_size;
+            mb_load_size = kernel_file_size - mb_kernel_text_offset;
+        }
+        if (mh_bss_end_addr) {
+            if (mh_bss_end_addr < (mh_load_addr + mb_load_size)) {
+                error_report("invalid bss_end_addr address");
+                exit(1);
+            }
+            mb_kernel_size = mh_bss_end_addr - mh_load_addr;
+        } else {
+            mb_kernel_size = mb_load_size;
         }
 
-        /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE.
-        uint32_t mh_mode_type = ldl_p(header+i+32);
-        uint32_t mh_width = ldl_p(header+i+36);
-        uint32_t mh_height = ldl_p(header+i+40);
-        uint32_t mh_depth = ldl_p(header+i+44); */
-
-        mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr);
-        mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr);
-        mb_debug("multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr);
-        mb_debug("multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr);
-        mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n",
+        mb_debug("multiboot: header_addr = %#x", mh_header_addr);
+        mb_debug("multiboot: load_addr = %#x", mh_load_addr);
+        mb_debug("multiboot: load_end_addr = %#x", mh_load_end_addr);
+        mb_debug("multiboot: bss_end_addr = %#x", mh_bss_end_addr);
+        mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x",
                  mb_load_size, mh_load_addr);
 
         mbs.mb_buf = g_malloc(mb_kernel_size);
         fseek(f, mb_kernel_text_offset, SEEK_SET);
         if (fread(mbs.mb_buf, 1, mb_load_size, f) != mb_load_size) {
-            fprintf(stderr, "fread() failed\n");
+            error_report("fread() failed");
             exit(1);
         }
         memset(mbs.mb_buf + mb_load_size, 0, mb_kernel_size - mb_load_size);
@@ -323,10 +321,10 @@ int load_multiboot(FWCfgState *fw_cfg,
             hwaddr c = mb_add_cmdline(&mbs, tmpbuf);
             if ((next_space = strchr(tmpbuf, ' ')))
                 *next_space = '\0';
-            mb_debug("multiboot loading module: %s\n", tmpbuf);
+            mb_debug("multiboot loading module: %s", tmpbuf);
             mb_mod_length = get_image_size(tmpbuf);
             if (mb_mod_length < 0) {
-                fprintf(stderr, "Failed to open file '%s'\n", tmpbuf);
+                error_report("Failed to open file '%s'", tmpbuf);
                 exit(1);
             }
 
@@ -337,7 +335,7 @@ int load_multiboot(FWCfgState *fw_cfg,
             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"\n",
+            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;
@@ -365,10 +363,11 @@ int load_multiboot(FWCfgState *fw_cfg,
     stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8000ffff); /* XXX: use the -boot switch? */
     stl_p(bootinfo + MBI_MMAP_ADDR,   ADDR_E820_MAP);
 
-    mb_debug("multiboot: mh_entry_addr = %#x\n", mh_entry_addr);
-    mb_debug("           mb_buf_phys   = "TARGET_FMT_plx"\n", mbs.mb_buf_phys);
-    mb_debug("           mod_start     = "TARGET_FMT_plx"\n", mbs.mb_buf_phys + mbs.offset_mods);
-    mb_debug("           mb_mods_count = %d\n", mbs.mb_mods_count);
+    mb_debug("multiboot: entry_addr = %#x", mh_entry_addr);
+    mb_debug("           mb_buf_phys   = "TARGET_FMT_plx, mbs.mb_buf_phys);
+    mb_debug("           mod_start     = "TARGET_FMT_plx,
+             mbs.mb_buf_phys + mbs.offset_mods);
+    mb_debug("           mb_mods_count = %d", mbs.mb_mods_count);
 
     /* save bootinfo off the stack */
     mb_bootinfo_data = g_memdup(bootinfo, sizeof(bootinfo));
index 94cfd40ef2c8d9fe954316c529c2f8eb11dd263e..35fcb6efdfb9ebeb515ca223883b721f08be6a3f 100644 (file)
@@ -1636,23 +1636,6 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
     rom_reset_order_override();
 }
 
-void pc_pci_device_init(PCIBus *pci_bus)
-{
-    int max_bus;
-    int bus;
-
-    /* Note: if=scsi is deprecated with PC machine types */
-    max_bus = drive_get_max_bus(IF_SCSI);
-    for (bus = 0; bus <= max_bus; bus++) {
-        pci_create_simple(pci_bus, -1, "lsi53c895a");
-        /*
-         * By not creating frontends here, we make
-         * scsi_legacy_handle_cmdline() create them, and warn that
-         * this usage is deprecated.
-         */
-    }
-}
-
 void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
 {
     DeviceState *dev;
index 456dc9e9f0647f47a947adbf42769b39d3b9ecc5..8658bcba63cb023cd09db7d67f8188eee9e8a6b3 100644 (file)
@@ -295,10 +295,6 @@ static void pc_init1(MachineState *machine,
                                  PC_MACHINE_ACPI_DEVICE_PROP, &error_abort);
     }
 
-    if (pcmc->pci_enabled) {
-        pc_pci_device_init(pci_bus);
-    }
-
     if (pcms->acpi_nvdimm_state.is_enabled) {
         nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
                                pcms->fw_cfg, OBJECT(pcms));
index aba7541a82bb7fedcbe1b86778ab89592ca270a0..0c0bc481379982325d86ff4b06900136c98a3958 100644 (file)
@@ -273,9 +273,6 @@ static void pc_q35_init(MachineState *machine)
     /* the rest devices to which pci devfn is automatically assigned */
     pc_vga_init(isa_bus, host_bus);
     pc_nic_init(isa_bus, host_bus);
-    if (pcmc->pci_enabled) {
-        pc_pci_device_init(host_bus);
-    }
 
     if (pcms->acpi_nvdimm_state.is_enabled) {
         nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io,
index 257b429381e011c43f95b4a85de3f34530e53499..139c843514994ceac150e2cb397be45c1f83c98b 100644 (file)
@@ -1087,15 +1087,7 @@ static void ide_flush_cache(IDEState *s)
     s->status |= BUSY_STAT;
     ide_set_retry(s);
     block_acct_start(blk_get_stats(s->blk), &s->acct, 0, BLOCK_ACCT_FLUSH);
-
-    if (blk_bs(s->blk)) {
-        s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s);
-    } else {
-        /* XXX blk_aio_flush() crashes when blk_bs(blk) is NULL, remove this
-         * temporary workaround when blk_aio_*() functions handle NULL blk_bs.
-         */
-        ide_flush_cb(s, 0);
-    }
+    s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s);
 }
 
 static void ide_cfata_metadata_inquiry(IDEState *s)
index f1a59e5a8519d9a56a1b5025fac242c2ba961e88..928bc04a4eb85b3d0c2fc4fb720da2bcffcb3d3a 100644 (file)
@@ -125,10 +125,6 @@ static void kvm_openpic_region_add(MemoryListener *listener,
     uint64_t reg_base;
     int ret;
 
-    if (section->fv != address_space_to_flatview(&address_space_memory)) {
-        abort();
-    }
-
     /* Ignore events on regions that are not us */
     if (section->mr != &opp->mem) {
         return;
index eb5ffcc0a872d025ecf5174816b47656de68c15f..562d9ed0059d6592322306844ae8708537c0cb24 100644 (file)
@@ -92,8 +92,8 @@ tz_ppc_cfg_sec_resp(int level) "TZ PPC: cfg_sec_resp = %d"
 tz_ppc_irq_enable(int level) "TZ PPC: int_enable = %d"
 tz_ppc_irq_clear(int level) "TZ PPC: int_clear = %d"
 tz_ppc_update_irq(int level) "TZ PPC: setting irq line to %d"
-tz_ppc_read_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " read (secure %d user %d) blocked"
-tz_ppc_write_blocked(int n, hwaddr offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" HWADDR_PRIx " write (secure %d user %d) blocked"
+tz_ppc_read_blocked(int n, uint64_t offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" PRIx64 " read (secure %d user %d) blocked"
+tz_ppc_write_blocked(int n, uint64_t offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" PRIx64 " write (secure %d user %d) blocked"
 
 # hw/misc/iotkit-secctl.c
 iotkit_secctl_s_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs read: offset 0x%x data 0x%" PRIx64 " size %u"
diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs
new file mode 100644 (file)
index 0000000..1dde01d
--- /dev/null
@@ -0,0 +1,11 @@
+obj-y += riscv_htif.o
+obj-y += riscv_hart.o
+obj-y += sifive_e.o
+obj-y += sifive_clint.o
+obj-y += sifive_prci.o
+obj-y += sifive_plic.o
+obj-y += sifive_test.o
+obj-y += sifive_u.o
+obj-y += sifive_uart.o
+obj-y += spike.o
+obj-y += virt.o
diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c
new file mode 100644 (file)
index 0000000..14e3c18
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * QEMU RISCV Hart Array
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Holds the state of a heterogenous array of RISC-V harts
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+
+static Property riscv_harts_props[] = {
+    DEFINE_PROP_UINT32("num-harts", RISCVHartArrayState, num_harts, 1),
+    DEFINE_PROP_STRING("cpu-type", RISCVHartArrayState, cpu_type),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void riscv_harts_cpu_reset(void *opaque)
+{
+    RISCVCPU *cpu = opaque;
+    cpu_reset(CPU(cpu));
+}
+
+static void riscv_harts_realize(DeviceState *dev, Error **errp)
+{
+    RISCVHartArrayState *s = RISCV_HART_ARRAY(dev);
+    Error *err = NULL;
+    int n;
+
+    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);
+        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);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+    }
+}
+
+static void riscv_harts_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = riscv_harts_props;
+    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,
+};
+
+static void riscv_harts_register_types(void)
+{
+    type_register_static(&riscv_harts_info);
+}
+
+type_init(riscv_harts_register_types)
diff --git a/hw/riscv/riscv_htif.c b/hw/riscv/riscv_htif.c
new file mode 100644 (file)
index 0000000..3e17f30
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * QEMU RISC-V Host Target Interface (HTIF) Emulation
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This provides HTIF device emulation for QEMU. At the moment this allows
+ * for identical copies of bbl/linux to run on both spike and QEMU.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "chardev/char.h"
+#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
+#define HTIF_DEBUG(fmt, ...)                                                   \
+    do {                                                                       \
+        if (RISCV_DEBUG_HTIF) {                                                \
+            qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\
+        }                                                                      \
+    } while (0)
+
+static uint64_t fromhost_addr, tohost_addr;
+
+void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
+    uint64_t st_size)
+{
+    if (strcmp("fromhost", st_name) == 0) {
+        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) {
+        tohost_addr = st_value;
+        if (st_size != 8) {
+            error_report("HTIF tohost must be 8 bytes");
+            exit(1);
+        }
+    }
+}
+
+/*
+ * Called by the char dev to see if HTIF is ready to accept input.
+ */
+static int htif_can_recv(void *opaque)
+{
+    return 1;
+}
+
+/*
+ * Called by the char dev to supply input to HTIF console.
+ * We assume that we will receive one character at a time.
+ */
+static void htif_recv(void *opaque, const uint8_t *buf, int size)
+{
+    HTIFState *htifstate = opaque;
+
+    if (size != 1) {
+        return;
+    }
+
+    /* TODO - we need to check whether mfromhost is zero which indicates
+              the device is ready to receive. The current implementation
+              will drop characters */
+
+    uint64_t val_written = htifstate->pending_read;
+    uint64_t resp = 0x100 | *buf;
+
+    htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
+}
+
+/*
+ * Called by the char dev to supply special events to the HTIF console.
+ * Not used for HTIF.
+ */
+static void htif_event(void *opaque, int event)
+{
+
+}
+
+static int htif_be_change(void *opaque)
+{
+    HTIFState *s = opaque;
+
+    qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
+        htif_be_change, s, NULL, true);
+
+    return 0;
+}
+
+static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written)
+{
+    uint8_t device = val_written >> 56;
+    uint8_t cmd = val_written >> 48;
+    uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;
+    int resp = 0;
+
+    HTIF_DEBUG("mtohost write: device: %d cmd: %d what: %02" PRIx64
+        " -payload: %016" PRIx64 "\n", device, cmd, payload & 0xFF, payload);
+
+    /*
+     * Currently, there is a fixed mapping of devices:
+     * 0: riscv-tests Pass/Fail Reporting Only (no syscall proxy)
+     * 1: Console
+     */
+    if (unlikely(device == 0x0)) {
+        /* frontend syscall handler, shutdown and exit code support */
+        if (cmd == 0x0) {
+            if (payload & 0x1) {
+                /* exit code */
+                int exit_code = payload >> 1;
+                exit(exit_code);
+            } else {
+                qemu_log_mask(LOG_UNIMP, "pk syscall proxy not supported\n");
+            }
+        } else {
+            qemu_log("HTIF device %d: unknown command\n", device);
+        }
+    } else if (likely(device == 0x1)) {
+        /* HTIF Console */
+        if (cmd == 0x0) {
+            /* this should be a queue, but not yet implemented as such */
+            htifstate->pending_read = val_written;
+            htifstate->env->mtohost = 0; /* clear to indicate we read */
+            return;
+        } else if (cmd == 0x1) {
+            qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1);
+            resp = 0x100 | (uint8_t)payload;
+        } else {
+            qemu_log("HTIF device %d: unknown command\n", device);
+        }
+    } else {
+        qemu_log("HTIF unknown device or command\n");
+        HTIF_DEBUG("device: %d cmd: %d what: %02" PRIx64
+            " payload: %016" PRIx64, device, cmd, payload & 0xFF, payload);
+    }
+    /*
+     * - latest bbl does not set fromhost to 0 if there is a value in tohost
+     * - with this code enabled, qemu hangs waiting for fromhost to go to 0
+     * - with this code disabled, qemu works with bbl priv v1.9.1 and v1.10
+     * - HTIF needs protocol documentation and a more complete state machine
+
+        while (!htifstate->fromhost_inprogress &&
+            htifstate->env->mfromhost != 0x0) {
+        }
+    */
+    htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
+    htifstate->env->mtohost = 0; /* clear to indicate we read */
+}
+
+#define TOHOST_OFFSET1 (htifstate->tohost_offset)
+#define TOHOST_OFFSET2 (htifstate->tohost_offset + 4)
+#define FROMHOST_OFFSET1 (htifstate->fromhost_offset)
+#define FROMHOST_OFFSET2 (htifstate->fromhost_offset + 4)
+
+/* CPU wants to read an HTIF register */
+static uint64_t htif_mm_read(void *opaque, hwaddr addr, unsigned size)
+{
+    HTIFState *htifstate = opaque;
+    if (addr == TOHOST_OFFSET1) {
+        return htifstate->env->mtohost & 0xFFFFFFFF;
+    } else if (addr == TOHOST_OFFSET2) {
+        return (htifstate->env->mtohost >> 32) & 0xFFFFFFFF;
+    } else if (addr == FROMHOST_OFFSET1) {
+        return htifstate->env->mfromhost & 0xFFFFFFFF;
+    } else if (addr == FROMHOST_OFFSET2) {
+        return (htifstate->env->mfromhost >> 32) & 0xFFFFFFFF;
+    } else {
+        qemu_log("Invalid htif read: address %016" PRIx64 "\n",
+            (uint64_t)addr);
+        return 0;
+    }
+}
+
+/* CPU wrote to an HTIF register */
+static void htif_mm_write(void *opaque, hwaddr addr,
+                            uint64_t value, unsigned size)
+{
+    HTIFState *htifstate = opaque;
+    if (addr == TOHOST_OFFSET1) {
+        if (htifstate->env->mtohost == 0x0) {
+            htifstate->allow_tohost = 1;
+            htifstate->env->mtohost = value & 0xFFFFFFFF;
+        } else {
+            htifstate->allow_tohost = 0;
+        }
+    } else if (addr == TOHOST_OFFSET2) {
+        if (htifstate->allow_tohost) {
+            htifstate->env->mtohost |= value << 32;
+            htif_handle_tohost_write(htifstate, htifstate->env->mtohost);
+        }
+    } else if (addr == FROMHOST_OFFSET1) {
+        htifstate->fromhost_inprogress = 1;
+        htifstate->env->mfromhost = value & 0xFFFFFFFF;
+    } else if (addr == FROMHOST_OFFSET2) {
+        htifstate->env->mfromhost |= value << 32;
+        htifstate->fromhost_inprogress = 0;
+    } else {
+        qemu_log("Invalid htif write: address %016" PRIx64 "\n",
+            (uint64_t)addr);
+    }
+}
+
+static const MemoryRegionOps htif_mm_ops = {
+    .read = htif_mm_read,
+    .write = htif_mm_write,
+};
+
+HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
+    CPURISCVState *env, Chardev *chr)
+{
+    uint64_t base = MIN(tohost_addr, fromhost_addr);
+    uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base;
+    uint64_t tohost_offset = tohost_addr - base;
+    uint64_t fromhost_offset = fromhost_addr - base;
+
+    HTIFState *s = g_malloc0(sizeof(HTIFState));
+    s->address_space = address_space;
+    s->main_mem = main_mem;
+    s->main_mem_ram_ptr = memory_region_get_ram_ptr(main_mem);
+    s->env = env;
+    s->tohost_offset = tohost_offset;
+    s->fromhost_offset = fromhost_offset;
+    s->pending_read = 0;
+    s->allow_tohost = 0;
+    s->fromhost_inprogress = 0;
+    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) {
+        memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
+                            TYPE_HTIF_UART, size);
+        memory_region_add_subregion(address_space, base, &s->mmio);
+    }
+
+    return s;
+}
diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c
new file mode 100644 (file)
index 0000000..4893453
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * SiFive CLINT (Core Local Interruptor)
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides real-time clock, timer and interprocessor interrupts.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#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);
+}
+
+/*
+ * Called when timecmp is written to update the QEMU timer or immediately
+ * trigger timer interrupt if mtimecmp <= current timer value.
+ */
+static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value)
+{
+    uint64_t next;
+    uint64_t diff;
+
+    uint64_t rtc_r = cpu_riscv_read_rtc();
+
+    cpu->env.timecmp = value;
+    if (cpu->env.timecmp <= rtc_r) {
+        /* if we're setting an MTIMECMP value in the "past",
+           immediately raise the timer interrupt */
+        riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
+        return;
+    }
+
+    /* otherwise, set up the future timer interrupt */
+    riscv_set_local_interrupt(cpu, MIP_MTIP, 0);
+    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);
+    timer_mod(cpu->env.timer, next);
+}
+
+/*
+ * Callback used when the timer set using timer_mod expires.
+ * Should raise the timer interrupt line
+ */
+static void sifive_clint_timer_cb(void *opaque)
+{
+    RISCVCPU *cpu = opaque;
+    riscv_set_local_interrupt(cpu, MIP_MTIP, 1);
+}
+
+/* CPU wants to read rtc or timecmp register */
+static uint64_t sifive_clint_read(void *opaque, hwaddr addr, unsigned size)
+{
+    SiFiveCLINTState *clint = opaque;
+    if (addr >= clint->sip_base &&
+        addr < clint->sip_base + (clint->num_harts << 2)) {
+        size_t hartid = (addr - clint->sip_base) >> 2;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            error_report("clint: invalid timecmp hartid: %zu", hartid);
+        } else if ((addr & 0x3) == 0) {
+            return (env->mip & MIP_MSIP) > 0;
+        } else {
+            error_report("clint: invalid read: %08x", (uint32_t)addr);
+            return 0;
+        }
+    } else if (addr >= clint->timecmp_base &&
+        addr < clint->timecmp_base + (clint->num_harts << 3)) {
+        size_t hartid = (addr - clint->timecmp_base) >> 3;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            error_report("clint: invalid timecmp hartid: %zu", hartid);
+        } else if ((addr & 0x7) == 0) {
+            /* timecmp_lo */
+            uint64_t timecmp = env->timecmp;
+            return timecmp & 0xFFFFFFFF;
+        } else if ((addr & 0x7) == 4) {
+            /* timecmp_hi */
+            uint64_t timecmp = env->timecmp;
+            return (timecmp >> 32) & 0xFFFFFFFF;
+        } else {
+            error_report("clint: invalid read: %08x", (uint32_t)addr);
+            return 0;
+        }
+    } else if (addr == clint->time_base) {
+        /* time_lo */
+        return cpu_riscv_read_rtc() & 0xFFFFFFFF;
+    } else if (addr == clint->time_base + 4) {
+        /* time_hi */
+        return (cpu_riscv_read_rtc() >> 32) & 0xFFFFFFFF;
+    }
+
+    error_report("clint: invalid read: %08x", (uint32_t)addr);
+    return 0;
+}
+
+/* CPU wrote to rtc or timecmp register */
+static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value,
+        unsigned size)
+{
+    SiFiveCLINTState *clint = opaque;
+
+    if (addr >= clint->sip_base &&
+        addr < clint->sip_base + (clint->num_harts << 2)) {
+        size_t hartid = (addr - clint->sip_base) >> 2;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            error_report("clint: invalid timecmp hartid: %zu", hartid);
+        } else if ((addr & 0x3) == 0) {
+            riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MSIP, value != 0);
+        } else {
+            error_report("clint: invalid sip write: %08x", (uint32_t)addr);
+        }
+        return;
+    } else if (addr >= clint->timecmp_base &&
+        addr < clint->timecmp_base + (clint->num_harts << 3)) {
+        size_t hartid = (addr - clint->timecmp_base) >> 3;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            error_report("clint: invalid timecmp hartid: %zu", hartid);
+        } else if ((addr & 0x7) == 0) {
+            /* timecmp_lo */
+            uint64_t timecmp = env->timecmp;
+            sifive_clint_write_timecmp(RISCV_CPU(cpu),
+                timecmp << 32 | (value & 0xFFFFFFFF));
+            return;
+        } else if ((addr & 0x7) == 4) {
+            /* timecmp_hi */
+            uint64_t timecmp = env->timecmp;
+            sifive_clint_write_timecmp(RISCV_CPU(cpu),
+                value << 32 | (timecmp & 0xFFFFFFFF));
+        } else {
+            error_report("clint: invalid timecmp write: %08x", (uint32_t)addr);
+        }
+        return;
+    } else if (addr == clint->time_base) {
+        /* time_lo */
+        error_report("clint: time_lo write not implemented");
+        return;
+    } else if (addr == clint->time_base + 4) {
+        /* time_hi */
+        error_report("clint: time_hi write not implemented");
+        return;
+    }
+
+    error_report("clint: invalid write: %08x", (uint32_t)addr);
+}
+
+static const MemoryRegionOps sifive_clint_ops = {
+    .read = sifive_clint_read,
+    .write = sifive_clint_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static Property sifive_clint_properties[] = {
+    DEFINE_PROP_UINT32("num-harts", SiFiveCLINTState, num_harts, 0),
+    DEFINE_PROP_UINT32("sip-base", SiFiveCLINTState, sip_base, 0),
+    DEFINE_PROP_UINT32("timecmp-base", SiFiveCLINTState, timecmp_base, 0),
+    DEFINE_PROP_UINT32("time-base", SiFiveCLINTState, time_base, 0),
+    DEFINE_PROP_UINT32("aperture-size", SiFiveCLINTState, aperture_size, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void sifive_clint_realize(DeviceState *dev, Error **errp)
+{
+    SiFiveCLINTState *s = SIFIVE_CLINT(dev);
+    memory_region_init_io(&s->mmio, OBJECT(dev), &sifive_clint_ops, s,
+                          TYPE_SIFIVE_CLINT, s->aperture_size);
+    sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mmio);
+}
+
+static void sifive_clint_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    dc->realize = sifive_clint_realize;
+    dc->props = sifive_clint_properties;
+}
+
+static const TypeInfo sifive_clint_info = {
+    .name          = TYPE_SIFIVE_CLINT,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFiveCLINTState),
+    .class_init    = sifive_clint_class_init,
+};
+
+static void sifive_clint_register_types(void)
+{
+    type_register_static(&sifive_clint_info);
+}
+
+type_init(sifive_clint_register_types)
+
+
+/*
+ * Create CLINT device.
+ */
+DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts,
+    uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base)
+{
+    int i;
+    for (i = 0; i < num_harts; i++) {
+        CPUState *cpu = qemu_get_cpu(i);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            continue;
+        }
+        env->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
+                                  &sifive_clint_timer_cb, cpu);
+        env->timecmp = 0;
+    }
+
+    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_CLINT);
+    qdev_prop_set_uint32(dev, "num-harts", num_harts);
+    qdev_prop_set_uint32(dev, "sip-base", sip_base);
+    qdev_prop_set_uint32(dev, "timecmp-base", timecmp_base);
+    qdev_prop_set_uint32(dev, "time-base", time_base);
+    qdev_prop_set_uint32(dev, "aperture-size", size);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+    return dev;
+}
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
new file mode 100644 (file)
index 0000000..19eca36
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * QEMU RISC-V Board Compatible with SiFive Freedom E SDK
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Provides a board compatible with the SiFive Freedom E SDK:
+ *
+ * 0) UART
+ * 1) CLINT (Core Level Interruptor)
+ * 2) PLIC (Platform Level Interrupt Controller)
+ * 3) PRCI (Power, Reset, Clock, Interrupt)
+ * 4) Registers emulated as RAM: AON, GPIO, QSPI, PWM
+ * 5) Flash memory emulated as RAM
+ *
+ * The Mask ROM reset vector jumps to the flash payload at 0x2040_0000.
+ * The OTP ROM and Flash boot code will be emulated in a future version.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_plic.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/sifive_prci.h"
+#include "hw/riscv/sifive_uart.h"
+#include "hw/riscv/sifive_e.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} sifive_e_memmap[] = {
+    [SIFIVE_E_DEBUG] =    {        0x0,      0x100 },
+    [SIFIVE_E_MROM] =     {     0x1000,     0x2000 },
+    [SIFIVE_E_OTP] =      {    0x20000,     0x2000 },
+    [SIFIVE_E_CLINT] =    {  0x2000000,    0x10000 },
+    [SIFIVE_E_PLIC] =     {  0xc000000,  0x4000000 },
+    [SIFIVE_E_AON] =      { 0x10000000,     0x8000 },
+    [SIFIVE_E_PRCI] =     { 0x10008000,     0x8000 },
+    [SIFIVE_E_OTP_CTRL] = { 0x10010000,     0x1000 },
+    [SIFIVE_E_GPIO0] =    { 0x10012000,     0x1000 },
+    [SIFIVE_E_UART0] =    { 0x10013000,     0x1000 },
+    [SIFIVE_E_QSPI0] =    { 0x10014000,     0x1000 },
+    [SIFIVE_E_PWM0] =     { 0x10015000,     0x1000 },
+    [SIFIVE_E_UART1] =    { 0x10023000,     0x1000 },
+    [SIFIVE_E_QSPI1] =    { 0x10024000,     0x1000 },
+    [SIFIVE_E_PWM1] =     { 0x10025000,     0x1000 },
+    [SIFIVE_E_QSPI2] =    { 0x10034000,     0x1000 },
+    [SIFIVE_E_PWM2] =     { 0x10035000,     0x1000 },
+    [SIFIVE_E_XIP] =      { 0x20000000, 0x20000000 },
+    [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,
+                 &kernel_entry, NULL, &kernel_high,
+                 0, ELF_MACHINE, 1, 0) < 0) {
+        error_report("qemu: could not load kernel '%s'", kernel_filename);
+        exit(1);
+    }
+    return kernel_entry;
+}
+
+static void sifive_mmio_emulate(MemoryRegion *parent, const char *name,
+                             uintptr_t offset, uintptr_t length)
+{
+    MemoryRegion *mock_mmio = g_new(MemoryRegion, 1);
+    memory_region_init_ram(mock_mmio, NULL, name, length, &error_fatal);
+    memory_region_add_subregion(parent, offset, mock_mmio);
+}
+
+static void riscv_sifive_e_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = sifive_e_memmap;
+
+    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);
+
+    /* 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);
+    object_property_set_bool(OBJECT(&s->soc), true, "realized",
+                            &error_abort);
+
+    /* Data Tightly Integrated Memory */
+    memory_region_init_ram(main_mem, NULL, "riscv.sifive.e.ram",
+        memmap[SIFIVE_E_DTIM].size, &error_fatal);
+    memory_region_add_subregion(sys_mem,
+        memmap[SIFIVE_E_DTIM].base, main_mem);
+
+    /* Mask ROM */
+    memory_region_init_ram(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);
+
+    /* MMIO */
+    s->plic = sifive_plic_create(memmap[SIFIVE_E_PLIC].base,
+        (char *)SIFIVE_E_PLIC_HART_CONFIG,
+        SIFIVE_E_PLIC_NUM_SOURCES,
+        SIFIVE_E_PLIC_NUM_PRIORITIES,
+        SIFIVE_E_PLIC_PRIORITY_BASE,
+        SIFIVE_E_PLIC_PENDING_BASE,
+        SIFIVE_E_PLIC_ENABLE_BASE,
+        SIFIVE_E_PLIC_ENABLE_STRIDE,
+        SIFIVE_E_PLIC_CONTEXT_BASE,
+        SIFIVE_E_PLIC_CONTEXT_STRIDE,
+        memmap[SIFIVE_E_PLIC].size);
+    sifive_clint_create(memmap[SIFIVE_E_CLINT].base,
+        memmap[SIFIVE_E_CLINT].size, smp_cpus,
+        SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.aon",
+        memmap[SIFIVE_E_AON].base, memmap[SIFIVE_E_AON].size);
+    sifive_prci_create(memmap[SIFIVE_E_PRCI].base);
+    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]);
+    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]); */
+    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",
+        memmap[SIFIVE_E_PWM1].base, memmap[SIFIVE_E_PWM1].size);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi2",
+        memmap[SIFIVE_E_QSPI2].base, memmap[SIFIVE_E_QSPI2].size);
+    sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm2",
+        memmap[SIFIVE_E_PWM2].base, memmap[SIFIVE_E_PWM2].size);
+
+    /* Flash memory */
+    memory_region_init_ram(xip_mem, NULL, "riscv.sifive.e.xip",
+        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";
+    mc->init = riscv_sifive_e_init;
+    mc->max_cpus = 1;
+}
+
+DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init)
+
+static void riscv_sifive_e_register_types(void)
+{
+    type_register_static(&riscv_sifive_e_device);
+}
+
+type_init(riscv_sifive_e_register_types);
diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c
new file mode 100644 (file)
index 0000000..874de2e
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * SiFive PLIC (Platform Level Interrupt Controller)
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides a parameterizable interrupt controller based on SiFive's PLIC.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_plic.h"
+
+#define RISCV_DEBUG_PLIC 0
+
+static PLICMode char_to_mode(char c)
+{
+    switch (c) {
+    case 'U': return PLICMode_U;
+    case 'S': return PLICMode_S;
+    case 'H': return PLICMode_H;
+    case 'M': return PLICMode_M;
+    default:
+        error_report("plic: invalid mode '%c'", c);
+        exit(1);
+    }
+}
+
+static char mode_to_char(PLICMode m)
+{
+    switch (m) {
+    case PLICMode_U: return 'U';
+    case PLICMode_S: return 'S';
+    case PLICMode_H: return 'H';
+    case PLICMode_M: return 'M';
+    default: return '?';
+    }
+}
+
+static void sifive_plic_print_state(SiFivePLICState *plic)
+{
+    int i;
+    int addrid;
+
+    /* pending */
+    qemu_log("pending       : ");
+    for (i = plic->bitfield_words - 1; i >= 0; i--) {
+        qemu_log("%08x", plic->pending[i]);
+    }
+    qemu_log("\n");
+
+    /* pending */
+    qemu_log("claimed       : ");
+    for (i = plic->bitfield_words - 1; i >= 0; i--) {
+        qemu_log("%08x", plic->claimed[i]);
+    }
+    qemu_log("\n");
+
+    for (addrid = 0; addrid < plic->num_addrs; addrid++) {
+        qemu_log("hart%d-%c enable: ",
+            plic->addr_config[addrid].hartid,
+            mode_to_char(plic->addr_config[addrid].mode));
+        for (i = plic->bitfield_words - 1; i >= 0; i--) {
+            qemu_log("%08x", plic->enable[addrid * plic->bitfield_words + i]);
+        }
+        qemu_log("\n");
+    }
+}
+
+static
+void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending)
+{
+    qemu_mutex_lock(&plic->lock);
+    uint32_t word = irq >> 5;
+    if (pending) {
+        plic->pending[word] |= (1 << (irq & 31));
+    } else {
+        plic->pending[word] &= ~(1 << (irq & 31));
+    }
+    qemu_mutex_unlock(&plic->lock);
+}
+
+static
+void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed)
+{
+    qemu_mutex_lock(&plic->lock);
+    uint32_t word = irq >> 5;
+    if (claimed) {
+        plic->claimed[word] |= (1 << (irq & 31));
+    } else {
+        plic->claimed[word] &= ~(1 << (irq & 31));
+    }
+    qemu_mutex_unlock(&plic->lock);
+}
+
+static
+int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid)
+{
+    int i, j, count = 0;
+    for (i = 0; i < plic->bitfield_words; i++) {
+        uint32_t pending_enabled_not_claimed =
+            (plic->pending[i] & ~plic->claimed[i]) &
+            plic->enable[addrid * plic->bitfield_words + i];
+        if (!pending_enabled_not_claimed) {
+            continue;
+        }
+        for (j = 0; j < 32; j++) {
+            int irq = (i << 5) + j;
+            uint32_t prio = plic->source_priority[irq];
+            int enabled = pending_enabled_not_claimed & (1 << j);
+            if (enabled && prio > plic->target_priority[addrid]) {
+                count++;
+            }
+        }
+    }
+    return count;
+}
+
+static void sifive_plic_update(SiFivePLICState *plic)
+{
+    int addrid;
+
+    /* raise irq on harts where this irq is enabled */
+    for (addrid = 0; addrid < plic->num_addrs; addrid++) {
+        uint32_t hartid = plic->addr_config[addrid].hartid;
+        PLICMode mode = plic->addr_config[addrid].mode;
+        CPUState *cpu = qemu_get_cpu(hartid);
+        CPURISCVState *env = cpu ? cpu->env_ptr : NULL;
+        if (!env) {
+            continue;
+        }
+        int level = sifive_plic_num_irqs_pending(plic, addrid) > 0;
+        switch (mode) {
+        case PLICMode_M:
+            riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level);
+            break;
+        case PLICMode_S:
+            riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level);
+            break;
+        default:
+            break;
+        }
+    }
+
+    if (RISCV_DEBUG_PLIC) {
+        sifive_plic_print_state(plic);
+    }
+}
+
+void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq)
+{
+    sifive_plic_set_pending(plic, irq, true);
+    sifive_plic_update(plic);
+}
+
+void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq)
+{
+    sifive_plic_set_pending(plic, irq, false);
+    sifive_plic_update(plic);
+}
+
+static uint32_t sifive_plic_claim(SiFivePLICState *plic, uint32_t addrid)
+{
+    int i, j;
+    for (i = 0; i < plic->bitfield_words; i++) {
+        uint32_t pending_enabled_not_claimed =
+            (plic->pending[i] & ~plic->claimed[i]) &
+            plic->enable[addrid * plic->bitfield_words + i];
+        if (!pending_enabled_not_claimed) {
+            continue;
+        }
+        for (j = 0; j < 32; j++) {
+            int irq = (i << 5) + j;
+            uint32_t prio = plic->source_priority[irq];
+            int enabled = pending_enabled_not_claimed & (1 << j);
+            if (enabled && prio > plic->target_priority[addrid]) {
+                sifive_plic_set_pending(plic, irq, false);
+                sifive_plic_set_claimed(plic, irq, true);
+                return irq;
+            }
+        }
+    }
+    return 0;
+}
+
+static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size)
+{
+    SiFivePLICState *plic = opaque;
+
+    /* writes must be 4 byte words */
+    if ((addr & 0x3) != 0) {
+        goto err;
+    }
+
+    if (addr >= plic->priority_base && /* 4 bytes per source */
+        addr < plic->priority_base + (plic->num_sources << 2))
+    {
+        uint32_t irq = (addr - plic->priority_base) >> 2;
+        if (RISCV_DEBUG_PLIC) {
+            qemu_log("plic: read priority: irq=%d priority=%d\n",
+                irq, plic->source_priority[irq]);
+        }
+        return plic->source_priority[irq];
+    } else if (addr >= plic->pending_base && /* 1 bit per source */
+               addr < plic->pending_base + (plic->num_sources >> 3))
+    {
+        uint32_t word = (addr - plic->priority_base) >> 2;
+        if (RISCV_DEBUG_PLIC) {
+            qemu_log("plic: read pending: word=%d value=%d\n",
+                word, plic->pending[word]);
+        }
+        return plic->pending[word];
+    } else if (addr >= plic->enable_base && /* 1 bit per source */
+             addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
+    {
+        uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
+        uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
+        if (wordid < plic->bitfield_words) {
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: read enable: hart%d-%c word=%d value=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode), wordid,
+                    plic->enable[addrid * plic->bitfield_words + wordid]);
+            }
+            return plic->enable[addrid * plic->bitfield_words + wordid];
+        }
+    } else if (addr >= plic->context_base && /* 1 bit per source */
+             addr < plic->context_base + plic->num_addrs * plic->context_stride)
+    {
+        uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
+        uint32_t contextid = (addr & (plic->context_stride - 1));
+        if (contextid == 0) {
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: read priority: hart%d-%c priority=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode),
+                    plic->target_priority[addrid]);
+            }
+            return plic->target_priority[addrid];
+        } else if (contextid == 4) {
+            uint32_t value = sifive_plic_claim(plic, addrid);
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: read claim: hart%d-%c irq=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode),
+                    value);
+                sifive_plic_print_state(plic);
+            }
+            return value;
+        }
+    }
+
+err:
+    error_report("plic: invalid register read: %08x", (uint32_t)addr);
+    return 0;
+}
+
+static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value,
+        unsigned size)
+{
+    SiFivePLICState *plic = opaque;
+
+    /* writes must be 4 byte words */
+    if ((addr & 0x3) != 0) {
+        goto err;
+    }
+
+    if (addr >= plic->priority_base && /* 4 bytes per source */
+        addr < plic->priority_base + (plic->num_sources << 2))
+    {
+        uint32_t irq = (addr - plic->priority_base) >> 2;
+        plic->source_priority[irq] = value & 7;
+        if (RISCV_DEBUG_PLIC) {
+            qemu_log("plic: write priority: irq=%d priority=%d\n",
+                irq, plic->source_priority[irq]);
+        }
+        return;
+    } else if (addr >= plic->pending_base && /* 1 bit per source */
+               addr < plic->pending_base + (plic->num_sources >> 3))
+    {
+        error_report("plic: invalid pending write: %08x", (uint32_t)addr);
+        return;
+    } else if (addr >= plic->enable_base && /* 1 bit per source */
+        addr < plic->enable_base + plic->num_addrs * plic->enable_stride)
+    {
+        uint32_t addrid = (addr - plic->enable_base) / plic->enable_stride;
+        uint32_t wordid = (addr & (plic->enable_stride - 1)) >> 2;
+        if (wordid < plic->bitfield_words) {
+            plic->enable[addrid * plic->bitfield_words + wordid] = value;
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: write enable: hart%d-%c word=%d value=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode), wordid,
+                    plic->enable[addrid * plic->bitfield_words + wordid]);
+            }
+            return;
+        }
+    } else if (addr >= plic->context_base && /* 4 bytes per reg */
+        addr < plic->context_base + plic->num_addrs * plic->context_stride)
+    {
+        uint32_t addrid = (addr - plic->context_base) / plic->context_stride;
+        uint32_t contextid = (addr & (plic->context_stride - 1));
+        if (contextid == 0) {
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: write priority: hart%d-%c priority=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode),
+                    plic->target_priority[addrid]);
+            }
+            if (value <= plic->num_priorities) {
+                plic->target_priority[addrid] = value;
+                sifive_plic_update(plic);
+            }
+            return;
+        } else if (contextid == 4) {
+            if (RISCV_DEBUG_PLIC) {
+                qemu_log("plic: write claim: hart%d-%c irq=%x\n",
+                    plic->addr_config[addrid].hartid,
+                    mode_to_char(plic->addr_config[addrid].mode),
+                    (uint32_t)value);
+            }
+            if (value < plic->num_sources) {
+                sifive_plic_set_claimed(plic, value, false);
+                sifive_plic_update(plic);
+            }
+            return;
+        }
+    }
+
+err:
+    error_report("plic: invalid register write: %08x", (uint32_t)addr);
+}
+
+static const MemoryRegionOps sifive_plic_ops = {
+    .read = sifive_plic_read,
+    .write = sifive_plic_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static Property sifive_plic_properties[] = {
+    DEFINE_PROP_STRING("hart-config", SiFivePLICState, hart_config),
+    DEFINE_PROP_UINT32("num-sources", SiFivePLICState, num_sources, 0),
+    DEFINE_PROP_UINT32("num-priorities", SiFivePLICState, num_priorities, 0),
+    DEFINE_PROP_UINT32("priority-base", SiFivePLICState, priority_base, 0),
+    DEFINE_PROP_UINT32("pending-base", SiFivePLICState, pending_base, 0),
+    DEFINE_PROP_UINT32("enable-base", SiFivePLICState, enable_base, 0),
+    DEFINE_PROP_UINT32("enable-stride", SiFivePLICState, enable_stride, 0),
+    DEFINE_PROP_UINT32("context-base", SiFivePLICState, context_base, 0),
+    DEFINE_PROP_UINT32("context-stride", SiFivePLICState, context_stride, 0),
+    DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+/*
+ * parse PLIC hart/mode address offset config
+ *
+ * "M"              1 hart with M mode
+ * "MS,MS"          2 harts, 0-1 with M and S mode
+ * "M,MS,MS,MS,MS"  5 harts, 0 with M mode, 1-5 with M and S mode
+ */
+static void parse_hart_config(SiFivePLICState *plic)
+{
+    int addrid, hartid, modes;
+    const char *p;
+    char c;
+
+    /* count and validate hart/mode combinations */
+    addrid = 0, hartid = 0, modes = 0;
+    p = plic->hart_config;
+    while ((c = *p++)) {
+        if (c == ',') {
+            addrid += __builtin_popcount(modes);
+            modes = 0;
+            hartid++;
+        } else {
+            int m = 1 << char_to_mode(c);
+            if (modes == (modes | m)) {
+                error_report("plic: duplicate mode '%c' in config: %s",
+                             c, plic->hart_config);
+                exit(1);
+            }
+            modes |= m;
+        }
+    }
+    if (modes) {
+        addrid += __builtin_popcount(modes);
+    }
+    hartid++;
+
+    /* store hart/mode combinations */
+    plic->num_addrs = addrid;
+    plic->addr_config = g_new(PLICAddr, plic->num_addrs);
+    addrid = 0, hartid = 0;
+    p = plic->hart_config;
+    while ((c = *p++)) {
+        if (c == ',') {
+            hartid++;
+        } else {
+            plic->addr_config[addrid].addrid = addrid;
+            plic->addr_config[addrid].hartid = hartid;
+            plic->addr_config[addrid].mode = char_to_mode(c);
+            addrid++;
+        }
+    }
+}
+
+static void sifive_plic_irq_request(void *opaque, int irq, int level)
+{
+    SiFivePLICState *plic = opaque;
+    if (RISCV_DEBUG_PLIC) {
+        qemu_log("sifive_plic_irq_request: irq=%d level=%d\n", irq, level);
+    }
+    sifive_plic_set_pending(plic, irq, level > 0);
+    sifive_plic_update(plic);
+}
+
+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);
+    parse_hart_config(plic);
+    qemu_mutex_init(&plic->lock);
+    plic->bitfield_words = (plic->num_sources + 31) >> 5;
+    plic->source_priority = g_new0(uint32_t, plic->num_sources);
+    plic->target_priority = g_new(uint32_t, plic->num_addrs);
+    plic->pending = g_new0(uint32_t, plic->bitfield_words);
+    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);
+    }
+}
+
+static void sifive_plic_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->props = sifive_plic_properties;
+    dc->realize = sifive_plic_realize;
+}
+
+static const TypeInfo sifive_plic_info = {
+    .name          = TYPE_SIFIVE_PLIC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFivePLICState),
+    .class_init    = sifive_plic_class_init,
+};
+
+static void sifive_plic_register_types(void)
+{
+    type_register_static(&sifive_plic_info);
+}
+
+type_init(sifive_plic_register_types)
+
+/*
+ * Create PLIC device.
+ */
+DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
+    uint32_t num_sources, uint32_t num_priorities,
+    uint32_t priority_base, uint32_t pending_base,
+    uint32_t enable_base, uint32_t enable_stride,
+    uint32_t context_base, uint32_t context_stride,
+    uint32_t aperture_size)
+{
+    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PLIC);
+    assert(enable_stride == (enable_stride & -enable_stride));
+    assert(context_stride == (context_stride & -context_stride));
+    qdev_prop_set_string(dev, "hart-config", hart_config);
+    qdev_prop_set_uint32(dev, "num-sources", num_sources);
+    qdev_prop_set_uint32(dev, "num-priorities", num_priorities);
+    qdev_prop_set_uint32(dev, "priority-base", priority_base);
+    qdev_prop_set_uint32(dev, "pending-base", pending_base);
+    qdev_prop_set_uint32(dev, "enable-base", enable_base);
+    qdev_prop_set_uint32(dev, "enable-stride", enable_stride);
+    qdev_prop_set_uint32(dev, "context-base", context_base);
+    qdev_prop_set_uint32(dev, "context-stride", context_stride);
+    qdev_prop_set_uint32(dev, "aperture-size", aperture_size);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+    return dev;
+}
diff --git a/hw/riscv/sifive_prci.c b/hw/riscv/sifive_prci.c
new file mode 100644 (file)
index 0000000..0910ea3
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt)
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Simple model of the PRCI to emulate register reads made by the SDK BSP
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_prci.h"
+
+/* currently implements enough to mock freedom-e-sdk BSP clock programming */
+
+static uint64_t sifive_prci_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    if (addr == 0 /* PRCI_HFROSCCFG */) {
+        return 1 << 31; /* ROSC_RDY */
+    }
+    if (addr == 8 /* PRCI_PLLCFG    */) {
+        return 1 << 31; /* PLL_LOCK */
+    }
+    hw_error("%s: read: addr=0x%x\n", __func__, (int)addr);
+    return 0;
+}
+
+static void sifive_prci_write(void *opaque, hwaddr addr,
+           uint64_t val64, unsigned int size)
+{
+    /* discard writes */
+}
+
+static const MemoryRegionOps sifive_prci_ops = {
+    .read = sifive_prci_read,
+    .write = sifive_prci_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void sifive_prci_init(Object *obj)
+{
+    SiFivePRCIState *s = SIFIVE_PRCI(obj);
+
+    memory_region_init_io(&s->mmio, obj, &sifive_prci_ops, s,
+                          TYPE_SIFIVE_PRCI, 0x8000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static const TypeInfo sifive_prci_info = {
+    .name          = TYPE_SIFIVE_PRCI,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFivePRCIState),
+    .instance_init = sifive_prci_init,
+};
+
+static void sifive_prci_register_types(void)
+{
+    type_register_static(&sifive_prci_info);
+}
+
+type_init(sifive_prci_register_types)
+
+
+/*
+ * Create PRCI device.
+ */
+DeviceState *sifive_prci_create(hwaddr addr)
+{
+    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_PRCI);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+    return dev;
+}
diff --git a/hw/riscv/sifive_test.c b/hw/riscv/sifive_test.c
new file mode 100644 (file)
index 0000000..8abd2cd
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * QEMU SiFive Test Finisher
+ *
+ * Copyright (c) 2018 SiFive, Inc.
+ *
+ * Test finisher memory mapped device used to exit simulation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_test.h"
+
+static uint64_t sifive_test_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    return 0;
+}
+
+static void sifive_test_write(void *opaque, hwaddr addr,
+           uint64_t val64, unsigned int size)
+{
+    if (addr == 0) {
+        int status = val64 & 0xffff;
+        int code = (val64 >> 16) & 0xffff;
+        switch (status) {
+        case FINISHER_FAIL:
+            exit(code);
+        case FINISHER_PASS:
+            exit(0);
+        default:
+            break;
+        }
+    }
+    hw_error("%s: write: addr=0x%x val=0x%016" PRIx64 "\n",
+        __func__, (int)addr, val64);
+}
+
+static const MemoryRegionOps sifive_test_ops = {
+    .read = sifive_test_read,
+    .write = sifive_test_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void sifive_test_init(Object *obj)
+{
+    SiFiveTestState *s = SIFIVE_TEST(obj);
+
+    memory_region_init_io(&s->mmio, obj, &sifive_test_ops, s,
+                          TYPE_SIFIVE_TEST, 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static const TypeInfo sifive_test_info = {
+    .name          = TYPE_SIFIVE_TEST,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(SiFiveTestState),
+    .instance_init = sifive_test_init,
+};
+
+static void sifive_test_register_types(void)
+{
+    type_register_static(&sifive_test_info);
+}
+
+type_init(sifive_test_register_types)
+
+
+/*
+ * Create Test device.
+ */
+DeviceState *sifive_test_create(hwaddr addr)
+{
+    DeviceState *dev = qdev_create(NULL, TYPE_SIFIVE_TEST);
+    qdev_init_nofail(dev);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+    return dev;
+}
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
new file mode 100644 (file)
index 0000000..1c2deef
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * QEMU RISC-V Board Compatible with SiFive Freedom U SDK
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Provides a board compatible with the SiFive Freedom U SDK:
+ *
+ * 0) UART
+ * 1) CLINT (Core Level Interruptor)
+ * 2) PLIC (Platform Level Interrupt Controller)
+ *
+ * This board currently uses a hardcoded devicetree that indicates one hart.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_plic.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/sifive_uart.h"
+#include "hw/riscv/sifive_prci.h"
+#include "hw/riscv/sifive_u.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} sifive_u_memmap[] = {
+    [SIFIVE_U_DEBUG] =    {        0x0,      0x100 },
+    [SIFIVE_U_MROM] =     {     0x1000,     0x2000 },
+    [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 },
+};
+
+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,
+                 &kernel_entry, NULL, &kernel_high,
+                 0, ELF_MACHINE, 1, 0) < 0) {
+        error_report("qemu: could not load kernel '%s'", kernel_filename);
+        exit(1);
+    }
+    return kernel_entry;
+}
+
+static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
+    uint64_t mem_size, const char *cmdline)
+{
+    void *fdt;
+    int cpu;
+    uint32_t *cells;
+    char *nodename;
+    uint32_t plic_phandle;
+
+    fdt = s->fdt = create_device_tree(&s->fdt_size);
+    if (!fdt) {
+        error_report("create_device_tree() failed");
+        exit(1);
+    }
+
+    qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
+    qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
+    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+    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_cell(fdt, "/soc", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+    nodename = g_strdup_printf("/memory@%lx",
+        (long)memmap[SIFIVE_U_DRAM].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        memmap[SIFIVE_U_DRAM].base >> 32, memmap[SIFIVE_U_DRAM].base,
+        mem_size >> 32, mem_size);
+    qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+    g_free(nodename);
+
+    qemu_fdt_add_subnode(fdt, "/cpus");
+    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
+    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--) {
+        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]);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
+        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");
+        qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
+        qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+        qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
+        qemu_fdt_add_subnode(fdt, intc);
+        qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
+        qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
+        qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
+        qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
+        qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
+        g_free(isa);
+        g_free(intc);
+        g_free(nodename);
+    }
+
+    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
+    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/clint@%lx",
+        (long)memmap[SIFIVE_U_CLINT].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        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);
+    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++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
+        (long)memmap[SIFIVE_U_PLIC].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
+    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);
+    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_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",
+        (long)memmap[SIFIVE_U_UART0].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,uart0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[SIFIVE_U_UART0].base,
+        0x0, memmap[SIFIVE_U_UART0].size);
+    qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+    qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 1);
+
+    qemu_fdt_add_subnode(fdt, "/chosen");
+    qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
+    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+    g_free(nodename);
+}
+
+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 *main_mem = g_new(MemoryRegion, 1);
+    MemoryRegion *boot_rom = g_new(MemoryRegion, 1);
+
+    /* 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);
+    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);
+
+    /* 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);
+    }
+
+    /* reset vector */
+    uint32_t reset_vec[8] = {
+        0x00000297,                    /* 1:  auipc  t0, %pcrel_hi(dtb) */
+        0x02028593,                    /*     addi   a1, t0, %pcrel_lo(1b) */
+        0xf1402573,                    /*     csrr   a0, mhartid  */
+#if defined(TARGET_RISCV32)
+        0x0182a283,                    /*     lw     t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+        0x0182b283,                    /*     ld     t0, 24(t0) */
+#endif
+        0x00028067,                    /*     jr     t0 */
+        0x00000000,
+        memmap[SIFIVE_U_DRAM].base, /* start: .dword DRAM_BASE */
+        0x00000000,
+                                       /* dtb: */
+    };
+
+    /* copy in the reset vector */
+    copy_le32_to_phys(memmap[SIFIVE_U_MROM].base, reset_vec, sizeof(reset_vec));
+
+    /* 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);
+
+    /* MMIO */
+    s->plic = sifive_plic_create(memmap[SIFIVE_U_PLIC].base,
+        (char *)SIFIVE_U_PLIC_HART_CONFIG,
+        SIFIVE_U_PLIC_NUM_SOURCES,
+        SIFIVE_U_PLIC_NUM_PRIORITIES,
+        SIFIVE_U_PLIC_PRIORITY_BASE,
+        SIFIVE_U_PLIC_PENDING_BASE,
+        SIFIVE_U_PLIC_ENABLE_BASE,
+        SIFIVE_U_PLIC_ENABLE_STRIDE,
+        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_clint_create(memmap[SIFIVE_U_CLINT].base,
+        memmap[SIFIVE_U_CLINT].size, smp_cpus,
+        SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+}
+
+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";
+    mc->init = riscv_sifive_u_init;
+    mc->max_cpus = 1;
+}
+
+DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init)
diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c
new file mode 100644 (file)
index 0000000..b0c3798
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * QEMU model of the UART on the SiFive E300 and U500 series SOCs.
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/sifive_uart.h"
+
+/*
+ * Not yet implemented:
+ *
+ * Transmit FIFO using "qemu/fifo8.h"
+ * SIFIVE_UART_IE_TXWM interrupts
+ * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark
+ * Rx FIFO watermark interrupt trigger threshold
+ * Tx FIFO watermark interrupt trigger threshold.
+ */
+
+static void update_irq(SiFiveUARTState *s)
+{
+    int cond = 0;
+    if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) {
+        cond = 1;
+    }
+    if (cond) {
+        qemu_irq_raise(s->irq);
+    } else {
+        qemu_irq_lower(s->irq);
+    }
+}
+
+static uint64_t
+uart_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    SiFiveUARTState *s = opaque;
+    unsigned char r;
+    switch (addr) {
+    case SIFIVE_UART_RXFIFO:
+        if (s->rx_fifo_len) {
+            r = s->rx_fifo[0];
+            memmove(s->rx_fifo, s->rx_fifo + 1, s->rx_fifo_len - 1);
+            s->rx_fifo_len--;
+            qemu_chr_fe_accept_input(&s->chr);
+            update_irq(s);
+            return r;
+        }
+        return 0x80000000;
+
+    case SIFIVE_UART_TXFIFO:
+        return 0; /* Should check tx fifo */
+    case SIFIVE_UART_IE:
+        return s->ie;
+    case SIFIVE_UART_IP:
+        return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0;
+    case SIFIVE_UART_TXCTRL:
+        return s->txctrl;
+    case SIFIVE_UART_RXCTRL:
+        return s->rxctrl;
+    case SIFIVE_UART_DIV:
+        return s->div;
+    }
+
+    hw_error("%s: bad read: addr=0x%x\n",
+        __func__, (int)addr);
+    return 0;
+}
+
+static void
+uart_write(void *opaque, hwaddr addr,
+           uint64_t val64, unsigned int size)
+{
+    SiFiveUARTState *s = opaque;
+    uint32_t value = val64;
+    unsigned char ch = value;
+
+    switch (addr) {
+    case SIFIVE_UART_TXFIFO:
+        qemu_chr_fe_write(&s->chr, &ch, 1);
+        return;
+    case SIFIVE_UART_IE:
+        s->ie = val64;
+        update_irq(s);
+        return;
+    case SIFIVE_UART_TXCTRL:
+        s->txctrl = val64;
+        return;
+    case SIFIVE_UART_RXCTRL:
+        s->rxctrl = val64;
+        return;
+    case SIFIVE_UART_DIV:
+        s->div = val64;
+        return;
+    }
+    hw_error("%s: bad write: addr=0x%x v=0x%x\n",
+        __func__, (int)addr, (int)value);
+}
+
+static const MemoryRegionOps uart_ops = {
+    .read = uart_read,
+    .write = uart_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void uart_rx(void *opaque, const uint8_t *buf, int size)
+{
+    SiFiveUARTState *s = opaque;
+
+    /* Got a byte.  */
+    if (s->rx_fifo_len >= sizeof(s->rx_fifo)) {
+        printf("WARNING: UART dropped char.\n");
+        return;
+    }
+    s->rx_fifo[s->rx_fifo_len++] = *buf;
+
+    update_irq(s);
+}
+
+static int uart_can_rx(void *opaque)
+{
+    SiFiveUARTState *s = opaque;
+
+    return s->rx_fifo_len < sizeof(s->rx_fifo);
+}
+
+static void uart_event(void *opaque, int event)
+{
+}
+
+static int uart_be_change(void *opaque)
+{
+    SiFiveUARTState *s = opaque;
+
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+        uart_be_change, s, NULL, true);
+
+    return 0;
+}
+
+/*
+ * Create UART device.
+ */
+SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
+    Chardev *chr, qemu_irq irq)
+{
+    SiFiveUARTState *s = g_malloc0(sizeof(SiFiveUARTState));
+    s->irq = irq;
+    qemu_chr_fe_init(&s->chr, chr, &error_abort);
+    qemu_chr_fe_set_handlers(&s->chr, uart_can_rx, uart_rx, uart_event,
+        uart_be_change, s, NULL, true);
+    memory_region_init_io(&s->mmio, NULL, &uart_ops, s,
+                          TYPE_SIFIVE_UART, SIFIVE_UART_MAX);
+    memory_region_add_subregion(address_space, base, &s->mmio);
+    return s;
+}
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
new file mode 100644 (file)
index 0000000..2d1f114
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * QEMU RISC-V Spike Board
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This provides a RISC-V Board with the following devices:
+ *
+ * 0) HTIF Console and Poweroff
+ * 1) CLINT (Timer and IPI)
+ * 2) PLIC (Platform Level Interrupt Controller)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_htif.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/spike.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} spike_memmap[] = {
+    [SPIKE_MROM] =     {     0x1000,     0x2000 },
+    [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,
+            NULL, true, htif_symbol_callback) < 0) {
+        error_report("qemu: could not load kernel '%s'", kernel_filename);
+        exit(1);
+    }
+    return kernel_entry;
+}
+
+static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
+    uint64_t mem_size, const char *cmdline)
+{
+    void *fdt;
+    int cpu;
+    uint32_t *cells;
+    char *nodename;
+
+    fdt = s->fdt = create_device_tree(&s->fdt_size);
+    if (!fdt) {
+        error_report("create_device_tree() failed");
+        exit(1);
+    }
+
+    qemu_fdt_setprop_string(fdt, "/", "model", "ucbbar,spike-bare,qemu");
+    qemu_fdt_setprop_string(fdt, "/", "compatible", "ucbbar,spike-bare-dev");
+    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+    qemu_fdt_add_subnode(fdt, "/htif");
+    qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0");
+
+    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_cell(fdt, "/soc", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+    nodename = g_strdup_printf("/memory@%lx",
+        (long)memmap[SPIKE_DRAM].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        memmap[SPIKE_DRAM].base >> 32, memmap[SPIKE_DRAM].base,
+        mem_size >> 32, mem_size);
+    qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+    g_free(nodename);
+
+    qemu_fdt_add_subnode(fdt, "/cpus");
+    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
+    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--) {
+        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]);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
+        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");
+        qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
+        qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+        qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
+        qemu_fdt_add_subnode(fdt, intc);
+        qemu_fdt_setprop_cell(fdt, intc, "phandle", 1);
+        qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", 1);
+        qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
+        qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
+        qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
+        g_free(isa);
+        g_free(intc);
+        g_free(nodename);
+    }
+
+    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
+    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/clint@%lx",
+        (long)memmap[SPIKE_CLINT].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[SPIKE_CLINT].base,
+        0x0, memmap[SPIKE_CLINT].size);
+    qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+        cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+    g_free(cells);
+    g_free(nodename);
+
+    qemu_fdt_add_subnode(fdt, "/chosen");
+    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ }
+
+static void spike_v1_10_0_board_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = spike_memmap;
+
+    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);
+
+    /* 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), SPIKE_V1_10_0_CPU, "cpu-type",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+                            &error_abort);
+    object_property_set_bool(OBJECT(&s->soc), true, "realized",
+                            &error_abort);
+
+    /* register system main memory (actual RAM) */
+    memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
+                           machine->ram_size, &error_fatal);
+    memory_region_add_subregion(system_memory, memmap[SPIKE_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.spike.bootrom",
+                           s->fdt_size + 0x2000, &error_fatal);
+    memory_region_add_subregion(system_memory, 0x0, boot_rom);
+
+    if (machine->kernel_filename) {
+        load_kernel(machine->kernel_filename);
+    }
+
+    /* reset vector */
+    uint32_t reset_vec[8] = {
+        0x00000297,                  /* 1:  auipc  t0, %pcrel_hi(dtb) */
+        0x02028593,                  /*     addi   a1, t0, %pcrel_lo(1b) */
+        0xf1402573,                  /*     csrr   a0, mhartid  */
+#if defined(TARGET_RISCV32)
+        0x0182a283,                  /*     lw     t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+        0x0182b283,                  /*     ld     t0, 24(t0) */
+#endif
+        0x00028067,                  /*     jr     t0 */
+        0x00000000,
+        memmap[SPIKE_DRAM].base,     /* start: .dword DRAM_BASE */
+        0x00000000,
+                                     /* dtb: */
+    };
+
+    /* copy in the reset vector */
+    copy_le32_to_phys(memmap[SPIKE_MROM].base, reset_vec, sizeof(reset_vec));
+
+    /* 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);
+
+    /* initialize HTIF using symbols found in load_kernel */
+    htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[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 void spike_v1_09_1_board_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = spike_memmap;
+
+    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);
+
+    /* 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), SPIKE_V1_09_1_CPU, "cpu-type",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+                            &error_abort);
+    object_property_set_bool(OBJECT(&s->soc), true, "realized",
+                            &error_abort);
+
+    /* register system main memory (actual RAM) */
+    memory_region_init_ram(main_mem, NULL, "riscv.spike.ram",
+                           machine->ram_size, &error_fatal);
+    memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
+        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);
+
+    if (machine->kernel_filename) {
+        load_kernel(machine->kernel_filename);
+    }
+
+    /* reset vector */
+    uint32_t reset_vec[8] = {
+        0x297 + memmap[SPIKE_DRAM].base - memmap[SPIKE_MROM].base, /* lui */
+        0x00028067,                   /* jump to DRAM_BASE */
+        0x00000000,                   /* reserved */
+        memmap[SPIKE_MROM].base + sizeof(reset_vec), /* config string pointer */
+        0, 0, 0, 0                    /* trap vector */
+    };
+
+    /* part one of config string - before memory size specified */
+    const char *config_string_tmpl =
+        "platform {\n"
+        "  vendor ucb;\n"
+        "  arch spike;\n"
+        "};\n"
+        "rtc {\n"
+        "  addr 0x%" PRIx64 "x;\n"
+        "};\n"
+        "ram {\n"
+        "  0 {\n"
+        "    addr 0x%" PRIx64 "x;\n"
+        "    size 0x%" PRIx64 "x;\n"
+        "  };\n"
+        "};\n"
+        "core {\n"
+        "  0" " {\n"
+        "    " "0 {\n"
+        "      isa %s;\n"
+        "      timecmp 0x%" PRIx64 "x;\n"
+        "      ipi 0x%" PRIx64 "x;\n"
+        "    };\n"
+        "  };\n"
+        "};\n";
+
+    /* build config string with supplied memory size */
+    char *isa = riscv_isa_string(&s->soc.harts[0]);
+    size_t config_string_size = strlen(config_string_tmpl) + 48;
+    char *config_string = malloc(config_string_size);
+    snprintf(config_string, config_string_size, config_string_tmpl,
+        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIME_BASE,
+        (uint64_t)memmap[SPIKE_DRAM].base,
+        (uint64_t)ram_size, isa,
+        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIMECMP_BASE,
+        (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_SIP_BASE);
+    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 config string */
+    cpu_physical_memory_write(memmap[SPIKE_MROM].base + sizeof(reset_vec),
+        config_string, config_string_len);
+
+    /* initialize HTIF using symbols found in load_kernel */
+    htif_mm_init(system_memory, boot_rom, &s->soc.harts[0].env, serial_hds[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)";
+    mc->init = spike_v1_09_1_board_init;
+    mc->max_cpus = 1;
+}
+
+static void spike_v1_10_0_machine_init(MachineClass *mc)
+{
+    mc->desc = "RISC-V Spike Board (Privileged ISA v1.10)";
+    mc->init = spike_v1_10_0_board_init;
+    mc->max_cpus = 1;
+    mc->is_default = 1;
+}
+
+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
new file mode 100644 (file)
index 0000000..e2c214e
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * QEMU RISC-V VirtIO Board
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * RISC-V machine with 16550a UART and VirtIO MMIO
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/char/serial.h"
+#include "target/riscv/cpu.h"
+#include "hw/riscv/riscv_htif.h"
+#include "hw/riscv/riscv_hart.h"
+#include "hw/riscv/sifive_plic.h"
+#include "hw/riscv/sifive_clint.h"
+#include "hw/riscv/sifive_test.h"
+#include "hw/riscv/virt.h"
+#include "chardev/char.h"
+#include "sysemu/arch_init.h"
+#include "sysemu/device_tree.h"
+#include "exec/address-spaces.h"
+#include "elf.h"
+
+static const struct MemmapEntry {
+    hwaddr base;
+    hwaddr size;
+} virt_memmap[] = {
+    [VIRT_DEBUG] =    {        0x0,      0x100 },
+    [VIRT_MROM] =     {     0x1000,     0x2000 },
+    [VIRT_TEST] =     {     0x4000,     0x1000 },
+    [VIRT_CLINT] =    {  0x2000000,    0x10000 },
+    [VIRT_PLIC] =     {  0xc000000,  0x4000000 },
+    [VIRT_UART0] =    { 0x10000000,      0x100 },
+    [VIRT_VIRTIO] =   { 0x10001000,     0x1000 },
+    [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,
+                 &kernel_entry, NULL, &kernel_high,
+                 0, ELF_MACHINE, 1, 0) < 0) {
+        error_report("qemu: could not load kernel '%s'", kernel_filename);
+        exit(1);
+    }
+    return kernel_entry;
+}
+
+static hwaddr load_initrd(const char *filename, uint64_t mem_size,
+                          uint64_t kernel_entry, hwaddr *start)
+{
+    int size;
+
+    /* We want to put the initrd far enough into RAM that when the
+     * kernel is uncompressed it will not clobber the initrd. However
+     * on boards without much RAM we must ensure that we still leave
+     * enough room for a decent sized initrd, and on boards with large
+     * amounts of RAM we must avoid the initrd being so far up in RAM
+     * that it is outside lowmem and inaccessible to the kernel.
+     * So for boards with less  than 256MB of RAM we put the initrd
+     * 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);
+
+    size = load_ramdisk(filename, *start, mem_size - *start);
+    if (size == -1) {
+        size = load_image_targphys(filename, *start, mem_size - *start);
+        if (size == -1) {
+            error_report("qemu: could not load ramdisk '%s'", filename);
+            exit(1);
+        }
+    }
+    return *start + size;
+}
+
+static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
+    uint64_t mem_size, const char *cmdline)
+{
+    void *fdt;
+    int cpu;
+    uint32_t *cells;
+    char *nodename;
+    uint32_t plic_phandle, phandle = 1;
+    int i;
+
+    fdt = s->fdt = create_device_tree(&s->fdt_size);
+    if (!fdt) {
+        error_report("create_device_tree() failed");
+        exit(1);
+    }
+
+    qemu_fdt_setprop_string(fdt, "/", "model", "riscv-virtio,qemu");
+    qemu_fdt_setprop_string(fdt, "/", "compatible", "riscv-virtio");
+    qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+    qemu_fdt_add_subnode(fdt, "/soc");
+    qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+    qemu_fdt_setprop_string(fdt, "/soc", "compatible", "riscv-virtio-soc");
+    qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
+    qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+    nodename = g_strdup_printf("/memory@%lx",
+        (long)memmap[VIRT_DRAM].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        memmap[VIRT_DRAM].base >> 32, memmap[VIRT_DRAM].base,
+        mem_size >> 32, mem_size);
+    qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
+    g_free(nodename);
+
+    qemu_fdt_add_subnode(fdt, "/cpus");
+    qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency", 10000000);
+    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--) {
+        int cpu_phandle = phandle++;
+        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]);
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 1000000000);
+        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");
+        qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
+        qemu_fdt_setprop_cell(fdt, nodename, "reg", cpu);
+        qemu_fdt_setprop_string(fdt, nodename, "device_type", "cpu");
+        qemu_fdt_add_subnode(fdt, intc);
+        qemu_fdt_setprop_cell(fdt, intc, "phandle", cpu_phandle);
+        qemu_fdt_setprop_cell(fdt, intc, "linux,phandle", cpu_phandle);
+        qemu_fdt_setprop_string(fdt, intc, "compatible", "riscv,cpu-intc");
+        qemu_fdt_setprop(fdt, intc, "interrupt-controller", NULL, 0);
+        qemu_fdt_setprop_cell(fdt, intc, "#interrupt-cells", 1);
+        g_free(isa);
+        g_free(intc);
+        g_free(nodename);
+    }
+
+    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
+    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_SOFT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/clint@%lx",
+        (long)memmap[VIRT_CLINT].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,clint0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[VIRT_CLINT].base,
+        0x0, memmap[VIRT_CLINT].size);
+    qemu_fdt_setprop(fdt, nodename, "interrupts-extended",
+        cells, s->soc.num_harts * sizeof(uint32_t) * 4);
+    g_free(cells);
+    g_free(nodename);
+
+    plic_phandle = phandle++;
+    cells =  g_new0(uint32_t, s->soc.num_harts * 4);
+    for (cpu = 0; cpu < s->soc.num_harts; cpu++) {
+        nodename =
+            g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
+        uint32_t intc_phandle = qemu_fdt_get_phandle(fdt, nodename);
+        cells[cpu * 4 + 0] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
+        cells[cpu * 4 + 2] = cpu_to_be32(intc_phandle);
+        cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+        g_free(nodename);
+    }
+    nodename = g_strdup_printf("/soc/interrupt-controller@%lx",
+        (long)memmap[VIRT_PLIC].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1);
+    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);
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[VIRT_PLIC].base,
+        0x0, memmap[VIRT_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", VIRTIO_NDEV);
+    qemu_fdt_setprop_cells(fdt, nodename, "phandle", plic_phandle);
+    qemu_fdt_setprop_cells(fdt, nodename, "linux,phandle", plic_phandle);
+    plic_phandle = qemu_fdt_get_phandle(fdt, nodename);
+    g_free(cells);
+    g_free(nodename);
+
+    for (i = 0; i < VIRTIO_COUNT; i++) {
+        nodename = g_strdup_printf("/virtio_mmio@%lx",
+            (long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size));
+        qemu_fdt_add_subnode(fdt, nodename);
+        qemu_fdt_setprop_string(fdt, nodename, "compatible", "virtio,mmio");
+        qemu_fdt_setprop_cells(fdt, nodename, "reg",
+            0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size,
+            0x0, memmap[VIRT_VIRTIO].size);
+        qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+        qemu_fdt_setprop_cells(fdt, nodename, "interrupts", VIRTIO_IRQ + i);
+        g_free(nodename);
+    }
+
+    nodename = g_strdup_printf("/test@%lx",
+        (long)memmap[VIRT_TEST].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "sifive,test0");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[VIRT_TEST].base,
+        0x0, memmap[VIRT_TEST].size);
+
+    nodename = g_strdup_printf("/uart@%lx",
+        (long)memmap[VIRT_UART0].base);
+    qemu_fdt_add_subnode(fdt, nodename);
+    qemu_fdt_setprop_string(fdt, nodename, "compatible", "ns16550a");
+    qemu_fdt_setprop_cells(fdt, nodename, "reg",
+        0x0, memmap[VIRT_UART0].base,
+        0x0, memmap[VIRT_UART0].size);
+    qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", 3686400);
+        qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle);
+        qemu_fdt_setprop_cells(fdt, nodename, "interrupts", UART0_IRQ);
+
+    qemu_fdt_add_subnode(fdt, "/chosen");
+    qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename);
+    qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+    g_free(nodename);
+
+    return fdt;
+}
+
+static void riscv_virt_board_init(MachineState *machine)
+{
+    const struct MemmapEntry *memmap = virt_memmap;
+
+    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);
+    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_property_set_str(OBJECT(&s->soc), VIRT_CPU, "cpu-type",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->soc), smp_cpus, "num-harts",
+                            &error_abort);
+    object_property_set_bool(OBJECT(&s->soc), true, "realized",
+                            &error_abort);
+
+    /* register system main memory (actual RAM) */
+    memory_region_init_ram(main_mem, NULL, "riscv_virt_board.ram",
+                           machine->ram_size, &error_fatal);
+    memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base,
+        main_mem);
+
+    /* create device tree */
+    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);
+
+    if (machine->kernel_filename) {
+        uint64_t kernel_entry = load_kernel(machine->kernel_filename);
+
+        if (machine->initrd_filename) {
+            hwaddr start;
+            hwaddr end = load_initrd(machine->initrd_filename,
+                                     machine->ram_size, kernel_entry,
+                                     &start);
+            qemu_fdt_setprop_cell(fdt, "/chosen",
+                                  "linux,initrd-start", start);
+            qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
+                                  end);
+        }
+    }
+
+    /* reset vector */
+    uint32_t reset_vec[8] = {
+        0x00000297,                  /* 1:  auipc  t0, %pcrel_hi(dtb) */
+        0x02028593,                  /*     addi   a1, t0, %pcrel_lo(1b) */
+        0xf1402573,                  /*     csrr   a0, mhartid  */
+#if defined(TARGET_RISCV32)
+        0x0182a283,                  /*     lw     t0, 24(t0) */
+#elif defined(TARGET_RISCV64)
+        0x0182b283,                  /*     ld     t0, 24(t0) */
+#endif
+        0x00028067,                  /*     jr     t0 */
+        0x00000000,
+        memmap[VIRT_DRAM].base,      /* start: .dword memmap[VIRT_DRAM].base */
+        0x00000000,
+                                     /* dtb: */
+    };
+
+    /* copy in the reset vector */
+    copy_le32_to_phys(ROM_BASE, reset_vec, sizeof(reset_vec));
+
+    /* 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);
+
+    /* create PLIC hart topology configuration string */
+    plic_hart_config_len = (strlen(VIRT_PLIC_HART_CONFIG) + 1) * smp_cpus;
+    plic_hart_config = g_malloc0(plic_hart_config_len);
+    for (i = 0; i < smp_cpus; i++) {
+        if (i != 0) {
+            strncat(plic_hart_config, ",", plic_hart_config_len);
+        }
+        strncat(plic_hart_config, VIRT_PLIC_HART_CONFIG, plic_hart_config_len);
+        plic_hart_config_len -= (strlen(VIRT_PLIC_HART_CONFIG) + 1);
+    }
+
+    /* MMIO */
+    s->plic = sifive_plic_create(memmap[VIRT_PLIC].base,
+        plic_hart_config,
+        VIRT_PLIC_NUM_SOURCES,
+        VIRT_PLIC_NUM_PRIORITIES,
+        VIRT_PLIC_PRIORITY_BASE,
+        VIRT_PLIC_PENDING_BASE,
+        VIRT_PLIC_ENABLE_BASE,
+        VIRT_PLIC_ENABLE_STRIDE,
+        VIRT_PLIC_CONTEXT_BASE,
+        VIRT_PLIC_CONTEXT_STRIDE,
+        memmap[VIRT_PLIC].size);
+    sifive_clint_create(memmap[VIRT_CLINT].base,
+        memmap[VIRT_CLINT].size, smp_cpus,
+        SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE);
+    sifive_test_create(memmap[VIRT_TEST].base);
+
+    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]);
+    }
+
+    serial_mm_init(system_memory, memmap[VIRT_UART0].base,
+        0, SIFIVE_PLIC(s->plic)->irqs[UART0_IRQ], 399193,
+        serial_hds[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->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);
index 155a69467b06cff4fd2d0b89135038e43c56c281..9c24bc6f7cecb45d2378792b25ae105a73d7d74c 100644 (file)
@@ -29,8 +29,17 @@ typedef struct SCLPEventsBus {
 struct SCLPEventFacility {
     SysBusDevice parent_obj;
     SCLPEventsBus sbus;
-    /* guest' receive mask */
-    unsigned int receive_mask;
+    /* guest's receive mask */
+    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
+     * the architecture correctly, allowing all valid mask sizes. Needed for
+     * migration toward older versions.
+     */
+    bool allow_all_mask_sizes;
+    /* length of the receive mask */
+    uint16_t mask_length;
 };
 
 /* return true if any child has event pending set */
@@ -52,9 +61,9 @@ static bool event_pending(SCLPEventFacility *ef)
     return false;
 }
 
-static unsigned int get_host_send_mask(SCLPEventFacility *ef)
+static sccb_mask_t get_host_send_mask(SCLPEventFacility *ef)
 {
-    unsigned int mask;
+    sccb_mask_t mask;
     BusChild *kid;
     SCLPEventClass *child;
 
@@ -68,9 +77,9 @@ static unsigned int get_host_send_mask(SCLPEventFacility *ef)
     return mask;
 }
 
-static unsigned int get_host_receive_mask(SCLPEventFacility *ef)
+static sccb_mask_t get_host_receive_mask(SCLPEventFacility *ef)
 {
-    unsigned int mask;
+    sccb_mask_t mask;
     BusChild *kid;
     SCLPEventClass *child;
 
@@ -180,7 +189,7 @@ out:
 }
 
 static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb,
-                                        unsigned int mask)
+                                        sccb_mask_t mask)
 {
     uint16_t rc;
     int slen;
@@ -220,10 +229,21 @@ static uint16_t handle_sccb_read_events(SCLPEventFacility *ef, SCCB *sccb,
     return rc;
 }
 
+/* copy up to src_len bytes and fill the rest of dst with zeroes */
+static void copy_mask(uint8_t *dst, uint8_t *src, uint16_t dst_len,
+                      uint16_t src_len)
+{
+    int i;
+
+    for (i = 0; i < dst_len; i++) {
+        dst[i] = i < src_len ? src[i] : 0;
+    }
+}
+
 static void read_event_data(SCLPEventFacility *ef, SCCB *sccb)
 {
-    unsigned int sclp_active_selection_mask;
-    unsigned int sclp_cp_receive_mask;
+    sccb_mask_t sclp_active_selection_mask;
+    sccb_mask_t sclp_cp_receive_mask;
 
     ReadEventData *red = (ReadEventData *) sccb;
 
@@ -240,7 +260,9 @@ static void read_event_data(SCLPEventFacility *ef, SCCB *sccb)
         sclp_active_selection_mask = sclp_cp_receive_mask;
         break;
     case SCLP_SELECTIVE_READ:
-        sclp_active_selection_mask = be32_to_cpu(red->mask);
+        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);
         if (!sclp_cp_receive_mask ||
             (sclp_active_selection_mask & ~sclp_cp_receive_mask)) {
             sccb->h.response_code =
@@ -259,24 +281,14 @@ out:
     return;
 }
 
-/* copy up to dst_len bytes and fill the rest of dst with zeroes */
-static void copy_mask(uint8_t *dst, uint8_t *src, uint16_t dst_len,
-                      uint16_t src_len)
-{
-    int i;
-
-    for (i = 0; i < dst_len; i++) {
-        dst[i] = i < src_len ? src[i] : 0;
-    }
-}
-
 static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb)
 {
     WriteEventMask *we_mask = (WriteEventMask *) sccb;
     uint16_t mask_length = be16_to_cpu(we_mask->mask_length);
-    uint32_t tmp_mask;
+    sccb_mask_t tmp_mask;
 
-    if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX)) {
+    if (!mask_length || (mask_length > SCLP_EVENT_MASK_LEN_MAX) ||
+        ((mask_length != 4) && !ef->allow_all_mask_sizes)) {
         sccb->h.response_code = cpu_to_be16(SCLP_RC_INVALID_MASK_LENGTH);
         goto out;
     }
@@ -301,6 +313,7 @@ static void write_event_mask(SCLPEventFacility *ef, SCCB *sccb)
               mask_length, sizeof(tmp_mask));
 
     sccb->h.response_code = cpu_to_be16(SCLP_RC_NORMAL_COMPLETION);
+    ef->mask_length = mask_length;
 
 out:
     return;
@@ -356,6 +369,24 @@ static void command_handler(SCLPEventFacility *ef, SCCB *sccb, uint64_t code)
     }
 }
 
+static bool vmstate_event_facility_mask_length_needed(void *opaque)
+{
+    SCLPEventFacility *ef = opaque;
+
+    return ef->allow_all_mask_sizes;
+}
+
+static const VMStateDescription vmstate_event_facility_mask_length = {
+    .name = "vmstate-event-facility/mask_length",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .needed = vmstate_event_facility_mask_length_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT16(mask_length, SCLPEventFacility),
+        VMSTATE_END_OF_LIST()
+     }
+};
+
 static const VMStateDescription vmstate_event_facility = {
     .name = "vmstate-event-facility",
     .version_id = 0,
@@ -363,15 +394,39 @@ static const VMStateDescription vmstate_event_facility = {
     .fields = (VMStateField[]) {
         VMSTATE_UINT32(receive_mask, SCLPEventFacility),
         VMSTATE_END_OF_LIST()
+     },
+    .subsections = (const VMStateDescription * []) {
+        &vmstate_event_facility_mask_length,
+        NULL
      }
 };
 
+static void sclp_event_set_allow_all_mask_sizes(Object *obj, bool value,
+                                                       Error **errp)
+{
+    SCLPEventFacility *ef = (SCLPEventFacility *)obj;
+
+    ef->allow_all_mask_sizes = value;
+}
+
+static bool sclp_event_get_allow_all_mask_sizes(Object *obj, Error **e)
+{
+    SCLPEventFacility *ef = (SCLPEventFacility *)obj;
+
+    return ef->allow_all_mask_sizes;
+}
+
 static void init_event_facility(Object *obj)
 {
     SCLPEventFacility *event_facility = EVENT_FACILITY(obj);
     DeviceState *sdev = DEVICE(obj);
     Object *new;
 
+    event_facility->mask_length = 4;
+    event_facility->allow_all_mask_sizes = true;
+    object_property_add_bool(obj, "allow_all_mask_sizes",
+                             sclp_event_get_allow_all_mask_sizes,
+                             sclp_event_set_allow_all_mask_sizes, NULL);
     /* Spawn a new bus for SCLP events */
     qbus_create_inplace(&event_facility->sbus, sizeof(event_facility->sbus),
                         TYPE_SCLP_EVENTS_BUS, sdev, NULL);
@@ -431,26 +486,12 @@ static void event_realize(DeviceState *qdev, Error **errp)
     }
 }
 
-static void event_unrealize(DeviceState *qdev, Error **errp)
-{
-    SCLPEvent *event = SCLP_EVENT(qdev);
-    SCLPEventClass *child = SCLP_EVENT_GET_CLASS(event);
-    if (child->exit) {
-        int rc = child->exit(event);
-        if (rc < 0) {
-            error_setg(errp, "SCLP event exit failed.");
-            return;
-        }
-    }
-}
-
 static void event_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->bus_type = TYPE_SCLP_EVENTS_BUS;
     dc->realize = event_realize;
-    dc->unrealize = event_unrealize;
 }
 
 static const TypeInfo sclp_event_type_info = {
index 798e99aadfa0b68c9f292100ff345d9edcffdd3c..fdeaec3a585d0845e4c6f003b9243cb6944b9d83 100644 (file)
@@ -234,7 +234,7 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
     if (!get_boot_device(0)) {
         if (boot_menu) {
             error_report("boot menu requires a bootindex to be specified for "
-                         "the IPL device.");
+                         "the IPL device");
         }
         return;
     }
@@ -250,7 +250,9 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
     case S390_IPL_TYPE_QEMU_SCSI:
         break;
     default:
-        error_report("boot menu is not supported for this device type.");
+        if (boot_menu) {
+            error_report("boot menu is not supported for this device type");
+        }
         return;
     }
 
@@ -263,13 +265,13 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl)
     tmp = qemu_opt_get(opts, "splash-time");
 
     if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) {
-        error_report("splash-time is invalid, forcing it to 0.");
+        error_report("splash-time is invalid, forcing it to 0");
         *timeout = 0;
         return;
     }
 
     if (splash_time > 0xffffffff) {
-        error_report("splash-time is too large, forcing it to max value.");
+        error_report("splash-time is too large, forcing it to max value");
         *timeout = 0xffffffff;
         return;
     }
@@ -380,7 +382,8 @@ static int load_netboot_image(Error **errp)
 
     netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
     if (netboot_filename == NULL) {
-        error_setg(errp, "Could not find network bootloader");
+        error_setg(errp, "Could not find network bootloader '%s'",
+                   ipl->netboot_fw);
         goto unref_mr;
     }
 
@@ -489,7 +492,7 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
     if (ipl->netboot) {
         if (load_netboot_image(&err) < 0) {
             error_report_err(err);
-            vm_stop(RUN_STATE_INTERNAL_ERROR);
+            exit(1);
         }
         ipl->qipl.netboot_start_addr = cpu_to_be64(ipl->start_addr);
     }
index 7fc1c603c07146a48376e24fd994da282be554e2..214c940593d8695ee7a08bc79fcd0bfb83cb1614 100644 (file)
@@ -48,7 +48,7 @@ static void s390_ccw_get_dev_info(S390CCWDevice *cdev,
         return;
     }
 
-    cdev->mdevid = g_strdup(basename(dev_path));
+    cdev->mdevid = g_path_get_basename(dev_path);
 
     tmp = basename(dirname(dev_path));
     if (sscanf(tmp, "%2x.%1x.%4x", &cssid, &ssid, &devid) != 3) {
index 4d0c3deba615ca5269eeaf9c4c0891ae88fb2708..864145a7c6f377d2d0c138daefec8eb268a59d93 100644 (file)
@@ -27,6 +27,7 @@
 #include "s390-pci-bus.h"
 #include "hw/s390x/storage-keys.h"
 #include "hw/s390x/storage-attributes.h"
+#include "hw/s390x/event-facility.h"
 #include "hw/compat.h"
 #include "ipl.h"
 #include "hw/s390x/s390-virtio-ccw.h"
@@ -254,8 +255,10 @@ static void s390_init_ipl_dev(const char *kernel_filename,
     }
     qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
     qdev_prop_set_string(dev, "firmware", firmware);
-    qdev_prop_set_string(dev, "netboot_fw", netboot_fw);
     qdev_prop_set_bit(dev, "enforce_bios", enforce_bios);
+    if (!strlen(object_property_get_str(new, "netboot_fw", &error_abort))) {
+        qdev_prop_set_string(dev, "netboot_fw", netboot_fw);
+    }
     object_property_add_child(qdev_get_machine(), TYPE_S390_IPL,
                               new, NULL);
     object_unref(new);
@@ -388,12 +391,14 @@ static void s390_machine_device_unplug_request(HotplugHandler *hotplug_dev,
     }
 }
 
-static CpuInstanceProperties s390_cpu_index_to_props(MachineState *machine,
+static CpuInstanceProperties s390_cpu_index_to_props(MachineState *ms,
                                                      unsigned cpu_index)
 {
-    g_assert(machine->possible_cpus && cpu_index < machine->possible_cpus->len);
+    MachineClass *mc = MACHINE_GET_CLASS(ms);
+    const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
 
-    return machine->possible_cpus->cpus[cpu_index].props;
+    assert(cpu_index < possible_cpus->len);
+    return possible_cpus->cpus[cpu_index].props;
 }
 
 static const CPUArchIdList *s390_possible_cpu_arch_ids(MachineState *ms)
@@ -664,7 +669,12 @@ bool css_migration_enabled(void)
     type_init(ccw_machine_register_##suffix)
 
 #define CCW_COMPAT_2_11 \
-        HW_COMPAT_2_11
+        HW_COMPAT_2_11 \
+        {\
+            .driver   = TYPE_SCLP_EVENT_FACILITY,\
+            .property = "allow_all_mask_sizes",\
+            .value    = "off",\
+        },
 
 #define CCW_COMPAT_2_10 \
         HW_COMPAT_2_10
index 3ee890b392dc5845f2522ad5fb5eae2e150845cf..50c021b9c23d6c3b57c65362c8081dd7f56ab043 100644 (file)
@@ -37,12 +37,12 @@ void raise_irq_cpu_hotplug(void)
     sclp_service_interrupt(0);
 }
 
-static unsigned int send_mask(void)
+static sccb_mask_t send_mask(void)
 {
     return SCLP_EVENT_MASK_CONFIG_MGT_DATA;
 }
 
-static unsigned int receive_mask(void)
+static sccb_mask_t receive_mask(void)
 {
     return 0;
 }
index 02416435a1abd98a8bec12477a4e92d36e018881..1c8f5c9393f33a493149b7dbd219d9ee428153c1 100644 (file)
@@ -28,12 +28,12 @@ static bool can_handle_event(uint8_t type)
     return type == SCLP_EVENT_SIGNAL_QUIESCE;
 }
 
-static unsigned int send_mask(void)
+static sccb_mask_t send_mask(void)
 {
     return SCLP_EVENT_MASK_SIGNAL_QUIESCE;
 }
 
-static unsigned int receive_mask(void)
+static sccb_mask_t receive_mask(void)
 {
     return 0;
 }
index 8f7fbc2ab713dd5f03f1801cc5f561fab635cdd8..e51fbefd2380d612fc15934fbe9d003764ae4ffd 100644 (file)
@@ -752,7 +752,7 @@ out_err:
     g_free(sch);
 }
 
-static int virtio_ccw_exit(VirtioCcwDevice *dev)
+static void virtio_ccw_unrealize(VirtioCcwDevice *dev, Error **errp)
 {
     CcwDevice *ccw_dev = CCW_DEVICE(dev);
     SubchDev *sch = ccw_dev->sch;
@@ -760,12 +760,12 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev)
     if (sch) {
         css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
         g_free(sch);
+        ccw_dev->sch = NULL;
     }
     if (dev->indicators) {
         release_indicator(&dev->routes.adapter, dev->indicators);
         dev->indicators = NULL;
     }
-    return 0;
 }
 
 static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp)
@@ -1344,7 +1344,7 @@ static void virtio_ccw_net_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = virtio_ccw_net_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_net_properties;
     set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
@@ -1372,7 +1372,7 @@ static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = virtio_ccw_blk_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_blk_properties;
     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
@@ -1400,7 +1400,7 @@ static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = virtio_ccw_serial_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_serial_properties;
     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
@@ -1428,7 +1428,7 @@ static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = virtio_ccw_balloon_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_balloon_properties;
     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
@@ -1456,7 +1456,7 @@ static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = virtio_ccw_scsi_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_scsi_properties;
     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
@@ -1483,7 +1483,7 @@ static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = vhost_ccw_scsi_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = vhost_ccw_scsi_properties;
     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
@@ -1520,7 +1520,7 @@ static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = virtio_ccw_rng_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_rng_properties;
     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
@@ -1558,7 +1558,7 @@ static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = virtio_ccw_crypto_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_crypto_properties;
     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
@@ -1596,7 +1596,7 @@ static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = virtio_ccw_gpu_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_gpu_properties;
     dc->hotpluggable = false;
@@ -1625,7 +1625,7 @@ static void virtio_ccw_input_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = virtio_ccw_input_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_input_properties;
     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
@@ -1705,12 +1705,12 @@ static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp)
     virtio_ccw_device_realize(_dev, errp);
 }
 
-static int virtio_ccw_busdev_exit(DeviceState *dev)
+static void virtio_ccw_busdev_unrealize(DeviceState *dev, Error **errp)
 {
     VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev;
     VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
 
-    return _info->exit(_dev);
+    _info->unrealize(_dev, errp);
 }
 
 static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev,
@@ -1728,7 +1728,7 @@ static void virtio_ccw_device_class_init(ObjectClass *klass, void *data)
 
     k->unplug = virtio_ccw_busdev_unplug;
     dc->realize = virtio_ccw_busdev_realize;
-    dc->exit = virtio_ccw_busdev_exit;
+    dc->unrealize = virtio_ccw_busdev_unrealize;
     dc->bus_type = TYPE_VIRTUAL_CSS_BUS;
 }
 
@@ -1804,7 +1804,7 @@ static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     k->realize = virtio_ccw_9p_realize;
     dc->reset = virtio_ccw_reset;
     dc->props = virtio_ccw_9p_properties;
@@ -1853,7 +1853,7 @@ static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data)
     VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass);
 
     k->realize = vhost_vsock_ccw_realize;
-    k->exit = virtio_ccw_exit;
+    k->unrealize = virtio_ccw_unrealize;
     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
     dc->props = vhost_vsock_ccw_properties;
     dc->reset = virtio_ccw_reset;
index 3905f3a3d63c9622faf2c99f90b63633a4b83348..2fc513001e09f1501692fc4762a5e3a71c936edb 100644 (file)
@@ -76,7 +76,7 @@ typedef struct VirtioCcwDevice VirtioCcwDevice;
 typedef struct VirtIOCCWDeviceClass {
     CCWDeviceClass parent_class;
     void (*realize)(VirtioCcwDevice *dev, Error **errp);
-    int (*exit)(VirtioCcwDevice *dev);
+    void (*unrealize)(VirtioCcwDevice *dev, Error **errp);
 } VirtIOCCWDeviceClass;
 
 /* Performance improves when virtqueue kick processing is decoupled from the
index 191505df5bcca6c210bc025555628ee228538c7b..f3d4c4d2308db001309607f8a15bde291dea619a 100644 (file)
@@ -2277,5 +2277,5 @@ void lsi53c895a_create(PCIBus *bus)
 {
     LSIState *s = LSI53C895A(pci_create_simple(bus, -1, "lsi53c895a"));
 
-    scsi_bus_legacy_handle_cmdline(&s->bus, false);
+    scsi_bus_legacy_handle_cmdline(&s->bus);
 }
index b7bafbed6edb7578c496fff3562862f2b1826b09..1eaeffc830cb621d3b758e5f12dd7d49448911f2 100644 (file)
@@ -271,7 +271,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk,
     return SCSI_DEVICE(dev);
 }
 
-void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, bool deprecated)
+void scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
 {
     Location loc;
     DriveInfo *dinfo;
@@ -284,59 +284,12 @@ void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, bool deprecated)
             continue;
         }
         qemu_opts_loc_restore(dinfo->opts);
-        if (deprecated) {
-            /* Handling -drive not claimed by machine initialization */
-            if (blk_get_attached_dev(blk_by_legacy_dinfo(dinfo))) {
-                continue;       /* claimed */
-            }
-            if (!dinfo->is_default) {
-                warn_report("bus=%d,unit=%d is deprecated with this"
-                            " machine type",
-                            bus->busnr, unit);
-            }
-        }
         scsi_bus_legacy_add_drive(bus, blk_by_legacy_dinfo(dinfo),
                                   unit, false, -1, false, NULL, &error_fatal);
     }
     loc_pop(&loc);
 }
 
-static bool is_scsi_hba_with_legacy_magic(Object *obj)
-{
-    static const char *magic[] = {
-        "am53c974", "dc390", "esp", "lsi53c810", "lsi53c895a",
-        "megasas", "megasas-gen2", "mptsas1068", "spapr-vscsi",
-        "virtio-scsi-device",
-        NULL
-    };
-    const char *typename = object_get_typename(obj);
-    int i;
-
-    for (i = 0; magic[i]; i++)
-        if (!strcmp(typename, magic[i])) {
-            return true;
-    }
-
-    return false;
-}
-
-static int scsi_legacy_handle_cmdline_cb(Object *obj, void *opaque)
-{
-    SCSIBus *bus = (SCSIBus *)object_dynamic_cast(obj, TYPE_SCSI_BUS);
-
-    if (bus && is_scsi_hba_with_legacy_magic(OBJECT(bus->qbus.parent))) {
-        scsi_bus_legacy_handle_cmdline(bus, true);
-    }
-
-    return 0;
-}
-
-void scsi_legacy_handle_cmdline(void)
-{
-    object_child_foreach_recursive(object_get_root(),
-                                   scsi_legacy_handle_cmdline_cb, NULL);
-}
-
 static int32_t scsi_invalid_field(SCSIRequest *req, uint8_t *buf)
 {
     scsi_req_build_sense(req, SENSE_CODE(INVALID_FIELD));
index 360db53ac8b5f764d107c4b4bc2d596e869fc554..a9e49c7cb58ab24c89ebc9fa0d6b83933d5d0ec8 100644 (file)
@@ -1215,8 +1215,7 @@ void spapr_vscsi_create(VIOsPAPRBus *bus)
     dev = qdev_create(&bus->bus, "spapr-vscsi");
 
     qdev_init_nofail(dev);
-    scsi_bus_legacy_handle_cmdline(&VIO_SPAPR_VSCSI_DEVICE(dev)->bus,
-                                   false);
+    scsi_bus_legacy_handle_cmdline(&VIO_SPAPR_VSCSI_DEVICE(dev)->bus);
 }
 
 static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off)
index 1c33322ba646c97f2b901a131787d6e1624651d6..912e5005d84530a57d44110e065303be4527e245 100644 (file)
@@ -107,9 +107,10 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n,
     return 0;
 }
 
-/* assumes s->ctx held */
-static void virtio_scsi_clear_aio(VirtIOSCSI *s)
+/* Context: BH in IOThread */
+static void virtio_scsi_dataplane_stop_bh(void *opaque)
 {
+    VirtIOSCSI *s = opaque;
     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
     int i;
 
@@ -171,7 +172,7 @@ int virtio_scsi_dataplane_start(VirtIODevice *vdev)
     return 0;
 
 fail_vrings:
-    virtio_scsi_clear_aio(s);
+    aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
     aio_context_release(s->ctx);
     for (i = 0; i < vs->conf.num_queues + 2; i++) {
         virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
@@ -207,7 +208,7 @@ void virtio_scsi_dataplane_stop(VirtIODevice *vdev)
     s->dataplane_stopping = true;
 
     aio_context_acquire(s->ctx);
-    virtio_scsi_clear_aio(s);
+    aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s);
     aio_context_release(s->ctx);
 
     blk_drain_all(); /* ensure there are no in-flight requests */
index 61eb424bbc73d41afa60568b9addd7344f33ed7f..0f5804b3b46037cc16689abfabdbe974f6bedfe0 100644 (file)
@@ -324,6 +324,7 @@ static void *sparc32_dma_init(hwaddr dma_base,
 
     esp = ESP_STATE(object_resolve_path_component(OBJECT(espdma), "esp"));
     sysbus_mmio_map(SYS_BUS_DEVICE(esp), 0, esp_base);
+    scsi_bus_legacy_handle_cmdline(&esp->esp.bus);
 
     ledma = SPARC32_LEDMA_DEVICE(object_resolve_path_component(
                                  OBJECT(dma), "ledma"));
index b5b8256360def7790fe0c396d18aa6417c37df3e..d8917cb101f2a63b77380dd4574ce12e01c4b49f 100644 (file)
@@ -29,6 +29,7 @@
 #include "sysemu/reset.h"
 #include "tpm_int.h"
 #include "tpm_util.h"
+#include "trace.h"
 
 typedef struct CRBState {
     DeviceState parent_obj;
@@ -44,14 +45,6 @@ typedef struct CRBState {
 
 #define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB)
 
-#define DEBUG_CRB 0
-
-#define DPRINTF(fmt, ...) do {                  \
-        if (DEBUG_CRB) {                        \
-            printf(fmt, ## __VA_ARGS__);        \
-        }                                       \
-    } while (0)
-
 #define CRB_INTF_TYPE_CRB_ACTIVE 0b1
 #define CRB_INTF_VERSION_CRB 0b1
 #define CRB_INTF_CAP_LOCALITY_0_ONLY 0b0
@@ -91,8 +84,8 @@ static uint64_t tpm_crb_mmio_read(void *opaque, hwaddr addr,
     unsigned offset = addr & 3;
     uint32_t val = *(uint32_t *)regs >> (8 * offset);
 
-    DPRINTF("CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 "\n",
-            addr, size, val);
+    trace_tpm_crb_mmio_read(addr, size, val);
+
     return val;
 }
 
@@ -100,8 +93,8 @@ static void tpm_crb_mmio_write(void *opaque, hwaddr addr,
                                uint64_t val, unsigned size)
 {
     CRBState *s = CRB(opaque);
-    DPRINTF("CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx64 "\n",
-            addr, size, val);
+
+    trace_tpm_crb_mmio_write(addr, size, val);
 
     switch (addr) {
     case A_CRB_CTRL_REQ:
index f187a72c101597b0250429110118efaf9e8a14cc..6418ef08310f401de5fea9c429a4c97bfd21e41f 100644 (file)
 #include "qapi/clone-visitor.h"
 #include "qapi/qapi-visit-tpm.h"
 #include "chardev/char-fe.h"
-
-#define DEBUG_TPM 0
-
-#define DPRINTF(fmt, ...) do { \
-    if (DEBUG_TPM) { \
-        fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \
-    } \
-} while (0)
+#include "trace.h"
 
 #define TYPE_TPM_EMULATOR "tpm-emulator"
 #define TPM_EMULATOR(obj) \
@@ -152,13 +145,12 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
 {
     ptm_loc loc;
 
-    DPRINTF("%s : locality: 0x%x", __func__, locty_number);
-
     if (tpm_emu->cur_locty_number == locty_number) {
         return 0;
     }
 
-    DPRINTF("setting locality : 0x%x", locty_number);
+    trace_tpm_emulator_set_locality(locty_number);
+
     loc.u.req.loc = locty_number;
     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc,
                              sizeof(loc), sizeof(loc)) < 0) {
@@ -184,7 +176,7 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
 {
     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
 
-    DPRINTF("processing TPM command");
+    trace_tpm_emulator_handle_request();
 
     if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 ||
         tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
@@ -196,7 +188,6 @@ static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
 
 static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
 {
-    DPRINTF("%s", __func__);
     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY,
                              &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) {
         error_report("tpm-emulator: probing failed : %s", strerror(errno));
@@ -205,7 +196,7 @@ static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
 
     tpm_emu->caps = be64_to_cpu(tpm_emu->caps);
 
-    DPRINTF("capabilities : 0x%"PRIx64, tpm_emu->caps);
+    trace_tpm_emulator_probe_caps(tpm_emu->caps);
 
     return 0;
 }
@@ -294,7 +285,7 @@ static int tpm_emulator_set_buffer_size(TPMBackend *tb,
         *actual_size = be32_to_cpu(psbs.u.resp.buffersize);
     }
 
-    DPRINTF("buffer size: %u, min: %u, max: %u\n",
+    trace_tpm_emulator_set_buffer_size(
             be32_to_cpu(psbs.u.resp.buffersize),
             be32_to_cpu(psbs.u.resp.minsize),
             be32_to_cpu(psbs.u.resp.maxsize));
@@ -315,7 +306,7 @@ static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
         goto err_exit;
     }
 
-    DPRINTF("%s", __func__);
+    trace_tpm_emulator_startup_tpm();
     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
                              sizeof(init)) < 0) {
         error_report("tpm-emulator: could not send INIT: %s",
@@ -349,7 +340,7 @@ static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
                      strerror(errno));
         return false;
     }
-    DPRINTF("got established flag: %0x", est.u.resp.bit);
+    trace_tpm_emulator_get_tpm_established_flag(est.u.resp.bit);
 
     tpm_emu->established_flag_cached = 1;
     tpm_emu->established_flag = (est.u.resp.bit != 0);
@@ -396,7 +387,7 @@ static void tpm_emulator_cancel_cmd(TPMBackend *tb)
     ptm_res res;
 
     if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) {
-        DPRINTF("Backend does not support CANCEL_TPM_CMD");
+        trace_tpm_emulator_cancel_cmd_not_supt();
         return;
     }
 
@@ -522,8 +513,16 @@ static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
         goto err;
     }
 
-    DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" :
-            (tpm_emu->tpm_version == TPM_VERSION_2_0 ?  "2.0" : "Unspecified"));
+    switch (tpm_emu->tpm_version) {
+    case TPM_VERSION_1_2:
+        trace_tpm_emulator_handle_device_opts_tpm12();
+        break;
+    case TPM_VERSION_2_0:
+        trace_tpm_emulator_handle_device_opts_tpm2();
+        break;
+    default:
+        trace_tpm_emulator_handle_device_opts_unspec();
+    }
 
     if (tpm_emulator_probe_caps(tpm_emu) ||
         tpm_emulator_check_caps(tpm_emu)) {
@@ -533,7 +532,8 @@ static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
     return tpm_emulator_block_migration(tpm_emu);
 
 err:
-    DPRINTF("Startup error");
+    trace_tpm_emulator_handle_device_opts_startup_error();
+
     return -1;
 }
 
@@ -574,7 +574,8 @@ static void tpm_emulator_inst_init(Object *obj)
 {
     TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
 
-    DPRINTF("%s", __func__);
+    trace_tpm_emulator_inst_init();
+
     tpm_emu->options = g_new0(TPMEmulatorOptions, 1);
     tpm_emu->cur_locty_number = ~0;
     qemu_mutex_init(&tpm_emu->mutex);
index 211df3191c96a2e79fc4326067848832d0de318a..479317ee508b2d5a67e92de1fd03c7860490232b 100644 (file)
 #include "qapi/clone-visitor.h"
 #include "qapi/qapi-visit-tpm.h"
 #include "tpm_util.h"
-
-#define DEBUG_TPM 0
-
-#define DPRINTF(fmt, ...) do { \
-    if (DEBUG_TPM) { \
-        fprintf(stderr, fmt, ## __VA_ARGS__); \
-    } \
-} while (0)
+#include "trace.h"
 
 #define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
 #define TPM_PASSTHROUGH(obj) \
@@ -138,7 +131,7 @@ static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
 {
     TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb);
 
-    DPRINTF("tpm_passthrough: processing command %p\n", cmd);
+    trace_tpm_passthrough_handle_request(cmd);
 
     tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len,
                                  cmd->out, cmd->out_len, &cmd->selftest_done,
@@ -147,7 +140,7 @@ static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
 
 static void tpm_passthrough_reset(TPMBackend *tb)
 {
-    DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n");
+    trace_tpm_passthrough_reset();
 
     tpm_passthrough_cancel_cmd(tb);
 }
index 834eef75fac0083807a50feaac3b9efef63f946c..2ac7e7430799bf8f94ac61d80ace70e48b0c98e7 100644 (file)
@@ -31,6 +31,7 @@
 #include "sysemu/tpm_backend.h"
 #include "tpm_int.h"
 #include "tpm_util.h"
+#include "trace.h"
 
 #define TPM_TIS_NUM_LOCALITIES      5     /* per spec */
 #define TPM_TIS_LOCALITY_SHIFT      12
@@ -86,12 +87,6 @@ typedef struct TPMState {
 
 #define DEBUG_TIS 0
 
-#define DPRINTF(fmt, ...) do { \
-    if (DEBUG_TIS) { \
-        printf(fmt, ## __VA_ARGS__); \
-    } \
-} while (0)
-
 /* local prototypes */
 
 static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
@@ -107,19 +102,17 @@ static uint8_t tpm_tis_locality_from_addr(hwaddr addr)
 static void tpm_tis_show_buffer(const unsigned char *buffer,
                                 size_t buffer_size, const char *string)
 {
-#ifdef DEBUG_TIS
     uint32_t len, i;
 
     len = MIN(tpm_cmd_get_size(buffer), buffer_size);
-    DPRINTF("tpm_tis: %s length = %d\n", string, len);
+    printf("tpm_tis: %s length = %d\n", string, len);
     for (i = 0; i < len; i++) {
         if (i && !(i % 16)) {
-            DPRINTF("\n");
+            printf("\n");
         }
-        DPRINTF("%.2X ", buffer[i]);
+        printf("%.2X ", buffer[i]);
     }
-    DPRINTF("\n");
-#endif
+    printf("\n");
 }
 
 /*
@@ -146,8 +139,10 @@ static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags)
  */
 static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
 {
-    tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
-                        "tpm_tis: To TPM");
+    if (DEBUG_TIS) {
+        tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
+                            "tpm_tis: To TPM");
+    }
 
     /*
      * rw_offset serves as length indicator for length of data;
@@ -175,7 +170,7 @@ static void tpm_tis_raise_irq(TPMState *s, uint8_t locty, uint32_t irqmask)
 
     if ((s->loc[locty].inte & TPM_TIS_INT_ENABLED) &&
         (s->loc[locty].inte & irqmask)) {
-        DPRINTF("tpm_tis: Raising IRQ for flag %08x\n", irqmask);
+        trace_tpm_tis_raise_irq(irqmask);
         qemu_irq_raise(s->irq);
         s->loc[locty].ints |= irqmask;
     }
@@ -223,7 +218,7 @@ static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty)
 
     s->active_locty = new_active_locty;
 
-    DPRINTF("tpm_tis: Active locality is now %d\n", s->active_locty);
+    trace_tpm_tis_new_active_locality(s->active_locty);
 
     if (TPM_TIS_IS_VALID_LOCTY(new_active_locty)) {
         /* set flags on the new active locality */
@@ -242,7 +237,7 @@ static void tpm_tis_abort(TPMState *s, uint8_t locty)
 {
     s->rw_offset = 0;
 
-    DPRINTF("tpm_tis: tis_abort: new active locality is %d\n", s->next_locty);
+    trace_tpm_tis_abort(s->next_locty);
 
     /*
      * Need to react differently depending on who's aborting now and
@@ -310,8 +305,10 @@ static void tpm_tis_request_completed(TPMIf *ti, int ret)
     s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
     s->rw_offset = 0;
 
-    tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
-                        "tpm_tis: From TPM");
+    if (DEBUG_TIS) {
+        tpm_tis_show_buffer(s->buffer, s->be_buffer_size,
+                            "tpm_tis: From TPM");
+    }
 
     if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
         tpm_tis_abort(s, locty);
@@ -339,8 +336,7 @@ static uint32_t tpm_tis_data_read(TPMState *s, uint8_t locty)
             tpm_tis_sts_set(&s->loc[locty], TPM_TIS_STS_VALID);
             tpm_tis_raise_irq(s, locty, TPM_TIS_INT_STS_VALID);
         }
-        DPRINTF("tpm_tis: tpm_tis_data_read byte 0x%02x   [%d]\n",
-                ret, s->rw_offset - 1);
+        trace_tpm_tis_data_read(ret, s->rw_offset - 1);
     }
 
     return ret;
@@ -364,29 +360,29 @@ static void tpm_tis_dump_state(void *opaque, hwaddr addr)
     hwaddr base = addr & ~0xfff;
     TPMState *s = opaque;
 
-    DPRINTF("tpm_tis: active locality      : %d\n"
-            "tpm_tis: state of locality %d : %d\n"
-            "tpm_tis: register dump:\n",
-            s->active_locty,
-            locty, s->loc[locty].state);
+    printf("tpm_tis: active locality      : %d\n"
+           "tpm_tis: state of locality %d : %d\n"
+           "tpm_tis: register dump:\n",
+           s->active_locty,
+           locty, s->loc[locty].state);
 
     for (idx = 0; regs[idx] != 0xfff; idx++) {
-        DPRINTF("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
-                (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
+        printf("tpm_tis: 0x%04x : 0x%08x\n", regs[idx],
+               (int)tpm_tis_mmio_read(opaque, base + regs[idx], 4));
     }
 
-    DPRINTF("tpm_tis: r/w offset    : %d\n"
-            "tpm_tis: result buffer : ",
-            s->rw_offset);
+    printf("tpm_tis: r/w offset    : %d\n"
+           "tpm_tis: result buffer : ",
+           s->rw_offset);
     for (idx = 0;
          idx < MIN(tpm_cmd_get_size(&s->buffer), s->be_buffer_size);
          idx++) {
-        DPRINTF("%c%02x%s",
-                s->rw_offset == idx ? '>' : ' ',
-                s->buffer[idx],
-                ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
+        printf("%c%02x%s",
+               s->rw_offset == idx ? '>' : ' ',
+               s->buffer[idx],
+               ((idx & 0xf) == 0xf) ? "\ntpm_tis:                 " : "");
     }
-    DPRINTF("\n");
+    printf("\n");
 }
 #endif
 
@@ -506,7 +502,7 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr,
         val >>= shift;
     }
 
-    DPRINTF("tpm_tis:  read.%u(%08x) = %08x\n", size, (int)addr, (int)val);
+    trace_tpm_tis_mmio_read(size, addr, val);
 
     return val;
 }
@@ -527,10 +523,10 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
     uint16_t len;
     uint32_t mask = (size == 1) ? 0xff : ((size == 2) ? 0xffff : ~0);
 
-    DPRINTF("tpm_tis: write.%u(%08x) = %08x\n", size, (int)addr, (int)val);
+    trace_tpm_tis_mmio_write(size, addr, val);
 
     if (locty == 4) {
-        DPRINTF("tpm_tis: Access to locality 4 only allowed from hardware\n");
+        trace_tpm_tis_mmio_write_locty4();
         return;
     }
 
@@ -560,20 +556,18 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
         if ((val & TPM_TIS_ACCESS_ACTIVE_LOCALITY)) {
             /* give up locality if currently owned */
             if (s->active_locty == locty) {
-                DPRINTF("tpm_tis: Releasing locality %d\n", locty);
+                trace_tpm_tis_mmio_write_release_locty(locty);
 
                 uint8_t newlocty = TPM_TIS_NO_LOCALITY;
                 /* anybody wants the locality ? */
                 for (c = TPM_TIS_NUM_LOCALITIES - 1; c >= 0; c--) {
                     if ((s->loc[c].access & TPM_TIS_ACCESS_REQUEST_USE)) {
-                        DPRINTF("tpm_tis: Locality %d requests use.\n", c);
+                        trace_tpm_tis_mmio_write_locty_req_use(c);
                         newlocty = c;
                         break;
                     }
                 }
-                DPRINTF("tpm_tis: TPM_TIS_ACCESS_ACTIVE_LOCALITY: "
-                        "Next active locality: %d\n",
-                        newlocty);
+                trace_tpm_tis_mmio_write_next_locty(newlocty);
 
                 if (TPM_TIS_IS_VALID_LOCTY(newlocty)) {
                     set_new_locty = 0;
@@ -627,10 +621,10 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
                 }
 
                 s->loc[locty].access |= TPM_TIS_ACCESS_SEIZE;
-                DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: "
-                        "Locality %d seized from locality %d\n",
-                        locty, s->active_locty);
-                DPRINTF("tpm_tis: TPM_TIS_ACCESS_SEIZE: Initiating abort.\n");
+
+                trace_tpm_tis_mmio_write_locty_seized(locty, s->active_locty);
+                trace_tpm_tis_mmio_write_init_abort();
+
                 set_new_locty = 0;
                 tpm_tis_prep_abort(s, s->active_locty, locty);
                 break;
@@ -677,7 +671,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
             s->loc[locty].ints &= ~val;
             if (s->loc[locty].ints == 0) {
                 qemu_irq_lower(s->irq);
-                DPRINTF("tpm_tis: Lowering IRQ\n");
+                trace_tpm_tis_mmio_write_lowering_irq();
             }
         }
         s->loc[locty].ints &= ~(val & TPM_TIS_INTERRUPTS_SUPPORTED);
@@ -725,8 +719,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
             case TPM_TIS_STATE_EXECUTION:
             case TPM_TIS_STATE_RECEPTION:
                 /* abort currently running command */
-                DPRINTF("tpm_tis: %s: Initiating abort.\n",
-                        __func__);
+                trace_tpm_tis_mmio_write_init_abort();
                 tpm_tis_prep_abort(s, locty, locty);
             break;
 
@@ -780,8 +773,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr,
             s->loc[locty].state == TPM_TIS_STATE_COMPLETION) {
             /* drop the byte */
         } else {
-            DPRINTF("tpm_tis: Data to send to TPM: %08x (size=%d)\n",
-                    (int)val, size);
+            trace_tpm_tis_mmio_write_data2send(val, size);
             if (s->loc[locty].state == TPM_TIS_STATE_READY) {
                 s->loc[locty].state = TPM_TIS_STATE_RECEPTION;
                 tpm_tis_sts_set(&s->loc[locty],
index 2de52a0f1be6dfac3b7c18aeea798f470bd67db9..ee41757ea2c195c55c661c5d803bef77ba61921d 100644 (file)
 #include "exec/memory.h"
 #include "sysemu/tpm_backend.h"
 #include "hw/qdev.h"
-
-#define DEBUG_TPM 0
-
-#define DPRINTF(fmt, ...) do { \
-    if (DEBUG_TPM) { \
-        fprintf(stderr, "tpm-util:"fmt"\n", ## __VA_ARGS__); \
-    } \
-} while (0)
+#include "trace.h"
 
 /* tpm backend property */
 
@@ -279,10 +272,11 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
 
         if (be32_to_cpu(tpm_resp.hdr.len) != sizeof(tpm_resp) ||
             be32_to_cpu(tpm_resp.len) != sizeof(uint32_t)) {
-            DPRINTF("tpm_resp->hdr.len = %u, expected = %zu\n",
-                    be32_to_cpu(tpm_resp.hdr.len), sizeof(tpm_resp));
-            DPRINTF("tpm_resp->len = %u, expected = %zu\n",
-                    be32_to_cpu(tpm_resp.len), sizeof(uint32_t));
+            trace_tpm_util_get_buffer_size_hdr_len(
+                be32_to_cpu(tpm_resp.hdr.len),
+                sizeof(tpm_resp));
+            trace_tpm_util_get_buffer_size_len(be32_to_cpu(tpm_resp.len),
+                                               sizeof(uint32_t));
             error_report("tpm_util: Got unexpected response to "
                          "TPM_GetCapability; errcode: 0x%x",
                          be32_to_cpu(tpm_resp.hdr.errcode));
@@ -327,10 +321,11 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
 
         if (be32_to_cpu(tpm2_resp.hdr.len) != sizeof(tpm2_resp) ||
             be32_to_cpu(tpm2_resp.count) != 2) {
-            DPRINTF("tpm2_resp->hdr.len = %u, expected = %zu\n",
-                    be32_to_cpu(tpm2_resp.hdr.len), sizeof(tpm2_resp));
-            DPRINTF("tpm2_resp->len = %u, expected = %u\n",
-                    be32_to_cpu(tpm2_resp.count), 2);
+            trace_tpm_util_get_buffer_size_hdr_len2(
+                be32_to_cpu(tpm2_resp.hdr.len),
+                sizeof(tpm2_resp));
+            trace_tpm_util_get_buffer_size_len2(
+                be32_to_cpu(tpm2_resp.count), 2);
             error_report("tpm_util: Got unexpected response to "
                          "TPM2_GetCapability; errcode: 0x%x",
                          be32_to_cpu(tpm2_resp.hdr.errcode));
@@ -344,7 +339,7 @@ int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
         return -EFAULT;
     }
 
-    DPRINTF("buffersize of device: %zu\n", *buffersize);
+    trace_tpm_util_get_buffer_size(*buffersize);
 
     return 0;
 }
diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events
new file mode 100644 (file)
index 0000000..9a65384
--- /dev/null
@@ -0,0 +1,46 @@
+# See docs/devel/tracing.txt for syntax documentation.
+
+# hw/tpm/tpm_crb.c
+tpm_crb_mmio_read(uint64_t addr, unsigned size, uint32_t val) "CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32
+tpm_crb_mmio_write(uint64_t addr, unsigned size, uint32_t val) "CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32
+
+# hw/tpm/tpm_passthrough.c
+tpm_passthrough_handle_request(void *cmd) "processing command %p"
+tpm_passthrough_reset(void) "reset"
+
+# hw/tpm/tpm_util.c
+tpm_util_get_buffer_size_hdr_len(uint32_t len, size_t expected) "tpm_resp->hdr.len = %u, expected = %zu"
+tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, expected = %zu"
+tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu"
+tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu"
+tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu"
+
+# hw/tpm/tpm_emulator.c
+tpm_emulator_set_locality(uint8_t locty) "setting locality to %d"
+tpm_emulator_handle_request(void) "processing TPM command"
+tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64
+tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u"
+tpm_emulator_startup_tpm(void) "startup"
+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_inst_init(void) ""
+
+# hw/tpm/tpm_tis.c
+tpm_tis_raise_irq(uint32_t irqmask) "Raising IRQ for flag 0x%08x"
+tpm_tis_new_active_locality(uint8_t locty) "Active locality is now %d"
+tpm_tis_abort(uint8_t locty) "New active locality is %d"
+tpm_tis_data_read(uint32_t value, uint32_t off) "byte 0x%02x   [%d]"
+tpm_tis_mmio_read(unsigned size, uint32_t addr, uint32_t val)  " read.%u(0x%08x) = 0x%08x"
+tpm_tis_mmio_write(unsigned size, uint32_t addr, uint32_t val) "write.%u(0x%08x) = 0x%08x"
+tpm_tis_mmio_write_locty4(void) "Access to locality 4 only allowed from hardware"
+tpm_tis_mmio_write_release_locty(uint8_t locty) "Releasing locality %d"
+tpm_tis_mmio_write_locty_req_use(uint8_t locty) "Locality %d requests use"
+tpm_tis_mmio_write_next_locty(uint8_t locty) "Next active locality is %d"
+tpm_tis_mmio_write_locty_seized(uint8_t locty, uint8_t active) "Locality %d seized from locality %d"
+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)"
index 16713f2c5218bf6573554bc6b3513a56046fe393..4e5855741a64d424111621e463522195745d793b 100644 (file)
@@ -6,8 +6,8 @@
  *            Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
  *            Pierre Morel <pmorel@linux.vnet.ibm.com>
  *
- * This work is licensed under the terms of the GNU GPL, version 2 or(at
- * your option) any version. See the COPYING file in the top-level
+ * 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.
  */
 
index 033cc8dea1b98c9a8331bdd4f41277dc5d9ed797..3ba3cbc1460ba60d4bd0e9bcb15fb473298f7ea3 100644 (file)
@@ -2807,7 +2807,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp)
         return;
     }
 
-    vdev->vbasedev.name = g_strdup(basename(vdev->vbasedev.sysfsdev));
+    vdev->vbasedev.name = g_path_get_basename(vdev->vbasedev.sysfsdev);
     vdev->vbasedev.ops = &vfio_pci_ops;
     vdev->vbasedev.type = VFIO_DEVICE_TYPE_PCI;
     vdev->vbasedev.dev = &vdev->pdev.qdev;
index 0d4bc0aae8891435eec55d9c6aacf4b339a865fe..5c921c27bab1a2b0037b0efde7405c7f8959bf49 100644 (file)
@@ -561,7 +561,7 @@ static int vfio_base_device_init(VFIODevice *vbasedev, Error **errp)
     /* @sysfsdev takes precedence over @host */
     if (vbasedev->sysfsdev) {
         g_free(vbasedev->name);
-        vbasedev->name = g_strdup(basename(vbasedev->sysfsdev));
+        vbasedev->name = g_path_get_basename(vbasedev->sysfsdev);
     } else {
         if (!vbasedev->name || strchr(vbasedev->name, '/')) {
             error_setg(errp, "wrong host device name");
diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h
new file mode 100644 (file)
index 0000000..f7a3972
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * AioContext wait support
+ *
+ * Copyright (C) 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 QEMU_AIO_WAIT_H
+#define QEMU_AIO_WAIT_H
+
+#include "block/aio.h"
+
+/**
+ * AioWait:
+ *
+ * An object that facilitates synchronous waiting on a condition.  The main
+ * loop can wait on an operation running in an IOThread as follows:
+ *
+ *   AioWait *wait = ...;
+ *   AioContext *ctx = ...;
+ *   MyWork work = { .done = false };
+ *   schedule_my_work_in_iothread(ctx, &work);
+ *   AIO_WAIT_WHILE(wait, ctx, !work.done);
+ *
+ * The IOThread must call aio_wait_kick() to notify the main loop when
+ * work.done changes:
+ *
+ *   static void do_work(...)
+ *   {
+ *       ...
+ *       work.done = true;
+ *       aio_wait_kick(wait);
+ *   }
+ */
+typedef struct {
+    /* Is the main loop waiting for a kick?  Accessed with atomic ops. */
+    bool need_kick;
+} AioWait;
+
+/**
+ * AIO_WAIT_WHILE:
+ * @wait: the aio wait object
+ * @ctx: the aio context
+ * @cond: wait while this conditional expression is true
+ *
+ * Wait while a condition is true.  Use this to implement synchronous
+ * operations that require event loop activity.
+ *
+ * The caller must be sure that something calls aio_wait_kick() when the value
+ * of @cond might have changed.
+ *
+ * The caller's thread must be the IOThread that owns @ctx or the main loop
+ * thread (with @ctx acquired exactly once).  This function cannot be used to
+ * wait on conditions between two IOThreads since that could lead to deadlock,
+ * go via the main loop instead.
+ */
+#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_;                    \
+        }                                                   \
+    } else {                                                \
+        assert(qemu_get_current_aio_context() ==            \
+               qemu_get_aio_context());                     \
+        assert(!wait_->need_kick);                          \
+        /* Set wait_->need_kick before evaluating cond.  */ \
+        atomic_mb_set(&wait_->need_kick, true);             \
+        while (busy_) {                                     \
+            if ((cond)) {                                   \
+                waited_ = busy_ = true;                     \
+                aio_context_release(ctx_);                  \
+                aio_poll(qemu_get_aio_context(), true);     \
+                aio_context_acquire(ctx_);                  \
+            } else {                                        \
+                busy_ = aio_poll(ctx_, false);              \
+                waited_ |= busy_;                           \
+            }                                               \
+        }                                                   \
+        atomic_set(&wait_->need_kick, false);               \
+    }                                                       \
+    waited_; })
+
+/**
+ * aio_wait_kick:
+ * @wait: the aio wait object that should re-evaluate its condition
+ *
+ * Wake up the main thread if it is waiting on AIO_WAIT_WHILE().  During
+ * synchronous operations performed in an IOThread, the main thread lets the
+ * IOThread's event loop run, waiting for the operation to complete.  A
+ * aio_wait_kick() call will wake up the main thread.
+ */
+void aio_wait_kick(AioWait *wait);
+
+/**
+ * aio_wait_bh_oneshot:
+ * @ctx: the aio context
+ * @cb: the BH callback function
+ * @opaque: user data for the BH callback function
+ *
+ * Run a BH in @ctx and wait for it to complete.
+ *
+ * Must be called from the main loop thread with @ctx acquired exactly once.
+ * Note that main loop event processing may occur.
+ */
+void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque);
+
+#endif /* QEMU_AIO_WAIT */
index e9aeeaec94484afe26f5b9d35c014ff7d117e710..a1d6b9e249394ddd43d9ba99026e3f48f31dffc2 100644 (file)
@@ -534,11 +534,14 @@ void aio_co_enter(AioContext *ctx, struct Coroutine *co);
 AioContext *qemu_get_current_aio_context(void);
 
 /**
+ * in_aio_context_home_thread:
  * @ctx: the aio context
  *
- * Return whether we are running in the I/O thread that manages @ctx.
+ * Return whether we are running in the thread that normally runs @ctx.  Note
+ * that acquiring/releasing ctx does not affect the outcome, each AioContext
+ * still only has one home thread that is responsible for running it.
  */
-static inline bool aio_context_in_iothread(AioContext *ctx)
+static inline bool in_aio_context_home_thread(AioContext *ctx)
 {
     return ctx == qemu_get_current_aio_context();
 }
index fac401ba3e679681be77cbbd8681e6891e3efef0..8b6db952a29b6343aaa101dc1e1c4f77264f5b50 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "block/aio.h"
 #include "qapi/qapi-types-block-core.h"
+#include "block/aio-wait.h"
 #include "qemu/iov.h"
 #include "qemu/coroutine.h"
 #include "block/accounting.h"
@@ -115,19 +116,19 @@ typedef struct HDGeometry {
  * BDRV_BLOCK_ZERO: offset reads as zero
  * BDRV_BLOCK_OFFSET_VALID: an associated offset exists for accessing raw data
  * BDRV_BLOCK_ALLOCATED: the content of the block is determined by this
- *                       layer (short for DATA || ZERO), set by block layer
- * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this layer
+ *                       layer rather than any backing, set by block layer
+ * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this
+ *                 layer, set by block layer
  *
  * Internal flag:
  * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request
  *                 that the block layer recompute the answer from the returned
  *                 BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID.
  *
- * If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 (BDRV_BLOCK_OFFSET_MASK) of
- * the return value (old interface) or the entire map parameter (new
- * interface) represent the offset in the returned BDS that is allocated for
- * the corresponding raw data.  However, whether that offset actually
- * contains data also depends on BDRV_BLOCK_DATA, as follows:
+ * If BDRV_BLOCK_OFFSET_VALID is set, the map parameter represents the
+ * host offset within the returned BDS that is allocated for the
+ * corresponding raw guest data.  However, whether that offset
+ * actually contains data also depends on BDRV_BLOCK_DATA, as follows:
  *
  * DATA ZERO OFFSET_VALID
  *  t    t        t       sectors read as zero, returned file is zero at offset
@@ -367,41 +368,14 @@ void bdrv_drain_all_begin(void);
 void bdrv_drain_all_end(void);
 void bdrv_drain_all(void);
 
+/* Returns NULL when bs == NULL */
+AioWait *bdrv_get_aio_wait(BlockDriverState *bs);
+
 #define BDRV_POLL_WHILE(bs, cond) ({                       \
-    bool waited_ = false;                                  \
-    bool busy_ = true;                                     \
     BlockDriverState *bs_ = (bs);                          \
-    AioContext *ctx_ = bdrv_get_aio_context(bs_);          \
-    if (aio_context_in_iothread(ctx_)) {                   \
-        while ((cond) || busy_) {                          \
-            busy_ = aio_poll(ctx_, (cond));                \
-            waited_ |= !!(cond) | busy_;                   \
-        }                                                  \
-    } else {                                               \
-        assert(qemu_get_current_aio_context() ==           \
-               qemu_get_aio_context());                    \
-        /* Ask bdrv_dec_in_flight to wake up the main      \
-         * QEMU AioContext.  Extra I/O threads never take  \
-         * other I/O threads' AioContexts (see for example \
-         * block_job_defer_to_main_loop for how to do it). \
-         */                                                \
-        assert(!bs_->wakeup);                              \
-        /* Set bs->wakeup before evaluating cond.  */      \
-        atomic_mb_set(&bs_->wakeup, true);                 \
-        while (busy_) {                                    \
-            if ((cond)) {                                  \
-                waited_ = busy_ = true;                    \
-                aio_context_release(ctx_);                 \
-                aio_poll(qemu_get_aio_context(), true);    \
-                aio_context_acquire(ctx_);                 \
-            } else {                                       \
-                busy_ = aio_poll(ctx_, false);             \
-                waited_ |= busy_;                          \
-            }                                              \
-        }                                                  \
-        atomic_set(&bs_->wakeup, false);                   \
-    }                                                      \
-    waited_; })
+    AIO_WAIT_WHILE(bdrv_get_aio_wait(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);
index 5ea63f8fa8ad4de5add475146e00b7158fd86797..64a5700f2b014d1d805ae39266e5259bb25724c1 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "block/accounting.h"
 #include "block/block.h"
+#include "block/aio-wait.h"
 #include "qemu/queue.h"
 #include "qemu/coroutine.h"
 #include "qemu/stats64.h"
@@ -128,7 +129,8 @@ struct BlockDriver {
     int (*bdrv_file_open)(BlockDriverState *bs, QDict *options, int flags,
                           Error **errp);
     void (*bdrv_close)(BlockDriverState *bs);
-    int (*bdrv_create)(const char *filename, QemuOpts *opts, Error **errp);
+    int coroutine_fn (*bdrv_co_create_opts)(const char *filename, QemuOpts *opts,
+                                       Error **errp);
     int (*bdrv_make_empty)(BlockDriverState *bs);
 
     void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options);
@@ -202,15 +204,22 @@ struct BlockDriver {
     /*
      * Building block for bdrv_block_status[_above] and
      * bdrv_is_allocated[_above].  The driver should answer only
-     * according to the current layer, and should not set
-     * BDRV_BLOCK_ALLOCATED, but may set BDRV_BLOCK_RAW.  See block.h
-     * for the meaning of _DATA, _ZERO, and _OFFSET_VALID.  The block
-     * layer guarantees input aligned to request_alignment, as well as
-     * non-NULL pnum and file.
+     * according to the current layer, and should only need to set
+     * BDRV_BLOCK_DATA, BDRV_BLOCK_ZERO, BDRV_BLOCK_OFFSET_VALID,
+     * and/or BDRV_BLOCK_RAW; if the current layer defers to a backing
+     * layer, the result should be 0 (and not BDRV_BLOCK_ZERO).  See
+     * block.h for the overall meaning of the bits.  As a hint, the
+     * flag want_zero is true if the caller cares more about precise
+     * mappings (favor accurate _OFFSET_VALID/_ZERO) or false for
+     * overall allocation (favor larger *pnum, perhaps by reporting
+     * _DATA instead of _ZERO).  The block layer guarantees input
+     * clamped to bdrv_getlength() and aligned to request_alignment,
+     * as well as non-NULL pnum, map, and file; in turn, the driver
+     * must return an error or set pnum to an aligned non-zero value.
      */
-    int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs,
-        int64_t sector_num, int nb_sectors, int *pnum,
-        BlockDriverState **file);
+    int coroutine_fn (*bdrv_co_block_status)(BlockDriverState *bs,
+        bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum,
+        int64_t *map, BlockDriverState **file);
 
     /*
      * Invalidate any cached meta-data.
@@ -709,10 +718,8 @@ struct BlockDriverState {
     unsigned int in_flight;
     unsigned int serialising_in_flight;
 
-    /* Internal to BDRV_POLL_WHILE and bdrv_wakeup.  Accessed with atomic
-     * ops.
-     */
-    bool wakeup;
+    /* Kicked to signal main loop when a request completes. */
+    AioWait wait;
 
     /* counter for nested bdrv_io_plug.
      * Accessed with atomic ops.
@@ -1031,23 +1038,27 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
                                uint64_t *nperm, uint64_t *nshared);
 
 /*
- * Default implementation for drivers to pass bdrv_co_get_block_status() to
+ * Default implementation for drivers to pass bdrv_co_block_status() to
  * their file.
  */
-int64_t coroutine_fn bdrv_co_get_block_status_from_file(BlockDriverState *bs,
-                                                        int64_t sector_num,
-                                                        int nb_sectors,
-                                                        int *pnum,
-                                                        BlockDriverState **file);
+int coroutine_fn bdrv_co_block_status_from_file(BlockDriverState *bs,
+                                                bool want_zero,
+                                                int64_t offset,
+                                                int64_t bytes,
+                                                int64_t *pnum,
+                                                int64_t *map,
+                                                BlockDriverState **file);
 /*
- * Default implementation for drivers to pass bdrv_co_get_block_status() to
+ * Default implementation for drivers to pass bdrv_co_block_status() to
  * their backing file.
  */
-int64_t coroutine_fn bdrv_co_get_block_status_from_backing(BlockDriverState *bs,
-                                                           int64_t sector_num,
-                                                           int nb_sectors,
-                                                           int *pnum,
-                                                           BlockDriverState **file);
+int coroutine_fn bdrv_co_block_status_from_backing(BlockDriverState *bs,
+                                                   bool want_zero,
+                                                   int64_t offset,
+                                                   int64_t bytes,
+                                                   int64_t *pnum,
+                                                   int64_t *map,
+                                                   BlockDriverState **file);
 const char *bdrv_get_parent_name(const BlockDriverState *bs);
 void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp);
 bool blk_dev_has_removable_media(BlockBackend *blk);
index 932453750cc0e257cc42b56600a140ed9f099acb..1f69a6e9d3993e7e46f9091489acee653f0f215c 100644 (file)
@@ -429,6 +429,8 @@ int print_insn_lm32             (bfd_vma, disassemble_info*);
 int print_insn_big_nios2        (bfd_vma, disassemble_info*);
 int print_insn_little_nios2     (bfd_vma, disassemble_info*);
 int print_insn_xtensa           (bfd_vma, disassemble_info*);
+int print_insn_riscv32          (bfd_vma, disassemble_info*);
+int print_insn_riscv64          (bfd_vma, disassemble_info*);
 
 #if 0
 /* Fetch the disassembler for a given BFD, if that support is available.  */
index 943ee21171f54ee29f29bae95301bc5ebfd86040..c0dc9bb5fd493e905b8b1d0e18cf50688e50a5d4 100644 (file)
@@ -119,6 +119,8 @@ typedef int64_t  Elf64_Sxword;
 
 #define EM_UNICORE32    110     /* UniCore32 */
 
+#define EM_RISCV        243     /* RISC-V */
+
 /*
  * This is an interim value that we will use until the committee comes
  * up with a final number.
index 4162474fd52e3698514aca04f10a321684aa688c..6a5ee42d36e7f8cfed4374838ec2357fec4556c1 100644 (file)
 #define MEMORY_INTERNAL_H
 
 #ifndef CONFIG_USER_ONLY
-typedef struct AddressSpaceDispatch AddressSpaceDispatch;
+static inline AddressSpaceDispatch *flatview_to_dispatch(FlatView *fv)
+{
+    return fv->dispatch;
+}
+
+static inline AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as)
+{
+    return flatview_to_dispatch(address_space_to_flatview(as));
+}
 
 extern const MemoryRegionOps unassigned_mem_ops;
 
@@ -31,9 +39,6 @@ bool memory_region_access_valid(MemoryRegion *mr, hwaddr addr,
 void flatview_add_to_dispatch(FlatView *fv, MemoryRegionSection *section);
 AddressSpaceDispatch *address_space_dispatch_new(FlatView *fv);
 void address_space_dispatch_compact(AddressSpaceDispatch *d);
-
-AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as);
-AddressSpaceDispatch *flatview_to_dispatch(FlatView *fv);
 void address_space_dispatch_free(AddressSpaceDispatch *d);
 
 void mtree_print_dispatch(fprintf_function mon, void *f,
index 15e81113bac9db591ba731e817124bbe7f6a63a3..31eae0a64062489aede78526dae91dcbceb1ac96 100644 (file)
@@ -326,7 +326,27 @@ struct AddressSpace {
     QTAILQ_ENTRY(AddressSpace) address_spaces_link;
 };
 
-FlatView *address_space_to_flatview(AddressSpace *as);
+typedef struct AddressSpaceDispatch AddressSpaceDispatch;
+typedef struct FlatRange FlatRange;
+
+/* Flattened global view of current active memory hierarchy.  Kept in sorted
+ * order.
+ */
+struct FlatView {
+    struct rcu_head rcu;
+    unsigned ref;
+    FlatRange *ranges;
+    unsigned nr;
+    unsigned nr_allocated;
+    struct AddressSpaceDispatch *dispatch;
+    MemoryRegion *root;
+};
+
+static inline FlatView *address_space_to_flatview(AddressSpace *as)
+{
+    return atomic_rcu_read(&as->current_map);
+}
+
 
 /**
  * MemoryRegionSection: describes a fragment of a #MemoryRegion
@@ -1897,13 +1917,12 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len,
 
 
 /* Internal functions, part of the implementation of address_space_read.  */
+MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
+                                    MemTxAttrs attrs, uint8_t *buf, int len);
 MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr,
                                    MemTxAttrs attrs, uint8_t *buf,
                                    int len, hwaddr addr1, hwaddr l,
                                    MemoryRegion *mr);
-
-MemTxResult flatview_read_full(FlatView *fv, hwaddr addr,
-                               MemTxAttrs attrs, uint8_t *buf, int len);
 void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr);
 
 static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
@@ -1922,25 +1941,28 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write)
  *
  * Return a MemTxResult indicating whether the operation succeeded
  * or failed (eg unassigned memory, device rejected the transaction,
- * IOMMU fault).
+ * IOMMU fault).  Called within RCU critical section.
  *
- * @fv: #FlatView to be accessed
+ * @as: #AddressSpace to be accessed
  * @addr: address within that address space
  * @attrs: memory transaction attributes
  * @buf: buffer with the data transferred
  */
 static inline __attribute__((__always_inline__))
-MemTxResult flatview_read(FlatView *fv, hwaddr addr, MemTxAttrs attrs,
-                          uint8_t *buf, int len)
+MemTxResult address_space_read(AddressSpace *as, hwaddr addr,
+                               MemTxAttrs attrs, uint8_t *buf,
+                               int len)
 {
     MemTxResult result = MEMTX_OK;
     hwaddr l, addr1;
     void *ptr;
     MemoryRegion *mr;
+    FlatView *fv;
 
     if (__builtin_constant_p(len)) {
         if (len) {
             rcu_read_lock();
+            fv = address_space_to_flatview(as);
             l = len;
             mr = flatview_translate(fv, addr, &addr1, &l, false);
             if (len == l && memory_access_is_direct(mr, false)) {
@@ -1953,18 +1975,11 @@ MemTxResult flatview_read(FlatView *fv, hwaddr addr, MemTxAttrs attrs,
             rcu_read_unlock();
         }
     } else {
-        result = flatview_read_full(fv, addr, attrs, buf, len);
+        result = address_space_read_full(as, addr, attrs, buf, len);
     }
     return result;
 }
 
-static inline MemTxResult address_space_read(AddressSpace *as, hwaddr addr,
-                                             MemTxAttrs attrs, uint8_t *buf,
-                                             int len)
-{
-    return flatview_read(address_space_to_flatview(as), addr, attrs, buf, len);
-}
-
 /**
  * address_space_read_cached: read from a cached RAM region
  *
index d192e7e2a383f007eb5c2434788e654193adb296..b6e19e35d056f66a1a829ba040ede0f0d73aa56e 100644 (file)
@@ -105,7 +105,7 @@ static int glue(symcmp, SZ)(const void *s0, const void *s1)
 }
 
 static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
-                                  int clear_lsb)
+                                  int clear_lsb, symbol_fn_t sym_cb)
 {
     struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
     struct elf_sym *syms = NULL;
@@ -133,10 +133,26 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
 
     nsyms = symtab->sh_size / sizeof(struct elf_sym);
 
+    /* String table */
+    if (symtab->sh_link >= ehdr->e_shnum) {
+        goto fail;
+    }
+    strtab = &shdr_table[symtab->sh_link];
+
+    str = load_at(fd, strtab->sh_offset, strtab->sh_size);
+    if (!str) {
+        goto fail;
+    }
+
     i = 0;
     while (i < nsyms) {
-        if (must_swab)
+        if (must_swab) {
             glue(bswap_sym, SZ)(&syms[i]);
+        }
+        if (sym_cb) {
+            sym_cb(str + syms[i].st_name, syms[i].st_info,
+                   syms[i].st_value, syms[i].st_size);
+        }
         /* We are only interested in function symbols.
            Throw everything else away.  */
         if (syms[i].st_shndx == SHN_UNDEF ||
@@ -163,15 +179,6 @@ static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
         }
     }
 
-    /* String table */
-    if (symtab->sh_link >= ehdr->e_shnum)
-        goto fail;
-    strtab = &shdr_table[symtab->sh_link];
-
-    str = load_at(fd, strtab->sh_offset, strtab->sh_size);
-    if (!str)
-        goto fail;
-
     /* Commit */
     s = g_malloc0(sizeof(*s));
     s->lookup_symbol = glue(lookup_symbol, SZ);
@@ -264,7 +271,8 @@ static int glue(load_elf, SZ)(const char *name, int fd,
                               int must_swab, uint64_t *pentry,
                               uint64_t *lowaddr, uint64_t *highaddr,
                               int elf_machine, int clear_lsb, int data_swab,
-                              AddressSpace *as, bool load_rom)
+                              AddressSpace *as, bool load_rom,
+                              symbol_fn_t sym_cb)
 {
     struct elfhdr ehdr;
     struct elf_phdr *phdr = NULL, *ph;
@@ -329,7 +337,7 @@ static int glue(load_elf, SZ)(const char *name, int fd,
     if (pentry)
        *pentry = (uint64_t)(elf_sword)ehdr.e_entry;
 
-    glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb);
+    glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb);
 
     size = ehdr.e_phnum * sizeof(phdr[0]);
     if (lseek(fd, ehdr.e_phoff, SEEK_SET) != ehdr.e_phoff) {
index 2504cc22592f1fc9617ae74e2d0e418739861a1c..5ed3fd8ae67afbd98fa770edde549de59fc61179 100644 (file)
@@ -64,7 +64,7 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz);
 #define ELF_LOAD_WRONG_ENDIAN -4
 const char *load_elf_strerror(int error);
 
-/** load_elf_ram:
+/** load_elf_ram_sym:
  * @filename: Path of ELF file
  * @translate_fn: optional function to translate load addresses
  * @translate_opaque: opaque data passed to @translate_fn
@@ -81,6 +81,7 @@ const char *load_elf_strerror(int error);
  * @as: The AddressSpace to load the ELF to. The value of address_space_memory
  *      is used if nothing is supplied here.
  * @load_rom : Load ELF binary as ROM
+ * @sym_cb: Callback function for symbol table entries
  *
  * Load an ELF file's contents to the emulated system's address space.
  * Clients may optionally specify a callback to perform address
@@ -93,6 +94,20 @@ const char *load_elf_strerror(int error);
  * If @elf_machine is EM_NONE then the machine type will be read from the
  * ELF header and no checks will be carried out against the machine type.
  */
+typedef void (*symbol_fn_t)(const char *st_name, int st_info,
+                            uint64_t st_value, uint64_t st_size);
+
+int load_elf_ram_sym(const char *filename,
+                     uint64_t (*translate_fn)(void *, uint64_t),
+                     void *translate_opaque, uint64_t *pentry,
+                     uint64_t *lowaddr, uint64_t *highaddr, int big_endian,
+                     int elf_machine, int clear_lsb, int data_swab,
+                     AddressSpace *as, bool load_rom, symbol_fn_t sym_cb);
+
+/** load_elf_ram:
+ * Same as load_elf_ram_sym(), but doesn't allow the caller to specify a
+ * symbol callback function
+ */
 int load_elf_ram(const char *filename,
                  uint64_t (*translate_fn)(void *, uint64_t),
                  void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
diff --git a/include/hw/riscv/riscv_hart.h b/include/hw/riscv/riscv_hart.h
new file mode 100644 (file)
index 0000000..0671d88
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * QEMU RISC-V Hart Array interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * Holds the state of a heterogenous array of RISC-V harts
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_RISCV_HART_H
+#define HW_RISCV_HART_H
+
+#define TYPE_RISCV_HART_ARRAY "riscv.hart_array"
+
+#define RISCV_HART_ARRAY(obj) \
+    OBJECT_CHECK(RISCVHartArrayState, (obj), TYPE_RISCV_HART_ARRAY)
+
+typedef struct RISCVHartArrayState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    uint32_t num_harts;
+    char *cpu_type;
+    RISCVCPU *harts;
+} RISCVHartArrayState;
+
+#endif
diff --git a/include/hw/riscv/riscv_htif.h b/include/hw/riscv/riscv_htif.h
new file mode 100644 (file)
index 0000000..fb5f881
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * QEMU RISCV Host Target Interface (HTIF) Emulation
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_RISCV_HTIF_H
+#define HW_RISCV_HTIF_H
+
+#include "hw/hw.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "sysemu/sysemu.h"
+#include "exec/memory.h"
+#include "target/riscv/cpu.h"
+
+#define TYPE_HTIF_UART "riscv.htif.uart"
+
+typedef struct HTIFState {
+    int allow_tohost;
+    int fromhost_inprogress;
+
+    hwaddr tohost_offset;
+    hwaddr fromhost_offset;
+    uint64_t tohost_size;
+    uint64_t fromhost_size;
+    MemoryRegion mmio;
+    MemoryRegion *address_space;
+    MemoryRegion *main_mem;
+    void *main_mem_ram_ptr;
+
+    CPURISCVState *env;
+    CharBackend chr;
+    uint64_t pending_read;
+} HTIFState;
+
+extern const VMStateDescription vmstate_htif;
+extern const MemoryRegionOps htif_io_ops;
+
+/* HTIF symbol callback */
+void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
+    uint64_t st_size);
+
+/* legacy pre qom */
+HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
+    CPURISCVState *env, Chardev *chr);
+
+#endif
diff --git a/include/hw/riscv/sifive_clint.h b/include/hw/riscv/sifive_clint.h
new file mode 100644 (file)
index 0000000..aaa2a58
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * SiFive CLINT (Core Local Interruptor) interface
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_SIFIVE_CLINT_H
+#define HW_SIFIVE_CLINT_H
+
+#define TYPE_SIFIVE_CLINT "riscv.sifive.clint"
+
+#define SIFIVE_CLINT(obj) \
+    OBJECT_CHECK(SiFiveCLINTState, (obj), TYPE_SIFIVE_CLINT)
+
+typedef struct SiFiveCLINTState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+    uint32_t num_harts;
+    uint32_t sip_base;
+    uint32_t timecmp_base;
+    uint32_t time_base;
+    uint32_t aperture_size;
+} SiFiveCLINTState;
+
+DeviceState *sifive_clint_create(hwaddr addr, hwaddr size, uint32_t num_harts,
+    uint32_t sip_base, uint32_t timecmp_base, uint32_t time_base);
+
+enum {
+    SIFIVE_SIP_BASE     = 0x0,
+    SIFIVE_TIMECMP_BASE = 0x4000,
+    SIFIVE_TIME_BASE    = 0xBFF8
+};
+
+#endif
diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h
new file mode 100644 (file)
index 0000000..0aebc57
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * SiFive E series machine interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_SIFIVE_E_H
+#define HW_SIFIVE_E_H
+
+#define TYPE_SIFIVE_E "riscv.sifive_e"
+
+#define SIFIVE_E(obj) \
+    OBJECT_CHECK(SiFiveEState, (obj), TYPE_SIFIVE_E)
+
+typedef struct SiFiveEState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState soc;
+    DeviceState *plic;
+} SiFiveEState;
+
+enum {
+    SIFIVE_E_DEBUG,
+    SIFIVE_E_MROM,
+    SIFIVE_E_OTP,
+    SIFIVE_E_CLINT,
+    SIFIVE_E_PLIC,
+    SIFIVE_E_AON,
+    SIFIVE_E_PRCI,
+    SIFIVE_E_OTP_CTRL,
+    SIFIVE_E_GPIO0,
+    SIFIVE_E_UART0,
+    SIFIVE_E_QSPI0,
+    SIFIVE_E_PWM0,
+    SIFIVE_E_UART1,
+    SIFIVE_E_QSPI1,
+    SIFIVE_E_PWM1,
+    SIFIVE_E_QSPI2,
+    SIFIVE_E_PWM2,
+    SIFIVE_E_XIP,
+    SIFIVE_E_DTIM
+};
+
+enum {
+    SIFIVE_E_UART0_IRQ = 3,
+    SIFIVE_E_UART1_IRQ = 4
+};
+
+#define SIFIVE_E_PLIC_HART_CONFIG "M"
+#define SIFIVE_E_PLIC_NUM_SOURCES 127
+#define SIFIVE_E_PLIC_NUM_PRIORITIES 7
+#define SIFIVE_E_PLIC_PRIORITY_BASE 0x0
+#define SIFIVE_E_PLIC_PENDING_BASE 0x1000
+#define SIFIVE_E_PLIC_ENABLE_BASE 0x2000
+#define SIFIVE_E_PLIC_ENABLE_STRIDE 0x80
+#define SIFIVE_E_PLIC_CONTEXT_BASE 0x200000
+#define SIFIVE_E_PLIC_CONTEXT_STRIDE 0x1000
+
+#if defined(TARGET_RISCV32)
+#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E31
+#elif defined(TARGET_RISCV64)
+#define SIFIVE_E_CPU TYPE_RISCV_CPU_SIFIVE_E51
+#endif
+
+#endif
diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h
new file mode 100644 (file)
index 0000000..11a5a98
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * SiFive PLIC (Platform Level Interrupt Controller) interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This provides a RISC-V PLIC device
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_SIFIVE_PLIC_H
+#define HW_SIFIVE_PLIC_H
+
+#include "hw/irq.h"
+
+#define TYPE_SIFIVE_PLIC "riscv.sifive.plic"
+
+#define SIFIVE_PLIC(obj) \
+    OBJECT_CHECK(SiFivePLICState, (obj), TYPE_SIFIVE_PLIC)
+
+typedef enum PLICMode {
+    PLICMode_U,
+    PLICMode_S,
+    PLICMode_H,
+    PLICMode_M
+} PLICMode;
+
+typedef struct PLICAddr {
+    uint32_t addrid;
+    uint32_t hartid;
+    PLICMode mode;
+} PLICAddr;
+
+typedef struct SiFivePLICState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+    uint32_t num_addrs;
+    uint32_t bitfield_words;
+    PLICAddr *addr_config;
+    uint32_t *source_priority;
+    uint32_t *target_priority;
+    uint32_t *pending;
+    uint32_t *claimed;
+    uint32_t *enable;
+    QemuMutex lock;
+    qemu_irq *irqs;
+
+    /* config */
+    char *hart_config;
+    uint32_t num_sources;
+    uint32_t num_priorities;
+    uint32_t priority_base;
+    uint32_t pending_base;
+    uint32_t enable_base;
+    uint32_t enable_stride;
+    uint32_t context_base;
+    uint32_t context_stride;
+    uint32_t aperture_size;
+} SiFivePLICState;
+
+void sifive_plic_raise_irq(SiFivePLICState *plic, uint32_t irq);
+void sifive_plic_lower_irq(SiFivePLICState *plic, uint32_t irq);
+
+DeviceState *sifive_plic_create(hwaddr addr, char *hart_config,
+    uint32_t num_sources, uint32_t num_priorities,
+    uint32_t priority_base, uint32_t pending_base,
+    uint32_t enable_base, uint32_t enable_stride,
+    uint32_t context_base, uint32_t context_stride,
+    uint32_t aperture_size);
+
+#endif
+
diff --git a/include/hw/riscv/sifive_prci.h b/include/hw/riscv/sifive_prci.h
new file mode 100644 (file)
index 0000000..b6f4c48
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * QEMU SiFive PRCI (Power, Reset, Clock, Interrupt) interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_SIFIVE_PRCI_H
+#define HW_SIFIVE_PRCI_H
+
+#define TYPE_SIFIVE_PRCI "riscv.sifive.prci"
+
+#define SIFIVE_PRCI(obj) \
+    OBJECT_CHECK(SiFivePRCIState, (obj), TYPE_SIFIVE_PRCI)
+
+typedef struct SiFivePRCIState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+} SiFivePRCIState;
+
+DeviceState *sifive_prci_create(hwaddr addr);
+
+#endif
diff --git a/include/hw/riscv/sifive_test.h b/include/hw/riscv/sifive_test.h
new file mode 100644 (file)
index 0000000..71d4c9f
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * QEMU Test Finisher interface
+ *
+ * Copyright (c) 2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_SIFIVE_TEST_H
+#define HW_SIFIVE_TEST_H
+
+#define TYPE_SIFIVE_TEST "riscv.sifive.test"
+
+#define SIFIVE_TEST(obj) \
+    OBJECT_CHECK(SiFiveTestState, (obj), TYPE_SIFIVE_TEST)
+
+typedef struct SiFiveTestState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion mmio;
+} SiFiveTestState;
+
+enum {
+    FINISHER_FAIL = 0x3333,
+    FINISHER_PASS = 0x5555
+};
+
+DeviceState *sifive_test_create(hwaddr addr);
+
+#endif
diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h
new file mode 100644 (file)
index 0000000..662e8a1
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * SiFive U series machine interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_SIFIVE_U_H
+#define HW_SIFIVE_U_H
+
+#define TYPE_SIFIVE_U "riscv.sifive_u"
+
+#define SIFIVE_U(obj) \
+    OBJECT_CHECK(SiFiveUState, (obj), TYPE_SIFIVE_U)
+
+typedef struct SiFiveUState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState soc;
+    DeviceState *plic;
+    void *fdt;
+    int fdt_size;
+} SiFiveUState;
+
+enum {
+    SIFIVE_U_DEBUG,
+    SIFIVE_U_MROM,
+    SIFIVE_U_CLINT,
+    SIFIVE_U_PLIC,
+    SIFIVE_U_UART0,
+    SIFIVE_U_UART1,
+    SIFIVE_U_DRAM
+};
+
+enum {
+    SIFIVE_U_UART0_IRQ = 3,
+    SIFIVE_U_UART1_IRQ = 4
+};
+
+#define SIFIVE_U_PLIC_HART_CONFIG "MS"
+#define SIFIVE_U_PLIC_NUM_SOURCES 127
+#define SIFIVE_U_PLIC_NUM_PRIORITIES 7
+#define SIFIVE_U_PLIC_PRIORITY_BASE 0x0
+#define SIFIVE_U_PLIC_PENDING_BASE 0x1000
+#define SIFIVE_U_PLIC_ENABLE_BASE 0x2000
+#define SIFIVE_U_PLIC_ENABLE_STRIDE 0x80
+#define SIFIVE_U_PLIC_CONTEXT_BASE 0x200000
+#define SIFIVE_U_PLIC_CONTEXT_STRIDE 0x1000
+
+#if defined(TARGET_RISCV32)
+#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U34
+#elif defined(TARGET_RISCV64)
+#define SIFIVE_U_CPU TYPE_RISCV_CPU_SIFIVE_U54
+#endif
+
+#endif
diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h
new file mode 100644 (file)
index 0000000..504f18a
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * SiFive UART interface
+ *
+ * Copyright (c) 2016 Stefan O'Rear
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_SIFIVE_UART_H
+#define HW_SIFIVE_UART_H
+
+enum {
+    SIFIVE_UART_TXFIFO        = 0,
+    SIFIVE_UART_RXFIFO        = 4,
+    SIFIVE_UART_TXCTRL        = 8,
+    SIFIVE_UART_TXMARK        = 10,
+    SIFIVE_UART_RXCTRL        = 12,
+    SIFIVE_UART_RXMARK        = 14,
+    SIFIVE_UART_IE            = 16,
+    SIFIVE_UART_IP            = 20,
+    SIFIVE_UART_DIV           = 24,
+    SIFIVE_UART_MAX           = 32
+};
+
+enum {
+    SIFIVE_UART_IE_TXWM       = 1, /* Transmit watermark interrupt enable */
+    SIFIVE_UART_IE_RXWM       = 2  /* Receive watermark interrupt enable */
+};
+
+enum {
+    SIFIVE_UART_IP_TXWM       = 1, /* Transmit watermark interrupt pending */
+    SIFIVE_UART_IP_RXWM       = 2  /* Receive watermark interrupt pending */
+};
+
+#define TYPE_SIFIVE_UART "riscv.sifive.uart"
+
+#define SIFIVE_UART(obj) \
+    OBJECT_CHECK(SiFiveUARTState, (obj), TYPE_SIFIVE_UART)
+
+typedef struct SiFiveUARTState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    qemu_irq irq;
+    MemoryRegion mmio;
+    CharBackend chr;
+    uint8_t rx_fifo[8];
+    unsigned int rx_fifo_len;
+    uint32_t ie;
+    uint32_t ip;
+    uint32_t txctrl;
+    uint32_t rxctrl;
+    uint32_t div;
+} SiFiveUARTState;
+
+SiFiveUARTState *sifive_uart_create(MemoryRegion *address_space, hwaddr base,
+    Chardev *chr, qemu_irq irq);
+
+#endif
diff --git a/include/hw/riscv/spike.h b/include/hw/riscv/spike.h
new file mode 100644 (file)
index 0000000..cb55a14
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Spike machine interface
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_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)
+
+typedef struct {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState soc;
+    void *fdt;
+    int fdt_size;
+} SpikeState;
+
+
+enum {
+    SPIKE_MROM,
+    SPIKE_CLINT,
+    SPIKE_DRAM
+};
+
+#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
+#elif defined(TARGET_RISCV64)
+#define SPIKE_V1_09_1_CPU TYPE_RISCV_CPU_RV64GCSU_V1_09_1
+#define SPIKE_V1_10_0_CPU TYPE_RISCV_CPU_RV64GCSU_V1_10_0
+#endif
+
+#endif
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
new file mode 100644 (file)
index 0000000..7525647
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * SiFive VirtIO Board
+ *
+ * Copyright (c) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_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 };
+
+typedef struct {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    RISCVHartArrayState soc;
+    DeviceState *plic;
+    void *fdt;
+    int fdt_size;
+} RISCVVirtState;
+
+enum {
+    VIRT_DEBUG,
+    VIRT_MROM,
+    VIRT_TEST,
+    VIRT_CLINT,
+    VIRT_PLIC,
+    VIRT_UART0,
+    VIRT_VIRTIO,
+    VIRT_DRAM
+};
+
+
+enum {
+    UART0_IRQ = 10,
+    VIRTIO_IRQ = 1, /* 1 to 8 */
+    VIRTIO_COUNT = 8,
+    VIRTIO_NDEV = 10
+};
+
+#define VIRT_PLIC_HART_CONFIG "MS"
+#define VIRT_PLIC_NUM_SOURCES 127
+#define VIRT_PLIC_NUM_PRIORITIES 7
+#define VIRT_PLIC_PRIORITY_BASE 0x0
+#define VIRT_PLIC_PENDING_BASE 0x1000
+#define VIRT_PLIC_ENABLE_BASE 0x2000
+#define VIRT_PLIC_ENABLE_STRIDE 0x80
+#define VIRT_PLIC_CONTEXT_BASE 0x200000
+#define VIRT_PLIC_CONTEXT_STRIDE 0x1000
+
+#if defined(TARGET_RISCV32)
+#define VIRT_CPU TYPE_RISCV_CPU_RV32GCSU_V1_10_0
+#elif defined(TARGET_RISCV64)
+#define VIRT_CPU TYPE_RISCV_CPU_RV64GCSU_V1_10_0
+#endif
+
+#endif
index 5119b9b7f0d9d744efc50043c52764a64e392f57..5698e5e96cb6ad7d6888824580794142b9bc2a0f 100644 (file)
 #define SCLP_EVENT_SIGNAL_QUIESCE               0x1d
 
 /* SCLP event masks */
-#define SCLP_EVENT_MASK_SIGNAL_QUIESCE          0x00000008
-#define SCLP_EVENT_MASK_MSG_ASCII               0x00000040
-#define SCLP_EVENT_MASK_CONFIG_MGT_DATA         0x10000000
-#define SCLP_EVENT_MASK_OP_CMD                  0x80000000
-#define SCLP_EVENT_MASK_MSG                     0x40000000
-#define SCLP_EVENT_MASK_PMSGCMD                 0x00800000
+#define SCLP_EVMASK(T)  (1ULL << (sizeof(sccb_mask_t) * 8 - (T)))
+
+#define SCLP_EVENT_MASK_OP_CMD          SCLP_EVMASK(SCLP_EVENT_OPRTNS_COMMAND)
+#define SCLP_EVENT_MASK_MSG             SCLP_EVMASK(SCLP_EVENT_MESSAGE)
+#define SCLP_EVENT_MASK_CONFIG_MGT_DATA SCLP_EVMASK(SCLP_EVENT_CONFIG_MGT_DATA)
+#define SCLP_EVENT_MASK_PMSGCMD         SCLP_EVMASK(SCLP_EVENT_PMSGCMD)
+#define SCLP_EVENT_MASK_MSG_ASCII       SCLP_EVMASK(SCLP_EVENT_ASCII_CONSOLE_DATA)
+#define SCLP_EVENT_MASK_SIGNAL_QUIESCE  SCLP_EVMASK(SCLP_EVENT_SIGNAL_QUIESCE)
 
 #define SCLP_UNCONDITIONAL_READ                 0x00
 #define SCLP_SELECTIVE_READ                     0x01
@@ -71,6 +73,8 @@ 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 struct EventBufferHeader {
     uint16_t length;
     uint8_t  type;
@@ -160,7 +164,7 @@ typedef struct WriteEventData {
 typedef struct ReadEventData {
     SCCBHeader h;
     union {
-        uint32_t mask;
+        sccb_mask_t mask;
         EventBufferHeader ebh;
     };
 } QEMU_PACKED ReadEventData;
@@ -174,13 +178,12 @@ typedef struct SCLPEvent {
 typedef struct SCLPEventClass {
     DeviceClass parent_class;
     int (*init)(SCLPEvent *event);
-    int (*exit)(SCLPEvent *event);
 
     /* get SCLP's send mask */
-    unsigned int (*get_send_mask)(void);
+    sccb_mask_t (*get_send_mask)(void);
 
     /* get SCLP's receive mask */
-    unsigned int (*get_receive_mask)(void);
+    sccb_mask_t (*get_receive_mask)(void);
 
     int (*read_event_data)(SCLPEvent *event, EventBufferHeader *evt_buf_hdr,
                            int *slen);
index 802a647cdcbbad9a6778c80ed2247b88efa1fa24..7ecaddac9dabbd05e85e25bf1ed1ac6af238dcf6 100644 (file)
@@ -153,7 +153,7 @@ SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockBackend *blk,
                                       int unit, bool removable, int bootindex,
                                       bool share_rw,
                                       const char *serial, Error **errp);
-void scsi_bus_legacy_handle_cmdline(SCSIBus *bus, bool deprecated);
+void scsi_bus_legacy_handle_cmdline(SCSIBus *bus);
 void scsi_legacy_handle_cmdline(void);
 
 SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
index 53801f6042514e13a373ace412a6e0956c80ff61..d7134d2cd618a678c561606568f8123b1d17faed 100644 (file)
@@ -101,6 +101,8 @@ int qio_channel_socket_connect_sync(QIOChannelSocket *ioc,
  * @callback: the function to invoke on completion
  * @opaque: user data to pass to @callback
  * @destroy: the function to free @opaque
+ * @context: the context to run the async task. If %NULL, the default
+ *           context will be used.
  *
  * Attempt to connect to the address @addr. This method
  * will run in the background so the caller will regain
@@ -113,7 +115,8 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc,
                                       SocketAddress *addr,
                                       QIOTaskFunc callback,
                                       gpointer opaque,
-                                      GDestroyNotify destroy);
+                                      GDestroyNotify destroy,
+                                      GMainContext *context);
 
 
 /**
@@ -138,6 +141,8 @@ int qio_channel_socket_listen_sync(QIOChannelSocket *ioc,
  * @callback: the function to invoke on completion
  * @opaque: user data to pass to @callback
  * @destroy: the function to free @opaque
+ * @context: the context to run the async task. If %NULL, the default
+ *           context will be used.
  *
  * Attempt to listen to the address @addr. This method
  * will run in the background so the caller will regain
@@ -150,7 +155,8 @@ void qio_channel_socket_listen_async(QIOChannelSocket *ioc,
                                      SocketAddress *addr,
                                      QIOTaskFunc callback,
                                      gpointer opaque,
-                                     GDestroyNotify destroy);
+                                     GDestroyNotify destroy,
+                                     GMainContext *context);
 
 
 /**
@@ -179,6 +185,8 @@ int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc,
  * @callback: the function to invoke on completion
  * @opaque: user data to pass to @callback
  * @destroy: the function to free @opaque
+ * @context: the context to run the async task. If %NULL, the default
+ *           context will be used.
  *
  * Attempt to initialize a datagram socket bound to
  * @localAddr and communicating with peer @remoteAddr.
@@ -194,7 +202,8 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc,
                                     SocketAddress *remoteAddr,
                                     QIOTaskFunc callback,
                                     gpointer opaque,
-                                    GDestroyNotify destroy);
+                                    GDestroyNotify destroy,
+                                    GMainContext *context);
 
 
 /**
index d157eb10e89f20868720b3d646d61a594cb39952..87fcaf91463e721921ad2f0eaa22dab8f42536d8 100644 (file)
@@ -116,6 +116,8 @@ qio_channel_tls_new_client(QIOChannel *master,
  * @func: the callback to invoke when completed
  * @opaque: opaque data to pass to @func
  * @destroy: optional callback to free @opaque
+ * @context: the context that TLS handshake will run with. If %NULL,
+ *           the default context will be used
  *
  * Perform the TLS session handshake. This method
  * will return immediately and the handshake will
@@ -126,7 +128,8 @@ qio_channel_tls_new_client(QIOChannel *master,
 void qio_channel_tls_handshake(QIOChannelTLS *ioc,
                                QIOTaskFunc func,
                                gpointer opaque,
-                               GDestroyNotify destroy);
+                               GDestroyNotify destroy,
+                               GMainContext *context);
 
 /**
  * qio_channel_tls_get_session:
index 3995e243a39e01a7610cc679a4eea039912e3623..e8cdadb0b0727773c8ca7d702cbf6f5383178f0b 100644 (file)
@@ -648,6 +648,50 @@ guint qio_channel_add_watch(QIOChannel *ioc,
                             gpointer user_data,
                             GDestroyNotify notify);
 
+/**
+ * qio_channel_add_watch_full:
+ * @ioc: the channel object
+ * @condition: the I/O condition to monitor
+ * @func: callback to invoke when the source becomes ready
+ * @user_data: opaque data to pass to @func
+ * @notify: callback to free @user_data
+ * @context: the context to run the watch source
+ *
+ * Similar as qio_channel_add_watch(), but allows to specify context
+ * to run the watch source.
+ *
+ * Returns: the source ID
+ */
+guint qio_channel_add_watch_full(QIOChannel *ioc,
+                                 GIOCondition condition,
+                                 QIOChannelFunc func,
+                                 gpointer user_data,
+                                 GDestroyNotify notify,
+                                 GMainContext *context);
+
+/**
+ * qio_channel_add_watch_source:
+ * @ioc: the channel object
+ * @condition: the I/O condition to monitor
+ * @func: callback to invoke when the source becomes ready
+ * @user_data: opaque data to pass to @func
+ * @notify: callback to free @user_data
+ * @context: gcontext to bind the source to
+ *
+ * Similar as qio_channel_add_watch(), but allows to specify context
+ * to run the watch source, meanwhile return the GSource object
+ * instead of tag ID, with the GSource referenced already.
+ *
+ * Note: callers is responsible to unref the source when not needed.
+ *
+ * Returns: the source pointer
+ */
+GSource *qio_channel_add_watch_source(QIOChannel *ioc,
+                                      GIOCondition condition,
+                                      QIOChannelFunc func,
+                                      gpointer user_data,
+                                      GDestroyNotify notify,
+                                      GMainContext *context);
 
 /**
  * qio_channel_attach_aio_context:
index 56d6da7a76420bbf89c0818c1f183cee8ae96a38..8081ac58a2fe692391149ccfe3fa0ed2d13f3b7b 100644 (file)
@@ -53,7 +53,7 @@ struct QIONetListener {
 
     char *name;
     QIOChannelSocket **sioc;
-    gulong *io_tag;
+    GSource **io_source;
     size_t nsioc;
 
     bool connected;
@@ -120,17 +120,35 @@ void qio_net_listener_add(QIONetListener *listener,
                           QIOChannelSocket *sioc);
 
 /**
- * qio_net_listener_set_client_func:
+ * qio_net_listener_set_client_func_full:
  * @listener: the network listener object
  * @func: the callback function
  * @data: opaque data to pass to @func
  * @notify: callback to free @data
+ * @context: the context that the sources will be bound to.  If %NULL,
+ *           the default context will be used.
  *
  * Register @func to be invoked whenever a new client
  * connects to the listener. @func will be invoked
  * passing in the QIOChannelSocket instance for the
  * client.
  */
+void qio_net_listener_set_client_func_full(QIONetListener *listener,
+                                           QIONetListenerClientFunc func,
+                                           gpointer data,
+                                           GDestroyNotify notify,
+                                           GMainContext *context);
+
+/**
+ * qio_net_listener_set_client_func:
+ * @listener: the network listener object
+ * @func: the callback function
+ * @data: opaque data to pass to @func
+ * @notify: callback to free @data
+ *
+ * Wrapper of qio_net_listener_set_client_func_full(), only that the
+ * sources will always be bound to default main context.
+ */
 void qio_net_listener_set_client_func(QIONetListener *listener,
                                       QIONetListenerClientFunc func,
                                       gpointer data,
index 6021f513360d939b3294c65bb05408c806eba5f9..9e09b95b2e8407fac90729b4babce6f939b172de 100644 (file)
@@ -227,15 +227,18 @@ QIOTask *qio_task_new(Object *source,
  * @worker: the function to invoke in a thread
  * @opaque: opaque data to pass to @worker
  * @destroy: function to free @opaque
+ * @context: the context to run the complete hook. If %NULL, the
+ *           default context will be used.
  *
  * Run a task in a background thread. When @worker
  * returns it will call qio_task_complete() in
- * the main event thread context.
+ * the event thread context that provided.
  */
 void qio_task_run_in_thread(QIOTask *task,
                             QIOTaskWorker worker,
                             gpointer opaque,
-                            GDestroyNotify destroy);
+                            GDestroyNotify destroy,
+                            GMainContext *context);
 
 /**
  * qio_task_complete:
index b6ed6c89ec745d4a05b9c39c88de9d2010cbe335..84ea794bcf616f7f06f85f342bbb60652ad45371 100644 (file)
@@ -28,7 +28,7 @@ struct QemuLockable {
  * to QEMU_MAKE_LOCKABLE.  For optimized builds, we can rely on dead-code elimination
  * from the compiler, and give the errors already at link time.
  */
-#ifdef __OPTIMIZE__
+#if defined(__OPTIMIZE__) && !defined(__SANITIZE_ADDRESS__)
 void unknown_lock_type(void *);
 #else
 static inline void unknown_lock_type(void *unused)
index 30db296af4c59b71086cbe5b911d800e10a4a8eb..4f07090db043a32152b21f2c5795b4ca082ee251 100644 (file)
@@ -1016,6 +1016,22 @@ typedef struct ObjectPropertyIterator {
 void object_property_iter_init(ObjectPropertyIterator *iter,
                                Object *obj);
 
+/**
+ * object_class_property_iter_init:
+ * @klass: the class
+ *
+ * Initializes an iterator for traversing all properties
+ * registered against an object class and all parent classes.
+ *
+ * It is forbidden to modify the property list while iterating,
+ * whether removing or adding properties.
+ *
+ * This can be used on abstract classes as it does not create a temporary
+ * instance.
+ */
+void object_class_property_iter_init(ObjectPropertyIterator *iter,
+                                     ObjectClass *klass);
+
 /**
  * object_property_iter_next:
  * @iter: the iterator instance
index cecd494159d9a8a680a0bd2c50bbf1c7dbd3c940..32abdfe6a15c481d09d22f99606c041fb96d1374 100644 (file)
@@ -24,6 +24,7 @@ enum {
     QEMU_ARCH_TRICORE = (1 << 16),
     QEMU_ARCH_NIOS2 = (1 << 17),
     QEMU_ARCH_HPPA = (1 << 18),
+    QEMU_ARCH_RISCV = (1 << 19),
 };
 
 extern const uint32_t arch_type;
index 799614ffd272be4db4667d4f7dbc8f9f7e5ef116..8a7ac2c5287d160badc72782fae923f503f29fed 100644 (file)
@@ -45,7 +45,6 @@ typedef struct {
 char *iothread_get_id(IOThread *iothread);
 IOThread *iothread_by_id(const char *id);
 AioContext *iothread_get_aio_context(IOThread *iothread);
-void iothread_stop_all(void);
 GMainContext *iothread_get_g_main_context(IOThread *iothread);
 
 /*
index d24ad09f37a71d0a29b277618babc9c6fbc246ac..356bfdc1c1cfe6eb35d34600373a49c244afeeb1 100644 (file)
@@ -56,6 +56,7 @@ void vm_start(void);
 int vm_prepare_start(void);
 int vm_stop(RunState state);
 int vm_stop_force_state(RunState state);
+int vm_shutdown(void);
 
 typedef enum WakeupReason {
     /* Always keep QEMU_WAKEUP_REASON_NONE = 0 */
index 8359b6683acfb27608ed5f424295be860fc17890..57cfb4d3a6d397204261ce39ff705d535aa63936 100644 (file)
@@ -174,7 +174,8 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc,
                                       SocketAddress *addr,
                                       QIOTaskFunc callback,
                                       gpointer opaque,
-                                      GDestroyNotify destroy)
+                                      GDestroyNotify destroy,
+                                      GMainContext *context)
 {
     QIOTask *task = qio_task_new(
         OBJECT(ioc), callback, opaque, destroy);
@@ -188,7 +189,8 @@ void qio_channel_socket_connect_async(QIOChannelSocket *ioc,
     qio_task_run_in_thread(task,
                            qio_channel_socket_connect_worker,
                            addrCopy,
-                           (GDestroyNotify)qapi_free_SocketAddress);
+                           (GDestroyNotify)qapi_free_SocketAddress,
+                           context);
 }
 
 
@@ -233,7 +235,8 @@ void qio_channel_socket_listen_async(QIOChannelSocket *ioc,
                                      SocketAddress *addr,
                                      QIOTaskFunc callback,
                                      gpointer opaque,
-                                     GDestroyNotify destroy)
+                                     GDestroyNotify destroy,
+                                     GMainContext *context)
 {
     QIOTask *task = qio_task_new(
         OBJECT(ioc), callback, opaque, destroy);
@@ -246,7 +249,8 @@ void qio_channel_socket_listen_async(QIOChannelSocket *ioc,
     qio_task_run_in_thread(task,
                            qio_channel_socket_listen_worker,
                            addrCopy,
-                           (GDestroyNotify)qapi_free_SocketAddress);
+                           (GDestroyNotify)qapi_free_SocketAddress,
+                           context);
 }
 
 
@@ -308,7 +312,8 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc,
                                     SocketAddress *remoteAddr,
                                     QIOTaskFunc callback,
                                     gpointer opaque,
-                                    GDestroyNotify destroy)
+                                    GDestroyNotify destroy,
+                                    GMainContext *context)
 {
     QIOTask *task = qio_task_new(
         OBJECT(ioc), callback, opaque, destroy);
@@ -322,7 +327,8 @@ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc,
     qio_task_run_in_thread(task,
                            qio_channel_socket_dgram_worker,
                            data,
-                           qio_channel_socket_dgram_worker_free);
+                           qio_channel_socket_dgram_worker_free,
+                           context);
 }
 
 
index 6182702dab1be8c481fe5e726e3180052252029f..9628e6fa479f899d3688ca92cbce9b2b75add327 100644 (file)
@@ -140,13 +140,19 @@ qio_channel_tls_new_client(QIOChannel *master,
     return NULL;
 }
 
+struct QIOChannelTLSData {
+    QIOTask *task;
+    GMainContext *context;
+};
+typedef struct QIOChannelTLSData QIOChannelTLSData;
 
 static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
                                              GIOCondition condition,
                                              gpointer user_data);
 
 static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
-                                           QIOTask *task)
+                                           QIOTask *task,
+                                           GMainContext *context)
 {
     Error *err = NULL;
     QCryptoTLSSessionHandshakeStatus status;
@@ -171,6 +177,15 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
         qio_task_complete(task);
     } else {
         GIOCondition condition;
+        QIOChannelTLSData *data = g_new0(typeof(*data), 1);
+
+        data->task = task;
+        data->context = context;
+
+        if (context) {
+            g_main_context_ref(context);
+        }
+
         if (status == QCRYPTO_TLS_HANDSHAKE_SENDING) {
             condition = G_IO_OUT;
         } else {
@@ -178,11 +193,12 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
         }
 
         trace_qio_channel_tls_handshake_pending(ioc, status);
-        qio_channel_add_watch(ioc->master,
-                              condition,
-                              qio_channel_tls_handshake_io,
-                              task,
-                              NULL);
+        qio_channel_add_watch_full(ioc->master,
+                                   condition,
+                                   qio_channel_tls_handshake_io,
+                                   data,
+                                   NULL,
+                                   context);
     }
 }
 
@@ -191,12 +207,18 @@ static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
                                              GIOCondition condition,
                                              gpointer user_data)
 {
-    QIOTask *task = user_data;
+    QIOChannelTLSData *data = user_data;
+    QIOTask *task = data->task;
+    GMainContext *context = data->context;
     QIOChannelTLS *tioc = QIO_CHANNEL_TLS(
         qio_task_get_source(task));
 
-    qio_channel_tls_handshake_task(
-       tioc, task);
+    g_free(data);
+    qio_channel_tls_handshake_task(tioc, task, context);
+
+    if (context) {
+        g_main_context_unref(context);
+    }
 
     return FALSE;
 }
@@ -204,7 +226,8 @@ static gboolean qio_channel_tls_handshake_io(QIOChannel *ioc,
 void qio_channel_tls_handshake(QIOChannelTLS *ioc,
                                QIOTaskFunc func,
                                gpointer opaque,
-                               GDestroyNotify destroy)
+                               GDestroyNotify destroy,
+                               GMainContext *context)
 {
     QIOTask *task;
 
@@ -212,7 +235,7 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc,
                         func, opaque, destroy);
 
     trace_qio_channel_tls_handshake_start(ioc);
-    qio_channel_tls_handshake_task(ioc, task);
+    qio_channel_tls_handshake_task(ioc, task, context);
 }
 
 
index ec4b86de7c855731b7f047418567d84d42cb6714..8dd0684f5d50cfa29fc65a653f72ea7086536fdc 100644 (file)
@@ -299,11 +299,12 @@ void qio_channel_set_aio_fd_handler(QIOChannel *ioc,
     klass->io_set_aio_fd_handler(ioc, ctx, io_read, io_write, opaque);
 }
 
-guint qio_channel_add_watch(QIOChannel *ioc,
-                            GIOCondition condition,
-                            QIOChannelFunc func,
-                            gpointer user_data,
-                            GDestroyNotify notify)
+guint qio_channel_add_watch_full(QIOChannel *ioc,
+                                 GIOCondition condition,
+                                 QIOChannelFunc func,
+                                 gpointer user_data,
+                                 GDestroyNotify notify,
+                                 GMainContext *context)
 {
     GSource *source;
     guint id;
@@ -312,12 +313,39 @@ guint qio_channel_add_watch(QIOChannel *ioc,
 
     g_source_set_callback(source, (GSourceFunc)func, user_data, notify);
 
-    id = g_source_attach(source, NULL);
+    id = g_source_attach(source, context);
     g_source_unref(source);
 
     return id;
 }
 
+guint qio_channel_add_watch(QIOChannel *ioc,
+                            GIOCondition condition,
+                            QIOChannelFunc func,
+                            gpointer user_data,
+                            GDestroyNotify notify)
+{
+    return qio_channel_add_watch_full(ioc, condition, func,
+                                      user_data, notify, NULL);
+}
+
+GSource *qio_channel_add_watch_source(QIOChannel *ioc,
+                                      GIOCondition condition,
+                                      QIOChannelFunc func,
+                                      gpointer user_data,
+                                      GDestroyNotify notify,
+                                      GMainContext *context)
+{
+    GSource *source;
+    guint id;
+
+    id = qio_channel_add_watch_full(ioc, condition, func,
+                                    user_data, notify, context);
+    source = g_main_context_find_source_by_id(context, id);
+    g_source_ref(source);
+    return source;
+}
+
 
 int qio_channel_shutdown(QIOChannel *ioc,
                          QIOChannelShutdown how,
index 8c924071c410acef876eb621bfdec07d37075bd2..187f725665b2dd5d5de3f416ddb8acb4fd68d745 100644 (file)
@@ -234,7 +234,8 @@ void qio_dns_resolver_lookup_async(QIODNSResolver *resolver,
     qio_task_run_in_thread(task,
                            qio_dns_resolver_lookup_worker,
                            data,
-                           qio_dns_resolver_lookup_data_free);
+                           qio_dns_resolver_lookup_data_free,
+                           NULL);
 }
 
 
index de38dfae99f056c3268f01c6dd638e2a5eda5b45..555e8acaa4742d5a29a28b1a1db10f3ec6c026b2 100644 (file)
@@ -118,29 +118,32 @@ void qio_net_listener_add(QIONetListener *listener,
 
     listener->sioc = g_renew(QIOChannelSocket *, listener->sioc,
                              listener->nsioc + 1);
-    listener->io_tag = g_renew(gulong, listener->io_tag, listener->nsioc + 1);
+    listener->io_source = g_renew(typeof(listener->io_source[0]),
+                                  listener->io_source,
+                                  listener->nsioc + 1);
     listener->sioc[listener->nsioc] = sioc;
-    listener->io_tag[listener->nsioc] = 0;
+    listener->io_source[listener->nsioc] = NULL;
 
     object_ref(OBJECT(sioc));
     listener->connected = true;
 
     if (listener->io_func != NULL) {
         object_ref(OBJECT(listener));
-        listener->io_tag[listener->nsioc] = qio_channel_add_watch(
+        listener->io_source[listener->nsioc] = qio_channel_add_watch_source(
             QIO_CHANNEL(listener->sioc[listener->nsioc]), G_IO_IN,
             qio_net_listener_channel_func,
-            listener, (GDestroyNotify)object_unref);
+            listener, (GDestroyNotify)object_unref, NULL);
     }
 
     listener->nsioc++;
 }
 
 
-void qio_net_listener_set_client_func(QIONetListener *listener,
-                                      QIONetListenerClientFunc func,
-                                      gpointer data,
-                                      GDestroyNotify notify)
+void qio_net_listener_set_client_func_full(QIONetListener *listener,
+                                           QIONetListenerClientFunc func,
+                                           gpointer data,
+                                           GDestroyNotify notify,
+                                           GMainContext *context)
 {
     size_t i;
 
@@ -152,23 +155,32 @@ void qio_net_listener_set_client_func(QIONetListener *listener,
     listener->io_notify = notify;
 
     for (i = 0; i < listener->nsioc; i++) {
-        if (listener->io_tag[i]) {
-            g_source_remove(listener->io_tag[i]);
-            listener->io_tag[i] = 0;
+        if (listener->io_source[i]) {
+            g_source_destroy(listener->io_source[i]);
+            g_source_unref(listener->io_source[i]);
+            listener->io_source[i] = NULL;
         }
     }
 
     if (listener->io_func != NULL) {
         for (i = 0; i < listener->nsioc; i++) {
             object_ref(OBJECT(listener));
-            listener->io_tag[i] = qio_channel_add_watch(
+            listener->io_source[i] = qio_channel_add_watch_source(
                 QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
                 qio_net_listener_channel_func,
-                listener, (GDestroyNotify)object_unref);
+                listener, (GDestroyNotify)object_unref, context);
         }
     }
 }
 
+void qio_net_listener_set_client_func(QIONetListener *listener,
+                                      QIONetListenerClientFunc func,
+                                      gpointer data,
+                                      GDestroyNotify notify)
+{
+    qio_net_listener_set_client_func_full(listener, func, data,
+                                          notify, NULL);
+}
 
 struct QIONetListenerClientWaitData {
     QIOChannelSocket *sioc;
@@ -211,9 +223,10 @@ QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener)
     size_t i;
 
     for (i = 0; i < listener->nsioc; i++) {
-        if (listener->io_tag[i]) {
-            g_source_remove(listener->io_tag[i]);
-            listener->io_tag[i] = 0;
+        if (listener->io_source[i]) {
+            g_source_destroy(listener->io_source[i]);
+            g_source_unref(listener->io_source[i]);
+            listener->io_source[i] = NULL;
         }
     }
 
@@ -241,10 +254,10 @@ QIOChannelSocket *qio_net_listener_wait_client(QIONetListener *listener)
     if (listener->io_func != NULL) {
         for (i = 0; i < listener->nsioc; i++) {
             object_ref(OBJECT(listener));
-            listener->io_tag[i] = qio_channel_add_watch(
+            listener->io_source[i] = qio_channel_add_watch_source(
                 QIO_CHANNEL(listener->sioc[i]), G_IO_IN,
                 qio_net_listener_channel_func,
-                listener, (GDestroyNotify)object_unref);
+                listener, (GDestroyNotify)object_unref, NULL);
         }
     }
 
@@ -260,9 +273,10 @@ void qio_net_listener_disconnect(QIONetListener *listener)
     }
 
     for (i = 0; i < listener->nsioc; i++) {
-        if (listener->io_tag[i]) {
-            g_source_remove(listener->io_tag[i]);
-            listener->io_tag[i] = 0;
+        if (listener->io_source[i]) {
+            g_source_destroy(listener->io_source[i]);
+            g_source_unref(listener->io_source[i]);
+            listener->io_source[i] = NULL;
         }
         qio_channel_close(QIO_CHANNEL(listener->sioc[i]), NULL);
     }
@@ -285,7 +299,7 @@ static void qio_net_listener_finalize(Object *obj)
     for (i = 0; i < listener->nsioc; i++) {
         object_unref(OBJECT(listener->sioc[i]));
     }
-    g_free(listener->io_tag);
+    g_free(listener->io_source);
     g_free(listener->sioc);
     g_free(listener->name);
 }
index 3ce556017c1167a4a03208af522de0ec5c2493bc..2886a2c1bcae728bdc4e0bd14eb7d4543312bed6 100644 (file)
--- a/io/task.c
+++ b/io/task.c
@@ -77,10 +77,11 @@ struct QIOTaskThreadData {
     QIOTaskWorker worker;
     gpointer opaque;
     GDestroyNotify destroy;
+    GMainContext *context;
 };
 
 
-static gboolean gio_task_thread_result(gpointer opaque)
+static gboolean qio_task_thread_result(gpointer opaque)
 {
     struct QIOTaskThreadData *data = opaque;
 
@@ -91,6 +92,10 @@ static gboolean gio_task_thread_result(gpointer opaque)
         data->destroy(data->opaque);
     }
 
+    if (data->context) {
+        g_main_context_unref(data->context);
+    }
+
     g_free(data);
 
     return FALSE;
@@ -100,6 +105,7 @@ static gboolean gio_task_thread_result(gpointer opaque)
 static gpointer qio_task_thread_worker(gpointer opaque)
 {
     struct QIOTaskThreadData *data = opaque;
+    GSource *idle;
 
     trace_qio_task_thread_run(data->task);
     data->worker(data->task, data->opaque);
@@ -110,7 +116,11 @@ static gpointer qio_task_thread_worker(gpointer opaque)
      * the worker results
      */
     trace_qio_task_thread_exit(data->task);
-    g_idle_add(gio_task_thread_result, data);
+
+    idle = g_idle_source_new();
+    g_source_set_callback(idle, qio_task_thread_result, data, NULL);
+    g_source_attach(idle, data->context);
+
     return NULL;
 }
 
@@ -118,15 +128,21 @@ static gpointer qio_task_thread_worker(gpointer opaque)
 void qio_task_run_in_thread(QIOTask *task,
                             QIOTaskWorker worker,
                             gpointer opaque,
-                            GDestroyNotify destroy)
+                            GDestroyNotify destroy,
+                            GMainContext *context)
 {
     struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1);
     QemuThread thread;
 
+    if (context) {
+        g_main_context_ref(context);
+    }
+
     data->task = task;
     data->worker = worker;
     data->opaque = opaque;
     data->destroy = destroy;
+    data->context = context;
 
     trace_qio_task_thread_start(task, worker, opaque);
     qemu_thread_create(&thread,
index 2ec5a3bffea4b8b4e493502970bf3471a80ad8be..1b3463cb004aebc698eb9fab88e40ff899034159 100644 (file)
@@ -101,18 +101,6 @@ void iothread_stop(IOThread *iothread)
     qemu_thread_join(&iothread->thread);
 }
 
-static int iothread_stop_iter(Object *object, void *opaque)
-{
-    IOThread *iothread;
-
-    iothread = (IOThread *)object_dynamic_cast(object, TYPE_IOTHREAD);
-    if (!iothread) {
-        return 0;
-    }
-    iothread_stop(iothread);
-    return 0;
-}
-
 static void iothread_instance_init(Object *obj)
 {
     IOThread *iothread = IOTHREAD(obj);
@@ -333,25 +321,6 @@ IOThreadInfoList *qmp_query_iothreads(Error **errp)
     return head;
 }
 
-void iothread_stop_all(void)
-{
-    Object *container = object_get_objects_root();
-    BlockDriverState *bs;
-    BdrvNextIterator it;
-
-    for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
-        AioContext *ctx = bdrv_get_aio_context(bs);
-        if (ctx == qemu_get_aio_context()) {
-            continue;
-        }
-        aio_context_acquire(ctx);
-        bdrv_set_aio_context(bs, qemu_get_aio_context());
-        aio_context_release(ctx);
-    }
-
-    object_child_foreach(container, iothread_stop_iter, NULL);
-}
-
 static gpointer iothread_g_main_context_init(gpointer opaque)
 {
     AioContext *ctx;
index e3689c658af3e281765a5978696c44aba1e579cc..5fc130cc20ca4f15057fbc1b4e587477d9c9a13d 100644 (file)
@@ -1295,6 +1295,28 @@ static inline void init_thread(struct target_pt_regs *regs,
 
 #endif /* TARGET_TILEGX */
 
+#ifdef TARGET_RISCV
+
+#define ELF_START_MMAP 0x80000000
+#define ELF_ARCH  EM_RISCV
+
+#ifdef TARGET_RISCV32
+#define ELF_CLASS ELFCLASS32
+#else
+#define ELF_CLASS ELFCLASS64
+#endif
+
+static inline void init_thread(struct target_pt_regs *regs,
+                               struct image_info *infop)
+{
+    regs->sepc = infop->entry;
+    regs->sp = infop->start_stack;
+}
+
+#define ELF_EXEC_PAGESIZE 4096
+
+#endif /* TARGET_RISCV */
+
 #ifdef TARGET_HPPA
 
 #define ELF_START_MMAP  0x80000000
index bbeb78fb892bbbfa167de94bf541217943d2ed1f..7bc9bc79b052e389c40dc2bb22e8527939816403 100644 (file)
@@ -3653,6 +3653,100 @@ void cpu_loop(CPUTLGState *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)
@@ -4803,6 +4897,11 @@ int main(int argc, char **argv, char **envp)
         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;
diff --git a/linux-user/riscv/syscall_nr.h b/linux-user/riscv/syscall_nr.h
new file mode 100644 (file)
index 0000000..7e30f1f
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Syscall numbers from asm-generic, common for most
+ * of recently-added arches including RISC-V.
+ */
+
+#define TARGET_NR_io_setup 0
+#define TARGET_NR_io_destroy 1
+#define TARGET_NR_io_submit 2
+#define TARGET_NR_io_cancel 3
+#define TARGET_NR_io_getevents 4
+#define TARGET_NR_setxattr 5
+#define TARGET_NR_lsetxattr 6
+#define TARGET_NR_fsetxattr 7
+#define TARGET_NR_getxattr 8
+#define TARGET_NR_lgetxattr 9
+#define TARGET_NR_fgetxattr 10
+#define TARGET_NR_listxattr 11
+#define TARGET_NR_llistxattr 12
+#define TARGET_NR_flistxattr 13
+#define TARGET_NR_removexattr 14
+#define TARGET_NR_lremovexattr 15
+#define TARGET_NR_fremovexattr 16
+#define TARGET_NR_getcwd 17
+#define TARGET_NR_lookup_dcookie 18
+#define TARGET_NR_eventfd2 19
+#define TARGET_NR_epoll_create1 20
+#define TARGET_NR_epoll_ctl 21
+#define TARGET_NR_epoll_pwait 22
+#define TARGET_NR_dup 23
+#define TARGET_NR_dup3 24
+#ifdef TARGET_RISCV32
+#define TARGET_NR_fcntl64 25
+#else
+#define TARGET_NR_fcntl 25
+#endif
+#define TARGET_NR_inotify_init1 26
+#define TARGET_NR_inotify_add_watch 27
+#define TARGET_NR_inotify_rm_watch 28
+#define TARGET_NR_ioctl 29
+#define TARGET_NR_ioprio_set 30
+#define TARGET_NR_ioprio_get 31
+#define TARGET_NR_flock 32
+#define TARGET_NR_mknodat 33
+#define TARGET_NR_mkdirat 34
+#define TARGET_NR_unlinkat 35
+#define TARGET_NR_symlinkat 36
+#define TARGET_NR_linkat 37
+#define TARGET_NR_renameat 38
+#define TARGET_NR_umount2 39
+#define TARGET_NR_mount 40
+#define TARGET_NR_pivot_root 41
+#define TARGET_NR_nfsservctl 42
+#define TARGET_NR_statfs 43
+#define TARGET_NR_fstatfs 44
+#define TARGET_NR_truncate 45
+#define TARGET_NR_ftruncate 46
+#define TARGET_NR_fallocate 47
+#define TARGET_NR_faccessat 48
+#define TARGET_NR_chdir 49
+#define TARGET_NR_fchdir 50
+#define TARGET_NR_chroot 51
+#define TARGET_NR_fchmod 52
+#define TARGET_NR_fchmodat 53
+#define TARGET_NR_fchownat 54
+#define TARGET_NR_fchown 55
+#define TARGET_NR_openat 56
+#define TARGET_NR_close 57
+#define TARGET_NR_vhangup 58
+#define TARGET_NR_pipe2 59
+#define TARGET_NR_quotactl 60
+#define TARGET_NR_getdents64 61
+#define TARGET_NR_lseek 62
+#define TARGET_NR_read 63
+#define TARGET_NR_write 64
+#define TARGET_NR_readv 65
+#define TARGET_NR_writev 66
+#define TARGET_NR_pread64 67
+#define TARGET_NR_pwrite64 68
+#define TARGET_NR_preadv 69
+#define TARGET_NR_pwritev 70
+#define TARGET_NR_sendfile 71
+#define TARGET_NR_pselect6 72
+#define TARGET_NR_ppoll 73
+#define TARGET_NR_signalfd4 74
+#define TARGET_NR_vmsplice 75
+#define TARGET_NR_splice 76
+#define TARGET_NR_tee 77
+#define TARGET_NR_readlinkat 78
+#define TARGET_NR_newfstatat 79
+#define TARGET_NR_fstat 80
+#define TARGET_NR_sync 81
+#define TARGET_NR_fsync 82
+#define TARGET_NR_fdatasync 83
+#define TARGET_NR_sync_file_range 84
+#define TARGET_NR_timerfd_create 85
+#define TARGET_NR_timerfd_settime 86
+#define TARGET_NR_timerfd_gettime 87
+#define TARGET_NR_utimensat 88
+#define TARGET_NR_acct 89
+#define TARGET_NR_capget 90
+#define TARGET_NR_capset 91
+#define TARGET_NR_personality 92
+#define TARGET_NR_exit 93
+#define TARGET_NR_exit_group 94
+#define TARGET_NR_waitid 95
+#define TARGET_NR_set_tid_address 96
+#define TARGET_NR_unshare 97
+#define TARGET_NR_futex 98
+#define TARGET_NR_set_robust_list 99
+#define TARGET_NR_get_robust_list 100
+#define TARGET_NR_nanosleep 101
+#define TARGET_NR_getitimer 102
+#define TARGET_NR_setitimer 103
+#define TARGET_NR_kexec_load 104
+#define TARGET_NR_init_module 105
+#define TARGET_NR_delete_module 106
+#define TARGET_NR_timer_create 107
+#define TARGET_NR_timer_gettime 108
+#define TARGET_NR_timer_getoverrun 109
+#define TARGET_NR_timer_settime 110
+#define TARGET_NR_timer_delete 111
+#define TARGET_NR_clock_settime 112
+#define TARGET_NR_clock_gettime 113
+#define TARGET_NR_clock_getres 114
+#define TARGET_NR_clock_nanosleep 115
+#define TARGET_NR_syslog 116
+#define TARGET_NR_ptrace 117
+#define TARGET_NR_sched_setparam 118
+#define TARGET_NR_sched_setscheduler 119
+#define TARGET_NR_sched_getscheduler 120
+#define TARGET_NR_sched_getparam 121
+#define TARGET_NR_sched_setaffinity 122
+#define TARGET_NR_sched_getaffinity 123
+#define TARGET_NR_sched_yield 124
+#define TARGET_NR_sched_get_priority_max 125
+#define TARGET_NR_sched_get_priority_min 126
+#define TARGET_NR_sched_rr_get_interval 127
+#define TARGET_NR_restart_syscall 128
+#define TARGET_NR_kill 129
+#define TARGET_NR_tkill 130
+#define TARGET_NR_tgkill 131
+#define TARGET_NR_sigaltstack 132
+#define TARGET_NR_rt_sigsuspend 133
+#define TARGET_NR_rt_sigaction 134
+#define TARGET_NR_rt_sigprocmask 135
+#define TARGET_NR_rt_sigpending 136
+#define TARGET_NR_rt_sigtimedwait 137
+#define TARGET_NR_rt_sigqueueinfo 138
+#define TARGET_NR_rt_sigreturn 139
+#define TARGET_NR_setpriority 140
+#define TARGET_NR_getpriority 141
+#define TARGET_NR_reboot 142
+#define TARGET_NR_setregid 143
+#define TARGET_NR_setgid 144
+#define TARGET_NR_setreuid 145
+#define TARGET_NR_setuid 146
+#define TARGET_NR_setresuid 147
+#define TARGET_NR_getresuid 148
+#define TARGET_NR_setresgid 149
+#define TARGET_NR_getresgid 150
+#define TARGET_NR_setfsuid 151
+#define TARGET_NR_setfsgid 152
+#define TARGET_NR_times 153
+#define TARGET_NR_setpgid 154
+#define TARGET_NR_getpgid 155
+#define TARGET_NR_getsid 156
+#define TARGET_NR_setsid 157
+#define TARGET_NR_getgroups 158
+#define TARGET_NR_setgroups 159
+#define TARGET_NR_uname 160
+#define TARGET_NR_sethostname 161
+#define TARGET_NR_setdomainname 162
+#define TARGET_NR_getrlimit 163
+#define TARGET_NR_setrlimit 164
+#define TARGET_NR_getrusage 165
+#define TARGET_NR_umask 166
+#define TARGET_NR_prctl 167
+#define TARGET_NR_getcpu 168
+#define TARGET_NR_gettimeofday 169
+#define TARGET_NR_settimeofday 170
+#define TARGET_NR_adjtimex 171
+#define TARGET_NR_getpid 172
+#define TARGET_NR_getppid 173
+#define TARGET_NR_getuid 174
+#define TARGET_NR_geteuid 175
+#define TARGET_NR_getgid 176
+#define TARGET_NR_getegid 177
+#define TARGET_NR_gettid 178
+#define TARGET_NR_sysinfo 179
+#define TARGET_NR_mq_open 180
+#define TARGET_NR_mq_unlink 181
+#define TARGET_NR_mq_timedsend 182
+#define TARGET_NR_mq_timedreceive 183
+#define TARGET_NR_mq_notify 184
+#define TARGET_NR_mq_getsetattr 185
+#define TARGET_NR_msgget 186
+#define TARGET_NR_msgctl 187
+#define TARGET_NR_msgrcv 188
+#define TARGET_NR_msgsnd 189
+#define TARGET_NR_semget 190
+#define TARGET_NR_semctl 191
+#define TARGET_NR_semtimedop 192
+#define TARGET_NR_semop 193
+#define TARGET_NR_shmget 194
+#define TARGET_NR_shmctl 195
+#define TARGET_NR_shmat 196
+#define TARGET_NR_shmdt 197
+#define TARGET_NR_socket 198
+#define TARGET_NR_socketpair 199
+#define TARGET_NR_bind 200
+#define TARGET_NR_listen 201
+#define TARGET_NR_accept 202
+#define TARGET_NR_connect 203
+#define TARGET_NR_getsockname 204
+#define TARGET_NR_getpeername 205
+#define TARGET_NR_sendto 206
+#define TARGET_NR_recvfrom 207
+#define TARGET_NR_setsockopt 208
+#define TARGET_NR_getsockopt 209
+#define TARGET_NR_shutdown 210
+#define TARGET_NR_sendmsg 211
+#define TARGET_NR_recvmsg 212
+#define TARGET_NR_readahead 213
+#define TARGET_NR_brk 214
+#define TARGET_NR_munmap 215
+#define TARGET_NR_mremap 216
+#define TARGET_NR_add_key 217
+#define TARGET_NR_request_key 218
+#define TARGET_NR_keyctl 219
+#define TARGET_NR_clone 220
+#define TARGET_NR_execve 221
+#ifdef TARGET_RISCV32
+#define TARGET_NR_mmap2 222
+#define TARGET_NR_fadvise64_64 223
+#else
+#define TARGET_NR_mmap 222
+#define TARGET_NR_fadvise64 223
+#endif
+#define TARGET_NR_swapon 224
+#define TARGET_NR_swapoff 225
+#define TARGET_NR_mprotect 226
+#define TARGET_NR_msync 227
+#define TARGET_NR_mlock 228
+#define TARGET_NR_munlock 229
+#define TARGET_NR_mlockall 230
+#define TARGET_NR_munlockall 231
+#define TARGET_NR_mincore 232
+#define TARGET_NR_madvise 233
+#define TARGET_NR_remap_file_pages 234
+#define TARGET_NR_mbind 235
+#define TARGET_NR_get_mempolicy 236
+#define TARGET_NR_set_mempolicy 237
+#define TARGET_NR_migrate_pages 238
+#define TARGET_NR_move_pages 239
+#define TARGET_NR_rt_tgsigqueueinfo 240
+#define TARGET_NR_perf_event_open 241
+#define TARGET_NR_accept4 242
+#define TARGET_NR_recvmmsg 243
+#define TARGET_NR_arch_specific_syscall 244
+#define TARGET_NR_wait4 260
+#define TARGET_NR_prlimit64 261
+#define TARGET_NR_fanotify_init 262
+#define TARGET_NR_fanotify_mark 263
+#define TARGET_NR_name_to_handle_at 264
+#define TARGET_NR_open_by_handle_at 265
+#define TARGET_NR_clock_adjtime 266
+#define TARGET_NR_syncfs 267
+#define TARGET_NR_setns 268
+#define TARGET_NR_sendmmsg 269
+#define TARGET_NR_process_vm_readv 270
+#define TARGET_NR_process_vm_writev 271
+#define TARGET_NR_kcmp 272
+#define TARGET_NR_finit_module 273
+#define TARGET_NR_sched_setattr 274
+#define TARGET_NR_sched_getattr 275
+#define TARGET_NR_renameat2 276
+#define TARGET_NR_seccomp 277
+#define TARGET_NR_getrandom 278
+#define TARGET_NR_memfd_create 279
+#define TARGET_NR_bpf 280
+#define TARGET_NR_execveat 281
+#define TARGET_NR_userfaultfd 282
+#define TARGET_NR_membarrier 283
+#define TARGET_NR_mlock2 284
+#define TARGET_NR_copy_file_range 285
+
+#define TARGET_NR_syscalls (TARGET_NR_copy_file_range + 1)
diff --git a/linux-user/riscv/target_cpu.h b/linux-user/riscv/target_cpu.h
new file mode 100644 (file)
index 0000000..c5549b1
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef TARGET_CPU_H
+#define TARGET_CPU_H
+
+static inline void cpu_clone_regs(CPURISCVState *env, target_ulong newsp)
+{
+    if (newsp) {
+        env->gpr[xSP] = newsp;
+    }
+
+    env->gpr[xA0] = 0;
+}
+
+static inline void cpu_set_tls(CPURISCVState *env, target_ulong newtls)
+{
+    env->gpr[xTP] = newtls;
+}
+
+#endif
diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h
new file mode 100644 (file)
index 0000000..a6716a6
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * 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_ELF_H
+#define RISCV_TARGET_ELF_H
+static inline const char *cpu_get_model(uint32_t eflags)
+{
+    return "any";
+}
+#endif
diff --git a/linux-user/riscv/target_signal.h b/linux-user/riscv/target_signal.h
new file mode 100644 (file)
index 0000000..ce77f75
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef TARGET_SIGNAL_H
+#define TARGET_SIGNAL_H
+
+#include "cpu.h"
+
+typedef struct target_sigaltstack {
+    abi_ulong ss_sp;
+    abi_int ss_flags;
+    abi_ulong ss_size;
+} target_stack_t;
+
+#define TARGET_SS_ONSTACK 1
+#define TARGET_SS_DISABLE 2
+
+#define TARGET_MINSIGSTKSZ 2048
+#define TARGET_SIGSTKSZ 8192
+
+static inline abi_ulong get_sp_from_cpustate(CPURISCVState *state)
+{
+   return state->gpr[xSP];
+}
+
+#endif /* TARGET_SIGNAL_H */
diff --git a/linux-user/riscv/target_structs.h b/linux-user/riscv/target_structs.h
new file mode 100644 (file)
index 0000000..4f0462c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * RISC-V specific structures for linux-user
+ *
+ * This is a copy of ../aarch64/target_structs.h atm.
+ *
+ */
+#ifndef TARGET_STRUCTS_H
+#define TARGET_STRUCTS_H
+
+struct target_ipc_perm {
+    abi_int __key;                      /* Key.  */
+    abi_uint uid;                       /* Owner's user ID.  */
+    abi_uint gid;                       /* Owner's group ID.  */
+    abi_uint cuid;                      /* Creator's user ID.  */
+    abi_uint cgid;                      /* Creator's group ID.  */
+    abi_ushort mode;                    /* Read/write permission.  */
+    abi_ushort __pad1;
+    abi_ushort __seq;                   /* Sequence number.  */
+    abi_ushort __pad2;
+    abi_ulong __unused1;
+    abi_ulong __unused2;
+};
+
+struct target_shmid_ds {
+    struct target_ipc_perm shm_perm;    /* operation permission struct */
+    abi_long shm_segsz;                 /* size of segment in bytes */
+    abi_ulong shm_atime;                /* time of last shmat() */
+#if TARGET_ABI_BITS == 32
+    abi_ulong __unused1;
+#endif
+    abi_ulong shm_dtime;                /* time of last shmdt() */
+#if TARGET_ABI_BITS == 32
+    abi_ulong __unused2;
+#endif
+    abi_ulong shm_ctime;                /* time of last change by shmctl() */
+#if TARGET_ABI_BITS == 32
+    abi_ulong __unused3;
+#endif
+    abi_int shm_cpid;                   /* pid of creator */
+    abi_int shm_lpid;                   /* pid of last shmop */
+    abi_ulong shm_nattch;               /* number of current attaches */
+    abi_ulong __unused4;
+    abi_ulong __unused5;
+};
+
+#endif
diff --git a/linux-user/riscv/target_syscall.h b/linux-user/riscv/target_syscall.h
new file mode 100644 (file)
index 0000000..d4e109a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This struct defines the way the registers are stored on the
+ *  stack during a system call.
+ *
+ * Reference: linux/arch/riscv/include/uapi/asm/ptrace.h
+ */
+
+struct target_pt_regs {
+    abi_long sepc;
+    abi_long ra;
+    abi_long sp;
+    abi_long gp;
+    abi_long tp;
+    abi_long t0;
+    abi_long t1;
+    abi_long t2;
+    abi_long s0;
+    abi_long s1;
+    abi_long a0;
+    abi_long a1;
+    abi_long a2;
+    abi_long a3;
+    abi_long a4;
+    abi_long a5;
+    abi_long a6;
+    abi_long a7;
+    abi_long s2;
+    abi_long s3;
+    abi_long s4;
+    abi_long s5;
+    abi_long s6;
+    abi_long s7;
+    abi_long s8;
+    abi_long s9;
+    abi_long s10;
+    abi_long s11;
+    abi_long t3;
+    abi_long t4;
+    abi_long t5;
+    abi_long t6;
+};
+
+#ifdef TARGET_RISCV32
+#define UNAME_MACHINE "riscv32"
+#else
+#define UNAME_MACHINE "riscv64"
+#endif
+#define UNAME_MINIMUM_RELEASE "3.8.0"
+
+#define TARGET_MINSIGSTKSZ 2048
+#define TARGET_MLOCKALL_MCL_CURRENT 1
+#define TARGET_MLOCKALL_MCL_FUTURE  2
+
+/* clone(flags, newsp, ptidptr, tls, ctidptr) for RISC-V */
+/* This comes from linux/kernel/fork.c, CONFIG_CLONE_BACKWARDS */
+#define TARGET_CLONE_BACKWARDS
diff --git a/linux-user/riscv/termbits.h b/linux-user/riscv/termbits.h
new file mode 100644 (file)
index 0000000..7e4e230
--- /dev/null
@@ -0,0 +1,222 @@
+/* from asm/termbits.h */
+/* NOTE: exactly the same as i386 */
+
+#define TARGET_NCCS 19
+
+struct target_termios {
+    unsigned int c_iflag;               /* input mode flags */
+    unsigned int c_oflag;               /* output mode flags */
+    unsigned int c_cflag;               /* control mode flags */
+    unsigned int c_lflag;               /* local mode flags */
+    unsigned char c_line;               /* line discipline */
+    unsigned char c_cc[TARGET_NCCS];    /* control characters */
+};
+
+/* c_iflag bits */
+#define TARGET_IGNBRK  0000001
+#define TARGET_BRKINT  0000002
+#define TARGET_IGNPAR  0000004
+#define TARGET_PARMRK  0000010
+#define TARGET_INPCK   0000020
+#define TARGET_ISTRIP  0000040
+#define TARGET_INLCR   0000100
+#define TARGET_IGNCR   0000200
+#define TARGET_ICRNL   0000400
+#define TARGET_IUCLC   0001000
+#define TARGET_IXON    0002000
+#define TARGET_IXANY   0004000
+#define TARGET_IXOFF   0010000
+#define TARGET_IMAXBEL 0020000
+#define TARGET_IUTF8   0040000
+
+/* c_oflag bits */
+#define TARGET_OPOST   0000001
+#define TARGET_OLCUC   0000002
+#define TARGET_ONLCR   0000004
+#define TARGET_OCRNL   0000010
+#define TARGET_ONOCR   0000020
+#define TARGET_ONLRET  0000040
+#define TARGET_OFILL   0000100
+#define TARGET_OFDEL   0000200
+#define TARGET_NLDLY   0000400
+#define   TARGET_NL0   0000000
+#define   TARGET_NL1   0000400
+#define TARGET_CRDLY   0003000
+#define   TARGET_CR0   0000000
+#define   TARGET_CR1   0001000
+#define   TARGET_CR2   0002000
+#define   TARGET_CR3   0003000
+#define TARGET_TABDLY  0014000
+#define   TARGET_TAB0  0000000
+#define   TARGET_TAB1  0004000
+#define   TARGET_TAB2  0010000
+#define   TARGET_TAB3  0014000
+#define   TARGET_XTABS 0014000
+#define TARGET_BSDLY   0020000
+#define   TARGET_BS0   0000000
+#define   TARGET_BS1   0020000
+#define TARGET_VTDLY   0040000
+#define   TARGET_VT0   0000000
+#define   TARGET_VT1   0040000
+#define TARGET_FFDLY   0100000
+#define   TARGET_FF0   0000000
+#define   TARGET_FF1   0100000
+
+/* c_cflag bit meaning */
+#define TARGET_CBAUD   0010017
+#define  TARGET_B0     0000000         /* hang up */
+#define  TARGET_B50    0000001
+#define  TARGET_B75    0000002
+#define  TARGET_B110   0000003
+#define  TARGET_B134   0000004
+#define  TARGET_B150   0000005
+#define  TARGET_B200   0000006
+#define  TARGET_B300   0000007
+#define  TARGET_B600   0000010
+#define  TARGET_B1200  0000011
+#define  TARGET_B1800  0000012
+#define  TARGET_B2400  0000013
+#define  TARGET_B4800  0000014
+#define  TARGET_B9600  0000015
+#define  TARGET_B19200 0000016
+#define  TARGET_B38400 0000017
+#define TARGET_EXTA B19200
+#define TARGET_EXTB B38400
+#define TARGET_CSIZE   0000060
+#define   TARGET_CS5   0000000
+#define   TARGET_CS6   0000020
+#define   TARGET_CS7   0000040
+#define   TARGET_CS8   0000060
+#define TARGET_CSTOPB  0000100
+#define TARGET_CREAD   0000200
+#define TARGET_PARENB  0000400
+#define TARGET_PARODD  0001000
+#define TARGET_HUPCL   0002000
+#define TARGET_CLOCAL  0004000
+#define TARGET_CBAUDEX 0010000
+#define  TARGET_B57600  0010001
+#define  TARGET_B115200 0010002
+#define  TARGET_B230400 0010003
+#define  TARGET_B460800 0010004
+#define TARGET_CIBAUD    002003600000  /* input baud rate (not used) */
+#define TARGET_CMSPAR    010000000000  /* mark or space (stick) parity */
+#define TARGET_CRTSCTS   020000000000  /* flow control */
+
+/* c_lflag bits */
+#define TARGET_ISIG    0000001
+#define TARGET_ICANON  0000002
+#define TARGET_XCASE   0000004
+#define TARGET_ECHO    0000010
+#define TARGET_ECHOE   0000020
+#define TARGET_ECHOK   0000040
+#define TARGET_ECHONL  0000100
+#define TARGET_NOFLSH  0000200
+#define TARGET_TOSTOP  0000400
+#define TARGET_ECHOCTL 0001000
+#define TARGET_ECHOPRT 0002000
+#define TARGET_ECHOKE  0004000
+#define TARGET_FLUSHO  0010000
+#define TARGET_PENDIN  0040000
+#define TARGET_IEXTEN  0100000
+
+/* c_cc character offsets */
+#define TARGET_VINTR    0
+#define TARGET_VQUIT    1
+#define TARGET_VERASE   2
+#define TARGET_VKILL    3
+#define TARGET_VEOF     4
+#define TARGET_VTIME    5
+#define TARGET_VMIN     6
+#define TARGET_VSWTC    7
+#define TARGET_VSTART   8
+#define TARGET_VSTOP    9
+#define TARGET_VSUSP    10
+#define TARGET_VEOL     11
+#define TARGET_VREPRINT 12
+#define TARGET_VDISCARD 13
+#define TARGET_VWERASE  14
+#define TARGET_VLNEXT   15
+#define TARGET_VEOL2    16
+
+/* ioctls */
+
+#define TARGET_TCGETS           0x5401
+#define TARGET_TCSETS           0x5402
+#define TARGET_TCSETSW          0x5403
+#define TARGET_TCSETSF          0x5404
+#define TARGET_TCGETA           0x5405
+#define TARGET_TCSETA           0x5406
+#define TARGET_TCSETAW          0x5407
+#define TARGET_TCSETAF          0x5408
+#define TARGET_TCSBRK           0x5409
+#define TARGET_TCXONC           0x540A
+#define TARGET_TCFLSH           0x540B
+
+#define TARGET_TIOCEXCL         0x540C
+#define TARGET_TIOCNXCL         0x540D
+#define TARGET_TIOCSCTTY        0x540E
+#define TARGET_TIOCGPGRP        0x540F
+#define TARGET_TIOCSPGRP        0x5410
+#define TARGET_TIOCOUTQ         0x5411
+#define TARGET_TIOCSTI          0x5412
+#define TARGET_TIOCGWINSZ       0x5413
+#define TARGET_TIOCSWINSZ       0x5414
+#define TARGET_TIOCMGET         0x5415
+#define TARGET_TIOCMBIS         0x5416
+#define TARGET_TIOCMBIC         0x5417
+#define TARGET_TIOCMSET         0x5418
+#define TARGET_TIOCGSOFTCAR     0x5419
+#define TARGET_TIOCSSOFTCAR     0x541A
+#define TARGET_FIONREAD         0x541B
+#define TARGET_TIOCINQ          TARGET_FIONREAD
+#define TARGET_TIOCLINUX        0x541C
+#define TARGET_TIOCCONS         0x541D
+#define TARGET_TIOCGSERIAL      0x541E
+#define TARGET_TIOCSSERIAL      0x541F
+#define TARGET_TIOCPKT          0x5420
+#define TARGET_FIONBIO          0x5421
+#define TARGET_TIOCNOTTY        0x5422
+#define TARGET_TIOCSETD         0x5423
+#define TARGET_TIOCGETD         0x5424
+#define TARGET_TCSBRKP          0x5425 /* Needed for POSIX tcsendbreak() */
+#define TARGET_TIOCTTYGSTRUCT   0x5426 /* For debugging only */
+#define TARGET_TIOCSBRK         0x5427 /* BSD compatibility */
+#define TARGET_TIOCCBRK         0x5428 /* BSD compatibility */
+#define TARGET_TIOCGSID         0x5429 /* Return the session ID of FD */
+#define TARGET_TIOCGPTN         TARGET_IOR('T', 0x30, unsigned int)
+        /* Get Pty Number (of pty-mux device) */
+#define TARGET_TIOCSPTLCK       TARGET_IOW('T', 0x31, int)
+        /* Lock/unlock Pty */
+#define TARGET_TIOCGPTPEER      TARGET_IO('T', 0x41)
+        /* Safely open the slave */
+
+#define TARGET_FIONCLEX         0x5450  /* these numbers need to be adjusted. */
+#define TARGET_FIOCLEX          0x5451
+#define TARGET_FIOASYNC         0x5452
+#define TARGET_TIOCSERCONFIG    0x5453
+#define TARGET_TIOCSERGWILD     0x5454
+#define TARGET_TIOCSERSWILD     0x5455
+#define TARGET_TIOCGLCKTRMIOS   0x5456
+#define TARGET_TIOCSLCKTRMIOS   0x5457
+#define TARGET_TIOCSERGSTRUCT   0x5458 /* For debugging only */
+#define TARGET_TIOCSERGETLSR    0x5459 /* Get line status register */
+#define TARGET_TIOCSERGETMULTI  0x545A /* Get multiport config  */
+#define TARGET_TIOCSERSETMULTI  0x545B /* Set multiport config */
+
+#define TARGET_TIOCMIWAIT      0x545C
+        /* wait for a change on serial input line(s) */
+#define TARGET_TIOCGICOUNT     0x545D
+        /* read serial port inline interrupt counts */
+#define TARGET_TIOCGHAYESESP   0x545E  /* Get Hayes ESP configuration */
+#define TARGET_TIOCSHAYESESP   0x545F  /* Set Hayes ESP configuration */
+
+/* Used for packet mode */
+#define TARGET_TIOCPKT_DATA              0
+#define TARGET_TIOCPKT_FLUSHREAD         1
+#define TARGET_TIOCPKT_FLUSHWRITE        2
+#define TARGET_TIOCPKT_STOP              4
+#define TARGET_TIOCPKT_START             8
+#define TARGET_TIOCPKT_NOSTOP           16
+#define TARGET_TIOCPKT_DOSTOP           32
+
+#define TARGET_TIOCSER_TEMT    0x01 /* Transmitter physically empty */
index 9a380b9e319bcbde934ed1f301e5850215aba7ca..4d3f244612d1af8f4c51d689f05895ef0dcd4212 100644 (file)
@@ -535,6 +535,7 @@ static void force_sig(int sig)
  * up the signal frame. oldsig is the signal we were trying to handle
  * at the point of failure.
  */
+#if !defined(TARGET_RISCV)
 static void force_sigsegv(int oldsig)
 {
     if (oldsig == SIGSEGV) {
@@ -547,6 +548,8 @@ static void force_sigsegv(int oldsig)
 }
 #endif
 
+#endif
+
 /* abort execution with signal */
 static void QEMU_NORETURN dump_core_and_abort(int target_sig)
 {
@@ -6385,6 +6388,203 @@ long do_rt_sigreturn(CPUTLGState *env)
     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 {
@@ -6676,7 +6876,8 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig,
 #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_NIOS2) || defined(TARGET_X86_64) \
+        || defined(TARGET_RISCV)
         /* These targets do not have traditional signals.  */
         setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env);
 #else
index e24f43c4a2591c77b4aaa32a5483ea63c1706aa3..a8abfd421d03246b3ae90a64594bba7c8b35d9d3 100644 (file)
@@ -8555,9 +8555,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
     case TARGET_NR_ioctl:
         ret = do_ioctl(arg1, arg2, arg3);
         break;
+#ifdef TARGET_NR_fcntl
     case TARGET_NR_fcntl:
         ret = do_fcntl(arg1, arg2, arg3);
         break;
+#endif
 #ifdef TARGET_NR_mpx
     case TARGET_NR_mpx:
         goto unimplemented;
index a35c52a60a1788169e889bfb3676c15fc146d1bd..e00e1b38628e20de8b15b00d2a33600ffdfc6e84 100644 (file)
@@ -71,7 +71,7 @@
     || defined(TARGET_M68K) || defined(TARGET_CRIS) \
     || defined(TARGET_UNICORE32) || defined(TARGET_S390X) \
     || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \
-    || defined(TARGET_NIOS2)
+    || defined(TARGET_NIOS2) || defined(TARGET_RISCV)
 
 #define TARGET_IOC_SIZEBITS    14
 #define TARGET_IOC_DIRBITS     2
@@ -435,7 +435,8 @@ int do_sigaction(int sig, const struct target_sigaction *act,
     || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \
     || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32) \
     || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \
-    || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2)
+    || defined(TARGET_TILEGX) || defined(TARGET_HPPA) || defined(TARGET_NIOS2) \
+    || defined(TARGET_RISCV)
 
 #if defined(TARGET_SPARC)
 #define TARGET_SA_NOCLDSTOP    8u
@@ -2093,7 +2094,7 @@ struct target_stat {
     unsigned int __unused[2];
 };
 #elif defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) || \
-      defined(TARGET_NIOS2)
+      defined(TARGET_NIOS2) || defined(TARGET_RISCV)
 
 /* These are the asm-generic versions of the stat and stat64 structures */
 
@@ -2120,6 +2121,7 @@ struct target_stat {
     unsigned int __unused5;
 };
 
+#if !defined(TARGET_RISCV64)
 #define TARGET_HAS_STRUCT_STAT64
 struct target_stat64 {
     uint64_t st_dev;
@@ -2143,6 +2145,7 @@ struct target_stat64 {
     unsigned int __unused4;
     unsigned int __unused5;
 };
+#endif
 
 #elif defined(TARGET_HPPA)
 
@@ -2258,8 +2261,8 @@ struct target_statfs64 {
        uint32_t        f_spare[6];
 };
 #elif (defined(TARGET_PPC64) || defined(TARGET_X86_64) || \
-       defined(TARGET_SPARC64) || defined(TARGET_AARCH64)) && \
-       !defined(TARGET_ABI32)
+       defined(TARGET_SPARC64) || defined(TARGET_AARCH64) || \
+       defined(TARGET_RISCV)) && !defined(TARGET_ABI32)
 struct target_statfs {
        abi_long f_type;
        abi_long f_bsize;
index 6515131ac241f9973d99193faa540522ab5e6ae1..e70b64b8b9122298c100b55a8c39bf33f113aaad 100644 (file)
--- a/memory.c
+++ b/memory.c
@@ -210,8 +210,6 @@ static bool memory_region_ioeventfd_equal(MemoryRegionIoeventfd a,
         && !memory_region_ioeventfd_before(b, a);
 }
 
-typedef struct FlatRange FlatRange;
-
 /* Range of memory in the global map.  Addresses are absolute. */
 struct FlatRange {
     MemoryRegion *mr;
@@ -222,19 +220,6 @@ struct FlatRange {
     bool readonly;
 };
 
-/* Flattened global view of current active memory hierarchy.  Kept in sorted
- * order.
- */
-struct FlatView {
-    struct rcu_head rcu;
-    unsigned ref;
-    FlatRange *ranges;
-    unsigned nr;
-    unsigned nr_allocated;
-    struct AddressSpaceDispatch *dispatch;
-    MemoryRegion *root;
-};
-
 typedef struct AddressSpaceOps AddressSpaceOps;
 
 #define FOR_EACH_FLAT_RANGE(var, view)          \
@@ -322,21 +307,6 @@ static void flatview_unref(FlatView *view)
     }
 }
 
-FlatView *address_space_to_flatview(AddressSpace *as)
-{
-    return atomic_rcu_read(&as->current_map);
-}
-
-AddressSpaceDispatch *flatview_to_dispatch(FlatView *fv)
-{
-    return fv->dispatch;
-}
-
-AddressSpaceDispatch *address_space_to_dispatch(AddressSpace *as)
-{
-    return flatview_to_dispatch(address_space_to_flatview(as));
-}
-
 static bool can_merge(FlatRange *r1, FlatRange *r2)
 {
     return int128_eq(addrrange_end(r1->addr), r2->addr.start)
index e0900970779a8a7c558bd92e5e47ae83019764fc..8a93fb1af580ce8bee9f207b836424ea6ef2c320 100644 (file)
@@ -103,7 +103,8 @@ static void socket_start_outgoing_migration(MigrationState *s,
                                      saddr,
                                      socket_outgoing_migration,
                                      data,
-                                     socket_connect_data_free);
+                                     socket_connect_data_free,
+                                     NULL);
     qapi_free_SocketAddress(saddr);
 }
 
index a29b35b33c702587f3c6323be881017f273a178f..3b9e8c92638c53f54efee4ddeff9bedb4ceb4f08 100644 (file)
@@ -105,6 +105,7 @@ void migration_tls_channel_process_incoming(MigrationState *s,
     qio_channel_tls_handshake(tioc,
                               migration_tls_incoming_handshake,
                               NULL,
+                              NULL,
                               NULL);
 }
 
@@ -159,5 +160,6 @@ void migration_tls_channel_connect(MigrationState *s,
     qio_channel_tls_handshake(tioc,
                               migration_tls_outgoing_handshake,
                               s,
+                              NULL,
                               NULL);
 }
index 9c3fe4aaa67bb5d14d6568ddd6067b89e423ea11..dcad23a0531c614e8018167e9f3f9536c1ce99f4 100644 (file)
@@ -579,6 +579,7 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
     qio_channel_tls_handshake(tioc,
                               nbd_tls_handshake,
                               &data,
+                              NULL,
                               NULL);
 
     if (!data.complete) {
index 4990a5826e61f2ea66bdd55e94d7e55dcd7fc7b9..e714bfe6a1755021fbc6a2a21faa0b9d8579f162 100644 (file)
@@ -599,6 +599,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
     qio_channel_tls_handshake(tioc,
                               nbd_tls_handshake,
                               &data,
+                              NULL,
                               NULL);
 
     if (!data.complete) {
diff --git a/numa.c b/numa.c
index 5d7529c1d90799486278ce98a2cdc25b94a0c84b..398e2c9a855f2e5106e63ed87ed911ed66c3dc60 100644 (file)
--- a/numa.c
+++ b/numa.c
@@ -80,7 +80,7 @@ static void parse_numa_node(MachineState *ms, NumaNodeOptions *node,
         return;
     }
 
-    if (!mc->cpu_index_to_instance_props) {
+    if (!mc->cpu_index_to_instance_props || !mc->get_default_cpu_node_id) {
         error_report("NUMA is not supported by this machine-type");
         exit(1);
     }
index 29bfd8c875d0c43adddd2b0623fe90cbc19e2363..fc2a9fe33b2c3fa864bb655a2dec7e904b4bfb66 100644 (file)
@@ -37,6 +37,26 @@ typedef struct 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";
+
+/*
+ * Match two CCWs located after PSW and eight filler bytes.
+ * From libmagic and arch/s390/kernel/head.S.
+ */
+const uint8_t linux_s390_magic[] = "\x02\x00\x00\x18\x60\x00\x00\x50\x02\x00"
+                                   "\x00\x68\x60\x00\x00\x50\x40\x40\x40\x40"
+                                   "\x40\x40\x40\x40";
+
+static inline bool is_iso_vd_valid(IsoVolDesc *vd)
+{
+    const uint8_t vol_desc_magic[] = "CD001";
+
+    return !memcmp(&vd->ident[0], vol_desc_magic, 5) &&
+           vd->version == 0x1 &&
+           vd->type <= VOL_DESC_TYPE_PARTITION;
+}
+
 static void jump_to_IPL_2(void)
 {
     ResetInfo *current = 0;
index c636626f1a009bfbaf7b0fd6bdbcc628fc998159..07eb600b0000563949fb4371ed1d60a0af3a6343 100644 (file)
@@ -375,9 +375,6 @@ static inline void read_iso_boot_image(uint32_t block_offset, void *load_addr,
                "Failed to read boot image!");
 }
 
-const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION"
-                                  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
-
 #define ISO9660_MAX_DIR_DEPTH 8
 
 typedef struct IsoDirHdr {
@@ -430,20 +427,12 @@ typedef struct IsoVolDesc {
     } vd;
 } __attribute__((packed)) IsoVolDesc;
 
-const uint8_t vol_desc_magic[] = "CD001";
 #define VOL_DESC_TYPE_BOOT 0
 #define VOL_DESC_TYPE_PRIMARY 1
 #define VOL_DESC_TYPE_SUPPLEMENT 2
 #define VOL_DESC_TYPE_PARTITION 3
 #define VOL_DESC_TERMINATOR 255
 
-static inline bool is_iso_vd_valid(IsoVolDesc *vd)
-{
-    return !memcmp(&vd->ident[0], vol_desc_magic, 5) &&
-           vd->version == 0x1 &&
-           vd->type <= VOL_DESC_TYPE_PARTITION;
-}
-
 typedef struct IsoBcValid {
     uint8_t platform_id;
     uint16_t reserved;
@@ -468,14 +457,6 @@ typedef struct IsoBcHdr {
     uint8_t id[28];
 } __attribute__((packed)) IsoBcHdr;
 
-/*
- * Match two CCWs located after PSW and eight filler bytes.
- * From libmagic and arch/s390/kernel/head.S.
- */
-const uint8_t linux_s390_magic[] = "\x02\x00\x00\x18\x60\x00\x00\x50\x02\x00"
-                                   "\x00\x68\x60\x00\x00\x50\x40\x40\x40\x40"
-                                   "\x40\x40\x40\x40";
-
 typedef struct IsoBcEntry {
     uint8_t id;
     union {
index 5c5921bfb707007b53fb134c2d6bb31a643df0f9..00475f08d4e077e45261901abb1085ab9253810e 100644 (file)
 #
 # @node-name: node name. Note that errors may be reported for the root node
 #             that is directly attached to a guest device rather than for the
-#             node where the error occurred. (Since: 2.8)
+#             node where the error occurred. The node name is not present if
+#             the drive is empty. (Since: 2.8)
 #
 # @operation: I/O operation
 #
 #
 ##
 { 'event': 'BLOCK_IO_ERROR',
-  'data': { 'device': 'str', 'node-name': 'str', 'operation': 'IoOperationType',
+  'data': { 'device': 'str', '*node-name': 'str',
+            'operation': 'IoOperationType',
             'action': 'BlockErrorAction', '*nospace': 'bool',
             'reason': 'str' } }
 
index a1702c906098f80de4b187d4b10c2c1e29bb3b9f..bcd5d10778ab5687b97c8ba30e144b9a29ceb5db 100644 (file)
 #
 # @s390: since 2.12
 #
+# @riscv: since 2.12
+#
 # Since: 2.6
 ##
 { 'enum': 'CpuInfoArch',
-  'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'other' ] }
+  'data': ['x86', 'sparc', 'ppc', 'mips', 'tricore', 's390', 'riscv', 'other' ] }
 
 ##
 # @CpuInfo:
             'mips': 'CpuInfoMIPS',
             'tricore': 'CpuInfoTricore',
             's390': 'CpuInfoS390',
+            'riscv': 'CpuInfoRISCV',
             'other': 'CpuInfoOther' } }
 
 ##
 ##
 { 'struct': 'CpuInfoTricore', 'data': { 'PC': 'int' } }
 
+##
+# @CpuInfoRISCV:
+#
+# Additional information about a virtual RISCV CPU
+#
+# @pc: the instruction pointer
+#
+# Since 2.12
+##
+{ 'struct': 'CpuInfoRISCV', 'data': { 'pc': 'int' } }
+
 ##
 # @CpuInfoOther:
 #
             'mips': 'CpuInfoOther',
             'tricore': 'CpuInfoOther',
             's390': 'CpuInfoS390',
+            'riscv': 'CpuInfoRISCV',
             'other': 'CpuInfoOther' } }
 
 ##
 #        3) A link type in the form 'link<subtype>' where subtype is a qdev
 #           device type name.  Link properties form the device model graph.
 #
+# @description: if specified, the description of the property.
+#
 # Since: 1.2
 ##
 { 'struct': 'ObjectPropertyInfo',
-  'data': { 'name': 'str', 'type': 'str' } }
+  'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }
 
 ##
 # @qom-list:
   'returns': [ 'ObjectTypeInfo' ] }
 
 ##
-# @DevicePropertyInfo:
+# @device-list-properties:
 #
-# Information about device properties.
+# List properties associated with a device.
 #
-# @name: the name of the property
-# @type: the typename of the property
-# @description: if specified, the description of the property.
-#               (since 2.2)
+# @typename: the type name of a device
+#
+# Returns: a list of ObjectPropertyInfo describing a devices properties
 #
 # Since: 1.2
 ##
-{ 'struct': 'DevicePropertyInfo',
-  'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }
+{ 'command': 'device-list-properties',
+  'data': { 'typename': 'str'},
+  'returns': [ 'ObjectPropertyInfo' ] }
 
 ##
-# @device-list-properties:
+# @qom-list-properties:
 #
-# List properties associated with a device.
+# List properties associated with a QOM object.
 #
-# @typename: the type name of a device
+# @typename: the type name of an object
 #
-# Returns: a list of DevicePropertyInfo describing a devices properties
+# Returns: a list of ObjectPropertyInfo describing object properties
 #
-# Since: 1.2
+# Since: 2.12
 ##
-{ 'command': 'device-list-properties',
+{ 'command': 'qom-list-properties',
   'data': { 'typename': 'str'},
-  'returns': [ 'DevicePropertyInfo' ] }
+  'returns': [ 'ObjectPropertyInfo' ] }
 
 ##
 # @xen-set-global-dirty-log:
index b8f6bc3f7edf2ae4205ee761c6a4c9143166060c..b7e3291f8b0af61f0c233a3ce9b32de8e39144f0 100644 (file)
@@ -258,8 +258,8 @@ int qdev_device_help(QemuOpts *opts)
 {
     Error *local_err = NULL;
     const char *driver;
-    DevicePropertyInfoList *prop_list;
-    DevicePropertyInfoList *prop;
+    ObjectPropertyInfoList *prop_list;
+    ObjectPropertyInfoList *prop;
 
     driver = qemu_opt_get(opts, "driver");
     if (driver && is_help_option(driver)) {
@@ -295,7 +295,7 @@ int qdev_device_help(QemuOpts *opts)
         }
     }
 
-    qapi_free_DevicePropertyInfoList(prop_list);
+    qapi_free_ObjectPropertyInfoList(prop_list);
     return 1;
 
 error:
index 4fcc85acc7e871c43e703f9227989d2b8c3eb080..39e38c87ecf6b0a0855254e417ef3484075c3fd8 100644 (file)
@@ -2606,13 +2606,6 @@ which is the default.
 
 @section System emulator command line arguments
 
-@subsection -tdf (since 1.3.0)
-
-The ``-tdf'' argument is ignored. The behaviour implemented
-by this argument is now the default when using the KVM PIT,
-but can be requested explicitly using
-``-global kvm-pit.lost_tick_policy=slew''.
-
 @subsection -no-kvm-pit-reinjection (since 1.3.0)
 
 The ``-no-kvm-pit-reinjection'' argument is now a
@@ -2687,11 +2680,6 @@ The ``-net vlan=NN'' argument is partially replaced with the
 new ``-netdev'' argument. The remaining use cases will no
 longer be directly supported in QEMU.
 
-@subsection -drive if=scsi (since 2.9.0)
-
-The ``-drive if=scsi'' argument is replaced by the the
-``-device BUS-TYPE'' argument combined with ``-drive if=none''.
-
 @subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
 
 The drive geometry arguments are replaced by the the geometry arguments
@@ -2719,6 +2707,11 @@ enabled via the ``-machine usb=on'' argument.
 
 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
@@ -2735,12 +2728,18 @@ 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)
+@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 ``-no-frame'' argument works with SDL 1.2 only.  SDL 2.0 lacks
-support for frameless windows, and the other user interfaces never
-implemented this in the first place.  So this will be removed together
-with SDL 1.2 support.
+The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}.
 
 @section qemu-img command line arguments
 
index 40bf7aa7d15dd9ee76454d1f570492258667df64..088d89043e925fbceda9745dc777c1ab49fb2f0c 100644 (file)
@@ -3469,7 +3469,7 @@ static int img_resize(int argc, char **argv)
         }
     }
     if (optind != argc - 1) {
-        error_exit("Expecting one image file name");
+        error_exit("Expecting image file name and size");
     }
     filename = argv[optind++];
 
index 2c00ea068ece9fcaddea66edc906abf1531ae158..160fb2a89f7db3cd45482c43f9be4e277eb9f376 100644 (file)
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -504,7 +504,7 @@ int main(int argc, char **argv)
 #endif
 
     module_call_init(MODULE_INIT_TRACE);
-    progname = basename(argv[0]);
+    progname = g_path_get_basename(argv[0]);
     qemu_init_exec_dir(argv[0]);
 
     qcrypto_init(&error_fatal);
index 2a22a62f743bdaa7ea2253c3d718fa09bd25520d..6585058c6cded02f1c18607afb7e782217100875 100644 (file)
@@ -462,16 +462,13 @@ modprobe i810_audio clocking=48000
 ETEXI
 
 DEF("balloon", HAS_ARG, QEMU_OPTION_balloon,
-    "-balloon none   disable balloon device\n"
     "-balloon virtio[,addr=str]\n"
-    "                enable virtio balloon device (default)\n", QEMU_ARCH_ALL)
+    "                enable virtio balloon device (deprecated)\n", QEMU_ARCH_ALL)
 STEXI
-@item -balloon none
-@findex -balloon
-Disable balloon device.
 @item -balloon virtio[,addr=@var{addr}]
-Enable virtio balloon device (default), optionally with PCI address
-@var{addr}.
+@findex -balloon
+Enable virtio balloon device, optionally with PCI address @var{addr}. This
+option is deprecated, use @option{--device virtio-balloon} instead.
 ETEXI
 
 DEF("device", HAS_ARG, QEMU_OPTION_device,
index ac17d0d6cf8861953ddd26dad57a125111f17abf..0dc219dbcf57343f55d4fb29575d01cbf8ba7163 100644 (file)
@@ -808,7 +808,7 @@ static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
     len = readlink(dpath, buf, sizeof(buf) - 1);
     if (len != -1) {
         buf[len] = 0;
-        driver = g_strdup(basename(buf));
+        driver = g_path_get_basename(buf);
     }
     g_free(dpath);
     g_free(path);
@@ -1053,7 +1053,7 @@ static void build_guest_fsinfo_for_device(char const *devpath,
     }
 
     if (!fs->name) {
-        fs->name = g_strdup(basename(syspath));
+        fs->name = g_path_get_basename(syspath);
     }
 
     g_debug("  parse sysfs path '%s'", syspath);
diff --git a/qmp.c b/qmp.c
index ba82e1df9f462d80db7d9f25cb987036dfe2be05..8c7d1cc4790e6d1f456ae44c968c346b650b8ecf 100644 (file)
--- a/qmp.c
+++ b/qmp.c
@@ -465,12 +465,12 @@ ObjectTypeInfoList *qmp_qom_list_types(bool has_implements,
  *
  * The caller must free the return value.
  */
-static DevicePropertyInfo *make_device_property_info(ObjectClass *klass,
-                                                     const char *name,
-                                                     const char *default_type,
-                                                     const char *description)
+static ObjectPropertyInfo *make_device_property_info(ObjectClass *klass,
+                                                  const char *name,
+                                                  const char *default_type,
+                                                  const char *description)
 {
-    DevicePropertyInfo *info;
+    ObjectPropertyInfo *info;
     Property *prop;
 
     do {
@@ -510,14 +510,14 @@ static DevicePropertyInfo *make_device_property_info(ObjectClass *klass,
     return info;
 }
 
-DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
-                                                   Error **errp)
+ObjectPropertyInfoList *qmp_device_list_properties(const char *typename,
+                                                Error **errp)
 {
     ObjectClass *klass;
     Object *obj;
     ObjectProperty *prop;
     ObjectPropertyIterator iter;
-    DevicePropertyInfoList *prop_list = NULL;
+    ObjectPropertyInfoList *prop_list = NULL;
 
     klass = object_class_by_name(typename);
     if (klass == NULL) {
@@ -542,8 +542,8 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
 
     object_property_iter_init(&iter, obj);
     while ((prop = object_property_iter_next(&iter))) {
-        DevicePropertyInfo *info;
-        DevicePropertyInfoList *entry;
+        ObjectPropertyInfo *info;
+        ObjectPropertyInfoList *entry;
 
         /* Skip Object and DeviceState properties */
         if (strcmp(prop->name, "type") == 0 ||
@@ -578,6 +578,55 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
     return prop_list;
 }
 
+ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename,
+                                             Error **errp)
+{
+    ObjectClass *klass;
+    Object *obj = NULL;
+    ObjectProperty *prop;
+    ObjectPropertyIterator iter;
+    ObjectPropertyInfoList *prop_list = NULL;
+
+    klass = object_class_by_name(typename);
+    if (klass == NULL) {
+        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
+                  "Class '%s' not found", typename);
+        return NULL;
+    }
+
+    klass = object_class_dynamic_cast(klass, TYPE_OBJECT);
+    if (klass == NULL) {
+        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "typename", TYPE_OBJECT);
+        return NULL;
+    }
+
+    if (object_class_is_abstract(klass)) {
+        object_class_property_iter_init(&iter, klass);
+    } else {
+        obj = object_new(typename);
+        object_property_iter_init(&iter, obj);
+    }
+    while ((prop = object_property_iter_next(&iter))) {
+        ObjectPropertyInfo *info;
+        ObjectPropertyInfoList *entry;
+
+        info = g_malloc0(sizeof(*info));
+        info->name = g_strdup(prop->name);
+        info->type = g_strdup(prop->type);
+        info->has_description = !!prop->description;
+        info->description = g_strdup(prop->description);
+
+        entry = g_malloc0(sizeof(*entry));
+        entry->value = info;
+        entry->next = prop_list;
+        prop_list = entry;
+    }
+
+    object_unref(obj);
+
+    return prop_list;
+}
+
 CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
 {
     return arch_query_cpu_definitions(errp);
index f70a75c3084dc4e6d564bd348b16477cc19d12c6..755ad038197338345c1db02815716365f92d53e0 100644 (file)
@@ -1037,6 +1037,13 @@ ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter)
     return val;
 }
 
+void object_class_property_iter_init(ObjectPropertyIterator *iter,
+                                     ObjectClass *klass)
+{
+    g_hash_table_iter_init(&iter->iter, klass->properties);
+    iter->nextclass = klass;
+}
+
 ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name,
                                            Error **errp)
 {
index 1b4b812e28fa16ac684a845779dcea5e02512749..d1fe79bcc47cc1a02863f94be140a634fde48184 100755 (executable)
@@ -2352,8 +2352,9 @@ sub process {
                        }
                }
 
-# check for missing bracing round if etc
-               if ($line =~ /(^.*)\bif\b/ && $line !~ /\#\s*if/) {
+# check for missing bracing around if etc
+               if ($line =~ /(^.*)\b(?:if|while|for)\b/ &&
+                       $line !~ /\#\s*if/) {
                        my ($level, $endln, @chunks) =
                                ctx_statement_full($linenr, $realcnt, 1);
                         if ($dbg_adv_apw) {
@@ -2584,6 +2585,11 @@ sub process {
                        ERROR("__func__ should be used instead of gcc specific __FUNCTION__\n"  . $herecurr);
                }
 
+# recommend g_path_get_* over g_strdup(basename/dirname(...))
+               if ($line =~ /\bg_strdup\s*\(\s*(basename|dirname)\s*\(/) {
+                       WARN("consider using g_path_get_$1() in preference to g_strdup($1())\n" . $herecurr);
+               }
+
 # recommend qemu_strto* over strto* for numeric conversions
                if ($line =~ /\b(strto[^kd].*?)\s*\(/) {
                        ERROR("consider using qemu_$1 in preference to $1\n" . $herecurr);
index ea5a7487457c6ec0264bac6da1260535bab63087..bdb21bdd58e1b9cec34bec07f7f444f0cf35ff23 100755 (executable)
@@ -4,7 +4,7 @@
 
 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"
+sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64"
 
 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'
@@ -100,6 +100,14 @@ hppa_magic='\x7f\x45\x4c\x46\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
 hppa_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff'
 hppa_family=hppa
 
+riscv32_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'
+riscv32_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
+riscv32_family=riscv
+
+riscv64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'
+riscv64_mask='\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'
+riscv64_family=riscv
+
 qemu_get_family() {
     cpu=${HOST_ARCH:-$(uname -m)}
     case "$cpu" in
@@ -124,6 +132,9 @@ qemu_get_family() {
     sparc*)
         echo "sparc"
         ;;
+    riscv*)
+        echo "riscv"
+        ;;
     *)
         echo "$cpu"
         ;;
index 0015b27509ad22811ead820486802b683b27e78f..940bbe590d3375281fca9a562a4e2eb47e640117 100644 (file)
 #include "qapi/error.h"
 #include "migration/blocker.h"
 
-#include <winhvplatform.h>
-#include <winhvemulation.h>
+#include <WinHvPlatform.h>
+#include <WinHvEmulation.h>
 
 struct whpx_state {
     uint64_t mem_quota;
     WHV_PARTITION_HANDLE partition;
-    uint32_t exit_ctx_size;
 };
 
 static const WHV_REGISTER_NAME whpx_register_names[] = {
@@ -364,7 +363,6 @@ static void whpx_set_registers(CPUState *cpu)
     if (FAILED(hr)) {
         error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
                      hr);
-        __debugbreak();
     }
 
     return;
@@ -391,7 +389,6 @@ static void whpx_get_registers(CPUState *cpu)
     if (FAILED(hr)) {
         error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
                      hr);
-        __debugbreak();
     }
 
     /* Indexes for first 16 registers match between HV and QEMU definitions */
@@ -529,7 +526,7 @@ static HRESULT CALLBACK whpx_emu_ioport_callback(
     return S_OK;
 }
 
-static HRESULT CALLBACK whpx_emu_memio_callback(
+static HRESULT CALLBACK whpx_emu_mmio_callback(
     void *ctx,
     WHV_EMULATOR_MEMORY_ACCESS_INFO *ma)
 {
@@ -554,7 +551,6 @@ static HRESULT CALLBACK whpx_emu_getreg_callback(
     if (FAILED(hr)) {
         error_report("WHPX: Failed to get virtual processor registers,"
                      " hr=%08lx", hr);
-        __debugbreak();
     }
 
     return hr;
@@ -576,7 +572,6 @@ static HRESULT CALLBACK whpx_emu_setreg_callback(
     if (FAILED(hr)) {
         error_report("WHPX: Failed to set virtual processor registers,"
                      " hr=%08lx", hr);
-        __debugbreak();
     }
 
     /*
@@ -604,7 +599,6 @@ static HRESULT CALLBACK whpx_emu_translate_callback(
                          Gva, TranslateFlags, &res, Gpa);
     if (FAILED(hr)) {
         error_report("WHPX: Failed to translate GVA, hr=%08lx", hr);
-        __debugbreak();
     } else {
         *TranslationResult = res.ResultCode;
     }
@@ -613,8 +607,9 @@ static HRESULT CALLBACK whpx_emu_translate_callback(
 }
 
 static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = {
+    .Size = sizeof(WHV_EMULATOR_CALLBACKS),
     .WHvEmulatorIoPortCallback = whpx_emu_ioport_callback,
-    .WHvEmulatorMemoryCallback = whpx_emu_memio_callback,
+    .WHvEmulatorMemoryCallback = whpx_emu_mmio_callback,
     .WHvEmulatorGetVirtualProcessorRegisters = whpx_emu_getreg_callback,
     .WHvEmulatorSetVirtualProcessorRegisters = whpx_emu_setreg_callback,
     .WHvEmulatorTranslateGvaPage = whpx_emu_translate_callback,
@@ -626,15 +621,15 @@ 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, ctx, &emu_status);
+    hr = WHvEmulatorTryMmioEmulation(vcpu->emulator, cpu,
+                                     &vcpu->exit_ctx.VpContext, ctx,
+                                     &emu_status);
     if (FAILED(hr)) {
-        __debugbreak();
         error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr);
         return -1;
     }
 
     if (!emu_status.EmulationSuccessful) {
-        __debugbreak();
         error_report("WHPX: Failed to emulate MMIO access");
         return -1;
     }
@@ -649,15 +644,15 @@ 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, ctx, &emu_status);
+    hr = WHvEmulatorTryIoEmulation(vcpu->emulator, cpu,
+                                   &vcpu->exit_ctx.VpContext, ctx,
+                                   &emu_status);
     if (FAILED(hr)) {
-        __debugbreak();
         error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr);
         return -1;
     }
 
     if (!emu_status.EmulationSuccessful) {
-        __debugbreak();
         error_report("WHPX: Failed to emulate PortMMIO access");
         return -1;
     }
@@ -691,6 +686,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
     X86CPU *x86_cpu = X86_CPU(cpu);
     int irq;
+    uint8_t tpr;
     WHV_X64_PENDING_INTERRUPTION_REGISTER new_int = {0};
     UINT32 reg_count = 0;
     WHV_REGISTER_VALUE reg_values[3] = {0};
@@ -709,10 +705,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
             new_int.InterruptionVector = 2;
         }
         if (cpu->interrupt_request & CPU_INTERRUPT_SMI) {
-            qemu_mutex_lock_iothread();
             cpu->interrupt_request &= ~CPU_INTERRUPT_SMI;
-            __debugbreak();
-            qemu_mutex_unlock_iothread();
         }
     }
 
@@ -753,21 +746,21 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
     }
 
     /* Sync the TPR to the CR8 if was modified during the intercept */
-    reg_values[reg_count].Reg64 = cpu_get_apic_tpr(x86_cpu->apic_state);
-    if (reg_values[reg_count].Reg64 != vcpu->tpr) {
-        vcpu->tpr = reg_values[reg_count].Reg64;
+    tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
+    if (tpr != vcpu->tpr) {
+        vcpu->tpr = tpr;
+        reg_values[reg_count].Reg64 = tpr;
         cpu->exit_request = 1;
         reg_names[reg_count] = WHvX64RegisterCr8;
         reg_count += 1;
     }
 
     /* Update the state of the interrupt delivery notification */
-    if (cpu->interrupt_request & CPU_INTERRUPT_HARD) {
+    if (!vcpu->window_registered &&
+        cpu->interrupt_request & CPU_INTERRUPT_HARD) {
         reg_values[reg_count].DeliverabilityNotifications.InterruptNotification
             = 1;
-        if (vcpu->window_registered != 1) {
-            vcpu->window_registered = 1;
-        }
+        vcpu->window_registered = 1;
         reg_names[reg_count] = WHvX64RegisterDeliverabilityNotifications;
         reg_count += 1;
     }
@@ -780,7 +773,6 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
         if (FAILED(hr)) {
             error_report("WHPX: Failed to set interrupt state registers,"
                          " hr=%08lx", hr);
-            __debugbreak();
         }
     }
 
@@ -807,7 +799,6 @@ static void whpx_vcpu_post_run(CPUState *cpu)
     if (FAILED(hr)) {
         error_report("WHPX: Failed to get interrupt state regusters,"
                      " hr=%08lx", hr);
-        __debugbreak();
         vcpu->interruptable = false;
         return;
     }
@@ -905,18 +896,8 @@ static int whpx_vcpu_run(CPUState *cpu)
             whpx_vcpu_kick(cpu);
         }
 
-        for (;;) {
-            hr = WHvRunVirtualProcessor(whpx->partition, cpu->cpu_index,
-                                        &vcpu->exit_ctx, whpx->exit_ctx_size);
-
-            if (SUCCEEDED(hr) && (vcpu->exit_ctx.ExitReason ==
-                                  WHvRunVpExitReasonAlerted)) {
-                WHvCancelRunVirtualProcessor(whpx->partition, cpu->cpu_index,
-                                             0);
-            } else {
-                break;
-            }
-        }
+        hr = 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,"
@@ -956,7 +937,6 @@ static int whpx_vcpu_run(CPUState *cpu)
         case WHvRunVpExitReasonX64MsrAccess:
         case WHvRunVpExitReasonX64Cpuid:
         case WHvRunVpExitReasonException:
-        case WHvRunVpExitReasonAlerted:
         default:
             error_report("WHPX: Unexpected VP exit code %d",
                          vcpu->exit_ctx.ExitReason);
@@ -1060,15 +1040,14 @@ int whpx_init_vcpu(CPUState *cpu)
         }
     }
 
-    vcpu = g_malloc0(FIELD_OFFSET(struct whpx_vcpu, exit_ctx) +
-                     whpx->exit_ctx_size);
+    vcpu = g_malloc0(sizeof(struct whpx_vcpu));
 
     if (!vcpu) {
         error_report("WHPX: Failed to allocte VCPU context.");
         return -ENOMEM;
     }
 
-    hr = WHvEmulatorCreateEmulator(whpx_emu_callbacks, &vcpu->emulator);
+    hr = WHvEmulatorCreateEmulator(&whpx_emu_callbacks, &vcpu->emulator);
     if (FAILED(hr)) {
         error_report("WHPX: Failed to setup instruction completion support,"
                      " hr=%08lx", hr);
@@ -1318,9 +1297,6 @@ static int whpx_accel_init(MachineState *ms)
         goto error;
     }
 
-    whpx->exit_ctx_size = WHvGetRunExitContextSize();
-    assert(whpx->exit_ctx_size);
-
     whpx_memory_init();
 
     cpu_interrupt_handler = whpx_handle_interrupt;
diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs
new file mode 100644 (file)
index 0000000..abd0a7c
--- /dev/null
@@ -0,0 +1 @@
+obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o gdbstub.o pmp.o
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
new file mode 100644 (file)
index 0000000..4851890
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * QEMU RISC-V CPU
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+
+/* RISC-V CPU definitions */
+
+static const char riscv_exts[26] = "IMAFDQECLBJTPVNSUHKORWXYZG";
+
+const char * const riscv_int_regnames[] = {
+  "zero", "ra  ", "sp  ", "gp  ", "tp  ", "t0  ", "t1  ", "t2  ",
+  "s0  ", "s1  ", "a0  ", "a1  ", "a2  ", "a3  ", "a4  ", "a5  ",
+  "a6  ", "a7  ", "s2  ", "s3  ", "s4  ", "s5  ", "s6  ", "s7  ",
+  "s8  ", "s9  ", "s10 ", "s11 ", "t3  ", "t4  ", "t5  ", "t6  "
+};
+
+const char * const riscv_fpr_regnames[] = {
+  "ft0 ", "ft1 ", "ft2 ", "ft3 ", "ft4 ", "ft5 ", "ft6 ",  "ft7 ",
+  "fs0 ", "fs1 ", "fa0 ", "fa1 ", "fa2 ", "fa3 ", "fa4 ",  "fa5 ",
+  "fa6 ", "fa7 ", "fs2 ", "fs3 ", "fs4 ", "fs5 ", "fs6 ",  "fs7 ",
+  "fs8 ", "fs9 ", "fs10", "fs11", "ft8 ", "ft9 ", "ft10",  "ft11"
+};
+
+const char * const riscv_excp_names[] = {
+    "misaligned_fetch",
+    "fault_fetch",
+    "illegal_instruction",
+    "breakpoint",
+    "misaligned_load",
+    "fault_load",
+    "misaligned_store",
+    "fault_store",
+    "user_ecall",
+    "supervisor_ecall",
+    "hypervisor_ecall",
+    "machine_ecall",
+    "exec_page_fault",
+    "load_page_fault",
+    "reserved",
+    "store_page_fault"
+};
+
+const char * const riscv_intr_names[] = {
+    "u_software",
+    "s_software",
+    "h_software",
+    "m_software",
+    "u_timer",
+    "s_timer",
+    "h_timer",
+    "m_timer",
+    "u_external",
+    "s_external",
+    "h_external",
+    "m_external",
+    "coprocessor",
+    "host"
+};
+
+typedef struct RISCVCPUInfo {
+    const int bit_widths;
+    const char *name;
+    void (*initfn)(Object *obj);
+} RISCVCPUInfo;
+
+static void set_misa(CPURISCVState *env, target_ulong misa)
+{
+    env->misa = misa;
+}
+
+static void set_versions(CPURISCVState *env, int user_ver, int priv_ver)
+{
+    env->user_ver = user_ver;
+    env->priv_ver = priv_ver;
+}
+
+static void set_feature(CPURISCVState *env, int feature)
+{
+    env->features |= (1ULL << feature);
+}
+
+static void set_resetvec(CPURISCVState *env, int resetvec)
+{
+#ifndef CONFIG_USER_ONLY
+    env->resetvec = resetvec;
+#endif
+}
+
+static void riscv_any_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RVXLEN | RVI | RVM | RVA | RVF | RVD | RVC | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+}
+
+static void rv32gcsu_priv1_09_1_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
+    set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_MMU);
+}
+
+static void rv32gcsu_priv1_10_0_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_MMU);
+}
+
+static void rv32imacu_nommu_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+}
+
+static void rv64gcsu_priv1_09_1_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1);
+    set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_MMU);
+}
+
+static void rv64gcsu_priv1_10_0_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV64 | RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+    set_feature(env, RISCV_FEATURE_MMU);
+}
+
+static void rv64imacu_nommu_cpu_init(Object *obj)
+{
+    CPURISCVState *env = &RISCV_CPU(obj)->env;
+    set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU);
+    set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0);
+    set_resetvec(env, DEFAULT_RSTVEC);
+}
+
+static const RISCVCPUInfo riscv_cpus[] = {
+    { 96, TYPE_RISCV_CPU_ANY,              riscv_any_cpu_init },
+    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_09_1, rv32gcsu_priv1_09_1_cpu_init },
+    { 32, TYPE_RISCV_CPU_RV32GCSU_V1_10_0, rv32gcsu_priv1_10_0_cpu_init },
+    { 32, TYPE_RISCV_CPU_RV32IMACU_NOMMU,  rv32imacu_nommu_cpu_init },
+    { 32, TYPE_RISCV_CPU_SIFIVE_E31,       rv32imacu_nommu_cpu_init },
+    { 32, TYPE_RISCV_CPU_SIFIVE_U34,       rv32gcsu_priv1_10_0_cpu_init },
+    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_09_1, rv64gcsu_priv1_09_1_cpu_init },
+    { 64, TYPE_RISCV_CPU_RV64GCSU_V1_10_0, rv64gcsu_priv1_10_0_cpu_init },
+    { 64, TYPE_RISCV_CPU_RV64IMACU_NOMMU,  rv64imacu_nommu_cpu_init },
+    { 64, TYPE_RISCV_CPU_SIFIVE_E51,       rv64imacu_nommu_cpu_init },
+    { 64, TYPE_RISCV_CPU_SIFIVE_U54,       rv64gcsu_priv1_10_0_cpu_init },
+    { 0, NULL, NULL }
+};
+
+static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
+{
+    ObjectClass *oc;
+    char *typename;
+    char **cpuname;
+
+    cpuname = g_strsplit(cpu_model, ",", 1);
+    typename = g_strdup_printf(RISCV_CPU_TYPE_NAME("%s"), cpuname[0]);
+    oc = object_class_by_name(typename);
+    g_strfreev(cpuname);
+    g_free(typename);
+    if (!oc || !object_class_dynamic_cast(oc, TYPE_RISCV_CPU) ||
+        object_class_is_abstract(oc)) {
+        return NULL;
+    }
+    return oc;
+}
+
+static void riscv_cpu_dump_state(CPUState *cs, FILE *f,
+    fprintf_function cpu_fprintf, int flags)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    int i;
+
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "pc      ", env->pc);
+#ifndef CONFIG_USER_ONLY
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mhartid ", env->mhartid);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mstatus ", env->mstatus);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mip     ",
+        (target_ulong)atomic_read(&env->mip));
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mie     ", env->mie);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mideleg ", env->mideleg);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "medeleg ", env->medeleg);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mtvec   ", env->mtvec);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mepc    ", env->mepc);
+    cpu_fprintf(f, " %s " TARGET_FMT_lx "\n", "mcause  ", env->mcause);
+#endif
+
+    for (i = 0; i < 32; i++) {
+        cpu_fprintf(f, " %s " TARGET_FMT_lx,
+            riscv_int_regnames[i], env->gpr[i]);
+        if ((i & 3) == 3) {
+            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");
+        }
+    }
+}
+
+static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    env->pc = value;
+}
+
+static void riscv_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    env->pc = tb->pc;
+}
+
+static bool riscv_cpu_has_work(CPUState *cs)
+{
+#ifndef CONFIG_USER_ONLY
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    /*
+     * Definition of the WFI instruction requires it to ignore the privilege
+     * mode and delegation registers, but respect individual enables
+     */
+    return (atomic_read(&env->mip) & env->mie) != 0;
+#else
+    return true;
+#endif
+}
+
+void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb,
+                          target_ulong *data)
+{
+    env->pc = data[0];
+}
+
+static void riscv_cpu_reset(CPUState *cs)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu);
+    CPURISCVState *env = &cpu->env;
+
+    mcc->parent_reset(cs);
+#ifndef CONFIG_USER_ONLY
+    env->priv = PRV_M;
+    env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV);
+    env->mcause = 0;
+    env->pc = env->resetvec;
+#endif
+    cs->exception_index = EXCP_NONE;
+    set_default_nan_mode(1, &env->fp_status);
+}
+
+static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
+{
+#if defined(TARGET_RISCV32)
+    info->print_insn = print_insn_riscv32;
+#elif defined(TARGET_RISCV64)
+    info->print_insn = print_insn_riscv64;
+#endif
+}
+
+static void riscv_cpu_realize(DeviceState *dev, Error **errp)
+{
+    CPUState *cs = CPU(dev);
+    RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+    Error *local_err = NULL;
+
+    cpu_exec_realizefn(cs, &local_err);
+    if (local_err != NULL) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    qemu_init_vcpu(cs);
+    cpu_reset(cs);
+
+    mcc->parent_realize(dev, errp);
+}
+
+static void riscv_cpu_init(Object *obj)
+{
+    CPUState *cs = CPU(obj);
+    RISCVCPU *cpu = RISCV_CPU(obj);
+
+    cs->env_ptr = &cpu->env;
+}
+
+static const VMStateDescription vmstate_riscv_cpu = {
+    .name = "cpu",
+    .unmigratable = 1,
+};
+
+static void riscv_cpu_class_init(ObjectClass *c, void *data)
+{
+    RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);
+    CPUClass *cc = CPU_CLASS(c);
+    DeviceClass *dc = DEVICE_CLASS(c);
+
+    mcc->parent_realize = dc->realize;
+    dc->realize = riscv_cpu_realize;
+
+    mcc->parent_reset = cc->reset;
+    cc->reset = riscv_cpu_reset;
+
+    cc->class_by_name = riscv_cpu_class_by_name;
+    cc->has_work = riscv_cpu_has_work;
+    cc->do_interrupt = riscv_cpu_do_interrupt;
+    cc->cpu_exec_interrupt = riscv_cpu_exec_interrupt;
+    cc->dump_state = riscv_cpu_dump_state;
+    cc->set_pc = riscv_cpu_set_pc;
+    cc->synchronize_from_tb = riscv_cpu_synchronize_from_tb;
+    cc->gdb_read_register = riscv_cpu_gdb_read_register;
+    cc->gdb_write_register = riscv_cpu_gdb_write_register;
+    cc->gdb_num_core_regs = 65;
+    cc->gdb_stop_before_watchpoint = true;
+    cc->disas_set_info = riscv_cpu_disas_set_info;
+#ifdef CONFIG_USER_ONLY
+    cc->handle_mmu_fault = riscv_cpu_handle_mmu_fault;
+#else
+    cc->do_unaligned_access = riscv_cpu_do_unaligned_access;
+    cc->get_phys_page_debug = riscv_cpu_get_phys_page_debug;
+#endif
+#ifdef CONFIG_TCG
+    cc->tcg_initialize = riscv_translate_init;
+#endif
+    /* For now, mark unmigratable: */
+    cc->vmsd = &vmstate_riscv_cpu;
+}
+
+static void cpu_register(const RISCVCPUInfo *info)
+{
+    TypeInfo type_info = {
+        .name = info->name,
+        .parent = TYPE_RISCV_CPU,
+        .instance_size = sizeof(RISCVCPU),
+        .instance_init = info->initfn,
+    };
+
+    type_register(&type_info);
+}
+
+static const TypeInfo riscv_cpu_type_info = {
+    .name = TYPE_RISCV_CPU,
+    .parent = TYPE_CPU,
+    .instance_size = sizeof(RISCVCPU),
+    .instance_init = riscv_cpu_init,
+    .abstract = false,
+    .class_size = sizeof(RISCVCPUClass),
+    .class_init = riscv_cpu_class_init,
+};
+
+char *riscv_isa_string(RISCVCPU *cpu)
+{
+    int i;
+    size_t maxlen = 5 + ctz32(cpu->env.misa);
+    char *isa_string = g_new0(char, maxlen);
+    snprintf(isa_string, maxlen, "rv%d", TARGET_LONG_BITS);
+    for (i = 0; i < sizeof(riscv_exts); i++) {
+        if (cpu->env.misa & RV(riscv_exts[i])) {
+            isa_string[strlen(isa_string)] = riscv_exts[i] - 'A' + 'a';
+
+        }
+    }
+    return isa_string;
+}
+
+void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf)
+{
+    const RISCVCPUInfo *info = riscv_cpus;
+
+    while (info->name) {
+        if (info->bit_widths & TARGET_LONG_BITS) {
+            (*cpu_fprintf)(f, "%s\n", info->name);
+        }
+        info++;
+    }
+}
+
+static void riscv_cpu_register_types(void)
+{
+    const RISCVCPUInfo *info = riscv_cpus;
+
+    type_register_static(&riscv_cpu_type_info);
+
+    while (info->name) {
+        if (info->bit_widths & TARGET_LONG_BITS) {
+            cpu_register(info);
+        }
+        info++;
+    }
+}
+
+type_init(riscv_cpu_register_types)
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
new file mode 100644 (file)
index 0000000..cff02a2
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * QEMU RISC-V CPU
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef RISCV_CPU_H
+#define RISCV_CPU_H
+
+/* QEMU addressing/paging config */
+#define TARGET_PAGE_BITS 12 /* 4 KiB Pages */
+#if defined(TARGET_RISCV64)
+#define TARGET_LONG_BITS 64
+#define TARGET_PHYS_ADDR_SPACE_BITS 50
+#define TARGET_VIRT_ADDR_SPACE_BITS 39
+#elif defined(TARGET_RISCV32)
+#define TARGET_LONG_BITS 32
+#define TARGET_PHYS_ADDR_SPACE_BITS 34
+#define TARGET_VIRT_ADDR_SPACE_BITS 32
+#endif
+
+#define TCG_GUEST_DEFAULT_MO 0
+
+#define ELF_MACHINE EM_RISCV
+#define CPUArchState struct CPURISCVState
+
+#include "qemu-common.h"
+#include "qom/cpu.h"
+#include "exec/cpu-defs.h"
+#include "fpu/softfloat.h"
+
+#define TYPE_RISCV_CPU "riscv-cpu"
+
+#define RISCV_CPU_TYPE_SUFFIX "-" TYPE_RISCV_CPU
+#define RISCV_CPU_TYPE_NAME(name) (name RISCV_CPU_TYPE_SUFFIX)
+
+#define TYPE_RISCV_CPU_ANY              RISCV_CPU_TYPE_NAME("any")
+#define TYPE_RISCV_CPU_RV32GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.9.1")
+#define TYPE_RISCV_CPU_RV32GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv32gcsu-v1.10.0")
+#define TYPE_RISCV_CPU_RV32IMACU_NOMMU  RISCV_CPU_TYPE_NAME("rv32imacu-nommu")
+#define TYPE_RISCV_CPU_RV64GCSU_V1_09_1 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.9.1")
+#define TYPE_RISCV_CPU_RV64GCSU_V1_10_0 RISCV_CPU_TYPE_NAME("rv64gcsu-v1.10.0")
+#define TYPE_RISCV_CPU_RV64IMACU_NOMMU  RISCV_CPU_TYPE_NAME("rv64imacu-nommu")
+#define TYPE_RISCV_CPU_SIFIVE_E31       RISCV_CPU_TYPE_NAME("sifive-e31")
+#define TYPE_RISCV_CPU_SIFIVE_E51       RISCV_CPU_TYPE_NAME("sifive-e51")
+#define TYPE_RISCV_CPU_SIFIVE_U34       RISCV_CPU_TYPE_NAME("sifive-u34")
+#define TYPE_RISCV_CPU_SIFIVE_U54       RISCV_CPU_TYPE_NAME("sifive-u54")
+
+#define RV32 ((target_ulong)1 << (TARGET_LONG_BITS - 2))
+#define RV64 ((target_ulong)2 << (TARGET_LONG_BITS - 2))
+
+#if defined(TARGET_RISCV32)
+#define RVXLEN RV32
+#elif defined(TARGET_RISCV64)
+#define RVXLEN RV64
+#endif
+
+#define RV(x) ((target_ulong)1 << (x - 'A'))
+
+#define RVI RV('I')
+#define RVM RV('M')
+#define RVA RV('A')
+#define RVF RV('F')
+#define RVD RV('D')
+#define RVC RV('C')
+#define RVS RV('S')
+#define RVU RV('U')
+
+/* S extension denotes that Supervisor mode exists, however it is possible
+   to have a core that support S mode but does not have an MMU and there
+   is currently no bit in misa to indicate whether an MMU exists or not
+   so a cpu features bitfield is required */
+enum {
+    RISCV_FEATURE_MMU
+};
+
+#define USER_VERSION_2_02_0 0x00020200
+#define PRIV_VERSION_1_09_1 0x00010901
+#define PRIV_VERSION_1_10_0 0x00011000
+
+#define TRANSLATE_FAIL 1
+#define TRANSLATE_SUCCESS 0
+#define NB_MMU_MODES 4
+#define MMU_USER_IDX 3
+
+#define MAX_RISCV_PMPS (16)
+
+typedef struct CPURISCVState CPURISCVState;
+
+#include "pmp.h"
+
+struct CPURISCVState {
+    target_ulong gpr[32];
+    uint64_t fpr[32]; /* assume both F and D extensions */
+    target_ulong pc;
+    target_ulong load_res;
+    target_ulong load_val;
+
+    target_ulong frm;
+
+    target_ulong badaddr;
+
+    target_ulong user_ver;
+    target_ulong priv_ver;
+    target_ulong misa;
+
+    uint32_t features;
+
+#ifndef CONFIG_USER_ONLY
+    target_ulong priv;
+    target_ulong resetvec;
+
+    target_ulong mhartid;
+    target_ulong mstatus;
+    /*
+     * CAUTION! Unlike the rest of this struct, mip is accessed asynchonously
+     * by I/O threads and other vCPUs, so hold the iothread mutex before
+     * operating on it.  CPU_INTERRUPT_HARD should be in effect iff this is
+     * non-zero.  Use riscv_cpu_set_local_interrupt.
+     */
+    uint32_t mip;        /* allow atomic_read for >= 32-bit hosts */
+    target_ulong mie;
+    target_ulong mideleg;
+
+    target_ulong sptbr;  /* until: priv-1.9.1 */
+    target_ulong satp;   /* since: priv-1.10.0 */
+    target_ulong sbadaddr;
+    target_ulong mbadaddr;
+    target_ulong medeleg;
+
+    target_ulong stvec;
+    target_ulong sepc;
+    target_ulong scause;
+
+    target_ulong mtvec;
+    target_ulong mepc;
+    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 sscratch;
+    target_ulong mscratch;
+
+    /* temporary htif regs */
+    uint64_t mfromhost;
+    uint64_t mtohost;
+    uint64_t timecmp;
+
+    /* physical memory protection */
+    pmp_table_t pmp_state;
+#endif
+
+    float_status fp_status;
+
+    /* QEMU */
+    CPU_COMMON
+
+    /* Fields from here on are preserved across CPU reset. */
+    QEMUTimer *timer; /* Internal timer */
+};
+
+#define RISCV_CPU_CLASS(klass) \
+    OBJECT_CLASS_CHECK(RISCVCPUClass, (klass), TYPE_RISCV_CPU)
+#define RISCV_CPU(obj) \
+    OBJECT_CHECK(RISCVCPU, (obj), TYPE_RISCV_CPU)
+#define RISCV_CPU_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(RISCVCPUClass, (obj), TYPE_RISCV_CPU)
+
+/**
+ * RISCVCPUClass:
+ * @parent_realize: The parent class' realize handler.
+ * @parent_reset: The parent class' reset handler.
+ *
+ * A RISCV CPU model.
+ */
+typedef struct RISCVCPUClass {
+    /*< private >*/
+    CPUClass parent_class;
+    /*< public >*/
+    DeviceRealize parent_realize;
+    void (*parent_reset)(CPUState *cpu);
+} RISCVCPUClass;
+
+/**
+ * RISCVCPU:
+ * @env: #CPURISCVState
+ *
+ * A RISCV CPU.
+ */
+typedef struct RISCVCPU {
+    /*< private >*/
+    CPUState parent_obj;
+    /*< public >*/
+    CPURISCVState env;
+} RISCVCPU;
+
+static inline RISCVCPU *riscv_env_get_cpu(CPURISCVState *env)
+{
+    return container_of(env, RISCVCPU, env);
+}
+
+static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext)
+{
+    return (env->misa & ext) != 0;
+}
+
+static inline bool riscv_feature(CPURISCVState *env, int feature)
+{
+    return env->features & (1ULL << feature);
+}
+
+#include "cpu_user.h"
+#include "cpu_bits.h"
+
+extern const char * const riscv_int_regnames[];
+extern const char * const riscv_fpr_regnames[];
+extern const char * const riscv_excp_names[];
+extern const char * const riscv_intr_names[];
+
+#define ENV_GET_CPU(e) CPU(riscv_env_get_cpu(e))
+#define ENV_OFFSET offsetof(RISCVCPU, env)
+
+void riscv_cpu_do_interrupt(CPUState *cpu);
+int riscv_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
+int riscv_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request);
+int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch);
+hwaddr riscv_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+void  riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+                                    MMUAccessType access_type, int mmu_idx,
+                                    uintptr_t retaddr);
+int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size,
+                              int rw, int mmu_idx);
+
+char *riscv_isa_string(RISCVCPU *cpu);
+void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+
+#define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model)
+#define cpu_signal_handler cpu_riscv_signal_handler
+#define cpu_list riscv_cpu_list
+#define cpu_mmu_index riscv_cpu_mmu_index
+
+void riscv_set_mode(CPURISCVState *env, target_ulong newpriv);
+
+void riscv_translate_init(void);
+RISCVCPU *cpu_riscv_init(const char *cpu_model);
+int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc);
+void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
+                                          uint32_t exception, uintptr_t pc);
+
+target_ulong cpu_riscv_get_fflags(CPURISCVState *env);
+void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong);
+
+#define TB_FLAGS_MMU_MASK  3
+#define TB_FLAGS_FP_ENABLE MSTATUS_FS
+
+static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
+                                        target_ulong *cs_base, uint32_t *flags)
+{
+    *pc = env->pc;
+    *cs_base = 0;
+#ifdef CONFIG_USER_ONLY
+    *flags = TB_FLAGS_FP_ENABLE;
+#else
+    *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS);
+#endif
+}
+
+void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
+        target_ulong csrno);
+target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno);
+
+#ifndef CONFIG_USER_ONLY
+void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value);
+#endif
+
+#include "exec/cpu-all.h"
+
+#endif /* RISCV_CPU_H */
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
new file mode 100644 (file)
index 0000000..64aa097
--- /dev/null
@@ -0,0 +1,411 @@
+/* RISC-V ISA constants */
+
+#define get_field(reg, mask) (((reg) & \
+                 (target_ulong)(mask)) / ((mask) & ~((mask) << 1)))
+#define set_field(reg, mask, val) (((reg) & ~(target_ulong)(mask)) | \
+                 (((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \
+                 (target_ulong)(mask)))
+
+#define PGSHIFT 12
+
+#define FSR_RD_SHIFT 5
+#define FSR_RD   (0x7 << FSR_RD_SHIFT)
+
+#define FPEXC_NX 0x01
+#define FPEXC_UF 0x02
+#define FPEXC_OF 0x04
+#define FPEXC_DZ 0x08
+#define FPEXC_NV 0x10
+
+#define FSR_AEXC_SHIFT 0
+#define FSR_NVA  (FPEXC_NV << FSR_AEXC_SHIFT)
+#define FSR_OFA  (FPEXC_OF << FSR_AEXC_SHIFT)
+#define FSR_UFA  (FPEXC_UF << FSR_AEXC_SHIFT)
+#define FSR_DZA  (FPEXC_DZ << FSR_AEXC_SHIFT)
+#define FSR_NXA  (FPEXC_NX << FSR_AEXC_SHIFT)
+#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
+
+/* CSR numbers */
+#define CSR_FFLAGS 0x1
+#define CSR_FRM 0x2
+#define CSR_FCSR 0x3
+#define CSR_CYCLE 0xc00
+#define CSR_TIME 0xc01
+#define CSR_INSTRET 0xc02
+#define CSR_HPMCOUNTER3 0xc03
+#define CSR_HPMCOUNTER4 0xc04
+#define CSR_HPMCOUNTER5 0xc05
+#define CSR_HPMCOUNTER6 0xc06
+#define CSR_HPMCOUNTER7 0xc07
+#define CSR_HPMCOUNTER8 0xc08
+#define CSR_HPMCOUNTER9 0xc09
+#define CSR_HPMCOUNTER10 0xc0a
+#define CSR_HPMCOUNTER11 0xc0b
+#define CSR_HPMCOUNTER12 0xc0c
+#define CSR_HPMCOUNTER13 0xc0d
+#define CSR_HPMCOUNTER14 0xc0e
+#define CSR_HPMCOUNTER15 0xc0f
+#define CSR_HPMCOUNTER16 0xc10
+#define CSR_HPMCOUNTER17 0xc11
+#define CSR_HPMCOUNTER18 0xc12
+#define CSR_HPMCOUNTER19 0xc13
+#define CSR_HPMCOUNTER20 0xc14
+#define CSR_HPMCOUNTER21 0xc15
+#define CSR_HPMCOUNTER22 0xc16
+#define CSR_HPMCOUNTER23 0xc17
+#define CSR_HPMCOUNTER24 0xc18
+#define CSR_HPMCOUNTER25 0xc19
+#define CSR_HPMCOUNTER26 0xc1a
+#define CSR_HPMCOUNTER27 0xc1b
+#define CSR_HPMCOUNTER28 0xc1c
+#define CSR_HPMCOUNTER29 0xc1d
+#define CSR_HPMCOUNTER30 0xc1e
+#define CSR_HPMCOUNTER31 0xc1f
+#define CSR_SSTATUS 0x100
+#define CSR_SIE 0x104
+#define CSR_STVEC 0x105
+#define CSR_SCOUNTEREN 0x106
+#define CSR_SSCRATCH 0x140
+#define CSR_SEPC 0x141
+#define CSR_SCAUSE 0x142
+#define CSR_SBADADDR 0x143
+#define CSR_SIP 0x144
+#define CSR_SPTBR 0x180
+#define CSR_SATP 0x180
+#define CSR_MSTATUS 0x300
+#define CSR_MISA 0x301
+#define CSR_MEDELEG 0x302
+#define CSR_MIDELEG 0x303
+#define CSR_MIE 0x304
+#define CSR_MTVEC 0x305
+#define CSR_MCOUNTEREN 0x306
+#define CSR_MSCRATCH 0x340
+#define CSR_MEPC 0x341
+#define CSR_MCAUSE 0x342
+#define CSR_MBADADDR 0x343
+#define CSR_MIP 0x344
+#define CSR_PMPCFG0 0x3a0
+#define CSR_PMPCFG1 0x3a1
+#define CSR_PMPCFG2 0x3a2
+#define CSR_PMPCFG3 0x3a3
+#define CSR_PMPADDR0 0x3b0
+#define CSR_PMPADDR1 0x3b1
+#define CSR_PMPADDR2 0x3b2
+#define CSR_PMPADDR3 0x3b3
+#define CSR_PMPADDR4 0x3b4
+#define CSR_PMPADDR5 0x3b5
+#define CSR_PMPADDR6 0x3b6
+#define CSR_PMPADDR7 0x3b7
+#define CSR_PMPADDR8 0x3b8
+#define CSR_PMPADDR9 0x3b9
+#define CSR_PMPADDR10 0x3ba
+#define CSR_PMPADDR11 0x3bb
+#define CSR_PMPADDR12 0x3bc
+#define CSR_PMPADDR13 0x3bd
+#define CSR_PMPADDR14 0x3be
+#define CSR_PMPADDR15 0x3bf
+#define CSR_TSELECT 0x7a0
+#define CSR_TDATA1 0x7a1
+#define CSR_TDATA2 0x7a2
+#define CSR_TDATA3 0x7a3
+#define CSR_DCSR 0x7b0
+#define CSR_DPC 0x7b1
+#define CSR_DSCRATCH 0x7b2
+#define CSR_MCYCLE 0xb00
+#define CSR_MINSTRET 0xb02
+#define CSR_MHPMCOUNTER3 0xb03
+#define CSR_MHPMCOUNTER4 0xb04
+#define CSR_MHPMCOUNTER5 0xb05
+#define CSR_MHPMCOUNTER6 0xb06
+#define CSR_MHPMCOUNTER7 0xb07
+#define CSR_MHPMCOUNTER8 0xb08
+#define CSR_MHPMCOUNTER9 0xb09
+#define CSR_MHPMCOUNTER10 0xb0a
+#define CSR_MHPMCOUNTER11 0xb0b
+#define CSR_MHPMCOUNTER12 0xb0c
+#define CSR_MHPMCOUNTER13 0xb0d
+#define CSR_MHPMCOUNTER14 0xb0e
+#define CSR_MHPMCOUNTER15 0xb0f
+#define CSR_MHPMCOUNTER16 0xb10
+#define CSR_MHPMCOUNTER17 0xb11
+#define CSR_MHPMCOUNTER18 0xb12
+#define CSR_MHPMCOUNTER19 0xb13
+#define CSR_MHPMCOUNTER20 0xb14
+#define CSR_MHPMCOUNTER21 0xb15
+#define CSR_MHPMCOUNTER22 0xb16
+#define CSR_MHPMCOUNTER23 0xb17
+#define CSR_MHPMCOUNTER24 0xb18
+#define CSR_MHPMCOUNTER25 0xb19
+#define CSR_MHPMCOUNTER26 0xb1a
+#define CSR_MHPMCOUNTER27 0xb1b
+#define CSR_MHPMCOUNTER28 0xb1c
+#define CSR_MHPMCOUNTER29 0xb1d
+#define CSR_MHPMCOUNTER30 0xb1e
+#define CSR_MHPMCOUNTER31 0xb1f
+#define CSR_MUCOUNTEREN 0x320
+#define CSR_MSCOUNTEREN 0x321
+#define CSR_MHPMEVENT3 0x323
+#define CSR_MHPMEVENT4 0x324
+#define CSR_MHPMEVENT5 0x325
+#define CSR_MHPMEVENT6 0x326
+#define CSR_MHPMEVENT7 0x327
+#define CSR_MHPMEVENT8 0x328
+#define CSR_MHPMEVENT9 0x329
+#define CSR_MHPMEVENT10 0x32a
+#define CSR_MHPMEVENT11 0x32b
+#define CSR_MHPMEVENT12 0x32c
+#define CSR_MHPMEVENT13 0x32d
+#define CSR_MHPMEVENT14 0x32e
+#define CSR_MHPMEVENT15 0x32f
+#define CSR_MHPMEVENT16 0x330
+#define CSR_MHPMEVENT17 0x331
+#define CSR_MHPMEVENT18 0x332
+#define CSR_MHPMEVENT19 0x333
+#define CSR_MHPMEVENT20 0x334
+#define CSR_MHPMEVENT21 0x335
+#define CSR_MHPMEVENT22 0x336
+#define CSR_MHPMEVENT23 0x337
+#define CSR_MHPMEVENT24 0x338
+#define CSR_MHPMEVENT25 0x339
+#define CSR_MHPMEVENT26 0x33a
+#define CSR_MHPMEVENT27 0x33b
+#define CSR_MHPMEVENT28 0x33c
+#define CSR_MHPMEVENT29 0x33d
+#define CSR_MHPMEVENT30 0x33e
+#define CSR_MHPMEVENT31 0x33f
+#define CSR_MVENDORID 0xf11
+#define CSR_MARCHID 0xf12
+#define CSR_MIMPID 0xf13
+#define CSR_MHARTID 0xf14
+#define CSR_CYCLEH 0xc80
+#define CSR_TIMEH 0xc81
+#define CSR_INSTRETH 0xc82
+#define CSR_HPMCOUNTER3H 0xc83
+#define CSR_HPMCOUNTER4H 0xc84
+#define CSR_HPMCOUNTER5H 0xc85
+#define CSR_HPMCOUNTER6H 0xc86
+#define CSR_HPMCOUNTER7H 0xc87
+#define CSR_HPMCOUNTER8H 0xc88
+#define CSR_HPMCOUNTER9H 0xc89
+#define CSR_HPMCOUNTER10H 0xc8a
+#define CSR_HPMCOUNTER11H 0xc8b
+#define CSR_HPMCOUNTER12H 0xc8c
+#define CSR_HPMCOUNTER13H 0xc8d
+#define CSR_HPMCOUNTER14H 0xc8e
+#define CSR_HPMCOUNTER15H 0xc8f
+#define CSR_HPMCOUNTER16H 0xc90
+#define CSR_HPMCOUNTER17H 0xc91
+#define CSR_HPMCOUNTER18H 0xc92
+#define CSR_HPMCOUNTER19H 0xc93
+#define CSR_HPMCOUNTER20H 0xc94
+#define CSR_HPMCOUNTER21H 0xc95
+#define CSR_HPMCOUNTER22H 0xc96
+#define CSR_HPMCOUNTER23H 0xc97
+#define CSR_HPMCOUNTER24H 0xc98
+#define CSR_HPMCOUNTER25H 0xc99
+#define CSR_HPMCOUNTER26H 0xc9a
+#define CSR_HPMCOUNTER27H 0xc9b
+#define CSR_HPMCOUNTER28H 0xc9c
+#define CSR_HPMCOUNTER29H 0xc9d
+#define CSR_HPMCOUNTER30H 0xc9e
+#define CSR_HPMCOUNTER31H 0xc9f
+#define CSR_MCYCLEH 0xb80
+#define CSR_MINSTRETH 0xb82
+#define CSR_MHPMCOUNTER3H 0xb83
+#define CSR_MHPMCOUNTER4H 0xb84
+#define CSR_MHPMCOUNTER5H 0xb85
+#define CSR_MHPMCOUNTER6H 0xb86
+#define CSR_MHPMCOUNTER7H 0xb87
+#define CSR_MHPMCOUNTER8H 0xb88
+#define CSR_MHPMCOUNTER9H 0xb89
+#define CSR_MHPMCOUNTER10H 0xb8a
+#define CSR_MHPMCOUNTER11H 0xb8b
+#define CSR_MHPMCOUNTER12H 0xb8c
+#define CSR_MHPMCOUNTER13H 0xb8d
+#define CSR_MHPMCOUNTER14H 0xb8e
+#define CSR_MHPMCOUNTER15H 0xb8f
+#define CSR_MHPMCOUNTER16H 0xb90
+#define CSR_MHPMCOUNTER17H 0xb91
+#define CSR_MHPMCOUNTER18H 0xb92
+#define CSR_MHPMCOUNTER19H 0xb93
+#define CSR_MHPMCOUNTER20H 0xb94
+#define CSR_MHPMCOUNTER21H 0xb95
+#define CSR_MHPMCOUNTER22H 0xb96
+#define CSR_MHPMCOUNTER23H 0xb97
+#define CSR_MHPMCOUNTER24H 0xb98
+#define CSR_MHPMCOUNTER25H 0xb99
+#define CSR_MHPMCOUNTER26H 0xb9a
+#define CSR_MHPMCOUNTER27H 0xb9b
+#define CSR_MHPMCOUNTER28H 0xb9c
+#define CSR_MHPMCOUNTER29H 0xb9d
+#define CSR_MHPMCOUNTER30H 0xb9e
+#define CSR_MHPMCOUNTER31H 0xb9f
+
+/* mstatus bits */
+#define MSTATUS_UIE         0x00000001
+#define MSTATUS_SIE         0x00000002
+#define MSTATUS_HIE         0x00000004
+#define MSTATUS_MIE         0x00000008
+#define MSTATUS_UPIE        0x00000010
+#define MSTATUS_SPIE        0x00000020
+#define MSTATUS_HPIE        0x00000040
+#define MSTATUS_MPIE        0x00000080
+#define MSTATUS_SPP         0x00000100
+#define MSTATUS_HPP         0x00000600
+#define MSTATUS_MPP         0x00001800
+#define MSTATUS_FS          0x00006000
+#define MSTATUS_XS          0x00018000
+#define MSTATUS_MPRV        0x00020000
+#define MSTATUS_PUM         0x00040000 /* until: priv-1.9.1 */
+#define MSTATUS_SUM         0x00040000 /* since: priv-1.10 */
+#define MSTATUS_MXR         0x00080000
+#define MSTATUS_VM          0x1F000000 /* until: priv-1.9.1 */
+#define MSTATUS_TVM         0x00100000 /* since: priv-1.10 */
+#define MSTATUS_TW          0x20000000 /* since: priv-1.10 */
+#define MSTATUS_TSR         0x40000000 /* since: priv-1.10 */
+
+#define MSTATUS64_UXL       0x0000000300000000ULL
+#define MSTATUS64_SXL       0x0000000C00000000ULL
+
+#define MSTATUS32_SD        0x80000000
+#define MSTATUS64_SD        0x8000000000000000ULL
+
+#if defined(TARGET_RISCV32)
+#define MSTATUS_SD MSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define MSTATUS_SD MSTATUS64_SD
+#endif
+
+/* sstatus bits */
+#define SSTATUS_UIE         0x00000001
+#define SSTATUS_SIE         0x00000002
+#define SSTATUS_UPIE        0x00000010
+#define SSTATUS_SPIE        0x00000020
+#define SSTATUS_SPP         0x00000100
+#define SSTATUS_FS          0x00006000
+#define SSTATUS_XS          0x00018000
+#define SSTATUS_PUM         0x00040000 /* until: priv-1.9.1 */
+#define SSTATUS_SUM         0x00040000 /* since: priv-1.10 */
+#define SSTATUS_MXR         0x00080000
+
+#define SSTATUS32_SD        0x80000000
+#define SSTATUS64_SD        0x8000000000000000ULL
+
+#if defined(TARGET_RISCV32)
+#define SSTATUS_SD SSTATUS32_SD
+#elif defined(TARGET_RISCV64)
+#define SSTATUS_SD SSTATUS64_SD
+#endif
+
+/* irqs */
+#define MIP_SSIP            (1 << IRQ_S_SOFT)
+#define MIP_HSIP            (1 << IRQ_H_SOFT)
+#define MIP_MSIP            (1 << IRQ_M_SOFT)
+#define MIP_STIP            (1 << IRQ_S_TIMER)
+#define MIP_HTIP            (1 << IRQ_H_TIMER)
+#define MIP_MTIP            (1 << IRQ_M_TIMER)
+#define MIP_SEIP            (1 << IRQ_S_EXT)
+#define MIP_HEIP            (1 << IRQ_H_EXT)
+#define MIP_MEIP            (1 << IRQ_M_EXT)
+
+#define SIP_SSIP            MIP_SSIP
+#define SIP_STIP            MIP_STIP
+#define SIP_SEIP            MIP_SEIP
+
+#define PRV_U 0
+#define PRV_S 1
+#define PRV_H 2
+#define PRV_M 3
+
+/* privileged ISA 1.9.1 VM modes (mstatus.vm) */
+#define VM_1_09_MBARE 0
+#define VM_1_09_MBB   1
+#define VM_1_09_MBBID 2
+#define VM_1_09_SV32  8
+#define VM_1_09_SV39  9
+#define VM_1_09_SV48  10
+
+/* privileged ISA 1.10.0 VM modes (satp.mode) */
+#define VM_1_10_MBARE 0
+#define VM_1_10_SV32  1
+#define VM_1_10_SV39  8
+#define VM_1_10_SV48  9
+#define VM_1_10_SV57  10
+#define VM_1_10_SV64  11
+
+/* privileged ISA interrupt causes */
+#define IRQ_U_SOFT      0  /* since: priv-1.10 */
+#define IRQ_S_SOFT      1
+#define IRQ_H_SOFT      2  /* until: priv-1.9.1 */
+#define IRQ_M_SOFT      3  /* until: priv-1.9.1 */
+#define IRQ_U_TIMER     4  /* since: priv-1.10 */
+#define IRQ_S_TIMER     5
+#define IRQ_H_TIMER     6  /* until: priv-1.9.1 */
+#define IRQ_M_TIMER     7  /* until: priv-1.9.1 */
+#define IRQ_U_EXT       8  /* since: priv-1.10 */
+#define IRQ_S_EXT       9
+#define IRQ_H_EXT       10 /* until: priv-1.9.1 */
+#define IRQ_M_EXT       11 /* until: priv-1.9.1 */
+#define IRQ_X_COP       12 /* non-standard */
+
+/* Default addresses */
+#define DEFAULT_RSTVEC     0x00001000
+
+/* RV32 satp field masks */
+#define SATP32_MODE 0x80000000
+#define SATP32_ASID 0x7fc00000
+#define SATP32_PPN  0x003fffff
+
+/* RV64 satp field masks */
+#define SATP64_MODE 0xF000000000000000ULL
+#define SATP64_ASID 0x0FFFF00000000000ULL
+#define SATP64_PPN  0x00000FFFFFFFFFFFULL
+
+#if defined(TARGET_RISCV32)
+#define SATP_MODE SATP32_MODE
+#define SATP_ASID SATP32_ASID
+#define SATP_PPN  SATP32_PPN
+#endif
+#if defined(TARGET_RISCV64)
+#define SATP_MODE SATP64_MODE
+#define SATP_ASID SATP64_ASID
+#define SATP_PPN  SATP64_PPN
+#endif
+
+/* RISCV Exception Codes */
+#define EXCP_NONE                       -1 /* not a real RISCV exception code */
+#define RISCV_EXCP_INST_ADDR_MIS           0x0
+#define RISCV_EXCP_INST_ACCESS_FAULT       0x1
+#define RISCV_EXCP_ILLEGAL_INST            0x2
+#define RISCV_EXCP_BREAKPOINT              0x3
+#define RISCV_EXCP_LOAD_ADDR_MIS           0x4
+#define RISCV_EXCP_LOAD_ACCESS_FAULT       0x5
+#define RISCV_EXCP_STORE_AMO_ADDR_MIS      0x6
+#define RISCV_EXCP_STORE_AMO_ACCESS_FAULT  0x7
+#define RISCV_EXCP_U_ECALL                 0x8 /* for convenience, report all
+                                                  ECALLs as this, handler
+                                                  fixes */
+#define RISCV_EXCP_S_ECALL                 0x9
+#define RISCV_EXCP_H_ECALL                 0xa
+#define RISCV_EXCP_M_ECALL                 0xb
+#define RISCV_EXCP_INST_PAGE_FAULT         0xc /* since: priv-1.10.0 */
+#define RISCV_EXCP_LOAD_PAGE_FAULT         0xd /* since: priv-1.10.0 */
+#define RISCV_EXCP_STORE_PAGE_FAULT        0xf /* since: priv-1.10.0 */
+
+#define RISCV_EXCP_INT_FLAG                0x80000000
+#define RISCV_EXCP_INT_MASK                0x7fffffff
+
+/* page table entry (PTE) fields */
+#define PTE_V     0x001 /* Valid */
+#define PTE_R     0x002 /* Read */
+#define PTE_W     0x004 /* Write */
+#define PTE_X     0x008 /* Execute */
+#define PTE_U     0x010 /* User */
+#define PTE_G     0x020 /* Global */
+#define PTE_A     0x040 /* Accessed */
+#define PTE_D     0x080 /* Dirty */
+#define PTE_SOFT  0x300 /* Reserved for Software */
+
+#define PTE_PPN_SHIFT 10
+
+#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V)
diff --git a/target/riscv/cpu_user.h b/target/riscv/cpu_user.h
new file mode 100644 (file)
index 0000000..c219961
--- /dev/null
@@ -0,0 +1,13 @@
+#define xRA 1   /* return address (aka link register) */
+#define xSP 2   /* stack pointer */
+#define xGP 3   /* global pointer */
+#define xTP 4   /* thread pointer */
+
+#define xA0 10  /* gpr[10-17] are syscall arguments */
+#define xA1 11
+#define xA2 12
+#define xA3 13
+#define xA4 14
+#define xA5 15
+#define xA6 16
+#define xA7 17  /* syscall number goes here */
diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c
new file mode 100644 (file)
index 0000000..abbadea
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * RISC-V FPU Emulation Helpers for QEMU.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include <stdlib.h>
+#include "cpu.h"
+#include "qemu/host-utils.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+
+target_ulong cpu_riscv_get_fflags(CPURISCVState *env)
+{
+    int soft = get_float_exception_flags(&env->fp_status);
+    target_ulong hard = 0;
+
+    hard |= (soft & float_flag_inexact) ? FPEXC_NX : 0;
+    hard |= (soft & float_flag_underflow) ? FPEXC_UF : 0;
+    hard |= (soft & float_flag_overflow) ? FPEXC_OF : 0;
+    hard |= (soft & float_flag_divbyzero) ? FPEXC_DZ : 0;
+    hard |= (soft & float_flag_invalid) ? FPEXC_NV : 0;
+
+    return hard;
+}
+
+void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong hard)
+{
+    int soft = 0;
+
+    soft |= (hard & FPEXC_NX) ? float_flag_inexact : 0;
+    soft |= (hard & FPEXC_UF) ? float_flag_underflow : 0;
+    soft |= (hard & FPEXC_OF) ? float_flag_overflow : 0;
+    soft |= (hard & FPEXC_DZ) ? float_flag_divbyzero : 0;
+    soft |= (hard & FPEXC_NV) ? float_flag_invalid : 0;
+
+    set_float_exception_flags(soft, &env->fp_status);
+}
+
+void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm)
+{
+    int softrm;
+
+    if (rm == 7) {
+        rm = env->frm;
+    }
+    switch (rm) {
+    case 0:
+        softrm = float_round_nearest_even;
+        break;
+    case 1:
+        softrm = float_round_to_zero;
+        break;
+    case 2:
+        softrm = float_round_down;
+        break;
+    case 3:
+        softrm = float_round_up;
+        break;
+    case 4:
+        softrm = float_round_ties_away;
+        break;
+    default:
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    set_float_rounding_mode(softrm, &env->fp_status);
+}
+
+uint64_t helper_fmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                        uint64_t frs3)
+{
+    return float32_muladd(frs1, frs2, frs3, 0, &env->fp_status);
+}
+
+uint64_t helper_fmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                        uint64_t frs3)
+{
+    return float64_muladd(frs1, frs2, frs3, 0, &env->fp_status);
+}
+
+uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                        uint64_t frs3)
+{
+    return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c,
+                          &env->fp_status);
+}
+
+uint64_t helper_fmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                        uint64_t frs3)
+{
+    return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c,
+                          &env->fp_status);
+}
+
+uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                         uint64_t frs3)
+{
+    return float32_muladd(frs1, frs2, frs3, float_muladd_negate_product,
+                          &env->fp_status);
+}
+
+uint64_t helper_fnmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                         uint64_t frs3)
+{
+    return float64_muladd(frs1, frs2, frs3, float_muladd_negate_product,
+                          &env->fp_status);
+}
+
+uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                         uint64_t frs3)
+{
+    return float32_muladd(frs1, frs2, frs3, float_muladd_negate_c |
+                          float_muladd_negate_product, &env->fp_status);
+}
+
+uint64_t helper_fnmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2,
+                         uint64_t frs3)
+{
+    return float64_muladd(frs1, frs2, frs3, float_muladd_negate_c |
+                          float_muladd_negate_product, &env->fp_status);
+}
+
+uint64_t helper_fadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_add(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_sub(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmul_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_mul(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fdiv_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_div(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_minnum(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmax_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_maxnum(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t frs1)
+{
+    return float32_sqrt(frs1, &env->fp_status);
+}
+
+target_ulong helper_fle_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_le(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_flt_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_lt(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_feq_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float32_eq_quiet(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_fcvt_w_s(CPURISCVState *env, uint64_t frs1)
+{
+    return float32_to_int32(frs1, &env->fp_status);
+}
+
+target_ulong helper_fcvt_wu_s(CPURISCVState *env, uint64_t frs1)
+{
+    return (int32_t)float32_to_uint32(frs1, &env->fp_status);
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_l_s(CPURISCVState *env, uint64_t frs1)
+{
+    return float32_to_int64(frs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_lu_s(CPURISCVState *env, uint64_t frs1)
+{
+    return float32_to_uint64(frs1, &env->fp_status);
+}
+#endif
+
+uint64_t helper_fcvt_s_w(CPURISCVState *env, target_ulong rs1)
+{
+    return int32_to_float32((int32_t)rs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_s_wu(CPURISCVState *env, target_ulong rs1)
+{
+    return uint32_to_float32((uint32_t)rs1, &env->fp_status);
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_s_l(CPURISCVState *env, uint64_t rs1)
+{
+    return int64_to_float32(rs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_s_lu(CPURISCVState *env, uint64_t rs1)
+{
+    return uint64_to_float32(rs1, &env->fp_status);
+}
+#endif
+
+target_ulong helper_fclass_s(uint64_t frs1)
+{
+    float32 f = frs1;
+    bool sign = float32_is_neg(f);
+
+    if (float32_is_infinity(f)) {
+        return sign ? 1 << 0 : 1 << 7;
+    } else if (float32_is_zero(f)) {
+        return sign ? 1 << 3 : 1 << 4;
+    } else if (float32_is_zero_or_denormal(f)) {
+        return sign ? 1 << 2 : 1 << 5;
+    } else if (float32_is_any_nan(f)) {
+        float_status s = { }; /* for snan_bit_is_one */
+        return float32_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8;
+    } else {
+        return sign ? 1 << 1 : 1 << 6;
+    }
+}
+
+uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_add(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_sub(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmul_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_mul(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fdiv_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_div(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_minnum(frs1, frs2, &env->fp_status);
+}
+
+uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_maxnum(frs1, frs2, &env->fp_status);
+}
+
+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);
+}
+
+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);
+}
+
+uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1)
+{
+    return float64_sqrt(frs1, &env->fp_status);
+}
+
+target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_le(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_lt(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
+{
+    return float64_eq_quiet(frs1, frs2, &env->fp_status);
+}
+
+target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1)
+{
+    return float64_to_int32(frs1, &env->fp_status);
+}
+
+target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1)
+{
+    return (int32_t)float64_to_uint32(frs1, &env->fp_status);
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_l_d(CPURISCVState *env, uint64_t frs1)
+{
+    return float64_to_int64(frs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_lu_d(CPURISCVState *env, uint64_t frs1)
+{
+    return float64_to_uint64(frs1, &env->fp_status);
+}
+#endif
+
+uint64_t helper_fcvt_d_w(CPURISCVState *env, target_ulong rs1)
+{
+    return int32_to_float64((int32_t)rs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_d_wu(CPURISCVState *env, target_ulong rs1)
+{
+    return uint32_to_float64((uint32_t)rs1, &env->fp_status);
+}
+
+#if defined(TARGET_RISCV64)
+uint64_t helper_fcvt_d_l(CPURISCVState *env, uint64_t rs1)
+{
+    return int64_to_float64(rs1, &env->fp_status);
+}
+
+uint64_t helper_fcvt_d_lu(CPURISCVState *env, uint64_t rs1)
+{
+    return uint64_to_float64(rs1, &env->fp_status);
+}
+#endif
+
+target_ulong helper_fclass_d(uint64_t frs1)
+{
+    float64 f = frs1;
+    bool sign = float64_is_neg(f);
+
+    if (float64_is_infinity(f)) {
+        return sign ? 1 << 0 : 1 << 7;
+    } else if (float64_is_zero(f)) {
+        return sign ? 1 << 3 : 1 << 4;
+    } else if (float64_is_zero_or_denormal(f)) {
+        return sign ? 1 << 2 : 1 << 5;
+    } else if (float64_is_any_nan(f)) {
+        float_status s = { }; /* for snan_bit_is_one */
+        return float64_is_quiet_nan(f, &s) ? 1 << 9 : 1 << 8;
+    } else {
+        return sign ? 1 << 1 : 1 << 6;
+    }
+}
diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c
new file mode 100644 (file)
index 0000000..4f919b6
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * RISC-V GDB Server Stub
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "exec/gdbstub.h"
+#include "cpu.h"
+
+int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+
+    if (n < 32) {
+        return gdb_get_regl(mem_buf, env->gpr[n]);
+    } else if (n == 32) {
+        return gdb_get_regl(mem_buf, env->pc);
+    } else if (n < 65) {
+        return gdb_get_reg64(mem_buf, env->fpr[n - 33]);
+    } else if (n < 4096 + 65) {
+        return gdb_get_regl(mem_buf, csr_read_helper(env, n - 65));
+    }
+    return 0;
+}
+
+int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+
+    if (n == 0) {
+        /* discard writes to x0 */
+        return sizeof(target_ulong);
+    } else if (n < 32) {
+        env->gpr[n] = ldtul_p(mem_buf);
+        return sizeof(target_ulong);
+    } else if (n == 32) {
+        env->pc = ldtul_p(mem_buf);
+        return sizeof(target_ulong);
+    } else if (n < 65) {
+        env->fpr[n - 33] = ldq_p(mem_buf); /* always 64-bit */
+        return sizeof(uint64_t);
+    } else if (n < 4096 + 65) {
+        csr_write_helper(env, ldtul_p(mem_buf), n - 65);
+    }
+    return 0;
+}
diff --git a/target/riscv/helper.c b/target/riscv/helper.c
new file mode 100644 (file)
index 0000000..02cbcea
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * RISC-V emulation helpers for qemu.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "tcg-op.h"
+
+#define RISCV_DEBUG_INTERRUPT 0
+
+int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
+{
+#ifdef CONFIG_USER_ONLY
+    return 0;
+#else
+    return env->priv;
+#endif
+}
+
+#ifndef CONFIG_USER_ONLY
+/*
+ * Return RISC-V IRQ number if an interrupt should be taken, else -1.
+ * Used in cpu-exec.c
+ *
+ * Adapted from Spike's processor_t::take_interrupt()
+ */
+static int riscv_cpu_hw_interrupts_pending(CPURISCVState *env)
+{
+    target_ulong pending_interrupts = atomic_read(&env->mip) & env->mie;
+
+    target_ulong mie = get_field(env->mstatus, MSTATUS_MIE);
+    target_ulong m_enabled = env->priv < PRV_M || (env->priv == PRV_M && mie);
+    target_ulong enabled_interrupts = pending_interrupts &
+                                      ~env->mideleg & -m_enabled;
+
+    target_ulong sie = get_field(env->mstatus, MSTATUS_SIE);
+    target_ulong s_enabled = env->priv < PRV_S || (env->priv == PRV_S && sie);
+    enabled_interrupts |= pending_interrupts & env->mideleg &
+                          -s_enabled;
+
+    if (enabled_interrupts) {
+        return ctz64(enabled_interrupts); /* since non-zero */
+    } else {
+        return EXCP_NONE; /* indicates no pending interrupt */
+    }
+}
+#endif
+
+bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+#if !defined(CONFIG_USER_ONLY)
+    if (interrupt_request & CPU_INTERRUPT_HARD) {
+        RISCVCPU *cpu = RISCV_CPU(cs);
+        CPURISCVState *env = &cpu->env;
+        int interruptno = riscv_cpu_hw_interrupts_pending(env);
+        if (interruptno >= 0) {
+            cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno;
+            riscv_cpu_do_interrupt(cs);
+            return true;
+        }
+    }
+#endif
+    return false;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+/* get_physical_address - get the physical address for this virtual address
+ *
+ * Do a page table walk to obtain the physical address corresponding to a
+ * virtual address. Returns 0 if the translation was successful
+ *
+ * Adapted from Spike's mmu_t::translate and mmu_t::walk
+ *
+ */
+static int get_physical_address(CPURISCVState *env, hwaddr *physical,
+                                int *prot, target_ulong addr,
+                                int access_type, int mmu_idx)
+{
+    /* NOTE: the env->pc value visible here will not be
+     * correct, but the value visible to the exception handler
+     * (riscv_cpu_do_interrupt) is correct */
+
+    int mode = mmu_idx;
+
+    if (mode == PRV_M && access_type != MMU_INST_FETCH) {
+        if (get_field(env->mstatus, MSTATUS_MPRV)) {
+            mode = get_field(env->mstatus, MSTATUS_MPP);
+        }
+    }
+
+    if (mode == PRV_M || !riscv_feature(env, RISCV_FEATURE_MMU)) {
+        *physical = addr;
+        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+        return TRANSLATE_SUCCESS;
+    }
+
+    *prot = 0;
+
+    target_ulong base;
+    int levels, ptidxbits, ptesize, vm, sum;
+    int mxr = get_field(env->mstatus, MSTATUS_MXR);
+
+    if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+        base = get_field(env->satp, SATP_PPN) << PGSHIFT;
+        sum = get_field(env->mstatus, MSTATUS_SUM);
+        vm = get_field(env->satp, SATP_MODE);
+        switch (vm) {
+        case VM_1_10_SV32:
+          levels = 2; ptidxbits = 10; ptesize = 4; break;
+        case VM_1_10_SV39:
+          levels = 3; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_10_SV48:
+          levels = 4; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_10_SV57:
+          levels = 5; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_10_MBARE:
+            *physical = addr;
+            *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+            return TRANSLATE_SUCCESS;
+        default:
+          g_assert_not_reached();
+        }
+    } else {
+        base = env->sptbr << PGSHIFT;
+        sum = !get_field(env->mstatus, MSTATUS_PUM);
+        vm = get_field(env->mstatus, MSTATUS_VM);
+        switch (vm) {
+        case VM_1_09_SV32:
+          levels = 2; ptidxbits = 10; ptesize = 4; break;
+        case VM_1_09_SV39:
+          levels = 3; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_09_SV48:
+          levels = 4; ptidxbits = 9; ptesize = 8; break;
+        case VM_1_09_MBARE:
+            *physical = addr;
+            *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
+            return TRANSLATE_SUCCESS;
+        default:
+          g_assert_not_reached();
+        }
+    }
+
+    CPUState *cs = CPU(riscv_env_get_cpu(env));
+    int va_bits = PGSHIFT + levels * ptidxbits;
+    target_ulong mask = (1L << (TARGET_LONG_BITS - (va_bits - 1))) - 1;
+    target_ulong masked_msbs = (addr >> (va_bits - 1)) & mask;
+    if (masked_msbs != 0 && masked_msbs != mask) {
+        return TRANSLATE_FAIL;
+    }
+
+    int ptshift = (levels - 1) * ptidxbits;
+    int i;
+
+#if !TCG_OVERSIZED_GUEST
+restart:
+#endif
+    for (i = 0; i < levels; i++, ptshift -= ptidxbits) {
+        target_ulong idx = (addr >> (PGSHIFT + ptshift)) &
+                           ((1 << ptidxbits) - 1);
+
+        /* check that physical address of PTE is legal */
+        target_ulong pte_addr = base + idx * ptesize;
+#if defined(TARGET_RISCV32)
+        target_ulong pte = ldl_phys(cs->as, pte_addr);
+#elif defined(TARGET_RISCV64)
+        target_ulong pte = ldq_phys(cs->as, pte_addr);
+#endif
+        target_ulong ppn = pte >> PTE_PPN_SHIFT;
+
+        if (PTE_TABLE(pte)) { /* next level of page table */
+            base = ppn << PGSHIFT;
+        } else if ((pte & PTE_U) ? (mode == PRV_S) && !sum : !(mode == PRV_S)) {
+            break;
+        } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) {
+            break;
+        } else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) :
+                  access_type == MMU_DATA_LOAD ?  !(pte & PTE_R) &&
+                  !(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) {
+            break;
+        } else {
+            /* if necessary, set accessed and dirty bits. */
+            target_ulong updated_pte = pte | PTE_A |
+                (access_type == MMU_DATA_STORE ? PTE_D : 0);
+
+            /* Page table updates need to be atomic with MTTCG enabled */
+            if (updated_pte != pte) {
+                /* if accessed or dirty bits need updating, and the PTE is
+                 * in RAM, then we do so atomically with a compare and swap.
+                 * if the PTE is in IO space, then it can't be updated.
+                 * if the PTE changed, then we must re-walk the page table
+                   as the PTE is no longer valid */
+                MemoryRegion *mr;
+                hwaddr l = sizeof(target_ulong), addr1;
+                mr = address_space_translate(cs->as, pte_addr,
+                    &addr1, &l, false);
+                if (memory_access_is_direct(mr, true)) {
+                    target_ulong *pte_pa =
+                        qemu_map_ram_ptr(mr->ram_block, addr1);
+#if TCG_OVERSIZED_GUEST
+                    /* MTTCG is not enabled on oversized TCG guests so
+                     * page table updates do not need to be atomic */
+                    *pte_pa = pte = updated_pte;
+#else
+                    target_ulong old_pte =
+                        atomic_cmpxchg(pte_pa, pte, updated_pte);
+                    if (old_pte != pte) {
+                        goto restart;
+                    } else {
+                        pte = updated_pte;
+                    }
+#endif
+                } else {
+                    /* misconfigured PTE in ROM (AD bits are not preset) or
+                     * PTE is in IO space and can't be updated atomically */
+                    return TRANSLATE_FAIL;
+                }
+            }
+
+            /* for superpage mappings, make a fake leaf PTE for the TLB's
+               benefit. */
+            target_ulong vpn = addr >> PGSHIFT;
+            *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT;
+
+            if ((pte & PTE_R)) {
+                *prot |= PAGE_READ;
+            }
+            if ((pte & PTE_X)) {
+                *prot |= PAGE_EXEC;
+            }
+           /* only add write permission on stores or if the page
+              is already dirty, so that we don't miss further
+              page table walks to update the dirty bit */
+            if ((pte & PTE_W) &&
+                    (access_type == MMU_DATA_STORE || (pte & PTE_D))) {
+                *prot |= PAGE_WRITE;
+            }
+            return TRANSLATE_SUCCESS;
+        }
+    }
+    return TRANSLATE_FAIL;
+}
+
+static void raise_mmu_exception(CPURISCVState *env, target_ulong address,
+                                MMUAccessType access_type)
+{
+    CPUState *cs = CPU(riscv_env_get_cpu(env));
+    int page_fault_exceptions =
+        (env->priv_ver >= PRIV_VERSION_1_10_0) &&
+        get_field(env->satp, SATP_MODE) != VM_1_10_MBARE;
+    switch (access_type) {
+    case MMU_INST_FETCH:
+        cs->exception_index = page_fault_exceptions ?
+            RISCV_EXCP_INST_PAGE_FAULT : RISCV_EXCP_INST_ACCESS_FAULT;
+        break;
+    case MMU_DATA_LOAD:
+        cs->exception_index = page_fault_exceptions ?
+            RISCV_EXCP_LOAD_PAGE_FAULT : RISCV_EXCP_LOAD_ACCESS_FAULT;
+        break;
+    case MMU_DATA_STORE:
+        cs->exception_index = page_fault_exceptions ?
+            RISCV_EXCP_STORE_PAGE_FAULT : RISCV_EXCP_STORE_AMO_ACCESS_FAULT;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    env->badaddr = address;
+}
+
+hwaddr riscv_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    hwaddr phys_addr;
+    int prot;
+    int mmu_idx = cpu_mmu_index(&cpu->env, false);
+
+    if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, mmu_idx)) {
+        return -1;
+    }
+    return phys_addr;
+}
+
+void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr,
+                                   MMUAccessType access_type, int mmu_idx,
+                                   uintptr_t retaddr)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+    switch (access_type) {
+    case MMU_INST_FETCH:
+        cs->exception_index = RISCV_EXCP_INST_ADDR_MIS;
+        break;
+    case MMU_DATA_LOAD:
+        cs->exception_index = RISCV_EXCP_LOAD_ADDR_MIS;
+        break;
+    case MMU_DATA_STORE:
+        cs->exception_index = RISCV_EXCP_STORE_AMO_ADDR_MIS;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+    env->badaddr = addr;
+    do_raise_exception_err(env, cs->exception_index, retaddr);
+}
+
+/* called by qemu's softmmu to fill the qemu tlb */
+void tlb_fill(CPUState *cs, target_ulong addr, int size,
+        MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
+{
+    int ret;
+    ret = riscv_cpu_handle_mmu_fault(cs, addr, size, access_type, mmu_idx);
+    if (ret == TRANSLATE_FAIL) {
+        RISCVCPU *cpu = RISCV_CPU(cs);
+        CPURISCVState *env = &cpu->env;
+        do_raise_exception_err(env, cs->exception_index, retaddr);
+    }
+}
+
+#endif
+
+int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
+        int rw, int mmu_idx)
+{
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+#if !defined(CONFIG_USER_ONLY)
+    hwaddr pa = 0;
+    int prot;
+#endif
+    int ret = TRANSLATE_FAIL;
+
+    qemu_log_mask(CPU_LOG_MMU,
+            "%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " rw %d mmu_idx \
+             %d\n", __func__, env->pc, address, rw, mmu_idx);
+
+#if !defined(CONFIG_USER_ONLY)
+    ret = get_physical_address(env, &pa, &prot, address, rw, mmu_idx);
+    qemu_log_mask(CPU_LOG_MMU,
+            "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx
+             " prot %d\n", __func__, address, ret, pa, prot);
+    if (!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) {
+        ret = TRANSLATE_FAIL;
+    }
+    if (ret == TRANSLATE_SUCCESS) {
+        tlb_set_page(cs, address & TARGET_PAGE_MASK, pa & TARGET_PAGE_MASK,
+                     prot, mmu_idx, TARGET_PAGE_SIZE);
+    } else if (ret == TRANSLATE_FAIL) {
+        raise_mmu_exception(env, address, rw);
+    }
+#else
+    switch (rw) {
+    case MMU_INST_FETCH:
+        cs->exception_index = RISCV_EXCP_INST_PAGE_FAULT;
+        break;
+    case MMU_DATA_LOAD:
+        cs->exception_index = RISCV_EXCP_LOAD_PAGE_FAULT;
+        break;
+    case MMU_DATA_STORE:
+        cs->exception_index = RISCV_EXCP_STORE_PAGE_FAULT;
+        break;
+    }
+#endif
+    return ret;
+}
+
+/*
+ * Handle Traps
+ *
+ * Adapted from Spike's processor_t::take_trap.
+ *
+ */
+void riscv_cpu_do_interrupt(CPUState *cs)
+{
+#if !defined(CONFIG_USER_ONLY)
+
+    RISCVCPU *cpu = RISCV_CPU(cs);
+    CPURISCVState *env = &cpu->env;
+
+    if (RISCV_DEBUG_INTERRUPT) {
+        int log_cause = cs->exception_index & RISCV_EXCP_INT_MASK;
+        if (cs->exception_index & RISCV_EXCP_INT_FLAG) {
+            qemu_log_mask(LOG_TRACE, "core   0: trap %s, epc 0x" TARGET_FMT_lx,
+                riscv_intr_names[log_cause], env->pc);
+        } else {
+            qemu_log_mask(LOG_TRACE, "core   0: intr %s, epc 0x" TARGET_FMT_lx,
+                riscv_excp_names[log_cause], env->pc);
+        }
+    }
+
+    target_ulong fixed_cause = 0;
+    if (cs->exception_index & (RISCV_EXCP_INT_FLAG)) {
+        /* hacky for now. the MSB (bit 63) indicates interrupt but cs->exception
+           index is only 32 bits wide */
+        fixed_cause = cs->exception_index & RISCV_EXCP_INT_MASK;
+        fixed_cause |= ((target_ulong)1) << (TARGET_LONG_BITS - 1);
+    } else {
+        /* fixup User ECALL -> correct priv ECALL */
+        if (cs->exception_index == RISCV_EXCP_U_ECALL) {
+            switch (env->priv) {
+            case PRV_U:
+                fixed_cause = RISCV_EXCP_U_ECALL;
+                break;
+            case PRV_S:
+                fixed_cause = RISCV_EXCP_S_ECALL;
+                break;
+            case PRV_H:
+                fixed_cause = RISCV_EXCP_H_ECALL;
+                break;
+            case PRV_M:
+                fixed_cause = RISCV_EXCP_M_ECALL;
+                break;
+            }
+        } else {
+            fixed_cause = cs->exception_index;
+        }
+    }
+
+    target_ulong backup_epc = env->pc;
+
+    target_ulong bit = fixed_cause;
+    target_ulong deleg = env->medeleg;
+
+    int hasbadaddr =
+        (fixed_cause == RISCV_EXCP_INST_ADDR_MIS) ||
+        (fixed_cause == RISCV_EXCP_INST_ACCESS_FAULT) ||
+        (fixed_cause == RISCV_EXCP_LOAD_ADDR_MIS) ||
+        (fixed_cause == RISCV_EXCP_STORE_AMO_ADDR_MIS) ||
+        (fixed_cause == RISCV_EXCP_LOAD_ACCESS_FAULT) ||
+        (fixed_cause == RISCV_EXCP_STORE_AMO_ACCESS_FAULT) ||
+        (fixed_cause == RISCV_EXCP_INST_PAGE_FAULT) ||
+        (fixed_cause == RISCV_EXCP_LOAD_PAGE_FAULT) ||
+        (fixed_cause == RISCV_EXCP_STORE_PAGE_FAULT);
+
+    if (bit & ((target_ulong)1 << (TARGET_LONG_BITS - 1))) {
+        deleg = env->mideleg;
+        bit &= ~((target_ulong)1 << (TARGET_LONG_BITS - 1));
+    }
+
+    if (env->priv <= PRV_S && bit < 64 && ((deleg >> bit) & 1)) {
+        /* handle the trap in S-mode */
+        /* No need to check STVEC for misaligned - lower 2 bits cannot be set */
+        env->pc = env->stvec;
+        env->scause = fixed_cause;
+        env->sepc = backup_epc;
+
+        if (hasbadaddr) {
+            if (RISCV_DEBUG_INTERRUPT) {
+                qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld
+                    ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr);
+            }
+            env->sbadaddr = env->badaddr;
+        }
+
+        target_ulong s = env->mstatus;
+        s = set_field(s, MSTATUS_SPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ?
+            get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv));
+        s = set_field(s, MSTATUS_SPP, env->priv);
+        s = set_field(s, MSTATUS_SIE, 0);
+        csr_write_helper(env, s, CSR_MSTATUS);
+        riscv_set_mode(env, PRV_S);
+    } else {
+        /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */
+        env->pc = env->mtvec;
+        env->mepc = backup_epc;
+        env->mcause = fixed_cause;
+
+        if (hasbadaddr) {
+            if (RISCV_DEBUG_INTERRUPT) {
+                qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld
+                    ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr);
+            }
+            env->mbadaddr = env->badaddr;
+        }
+
+        target_ulong s = env->mstatus;
+        s = set_field(s, MSTATUS_MPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ?
+            get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv));
+        s = set_field(s, MSTATUS_MPP, env->priv);
+        s = set_field(s, MSTATUS_MIE, 0);
+        csr_write_helper(env, s, CSR_MSTATUS);
+        riscv_set_mode(env, PRV_M);
+    }
+    /* TODO yield load reservation  */
+#endif
+    cs->exception_index = EXCP_NONE; /* mark handled to qemu */
+}
diff --git a/target/riscv/helper.h b/target/riscv/helper.h
new file mode 100644 (file)
index 0000000..debb22a
--- /dev/null
@@ -0,0 +1,78 @@
+/* Exceptions */
+DEF_HELPER_2(raise_exception, noreturn, env, i32)
+
+/* Floating Point - rounding mode */
+DEF_HELPER_FLAGS_2(set_rounding_mode, TCG_CALL_NO_WG, void, env, i32)
+
+/* Floating Point - fused */
+DEF_HELPER_FLAGS_4(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fnmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fnmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fnmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+DEF_HELPER_FLAGS_4(fnmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64)
+
+/* Floating Point - Single Precision */
+DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_3(fle_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(flt_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(feq_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_2(fcvt_w_s, TCG_CALL_NO_RWG, tl, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_wu_s, TCG_CALL_NO_RWG, tl, env, i64)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_2(fcvt_l_s, TCG_CALL_NO_RWG, tl, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_lu_s, TCG_CALL_NO_RWG, tl, env, i64)
+#endif
+DEF_HELPER_FLAGS_2(fcvt_s_w, TCG_CALL_NO_RWG, i64, env, tl)
+DEF_HELPER_FLAGS_2(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_2(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, tl)
+DEF_HELPER_FLAGS_2(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, tl)
+#endif
+DEF_HELPER_FLAGS_1(fclass_s, TCG_CALL_NO_RWG_SE, tl, i64)
+
+/* Floating Point - Double Precision */
+DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
+DEF_HELPER_FLAGS_2(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64)
+DEF_HELPER_FLAGS_3(fle_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(flt_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_3(feq_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
+DEF_HELPER_FLAGS_2(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_2(fcvt_l_d, TCG_CALL_NO_RWG, tl, env, i64)
+DEF_HELPER_FLAGS_2(fcvt_lu_d, TCG_CALL_NO_RWG, tl, env, i64)
+#endif
+DEF_HELPER_FLAGS_2(fcvt_d_w, TCG_CALL_NO_RWG, i64, env, tl)
+DEF_HELPER_FLAGS_2(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl)
+#if defined(TARGET_RISCV64)
+DEF_HELPER_FLAGS_2(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, tl)
+DEF_HELPER_FLAGS_2(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, tl)
+#endif
+DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64)
+
+/* Special functions */
+DEF_HELPER_3(csrrw, tl, env, tl, tl)
+DEF_HELPER_4(csrrs, tl, env, tl, tl, tl)
+DEF_HELPER_4(csrrc, tl, env, tl, tl, tl)
+#ifndef CONFIG_USER_ONLY
+DEF_HELPER_2(sret, tl, env, tl)
+DEF_HELPER_2(mret, tl, env, tl)
+DEF_HELPER_1(wfi, void, env)
+DEF_HELPER_1(tlb_flush, void, env)
+#endif
diff --git a/target/riscv/instmap.h b/target/riscv/instmap.h
new file mode 100644 (file)
index 0000000..58baa1b
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * RISC-V emulation for qemu: Instruction decode helpers
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define MASK_OP_MAJOR(op)  (op & 0x7F)
+enum {
+    /* rv32i, rv64i, rv32m */
+    OPC_RISC_LUI    = (0x37),
+    OPC_RISC_AUIPC  = (0x17),
+    OPC_RISC_JAL    = (0x6F),
+    OPC_RISC_JALR   = (0x67),
+    OPC_RISC_BRANCH = (0x63),
+    OPC_RISC_LOAD   = (0x03),
+    OPC_RISC_STORE  = (0x23),
+    OPC_RISC_ARITH_IMM  = (0x13),
+    OPC_RISC_ARITH      = (0x33),
+    OPC_RISC_FENCE      = (0x0F),
+    OPC_RISC_SYSTEM     = (0x73),
+
+    /* rv64i, rv64m */
+    OPC_RISC_ARITH_IMM_W = (0x1B),
+    OPC_RISC_ARITH_W = (0x3B),
+
+    /* rv32a, rv64a */
+    OPC_RISC_ATOMIC = (0x2F),
+
+    /* floating point */
+    OPC_RISC_FP_LOAD = (0x7),
+    OPC_RISC_FP_STORE = (0x27),
+
+    OPC_RISC_FMADD = (0x43),
+    OPC_RISC_FMSUB = (0x47),
+    OPC_RISC_FNMSUB = (0x4B),
+    OPC_RISC_FNMADD = (0x4F),
+
+    OPC_RISC_FP_ARITH = (0x53),
+};
+
+#define MASK_OP_ARITH(op)   (MASK_OP_MAJOR(op) | (op & ((0x7 << 12) | \
+                            (0x7F << 25))))
+enum {
+    OPC_RISC_ADD   = OPC_RISC_ARITH | (0x0 << 12) | (0x00 << 25),
+    OPC_RISC_SUB   = OPC_RISC_ARITH | (0x0 << 12) | (0x20 << 25),
+    OPC_RISC_SLL   = OPC_RISC_ARITH | (0x1 << 12) | (0x00 << 25),
+    OPC_RISC_SLT   = OPC_RISC_ARITH | (0x2 << 12) | (0x00 << 25),
+    OPC_RISC_SLTU  = OPC_RISC_ARITH | (0x3 << 12) | (0x00 << 25),
+    OPC_RISC_XOR   = OPC_RISC_ARITH | (0x4 << 12) | (0x00 << 25),
+    OPC_RISC_SRL   = OPC_RISC_ARITH | (0x5 << 12) | (0x00 << 25),
+    OPC_RISC_SRA   = OPC_RISC_ARITH | (0x5 << 12) | (0x20 << 25),
+    OPC_RISC_OR    = OPC_RISC_ARITH | (0x6 << 12) | (0x00 << 25),
+    OPC_RISC_AND   = OPC_RISC_ARITH | (0x7 << 12) | (0x00 << 25),
+
+    /* RV64M */
+    OPC_RISC_MUL    = OPC_RISC_ARITH | (0x0 << 12) | (0x01 << 25),
+    OPC_RISC_MULH   = OPC_RISC_ARITH | (0x1 << 12) | (0x01 << 25),
+    OPC_RISC_MULHSU = OPC_RISC_ARITH | (0x2 << 12) | (0x01 << 25),
+    OPC_RISC_MULHU  = OPC_RISC_ARITH | (0x3 << 12) | (0x01 << 25),
+
+    OPC_RISC_DIV    = OPC_RISC_ARITH | (0x4 << 12) | (0x01 << 25),
+    OPC_RISC_DIVU   = OPC_RISC_ARITH | (0x5 << 12) | (0x01 << 25),
+    OPC_RISC_REM    = OPC_RISC_ARITH | (0x6 << 12) | (0x01 << 25),
+    OPC_RISC_REMU   = OPC_RISC_ARITH | (0x7 << 12) | (0x01 << 25),
+};
+
+
+#define MASK_OP_ARITH_IMM(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_ADDI   = OPC_RISC_ARITH_IMM | (0x0 << 12),
+    OPC_RISC_SLTI   = OPC_RISC_ARITH_IMM | (0x2 << 12),
+    OPC_RISC_SLTIU  = OPC_RISC_ARITH_IMM | (0x3 << 12),
+    OPC_RISC_XORI   = OPC_RISC_ARITH_IMM | (0x4 << 12),
+    OPC_RISC_ORI    = OPC_RISC_ARITH_IMM | (0x6 << 12),
+    OPC_RISC_ANDI   = OPC_RISC_ARITH_IMM | (0x7 << 12),
+    OPC_RISC_SLLI   = OPC_RISC_ARITH_IMM | (0x1 << 12), /* additional part of
+                                                           IMM */
+    OPC_RISC_SHIFT_RIGHT_I = OPC_RISC_ARITH_IMM | (0x5 << 12) /* SRAI, SRLI */
+};
+
+#define MASK_OP_BRANCH(op)     (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_BEQ  = OPC_RISC_BRANCH  | (0x0  << 12),
+    OPC_RISC_BNE  = OPC_RISC_BRANCH  | (0x1  << 12),
+    OPC_RISC_BLT  = OPC_RISC_BRANCH  | (0x4  << 12),
+    OPC_RISC_BGE  = OPC_RISC_BRANCH  | (0x5  << 12),
+    OPC_RISC_BLTU = OPC_RISC_BRANCH  | (0x6  << 12),
+    OPC_RISC_BGEU = OPC_RISC_BRANCH  | (0x7  << 12)
+};
+
+enum {
+    OPC_RISC_ADDIW   = OPC_RISC_ARITH_IMM_W | (0x0 << 12),
+    OPC_RISC_SLLIW   = OPC_RISC_ARITH_IMM_W | (0x1 << 12), /* additional part of
+                                                              IMM */
+    OPC_RISC_SHIFT_RIGHT_IW = OPC_RISC_ARITH_IMM_W | (0x5 << 12) /* SRAI, SRLI
+                                                                  */
+};
+
+enum {
+    OPC_RISC_ADDW   = OPC_RISC_ARITH_W | (0x0 << 12) | (0x00 << 25),
+    OPC_RISC_SUBW   = OPC_RISC_ARITH_W | (0x0 << 12) | (0x20 << 25),
+    OPC_RISC_SLLW   = OPC_RISC_ARITH_W | (0x1 << 12) | (0x00 << 25),
+    OPC_RISC_SRLW   = OPC_RISC_ARITH_W | (0x5 << 12) | (0x00 << 25),
+    OPC_RISC_SRAW   = OPC_RISC_ARITH_W | (0x5 << 12) | (0x20 << 25),
+
+    /* RV64M */
+    OPC_RISC_MULW   = OPC_RISC_ARITH_W | (0x0 << 12) | (0x01 << 25),
+    OPC_RISC_DIVW   = OPC_RISC_ARITH_W | (0x4 << 12) | (0x01 << 25),
+    OPC_RISC_DIVUW  = OPC_RISC_ARITH_W | (0x5 << 12) | (0x01 << 25),
+    OPC_RISC_REMW   = OPC_RISC_ARITH_W | (0x6 << 12) | (0x01 << 25),
+    OPC_RISC_REMUW  = OPC_RISC_ARITH_W | (0x7 << 12) | (0x01 << 25),
+};
+
+#define MASK_OP_LOAD(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_LB   = OPC_RISC_LOAD | (0x0 << 12),
+    OPC_RISC_LH   = OPC_RISC_LOAD | (0x1 << 12),
+    OPC_RISC_LW   = OPC_RISC_LOAD | (0x2 << 12),
+    OPC_RISC_LD   = OPC_RISC_LOAD | (0x3 << 12),
+    OPC_RISC_LBU  = OPC_RISC_LOAD | (0x4 << 12),
+    OPC_RISC_LHU  = OPC_RISC_LOAD | (0x5 << 12),
+    OPC_RISC_LWU  = OPC_RISC_LOAD | (0x6 << 12),
+};
+
+#define MASK_OP_STORE(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_SB   = OPC_RISC_STORE | (0x0 << 12),
+    OPC_RISC_SH   = OPC_RISC_STORE | (0x1 << 12),
+    OPC_RISC_SW   = OPC_RISC_STORE | (0x2 << 12),
+    OPC_RISC_SD   = OPC_RISC_STORE | (0x3 << 12),
+};
+
+#define MASK_OP_JALR(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+/* no enum since OPC_RISC_JALR is the actual value */
+
+#define MASK_OP_ATOMIC(op) \
+    (MASK_OP_MAJOR(op) | (op & ((0x7 << 12) | (0x7F << 25))))
+#define MASK_OP_ATOMIC_NO_AQ_RL_SZ(op) \
+    (MASK_OP_MAJOR(op) | (op & (0x1F << 27)))
+
+enum {
+    OPC_RISC_LR          = OPC_RISC_ATOMIC | (0x02 << 27),
+    OPC_RISC_SC          = OPC_RISC_ATOMIC | (0x03 << 27),
+    OPC_RISC_AMOSWAP     = OPC_RISC_ATOMIC | (0x01 << 27),
+    OPC_RISC_AMOADD      = OPC_RISC_ATOMIC | (0x00 << 27),
+    OPC_RISC_AMOXOR      = OPC_RISC_ATOMIC | (0x04 << 27),
+    OPC_RISC_AMOAND      = OPC_RISC_ATOMIC | (0x0C << 27),
+    OPC_RISC_AMOOR       = OPC_RISC_ATOMIC | (0x08 << 27),
+    OPC_RISC_AMOMIN      = OPC_RISC_ATOMIC | (0x10 << 27),
+    OPC_RISC_AMOMAX      = OPC_RISC_ATOMIC | (0x14 << 27),
+    OPC_RISC_AMOMINU     = OPC_RISC_ATOMIC | (0x18 << 27),
+    OPC_RISC_AMOMAXU     = OPC_RISC_ATOMIC | (0x1C << 27),
+};
+
+#define MASK_OP_SYSTEM(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_ECALL       = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_EBREAK      = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_ERET        = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_MRTS        = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_MRTH        = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_HRTS        = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_WFI         = OPC_RISC_SYSTEM | (0x0 << 12),
+    OPC_RISC_SFENCEVM    = OPC_RISC_SYSTEM | (0x0 << 12),
+
+    OPC_RISC_CSRRW       = OPC_RISC_SYSTEM | (0x1 << 12),
+    OPC_RISC_CSRRS       = OPC_RISC_SYSTEM | (0x2 << 12),
+    OPC_RISC_CSRRC       = OPC_RISC_SYSTEM | (0x3 << 12),
+    OPC_RISC_CSRRWI      = OPC_RISC_SYSTEM | (0x5 << 12),
+    OPC_RISC_CSRRSI      = OPC_RISC_SYSTEM | (0x6 << 12),
+    OPC_RISC_CSRRCI      = OPC_RISC_SYSTEM | (0x7 << 12),
+};
+
+#define MASK_OP_FP_LOAD(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_FLW   = OPC_RISC_FP_LOAD | (0x2 << 12),
+    OPC_RISC_FLD   = OPC_RISC_FP_LOAD | (0x3 << 12),
+};
+
+#define MASK_OP_FP_STORE(op)   (MASK_OP_MAJOR(op) | (op & (0x7 << 12)))
+enum {
+    OPC_RISC_FSW   = OPC_RISC_FP_STORE | (0x2 << 12),
+    OPC_RISC_FSD   = OPC_RISC_FP_STORE | (0x3 << 12),
+};
+
+#define MASK_OP_FP_FMADD(op)   (MASK_OP_MAJOR(op) | (op & (0x3 << 25)))
+enum {
+    OPC_RISC_FMADD_S = OPC_RISC_FMADD | (0x0 << 25),
+    OPC_RISC_FMADD_D = OPC_RISC_FMADD | (0x1 << 25),
+};
+
+#define MASK_OP_FP_FMSUB(op)   (MASK_OP_MAJOR(op) | (op & (0x3 << 25)))
+enum {
+    OPC_RISC_FMSUB_S = OPC_RISC_FMSUB | (0x0 << 25),
+    OPC_RISC_FMSUB_D = OPC_RISC_FMSUB | (0x1 << 25),
+};
+
+#define MASK_OP_FP_FNMADD(op)   (MASK_OP_MAJOR(op) | (op & (0x3 << 25)))
+enum {
+    OPC_RISC_FNMADD_S = OPC_RISC_FNMADD | (0x0 << 25),
+    OPC_RISC_FNMADD_D = OPC_RISC_FNMADD | (0x1 << 25),
+};
+
+#define MASK_OP_FP_FNMSUB(op)   (MASK_OP_MAJOR(op) | (op & (0x3 << 25)))
+enum {
+    OPC_RISC_FNMSUB_S = OPC_RISC_FNMSUB | (0x0 << 25),
+    OPC_RISC_FNMSUB_D = OPC_RISC_FNMSUB | (0x1 << 25),
+};
+
+#define MASK_OP_FP_ARITH(op)   (MASK_OP_MAJOR(op) | (op & (0x7F << 25)))
+enum {
+    /* float */
+    OPC_RISC_FADD_S    = OPC_RISC_FP_ARITH | (0x0 << 25),
+    OPC_RISC_FSUB_S    = OPC_RISC_FP_ARITH | (0x4 << 25),
+    OPC_RISC_FMUL_S    = OPC_RISC_FP_ARITH | (0x8 << 25),
+    OPC_RISC_FDIV_S    = OPC_RISC_FP_ARITH | (0xC << 25),
+
+    OPC_RISC_FSGNJ_S   = OPC_RISC_FP_ARITH | (0x10 << 25),
+    OPC_RISC_FSGNJN_S  = OPC_RISC_FP_ARITH | (0x10 << 25),
+    OPC_RISC_FSGNJX_S  = OPC_RISC_FP_ARITH | (0x10 << 25),
+
+    OPC_RISC_FMIN_S    = OPC_RISC_FP_ARITH | (0x14 << 25),
+    OPC_RISC_FMAX_S    = OPC_RISC_FP_ARITH | (0x14 << 25),
+
+    OPC_RISC_FSQRT_S   = OPC_RISC_FP_ARITH | (0x2C << 25),
+
+    OPC_RISC_FEQ_S     = OPC_RISC_FP_ARITH | (0x50 << 25),
+    OPC_RISC_FLT_S     = OPC_RISC_FP_ARITH | (0x50 << 25),
+    OPC_RISC_FLE_S     = OPC_RISC_FP_ARITH | (0x50 << 25),
+
+    OPC_RISC_FCVT_W_S  = OPC_RISC_FP_ARITH | (0x60 << 25),
+    OPC_RISC_FCVT_WU_S = OPC_RISC_FP_ARITH | (0x60 << 25),
+    OPC_RISC_FCVT_L_S  = OPC_RISC_FP_ARITH | (0x60 << 25),
+    OPC_RISC_FCVT_LU_S = OPC_RISC_FP_ARITH | (0x60 << 25),
+
+    OPC_RISC_FCVT_S_W  = OPC_RISC_FP_ARITH | (0x68 << 25),
+    OPC_RISC_FCVT_S_WU = OPC_RISC_FP_ARITH | (0x68 << 25),
+    OPC_RISC_FCVT_S_L  = OPC_RISC_FP_ARITH | (0x68 << 25),
+    OPC_RISC_FCVT_S_LU = OPC_RISC_FP_ARITH | (0x68 << 25),
+
+    OPC_RISC_FMV_X_S   = OPC_RISC_FP_ARITH | (0x70 << 25),
+    OPC_RISC_FCLASS_S  = OPC_RISC_FP_ARITH | (0x70 << 25),
+
+    OPC_RISC_FMV_S_X   = OPC_RISC_FP_ARITH | (0x78 << 25),
+
+    /* double */
+    OPC_RISC_FADD_D    = OPC_RISC_FP_ARITH | (0x1 << 25),
+    OPC_RISC_FSUB_D    = OPC_RISC_FP_ARITH | (0x5 << 25),
+    OPC_RISC_FMUL_D    = OPC_RISC_FP_ARITH | (0x9 << 25),
+    OPC_RISC_FDIV_D    = OPC_RISC_FP_ARITH | (0xD << 25),
+
+    OPC_RISC_FSGNJ_D   = OPC_RISC_FP_ARITH | (0x11 << 25),
+    OPC_RISC_FSGNJN_D  = OPC_RISC_FP_ARITH | (0x11 << 25),
+    OPC_RISC_FSGNJX_D  = OPC_RISC_FP_ARITH | (0x11 << 25),
+
+    OPC_RISC_FMIN_D    = OPC_RISC_FP_ARITH | (0x15 << 25),
+    OPC_RISC_FMAX_D    = OPC_RISC_FP_ARITH | (0x15 << 25),
+
+    OPC_RISC_FCVT_S_D = OPC_RISC_FP_ARITH | (0x20 << 25),
+
+    OPC_RISC_FCVT_D_S = OPC_RISC_FP_ARITH | (0x21 << 25),
+
+    OPC_RISC_FSQRT_D   = OPC_RISC_FP_ARITH | (0x2D << 25),
+
+    OPC_RISC_FEQ_D     = OPC_RISC_FP_ARITH | (0x51 << 25),
+    OPC_RISC_FLT_D     = OPC_RISC_FP_ARITH | (0x51 << 25),
+    OPC_RISC_FLE_D     = OPC_RISC_FP_ARITH | (0x51 << 25),
+
+    OPC_RISC_FCVT_W_D  = OPC_RISC_FP_ARITH | (0x61 << 25),
+    OPC_RISC_FCVT_WU_D = OPC_RISC_FP_ARITH | (0x61 << 25),
+    OPC_RISC_FCVT_L_D  = OPC_RISC_FP_ARITH | (0x61 << 25),
+    OPC_RISC_FCVT_LU_D = OPC_RISC_FP_ARITH | (0x61 << 25),
+
+    OPC_RISC_FCVT_D_W  = OPC_RISC_FP_ARITH | (0x69 << 25),
+    OPC_RISC_FCVT_D_WU = OPC_RISC_FP_ARITH | (0x69 << 25),
+    OPC_RISC_FCVT_D_L  = OPC_RISC_FP_ARITH | (0x69 << 25),
+    OPC_RISC_FCVT_D_LU = OPC_RISC_FP_ARITH | (0x69 << 25),
+
+    OPC_RISC_FMV_X_D   = OPC_RISC_FP_ARITH | (0x71 << 25),
+    OPC_RISC_FCLASS_D  = OPC_RISC_FP_ARITH | (0x71 << 25),
+
+    OPC_RISC_FMV_D_X   = OPC_RISC_FP_ARITH | (0x79 << 25),
+};
+
+#define GET_B_IMM(inst) ((extract32(inst, 8, 4) << 1) \
+                         | (extract32(inst, 25, 6) << 5) \
+                         | (extract32(inst, 7, 1) << 11) \
+                         | (sextract64(inst, 31, 1) << 12))
+
+#define GET_STORE_IMM(inst) ((extract32(inst, 7, 5)) \
+                             | (sextract64(inst, 25, 7) << 5))
+
+#define GET_JAL_IMM(inst) ((extract32(inst, 21, 10) << 1) \
+                           | (extract32(inst, 20, 1) << 11) \
+                           | (extract32(inst, 12, 8) << 12) \
+                           | (sextract64(inst, 31, 1) << 20))
+
+#define GET_RM(inst)   extract32(inst, 12, 3)
+#define GET_RS3(inst)  extract32(inst, 27, 5)
+#define GET_RS1(inst)  extract32(inst, 15, 5)
+#define GET_RS2(inst)  extract32(inst, 20, 5)
+#define GET_RD(inst)   extract32(inst, 7, 5)
+#define GET_IMM(inst)  sextract64(inst, 20, 12)
+
+/* RVC decoding macros */
+#define GET_C_IMM(inst)             (extract32(inst, 2, 5) \
+                                    | (sextract64(inst, 12, 1) << 5))
+#define GET_C_ZIMM(inst)            (extract32(inst, 2, 5) \
+                                    | (extract32(inst, 12, 1) << 5))
+#define GET_C_ADDI4SPN_IMM(inst)    ((extract32(inst, 6, 1) << 2) \
+                                    | (extract32(inst, 5, 1) << 3) \
+                                    | (extract32(inst, 11, 2) << 4) \
+                                    | (extract32(inst, 7, 4) << 6))
+#define GET_C_ADDI16SP_IMM(inst)    ((extract32(inst, 6, 1) << 4) \
+                                    | (extract32(inst, 2, 1) << 5) \
+                                    | (extract32(inst, 5, 1) << 6) \
+                                    | (extract32(inst, 3, 2) << 7) \
+                                    | (sextract64(inst, 12, 1) << 9))
+#define GET_C_LWSP_IMM(inst)        ((extract32(inst, 4, 3) << 2) \
+                                    | (extract32(inst, 12, 1) << 5) \
+                                    | (extract32(inst, 2, 2) << 6))
+#define GET_C_LDSP_IMM(inst)        ((extract32(inst, 5, 2) << 3) \
+                                    | (extract32(inst, 12, 1) << 5) \
+                                    | (extract32(inst, 2, 3) << 6))
+#define GET_C_SWSP_IMM(inst)        ((extract32(inst, 9, 4) << 2) \
+                                    | (extract32(inst, 7, 2) << 6))
+#define GET_C_SDSP_IMM(inst)        ((extract32(inst, 10, 3) << 3) \
+                                    | (extract32(inst, 7, 3) << 6))
+#define GET_C_LW_IMM(inst)          ((extract32(inst, 6, 1) << 2) \
+                                    | (extract32(inst, 10, 3) << 3) \
+                                    | (extract32(inst, 5, 1) << 6))
+#define GET_C_LD_IMM(inst)          ((extract32(inst, 10, 3) << 3) \
+                                    | (extract32(inst, 5, 2) << 6))
+#define GET_C_J_IMM(inst)           ((extract32(inst, 3, 3) << 1) \
+                                    | (extract32(inst, 11, 1) << 4) \
+                                    | (extract32(inst, 2, 1) << 5) \
+                                    | (extract32(inst, 7, 1) << 6) \
+                                    | (extract32(inst, 6, 1) << 7) \
+                                    | (extract32(inst, 9, 2) << 8) \
+                                    | (extract32(inst, 8, 1) << 10) \
+                                    | (sextract64(inst, 12, 1) << 11))
+#define GET_C_B_IMM(inst)           ((extract32(inst, 3, 2) << 1) \
+                                    | (extract32(inst, 10, 2) << 3) \
+                                    | (extract32(inst, 2, 1) << 5) \
+                                    | (extract32(inst, 5, 2) << 6) \
+                                    | (sextract64(inst, 12, 1) << 8))
+#define GET_C_SIMM3(inst)           extract32(inst, 10, 3)
+#define GET_C_RD(inst)              GET_RD(inst)
+#define GET_C_RS1(inst)             GET_RD(inst)
+#define GET_C_RS2(inst)             extract32(inst, 2, 5)
+#define GET_C_RS1S(inst)            (8 + extract32(inst, 7, 3))
+#define GET_C_RS2S(inst)            (8 + extract32(inst, 2, 3))
diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c
new file mode 100644 (file)
index 0000000..e34715d
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * RISC-V Emulation Helpers for QEMU.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ * Copyright (c) 2017-2018 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "qemu/main-loop.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+
+#ifndef CONFIG_USER_ONLY
+
+#if defined(TARGET_RISCV32)
+static const char valid_vm_1_09[16] = {
+    [VM_1_09_MBARE] = 1,
+    [VM_1_09_SV32] = 1,
+};
+static const char valid_vm_1_10[16] = {
+    [VM_1_10_MBARE] = 1,
+    [VM_1_10_SV32] = 1
+};
+#elif defined(TARGET_RISCV64)
+static const char valid_vm_1_09[16] = {
+    [VM_1_09_MBARE] = 1,
+    [VM_1_09_SV39] = 1,
+    [VM_1_09_SV48] = 1,
+};
+static const char valid_vm_1_10[16] = {
+    [VM_1_10_MBARE] = 1,
+    [VM_1_10_SV39] = 1,
+    [VM_1_10_SV48] = 1,
+    [VM_1_10_SV57] = 1
+};
+#endif
+
+static int validate_vm(CPURISCVState *env, target_ulong vm)
+{
+    return (env->priv_ver >= PRIV_VERSION_1_10_0) ?
+        valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf];
+}
+
+#endif
+
+/* Exceptions processing helpers */
+void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env,
+                                          uint32_t exception, uintptr_t pc)
+{
+    CPUState *cs = CPU(riscv_env_get_cpu(env));
+    qemu_log_mask(CPU_LOG_INT, "%s: %d\n", __func__, exception);
+    cs->exception_index = exception;
+    cpu_loop_exit_restore(cs, pc);
+}
+
+void helper_raise_exception(CPURISCVState *env, uint32_t exception)
+{
+    do_raise_exception_err(env, exception, 0);
+}
+
+static void validate_mstatus_fs(CPURISCVState *env, uintptr_t ra)
+{
+#ifndef CONFIG_USER_ONLY
+    if (!(env->mstatus & MSTATUS_FS)) {
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra);
+    }
+#endif
+}
+
+/*
+ * Handle writes to CSRs and any resulting special behavior
+ *
+ * Adapted from Spike's processor_t::set_csr
+ */
+void csr_write_helper(CPURISCVState *env, target_ulong val_to_write,
+        target_ulong csrno)
+{
+#ifndef CONFIG_USER_ONLY
+    uint64_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP | (1 << IRQ_X_COP);
+    uint64_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP;
+#endif
+
+    switch (csrno) {
+    case CSR_FFLAGS:
+        validate_mstatus_fs(env, GETPC());
+        cpu_riscv_set_fflags(env, val_to_write & (FSR_AEXC >> FSR_AEXC_SHIFT));
+        break;
+    case CSR_FRM:
+        validate_mstatus_fs(env, GETPC());
+        env->frm = val_to_write & (FSR_RD >> FSR_RD_SHIFT);
+        break;
+    case CSR_FCSR:
+        validate_mstatus_fs(env, GETPC());
+        env->frm = (val_to_write & FSR_RD) >> FSR_RD_SHIFT;
+        cpu_riscv_set_fflags(env, (val_to_write & FSR_AEXC) >> FSR_AEXC_SHIFT);
+        break;
+#ifndef CONFIG_USER_ONLY
+    case CSR_MSTATUS: {
+        target_ulong mstatus = env->mstatus;
+        target_ulong mask = 0;
+        target_ulong mpp = get_field(val_to_write, MSTATUS_MPP);
+
+        /* flush tlb on mstatus fields that affect VM */
+        if (env->priv_ver <= PRIV_VERSION_1_09_1) {
+            if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP |
+                    MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) {
+                helper_tlb_flush(env);
+            }
+            mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
+                MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
+                MSTATUS_MPP | MSTATUS_MXR |
+                (validate_vm(env, get_field(val_to_write, MSTATUS_VM)) ?
+                    MSTATUS_VM : 0);
+        }
+        if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+            if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP |
+                    MSTATUS_MPRV | MSTATUS_SUM)) {
+                helper_tlb_flush(env);
+            }
+            mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE |
+                MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM |
+                MSTATUS_MPP | MSTATUS_MXR;
+        }
+
+        /* silenty discard mstatus.mpp writes for unsupported modes */
+        if (mpp == PRV_H ||
+            (!riscv_has_ext(env, RVS) && mpp == PRV_S) ||
+            (!riscv_has_ext(env, RVU) && mpp == PRV_U)) {
+            mask &= ~MSTATUS_MPP;
+        }
+
+        mstatus = (mstatus & ~mask) | (val_to_write & mask);
+        int dirty = (mstatus & MSTATUS_FS) == MSTATUS_FS;
+        dirty |= (mstatus & MSTATUS_XS) == MSTATUS_XS;
+        mstatus = set_field(mstatus, MSTATUS_SD, dirty);
+        env->mstatus = mstatus;
+        break;
+    }
+    case CSR_MIP: {
+        /*
+         * Since the writeable bits in MIP are not set asynchrously by the
+         * CLINT, no additional locking is needed for read-modifiy-write
+         * CSR operations
+         */
+        qemu_mutex_lock_iothread();
+        RISCVCPU *cpu = riscv_env_get_cpu(env);
+        riscv_set_local_interrupt(cpu, MIP_SSIP,
+                                  (val_to_write & MIP_SSIP) != 0);
+        riscv_set_local_interrupt(cpu, MIP_STIP,
+                                  (val_to_write & MIP_STIP) != 0);
+        /*
+         * csrs, csrc on mip.SEIP is not decomposable into separate read and
+         * write steps, so a different implementation is needed
+         */
+        qemu_mutex_unlock_iothread();
+        break;
+    }
+    case CSR_MIE: {
+        env->mie = (env->mie & ~all_ints) |
+            (val_to_write & all_ints);
+        break;
+    }
+    case CSR_MIDELEG:
+        env->mideleg = (env->mideleg & ~delegable_ints)
+                                | (val_to_write & delegable_ints);
+        break;
+    case CSR_MEDELEG: {
+        target_ulong mask = 0;
+        mask |= 1ULL << (RISCV_EXCP_INST_ADDR_MIS);
+        mask |= 1ULL << (RISCV_EXCP_INST_ACCESS_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_ILLEGAL_INST);
+        mask |= 1ULL << (RISCV_EXCP_BREAKPOINT);
+        mask |= 1ULL << (RISCV_EXCP_LOAD_ADDR_MIS);
+        mask |= 1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS);
+        mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_U_ECALL);
+        mask |= 1ULL << (RISCV_EXCP_S_ECALL);
+        mask |= 1ULL << (RISCV_EXCP_H_ECALL);
+        mask |= 1ULL << (RISCV_EXCP_M_ECALL);
+        mask |= 1ULL << (RISCV_EXCP_INST_PAGE_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT);
+        mask |= 1ULL << (RISCV_EXCP_STORE_PAGE_FAULT);
+        env->medeleg = (env->medeleg & ~mask)
+                                | (val_to_write & mask);
+        break;
+    }
+    case CSR_MINSTRET:
+        qemu_log_mask(LOG_UNIMP, "CSR_MINSTRET: write not implemented");
+        goto do_illegal;
+    case CSR_MCYCLE:
+        qemu_log_mask(LOG_UNIMP, "CSR_MCYCLE: write not implemented");
+        goto do_illegal;
+    case CSR_MINSTRETH:
+        qemu_log_mask(LOG_UNIMP, "CSR_MINSTRETH: write not implemented");
+        goto do_illegal;
+    case CSR_MCYCLEH:
+        qemu_log_mask(LOG_UNIMP, "CSR_MCYCLEH: write not implemented");
+        goto do_illegal;
+    case CSR_MUCOUNTEREN:
+        env->mucounteren = val_to_write;
+        break;
+    case CSR_MSCOUNTEREN:
+        env->mscounteren = val_to_write;
+        break;
+    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;
+        ms = (ms & ~mask) | (val_to_write & mask);
+        csr_write_helper(env, ms, CSR_MSTATUS);
+        break;
+    }
+    case CSR_SIP: {
+        qemu_mutex_lock_iothread();
+        target_ulong next_mip = (env->mip & ~env->mideleg)
+                                | (val_to_write & env->mideleg);
+        qemu_mutex_unlock_iothread();
+        csr_write_helper(env, next_mip, CSR_MIP);
+        break;
+    }
+    case CSR_SIE: {
+        target_ulong next_mie = (env->mie & ~env->mideleg)
+                                | (val_to_write & env->mideleg);
+        csr_write_helper(env, next_mie, CSR_MIE);
+        break;
+    }
+    case CSR_SATP: /* CSR_SPTBR */ {
+        if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
+            goto do_illegal;
+        }
+        if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val_to_write ^ env->sptbr))
+        {
+            helper_tlb_flush(env);
+            env->sptbr = val_to_write & (((target_ulong)
+                1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1);
+        }
+        if (env->priv_ver >= PRIV_VERSION_1_10_0 &&
+            validate_vm(env, get_field(val_to_write, SATP_MODE)) &&
+            ((val_to_write ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN)))
+        {
+            helper_tlb_flush(env);
+            env->satp = val_to_write;
+        }
+        break;
+    }
+    case CSR_SEPC:
+        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;
+        }
+        env->stvec = val_to_write >> 2 << 2;
+        break;
+    case CSR_SCOUNTEREN:
+        env->scounteren = val_to_write;
+        break;
+    case CSR_SSCRATCH:
+        env->sscratch = val_to_write;
+        break;
+    case CSR_SCAUSE:
+        env->scause = val_to_write;
+        break;
+    case CSR_SBADADDR:
+        env->sbadaddr = val_to_write;
+        break;
+    case CSR_MEPC:
+        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;
+        }
+        env->mtvec = val_to_write >> 2 << 2;
+        break;
+    case CSR_MCOUNTEREN:
+        env->mcounteren = val_to_write;
+        break;
+    case CSR_MSCRATCH:
+        env->mscratch = val_to_write;
+        break;
+    case CSR_MCAUSE:
+        env->mcause = val_to_write;
+        break;
+    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_PMPCFG0:
+    case CSR_PMPCFG1:
+    case CSR_PMPCFG2:
+    case CSR_PMPCFG3:
+       pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val_to_write);
+       break;
+    case CSR_PMPADDR0:
+    case CSR_PMPADDR1:
+    case CSR_PMPADDR2:
+    case CSR_PMPADDR3:
+    case CSR_PMPADDR4:
+    case CSR_PMPADDR5:
+    case CSR_PMPADDR6:
+    case CSR_PMPADDR7:
+    case CSR_PMPADDR8:
+    case CSR_PMPADDR9:
+    case CSR_PMPADDR10:
+    case CSR_PMPADDR11:
+    case CSR_PMPADDR12:
+    case CSR_PMPADDR13:
+    case CSR_PMPADDR14:
+    case CSR_PMPADDR15:
+       pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val_to_write);
+       break;
+    do_illegal:
+#endif
+    default:
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+}
+
+/*
+ * Handle reads to CSRs and any resulting special behavior
+ *
+ * Adapted from Spike's processor_t::get_csr
+ */
+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;
+#else
+    target_ulong ctr_en = -1;
+#endif
+    target_ulong ctr_ok = (ctr_en >> (csrno & 31)) & 1;
+
+    if (csrno >= CSR_HPMCOUNTER3 && csrno <= CSR_HPMCOUNTER31) {
+        if (ctr_ok) {
+            return 0;
+        }
+    }
+#if defined(TARGET_RISCV32)
+    if (csrno >= CSR_HPMCOUNTER3H && csrno <= CSR_HPMCOUNTER31H) {
+        if (ctr_ok) {
+            return 0;
+        }
+    }
+#endif
+    if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) {
+        return 0;
+    }
+#if defined(TARGET_RISCV32)
+    if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) {
+        return 0;
+    }
+#endif
+    if (csrno >= CSR_MHPMEVENT3 && csrno <= CSR_MHPMEVENT31) {
+        return 0;
+    }
+
+    switch (csrno) {
+    case CSR_FFLAGS:
+        validate_mstatus_fs(env, GETPC());
+        return cpu_riscv_get_fflags(env);
+    case CSR_FRM:
+        validate_mstatus_fs(env, GETPC());
+        return env->frm;
+    case CSR_FCSR:
+        validate_mstatus_fs(env, GETPC());
+        return (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT)
+                | (env->frm << FSR_RD_SHIFT);
+    /* rdtime/rdtimeh is trapped and emulated by bbl in system mode */
+#ifdef CONFIG_USER_ONLY
+    case CSR_TIME:
+        return cpu_get_host_ticks();
+#if defined(TARGET_RISCV32)
+    case CSR_TIMEH:
+        return cpu_get_host_ticks() >> 32;
+#endif
+#endif
+    case CSR_INSTRET:
+    case CSR_CYCLE:
+        if (ctr_ok) {
+            return cpu_get_host_ticks();
+        }
+        break;
+#if defined(TARGET_RISCV32)
+    case CSR_INSTRETH:
+    case CSR_CYCLEH:
+        if (ctr_ok) {
+            return cpu_get_host_ticks() >> 32;
+        }
+        break;
+#endif
+#ifndef CONFIG_USER_ONLY
+    case CSR_MINSTRET:
+    case CSR_MCYCLE:
+        return cpu_get_host_ticks();
+    case CSR_MINSTRETH:
+    case CSR_MCYCLEH:
+#if defined(TARGET_RISCV32)
+        return cpu_get_host_ticks() >> 32;
+#endif
+        break;
+    case CSR_MUCOUNTEREN:
+        return env->mucounteren;
+    case CSR_MSCOUNTEREN:
+        return env->mscounteren;
+    case CSR_SSTATUS: {
+        target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE
+            | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS
+            | SSTATUS_SUM |  SSTATUS_SD;
+        if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+            mask |= SSTATUS_MXR;
+        }
+        return env->mstatus & mask;
+    }
+    case CSR_SIP: {
+        qemu_mutex_lock_iothread();
+        target_ulong tmp = env->mip & env->mideleg;
+        qemu_mutex_unlock_iothread();
+        return tmp;
+    }
+    case CSR_SIE:
+        return env->mie & env->mideleg;
+    case CSR_SEPC:
+        return env->sepc;
+    case CSR_SBADADDR:
+        return env->sbadaddr;
+    case CSR_STVEC:
+        return env->stvec;
+    case CSR_SCOUNTEREN:
+        return env->scounteren;
+    case CSR_SCAUSE:
+        return env->scause;
+    case CSR_SPTBR:
+        if (env->priv_ver >= PRIV_VERSION_1_10_0) {
+            return env->satp;
+        } else {
+            return env->sptbr;
+        }
+    case CSR_SSCRATCH:
+        return env->sscratch;
+    case CSR_MSTATUS:
+        return env->mstatus;
+    case CSR_MIP: {
+        qemu_mutex_lock_iothread();
+        target_ulong tmp = env->mip;
+        qemu_mutex_unlock_iothread();
+        return tmp;
+    }
+    case CSR_MIE:
+        return env->mie;
+    case CSR_MEPC:
+        return env->mepc;
+    case CSR_MSCRATCH:
+        return env->mscratch;
+    case CSR_MCAUSE:
+        return env->mcause;
+    case CSR_MBADADDR:
+        return env->mbadaddr;
+    case CSR_MISA:
+        return env->misa;
+    case CSR_MARCHID:
+        return 0; /* as spike does */
+    case CSR_MIMPID:
+        return 0; /* as spike does */
+    case CSR_MVENDORID:
+        return 0; /* as spike does */
+    case CSR_MHARTID:
+        return env->mhartid;
+    case CSR_MTVEC:
+        return env->mtvec;
+    case CSR_MCOUNTEREN:
+        return env->mcounteren;
+    case CSR_MEDELEG:
+        return env->medeleg;
+    case CSR_MIDELEG:
+        return env->mideleg;
+    case CSR_PMPCFG0:
+    case CSR_PMPCFG1:
+    case CSR_PMPCFG2:
+    case CSR_PMPCFG3:
+       return pmpcfg_csr_read(env, csrno - CSR_PMPCFG0);
+    case CSR_PMPADDR0:
+    case CSR_PMPADDR1:
+    case CSR_PMPADDR2:
+    case CSR_PMPADDR3:
+    case CSR_PMPADDR4:
+    case CSR_PMPADDR5:
+    case CSR_PMPADDR6:
+    case CSR_PMPADDR7:
+    case CSR_PMPADDR8:
+    case CSR_PMPADDR9:
+    case CSR_PMPADDR10:
+    case CSR_PMPADDR11:
+    case CSR_PMPADDR12:
+    case CSR_PMPADDR13:
+    case CSR_PMPADDR14:
+    case CSR_PMPADDR15:
+       return pmpaddr_csr_read(env, csrno - CSR_PMPADDR0);
+#endif
+    }
+    /* used by e.g. MTIME read */
+    do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+}
+
+/*
+ * Check that CSR access is allowed.
+ *
+ * Adapted from Spike's decode.h:validate_csr
+ */
+static void validate_csr(CPURISCVState *env, uint64_t which,
+                         uint64_t write, uintptr_t ra)
+{
+#ifndef CONFIG_USER_ONLY
+    unsigned csr_priv = get_field((which), 0x300);
+    unsigned csr_read_only = get_field((which), 0xC00) == 3;
+    if (((write) && csr_read_only) || (env->priv < csr_priv)) {
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra);
+    }
+#endif
+}
+
+target_ulong helper_csrrw(CPURISCVState *env, target_ulong src,
+        target_ulong csr)
+{
+    validate_csr(env, csr, 1, GETPC());
+    uint64_t csr_backup = csr_read_helper(env, csr);
+    csr_write_helper(env, src, csr);
+    return csr_backup;
+}
+
+target_ulong helper_csrrs(CPURISCVState *env, target_ulong src,
+        target_ulong csr, target_ulong rs1_pass)
+{
+    validate_csr(env, csr, rs1_pass != 0, GETPC());
+    uint64_t csr_backup = csr_read_helper(env, csr);
+    if (rs1_pass != 0) {
+        csr_write_helper(env, src | csr_backup, csr);
+    }
+    return csr_backup;
+}
+
+target_ulong helper_csrrc(CPURISCVState *env, target_ulong src,
+        target_ulong csr, target_ulong rs1_pass)
+{
+    validate_csr(env, csr, rs1_pass != 0, GETPC());
+    uint64_t csr_backup = csr_read_helper(env, csr);
+    if (rs1_pass != 0) {
+        csr_write_helper(env, (~src) & csr_backup, csr);
+    }
+    return csr_backup;
+}
+
+#ifndef CONFIG_USER_ONLY
+
+/* iothread_mutex must be held */
+void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value)
+{
+    target_ulong old_mip = cpu->env.mip;
+    cpu->env.mip = (old_mip & ~mask) | (value ? mask : 0);
+
+    if (cpu->env.mip && !old_mip) {
+        cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+    } else if (!cpu->env.mip && old_mip) {
+        cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
+    }
+}
+
+void riscv_set_mode(CPURISCVState *env, target_ulong newpriv)
+{
+    if (newpriv > PRV_M) {
+        g_assert_not_reached();
+    }
+    if (newpriv == PRV_H) {
+        newpriv = PRV_U;
+    }
+    /* tlb_flush is unnecessary as mode is contained in mmu_idx */
+    env->priv = newpriv;
+}
+
+target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
+{
+    if (!(env->priv >= PRV_S)) {
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    target_ulong retpc = env->sepc;
+    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
+        do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+    }
+
+    target_ulong mstatus = env->mstatus;
+    target_ulong prev_priv = get_field(mstatus, MSTATUS_SPP);
+    mstatus = set_field(mstatus,
+        env->priv_ver >= PRIV_VERSION_1_10_0 ?
+        MSTATUS_SIE : MSTATUS_UIE << prev_priv,
+        get_field(mstatus, MSTATUS_SPIE));
+    mstatus = set_field(mstatus, MSTATUS_SPIE, 0);
+    mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U);
+    riscv_set_mode(env, prev_priv);
+    csr_write_helper(env, mstatus, CSR_MSTATUS);
+
+    return retpc;
+}
+
+target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
+{
+    if (!(env->priv >= PRV_M)) {
+        do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
+    }
+
+    target_ulong retpc = env->mepc;
+    if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) {
+        do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC());
+    }
+
+    target_ulong mstatus = env->mstatus;
+    target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP);
+    mstatus = set_field(mstatus,
+        env->priv_ver >= PRIV_VERSION_1_10_0 ?
+        MSTATUS_MIE : MSTATUS_UIE << prev_priv,
+        get_field(mstatus, MSTATUS_MPIE));
+    mstatus = set_field(mstatus, MSTATUS_MPIE, 0);
+    mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U);
+    riscv_set_mode(env, prev_priv);
+    csr_write_helper(env, mstatus, CSR_MSTATUS);
+
+    return retpc;
+}
+
+
+void helper_wfi(CPURISCVState *env)
+{
+    CPUState *cs = CPU(riscv_env_get_cpu(env));
+
+    cs->halted = 1;
+    cs->exception_index = EXCP_HLT;
+    cpu_loop_exit(cs);
+}
+
+void helper_tlb_flush(CPURISCVState *env)
+{
+    RISCVCPU *cpu = riscv_env_get_cpu(env);
+    CPUState *cs = CPU(cpu);
+    tlb_flush(cs);
+}
+
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c
new file mode 100644 (file)
index 0000000..f432f3b
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * QEMU RISC-V PMP (Physical Memory Protection)
+ *
+ * Author: Daire McNamara, daire.mcnamara@emdalo.com
+ *         Ivan Griffin, ivan.griffin@emdalo.com
+ *
+ * This provides a RISC-V Physical Memory Protection implementation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * PMP (Physical Memory Protection) is as-of-yet unused and needs testing.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "cpu.h"
+#include "qemu-common.h"
+
+#ifndef CONFIG_USER_ONLY
+
+#define RISCV_DEBUG_PMP 0
+#define PMP_DEBUG(fmt, ...)                                                    \
+    do {                                                                       \
+        if (RISCV_DEBUG_PMP) {                                                 \
+            qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\
+        }                                                                      \
+    } while (0)
+
+static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index,
+    uint8_t val);
+static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index);
+static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index);
+
+/*
+ * Accessor method to extract address matching type 'a field' from cfg reg
+ */
+static inline uint8_t pmp_get_a_field(uint8_t cfg)
+{
+    uint8_t a = cfg >> 3;
+    return a & 0x3;
+}
+
+/*
+ * Check whether a PMP is locked or not.
+ */
+static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index)
+{
+
+    if (env->pmp_state.pmp[pmp_index].cfg_reg & PMP_LOCK) {
+        return 1;
+    }
+
+    /* Top PMP has no 'next' to check */
+    if ((pmp_index + 1u) >= MAX_RISCV_PMPS) {
+        return 0;
+    }
+
+    /* In TOR mode, need to check the lock bit of the next pmp
+     * (if there is a next)
+     */
+    const uint8_t a_field =
+        pmp_get_a_field(env->pmp_state.pmp[pmp_index + 1].cfg_reg);
+    if ((env->pmp_state.pmp[pmp_index + 1u].cfg_reg & PMP_LOCK) &&
+         (PMP_AMATCH_TOR == a_field)) {
+        return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Count the number of active rules.
+ */
+static inline uint32_t pmp_get_num_rules(CPURISCVState *env)
+{
+     return env->pmp_state.num_rules;
+}
+
+/*
+ * Accessor to get the cfg reg for a specific PMP/HART
+ */
+static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index)
+{
+    if (pmp_index < MAX_RISCV_PMPS) {
+        return env->pmp_state.pmp[pmp_index].cfg_reg;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Accessor to set the cfg reg for a specific PMP/HART
+ * Bounds checks and relevant lock bit.
+ */
+static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val)
+{
+    if (pmp_index < MAX_RISCV_PMPS) {
+        if (!pmp_is_locked(env, pmp_index)) {
+            env->pmp_state.pmp[pmp_index].cfg_reg = val;
+            pmp_update_rule(env, pmp_index);
+        } else {
+            PMP_DEBUG("ignoring write - locked");
+        }
+    } else {
+        PMP_DEBUG("ignoring write - out of bounds");
+    }
+}
+
+static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea)
+{
+    /*
+       aaaa...aaa0   8-byte NAPOT range
+       aaaa...aa01   16-byte NAPOT range
+       aaaa...a011   32-byte NAPOT range
+       ...
+       aa01...1111   2^XLEN-byte NAPOT range
+       a011...1111   2^(XLEN+1)-byte NAPOT range
+       0111...1111   2^(XLEN+2)-byte NAPOT range
+       1111...1111   Reserved
+    */
+    if (a == -1) {
+        *sa = 0u;
+        *ea = -1;
+        return;
+    } else {
+        target_ulong t1 = ctz64(~a);
+        target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 3;
+        target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1;
+        *sa = base;
+        *ea = base + range;
+    }
+}
+
+
+/* Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea'
+ *   end address values.
+ *   This function is called relatively infrequently whereas the check that
+ *   an address is within a pmp rule is called often, so optimise that one
+ */
+static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index)
+{
+    int i;
+
+    env->pmp_state.num_rules = 0;
+
+    uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg;
+    target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg;
+    target_ulong prev_addr = 0u;
+    target_ulong sa = 0u;
+    target_ulong ea = 0u;
+
+    if (pmp_index >= 1u) {
+        prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg;
+    }
+
+    switch (pmp_get_a_field(this_cfg)) {
+    case PMP_AMATCH_OFF:
+        sa = 0u;
+        ea = -1;
+        break;
+
+    case PMP_AMATCH_TOR:
+        sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
+        ea = (this_addr << 2) - 1u;
+        break;
+
+    case PMP_AMATCH_NA4:
+        sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */
+        ea = (this_addr + 4u) - 1u;
+        break;
+
+    case PMP_AMATCH_NAPOT:
+        pmp_decode_napot(this_addr, &sa, &ea);
+        break;
+
+    default:
+        sa = 0u;
+        ea = 0u;
+        break;
+    }
+
+    env->pmp_state.addr[pmp_index].sa = sa;
+    env->pmp_state.addr[pmp_index].ea = ea;
+
+    for (i = 0; i < MAX_RISCV_PMPS; i++) {
+        const uint8_t a_field =
+            pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
+        if (PMP_AMATCH_OFF != a_field) {
+            env->pmp_state.num_rules++;
+        }
+    }
+}
+
+static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr)
+{
+    int result = 0;
+
+    if ((addr >= env->pmp_state.addr[pmp_index].sa)
+        && (addr <= env->pmp_state.addr[pmp_index].ea)) {
+        result = 1;
+    } else {
+        result = 0;
+    }
+
+    return result;
+}
+
+
+/*
+ * Public Interface
+ */
+
+/*
+ * Check if the address has required RWX privs to complete desired operation
+ */
+bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
+    target_ulong size, pmp_priv_t privs)
+{
+    int i = 0;
+    int ret = -1;
+    target_ulong s = 0;
+    target_ulong e = 0;
+    pmp_priv_t allowed_privs = 0;
+
+    /* Short cut if no rules */
+    if (0 == pmp_get_num_rules(env)) {
+        return true;
+    }
+
+    /* 1.10 draft priv spec states there is an implicit order
+         from low to high */
+    for (i = 0; i < MAX_RISCV_PMPS; i++) {
+        s = pmp_is_in_range(env, i, addr);
+        e = pmp_is_in_range(env, i, addr + size);
+
+        /* partially inside */
+        if ((s + e) == 1) {
+            PMP_DEBUG("pmp violation - access is partially inside");
+            ret = 0;
+            break;
+        }
+
+        /* fully inside */
+        const uint8_t a_field =
+            pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg);
+        if ((s + e) == 2) {
+            if (PMP_AMATCH_OFF == a_field) {
+                return 1;
+            }
+
+            allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC;
+            if ((env->priv != PRV_M) || pmp_is_locked(env, i)) {
+                allowed_privs &= env->pmp_state.pmp[i].cfg_reg;
+            }
+
+            if ((privs & allowed_privs) == privs) {
+                ret = 1;
+                break;
+            } else {
+                ret = 0;
+                break;
+            }
+        }
+    }
+
+    /* No rule matched */
+    if (ret == -1) {
+        if (env->priv == PRV_M) {
+            ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an
+                      * M-Mode access, the access succeeds */
+        } else {
+            ret = 0; /* Other modes are not allowed to succeed if they don't
+                      * match a rule, but there are rules.  We've checked for
+                      * no rule earlier in this function. */
+        }
+    }
+
+    return ret == 1 ? true : false;
+}
+
+
+/*
+ * Handle a write to a pmpcfg CSP
+ */
+void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index,
+    target_ulong val)
+{
+    int i;
+    uint8_t cfg_val;
+
+    PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx,
+        env->mhartid, reg_index, val);
+
+    if ((reg_index & 1) && (sizeof(target_ulong) == 8)) {
+        PMP_DEBUG("ignoring write - incorrect address");
+        return;
+    }
+
+    for (i = 0; i < sizeof(target_ulong); i++) {
+        cfg_val = (val >> 8 * i)  & 0xff;
+        pmp_write_cfg(env, (reg_index * sizeof(target_ulong)) + i,
+            cfg_val);
+    }
+}
+
+
+/*
+ * Handle a read from a pmpcfg CSP
+ */
+target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index)
+{
+    int i;
+    target_ulong cfg_val = 0;
+    uint8_t val = 0;
+
+    for (i = 0; i < sizeof(target_ulong); i++) {
+        val = pmp_read_cfg(env, (reg_index * sizeof(target_ulong)) + i);
+        cfg_val |= (val << (i * 8));
+    }
+
+    PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx,
+        env->mhartid, reg_index, cfg_val);
+
+    return cfg_val;
+}
+
+
+/*
+ * Handle a write to a pmpaddr CSP
+ */
+void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
+    target_ulong val)
+{
+    PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx,
+        env->mhartid, addr_index, val);
+
+    if (addr_index < MAX_RISCV_PMPS) {
+        if (!pmp_is_locked(env, addr_index)) {
+            env->pmp_state.pmp[addr_index].addr_reg = val;
+            pmp_update_rule(env, addr_index);
+        } else {
+            PMP_DEBUG("ignoring write - locked");
+        }
+    } else {
+        PMP_DEBUG("ignoring write - out of bounds");
+    }
+}
+
+
+/*
+ * Handle a read from a pmpaddr CSP
+ */
+target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index)
+{
+    PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx,
+        env->mhartid, addr_index,
+        env->pmp_state.pmp[addr_index].addr_reg);
+    if (addr_index < MAX_RISCV_PMPS) {
+        return env->pmp_state.pmp[addr_index].addr_reg;
+    } else {
+        PMP_DEBUG("ignoring read - out of bounds");
+        return 0;
+    }
+}
+
+#endif
diff --git a/target/riscv/pmp.h b/target/riscv/pmp.h
new file mode 100644 (file)
index 0000000..e3953c8
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * QEMU RISC-V PMP (Physical Memory Protection)
+ *
+ * Author: Daire McNamara, daire.mcnamara@emdalo.com
+ *         Ivan Griffin, ivan.griffin@emdalo.com
+ *
+ * This provides a RISC-V Physical Memory Protection interface
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _RISCV_PMP_H_
+#define _RISCV_PMP_H_
+
+typedef enum {
+    PMP_READ  = 1 << 0,
+    PMP_WRITE = 1 << 1,
+    PMP_EXEC  = 1 << 2,
+    PMP_LOCK  = 1 << 7
+} pmp_priv_t;
+
+typedef enum {
+    PMP_AMATCH_OFF,  /* Null (off)                            */
+    PMP_AMATCH_TOR,  /* Top of Range                          */
+    PMP_AMATCH_NA4,  /* Naturally aligned four-byte region    */
+    PMP_AMATCH_NAPOT /* Naturally aligned power-of-two region */
+} pmp_am_t;
+
+typedef struct {
+    target_ulong addr_reg;
+    uint8_t  cfg_reg;
+} pmp_entry_t;
+
+typedef struct {
+    target_ulong sa;
+    target_ulong ea;
+} pmp_addr_t;
+
+typedef struct {
+    pmp_entry_t pmp[MAX_RISCV_PMPS];
+    pmp_addr_t  addr[MAX_RISCV_PMPS];
+    uint32_t num_rules;
+} pmp_table_t;
+
+void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index,
+    target_ulong val);
+target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index);
+void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index,
+    target_ulong val);
+target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index);
+bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr,
+    target_ulong size, pmp_priv_t priv);
+
+#endif
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
new file mode 100644 (file)
index 0000000..808eab7
--- /dev/null
@@ -0,0 +1,1978 @@
+/*
+ * RISC-V emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "cpu.h"
+#include "tcg-op.h"
+#include "disas/disas.h"
+#include "exec/cpu_ldst.h"
+#include "exec/exec-all.h"
+#include "exec/helper-proto.h"
+#include "exec/helper-gen.h"
+
+#include "exec/log.h"
+
+#include "instmap.h"
+
+/* global register indices */
+static TCGv cpu_gpr[32], cpu_pc;
+static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */
+static TCGv load_res;
+static TCGv load_val;
+
+#include "exec/gen-icount.h"
+
+typedef struct DisasContext {
+    struct TranslationBlock *tb;
+    target_ulong pc;
+    target_ulong next_pc;
+    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
+       to any system register, which includes CSR_FRM, so we do not have
+       to reset this known value.  */
+    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,
+    [0] = MO_SB,
+    [1] = MO_TESW,
+    [2] = MO_TESL,
+    [4] = MO_UB,
+    [5] = MO_TEUW,
+#ifdef TARGET_RISCV64
+    [3] = MO_TEQ,
+    [6] = MO_TEUL,
+#endif
+};
+
+#ifdef TARGET_RISCV64
+#define CASE_OP_32_64(X) case X: case glue(X, W)
+#else
+#define CASE_OP_32_64(X) case X
+#endif
+
+static void generate_exception(DisasContext *ctx, int excp)
+{
+    tcg_gen_movi_tl(cpu_pc, ctx->pc);
+    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;
+}
+
+static void generate_exception_mbadaddr(DisasContext *ctx, int excp)
+{
+    tcg_gen_movi_tl(cpu_pc, ctx->pc);
+    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;
+}
+
+static void gen_exception_debug(void)
+{
+    TCGv_i32 helper_tmp = tcg_const_i32(EXCP_DEBUG);
+    gen_helper_raise_exception(cpu_env, helper_tmp);
+    tcg_temp_free_i32(helper_tmp);
+}
+
+static void gen_exception_illegal(DisasContext *ctx)
+{
+    generate_exception(ctx, RISCV_EXCP_ILLEGAL_INST);
+}
+
+static void gen_exception_inst_addr_mis(DisasContext *ctx)
+{
+    generate_exception_mbadaddr(ctx, RISCV_EXCP_INST_ADDR_MIS);
+}
+
+static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest)
+{
+    if (unlikely(ctx->singlestep_enabled)) {
+        return false;
+    }
+
+#ifndef CONFIG_USER_ONLY
+    return (ctx->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
+#else
+    return true;
+#endif
+}
+
+static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+    if (use_goto_tb(ctx, 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);
+    } else {
+        tcg_gen_movi_tl(cpu_pc, dest);
+        if (ctx->singlestep_enabled) {
+            gen_exception_debug();
+        } else {
+            tcg_gen_exit_tb(0);
+        }
+    }
+}
+
+/* Wrapper for getting reg values - need to check of reg is zero since
+ * cpu_gpr[0] is not actually allocated
+ */
+static inline void gen_get_gpr(TCGv t, int reg_num)
+{
+    if (reg_num == 0) {
+        tcg_gen_movi_tl(t, 0);
+    } else {
+        tcg_gen_mov_tl(t, cpu_gpr[reg_num]);
+    }
+}
+
+/* Wrapper for setting reg values - need to check of reg is zero since
+ * cpu_gpr[0] is not actually allocated. this is more for safety purposes,
+ * since we usually avoid calling the OP_TYPE_gen function if we see a write to
+ * $zero
+ */
+static inline void gen_set_gpr(int reg_num_dst, TCGv t)
+{
+    if (reg_num_dst != 0) {
+        tcg_gen_mov_tl(cpu_gpr[reg_num_dst], t);
+    }
+}
+
+static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv rl = tcg_temp_new();
+    TCGv rh = tcg_temp_new();
+
+    tcg_gen_mulu2_tl(rl, rh, arg1, arg2);
+    /* fix up for one negative */
+    tcg_gen_sari_tl(rl, arg1, TARGET_LONG_BITS - 1);
+    tcg_gen_and_tl(rl, rl, arg2);
+    tcg_gen_sub_tl(ret, rh, rl);
+
+    tcg_temp_free(rl);
+    tcg_temp_free(rh);
+}
+
+static void gen_fsgnj(DisasContext *ctx, uint32_t rd, uint32_t rs1,
+    uint32_t rs2, int rm, uint64_t min)
+{
+    switch (rm) {
+    case 0: /* fsgnj */
+        if (rs1 == rs2) { /* FMOV */
+            tcg_gen_mov_i64(cpu_fpr[rd], cpu_fpr[rs1]);
+        } else {
+            tcg_gen_deposit_i64(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1],
+                                0, min == INT32_MIN ? 31 : 63);
+        }
+        break;
+    case 1: /* fsgnjn */
+        if (rs1 == rs2) { /* FNEG */
+            tcg_gen_xori_i64(cpu_fpr[rd], cpu_fpr[rs1], min);
+        } else {
+            TCGv_i64 t0 = tcg_temp_new_i64();
+            tcg_gen_not_i64(t0, cpu_fpr[rs2]);
+            tcg_gen_deposit_i64(cpu_fpr[rd], t0, cpu_fpr[rs1],
+                                0, min == INT32_MIN ? 31 : 63);
+            tcg_temp_free_i64(t0);
+        }
+        break;
+    case 2: /* fsgnjx */
+        if (rs1 == rs2) { /* FABS */
+            tcg_gen_andi_i64(cpu_fpr[rd], cpu_fpr[rs1], ~min);
+        } else {
+            TCGv_i64 t0 = tcg_temp_new_i64();
+            tcg_gen_andi_i64(t0, cpu_fpr[rs2], min);
+            tcg_gen_xor_i64(cpu_fpr[rd], cpu_fpr[rs1], t0);
+            tcg_temp_free_i64(t0);
+        }
+        break;
+    default:
+        gen_exception_illegal(ctx);
+    }
+}
+
+static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1,
+        int rs2)
+{
+    TCGv source1, source2, cond1, cond2, zeroreg, resultopt1;
+    source1 = tcg_temp_new();
+    source2 = tcg_temp_new();
+    gen_get_gpr(source1, rs1);
+    gen_get_gpr(source2, rs2);
+
+    switch (opc) {
+    CASE_OP_32_64(OPC_RISC_ADD):
+        tcg_gen_add_tl(source1, source1, source2);
+        break;
+    CASE_OP_32_64(OPC_RISC_SUB):
+        tcg_gen_sub_tl(source1, source1, source2);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SLLW:
+        tcg_gen_andi_tl(source2, source2, 0x1F);
+        tcg_gen_shl_tl(source1, source1, source2);
+        break;
+#endif
+    case OPC_RISC_SLL:
+        tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1);
+        tcg_gen_shl_tl(source1, source1, source2);
+        break;
+    case OPC_RISC_SLT:
+        tcg_gen_setcond_tl(TCG_COND_LT, source1, source1, source2);
+        break;
+    case OPC_RISC_SLTU:
+        tcg_gen_setcond_tl(TCG_COND_LTU, source1, source1, source2);
+        break;
+    case OPC_RISC_XOR:
+        tcg_gen_xor_tl(source1, source1, source2);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SRLW:
+        /* clear upper 32 */
+        tcg_gen_ext32u_tl(source1, source1);
+        tcg_gen_andi_tl(source2, source2, 0x1F);
+        tcg_gen_shr_tl(source1, source1, source2);
+        break;
+#endif
+    case OPC_RISC_SRL:
+        tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1);
+        tcg_gen_shr_tl(source1, source1, source2);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SRAW:
+        /* first, trick to get it to act like working on 32 bits (get rid of
+        upper 32, sign extend to fill space) */
+        tcg_gen_ext32s_tl(source1, source1);
+        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);
+        tcg_gen_sar_tl(source1, source1, source2);
+        break;
+    case OPC_RISC_OR:
+        tcg_gen_or_tl(source1, source1, source2);
+        break;
+    case OPC_RISC_AND:
+        tcg_gen_and_tl(source1, source1, source2);
+        break;
+    CASE_OP_32_64(OPC_RISC_MUL):
+        tcg_gen_mul_tl(source1, source1, source2);
+        break;
+    case OPC_RISC_MULH:
+        tcg_gen_muls2_tl(source2, source1, source1, source2);
+        break;
+    case OPC_RISC_MULHSU:
+        gen_mulhsu(source1, source1, source2);
+        break;
+    case OPC_RISC_MULHU:
+        tcg_gen_mulu2_tl(source2, source1, source1, source2);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_DIVW:
+        tcg_gen_ext32s_tl(source1, source1);
+        tcg_gen_ext32s_tl(source2, source2);
+        /* fall through to DIV */
+#endif
+    case OPC_RISC_DIV:
+        /* Handle by altering args to tcg_gen_div to produce req'd results:
+         * For overflow: want source1 in source1 and 1 in source2
+         * For div by zero: want -1 in source1 and 1 in source2 -> -1 result */
+        cond1 = tcg_temp_new();
+        cond2 = tcg_temp_new();
+        zeroreg = tcg_const_tl(0);
+        resultopt1 = tcg_temp_new();
+
+        tcg_gen_movi_tl(resultopt1, (target_ulong)-1);
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)(~0L));
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1,
+                            ((target_ulong)1) << (TARGET_LONG_BITS - 1));
+        tcg_gen_and_tl(cond1, cond1, cond2); /* cond1 = overflow */
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, 0); /* cond2 = div 0 */
+        /* if div by zero, set source1 to -1, otherwise don't change */
+        tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond2, zeroreg, source1,
+                resultopt1);
+        /* if overflow or div by zero, set source2 to 1, else don't change */
+        tcg_gen_or_tl(cond1, cond1, cond2);
+        tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+                resultopt1);
+        tcg_gen_div_tl(source1, source1, source2);
+
+        tcg_temp_free(cond1);
+        tcg_temp_free(cond2);
+        tcg_temp_free(zeroreg);
+        tcg_temp_free(resultopt1);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_DIVUW:
+        tcg_gen_ext32u_tl(source1, source1);
+        tcg_gen_ext32u_tl(source2, source2);
+        /* fall through to DIVU */
+#endif
+    case OPC_RISC_DIVU:
+        cond1 = tcg_temp_new();
+        zeroreg = tcg_const_tl(0);
+        resultopt1 = tcg_temp_new();
+
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0);
+        tcg_gen_movi_tl(resultopt1, (target_ulong)-1);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, source1,
+                resultopt1);
+        tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+                resultopt1);
+        tcg_gen_divu_tl(source1, source1, source2);
+
+        tcg_temp_free(cond1);
+        tcg_temp_free(zeroreg);
+        tcg_temp_free(resultopt1);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_REMW:
+        tcg_gen_ext32s_tl(source1, source1);
+        tcg_gen_ext32s_tl(source2, source2);
+        /* fall through to REM */
+#endif
+    case OPC_RISC_REM:
+        cond1 = tcg_temp_new();
+        cond2 = tcg_temp_new();
+        zeroreg = tcg_const_tl(0);
+        resultopt1 = tcg_temp_new();
+
+        tcg_gen_movi_tl(resultopt1, 1L);
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)-1);
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1,
+                            (target_ulong)1 << (TARGET_LONG_BITS - 1));
+        tcg_gen_and_tl(cond2, cond1, cond2); /* cond1 = overflow */
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); /* cond2 = div 0 */
+        /* if overflow or div by zero, set source2 to 1, else don't change */
+        tcg_gen_or_tl(cond2, cond1, cond2);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond2, zeroreg, source2,
+                resultopt1);
+        tcg_gen_rem_tl(resultopt1, source1, source2);
+        /* if div by zero, just return the original dividend */
+        tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1,
+                source1);
+
+        tcg_temp_free(cond1);
+        tcg_temp_free(cond2);
+        tcg_temp_free(zeroreg);
+        tcg_temp_free(resultopt1);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_REMUW:
+        tcg_gen_ext32u_tl(source1, source1);
+        tcg_gen_ext32u_tl(source2, source2);
+        /* fall through to REMU */
+#endif
+    case OPC_RISC_REMU:
+        cond1 = tcg_temp_new();
+        zeroreg = tcg_const_tl(0);
+        resultopt1 = tcg_temp_new();
+
+        tcg_gen_movi_tl(resultopt1, (target_ulong)1);
+        tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0);
+        tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2,
+                resultopt1);
+        tcg_gen_remu_tl(resultopt1, source1, source2);
+        /* if div by zero, just return the original dividend */
+        tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1,
+                source1);
+
+        tcg_temp_free(cond1);
+        tcg_temp_free(zeroreg);
+        tcg_temp_free(resultopt1);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    if (opc & 0x8) { /* sign extend for W instructions */
+        tcg_gen_ext32s_tl(source1, source1);
+    }
+
+    gen_set_gpr(rd, source1);
+    tcg_temp_free(source1);
+    tcg_temp_free(source2);
+}
+
+static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rd,
+        int rs1, target_long imm)
+{
+    TCGv source1 = tcg_temp_new();
+    int shift_len = TARGET_LONG_BITS;
+    int shift_a;
+
+    gen_get_gpr(source1, rs1);
+
+    switch (opc) {
+    case OPC_RISC_ADDI:
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_ADDIW:
+#endif
+        tcg_gen_addi_tl(source1, source1, imm);
+        break;
+    case OPC_RISC_SLTI:
+        tcg_gen_setcondi_tl(TCG_COND_LT, source1, source1, imm);
+        break;
+    case OPC_RISC_SLTIU:
+        tcg_gen_setcondi_tl(TCG_COND_LTU, source1, source1, imm);
+        break;
+    case OPC_RISC_XORI:
+        tcg_gen_xori_tl(source1, source1, imm);
+        break;
+    case OPC_RISC_ORI:
+        tcg_gen_ori_tl(source1, source1, imm);
+        break;
+    case OPC_RISC_ANDI:
+        tcg_gen_andi_tl(source1, source1, imm);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SLLIW:
+        shift_len = 32;
+        /* FALLTHRU */
+#endif
+    case OPC_RISC_SLLI:
+        if (imm >= shift_len) {
+            goto do_illegal;
+        }
+        tcg_gen_shli_tl(source1, source1, imm);
+        break;
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_SHIFT_RIGHT_IW:
+        shift_len = 32;
+        /* FALLTHRU */
+#endif
+    case OPC_RISC_SHIFT_RIGHT_I:
+        /* differentiate on IMM */
+        shift_a = imm & 0x400;
+        imm &= 0x3ff;
+        if (imm >= shift_len) {
+            goto do_illegal;
+        }
+        if (imm != 0) {
+            if (shift_a) {
+                /* SRAI[W] */
+                tcg_gen_sextract_tl(source1, source1, imm, shift_len - imm);
+            } else {
+                /* SRLI[W] */
+                tcg_gen_extract_tl(source1, source1, imm, shift_len - imm);
+            }
+            /* No further sign-extension needed for W instructions.  */
+            opc &= ~0x8;
+        }
+        break;
+    default:
+    do_illegal:
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    if (opc & 0x8) { /* sign-extend for W instructions */
+        tcg_gen_ext32s_tl(source1, source1);
+    }
+
+    gen_set_gpr(rd, source1);
+    tcg_temp_free(source1);
+}
+
+static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd,
+                    target_ulong imm)
+{
+    target_ulong next_pc;
+
+    /* check misaligned: */
+    next_pc = ctx->pc + imm;
+    if (!riscv_has_ext(env, RVC)) {
+        if ((next_pc & 0x3) != 0) {
+            gen_exception_inst_addr_mis(ctx);
+            return;
+        }
+    }
+    if (rd != 0) {
+        tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc);
+    }
+
+    gen_goto_tb(ctx, 0, ctx->pc + imm); /* must use this for safety */
+    ctx->bstate = BS_BRANCH;
+}
+
+static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
+                     int rd, int rs1, target_long imm)
+{
+    /* no chaining with JALR */
+    TCGLabel *misaligned = NULL;
+    TCGv t0 = tcg_temp_new();
+
+    switch (opc) {
+    case OPC_RISC_JALR:
+        gen_get_gpr(cpu_pc, rs1);
+        tcg_gen_addi_tl(cpu_pc, cpu_pc, imm);
+        tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2);
+
+        if (!riscv_has_ext(env, RVC)) {
+            misaligned = gen_new_label();
+            tcg_gen_andi_tl(t0, cpu_pc, 0x2);
+            tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned);
+        }
+
+        if (rd != 0) {
+            tcg_gen_movi_tl(cpu_gpr[rd], ctx->next_pc);
+        }
+        tcg_gen_exit_tb(0);
+
+        if (misaligned) {
+            gen_set_label(misaligned);
+            gen_exception_inst_addr_mis(ctx);
+        }
+        ctx->bstate = BS_BRANCH;
+        break;
+
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+    tcg_temp_free(t0);
+}
+
+static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
+                       int rs1, int rs2, target_long bimm)
+{
+    TCGLabel *l = gen_new_label();
+    TCGv source1, source2;
+    source1 = tcg_temp_new();
+    source2 = tcg_temp_new();
+    gen_get_gpr(source1, rs1);
+    gen_get_gpr(source2, rs2);
+
+    switch (opc) {
+    case OPC_RISC_BEQ:
+        tcg_gen_brcond_tl(TCG_COND_EQ, source1, source2, l);
+        break;
+    case OPC_RISC_BNE:
+        tcg_gen_brcond_tl(TCG_COND_NE, source1, source2, l);
+        break;
+    case OPC_RISC_BLT:
+        tcg_gen_brcond_tl(TCG_COND_LT, source1, source2, l);
+        break;
+    case OPC_RISC_BGE:
+        tcg_gen_brcond_tl(TCG_COND_GE, source1, source2, l);
+        break;
+    case OPC_RISC_BLTU:
+        tcg_gen_brcond_tl(TCG_COND_LTU, source1, source2, l);
+        break;
+    case OPC_RISC_BGEU:
+        tcg_gen_brcond_tl(TCG_COND_GEU, source1, source2, l);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        return;
+    }
+    tcg_temp_free(source1);
+    tcg_temp_free(source2);
+
+    gen_goto_tb(ctx, 1, ctx->next_pc);
+    gen_set_label(l); /* branch taken */
+    if (!riscv_has_ext(env, RVC) && ((ctx->pc + bimm) & 0x3)) {
+        /* misaligned */
+        gen_exception_inst_addr_mis(ctx);
+    } else {
+        gen_goto_tb(ctx, 0, ctx->pc + bimm);
+    }
+    ctx->bstate = BS_BRANCH;
+}
+
+static void gen_load(DisasContext *ctx, uint32_t opc, int rd, int rs1,
+        target_long imm)
+{
+    TCGv t0 = tcg_temp_new();
+    TCGv t1 = tcg_temp_new();
+    gen_get_gpr(t0, rs1);
+    tcg_gen_addi_tl(t0, t0, imm);
+    int memop = tcg_memop_lookup[(opc >> 12) & 0x7];
+
+    if (memop < 0) {
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, memop);
+    gen_set_gpr(rd, t1);
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+}
+
+static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2,
+        target_long imm)
+{
+    TCGv t0 = tcg_temp_new();
+    TCGv dat = tcg_temp_new();
+    gen_get_gpr(t0, rs1);
+    tcg_gen_addi_tl(t0, t0, imm);
+    gen_get_gpr(dat, rs2);
+    int memop = tcg_memop_lookup[(opc >> 12) & 0x7];
+
+    if (memop < 0) {
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx, memop);
+    tcg_temp_free(t0);
+    tcg_temp_free(dat);
+}
+
+static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd,
+        int rs1, target_long imm)
+{
+    TCGv t0;
+
+    if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    t0 = tcg_temp_new();
+    gen_get_gpr(t0, rs1);
+    tcg_gen_addi_tl(t0, t0, imm);
+
+    switch (opc) {
+    case OPC_RISC_FLW:
+        tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEUL);
+        /* RISC-V requires NaN-boxing of narrower width floating point values */
+        tcg_gen_ori_i64(cpu_fpr[rd], cpu_fpr[rd], 0xffffffff00000000ULL);
+        break;
+    case OPC_RISC_FLD:
+        tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+    tcg_temp_free(t0);
+}
+
+static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1,
+        int rs2, target_long imm)
+{
+    TCGv t0;
+
+    if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+        gen_exception_illegal(ctx);
+        return;
+    }
+
+    t0 = tcg_temp_new();
+    gen_get_gpr(t0, rs1);
+    tcg_gen_addi_tl(t0, t0, imm);
+
+    switch (opc) {
+    case OPC_RISC_FSW:
+        tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEUL);
+        break;
+    case OPC_RISC_FSD:
+        tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEQ);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+
+    tcg_temp_free(t0);
+}
+
+static void gen_atomic(DisasContext *ctx, uint32_t opc,
+                      int rd, int rs1, int rs2)
+{
+    TCGv src1, src2, dat;
+    TCGLabel *l1, *l2;
+    TCGMemOp mop;
+    TCGCond cond;
+    bool aq, rl;
+
+    /* Extract the size of the atomic operation.  */
+    switch (extract32(opc, 12, 3)) {
+    case 2: /* 32-bit */
+        mop = MO_ALIGN | MO_TESL;
+        break;
+#if defined(TARGET_RISCV64)
+    case 3: /* 64-bit */
+        mop = MO_ALIGN | MO_TEQ;
+        break;
+#endif
+    default:
+        gen_exception_illegal(ctx);
+        return;
+    }
+    rl = extract32(opc, 25, 1);
+    aq = extract32(opc, 26, 1);
+
+    src1 = tcg_temp_new();
+    src2 = tcg_temp_new();
+
+    switch (MASK_OP_ATOMIC_NO_AQ_RL_SZ(opc)) {
+    case OPC_RISC_LR:
+        /* Put addr in load_res, data in load_val.  */
+        gen_get_gpr(src1, rs1);
+        if (rl) {
+            tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL);
+        }
+        tcg_gen_qemu_ld_tl(load_val, src1, ctx->mem_idx, mop);
+        if (aq) {
+            tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ);
+        }
+        tcg_gen_mov_tl(load_res, src1);
+        gen_set_gpr(rd, load_val);
+        break;
+
+    case OPC_RISC_SC:
+        l1 = gen_new_label();
+        l2 = gen_new_label();
+        dat = tcg_temp_new();
+
+        gen_get_gpr(src1, rs1);
+        tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1);
+
+        gen_get_gpr(src2, rs2);
+        /* Note that the TCG atomic primitives are SC,
+           so we can ignore AQ/RL along this path.  */
+        tcg_gen_atomic_cmpxchg_tl(src1, load_res, load_val, src2,
+                                  ctx->mem_idx, mop);
+        tcg_gen_setcond_tl(TCG_COND_NE, dat, src1, load_val);
+        gen_set_gpr(rd, dat);
+        tcg_gen_br(l2);
+
+        gen_set_label(l1);
+        /* Address comparion failure.  However, we still need to
+           provide the memory barrier implied by AQ/RL.  */
+        tcg_gen_mb(TCG_MO_ALL + aq * TCG_BAR_LDAQ + rl * TCG_BAR_STRL);
+        tcg_gen_movi_tl(dat, 1);
+        gen_set_gpr(rd, dat);
+
+        gen_set_label(l2);
+        tcg_temp_free(dat);
+        break;
+
+    case OPC_RISC_AMOSWAP:
+        /* Note that the TCG atomic primitives are SC,
+           so we can ignore AQ/RL along this path.  */
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        tcg_gen_atomic_xchg_tl(src2, src1, src2, ctx->mem_idx, mop);
+        gen_set_gpr(rd, src2);
+        break;
+    case OPC_RISC_AMOADD:
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        tcg_gen_atomic_fetch_add_tl(src2, src1, src2, ctx->mem_idx, mop);
+        gen_set_gpr(rd, src2);
+        break;
+    case OPC_RISC_AMOXOR:
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        tcg_gen_atomic_fetch_xor_tl(src2, src1, src2, ctx->mem_idx, mop);
+        gen_set_gpr(rd, src2);
+        break;
+    case OPC_RISC_AMOAND:
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        tcg_gen_atomic_fetch_and_tl(src2, src1, src2, ctx->mem_idx, mop);
+        gen_set_gpr(rd, src2);
+        break;
+    case OPC_RISC_AMOOR:
+        gen_get_gpr(src1, rs1);
+        gen_get_gpr(src2, rs2);
+        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);
+        break;
+
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+
+    tcg_temp_free(src1);
+    tcg_temp_free(src2);
+}
+
+static void gen_set_rm(DisasContext *ctx, int rm)
+{
+    TCGv_i32 t0;
+
+    if (ctx->frm == rm) {
+        return;
+    }
+    ctx->frm = rm;
+    t0 = tcg_const_i32(rm);
+    gen_helper_set_rounding_mode(cpu_env, t0);
+    tcg_temp_free_i32(t0);
+}
+
+static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd,
+                         int rs1, int rs2, int rs3, int rm)
+{
+    switch (opc) {
+    case OPC_RISC_FMADD_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                           cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    case OPC_RISC_FMADD_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                           cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd,
+                         int rs1, int rs2, int rs3, int rm)
+{
+    switch (opc) {
+    case OPC_RISC_FMSUB_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                           cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    case OPC_RISC_FMSUB_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                           cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd,
+                          int rs1, int rs2, int rs3, int rm)
+{
+    switch (opc) {
+    case OPC_RISC_FNMSUB_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                            cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    case OPC_RISC_FNMSUB_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                            cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd,
+                          int rs1, int rs2, int rs3, int rm)
+{
+    switch (opc) {
+    case OPC_RISC_FNMADD_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                            cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    case OPC_RISC_FNMADD_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1],
+                            cpu_fpr[rs2], cpu_fpr[rs3]);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd,
+                         int rs1, int rs2, int rm)
+{
+    TCGv t0 = NULL;
+
+    if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) {
+        goto do_illegal;
+    }
+
+    switch (opc) {
+    case OPC_RISC_FADD_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FSUB_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FMUL_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FDIV_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FSQRT_S:
+        gen_set_rm(ctx, rm);
+        gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
+        break;
+    case OPC_RISC_FSGNJ_S:
+        gen_fsgnj(ctx, rd, rs1, rs2, rm, INT32_MIN);
+        break;
+
+    case OPC_RISC_FMIN_S:
+        /* also handles: OPC_RISC_FMAX_S */
+        switch (rm) {
+        case 0x0:
+            gen_helper_fmin_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 0x1:
+            gen_helper_fmax_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FEQ_S:
+        /* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */
+        t0 = tcg_temp_new();
+        switch (rm) {
+        case 0x0:
+            gen_helper_fle_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 0x1:
+            gen_helper_flt_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 0x2:
+            gen_helper_feq_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FCVT_W_S:
+        /* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */
+        t0 = tcg_temp_new();
+        switch (rs2) {
+        case 0: /* FCVT_W_S */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_w_s(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+        case 1: /* FCVT_WU_S */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_wu_s(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+#if defined(TARGET_RISCV64)
+        case 2: /* FCVT_L_S */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_l_s(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+        case 3: /* FCVT_LU_S */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_lu_s(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+#endif
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FCVT_S_W:
+        /* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */
+        t0 = tcg_temp_new();
+        gen_get_gpr(t0, rs1);
+        switch (rs2) {
+        case 0: /* FCVT_S_W */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_w(cpu_fpr[rd], cpu_env, t0);
+            break;
+        case 1: /* FCVT_S_WU */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_wu(cpu_fpr[rd], cpu_env, t0);
+            break;
+#if defined(TARGET_RISCV64)
+        case 2: /* FCVT_S_L */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_l(cpu_fpr[rd], cpu_env, t0);
+            break;
+        case 3: /* FCVT_S_LU */
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_lu(cpu_fpr[rd], cpu_env, t0);
+            break;
+#endif
+        default:
+            goto do_illegal;
+        }
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FMV_X_S:
+        /* also OPC_RISC_FCLASS_S */
+        t0 = tcg_temp_new();
+        switch (rm) {
+        case 0: /* FMV */
+#if defined(TARGET_RISCV64)
+            tcg_gen_ext32s_tl(t0, cpu_fpr[rs1]);
+#else
+            tcg_gen_extrl_i64_i32(t0, cpu_fpr[rs1]);
+#endif
+            break;
+        case 1:
+            gen_helper_fclass_s(t0, cpu_fpr[rs1]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FMV_S_X:
+        t0 = tcg_temp_new();
+        gen_get_gpr(t0, rs1);
+#if defined(TARGET_RISCV64)
+        tcg_gen_mov_i64(cpu_fpr[rd], t0);
+#else
+        tcg_gen_extu_i32_i64(cpu_fpr[rd], t0);
+#endif
+        tcg_temp_free(t0);
+        break;
+
+    /* double */
+    case OPC_RISC_FADD_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FSUB_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FMUL_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FDIV_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+        break;
+    case OPC_RISC_FSQRT_D:
+        gen_set_rm(ctx, rm);
+        gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
+        break;
+    case OPC_RISC_FSGNJ_D:
+        gen_fsgnj(ctx, rd, rs1, rs2, rm, INT64_MIN);
+        break;
+
+    case OPC_RISC_FMIN_D:
+        /* also OPC_RISC_FMAX_D */
+        switch (rm) {
+        case 0:
+            gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 1:
+            gen_helper_fmax_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FCVT_S_D:
+        switch (rs2) {
+        case 1:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_s_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FCVT_D_S:
+        switch (rs2) {
+        case 0:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FEQ_D:
+        /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */
+        t0 = tcg_temp_new();
+        switch (rm) {
+        case 0:
+            gen_helper_fle_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 1:
+            gen_helper_flt_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        case 2:
+            gen_helper_feq_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
+            break;
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FCVT_W_D:
+        /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */
+        t0 = tcg_temp_new();
+        switch (rs2) {
+        case 0:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_w_d(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+        case 1:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_wu_d(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+#if defined(TARGET_RISCV64)
+        case 2:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_l_d(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+        case 3:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_lu_d(t0, cpu_env, cpu_fpr[rs1]);
+            break;
+#endif
+        default:
+            goto do_illegal;
+        }
+        gen_set_gpr(rd, t0);
+        tcg_temp_free(t0);
+        break;
+
+    case OPC_RISC_FCVT_D_W:
+        /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */
+        t0 = tcg_temp_new();
+        gen_get_gpr(t0, rs1);
+        switch (rs2) {
+        case 0:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_w(cpu_fpr[rd], cpu_env, t0);
+            break;
+        case 1:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_wu(cpu_fpr[rd], cpu_env, t0);
+            break;
+#if defined(TARGET_RISCV64)
+        case 2:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_l(cpu_fpr[rd], cpu_env, t0);
+            break;
+        case 3:
+            gen_set_rm(ctx, rm);
+            gen_helper_fcvt_d_lu(cpu_fpr[rd], cpu_env, t0);
+            break;
+#endif
+        default:
+            goto do_illegal;
+        }
+        tcg_temp_free(t0);
+        break;
+
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_FMV_X_D:
+        /* also OPC_RISC_FCLASS_D */
+        switch (rm) {
+        case 0: /* FMV */
+            gen_set_gpr(rd, cpu_fpr[rs1]);
+            break;
+        case 1:
+            t0 = tcg_temp_new();
+            gen_helper_fclass_d(t0, cpu_fpr[rs1]);
+            gen_set_gpr(rd, t0);
+            tcg_temp_free(t0);
+            break;
+        default:
+            goto do_illegal;
+        }
+        break;
+
+    case OPC_RISC_FMV_D_X:
+        t0 = tcg_temp_new();
+        gen_get_gpr(t0, rs1);
+        tcg_gen_mov_tl(cpu_fpr[rd], t0);
+        tcg_temp_free(t0);
+        break;
+#endif
+
+    default:
+    do_illegal:
+        if (t0) {
+            tcg_temp_free(t0);
+        }
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc,
+                      int rd, int rs1, int csr)
+{
+    TCGv source1, csr_store, dest, rs1_pass, imm_rs1;
+    source1 = tcg_temp_new();
+    csr_store = tcg_temp_new();
+    dest = tcg_temp_new();
+    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(rs1_pass, rs1);
+    tcg_gen_movi_tl(csr_store, csr); /* copy into temp reg to feed to helper */
+
+#ifndef CONFIG_USER_ONLY
+    /* Extract funct7 value and check whether it matches SFENCE.VMA */
+    if ((opc == OPC_RISC_ECALL) && ((csr >> 5) == 9)) {
+        /* sfence.vma */
+        /* TODO: handle ASID specific fences */
+        gen_helper_tlb_flush(cpu_env);
+        return;
+    }
+#endif
+
+    switch (opc) {
+    case OPC_RISC_ECALL:
+        switch (csr) {
+        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;
+            break;
+        case 0x1: /* EBREAK */
+            generate_exception(ctx, RISCV_EXCP_BREAKPOINT);
+            tcg_gen_exit_tb(0); /* no chaining */
+            ctx->bstate = BS_BRANCH;
+            break;
+#ifndef CONFIG_USER_ONLY
+        case 0x002: /* URET */
+            gen_exception_illegal(ctx);
+            break;
+        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;
+            } else {
+                gen_exception_illegal(ctx);
+            }
+            break;
+        case 0x202: /* HRET */
+            gen_exception_illegal(ctx);
+            break;
+        case 0x302: /* MRET */
+            gen_helper_mret(cpu_pc, cpu_env, cpu_pc);
+            tcg_gen_exit_tb(0); /* no chaining */
+            ctx->bstate = BS_BRANCH;
+            break;
+        case 0x7b2: /* DRET */
+            gen_exception_illegal(ctx);
+            break;
+        case 0x105: /* WFI */
+            tcg_gen_movi_tl(cpu_pc, ctx->next_pc);
+            gen_helper_wfi(cpu_env);
+            break;
+        case 0x104: /* SFENCE.VM */
+            gen_helper_tlb_flush(cpu_env);
+            break;
+#endif
+        default:
+            gen_exception_illegal(ctx);
+            break;
+        }
+        break;
+    default:
+        tcg_gen_movi_tl(imm_rs1, rs1);
+        switch (opc) {
+        case OPC_RISC_CSRRW:
+            gen_helper_csrrw(dest, cpu_env, source1, csr_store);
+            break;
+        case OPC_RISC_CSRRS:
+            gen_helper_csrrs(dest, cpu_env, source1, csr_store, rs1_pass);
+            break;
+        case OPC_RISC_CSRRC:
+            gen_helper_csrrc(dest, cpu_env, source1, csr_store, rs1_pass);
+            break;
+        case OPC_RISC_CSRRWI:
+            gen_helper_csrrw(dest, cpu_env, imm_rs1, csr_store);
+            break;
+        case OPC_RISC_CSRRSI:
+            gen_helper_csrrs(dest, cpu_env, imm_rs1, csr_store, rs1_pass);
+            break;
+        case OPC_RISC_CSRRCI:
+            gen_helper_csrrc(dest, cpu_env, imm_rs1, csr_store, rs1_pass);
+            break;
+        default:
+            gen_exception_illegal(ctx);
+            return;
+        }
+        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;
+        break;
+    }
+    tcg_temp_free(source1);
+    tcg_temp_free(csr_store);
+    tcg_temp_free(dest);
+    tcg_temp_free(rs1_pass);
+    tcg_temp_free(imm_rs1);
+}
+
+static void decode_RV32_64C0(DisasContext *ctx)
+{
+    uint8_t funct3 = extract32(ctx->opcode, 13, 3);
+    uint8_t rd_rs2 = GET_C_RS2S(ctx->opcode);
+    uint8_t rs1s = GET_C_RS1S(ctx->opcode);
+
+    switch (funct3) {
+    case 0:
+        /* illegal */
+        if (ctx->opcode == 0) {
+            gen_exception_illegal(ctx);
+        } else {
+            /* C.ADDI4SPN -> addi rd', x2, zimm[9:2]*/
+            gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs2, 2,
+                          GET_C_ADDI4SPN_IMM(ctx->opcode));
+        }
+        break;
+    case 1:
+        /* C.FLD -> fld rd', offset[7:3](rs1')*/
+        gen_fp_load(ctx, OPC_RISC_FLD, rd_rs2, rs1s,
+                    GET_C_LD_IMM(ctx->opcode));
+        /* C.LQ(RV128) */
+        break;
+    case 2:
+        /* C.LW -> lw rd', offset[6:2](rs1') */
+        gen_load(ctx, OPC_RISC_LW, rd_rs2, rs1s,
+                 GET_C_LW_IMM(ctx->opcode));
+        break;
+    case 3:
+#if defined(TARGET_RISCV64)
+        /* C.LD(RV64/128) -> ld rd', offset[7:3](rs1')*/
+        gen_load(ctx, OPC_RISC_LD, rd_rs2, rs1s,
+                 GET_C_LD_IMM(ctx->opcode));
+#else
+        /* C.FLW (RV32) -> flw rd', offset[6:2](rs1')*/
+        gen_fp_load(ctx, OPC_RISC_FLW, rd_rs2, rs1s,
+                    GET_C_LW_IMM(ctx->opcode));
+#endif
+        break;
+    case 4:
+        /* reserved */
+        gen_exception_illegal(ctx);
+        break;
+    case 5:
+        /* C.FSD(RV32/64) -> fsd rs2', offset[7:3](rs1') */
+        gen_fp_store(ctx, OPC_RISC_FSD, rs1s, rd_rs2,
+                     GET_C_LD_IMM(ctx->opcode));
+        /* C.SQ (RV128) */
+        break;
+    case 6:
+        /* C.SW -> sw rs2', offset[6:2](rs1')*/
+        gen_store(ctx, OPC_RISC_SW, rs1s, rd_rs2,
+                  GET_C_LW_IMM(ctx->opcode));
+        break;
+    case 7:
+#if defined(TARGET_RISCV64)
+        /* C.SD (RV64/128) -> sd rs2', offset[7:3](rs1')*/
+        gen_store(ctx, OPC_RISC_SD, rs1s, rd_rs2,
+                  GET_C_LD_IMM(ctx->opcode));
+#else
+        /* C.FSW (RV32) -> fsw rs2', offset[6:2](rs1')*/
+        gen_fp_store(ctx, OPC_RISC_FSW, rs1s, rd_rs2,
+                     GET_C_LW_IMM(ctx->opcode));
+#endif
+        break;
+    }
+}
+
+static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx)
+{
+    uint8_t funct3 = extract32(ctx->opcode, 13, 3);
+    uint8_t rd_rs1 = GET_C_RS1(ctx->opcode);
+    uint8_t rs1s, rs2s;
+    uint8_t funct2;
+
+    switch (funct3) {
+    case 0:
+        /* C.ADDI -> addi rd, rd, nzimm[5:0] */
+        gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, rd_rs1,
+                      GET_C_IMM(ctx->opcode));
+        break;
+    case 1:
+#if defined(TARGET_RISCV64)
+        /* C.ADDIW (RV64/128) -> addiw rd, rd, imm[5:0]*/
+        gen_arith_imm(ctx, OPC_RISC_ADDIW, rd_rs1, rd_rs1,
+                      GET_C_IMM(ctx->opcode));
+#else
+        /* C.JAL(RV32) -> jal x1, offset[11:1] */
+        gen_jal(env, ctx, 1, GET_C_J_IMM(ctx->opcode));
+#endif
+        break;
+    case 2:
+        /* C.LI -> addi rd, x0, imm[5:0]*/
+        gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, 0, GET_C_IMM(ctx->opcode));
+        break;
+    case 3:
+        if (rd_rs1 == 2) {
+            /* C.ADDI16SP -> addi x2, x2, nzimm[9:4]*/
+            gen_arith_imm(ctx, OPC_RISC_ADDI, 2, 2,
+                          GET_C_ADDI16SP_IMM(ctx->opcode));
+        } else if (rd_rs1 != 0) {
+            /* C.LUI (rs1/rd =/= {0,2}) -> lui rd, nzimm[17:12]*/
+            tcg_gen_movi_tl(cpu_gpr[rd_rs1],
+                            GET_C_IMM(ctx->opcode) << 12);
+        }
+        break;
+    case 4:
+        funct2 = extract32(ctx->opcode, 10, 2);
+        rs1s = GET_C_RS1S(ctx->opcode);
+        switch (funct2) {
+        case 0: /* C.SRLI(RV32) -> srli rd', rd', shamt[5:0] */
+            gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s,
+                               GET_C_ZIMM(ctx->opcode));
+            /* C.SRLI64(RV128) */
+            break;
+        case 1:
+            /* C.SRAI -> srai rd', rd', shamt[5:0]*/
+            gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s,
+                            GET_C_ZIMM(ctx->opcode) | 0x400);
+            /* C.SRAI64(RV128) */
+            break;
+        case 2:
+            /* C.ANDI -> andi rd', rd', imm[5:0]*/
+            gen_arith_imm(ctx, OPC_RISC_ANDI, rs1s, rs1s,
+                          GET_C_IMM(ctx->opcode));
+            break;
+        case 3:
+            funct2 = extract32(ctx->opcode, 5, 2);
+            rs2s = GET_C_RS2S(ctx->opcode);
+            switch (funct2) {
+            case 0:
+                /* C.SUB -> sub rd', rd', rs2' */
+                if (extract32(ctx->opcode, 12, 1) == 0) {
+                    gen_arith(ctx, OPC_RISC_SUB, rs1s, rs1s, rs2s);
+                }
+#if defined(TARGET_RISCV64)
+                else {
+                    gen_arith(ctx, OPC_RISC_SUBW, rs1s, rs1s, rs2s);
+                }
+#endif
+                break;
+            case 1:
+                /* C.XOR -> xor rs1', rs1', rs2' */
+                if (extract32(ctx->opcode, 12, 1) == 0) {
+                    gen_arith(ctx, OPC_RISC_XOR, rs1s, rs1s, rs2s);
+                }
+#if defined(TARGET_RISCV64)
+                else {
+                    /* C.ADDW (RV64/128) */
+                    gen_arith(ctx, OPC_RISC_ADDW, rs1s, rs1s, rs2s);
+                }
+#endif
+                break;
+            case 2:
+                /* C.OR -> or rs1', rs1', rs2' */
+                gen_arith(ctx, OPC_RISC_OR, rs1s, rs1s, rs2s);
+                break;
+            case 3:
+                /* C.AND -> and rs1', rs1', rs2' */
+                gen_arith(ctx, OPC_RISC_AND, rs1s, rs1s, rs2s);
+                break;
+            }
+            break;
+        }
+        break;
+    case 5:
+        /* C.J -> jal x0, offset[11:1]*/
+        gen_jal(env, ctx, 0, GET_C_J_IMM(ctx->opcode));
+        break;
+    case 6:
+        /* C.BEQZ -> beq rs1', x0, offset[8:1]*/
+        rs1s = GET_C_RS1S(ctx->opcode);
+        gen_branch(env, ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode));
+        break;
+    case 7:
+        /* C.BNEZ -> bne rs1', x0, offset[8:1]*/
+        rs1s = GET_C_RS1S(ctx->opcode);
+        gen_branch(env, ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode));
+        break;
+    }
+}
+
+static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx)
+{
+    uint8_t rd, rs2;
+    uint8_t funct3 = extract32(ctx->opcode, 13, 3);
+
+
+    rd = GET_RD(ctx->opcode);
+
+    switch (funct3) {
+    case 0: /* C.SLLI -> slli rd, rd, shamt[5:0]
+               C.SLLI64 -> */
+        gen_arith_imm(ctx, OPC_RISC_SLLI, rd, rd, GET_C_ZIMM(ctx->opcode));
+        break;
+    case 1: /* C.FLDSP(RV32/64DC) -> fld rd, offset[8:3](x2) */
+        gen_fp_load(ctx, OPC_RISC_FLD, rd, 2, GET_C_LDSP_IMM(ctx->opcode));
+        break;
+    case 2: /* C.LWSP -> lw rd, offset[7:2](x2) */
+        gen_load(ctx, OPC_RISC_LW, rd, 2, GET_C_LWSP_IMM(ctx->opcode));
+        break;
+    case 3:
+#if defined(TARGET_RISCV64)
+        /* C.LDSP(RVC64) -> ld rd, offset[8:3](x2) */
+        gen_load(ctx, OPC_RISC_LD, rd, 2, GET_C_LDSP_IMM(ctx->opcode));
+#else
+        /* C.FLWSP(RV32FC) -> flw rd, offset[7:2](x2) */
+        gen_fp_load(ctx, OPC_RISC_FLW, rd, 2, GET_C_LWSP_IMM(ctx->opcode));
+#endif
+        break;
+    case 4:
+        rs2 = GET_C_RS2(ctx->opcode);
+
+        if (extract32(ctx->opcode, 12, 1) == 0) {
+            if (rs2 == 0) {
+                /* C.JR -> jalr x0, rs1, 0*/
+                gen_jalr(env, ctx, OPC_RISC_JALR, 0, rd, 0);
+            } else {
+                /* C.MV -> add rd, x0, rs2 */
+                gen_arith(ctx, OPC_RISC_ADD, rd, 0, rs2);
+            }
+        } else {
+            if (rd == 0) {
+                /* C.EBREAK -> ebreak*/
+                gen_system(env, ctx, OPC_RISC_ECALL, 0, 0, 0x1);
+            } else {
+                if (rs2 == 0) {
+                    /* C.JALR -> jalr x1, rs1, 0*/
+                    gen_jalr(env, ctx, OPC_RISC_JALR, 1, rd, 0);
+                } else {
+                    /* C.ADD -> add rd, rd, rs2 */
+                    gen_arith(ctx, OPC_RISC_ADD, rd, rd, rs2);
+                }
+            }
+        }
+        break;
+    case 5:
+        /* C.FSDSP -> fsd rs2, offset[8:3](x2)*/
+        gen_fp_store(ctx, OPC_RISC_FSD, 2, GET_C_RS2(ctx->opcode),
+                     GET_C_SDSP_IMM(ctx->opcode));
+        /* C.SQSP */
+        break;
+    case 6: /* C.SWSP -> sw rs2, offset[7:2](x2)*/
+        gen_store(ctx, OPC_RISC_SW, 2, GET_C_RS2(ctx->opcode),
+                  GET_C_SWSP_IMM(ctx->opcode));
+        break;
+    case 7:
+#if defined(TARGET_RISCV64)
+        /* C.SDSP(Rv64/128) -> sd rs2, offset[8:3](x2)*/
+        gen_store(ctx, OPC_RISC_SD, 2, GET_C_RS2(ctx->opcode),
+                  GET_C_SDSP_IMM(ctx->opcode));
+#else
+        /* C.FSWSP(RV32) -> fsw rs2, offset[7:2](x2) */
+        gen_fp_store(ctx, OPC_RISC_FSW, 2, GET_C_RS2(ctx->opcode),
+                     GET_C_SWSP_IMM(ctx->opcode));
+#endif
+        break;
+    }
+}
+
+static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx)
+{
+    uint8_t op = extract32(ctx->opcode, 0, 2);
+
+    switch (op) {
+    case 0:
+        decode_RV32_64C0(ctx);
+        break;
+    case 1:
+        decode_RV32_64C1(env, ctx);
+        break;
+    case 2:
+        decode_RV32_64C2(env, ctx);
+        break;
+    }
+}
+
+static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx)
+{
+    int rs1;
+    int rs2;
+    int rd;
+    uint32_t op;
+    target_long imm;
+
+    /* We do not do misaligned address check here: the address should never be
+     * misaligned at this point. Instructions that set PC must do the check,
+     * since epc must be the address of the instruction that caused us to
+     * perform the misaligned instruction fetch */
+
+    op = MASK_OP_MAJOR(ctx->opcode);
+    rs1 = GET_RS1(ctx->opcode);
+    rs2 = GET_RS2(ctx->opcode);
+    rd = GET_RD(ctx->opcode);
+    imm = GET_IMM(ctx->opcode);
+
+    switch (op) {
+    case OPC_RISC_LUI:
+        if (rd == 0) {
+            break; /* NOP */
+        }
+        tcg_gen_movi_tl(cpu_gpr[rd], sextract64(ctx->opcode, 12, 20) << 12);
+        break;
+    case OPC_RISC_AUIPC:
+        if (rd == 0) {
+            break; /* NOP */
+        }
+        tcg_gen_movi_tl(cpu_gpr[rd], (sextract64(ctx->opcode, 12, 20) << 12) +
+               ctx->pc);
+        break;
+    case OPC_RISC_JAL:
+        imm = GET_JAL_IMM(ctx->opcode);
+        gen_jal(env, ctx, rd, imm);
+        break;
+    case OPC_RISC_JALR:
+        gen_jalr(env, ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm);
+        break;
+    case OPC_RISC_BRANCH:
+        gen_branch(env, ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2,
+                   GET_B_IMM(ctx->opcode));
+        break;
+    case OPC_RISC_LOAD:
+        gen_load(ctx, MASK_OP_LOAD(ctx->opcode), rd, rs1, imm);
+        break;
+    case OPC_RISC_STORE:
+        gen_store(ctx, MASK_OP_STORE(ctx->opcode), rs1, rs2,
+                  GET_STORE_IMM(ctx->opcode));
+        break;
+    case OPC_RISC_ARITH_IMM:
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_ARITH_IMM_W:
+#endif
+        if (rd == 0) {
+            break; /* NOP */
+        }
+        gen_arith_imm(ctx, MASK_OP_ARITH_IMM(ctx->opcode), rd, rs1, imm);
+        break;
+    case OPC_RISC_ARITH:
+#if defined(TARGET_RISCV64)
+    case OPC_RISC_ARITH_W:
+#endif
+        if (rd == 0) {
+            break; /* NOP */
+        }
+        gen_arith(ctx, MASK_OP_ARITH(ctx->opcode), rd, rs1, rs2);
+        break;
+    case OPC_RISC_FP_LOAD:
+        gen_fp_load(ctx, MASK_OP_FP_LOAD(ctx->opcode), rd, rs1, imm);
+        break;
+    case OPC_RISC_FP_STORE:
+        gen_fp_store(ctx, MASK_OP_FP_STORE(ctx->opcode), rs1, rs2,
+                     GET_STORE_IMM(ctx->opcode));
+        break;
+    case OPC_RISC_ATOMIC:
+        gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2);
+        break;
+    case OPC_RISC_FMADD:
+        gen_fp_fmadd(ctx, MASK_OP_FP_FMADD(ctx->opcode), rd, rs1, rs2,
+                     GET_RS3(ctx->opcode), GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FMSUB:
+        gen_fp_fmsub(ctx, MASK_OP_FP_FMSUB(ctx->opcode), rd, rs1, rs2,
+                     GET_RS3(ctx->opcode), GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FNMSUB:
+        gen_fp_fnmsub(ctx, MASK_OP_FP_FNMSUB(ctx->opcode), rd, rs1, rs2,
+                      GET_RS3(ctx->opcode), GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FNMADD:
+        gen_fp_fnmadd(ctx, MASK_OP_FP_FNMADD(ctx->opcode), rd, rs1, rs2,
+                      GET_RS3(ctx->opcode), GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FP_ARITH:
+        gen_fp_arith(ctx, MASK_OP_FP_ARITH(ctx->opcode), rd, rs1, rs2,
+                     GET_RM(ctx->opcode));
+        break;
+    case OPC_RISC_FENCE:
+#ifndef CONFIG_USER_ONLY
+        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;
+        } else {
+            /* FENCE is a full memory barrier. */
+            tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
+        }
+#endif
+        break;
+    case OPC_RISC_SYSTEM:
+        gen_system(env, ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1,
+                   (ctx->opcode & 0xFFF00000) >> 20);
+        break;
+    default:
+        gen_exception_illegal(ctx);
+        break;
+    }
+}
+
+static void decode_opc(CPURISCVState *env, DisasContext *ctx)
+{
+    /* check for compressed insn */
+    if (extract32(ctx->opcode, 0, 2) != 3) {
+        if (!riscv_has_ext(env, RVC)) {
+            gen_exception_illegal(ctx);
+        } else {
+            ctx->next_pc = ctx->pc + 2;
+            decode_RV32_64C(env, ctx);
+        }
+    } else {
+        ctx->next_pc = ctx->pc + 4;
+        decode_RV32_64G(env, ctx);
+    }
+}
+
+void gen_intermediate_code(CPUState *cs, TranslationBlock *tb)
+{
+    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;
+
+    /* once we have GDB, the rest of the translate.c implementation should be
+       ready for singlestep */
+    ctx.singlestep_enabled = cs->singlestep_enabled;
+
+    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 */
+
+    num_insns = 0;
+    max_insns = tb->cflags & CF_COUNT_MASK;
+    if (max_insns == 0) {
+        max_insns = CF_COUNT_MASK;
+    }
+    if (max_insns > TCG_MAX_INSNS) {
+        max_insns = TCG_MAX_INSNS;
+    }
+    gen_tb_start(tb);
+
+    while (ctx.bstate == BS_NONE) {
+        tcg_gen_insn_start(ctx.pc);
+        num_insns++;
+
+        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) {
+            gen_exception_debug();
+        } else {
+            tcg_gen_exit_tb(0);
+        }
+        break;
+    case BS_BRANCH: /* ops using BS_BRANCH generate own exit seq */
+    default:
+        break;
+    }
+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
+}
+
+void riscv_translate_init(void)
+{
+    int i;
+
+    /* cpu_gpr[0] is a placeholder for the zero register. Do not use it. */
+    /* Use the gen_set_gpr and gen_get_gpr helper functions when accessing */
+    /* registers, unless you specifically block reads/writes to reg 0 */
+    cpu_gpr[0] = NULL;
+
+    for (i = 1; i < 32; i++) {
+        cpu_gpr[i] = tcg_global_mem_new(cpu_env,
+            offsetof(CPURISCVState, gpr[i]), riscv_int_regnames[i]);
+    }
+
+    for (i = 0; i < 32; i++) {
+        cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env,
+            offsetof(CPURISCVState, fpr[i]), riscv_fpr_regnames[i]);
+    }
+
+    cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, pc), "pc");
+    load_res = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_res),
+                             "load_res");
+    load_val = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_val),
+                             "load_val");
+}
index c5ef93087637cdde3e2e8e055209ab78f9b1092b..5f357a4e2d5fac5b67e380c3bfe5036c6fe9c2fe 100644 (file)
@@ -538,39 +538,39 @@ typedef union SysIB {
 QEMU_BUILD_BUG_ON(sizeof(SysIB) != 4096);
 
 /* MMU defines */
-#define _ASCE_ORIGIN            ~0xfffULL /* segment table origin             */
-#define _ASCE_SUBSPACE          0x200     /* subspace group control           */
-#define _ASCE_PRIVATE_SPACE     0x100     /* private space control            */
-#define _ASCE_ALT_EVENT         0x80      /* storage alteration event control */
-#define _ASCE_SPACE_SWITCH      0x40      /* space switch event               */
-#define _ASCE_REAL_SPACE        0x20      /* real space control               */
-#define _ASCE_TYPE_MASK         0x0c      /* asce table type mask             */
-#define _ASCE_TYPE_REGION1      0x0c      /* region first table type          */
-#define _ASCE_TYPE_REGION2      0x08      /* region second table type         */
-#define _ASCE_TYPE_REGION3      0x04      /* region third table type          */
-#define _ASCE_TYPE_SEGMENT      0x00      /* segment table type               */
-#define _ASCE_TABLE_LENGTH      0x03      /* region table length              */
-
-#define _REGION_ENTRY_ORIGIN    ~0xfffULL /* region/segment table origin      */
-#define _REGION_ENTRY_RO        0x200     /* region/segment protection bit    */
-#define _REGION_ENTRY_TF        0xc0      /* region/segment table offset      */
-#define _REGION_ENTRY_INV       0x20      /* invalid region table entry       */
-#define _REGION_ENTRY_TYPE_MASK 0x0c      /* region/segment table type mask   */
-#define _REGION_ENTRY_TYPE_R1   0x0c      /* region first table type          */
-#define _REGION_ENTRY_TYPE_R2   0x08      /* region second table type         */
-#define _REGION_ENTRY_TYPE_R3   0x04      /* region third table type          */
-#define _REGION_ENTRY_LENGTH    0x03      /* region third length              */
-
-#define _SEGMENT_ENTRY_ORIGIN   ~0x7ffULL /* segment table origin             */
-#define _SEGMENT_ENTRY_FC       0x400     /* format control                   */
-#define _SEGMENT_ENTRY_RO       0x200     /* page protection bit              */
-#define _SEGMENT_ENTRY_INV      0x20      /* invalid segment table entry      */
-
-#define VADDR_PX                0xff000   /* page index bits                  */
-
-#define _PAGE_RO        0x200            /* HW read-only bit  */
-#define _PAGE_INVALID   0x400            /* HW invalid bit    */
-#define _PAGE_RES0      0x800            /* bit must be zero  */
+#define ASCE_ORIGIN           (~0xfffULL) /* segment table origin             */
+#define ASCE_SUBSPACE         0x200       /* subspace group control           */
+#define ASCE_PRIVATE_SPACE    0x100       /* private space control            */
+#define ASCE_ALT_EVENT        0x80        /* storage alteration event control */
+#define ASCE_SPACE_SWITCH     0x40        /* space switch event               */
+#define ASCE_REAL_SPACE       0x20        /* real space control               */
+#define ASCE_TYPE_MASK        0x0c        /* asce table type mask             */
+#define ASCE_TYPE_REGION1     0x0c        /* region first table type          */
+#define ASCE_TYPE_REGION2     0x08        /* region second table type         */
+#define ASCE_TYPE_REGION3     0x04        /* region third table type          */
+#define ASCE_TYPE_SEGMENT     0x00        /* segment table type               */
+#define ASCE_TABLE_LENGTH     0x03        /* region table length              */
+
+#define REGION_ENTRY_ORIGIN   (~0xfffULL) /* region/segment table origin    */
+#define REGION_ENTRY_RO       0x200       /* region/segment protection bit  */
+#define REGION_ENTRY_TF       0xc0        /* region/segment table offset    */
+#define REGION_ENTRY_INV      0x20        /* invalid region table entry     */
+#define REGION_ENTRY_TYPE_MASK 0x0c       /* region/segment table type mask */
+#define REGION_ENTRY_TYPE_R1  0x0c        /* region first table type        */
+#define REGION_ENTRY_TYPE_R2  0x08        /* region second table type       */
+#define REGION_ENTRY_TYPE_R3  0x04        /* region third table type        */
+#define REGION_ENTRY_LENGTH   0x03        /* region third length            */
+
+#define SEGMENT_ENTRY_ORIGIN  (~0x7ffULL) /* segment table origin        */
+#define SEGMENT_ENTRY_FC      0x400       /* format control              */
+#define SEGMENT_ENTRY_RO      0x200       /* page protection bit         */
+#define SEGMENT_ENTRY_INV     0x20        /* invalid segment table entry */
+
+#define VADDR_PX              0xff000     /* page index bits   */
+
+#define PAGE_RO               0x200       /* HW read-only bit  */
+#define PAGE_INVALID          0x400       /* HW invalid bit    */
+#define PAGE_RES0             0x800       /* bit must be zero  */
 
 #define SK_C                    (0x1 << 1)
 #define SK_R                    (0x1 << 2)
index d5291b246eb6f2cdcd466b8d934e8a555548ed77..a0e28bd124c3ddc59b186a91680438709ea6ae8a 100644 (file)
@@ -1924,20 +1924,20 @@ void HELPER(idte)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint32_t m4)
 
     if (!(r2 & 0x800)) {
         /* invalidation-and-clearing operation */
-        table = r1 & _ASCE_ORIGIN;
+        table = r1 & ASCE_ORIGIN;
         entries = (r2 & 0x7ff) + 1;
 
-        switch (r1 & _ASCE_TYPE_MASK) {
-        case _ASCE_TYPE_REGION1:
+        switch (r1 & ASCE_TYPE_MASK) {
+        case ASCE_TYPE_REGION1:
             index = (r2 >> 53) & 0x7ff;
             break;
-        case _ASCE_TYPE_REGION2:
+        case ASCE_TYPE_REGION2:
             index = (r2 >> 42) & 0x7ff;
             break;
-        case _ASCE_TYPE_REGION3:
+        case ASCE_TYPE_REGION3:
             index = (r2 >> 31) & 0x7ff;
             break;
-        case _ASCE_TYPE_SEGMENT:
+        case ASCE_TYPE_SEGMENT:
             index = (r2 >> 20) & 0x7ff;
             break;
         }
@@ -1945,9 +1945,9 @@ void HELPER(idte)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint32_t m4)
             /* addresses are not wrapped in 24/31bit mode but table index is */
             raddr = table + ((index + i) & 0x7ff) * sizeof(entry);
             entry = cpu_ldq_real_ra(env, raddr, ra);
-            if (!(entry & _REGION_ENTRY_INV)) {
+            if (!(entry & REGION_ENTRY_INV)) {
                 /* we are allowed to not store if already invalid */
-                entry |= _REGION_ENTRY_INV;
+                entry |= REGION_ENTRY_INV;
                 cpu_stq_real_ra(env, raddr, entry, ra);
             }
         }
@@ -1971,12 +1971,12 @@ void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr,
     uint64_t pte_addr, pte;
 
     /* Compute the page table entry address */
-    pte_addr = (pto & _SEGMENT_ENTRY_ORIGIN);
+    pte_addr = (pto & SEGMENT_ENTRY_ORIGIN);
     pte_addr += (vaddr & VADDR_PX) >> 9;
 
     /* Mark the page table entry as invalid */
     pte = cpu_ldq_real_ra(env, pte_addr, ra);
-    pte |= _PAGE_INVALID;
+    pte |= PAGE_INVALID;
     cpu_stq_real_ra(env, pte_addr, pte, ra);
 
     /* XXX we exploit the fact that Linux passes the exact virtual
index 23fb2e7501000a4c92903477ed50f2ee9dd2531d..1deeb6e6e47a42a87fdd683786bdc5ac9d29bbd2 100644 (file)
@@ -128,11 +128,11 @@ static bool lowprot_enabled(const CPUS390XState *env, uint64_t asc)
     /* Check the private-space control bit */
     switch (asc) {
     case PSW_ASC_PRIMARY:
-        return !(env->cregs[1] & _ASCE_PRIVATE_SPACE);
+        return !(env->cregs[1] & ASCE_PRIVATE_SPACE);
     case PSW_ASC_SECONDARY:
-        return !(env->cregs[7] & _ASCE_PRIVATE_SPACE);
+        return !(env->cregs[7] & ASCE_PRIVATE_SPACE);
     case PSW_ASC_HOME:
-        return !(env->cregs[13] & _ASCE_PRIVATE_SPACE);
+        return !(env->cregs[13] & ASCE_PRIVATE_SPACE);
     default:
         /* We don't support access register mode */
         error_report("unsupported addressing mode");
@@ -159,20 +159,20 @@ static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr,
                              uint64_t asc, uint64_t pt_entry,
                              target_ulong *raddr, int *flags, int rw, bool exc)
 {
-    if (pt_entry & _PAGE_INVALID) {
+    if (pt_entry & PAGE_INVALID) {
         DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, pt_entry);
         trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw, exc);
         return -1;
     }
-    if (pt_entry & _PAGE_RES0) {
+    if (pt_entry & PAGE_RES0) {
         trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
         return -1;
     }
-    if (pt_entry & _PAGE_RO) {
+    if (pt_entry & PAGE_RO) {
         *flags &= ~PAGE_WRITE;
     }
 
-    *raddr = pt_entry & _ASCE_ORIGIN;
+    *raddr = pt_entry & ASCE_ORIGIN;
 
     PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, pt_entry);
 
@@ -188,11 +188,11 @@ static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr,
     CPUState *cs = CPU(s390_env_get_cpu(env));
     uint64_t origin, offs, pt_entry;
 
-    if (st_entry & _SEGMENT_ENTRY_RO) {
+    if (st_entry & SEGMENT_ENTRY_RO) {
         *flags &= ~PAGE_WRITE;
     }
 
-    if ((st_entry & _SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) {
+    if ((st_entry & SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) {
         /* Decode EDAT1 segment frame absolute address (1MB page) */
         *raddr = (st_entry & 0xfffffffffff00000ULL) | (vaddr & 0xfffff);
         PTE_DPRINTF("%s: SEG=0x%" PRIx64 "\n", __func__, st_entry);
@@ -200,7 +200,7 @@ static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr,
     }
 
     /* Look up 4KB page entry */
-    origin = st_entry & _SEGMENT_ENTRY_ORIGIN;
+    origin = st_entry & SEGMENT_ENTRY_ORIGIN;
     offs  = (vaddr & VADDR_PX) >> 9;
     pt_entry = ldq_phys(cs->as, origin + offs);
     PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
@@ -223,39 +223,39 @@ static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
 
     PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, entry);
 
-    origin = entry & _REGION_ENTRY_ORIGIN;
+    origin = entry & REGION_ENTRY_ORIGIN;
     offs = (vaddr >> (17 + 11 * level / 4)) & 0x3ff8;
 
     new_entry = ldq_phys(cs->as, origin + offs);
     PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
                 __func__, origin, offs, new_entry);
 
-    if ((new_entry & _REGION_ENTRY_INV) != 0) {
+    if ((new_entry & REGION_ENTRY_INV) != 0) {
         DPRINTF("%s: invalid region\n", __func__);
         trigger_page_fault(env, vaddr, pchks[level / 4], asc, rw, exc);
         return -1;
     }
 
-    if ((new_entry & _REGION_ENTRY_TYPE_MASK) != level) {
+    if ((new_entry & REGION_ENTRY_TYPE_MASK) != level) {
         trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
         return -1;
     }
 
-    if (level == _ASCE_TYPE_SEGMENT) {
+    if (level == ASCE_TYPE_SEGMENT) {
         return mmu_translate_segment(env, vaddr, asc, new_entry, raddr, flags,
                                      rw, exc);
     }
 
     /* Check region table offset and length */
     offs = (vaddr >> (28 + 11 * (level - 4) / 4)) & 3;
-    if (offs < ((new_entry & _REGION_ENTRY_TF) >> 6)
-        || offs > (new_entry & _REGION_ENTRY_LENGTH)) {
+    if (offs < ((new_entry & REGION_ENTRY_TF) >> 6)
+        || offs > (new_entry & REGION_ENTRY_LENGTH)) {
         DPRINTF("%s: invalid offset or len (%lx)\n", __func__, new_entry);
         trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw, exc);
         return -1;
     }
 
-    if ((env->cregs[0] & CR0_EDAT) && (new_entry & _REGION_ENTRY_RO)) {
+    if ((env->cregs[0] & CR0_EDAT) && (new_entry & REGION_ENTRY_RO)) {
         *flags &= ~PAGE_WRITE;
     }
 
@@ -271,52 +271,52 @@ static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
     int level;
     int r;
 
-    if (asce & _ASCE_REAL_SPACE) {
+    if (asce & ASCE_REAL_SPACE) {
         /* direct mapping */
         *raddr = vaddr;
         return 0;
     }
 
-    level = asce & _ASCE_TYPE_MASK;
+    level = asce & ASCE_TYPE_MASK;
     switch (level) {
-    case _ASCE_TYPE_REGION1:
-        if ((vaddr >> 62) > (asce & _ASCE_TABLE_LENGTH)) {
+    case ASCE_TYPE_REGION1:
+        if ((vaddr >> 62) > (asce & ASCE_TABLE_LENGTH)) {
             trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw, exc);
             return -1;
         }
         break;
-    case _ASCE_TYPE_REGION2:
+    case ASCE_TYPE_REGION2:
         if (vaddr & 0xffe0000000000000ULL) {
             DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
                     " 0xffe0000000000000ULL\n", __func__, vaddr);
             trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
             return -1;
         }
-        if ((vaddr >> 51 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
+        if ((vaddr >> 51 & 3) > (asce & ASCE_TABLE_LENGTH)) {
             trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw, exc);
             return -1;
         }
         break;
-    case _ASCE_TYPE_REGION3:
+    case ASCE_TYPE_REGION3:
         if (vaddr & 0xfffffc0000000000ULL) {
             DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
                     " 0xfffffc0000000000ULL\n", __func__, vaddr);
             trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
             return -1;
         }
-        if ((vaddr >> 40 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
+        if ((vaddr >> 40 & 3) > (asce & ASCE_TABLE_LENGTH)) {
             trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw, exc);
             return -1;
         }
         break;
-    case _ASCE_TYPE_SEGMENT:
+    case ASCE_TYPE_SEGMENT:
         if (vaddr & 0xffffffff80000000ULL) {
             DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
                     " 0xffffffff80000000ULL\n", __func__, vaddr);
             trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
             return -1;
         }
-        if ((vaddr >> 29 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
+        if ((vaddr >> 29 & 3) > (asce & ASCE_TABLE_LENGTH)) {
             trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc);
             return -1;
         }
index 71e0853e43d2ae0db7365baf783eb162635fb361..5aa367a18227d6fb0a332ab310959261cfdd9935 100644 (file)
@@ -2093,6 +2093,11 @@ static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop)
             type = GET_ASI_BFILL;
             break;
         }
+
+        /* MMU_PHYS_IDX is used when the MMU is disabled to passthrough the
+         * permissions check in get_physical_address(..).
+         */
+        mem_idx = (dc->mem_idx == MMU_PHYS_IDX) ? MMU_PHYS_IDX : mem_idx;
     } else {
         gen_exception(dc, TT_PRIV_INSN);
         type = GET_ASI_EXCP;
index fdca0625911aae3bdc57e47a5d6918844de34396..ef9b88c3694ba18caee0f58e03741229b9ff8891 100644 (file)
@@ -92,6 +92,7 @@ gcov-files-test-hbitmap-y = blockjob.c
 check-unit-y += tests/test-bdrv-drain$(EXESUF)
 check-unit-y += tests/test-blockjob$(EXESUF)
 check-unit-y += tests/test-blockjob-txn$(EXESUF)
+check-unit-y += tests/test-block-backend$(EXESUF)
 check-unit-y += tests/test-x86-cpuid$(EXESUF)
 # all code tested by test-x86-cpuid is inside topology.h
 gcov-files-test-x86-cpuid-y =
@@ -622,6 +623,7 @@ tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
 tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
 tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
 tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
+tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y)
 tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
 tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y)
 tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) $(test-crypto-obj-y)
index 2342fe3099698c7a70546f93eea18f1c74021a4c..fb3cd84d07147136e61b52b795c770403f3a938a 100644 (file)
@@ -1823,6 +1823,7 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
     if ((addr == ADDR_MODE_LBA48) && (offset == OFFSET_HIGH) &&
         (mb_to_sectors(test_image_size_mb) <= 0xFFFFFFF)) {
         g_test_message("%s: skipped; test image too small", name);
+        g_free(opts);
         g_free(name);
         return;
     }
index 2cdfd1397aff485ad4463aa3469da4b02cbb7393..a1d8357331d9295c606b1949c25e41daae4579a2 100755 (executable)
@@ -64,6 +64,9 @@ do_test()
        } | $QEMU_IO $IO_EXTRA_ARGS
 }
 
+echo
+echo "=== Test aligned and misaligned write zeroes operations ==="
+
 for write_zero_cmd in "write -z" "aio_write -z"; do
 for align in 512 4k; do
        echo
@@ -102,7 +105,33 @@ for align in 512 4k; do
 done
 done
 
+
+# Trigger truncate that would shrink qcow2 L1 table, which is done by
+#   clearing one entry (8 bytes) with bdrv_co_pwrite_zeroes()
+
+echo
+echo "=== Test misaligned write zeroes via truncate ==="
+echo
+
+# any size will do, but the smaller the size the smaller the required image
+CLUSTER_SIZE=$((4 * 1024))
+L2_COVERAGE=$(($CLUSTER_SIZE * $CLUSTER_SIZE / 8))
+_make_test_img $(($L2_COVERAGE * 2))
+
+do_test 512 "write -P 1 0 0x200" "$TEST_IMG" | _filter_qemu_io
+# next L2 table
+do_test 512 "write -P 1 $L2_COVERAGE 0x200" "$TEST_IMG" | _filter_qemu_io
+
+# only interested in qcow2 here; also other formats might respond with
+#  "not supported" error message
+if [ $IMGFMT = "qcow2" ]; then
+    do_test 512 "truncate $L2_COVERAGE" "$TEST_IMG" | _filter_qemu_io
+fi
+
+do_test 512 "read -P 1 0 0x200" "$TEST_IMG" | _filter_qemu_io
+
 # success, all done
+echo
 echo "*** done"
 rm -f $seq.full
 status=0
index 95929eff703fe076c465e163acfd20c81e24bd04..9683f6b29049b9a4cfdf99bbdc4136e9a14a019e 100644 (file)
@@ -1,6 +1,8 @@
 QA output created by 033
 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
 
+=== Test aligned and misaligned write zeroes operations ===
+
 == preparing image ==
 wrote 1024/1024 bytes at offset 512
 1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -164,4 +166,15 @@ read 512/512 bytes at offset 512
 read 3072/3072 bytes at offset 1024
 3 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 
+
+=== Test misaligned write zeroes via truncate ===
+
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304
+wrote 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 512/512 bytes at offset 2097152
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+read 512/512 bytes at offset 0
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
 *** done
index 0c3be16489cfad087b553fdfa8c8e9359b74a151..f617e25e24a427da86e137550734f15a96a615e5 100755 (executable)
@@ -157,9 +157,7 @@ case "$QEMU_DEFAULT_MACHINE" in
     pc)
         run_qemu -drive if=floppy
         run_qemu -drive if=ide,media=cdrom
-        run_qemu -drive if=scsi,media=cdrom
         run_qemu -drive if=ide
-        run_qemu -drive if=scsi
         ;;
      *)
         ;;
@@ -188,9 +186,7 @@ case "$QEMU_DEFAULT_MACHINE" in
     pc)
         run_qemu -drive file="$TEST_IMG",if=floppy,readonly=on
         run_qemu -drive file="$TEST_IMG",if=ide,media=cdrom,readonly=on
-        run_qemu -drive file="$TEST_IMG",if=scsi,media=cdrom,readonly=on
         run_qemu -drive file="$TEST_IMG",if=ide,readonly=on
-        run_qemu -drive file="$TEST_IMG",if=scsi,readonly=on
         ;;
      *)
         ;;
index 6b3a5328e0d96abfe766fe4da4b127c2337189bf..1d825eb010f789301540ea194613b6ce5bd93cf9 100644 (file)
@@ -209,8 +209,10 @@ static QSDHCI *machine_start(const struct sdhci_t *test)
 
 static void machine_stop(QSDHCI *s)
 {
+    qpci_free_pc(s->pci.bus);
     g_free(s->pci.dev);
     qtest_quit(global_qtest);
+    g_free(s);
 }
 
 static void test_machine(const void *data)
diff --git a/tests/test-block-backend.c b/tests/test-block-backend.c
new file mode 100644 (file)
index 0000000..fd59f02
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * BlockBackend tests
+ *
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "block/block.h"
+#include "sysemu/block-backend.h"
+#include "qapi/error.h"
+
+static void test_drain_aio_error_flush_cb(void *opaque, int ret)
+{
+    bool *completed = opaque;
+
+    g_assert(ret == -ENOMEDIUM);
+    *completed = true;
+}
+
+static void test_drain_aio_error(void)
+{
+    BlockBackend *blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+    BlockAIOCB *acb;
+    bool completed = false;
+
+    acb = blk_aio_flush(blk, test_drain_aio_error_flush_cb, &completed);
+    g_assert(acb != NULL);
+    g_assert(completed == false);
+
+    blk_drain(blk);
+    g_assert(completed == true);
+
+    blk_unref(blk);
+}
+
+static void test_drain_all_aio_error(void)
+{
+    BlockBackend *blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
+    BlockAIOCB *acb;
+    bool completed = false;
+
+    acb = blk_aio_flush(blk, test_drain_aio_error_flush_cb, &completed);
+    g_assert(acb != NULL);
+    g_assert(completed == false);
+
+    blk_drain_all();
+    g_assert(completed == true);
+
+    blk_unref(blk);
+}
+
+int main(int argc, char **argv)
+{
+    bdrv_init();
+    qemu_init_main_loop(&error_abort);
+
+    g_test_init(&argc, &argv, NULL);
+
+    g_test_add_func("/block-backend/drain_aio_error", test_drain_aio_error);
+    g_test_add_func("/block-backend/drain_all_aio_error",
+                    test_drain_all_aio_error);
+
+    return g_test_run();
+}
index d357cd2a8e58ccb85de9532044ce03e3c795f67d..b273fd3ba204746745838a910d6add49cabc6c31 100644 (file)
@@ -179,7 +179,7 @@ static void test_io_channel_setup_async(SocketAddress *listen_addr,
     lioc = qio_channel_socket_new();
     qio_channel_socket_listen_async(
         lioc, listen_addr,
-        test_io_channel_complete, &data, NULL);
+        test_io_channel_complete, &data, NULL, NULL);
 
     g_main_loop_run(data.loop);
     g_main_context_iteration(g_main_context_default(), FALSE);
@@ -200,7 +200,7 @@ static void test_io_channel_setup_async(SocketAddress *listen_addr,
 
     qio_channel_socket_connect_async(
         QIO_CHANNEL_SOCKET(*src), connect_addr,
-        test_io_channel_complete, &data, NULL);
+        test_io_channel_complete, &data, NULL, NULL);
 
     g_main_loop_run(data.loop);
     g_main_context_iteration(g_main_context_default(), FALSE);
index a210d01ba538a77a10ab5d8635d3f6072ae19afb..32743b2c9637beb99601d269293106d6656d2c8b 100644 (file)
@@ -203,10 +203,12 @@ static void test_io_channel_tls(const void *opaque)
     qio_channel_tls_handshake(clientChanTLS,
                               test_tls_handshake_done,
                               &clientHandshake,
+                              NULL,
                               NULL);
     qio_channel_tls_handshake(serverChanTLS,
                               test_tls_handshake_done,
                               &serverHandshake,
+                              NULL,
                               NULL);
 
     /*
index 141aa2c55d781b8a1e87ccebb757af74ec3708eb..bac1bb4e7a3ffca5b4a58ef30cce09b558f4542d 100644 (file)
@@ -187,6 +187,7 @@ static void test_task_thread_complete(void)
     qio_task_run_in_thread(task,
                            test_task_thread_worker,
                            &data,
+                           NULL,
                            NULL);
 
     g_main_loop_run(data.loop);
@@ -228,6 +229,7 @@ static void test_task_thread_failure(void)
     qio_task_run_in_thread(task,
                            test_task_thread_worker,
                            &data,
+                           NULL,
                            NULL);
 
     g_main_loop_run(data.loop);
index 7833631275702abd2d25cf0b6af39c984c1e1f23..d99ea362c1fe261974e3536c1fd99846bef4ab1d 100644 (file)
@@ -128,6 +128,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len
         qio_channel_tls_handshake(tls,
                                   vnc_tls_handshake_done,
                                   vs,
+                                  NULL,
                                   NULL);
     }
     return 0;
index 6ccad22cef424ac9bbaf81c7b3695ad119572b18..950f1cd2ac04db6d25cfecf3f5fce2ea7fba6a2f 100644 (file)
@@ -81,6 +81,7 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED,
     qio_channel_tls_handshake(tls,
                               vncws_tls_handshake_done,
                               vs,
+                              NULL,
                               NULL);
 
     return TRUE;
index 3fb611631fe21d1654469e49f6f113b238e4b3ce..ae90b9963da84d0ab6195ef09e6d35a708b59778 100644 (file)
@@ -1,7 +1,7 @@
 util-obj-y = osdep.o cutils.o unicode.o qemu-timer-common.o
 util-obj-y += bufferiszero.o
 util-obj-y += lockcnt.o
-util-obj-y += aiocb.o async.o thread-pool.o qemu-timer.o
+util-obj-y += aiocb.o async.o aio-wait.o thread-pool.o qemu-timer.o
 util-obj-y += main-loop.o iohandler.o
 util-obj-$(CONFIG_POSIX) += aio-posix.o
 util-obj-$(CONFIG_POSIX) += compatfd.o
diff --git a/util/aio-wait.c b/util/aio-wait.c
new file mode 100644 (file)
index 0000000..975afdd
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * AioContext wait support
+ *
+ * Copyright (C) 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/main-loop.h"
+#include "block/aio-wait.h"
+
+static void dummy_bh_cb(void *opaque)
+{
+    /* The point is to make AIO_WAIT_WHILE()'s aio_poll() return */
+}
+
+void aio_wait_kick(AioWait *wait)
+{
+    /* The barrier (or an atomic op) is in the caller.  */
+    if (atomic_read(&wait->need_kick)) {
+        aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL);
+    }
+}
+
+typedef struct {
+    AioWait wait;
+    bool done;
+    QEMUBHFunc *cb;
+    void *opaque;
+} AioWaitBHData;
+
+/* Context: BH in IOThread */
+static void aio_wait_bh(void *opaque)
+{
+    AioWaitBHData *data = opaque;
+
+    data->cb(data->opaque);
+
+    data->done = true;
+    aio_wait_kick(&data->wait);
+}
+
+void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque)
+{
+    AioWaitBHData data = {
+        .cb = cb,
+        .opaque = opaque,
+    };
+
+    assert(qemu_get_current_aio_context() == qemu_get_aio_context());
+
+    aio_bh_schedule_oneshot(ctx, aio_wait_bh, &data);
+    AIO_WAIT_WHILE(&data.wait, ctx, !data.done);
+}
diff --git a/vl.c b/vl.c
index 6120b889ed418eaccab5ca4942e1d2cb0d921357..3ef04ce991fee332ca2faf883114dda6fb87d419 100644 (file)
--- a/vl.c
+++ b/vl.c
@@ -2209,6 +2209,9 @@ static int balloon_parse(const char *arg)
 {
     QemuOpts *opts;
 
+    warn_report("This option is deprecated. "
+                "Use '--device virtio-balloon' to enable the balloon device.");
+
     if (strcmp(arg, "none") == 0) {
         return 0;
     }
@@ -3406,6 +3409,8 @@ int main(int argc, char **argv, char **envp)
                 break;
             case QEMU_OPTION_localtime:
                 rtc_utc = 0;
+                warn_report("This option is deprecated, "
+                            "use '-rtc base=localtime' instead.");
                 break;
             case QEMU_OPTION_vga:
                 vga_model = optarg;
@@ -3665,6 +3670,8 @@ int main(int argc, char **argv, char **envp)
                 };
 
                 qdev_prop_register_global(&slew_lost_ticks);
+                warn_report("This option is deprecated, "
+                            "use '-rtc driftfix=slew' instead.");
                 break;
             }
             case QEMU_OPTION_acpitable:
@@ -3842,9 +3849,6 @@ int main(int argc, char **argv, char **envp)
                     exit(1);
                 }
                 break;
-            case QEMU_OPTION_tdf:
-                warn_report("ignoring deprecated option");
-                break;
             case QEMU_OPTION_name:
                 opts = qemu_opts_parse_noisily(qemu_find_opts("name"),
                                                optarg, true);
@@ -3869,6 +3873,7 @@ int main(int argc, char **argv, char **envp)
                  */
                 break;
             case QEMU_OPTION_startdate:
+                warn_report("This option is deprecated, use '-rtc base=' instead.");
                 configure_rtc_date_offset(optarg, 1);
                 break;
             case QEMU_OPTION_rtc:
@@ -4624,15 +4629,6 @@ int main(int argc, char **argv, char **envp)
 
     rom_reset_order_override();
 
-    /*
-     * Create frontends for -drive if=scsi leftovers.
-     * Normally, frontends for -drive get created by machine
-     * initialization for onboard SCSI HBAs.  However, we create a few
-     * more ever since SCSI qdevification, but this is pretty much an
-     * implementation accident, and deprecated.
-     */
-    scsi_legacy_handle_cmdline();
-
     /* Did we create any drives that we failed to create a device for? */
     drive_check_orphaned();
 
@@ -4726,17 +4722,10 @@ int main(int argc, char **argv, char **envp)
     os_setup_post();
 
     main_loop();
-    replay_disable_events();
-
-    /* The ordering of the following is delicate.  Stop vcpus to prevent new
-     * I/O requests being queued by the guest.  Then stop IOThreads (this
-     * includes a drain operation and completes all request processing).  At
-     * this point emulated devices are still associated with their IOThreads
-     * (if any) but no longer have any work to do.  Only then can we close
-     * block devices safely because we know there is no more I/O coming.
-     */
-    pause_all_vcpus();
-    iothread_stop_all();
+
+    /* No more vcpu or device emulation activity beyond this point */
+    vm_shutdown();
+
     bdrv_close_all();
 
     res_free();