aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--MAINTAINERS11
-rw-r--r--Makefile32
-rw-r--r--Makefile.objs10
-rw-r--r--Makefile.target1
-rw-r--r--balloon.c72
-rw-r--r--balloon.h6
-rw-r--r--block.c252
-rw-r--r--block.h5
-rw-r--r--block/iscsi.c591
-rw-r--r--block/qcow.c30
-rw-r--r--block/qcow2.c2
-rw-r--r--block/qed.c7
-rw-r--r--block/vdi.c23
-rw-r--r--block/vmdk.c31
-rw-r--r--block_int.h4
-rwxr-xr-xconfigure33
-rw-r--r--console.h9
-rw-r--r--cpus.c45
-rw-r--r--device_tree.c1
-rw-r--r--docs/specs/qcow2.txt6
-rw-r--r--error.c3
-rw-r--r--exec-all.h3
-rw-r--r--exec.c1
-rw-r--r--fsdev/file-op-9p.h36
-rw-r--r--fsdev/qemu-fsdev.c8
-rw-r--r--fsdev/qemu-fsdev.h1
-rw-r--r--gdbstub.c35
-rw-r--r--hmp-commands.hx6
-rw-r--r--hmp.c407
-rw-r--r--hmp.h10
-rw-r--r--hw/9pfs/codir.c16
-rw-r--r--hw/9pfs/cofile.c37
-rw-r--r--hw/9pfs/virtio-9p-coth.h6
-rw-r--r--hw/9pfs/virtio-9p-handle.c96
-rw-r--r--hw/9pfs/virtio-9p-local.c113
-rw-r--r--hw/9pfs/virtio-9p-synth.c571
-rw-r--r--hw/9pfs/virtio-9p-synth.h50
-rw-r--r--hw/9pfs/virtio-9p.c122
-rw-r--r--hw/9pfs/virtio-9p.h27
-rw-r--r--hw/acpi_piix4.c4
-rw-r--r--hw/audiodev.h2
-rw-r--r--hw/esp.c16
-rw-r--r--hw/i2c.c2
-rw-r--r--hw/ide/ahci.c16
-rw-r--r--hw/ide/atapi.c119
-rw-r--r--hw/ide/core.c6
-rw-r--r--hw/ide/internal.h71
-rw-r--r--hw/ide/macio.c2
-rw-r--r--hw/intel-hda.c6
-rw-r--r--hw/lm4549.c336
-rw-r--r--hw/lm4549.h43
-rw-r--r--hw/lsi53c895a.c30
-rw-r--r--hw/pci-stub.c15
-rw-r--r--hw/pci.c322
-rw-r--r--hw/pci.h4
-rw-r--r--hw/pl041.c636
-rw-r--r--hw/pl041.h135
-rw-r--r--hw/pl041.hx81
-rw-r--r--hw/qdev.c24
-rw-r--r--hw/qdev.h4
-rw-r--r--hw/qxl.c66
-rw-r--r--hw/qxl.h3
-rw-r--r--hw/realview.c8
-rw-r--r--hw/s390-virtio-bus.c4
-rw-r--r--hw/scsi-bus.c279
-rw-r--r--hw/scsi-defs.h90
-rw-r--r--hw/scsi-disk.c824
-rw-r--r--hw/scsi-generic.c201
-rw-r--r--hw/scsi.h39
-rw-r--r--hw/spapr_vio.c6
-rw-r--r--hw/spapr_vscsi.c54
-rw-r--r--hw/ssi.c6
-rw-r--r--hw/usb-msd.c8
-rw-r--r--hw/versatilepb.c8
-rw-r--r--hw/vexpress.c7
-rw-r--r--hw/virtio-balloon.c78
-rw-r--r--input.c64
-rw-r--r--kvm-all.c10
-rw-r--r--libcacard/cac.c3
-rw-r--r--libcacard/card_7816.c2
-rw-r--r--libcacard/vscclient.c9
-rw-r--r--linux-user/signal.c28
-rw-r--r--migration.c93
-rw-r--r--migration.h2
-rw-r--r--monitor.c415
-rw-r--r--monitor.h2
-rw-r--r--qapi-schema.json616
-rw-r--r--qemu-config.c7
-rw-r--r--qemu-doc.texi2
-rw-r--r--qemu-io.c5
-rw-r--r--qemu-option.c39
-rw-r--r--qemu-option.h3
-rw-r--r--qemu-options.hx113
-rw-r--r--qemu-queue.h13
-rw-r--r--qemu-thread.h3
-rw-r--r--qerror.c4
-rw-r--r--qmp-commands.hx63
-rw-r--r--qmp.c27
-rwxr-xr-xscripts/analyse-9p-simpletrace.py164
-rwxr-xr-xscripts/kvm/kvm_stat480
-rwxr-xr-xscripts/kvm/vmxcap224
-rw-r--r--scripts/qapi-commands.py4
-rw-r--r--target-i386/cpu.h7
-rw-r--r--target-i386/helper.c1
-rw-r--r--target-i386/kvm.c15
-rw-r--r--target-i386/machine.c43
-rw-r--r--target-i386/op_helper.c6
-rw-r--r--target-sparc/cpu.h8
-rw-r--r--target-sparc/cpu_init.c6
-rw-r--r--target-sparc/fop_helper.c294
-rw-r--r--target-sparc/helper.h122
-rw-r--r--target-sparc/ldst_helper.c123
-rw-r--r--target-sparc/machine.c20
-rw-r--r--target-sparc/translate.c1460
-rw-r--r--target-sparc/vis_helper.c251
-rw-r--r--tcg/tcg-op.h65
-rw-r--r--trace-events9
-rw-r--r--ui/qemu-spice.h14
-rw-r--r--ui/spice-core.c217
-rw-r--r--ui/vnc.c135
-rw-r--r--vl.c22
122 files changed, 8351 insertions, 3137 deletions
diff --git a/.gitignore b/.gitignore
index 59c343c414..6d2acab09a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ libdis*
libhw32
libhw64
libuser
+linux-headers/asm
qapi-generated
qemu-doc.html
qemu-tech.html
diff --git a/MAINTAINERS b/MAINTAINERS
index 2b4c5d727e..4535eeb61f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -118,7 +118,7 @@ F: target-i386/
Xtensa
M: Max Filippov <jcmvbkbc@gmail.com>
-W: http://kkv.spb.su/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
+W: http://wiki.osll.spb.ru/doku.php?id=etc:users:jcmvbkbc:qemu-target-xtensa
S: Maintained
F: target-xtensa/
@@ -348,10 +348,15 @@ F: hw/pc.[ch] hw/pc_piix.c
Xtensa Machines
---------------
-DC232B
+sim
M: Max Filippov <jcmvbkbc@gmail.com>
S: Maintained
-F: hw/xtensa_dc232b.c
+F: hw/xtensa_sim.c
+
+Avnet LX60
+M: Max Filippov <jcmvbkbc@gmail.com>
+S: Maintained
+F: hw/xtensa_lx60.c
Devices
-------
diff --git a/Makefile b/Makefile
index f63fc02ca3..ba8d738d9b 100644
--- a/Makefile
+++ b/Makefile
@@ -146,27 +146,25 @@ endif
qemu-img.o: qemu-img-cmds.h
qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
-tools-obj-y = qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) \
- $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
+tools-obj-y = qemu-tool.o $(oslib-obj-y) $(trace-obj-y) \
+ qemu-timer-common.o cutils.o
-qemu-img$(EXESUF): qemu-img.o $(tools-obj-y)
-qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y)
-qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y)
+qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y)
+qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y)
+qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y)
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@")
check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o test-coroutine.o: $(GENERATED_HEADERS)
-CHECK_PROG_DEPS = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o
-
-check-qint: check-qint.o qint.o $(CHECK_PROG_DEPS)
-check-qstring: check-qstring.o qstring.o $(CHECK_PROG_DEPS)
-check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(CHECK_PROG_DEPS)
-check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
-check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
-check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o error.o qerror.o qemu-error.o $(CHECK_PROG_DEPS)
-test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(CHECK_PROG_DEPS)
+check-qint: check-qint.o qint.o $(tools-obj-y)
+check-qstring: check-qstring.o qstring.o $(tools-obj-y)
+check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(tools-obj-y)
+check-qlist: check-qlist.o qlist.o qint.o $(tools-obj-y)
+check-qfloat: check-qfloat.o qfloat.o $(tools-obj-y)
+check-qjson: check-qjson.o $(qobject-obj-y) $(tools-obj-y)
+test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(tools-obj-y)
$(qapi-obj-y): $(GENERATED_HEADERS)
qapi-dir := qapi-generated
@@ -204,16 +202,16 @@ qmp-marshal.c: $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -m -o "." < $<, " GEN $@")
test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
-test-visitor: test-visitor.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
+test-visitor: test-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
-test-qmp-commands: test-qmp-commands.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
+test-qmp-commands: test-qmp-commands.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
QGALIB_GEN=$(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c)
$(QGALIB_GEN): $(GENERATED_HEADERS)
$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
-qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(qapi-obj-y) $(trace-obj-y) $(qobject-obj-y) $(version-obj-y) $(addprefix $(qapi-dir)/, qga-qapi-visit.o qga-qapi-types.o qga-qmp-marshal.o)
+qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qobject-obj-y) $(version-obj-y) $(addprefix $(qapi-dir)/, qga-qapi-visit.o qga-qapi-types.o qga-qmp-marshal.o)
QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
diff --git a/Makefile.objs b/Makefile.objs
index 01587c8f8f..d7a65393b3 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -2,7 +2,7 @@
# QObject
qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
-qobject-obj-y += qerror.o error.o
+qobject-obj-y += qerror.o error.o qemu-error.o
#######################################################################
# oslib-obj-y is code depending on the OS (win32 vs posix)
@@ -25,7 +25,7 @@ coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
block-obj-y = cutils.o cache-utils.o qemu-option.o module.o async.o
block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o qemu-progress.o qemu-sockets.o
-block-obj-y += $(coroutine-obj-y)
+block-obj-y += $(coroutine-obj-y) $(qobject-obj-y) $(version-obj-y)
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
@@ -36,6 +36,7 @@ block-nested-y += qed-check.o
block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
block-nested-$(CONFIG_WIN32) += raw-win32.o
block-nested-$(CONFIG_POSIX) += raw-posix.o
+block-nested-$(CONFIG_LIBISCSI) += iscsi.o
block-nested-$(CONFIG_CURL) += curl.o
block-nested-$(CONFIG_RBD) += rbd.o
@@ -76,7 +77,7 @@ common-obj-y = $(block-obj-y) blockdev.o
common-obj-y += $(net-obj-y)
common-obj-y += $(qobject-obj-y)
common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
-common-obj-y += readline.o console.o cursor.o qemu-error.o
+common-obj-y += readline.o console.o cursor.o
common-obj-y += $(oslib-obj-y)
common-obj-$(CONFIG_WIN32) += os-win32.o
common-obj-$(CONFIG_POSIX) += os-posix.o
@@ -310,6 +311,7 @@ hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-coth.o cofs.o codir.o cofile.o
9pfs-nested-$(CONFIG_VIRTFS) += coxattr.o virtio-9p-handle.o
+9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-synth.o
hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
$(addprefix 9pfs/, $(9pfs-nested-y)): QEMU_CFLAGS+=$(GLIB_CFLAGS)
@@ -415,7 +417,7 @@ common-obj-y += qmp.o hmp.o
qga-nested-y = guest-agent-commands.o guest-agent-command-state.o
qga-obj-y = $(addprefix qga/, $(qga-nested-y))
-qga-obj-y += qemu-ga.o qemu-tool.o qemu-error.o qemu-sockets.o module.o qemu-option.o cutils.o osdep.o
+qga-obj-y += qemu-ga.o qemu-sockets.o module.o qemu-option.o
qga-obj-$(CONFIG_WIN32) += oslib-win32.o
qga-obj-$(CONFIG_POSIX) += oslib-posix.o
diff --git a/Makefile.target b/Makefile.target
index fe5f6f70af..530c1d1e63 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -362,6 +362,7 @@ obj-arm-y += syborg_virtio.o
obj-arm-y += vexpress.o
obj-arm-y += strongarm.o
obj-arm-y += collie.o
+obj-arm-y += pl041.o lm4549.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
diff --git a/balloon.c b/balloon.c
index a2133dba75..e1cd5fac4c 100644
--- a/balloon.c
+++ b/balloon.c
@@ -25,12 +25,11 @@
*/
#include "monitor.h"
-#include "qjson.h"
-#include "qint.h"
#include "cpu-common.h"
#include "kvm.h"
#include "balloon.h"
#include "trace.h"
+#include "qmp-commands.h"
static QEMUBalloonEvent *balloon_event_fn;
static QEMUBalloonStatus *balloon_stat_fn;
@@ -72,76 +71,33 @@ static int qemu_balloon(ram_addr_t target)
return 1;
}
-static int qemu_balloon_status(MonitorCompletion cb, void *opaque)
+static int qemu_balloon_status(BalloonInfo *info)
{
if (!balloon_stat_fn) {
return 0;
}
- balloon_stat_fn(balloon_opaque, cb, opaque);
+ balloon_stat_fn(balloon_opaque, info);
return 1;
}
-static void print_balloon_stat(const char *key, QObject *obj, void *opaque)
+BalloonInfo *qmp_query_balloon(Error **errp)
{
- Monitor *mon = opaque;
-
- if (strcmp(key, "actual")) {
- monitor_printf(mon, ",%s=%" PRId64, key,
- qint_get_int(qobject_to_qint(obj)));
- }
-}
-
-void monitor_print_balloon(Monitor *mon, const QObject *data)
-{
- QDict *qdict;
-
- qdict = qobject_to_qdict(data);
- if (!qdict_haskey(qdict, "actual")) {
- return;
- }
- monitor_printf(mon, "balloon: actual=%" PRId64,
- qdict_get_int(qdict, "actual") >> 20);
- qdict_iter(qdict, print_balloon_stat, mon);
- monitor_printf(mon, "\n");
-}
-
-/**
- * do_info_balloon(): Balloon information
- *
- * Make an asynchronous request for balloon info. When the request completes
- * a QDict will be returned according to the following specification:
- *
- * - "actual": current balloon value in bytes
- * The following fields may or may not be present:
- * - "mem_swapped_in": Amount of memory swapped in (bytes)
- * - "mem_swapped_out": Amount of memory swapped out (bytes)
- * - "major_page_faults": Number of major faults
- * - "minor_page_faults": Number of minor faults
- * - "free_mem": Total amount of free and unused memory (bytes)
- * - "total_mem": Total amount of available memory (bytes)
- *
- * Example:
- *
- * { "actual": 1073741824, "mem_swapped_in": 0, "mem_swapped_out": 0,
- * "major_page_faults": 142, "minor_page_faults": 239245,
- * "free_mem": 1014185984, "total_mem": 1044668416 }
- */
-int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque)
-{
- int ret;
+ BalloonInfo *info;
if (kvm_enabled() && !kvm_has_sync_mmu()) {
- qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
- return -1;
+ error_set(errp, QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
+ return NULL;
}
- ret = qemu_balloon_status(cb, opaque);
- if (!ret) {
- qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
- return -1;
+ info = g_malloc0(sizeof(*info));
+
+ if (qemu_balloon_status(info) == 0) {
+ error_set(errp, QERR_DEVICE_NOT_ACTIVE, "balloon");
+ qapi_free_BalloonInfo(info);
+ return NULL;
}
- return 0;
+ return info;
}
/**
diff --git a/balloon.h b/balloon.h
index f59e2881f6..b36abeadf0 100644
--- a/balloon.h
+++ b/balloon.h
@@ -15,17 +15,15 @@
#define _QEMU_BALLOON_H
#include "monitor.h"
+#include "qapi-types.h"
typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target);
-typedef void (QEMUBalloonStatus)(void *opaque, MonitorCompletion cb,
- void *cb_data);
+typedef void (QEMUBalloonStatus)(void *opaque, BalloonInfo *info);
int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
QEMUBalloonStatus *stat_func, void *opaque);
void qemu_remove_balloon_handler(void *opaque);
-void monitor_print_balloon(Monitor *mon, const QObject *data);
-int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque);
int do_balloon(Monitor *mon, const QDict *params,
MonitorCompletion cb, void *opaque);
diff --git a/block.c b/block.c
index 70aab63e4b..9bb236c989 100644
--- a/block.c
+++ b/block.c
@@ -27,8 +27,9 @@
#include "monitor.h"
#include "block_int.h"
#include "module.h"
-#include "qemu-objects.h"
+#include "qjson.h"
#include "qemu-coroutine.h"
+#include "qmp-commands.h"
#ifdef CONFIG_BSD
#include <sys/types.h>
@@ -472,10 +473,13 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
bs->total_sectors = 0;
bs->encrypted = 0;
bs->valid_key = 0;
+ bs->sg = 0;
bs->open_flags = flags;
+ bs->growable = 0;
bs->buffer_alignment = 512;
pstrcpy(bs->filename, sizeof(bs->filename), filename);
+ bs->backing_file[0] = '\0';
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) {
return -ENOTSUP;
@@ -484,8 +488,7 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
bs->drv = drv;
bs->opaque = g_malloc0(drv->instance_size);
- if (flags & BDRV_O_CACHE_WB)
- bs->enable_write_cache = 1;
+ bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
/*
* Clear flags that are internal to the block layer before opening the
@@ -500,6 +503,8 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
open_flags |= BDRV_O_RDWR;
}
+ bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
+
/* Open the image, either directly or using a protocol */
if (drv->bdrv_file_open) {
ret = drv->bdrv_file_open(bs, filename, open_flags);
@@ -514,8 +519,6 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
goto free_and_fail;
}
- bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR);
-
ret = refresh_total_sectors(bs, bs->total_sectors);
if (ret < 0) {
goto free_and_fail;
@@ -571,6 +574,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
BlockDriver *drv)
{
int ret;
+ char tmp_filename[PATH_MAX];
if (flags & BDRV_O_SNAPSHOT) {
BlockDriverState *bs1;
@@ -578,7 +582,6 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
int is_protocol = 0;
BlockDriver *bdrv_qcow2;
QEMUOptionParameter *options;
- char tmp_filename[PATH_MAX];
char backing_filename[PATH_MAX];
/* if snapshot, we create a temporary backing file and open it
@@ -1824,195 +1827,105 @@ void bdrv_mon_event(const BlockDriverState *bdrv,
qobject_decref(data);
}
-static void bdrv_print_dict(QObject *obj, void *opaque)
-{
- QDict *bs_dict;
- Monitor *mon = opaque;
-
- bs_dict = qobject_to_qdict(obj);
-
- monitor_printf(mon, "%s: removable=%d",
- qdict_get_str(bs_dict, "device"),
- qdict_get_bool(bs_dict, "removable"));
-
- if (qdict_get_bool(bs_dict, "removable")) {
- monitor_printf(mon, " locked=%d", qdict_get_bool(bs_dict, "locked"));
- monitor_printf(mon, " tray-open=%d",
- qdict_get_bool(bs_dict, "tray-open"));
- }
-
- if (qdict_haskey(bs_dict, "io-status")) {
- monitor_printf(mon, " io-status=%s", qdict_get_str(bs_dict, "io-status"));
- }
-
- if (qdict_haskey(bs_dict, "inserted")) {
- QDict *qdict = qobject_to_qdict(qdict_get(bs_dict, "inserted"));
-
- monitor_printf(mon, " file=");
- monitor_print_filename(mon, qdict_get_str(qdict, "file"));
- if (qdict_haskey(qdict, "backing_file")) {
- monitor_printf(mon, " backing_file=");
- monitor_print_filename(mon, qdict_get_str(qdict, "backing_file"));
- }
- monitor_printf(mon, " ro=%d drv=%s encrypted=%d",
- qdict_get_bool(qdict, "ro"),
- qdict_get_str(qdict, "drv"),
- qdict_get_bool(qdict, "encrypted"));
- } else {
- monitor_printf(mon, " [not inserted]");
- }
-
- monitor_printf(mon, "\n");
-}
-
-void bdrv_info_print(Monitor *mon, const QObject *data)
-{
- qlist_iter(qobject_to_qlist(data), bdrv_print_dict, mon);
-}
-
-static const char *const io_status_name[BDRV_IOS_MAX] = {
- [BDRV_IOS_OK] = "ok",
- [BDRV_IOS_FAILED] = "failed",
- [BDRV_IOS_ENOSPC] = "nospace",
-};
-
-void bdrv_info(Monitor *mon, QObject **ret_data)
+BlockInfoList *qmp_query_block(Error **errp)
{
- QList *bs_list;
+ BlockInfoList *head = NULL, *cur_item = NULL;
BlockDriverState *bs;
- bs_list = qlist_new();
-
QTAILQ_FOREACH(bs, &bdrv_states, list) {
- QObject *bs_obj;
- QDict *bs_dict;
+ BlockInfoList *info = g_malloc0(sizeof(*info));
- bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': 'unknown', "
- "'removable': %i, 'locked': %i }",
- bs->device_name,
- bdrv_dev_has_removable_media(bs),
- bdrv_dev_is_medium_locked(bs));
- bs_dict = qobject_to_qdict(bs_obj);
+ info->value = g_malloc0(sizeof(*info->value));
+ info->value->device = g_strdup(bs->device_name);
+ info->value->type = g_strdup("unknown");
+ info->value->locked = bdrv_dev_is_medium_locked(bs);
+ info->value->removable = bdrv_dev_has_removable_media(bs);
if (bdrv_dev_has_removable_media(bs)) {
- qdict_put(bs_dict, "tray-open",
- qbool_from_int(bdrv_dev_is_tray_open(bs)));
+ info->value->has_tray_open = true;
+ info->value->tray_open = bdrv_dev_is_tray_open(bs);
}
if (bdrv_iostatus_is_enabled(bs)) {
- qdict_put(bs_dict, "io-status",
- qstring_from_str(io_status_name[bs->iostatus]));
+ info->value->has_io_status = true;
+ info->value->io_status = bs->iostatus;
}
if (bs->drv) {
- QObject *obj;
-
- obj = qobject_from_jsonf("{ 'file': %s, 'ro': %i, 'drv': %s, "
- "'encrypted': %i }",
- bs->filename, bs->read_only,
- bs->drv->format_name,
- bdrv_is_encrypted(bs));
- if (bs->backing_file[0] != '\0') {
- QDict *qdict = qobject_to_qdict(obj);
- qdict_put(qdict, "backing_file",
- qstring_from_str(bs->backing_file));
+ info->value->has_inserted = true;
+ info->value->inserted = g_malloc0(sizeof(*info->value->inserted));
+ info->value->inserted->file = g_strdup(bs->filename);
+ info->value->inserted->ro = bs->read_only;
+ info->value->inserted->drv = g_strdup(bs->drv->format_name);
+ info->value->inserted->encrypted = bs->encrypted;
+ if (bs->backing_file[0]) {
+ info->value->inserted->has_backing_file = true;
+ info->value->inserted->backing_file = g_strdup(bs->backing_file);
}
+ }
- qdict_put_obj(bs_dict, "inserted", obj);
+ /* XXX: waiting for the qapi to support GSList */
+ if (!cur_item) {
+ head = cur_item = info;
+ } else {
+ cur_item->next = info;
+ cur_item = info;
}
- qlist_append_obj(bs_list, bs_obj);
}
- *ret_data = QOBJECT(bs_list);
+ return head;
}
-static void bdrv_stats_iter(QObject *data, void *opaque)
+/* Consider exposing this as a full fledged QMP command */
+static BlockStats *qmp_query_blockstat(const BlockDriverState *bs, Error **errp)
{
- QDict *qdict;
- Monitor *mon = opaque;
+ BlockStats *s;
- qdict = qobject_to_qdict(data);
- monitor_printf(mon, "%s:", qdict_get_str(qdict, "device"));
-
- qdict = qobject_to_qdict(qdict_get(qdict, "stats"));
- monitor_printf(mon, " rd_bytes=%" PRId64
- " wr_bytes=%" PRId64
- " rd_operations=%" PRId64
- " wr_operations=%" PRId64
- " flush_operations=%" PRId64
- " wr_total_time_ns=%" PRId64
- " rd_total_time_ns=%" PRId64
- " flush_total_time_ns=%" PRId64
- "\n",
- qdict_get_int(qdict, "rd_bytes"),
- qdict_get_int(qdict, "wr_bytes"),
- qdict_get_int(qdict, "rd_operations"),
- qdict_get_int(qdict, "wr_operations"),
- qdict_get_int(qdict, "flush_operations"),
- qdict_get_int(qdict, "wr_total_time_ns"),
- qdict_get_int(qdict, "rd_total_time_ns"),
- qdict_get_int(qdict, "flush_total_time_ns"));
-}
-
-void bdrv_stats_print(Monitor *mon, const QObject *data)
-{
- qlist_iter(qobject_to_qlist(data), bdrv_stats_iter, mon);
-}
+ s = g_malloc0(sizeof(*s));
-static QObject* bdrv_info_stats_bs(BlockDriverState *bs)
-{
- QObject *res;
- QDict *dict;
-
- res = qobject_from_jsonf("{ 'stats': {"
- "'rd_bytes': %" PRId64 ","
- "'wr_bytes': %" PRId64 ","
- "'rd_operations': %" PRId64 ","
- "'wr_operations': %" PRId64 ","
- "'wr_highest_offset': %" PRId64 ","
- "'flush_operations': %" PRId64 ","
- "'wr_total_time_ns': %" PRId64 ","
- "'rd_total_time_ns': %" PRId64 ","
- "'flush_total_time_ns': %" PRId64
- "} }",
- bs->nr_bytes[BDRV_ACCT_READ],
- bs->nr_bytes[BDRV_ACCT_WRITE],
- bs->nr_ops[BDRV_ACCT_READ],
- bs->nr_ops[BDRV_ACCT_WRITE],
- bs->wr_highest_sector *
- (uint64_t)BDRV_SECTOR_SIZE,
- bs->nr_ops[BDRV_ACCT_FLUSH],
- bs->total_time_ns[BDRV_ACCT_WRITE],
- bs->total_time_ns[BDRV_ACCT_READ],
- bs->total_time_ns[BDRV_ACCT_FLUSH]);
- dict = qobject_to_qdict(res);
-
- if (*bs->device_name) {
- qdict_put(dict, "device", qstring_from_str(bs->device_name));
+ if (bs->device_name[0]) {
+ s->has_device = true;
+ s->device = g_strdup(bs->device_name);
}
+ s->stats = g_malloc0(sizeof(*s->stats));
+ s->stats->rd_bytes = bs->nr_bytes[BDRV_ACCT_READ];
+ s->stats->wr_bytes = bs->nr_bytes[BDRV_ACCT_WRITE];
+ s->stats->rd_operations = bs->nr_ops[BDRV_ACCT_READ];
+ s->stats->wr_operations = bs->nr_ops[BDRV_ACCT_WRITE];
+ s->stats->wr_highest_offset = bs->wr_highest_sector * BDRV_SECTOR_SIZE;
+ s->stats->flush_operations = bs->nr_ops[BDRV_ACCT_FLUSH];
+ s->stats->wr_total_time_ns = bs->total_time_ns[BDRV_ACCT_WRITE];
+ s->stats->rd_total_time_ns = bs->total_time_ns[BDRV_ACCT_READ];
+ s->stats->flush_total_time_ns = bs->total_time_ns[BDRV_ACCT_FLUSH];
+
if (bs->file) {
- QObject *parent = bdrv_info_stats_bs(bs->file);
- qdict_put_obj(dict, "parent", parent);
+ s->has_parent = true;
+ s->parent = qmp_query_blockstat(bs->file, NULL);
}
- return res;
+ return s;
}
-void bdrv_info_stats(Monitor *mon, QObject **ret_data)
+BlockStatsList *qmp_query_blockstats(Error **errp)
{
- QObject *obj;
- QList *devices;
+ BlockStatsList *head = NULL, *cur_item = NULL;
BlockDriverState *bs;
- devices = qlist_new();
-
QTAILQ_FOREACH(bs, &bdrv_states, list) {
- obj = bdrv_info_stats_bs(bs);
- qlist_append_obj(devices, obj);
+ BlockStatsList *info = g_malloc0(sizeof(*info));
+ info->value = qmp_query_blockstat(bs, NULL);
+
+ /* XXX: waiting for the qapi to support GSList */
+ if (!cur_item) {
+ head = cur_item = info;
+ } else {
+ cur_item->next = info;
+ cur_item = info;
+ }
}
- *ret_data = QOBJECT(devices);
+ return head;
}
const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
@@ -2028,11 +1941,7 @@ const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
void bdrv_get_backing_filename(BlockDriverState *bs,
char *filename, int filename_size)
{
- if (!bs->backing_file) {
- pstrcpy(filename, filename_size, "");
- } else {
- pstrcpy(filename, filename_size, bs->backing_file);
- }
+ pstrcpy(filename, filename_size, bs->backing_file);
}
int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
@@ -3139,14 +3048,15 @@ int bdrv_in_use(BlockDriverState *bs)
void bdrv_iostatus_enable(BlockDriverState *bs)
{
- bs->iostatus = BDRV_IOS_OK;
+ bs->iostatus_enabled = true;
+ bs->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
}
/* The I/O status is only enabled if the drive explicitly
* enables it _and_ the VM is configured to stop on errors */
bool bdrv_iostatus_is_enabled(const BlockDriverState *bs)
{
- return (bs->iostatus != BDRV_IOS_INVAL &&
+ return (bs->iostatus_enabled &&
(bs->on_write_error == BLOCK_ERR_STOP_ENOSPC ||
bs->on_write_error == BLOCK_ERR_STOP_ANY ||
bs->on_read_error == BLOCK_ERR_STOP_ANY));
@@ -3154,13 +3064,13 @@ bool bdrv_iostatus_is_enabled(const BlockDriverState *bs)
void bdrv_iostatus_disable(BlockDriverState *bs)
{
- bs->iostatus = BDRV_IOS_INVAL;
+ bs->iostatus_enabled = false;
}
void bdrv_iostatus_reset(BlockDriverState *bs)
{
if (bdrv_iostatus_is_enabled(bs)) {
- bs->iostatus = BDRV_IOS_OK;
+ bs->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
}
}
@@ -3169,9 +3079,11 @@ void bdrv_iostatus_reset(BlockDriverState *bs)
possible to implement this without device models being involved */
void bdrv_iostatus_set_err(BlockDriverState *bs, int error)
{
- if (bdrv_iostatus_is_enabled(bs) && bs->iostatus == BDRV_IOS_OK) {
+ if (bdrv_iostatus_is_enabled(bs) &&
+ bs->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
assert(error >= 0);
- bs->iostatus = error == ENOSPC ? BDRV_IOS_ENOSPC : BDRV_IOS_FAILED;
+ bs->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE :
+ BLOCK_DEVICE_IO_STATUS_FAILED;
}
}
diff --git a/block.h b/block.h
index 5a042c96e7..38cd748b60 100644
--- a/block.h
+++ b/block.h
@@ -77,11 +77,6 @@ typedef enum {
BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
} BlockMonEventAction;
-typedef enum {
- BDRV_IOS_INVAL, BDRV_IOS_OK, BDRV_IOS_FAILED, BDRV_IOS_ENOSPC,
- BDRV_IOS_MAX
-} BlockIOStatus;
-
void bdrv_iostatus_enable(BlockDriverState *bs);
void bdrv_iostatus_reset(BlockDriverState *bs);
void bdrv_iostatus_disable(BlockDriverState *bs);
diff --git a/block/iscsi.c b/block/iscsi.c
new file mode 100644
index 0000000000..938c568071
--- /dev/null
+++ b/block/iscsi.c
@@ -0,0 +1,591 @@
+/*
+ * QEMU Block driver for iSCSI images
+ *
+ * Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.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 "config-host.h"
+
+#include <poll.h>
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "block_int.h"
+#include "trace.h"
+
+#include <iscsi/iscsi.h>
+#include <iscsi/scsi-lowlevel.h>
+
+
+typedef struct IscsiLun {
+ struct iscsi_context *iscsi;
+ int lun;
+ int block_size;
+ unsigned long num_blocks;
+} IscsiLun;
+
+typedef struct IscsiAIOCB {
+ BlockDriverAIOCB common;
+ QEMUIOVector *qiov;
+ QEMUBH *bh;
+ IscsiLun *iscsilun;
+ struct scsi_task *task;
+ uint8_t *buf;
+ int status;
+ int canceled;
+ size_t read_size;
+ size_t read_offset;
+} IscsiAIOCB;
+
+struct IscsiTask {
+ IscsiLun *iscsilun;
+ BlockDriverState *bs;
+ int status;
+ int complete;
+};
+
+static void
+iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data,
+ void *private_data)
+{
+}
+
+static void
+iscsi_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+ IscsiAIOCB *acb = (IscsiAIOCB *)blockacb;
+ IscsiLun *iscsilun = acb->iscsilun;
+
+ acb->common.cb(acb->common.opaque, -ECANCELED);
+ acb->canceled = 1;
+
+ /* send a task mgmt call to the target to cancel the task on the target */
+ iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task,
+ iscsi_abort_task_cb, NULL);
+
+ /* then also cancel the task locally in libiscsi */
+ iscsi_scsi_task_cancel(iscsilun->iscsi, acb->task);
+}
+
+static AIOPool iscsi_aio_pool = {
+ .aiocb_size = sizeof(IscsiAIOCB),
+ .cancel = iscsi_aio_cancel,
+};
+
+
+static void iscsi_process_read(void *arg);
+static void iscsi_process_write(void *arg);
+
+static int iscsi_process_flush(void *arg)
+{
+ IscsiLun *iscsilun = arg;
+
+ return iscsi_queue_length(iscsilun->iscsi) > 0;
+}
+
+static void
+iscsi_set_events(IscsiLun *iscsilun)
+{
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+
+ qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read,
+ (iscsi_which_events(iscsi) & POLLOUT)
+ ? iscsi_process_write : NULL,
+ iscsi_process_flush, NULL, iscsilun);
+}
+
+static void
+iscsi_process_read(void *arg)
+{
+ IscsiLun *iscsilun = arg;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+
+ iscsi_service(iscsi, POLLIN);
+ iscsi_set_events(iscsilun);
+}
+
+static void
+iscsi_process_write(void *arg)
+{
+ IscsiLun *iscsilun = arg;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+
+ iscsi_service(iscsi, POLLOUT);
+ iscsi_set_events(iscsilun);
+}
+
+
+static int
+iscsi_schedule_bh(QEMUBHFunc *cb, IscsiAIOCB *acb)
+{
+ acb->bh = qemu_bh_new(cb, acb);
+ if (!acb->bh) {
+ error_report("oom: could not create iscsi bh");
+ return -EIO;
+ }
+
+ qemu_bh_schedule(acb->bh);
+ return 0;
+}
+
+static void
+iscsi_readv_writev_bh_cb(void *p)
+{
+ IscsiAIOCB *acb = p;
+
+ qemu_bh_delete(acb->bh);
+
+ if (acb->canceled == 0) {
+ acb->common.cb(acb->common.opaque, acb->status);
+ }
+
+ qemu_aio_release(acb);
+}
+
+
+static void
+iscsi_aio_write10_cb(struct iscsi_context *iscsi, int status,
+ void *command_data, void *opaque)
+{
+ IscsiAIOCB *acb = opaque;
+
+ trace_iscsi_aio_write10_cb(iscsi, status, acb, acb->canceled);
+
+ g_free(acb->buf);
+
+ if (acb->canceled != 0) {
+ qemu_aio_release(acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+ return;
+ }
+
+ acb->status = 0;
+ if (status < 0) {
+ error_report("Failed to write10 data to iSCSI lun. %s",
+ iscsi_get_error(iscsi));
+ acb->status = -EIO;
+ }
+
+ iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+}
+
+static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
+{
+ return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_writev(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+ IscsiAIOCB *acb;
+ size_t size;
+ int fua = 0;
+
+ /* set FUA on writes when cache mode is write through */
+ if (!(bs->open_flags & BDRV_O_CACHE_WB)) {
+ fua = 1;
+ }
+
+ acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+ trace_iscsi_aio_writev(iscsi, sector_num, nb_sectors, opaque, acb);
+
+ acb->iscsilun = iscsilun;
+ acb->qiov = qiov;
+
+ acb->canceled = 0;
+
+ /* XXX we should pass the iovec to write10 to avoid the extra copy */
+ /* this will allow us to get rid of 'buf' completely */
+ size = nb_sectors * BDRV_SECTOR_SIZE;
+ acb->buf = g_malloc(size);
+ qemu_iovec_to_buffer(acb->qiov, acb->buf);
+ acb->task = iscsi_write10_task(iscsi, iscsilun->lun, acb->buf, size,
+ sector_qemu2lun(sector_num, iscsilun),
+ fua, 0, iscsilun->block_size,
+ iscsi_aio_write10_cb, acb);
+ if (acb->task == NULL) {
+ error_report("iSCSI: Failed to send write10 command. %s",
+ iscsi_get_error(iscsi));
+ g_free(acb->buf);
+ qemu_aio_release(acb);
+ return NULL;
+ }
+
+ iscsi_set_events(iscsilun);
+
+ return &acb->common;
+}
+
+static void
+iscsi_aio_read10_cb(struct iscsi_context *iscsi, int status,
+ void *command_data, void *opaque)
+{
+ IscsiAIOCB *acb = opaque;
+
+ trace_iscsi_aio_read10_cb(iscsi, status, acb, acb->canceled);
+
+ if (acb->canceled != 0) {
+ qemu_aio_release(acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+ return;
+ }
+
+ acb->status = 0;
+ if (status != 0) {
+ error_report("Failed to read10 data from iSCSI lun. %s",
+ iscsi_get_error(iscsi));
+ acb->status = -EIO;
+ }
+
+ iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_readv(BlockDriverState *bs, int64_t sector_num,
+ QEMUIOVector *qiov, int nb_sectors,
+ BlockDriverCompletionFunc *cb,
+ void *opaque)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+ IscsiAIOCB *acb;
+ size_t qemu_read_size, lun_read_size;
+ int i;
+
+ qemu_read_size = BDRV_SECTOR_SIZE * (size_t)nb_sectors;
+
+ acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+ trace_iscsi_aio_readv(iscsi, sector_num, nb_sectors, opaque, acb);
+
+ acb->iscsilun = iscsilun;
+ acb->qiov = qiov;
+
+ acb->canceled = 0;
+ acb->read_size = qemu_read_size;
+ acb->buf = NULL;
+
+ /* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU
+ * may be misaligned to the LUN, so we may need to read some extra
+ * data.
+ */
+ acb->read_offset = 0;
+ if (iscsilun->block_size > BDRV_SECTOR_SIZE) {
+ uint64_t bdrv_offset = BDRV_SECTOR_SIZE * sector_num;
+
+ acb->read_offset = bdrv_offset % iscsilun->block_size;
+ }
+
+ lun_read_size = (qemu_read_size + iscsilun->block_size
+ + acb->read_offset - 1)
+ / iscsilun->block_size * iscsilun->block_size;
+ acb->task = iscsi_read10_task(iscsi, iscsilun->lun,
+ sector_qemu2lun(sector_num, iscsilun),
+ lun_read_size, iscsilun->block_size,
+ iscsi_aio_read10_cb, acb);
+ if (acb->task == NULL) {
+ error_report("iSCSI: Failed to send read10 command. %s",
+ iscsi_get_error(iscsi));
+ qemu_aio_release(acb);
+ return NULL;
+ }
+
+ for (i = 0; i < acb->qiov->niov; i++) {
+ scsi_task_add_data_in_buffer(acb->task,
+ acb->qiov->iov[i].iov_len,
+ acb->qiov->iov[i].iov_base);
+ }
+
+ iscsi_set_events(iscsilun);
+
+ return &acb->common;
+}
+
+
+static void
+iscsi_synccache10_cb(struct iscsi_context *iscsi, int status,
+ void *command_data, void *opaque)
+{
+ IscsiAIOCB *acb = opaque;
+
+ if (acb->canceled != 0) {
+ qemu_aio_release(acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+ return;
+ }
+
+ acb->status = 0;
+ if (status < 0) {
+ error_report("Failed to sync10 data on iSCSI lun. %s",
+ iscsi_get_error(iscsi));
+ acb->status = -EIO;
+ }
+
+ iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
+ scsi_free_scsi_task(acb->task);
+ acb->task = NULL;
+}
+
+static BlockDriverAIOCB *
+iscsi_aio_flush(BlockDriverState *bs,
+ BlockDriverCompletionFunc *cb, void *opaque)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+ IscsiAIOCB *acb;
+
+ acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
+
+ acb->iscsilun = iscsilun;
+ acb->canceled = 0;
+
+ acb->task = iscsi_synchronizecache10_task(iscsi, iscsilun->lun,
+ 0, 0, 0, 0,
+ iscsi_synccache10_cb,
+ acb);
+ if (acb->task == NULL) {
+ error_report("iSCSI: Failed to send synchronizecache10 command. %s",
+ iscsi_get_error(iscsi));
+ qemu_aio_release(acb);
+ return NULL;
+ }
+
+ iscsi_set_events(iscsilun);
+
+ return &acb->common;
+}
+
+static int64_t
+iscsi_getlength(BlockDriverState *bs)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ int64_t len;
+
+ len = iscsilun->num_blocks;
+ len *= iscsilun->block_size;
+
+ return len;
+}
+
+static void
+iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
+ void *command_data, void *opaque)
+{
+ struct IscsiTask *itask = opaque;
+ struct scsi_readcapacity10 *rc10;
+ struct scsi_task *task = command_data;
+
+ if (status != 0) {
+ error_report("iSCSI: Failed to read capacity of iSCSI lun. %s",
+ iscsi_get_error(iscsi));
+ itask->status = 1;
+ itask->complete = 1;
+ scsi_free_scsi_task(task);
+ return;
+ }
+
+ rc10 = scsi_datain_unmarshall(task);
+ if (rc10 == NULL) {
+ error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
+ itask->status = 1;
+ itask->complete = 1;
+ scsi_free_scsi_task(task);
+ return;
+ }
+
+ itask->iscsilun->block_size = rc10->block_size;
+ itask->iscsilun->num_blocks = rc10->lba;
+ itask->bs->total_sectors = (uint64_t)rc10->lba *
+ rc10->block_size / BDRV_SECTOR_SIZE ;
+
+ itask->status = 0;
+ itask->complete = 1;
+ scsi_free_scsi_task(task);
+}
+
+
+static void
+iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
+ void *opaque)
+{
+ struct IscsiTask *itask = opaque;
+ struct scsi_task *task;
+
+ if (status != 0) {
+ itask->status = 1;
+ itask->complete = 1;
+ return;
+ }
+
+ task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, 0, 0,
+ iscsi_readcapacity10_cb, opaque);
+ if (task == NULL) {
+ error_report("iSCSI: failed to send readcapacity command.");
+ itask->status = 1;
+ itask->complete = 1;
+ return;
+ }
+}
+
+/*
+ * We support iscsi url's on the form
+ * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
+ */
+static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = NULL;
+ struct iscsi_url *iscsi_url = NULL;
+ struct IscsiTask task;
+ int ret;
+
+ if ((BDRV_SECTOR_SIZE % 512) != 0) {
+ error_report("iSCSI: Invalid BDRV_SECTOR_SIZE. "
+ "BDRV_SECTOR_SIZE(%lld) is not a multiple "
+ "of 512", BDRV_SECTOR_SIZE);
+ return -EINVAL;
+ }
+
+ memset(iscsilun, 0, sizeof(IscsiLun));
+
+ /* Should really append the KVM name after the ':' here */
+ iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
+ if (iscsi == NULL) {
+ error_report("iSCSI: Failed to create iSCSI context.");
+ ret = -ENOMEM;
+ goto failed;
+ }
+
+ iscsi_url = iscsi_parse_full_url(iscsi, filename);
+ if (iscsi_url == NULL) {
+ error_report("Failed to parse URL : %s %s", filename,
+ iscsi_get_error(iscsi));
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
+ error_report("iSCSI: Failed to set target name.");
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (iscsi_url->user != NULL) {
+ ret = iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user,
+ iscsi_url->passwd);
+ if (ret != 0) {
+ error_report("Failed to set initiator username and password");
+ ret = -EINVAL;
+ goto failed;
+ }
+ }
+ if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
+ error_report("iSCSI: Failed to set session type to normal.");
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
+
+ task.iscsilun = iscsilun;
+ task.status = 0;
+ task.complete = 0;
+ task.bs = bs;
+
+ iscsilun->iscsi = iscsi;
+ iscsilun->lun = iscsi_url->lun;
+
+ if (iscsi_full_connect_async(iscsi, iscsi_url->portal, iscsi_url->lun,
+ iscsi_connect_cb, &task)
+ != 0) {
+ error_report("iSCSI: Failed to start async connect.");
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ while (!task.complete) {
+ iscsi_set_events(iscsilun);
+ qemu_aio_wait();
+ }
+ if (task.status != 0) {
+ error_report("iSCSI: Failed to connect to LUN : %s",
+ iscsi_get_error(iscsi));
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (iscsi_url != NULL) {
+ iscsi_destroy_url(iscsi_url);
+ }
+ return 0;
+
+failed:
+ if (iscsi_url != NULL) {
+ iscsi_destroy_url(iscsi_url);
+ }
+ if (iscsi != NULL) {
+ iscsi_destroy_context(iscsi);
+ }
+ memset(iscsilun, 0, sizeof(IscsiLun));
+ return ret;
+}
+
+static void iscsi_close(BlockDriverState *bs)
+{
+ IscsiLun *iscsilun = bs->opaque;
+ struct iscsi_context *iscsi = iscsilun->iscsi;
+
+ qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL);
+ iscsi_destroy_context(iscsi);
+ memset(iscsilun, 0, sizeof(IscsiLun));
+}
+
+static BlockDriver bdrv_iscsi = {
+ .format_name = "iscsi",
+ .protocol_name = "iscsi",
+
+ .instance_size = sizeof(IscsiLun),
+ .bdrv_file_open = iscsi_open,
+ .bdrv_close = iscsi_close,
+
+ .bdrv_getlength = iscsi_getlength,
+
+ .bdrv_aio_readv = iscsi_aio_readv,
+ .bdrv_aio_writev = iscsi_aio_writev,
+ .bdrv_aio_flush = iscsi_aio_flush,
+};
+
+static void iscsi_block_init(void)
+{
+ bdrv_register(&bdrv_iscsi);
+}
+
+block_init(iscsi_block_init);
diff --git a/block/qcow.c b/block/qcow.c
index ab36b2995c..35e21eb6b3 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -736,8 +736,6 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
return -EINVAL;
out_buf = g_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
- if (!out_buf)
- return -1;
/* best compression, small window, no zlib header */
memset(&strm, 0, sizeof(strm));
@@ -745,8 +743,8 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
Z_DEFLATED, -12,
9, Z_DEFAULT_STRATEGY);
if (ret != 0) {
- g_free(out_buf);
- return -1;
+ ret = -EINVAL;
+ goto fail;
}
strm.avail_in = s->cluster_size;
@@ -756,9 +754,9 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
ret = deflate(&strm, Z_FINISH);
if (ret != Z_STREAM_END && ret != Z_OK) {
- g_free(out_buf);
deflateEnd(&strm);
- return -1;
+ ret = -EINVAL;
+ goto fail;
}
out_len = strm.next_out - out_buf;
@@ -766,19 +764,29 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
/* could not compress: write normal cluster */
- bdrv_write(bs, sector_num, buf, s->cluster_sectors);
+ ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors);
+ if (ret < 0) {
+ goto fail;
+ }
} else {
cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
out_len, 0, 0);
+ if (cluster_offset == 0) {
+ ret = -EIO;
+ goto fail;
+ }
+
cluster_offset &= s->cluster_offset_mask;
- if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) {
- g_free(out_buf);
- return -1;
+ ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
+ if (ret < 0) {
+ goto fail;
}
}
+ ret = 0;
+fail:
g_free(out_buf);
- return 0;
+ return ret;
}
static coroutine_fn int qcow_co_flush(BlockDriverState *bs)
diff --git a/block/qcow2.c b/block/qcow2.c
index a181932b67..ef057d31e0 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1113,11 +1113,13 @@ static int qcow2_co_flush(BlockDriverState *bs)
qemu_co_mutex_lock(&s->lock);
ret = qcow2_cache_flush(bs, s->l2_table_cache);
if (ret < 0) {
+ qemu_co_mutex_unlock(&s->lock);
return ret;
}
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) {
+ qemu_co_mutex_unlock(&s->lock);
return ret;
}
qemu_co_mutex_unlock(&s->lock);
diff --git a/block/qed.c b/block/qed.c
index 2e06992784..d032a4574c 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -388,7 +388,6 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
if (ret < 0) {
return ret;
}
- ret = 0; /* ret should always be 0 or -errno */
qed_header_le_to_cpu(&le_header, &s->header);
if (s->header.magic != QED_MAGIC) {
@@ -1420,8 +1419,10 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs,
memcpy(buffer, &le_header, sizeof(le_header));
buffer_len = sizeof(le_header);
- memcpy(buffer + buffer_len, backing_file, backing_file_len);
- buffer_len += backing_file_len;
+ if (backing_file) {
+ memcpy(buffer + buffer_len, backing_file, backing_file_len);
+ buffer_len += backing_file_len;
+ }
/* Write new header */
ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len);
diff --git a/block/vdi.c b/block/vdi.c
index 883046d5a2..523a6409c5 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -114,8 +114,13 @@ void uuid_unparse(const uuid_t uu, char *out);
*/
#define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n"
-/* Unallocated blocks use this index (no need to convert endianness). */
-#define VDI_UNALLOCATED UINT32_MAX
+/* A never-allocated block; semantically arbitrary content. */
+#define VDI_UNALLOCATED 0xffffffffU
+
+/* A discarded (no longer allocated) block; semantically zero-filled. */
+#define VDI_DISCARDED 0xfffffffeU
+
+#define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED)
#if !defined(CONFIG_UUID)
void uuid_generate(uuid_t out)
@@ -307,10 +312,10 @@ static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res)
/* Check block map and value of blocks_allocated. */
for (block = 0; block < s->header.blocks_in_image; block++) {
uint32_t bmap_entry = le32_to_cpu(s->bmap[block]);
- if (bmap_entry != VDI_UNALLOCATED) {
+ if (VDI_IS_ALLOCATED(bmap_entry)) {
if (bmap_entry < s->header.blocks_in_image) {
blocks_allocated++;
- if (bmap[bmap_entry] == VDI_UNALLOCATED) {
+ if (!VDI_IS_ALLOCATED(bmap[bmap_entry])) {
bmap[bmap_entry] = bmap_entry;
} else {
fprintf(stderr, "ERROR: block index %" PRIu32
@@ -472,7 +477,7 @@ static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num,
n_sectors = nb_sectors;
}
*pnum = n_sectors;
- return bmap_entry != VDI_UNALLOCATED;
+ return VDI_IS_ALLOCATED(bmap_entry);
}
static void vdi_aio_cancel(BlockDriverAIOCB *blockacb)
@@ -603,7 +608,7 @@ static void vdi_aio_read_cb(void *opaque, int ret)
/* prepare next AIO request */
acb->n_sectors = n_sectors;
bmap_entry = le32_to_cpu(s->bmap[block_index]);
- if (bmap_entry == VDI_UNALLOCATED) {
+ if (!VDI_IS_ALLOCATED(bmap_entry)) {
/* Block not allocated, return zeros, no need to wait. */
memset(acb->buf, 0, n_sectors * SECTOR_SIZE);
ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
@@ -685,7 +690,7 @@ static void vdi_aio_write_cb(void *opaque, int ret)
if (acb->header_modified) {
VdiHeader *header = acb->block_buffer;
logout("now writing modified header\n");
- assert(acb->bmap_first != VDI_UNALLOCATED);
+ assert(VDI_IS_ALLOCATED(acb->bmap_first));
*header = s->header;
vdi_header_to_le(header);
acb->header_modified = 0;
@@ -699,7 +704,7 @@ static void vdi_aio_write_cb(void *opaque, int ret)
goto done;
}
return;
- } else if (acb->bmap_first != VDI_UNALLOCATED) {
+ } else if (VDI_IS_ALLOCATED(acb->bmap_first)) {
/* One or more new blocks were allocated. */
uint64_t offset;
uint32_t bmap_first;
@@ -749,7 +754,7 @@ static void vdi_aio_write_cb(void *opaque, int ret)
/* prepare next AIO request */
acb->n_sectors = n_sectors;
bmap_entry = le32_to_cpu(s->bmap[block_index]);
- if (bmap_entry == VDI_UNALLOCATED) {
+ if (!VDI_IS_ALLOCATED(bmap_entry)) {
/* Allocate new block and write to it. */
uint64_t offset;
uint8_t *block;
diff --git a/block/vmdk.c b/block/vmdk.c
index 6be592ffd6..985006e203 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -208,12 +208,14 @@ static void vmdk_free_last_extent(BlockDriverState *bs)
static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
{
char desc[DESC_SIZE];
- uint32_t cid;
+ uint32_t cid = 0xffffffff;
const char *p_name, *cid_str;
size_t cid_str_size;
BDRVVmdkState *s = bs->opaque;
+ int ret;
- if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
+ ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
+ if (ret < 0) {
return 0;
}
@@ -225,6 +227,7 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
cid_str_size = sizeof("CID");
}
+ desc[DESC_SIZE - 1] = '\0';
p_name = strstr(desc, cid_str);
if (p_name != NULL) {
p_name += cid_str_size;
@@ -239,13 +242,19 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
char *p_name, *tmp_str;
BDRVVmdkState *s = bs->opaque;
+ int ret;
- memset(desc, 0, sizeof(desc));
- if (bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE) != DESC_SIZE) {
- return -EIO;
+ ret = bdrv_pread(bs->file, s->desc_offset, desc, DESC_SIZE);
+ if (ret < 0) {
+ return ret;
}
+ desc[DESC_SIZE - 1] = '\0';
tmp_str = strstr(desc, "parentCID");
+ if (tmp_str == NULL) {
+ return -EINVAL;
+ }
+
pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
p_name = strstr(desc, "CID");
if (p_name != NULL) {
@@ -254,9 +263,11 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
pstrcat(desc, sizeof(desc), tmp_desc);
}
- if (bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE) < 0) {
- return -EIO;
+ ret = bdrv_pwrite_sync(bs->file, s->desc_offset, desc, DESC_SIZE);
+ if (ret < 0) {
+ return ret;
}
+
return 0;
}
@@ -1109,7 +1120,10 @@ static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
/* update CID on the first write every time the virtual disk is
* opened */
if (!s->cid_updated) {
- vmdk_write_cid(bs, time(NULL));
+ ret = vmdk_write_cid(bs, time(NULL));
+ if (ret < 0) {
+ return ret;
+ }
s->cid_updated = true;
}
}
@@ -1407,7 +1421,6 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
bdrv_delete(bs);
return -EINVAL;
}
- filesize = bdrv_getlength(bs);
parent_cid = vmdk_read_cid(bs, 0);
bdrv_delete(bs);
relative_path(parent_filename, sizeof(parent_filename),
diff --git a/block_int.h b/block_int.h
index dac00f504f..f4547f6d93 100644
--- a/block_int.h
+++ b/block_int.h
@@ -29,6 +29,7 @@
#include "qemu-queue.h"
#include "qemu-coroutine.h"
#include "qemu-timer.h"
+#include "qapi-types.h"
#define BLOCK_FLAG_ENCRYPT 1
#define BLOCK_FLAG_COMPAT6 4
@@ -202,7 +203,8 @@ struct BlockDriverState {
drivers. They are not used by the block driver */
int cyls, heads, secs, translation;
BlockErrorAction on_read_error, on_write_error;
- BlockIOStatus iostatus;
+ bool iostatus_enabled;
+ BlockDeviceIoStatus iostatus;
char device_name[32];
unsigned long *dirty_bitmap;
int64_t dirty_count;
diff --git a/configure b/configure
index 4f87e0a43b..a6cf6d680d 100755
--- a/configure
+++ b/configure
@@ -182,6 +182,7 @@ usb_redir=""
opengl=""
zlib="yes"
guest_agent="yes"
+libiscsi=""
# parse CC options first
for opt do
@@ -657,6 +658,10 @@ for opt do
;;
--enable-spice) spice="yes"
;;
+ --disable-libiscsi) libiscsi="no"
+ ;;
+ --enable-libiscsi) libiscsi="yes"
+ ;;
--enable-profiler) profiler="yes"
;;
--enable-cocoa)
@@ -1046,6 +1051,8 @@ echo " Default:trace-<pid>"
echo " --disable-spice disable spice"
echo " --enable-spice enable spice"
echo " --enable-rbd enable building the rados block device (rbd)"
+echo " --disable-libiscsi disable iscsi support"
+echo " --enable-libiscsi enable iscsi support"
echo " --disable-smartcard disable smartcard support"
echo " --enable-smartcard enable smartcard support"
echo " --disable-smartcard-nss disable smartcard nss support"
@@ -2335,6 +2342,25 @@ if compile_prog "" "" ; then
fi
##########################################
+# Do we have libiscsi
+if test "$libiscsi" != "no" ; then
+ cat > $TMPC << EOF
+#include <iscsi/iscsi.h>
+int main(void) { iscsi_create_context(""); return 0; }
+EOF
+ if compile_prog "-Werror" "-liscsi" ; then
+ libiscsi="yes"
+ LIBS="$LIBS -liscsi"
+ else
+ if test "$libiscsi" = "yes" ; then
+ feature_not_found "libiscsi"
+ fi
+ libiscsi="no"
+ fi
+fi
+
+
+##########################################
# Do we need librt
cat > $TMPC <<EOF
#include <signal.h>
@@ -2562,7 +2588,7 @@ fi
open_by_hande_at=no
cat > $TMPC << EOF
#include <fcntl.h>
-int main(void) { struct file_handle *fh; open_by_handle_at(0, fh, 0); }
+int main(void) { struct file_handle fh; open_by_handle_at(0, &fh, 0); }
EOF
if compile_prog "" "" ; then
open_by_handle_at=yes
@@ -2744,6 +2770,7 @@ echo "xfsctl support $xfs"
echo "nss used $smartcard_nss"
echo "usb net redir $usb_redir"
echo "OpenGL support $opengl"
+echo "libiscsi support $libiscsi"
echo "build guest agent $guest_agent"
if test "$sdl_too_old" = "yes"; then
@@ -3042,6 +3069,10 @@ if test "$opengl" = "yes" ; then
echo "CONFIG_OPENGL=y" >> $config_host_mak
fi
+if test "$libiscsi" = "yes" ; then
+ echo "CONFIG_LIBISCSI=y" >> $config_host_mak
+fi
+
# XXX: suppress that
if [ "$bsd" = "yes" ] ; then
echo "CONFIG_BSD=y" >> $config_host_mak
diff --git a/console.h b/console.h
index 9c1487e041..6ac4ed31ec 100644
--- a/console.h
+++ b/console.h
@@ -383,8 +383,6 @@ char *vnc_display_local_addr(DisplayState *ds);
#ifdef CONFIG_VNC
int vnc_display_password(DisplayState *ds, const char *password);
int vnc_display_pw_expire(DisplayState *ds, time_t expires);
-void do_info_vnc_print(Monitor *mon, const QObject *data);
-void do_info_vnc(Monitor *mon, QObject **ret_data);
#else
static inline int vnc_display_password(DisplayState *ds, const char *password)
{
@@ -396,13 +394,6 @@ static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires)
qerror_report(QERR_FEATURE_DISABLED, "vnc");
return -ENODEV;
};
-static inline void do_info_vnc(Monitor *mon, QObject **ret_data)
-{
-};
-static inline void do_info_vnc_print(Monitor *mon, const QObject *data)
-{
- monitor_printf(mon, "VNC support disabled\n");
-};
#endif
/* curses.c */
diff --git a/cpus.c b/cpus.c
index 79a76560cb..f768683ad6 100644
--- a/cpus.c
+++ b/cpus.c
@@ -30,6 +30,7 @@
#include "gdbstub.h"
#include "dma.h"
#include "kvm.h"
+#include "qmp-commands.h"
#include "qemu-thread.h"
#include "cpus.h"
@@ -1094,3 +1095,47 @@ void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
cpu_list(f, cpu_fprintf); /* deprecated */
#endif
}
+
+CpuInfoList *qmp_query_cpus(Error **errp)
+{
+ CpuInfoList *head = NULL, *cur_item = NULL;
+ CPUState *env;
+
+ for(env = first_cpu; env != NULL; env = env->next_cpu) {
+ CpuInfoList *info;
+
+ cpu_synchronize_state(env);
+
+ info = g_malloc0(sizeof(*info));
+ info->value = g_malloc0(sizeof(*info->value));
+ info->value->CPU = env->cpu_index;
+ info->value->current = (env == first_cpu);
+ info->value->halted = env->halted;
+ info->value->thread_id = env->thread_id;
+#if defined(TARGET_I386)
+ info->value->has_pc = true;
+ info->value->pc = env->eip + env->segs[R_CS].base;
+#elif defined(TARGET_PPC)
+ info->value->has_nip = true;
+ info->value->nip = env->nip;
+#elif defined(TARGET_SPARC)
+ info->value->has_pc = true;
+ info->value->pc = env->pc;
+ info->value->has_npc = true;
+ info->value->npc = env->npc;
+#elif defined(TARGET_MIPS)
+ info->value->has_PC = true;
+ info->value->PC = env->active_tc.PC;
+#endif
+
+ /* XXX: waiting for the qapi to support GSList */
+ if (!cur_item) {
+ head = cur_item = info;
+ } else {
+ cur_item->next = info;
+ cur_item = info;
+ }
+ }
+
+ return head;
+}
diff --git a/device_tree.c b/device_tree.c
index dc69232f10..86a694c955 100644
--- a/device_tree.c
+++ b/device_tree.c
@@ -153,6 +153,7 @@ int qemu_devtree_add_subnode(void *fdt, const char *name)
int retval;
if (!basename) {
+ g_free(dupname);
return -1;
}
diff --git a/docs/specs/qcow2.txt b/docs/specs/qcow2.txt
index 8fc3cb2f1a..e792953c8f 100644
--- a/docs/specs/qcow2.txt
+++ b/docs/specs/qcow2.txt
@@ -108,8 +108,8 @@ as follows:
refcount_block_entries = (cluster_size / sizeof(uint16_t))
- refcount_block_index = (offset / cluster_size) % refcount_table_entries
- refcount_table_index = (offset / cluster_size) / refcount_table_entries
+ refcount_block_index = (offset / cluster_size) % refcount_block_entries
+ refcount_table_index = (offset / cluster_size) / refcount_block_entries
refcount_block = load_cluster(refcount_table[refcount_table_index]);
return refcount_block[refcount_block_index];
@@ -211,7 +211,7 @@ switch the active L1 table, so that a different set of host clusters are
exposed to the guest.
When creating a snapshot, the L1 table should be copied and the refcount of all
-L2 tables and clusters reachable form this L1 table must be increased, so that
+L2 tables and clusters reachable from this L1 table must be increased, so that
a write causes a COW and isn't visible in other snapshots.
When loading a snapshot, bit 63 of all entries in the new active L1 table and
diff --git a/error.c b/error.c
index 68c0039dba..990050f792 100644
--- a/error.c
+++ b/error.c
@@ -12,8 +12,9 @@
#include "qemu-common.h"
#include "error.h"
+#include "qjson.h"
+#include "qdict.h"
#include "error_int.h"
-#include "qemu-objects.h"
#include "qerror.h"
struct Error
diff --git a/exec-all.h b/exec-all.h
index 72ef246793..85a37bf1ed 100644
--- a/exec-all.h
+++ b/exec-all.h
@@ -340,8 +340,7 @@ static inline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong add
cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr);
#endif
}
- p = (void *)(unsigned long)addr
- + env1->tlb_table[mmu_idx][page_index].addend;
+ p = (void *)((uintptr_t)addr + env1->tlb_table[mmu_idx][page_index].addend);
return qemu_ram_addr_from_host_nofail(p);
}
#endif
diff --git a/exec.c b/exec.c
index 9dc4edbf61..2f3c6a0ce3 100644
--- a/exec.c
+++ b/exec.c
@@ -469,7 +469,6 @@ static void code_gen_alloc(unsigned long tb_size)
code_gen_buffer_size = tb_size;
if (code_gen_buffer_size == 0) {
#if defined(CONFIG_USER_ONLY)
- /* in user mode, phys_ram_size is not meaningful */
code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
#else
/* XXX: needs adjustments */
diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
index 908e2a5edc..1928da2525 100644
--- a/fsdev/file-op-9p.h
+++ b/fsdev/file-op-9p.h
@@ -56,10 +56,12 @@ typedef struct extended_ops {
* On failure ignore the error.
*/
#define V9FS_SM_NONE 0x00000010
-
+#define V9FS_RDONLY 0x00000020
#define V9FS_SEC_MASK 0x0000001C
+
+
typedef struct FsContext
{
uid_t uid;
@@ -76,6 +78,8 @@ typedef struct V9fsPath {
char *data;
} V9fsPath;
+typedef union V9fsFidOpenState V9fsFidOpenState;
+
void cred_init(FsCred *);
typedef struct FileOperations
@@ -92,22 +96,26 @@ typedef struct FileOperations
const char *, FsCred *);
int (*link)(FsContext *, V9fsPath *, V9fsPath *, const char *);
int (*setuid)(FsContext *, uid_t);
- int (*close)(FsContext *, int);
- int (*closedir)(FsContext *, DIR *);
- DIR *(*opendir)(FsContext *, V9fsPath *);
- int (*open)(FsContext *, V9fsPath *, int);
- int (*open2)(FsContext *, V9fsPath *, const char *, int, FsCred *);
- void (*rewinddir)(FsContext *, DIR *);
- off_t (*telldir)(FsContext *, DIR *);
- int (*readdir_r)(FsContext *, DIR *, struct dirent *, struct dirent **);
- void (*seekdir)(FsContext *, DIR *, off_t);
- ssize_t (*preadv)(FsContext *, int, const struct iovec *, int, off_t);
- ssize_t (*pwritev)(FsContext *, int, const struct iovec *, int, off_t);
+ int (*close)(FsContext *, V9fsFidOpenState *);
+ int (*closedir)(FsContext *, V9fsFidOpenState *);
+ int (*opendir)(FsContext *, V9fsPath *, V9fsFidOpenState *);
+ int (*open)(FsContext *, V9fsPath *, int, V9fsFidOpenState *);
+ int (*open2)(FsContext *, V9fsPath *, const char *,
+ int, FsCred *, V9fsFidOpenState *);
+ void (*rewinddir)(FsContext *, V9fsFidOpenState *);
+ off_t (*telldir)(FsContext *, V9fsFidOpenState *);
+ int (*readdir_r)(FsContext *, V9fsFidOpenState *,
+ struct dirent *, struct dirent **);
+ void (*seekdir)(FsContext *, V9fsFidOpenState *, off_t);
+ ssize_t (*preadv)(FsContext *, V9fsFidOpenState *,
+ const struct iovec *, int, off_t);
+ ssize_t (*pwritev)(FsContext *, V9fsFidOpenState *,
+ const struct iovec *, int, off_t);
int (*mkdir)(FsContext *, V9fsPath *, const char *, FsCred *);
- int (*fstat)(FsContext *, int, struct stat *);
+ int (*fstat)(FsContext *, V9fsFidOpenState *, struct stat *);
int (*rename)(FsContext *, const char *, const char *);
int (*truncate)(FsContext *, V9fsPath *, off_t);
- int (*fsync)(FsContext *, int, int);
+ int (*fsync)(FsContext *, V9fsFidOpenState *, int);
int (*statfs)(FsContext *s, V9fsPath *path, struct statfs *stbuf);
ssize_t (*lgetxattr)(FsContext *, V9fsPath *,
const char *, void *, size_t);
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
index 5977bcca4c..7fd2aa7793 100644
--- a/fsdev/qemu-fsdev.c
+++ b/fsdev/qemu-fsdev.c
@@ -24,6 +24,7 @@ static QTAILQ_HEAD(FsDriverEntry_head, FsDriverListEntry) fsdriver_entries =
static FsDriverTable FsDrivers[] = {
{ .name = "local", .ops = &local_ops},
{ .name = "handle", .ops = &handle_ops},
+ { .name = "synth", .ops = &synth_ops},
};
int qemu_fsdev_add(QemuOpts *opts)
@@ -35,7 +36,7 @@ int qemu_fsdev_add(QemuOpts *opts)
const char *path = qemu_opt_get(opts, "path");
const char *sec_model = qemu_opt_get(opts, "security_model");
const char *writeout = qemu_opt_get(opts, "writeout");
-
+ bool ro = qemu_opt_get_bool(opts, "readonly", 0);
if (!fsdev_id) {
fprintf(stderr, "fsdev: No id specified\n");
@@ -86,6 +87,11 @@ int qemu_fsdev_add(QemuOpts *opts)
fsle->fse.export_flags |= V9FS_IMMEDIATE_WRITEOUT;
}
}
+ if (ro) {
+ fsle->fse.export_flags |= V9FS_RDONLY;
+ } else {
+ fsle->fse.export_flags &= ~V9FS_RDONLY;
+ }
if (strcmp(fsdriver, "local")) {
goto done;
diff --git a/fsdev/qemu-fsdev.h b/fsdev/qemu-fsdev.h
index 5099085720..8ef847374a 100644
--- a/fsdev/qemu-fsdev.h
+++ b/fsdev/qemu-fsdev.h
@@ -53,4 +53,5 @@ int qemu_fsdev_add(QemuOpts *opts);
FsDriverEntry *get_fsdev_fsentry(char *id);
extern FileOperations local_ops;
extern FileOperations handle_ops;
+extern FileOperations synth_ops;
#endif
diff --git a/gdbstub.c b/gdbstub.c
index 4009058788..a25f404430 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -814,7 +814,11 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64)
if (n < 64) {
/* fprs */
- GET_REG32(*((uint32_t *)&env->fpr[n - 32]));
+ if (n & 1) {
+ GET_REG32(env->fpr[(n - 32) / 2].l.lower);
+ } else {
+ GET_REG32(env->fpr[(n - 32) / 2].l.upper);
+ }
}
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
switch (n) {
@@ -831,15 +835,15 @@ static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n)
#else
if (n < 64) {
/* f0-f31 */
- GET_REG32(*((uint32_t *)&env->fpr[n - 32]));
+ if (n & 1) {
+ GET_REG32(env->fpr[(n - 32) / 2].l.lower);
+ } else {
+ GET_REG32(env->fpr[(n - 32) / 2].l.upper);
+ }
}
if (n < 80) {
/* f32-f62 (double width, even numbers only) */
- uint64_t val;
-
- val = (uint64_t)*((uint32_t *)&env->fpr[(n - 64) * 2 + 32]) << 32;
- val |= *((uint32_t *)&env->fpr[(n - 64) * 2 + 33]);
- GET_REG64(val);
+ GET_REG64(env->fpr[(n - 32) / 2].ll);
}
switch (n) {
case 80: GET_REGL(env->pc);
@@ -878,7 +882,12 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64)
else if (n < 64) {
/* fprs */
- *((uint32_t *)&env->fpr[n - 32]) = tmp;
+ /* f0-f31 */
+ if (n & 1) {
+ env->fpr[(n - 32) / 2].l.lower = tmp;
+ } else {
+ env->fpr[(n - 32) / 2].l.upper = tmp;
+ }
} else {
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
switch (n) {
@@ -896,12 +905,16 @@ static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n)
#else
else if (n < 64) {
/* f0-f31 */
- env->fpr[n] = ldfl_p(mem_buf);
+ tmp = ldl_p(mem_buf);
+ if (n & 1) {
+ env->fpr[(n - 32) / 2].l.lower = tmp;
+ } else {
+ env->fpr[(n - 32) / 2].l.upper = tmp;
+ }
return 4;
} else if (n < 80) {
/* f32-f62 (double width, even numbers only) */
- *((uint32_t *)&env->fpr[(n - 64) * 2 + 32]) = tmp >> 32;
- *((uint32_t *)&env->fpr[(n - 64) * 2 + 33]) = tmp;
+ env->fpr[(n - 32) / 2].ll = tmp;
} else {
switch (n) {
case 80: env->pc = tmp; break;
diff --git a/hmp-commands.hx b/hmp-commands.hx
index ab08d583df..089c1ac23d 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -587,8 +587,7 @@ ETEXI
.args_type = "index:i",
.params = "index",
.help = "set the default CPU",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_cpu_set,
+ .mhandler.cmd = hmp_cpu,
},
STEXI
@@ -824,7 +823,8 @@ ETEXI
.params = "protocol hostname port tls-port cert-subject",
.help = "send migration info to spice/vnc client",
.user_print = monitor_user_noop,
- .mhandler.cmd_new = client_migrate_info,
+ .mhandler.cmd_async = client_migrate_info,
+ .flags = MONITOR_CMD_ASYNC,
},
STEXI
diff --git a/hmp.c b/hmp.c
index 34416fc3c7..443d3a7ff4 100644
--- a/hmp.c
+++ b/hmp.c
@@ -94,6 +94,401 @@ void hmp_info_chardev(Monitor *mon)
qapi_free_ChardevInfoList(char_info);
}
+void hmp_info_mice(Monitor *mon)
+{
+ MouseInfoList *mice_list, *mouse;
+
+ mice_list = qmp_query_mice(NULL);
+ if (!mice_list) {
+ monitor_printf(mon, "No mouse devices connected\n");
+ return;
+ }
+
+ for (mouse = mice_list; mouse; mouse = mouse->next) {
+ monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n",
+ mouse->value->current ? '*' : ' ',
+ mouse->value->index, mouse->value->name,
+ mouse->value->absolute ? " (absolute)" : "");
+ }
+
+ qapi_free_MouseInfoList(mice_list);
+}
+
+void hmp_info_migrate(Monitor *mon)
+{
+ MigrationInfo *info;
+
+ info = qmp_query_migrate(NULL);
+
+ if (info->has_status) {
+ monitor_printf(mon, "Migration status: %s\n", info->status);
+ }
+
+ if (info->has_ram) {
+ monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n",
+ info->ram->transferred >> 10);
+ monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n",
+ info->ram->remaining >> 10);
+ monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n",
+ info->ram->total >> 10);
+ }
+
+ if (info->has_disk) {
+ monitor_printf(mon, "transferred disk: %" PRIu64 " kbytes\n",
+ info->disk->transferred >> 10);
+ monitor_printf(mon, "remaining disk: %" PRIu64 " kbytes\n",
+ info->disk->remaining >> 10);
+ monitor_printf(mon, "total disk: %" PRIu64 " kbytes\n",
+ info->disk->total >> 10);
+ }
+
+ qapi_free_MigrationInfo(info);
+}
+
+void hmp_info_cpus(Monitor *mon)
+{
+ CpuInfoList *cpu_list, *cpu;
+
+ cpu_list = qmp_query_cpus(NULL);
+
+ for (cpu = cpu_list; cpu; cpu = cpu->next) {
+ int active = ' ';
+
+ if (cpu->value->CPU == monitor_get_cpu_index()) {
+ active = '*';
+ }
+
+ monitor_printf(mon, "%c CPU #%" PRId64 ": ", active, cpu->value->CPU);
+
+ if (cpu->value->has_pc) {
+ monitor_printf(mon, "pc=0x%016" PRIx64, cpu->value->pc);
+ }
+ if (cpu->value->has_nip) {
+ monitor_printf(mon, "nip=0x%016" PRIx64, cpu->value->nip);
+ }
+ if (cpu->value->has_npc) {
+ monitor_printf(mon, "pc=0x%016" PRIx64, cpu->value->pc);
+ monitor_printf(mon, "npc=0x%016" PRIx64, cpu->value->npc);
+ }
+ if (cpu->value->has_PC) {
+ monitor_printf(mon, "PC=0x%016" PRIx64, cpu->value->PC);
+ }
+
+ if (cpu->value->halted) {
+ monitor_printf(mon, " (halted)");
+ }
+
+ monitor_printf(mon, " thread_id=%" PRId64 "\n", cpu->value->thread_id);
+ }
+
+ qapi_free_CpuInfoList(cpu_list);
+}
+
+void hmp_info_block(Monitor *mon)
+{
+ BlockInfoList *block_list, *info;
+
+ block_list = qmp_query_block(NULL);
+
+ for (info = block_list; info; info = info->next) {
+ monitor_printf(mon, "%s: removable=%d",
+ info->value->device, info->value->removable);
+
+ if (info->value->removable) {
+ monitor_printf(mon, " locked=%d", info->value->locked);
+ monitor_printf(mon, " tray-open=%d", info->value->tray_open);
+ }
+
+ if (info->value->has_io_status) {
+ monitor_printf(mon, " io-status=%s",
+ BlockDeviceIoStatus_lookup[info->value->io_status]);
+ }
+
+ if (info->value->has_inserted) {
+ monitor_printf(mon, " file=");
+ monitor_print_filename(mon, info->value->inserted->file);
+
+ if (info->value->inserted->has_backing_file) {
+ monitor_printf(mon, " backing_file=");
+ monitor_print_filename(mon, info->value->inserted->backing_file);
+ }
+ monitor_printf(mon, " ro=%d drv=%s encrypted=%d",
+ info->value->inserted->ro,
+ info->value->inserted->drv,
+ info->value->inserted->encrypted);
+ } else {
+ monitor_printf(mon, " [not inserted]");
+ }
+
+ monitor_printf(mon, "\n");
+ }
+
+ qapi_free_BlockInfoList(block_list);
+}
+
+void hmp_info_blockstats(Monitor *mon)
+{
+ BlockStatsList *stats_list, *stats;
+
+ stats_list = qmp_query_blockstats(NULL);
+
+ for (stats = stats_list; stats; stats = stats->next) {
+ if (!stats->value->has_device) {
+ continue;
+ }
+
+ monitor_printf(mon, "%s:", stats->value->device);
+ monitor_printf(mon, " rd_bytes=%" PRId64
+ " wr_bytes=%" PRId64
+ " rd_operations=%" PRId64
+ " wr_operations=%" PRId64
+ " flush_operations=%" PRId64
+ " wr_total_time_ns=%" PRId64
+ " rd_total_time_ns=%" PRId64
+ " flush_total_time_ns=%" PRId64
+ "\n",
+ stats->value->stats->rd_bytes,
+ stats->value->stats->wr_bytes,
+ stats->value->stats->rd_operations,
+ stats->value->stats->wr_operations,
+ stats->value->stats->flush_operations,
+ stats->value->stats->wr_total_time_ns,
+ stats->value->stats->rd_total_time_ns,
+ stats->value->stats->flush_total_time_ns);
+ }
+
+ qapi_free_BlockStatsList(stats_list);
+}
+
+void hmp_info_vnc(Monitor *mon)
+{
+ VncInfo *info;
+ Error *err = NULL;
+ VncClientInfoList *client;
+
+ info = qmp_query_vnc(&err);
+ if (err) {
+ monitor_printf(mon, "%s\n", error_get_pretty(err));
+ error_free(err);
+ return;
+ }
+
+ if (!info->enabled) {
+ monitor_printf(mon, "Server: disabled\n");
+ goto out;
+ }
+
+ monitor_printf(mon, "Server:\n");
+ if (info->has_host && info->has_service) {
+ monitor_printf(mon, " address: %s:%s\n", info->host, info->service);
+ }
+ if (info->has_auth) {
+ monitor_printf(mon, " auth: %s\n", info->auth);
+ }
+
+ if (!info->has_clients || info->clients == NULL) {
+ monitor_printf(mon, "Client: none\n");
+ } else {
+ for (client = info->clients; client; client = client->next) {
+ monitor_printf(mon, "Client:\n");
+ monitor_printf(mon, " address: %s:%s\n",
+ client->value->host, client->value->service);
+ monitor_printf(mon, " x509_dname: %s\n",
+ client->value->x509_dname ?
+ client->value->x509_dname : "none");
+ monitor_printf(mon, " username: %s\n",
+ client->value->has_sasl_username ?
+ client->value->sasl_username : "none");
+ }
+ }
+
+out:
+ qapi_free_VncInfo(info);
+}
+
+void hmp_info_spice(Monitor *mon)
+{
+ SpiceChannelList *chan;
+ SpiceInfo *info;
+
+ info = qmp_query_spice(NULL);
+
+ if (!info->enabled) {
+ monitor_printf(mon, "Server: disabled\n");
+ goto out;
+ }
+
+ monitor_printf(mon, "Server:\n");
+ if (info->has_port) {
+ monitor_printf(mon, " address: %s:%" PRId64 "\n",
+ info->host, info->port);
+ }
+ if (info->has_tls_port) {
+ monitor_printf(mon, " address: %s:%" PRId64 " [tls]\n",
+ info->host, info->tls_port);
+ }
+ monitor_printf(mon, " auth: %s\n", info->auth);
+ monitor_printf(mon, " compiled: %s\n", info->compiled_version);
+
+ if (!info->has_channels || info->channels == NULL) {
+ monitor_printf(mon, "Channels: none\n");
+ } else {
+ for (chan = info->channels; chan; chan = chan->next) {
+ monitor_printf(mon, "Channel:\n");
+ monitor_printf(mon, " address: %s:%s%s\n",
+ chan->value->host, chan->value->port,
+ chan->value->tls ? " [tls]" : "");
+ monitor_printf(mon, " session: %" PRId64 "\n",
+ chan->value->connection_id);
+ monitor_printf(mon, " channel: %" PRId64 ":%" PRId64 "\n",
+ chan->value->channel_type, chan->value->channel_id);
+ }
+ }
+
+out:
+ qapi_free_SpiceInfo(info);
+}
+
+void hmp_info_balloon(Monitor *mon)
+{
+ BalloonInfo *info;
+ Error *err = NULL;
+
+ info = qmp_query_balloon(&err);
+ if (err) {
+ monitor_printf(mon, "%s\n", error_get_pretty(err));
+ error_free(err);
+ return;
+ }
+
+ monitor_printf(mon, "balloon: actual=%" PRId64, info->actual >> 20);
+ if (info->has_mem_swapped_in) {
+ monitor_printf(mon, " mem_swapped_in=%" PRId64, info->mem_swapped_in);
+ }
+ if (info->has_mem_swapped_out) {
+ monitor_printf(mon, " mem_swapped_out=%" PRId64, info->mem_swapped_out);
+ }
+ if (info->has_major_page_faults) {
+ monitor_printf(mon, " major_page_faults=%" PRId64,
+ info->major_page_faults);
+ }
+ if (info->has_minor_page_faults) {
+ monitor_printf(mon, " minor_page_faults=%" PRId64,
+ info->minor_page_faults);
+ }
+ if (info->has_free_mem) {
+ monitor_printf(mon, " free_mem=%" PRId64, info->free_mem);
+ }
+ if (info->has_total_mem) {
+ monitor_printf(mon, " total_mem=%" PRId64, info->total_mem);
+ }
+
+ monitor_printf(mon, "\n");
+
+ qapi_free_BalloonInfo(info);
+}
+
+static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev)
+{
+ PciMemoryRegionList *region;
+
+ monitor_printf(mon, " Bus %2" PRId64 ", ", dev->bus);
+ monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n",
+ dev->slot, dev->function);
+ monitor_printf(mon, " ");
+
+ if (dev->class_info.has_desc) {
+ monitor_printf(mon, "%s", dev->class_info.desc);
+ } else {
+ monitor_printf(mon, "Class %04" PRId64, dev->class_info.class);
+ }
+
+ monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n",
+ dev->id.vendor, dev->id.device);
+
+ if (dev->has_irq) {
+ monitor_printf(mon, " IRQ %" PRId64 ".\n", dev->irq);
+ }
+
+ if (dev->has_pci_bridge) {
+ monitor_printf(mon, " BUS %" PRId64 ".\n",
+ dev->pci_bridge->bus.number);
+ monitor_printf(mon, " secondary bus %" PRId64 ".\n",
+ dev->pci_bridge->bus.secondary);
+ monitor_printf(mon, " subordinate bus %" PRId64 ".\n",
+ dev->pci_bridge->bus.subordinate);
+
+ monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n",
+ dev->pci_bridge->bus.io_range->base,
+ dev->pci_bridge->bus.io_range->limit);
+
+ monitor_printf(mon,
+ " memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n",
+ dev->pci_bridge->bus.memory_range->base,
+ dev->pci_bridge->bus.memory_range->limit);
+
+ monitor_printf(mon, " prefetchable memory range "
+ "[0x%08"PRIx64", 0x%08"PRIx64"]\n",
+ dev->pci_bridge->bus.prefetchable_range->base,
+ dev->pci_bridge->bus.prefetchable_range->limit);
+ }
+
+ for (region = dev->regions; region; region = region->next) {
+ uint64_t addr, size;
+
+ addr = region->value->address;
+ size = region->value->size;
+
+ monitor_printf(mon, " BAR%" PRId64 ": ", region->value->bar);
+
+ if (!strcmp(region->value->type, "io")) {
+ monitor_printf(mon, "I/O at 0x%04" PRIx64
+ " [0x%04" PRIx64 "].\n",
+ addr, addr + size - 1);
+ } else {
+ monitor_printf(mon, "%d bit%s memory at 0x%08" PRIx64
+ " [0x%08" PRIx64 "].\n",
+ region->value->mem_type_64 ? 64 : 32,
+ region->value->prefetch ? " prefetchable" : "",
+ addr, addr + size - 1);
+ }
+ }
+
+ monitor_printf(mon, " id \"%s\"\n", dev->qdev_id);
+
+ if (dev->has_pci_bridge) {
+ if (dev->pci_bridge->has_devices) {
+ PciDeviceInfoList *cdev;
+ for (cdev = dev->pci_bridge->devices; cdev; cdev = cdev->next) {
+ hmp_info_pci_device(mon, cdev->value);
+ }
+ }
+ }
+}
+
+void hmp_info_pci(Monitor *mon)
+{
+ PciInfoList *info;
+ Error *err = NULL;
+
+ info = qmp_query_pci(&err);
+ if (err) {
+ monitor_printf(mon, "PCI devices not supported\n");
+ error_free(err);
+ return;
+ }
+
+ for (; info; info = info->next) {
+ PciDeviceInfoList *dev;
+
+ for (dev = info->value->devices; dev; dev = dev->next) {
+ hmp_info_pci_device(mon, dev->value);
+ }
+ }
+
+ qapi_free_PciInfoList(info);
+}
+
void hmp_quit(Monitor *mon, const QDict *qdict)
{
monitor_suspend(mon);
@@ -114,3 +509,15 @@ void hmp_system_powerdown(Monitor *mon, const QDict *qdict)
{
qmp_system_powerdown(NULL);
}
+
+void hmp_cpu(Monitor *mon, const QDict *qdict)
+{
+ int64_t cpu_index;
+
+ /* XXX: drop the monitor_set_cpu() usage when all HMP commands that
+ use it are converted to the QAPI */
+ cpu_index = qdict_get_int(qdict, "index");
+ if (monitor_set_cpu(cpu_index) < 0) {
+ monitor_printf(mon, "invalid CPU index\n");
+ }
+}
diff --git a/hmp.h b/hmp.h
index 92433cff97..4422578448 100644
--- a/hmp.h
+++ b/hmp.h
@@ -23,9 +23,19 @@ void hmp_info_kvm(Monitor *mon);
void hmp_info_status(Monitor *mon);
void hmp_info_uuid(Monitor *mon);
void hmp_info_chardev(Monitor *mon);
+void hmp_info_mice(Monitor *mon);
+void hmp_info_migrate(Monitor *mon);
+void hmp_info_cpus(Monitor *mon);
+void hmp_info_block(Monitor *mon);
+void hmp_info_blockstats(Monitor *mon);
+void hmp_info_vnc(Monitor *mon);
+void hmp_info_spice(Monitor *mon);
+void hmp_info_balloon(Monitor *mon);
+void hmp_info_pci(Monitor *mon);
void hmp_quit(Monitor *mon, const QDict *qdict);
void hmp_stop(Monitor *mon, const QDict *qdict);
void hmp_system_reset(Monitor *mon, const QDict *qdict);
void hmp_system_powerdown(Monitor *mon, const QDict *qdict);
+void hmp_cpu(Monitor *mon, const QDict *qdict);
#endif
diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c
index 72732e7c53..9b6d47d91d 100644
--- a/hw/9pfs/codir.c
+++ b/hw/9pfs/codir.c
@@ -29,7 +29,7 @@ int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent,
v9fs_co_run_in_worker(
{
errno = 0;
- err = s->ops->readdir_r(&s->ctx, fidp->fs.dir, dent, result);
+ err = s->ops->readdir_r(&s->ctx, &fidp->fs, dent, result);
if (!*result && errno) {
err = -errno;
} else {
@@ -49,7 +49,7 @@ off_t v9fs_co_telldir(V9fsPDU *pdu, V9fsFidState *fidp)
}
v9fs_co_run_in_worker(
{
- err = s->ops->telldir(&s->ctx, fidp->fs.dir);
+ err = s->ops->telldir(&s->ctx, &fidp->fs);
if (err < 0) {
err = -errno;
}
@@ -65,7 +65,7 @@ void v9fs_co_seekdir(V9fsPDU *pdu, V9fsFidState *fidp, off_t offset)
}
v9fs_co_run_in_worker(
{
- s->ops->seekdir(&s->ctx, fidp->fs.dir, offset);
+ s->ops->seekdir(&s->ctx, &fidp->fs, offset);
});
}
@@ -77,7 +77,7 @@ void v9fs_co_rewinddir(V9fsPDU *pdu, V9fsFidState *fidp)
}
v9fs_co_run_in_worker(
{
- s->ops->rewinddir(&s->ctx, fidp->fs.dir);
+ s->ops->rewinddir(&s->ctx, &fidp->fs);
});
}
@@ -129,8 +129,8 @@ int v9fs_co_opendir(V9fsPDU *pdu, V9fsFidState *fidp)
v9fs_path_read_lock(s);
v9fs_co_run_in_worker(
{
- fidp->fs.dir = s->ops->opendir(&s->ctx, &fidp->path);
- if (!fidp->fs.dir) {
+ err = s->ops->opendir(&s->ctx, &fidp->path, &fidp->fs);
+ if (err < 0) {
err = -errno;
} else {
err = 0;
@@ -146,7 +146,7 @@ int v9fs_co_opendir(V9fsPDU *pdu, V9fsFidState *fidp)
return err;
}
-int v9fs_co_closedir(V9fsPDU *pdu, DIR *dir)
+int v9fs_co_closedir(V9fsPDU *pdu, V9fsFidOpenState *fs)
{
int err;
V9fsState *s = pdu->s;
@@ -156,7 +156,7 @@ int v9fs_co_closedir(V9fsPDU *pdu, DIR *dir)
}
v9fs_co_run_in_worker(
{
- err = s->ops->closedir(&s->ctx, dir);
+ err = s->ops->closedir(&s->ctx, fs);
if (err < 0) {
err = -errno;
}
diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c
index 692811e5ab..586b0382f6 100644
--- a/hw/9pfs/cofile.c
+++ b/hw/9pfs/cofile.c
@@ -61,7 +61,7 @@ int v9fs_co_lstat(V9fsPDU *pdu, V9fsPath *path, struct stat *stbuf)
return err;
}
-int v9fs_co_fstat(V9fsPDU *pdu, int fd, struct stat *stbuf)
+int v9fs_co_fstat(V9fsPDU *pdu, V9fsFidState *fidp, struct stat *stbuf)
{
int err;
V9fsState *s = pdu->s;
@@ -71,7 +71,7 @@ int v9fs_co_fstat(V9fsPDU *pdu, int fd, struct stat *stbuf)
}
v9fs_co_run_in_worker(
{
- err = s->ops->fstat(&s->ctx, fd, stbuf);
+ err = s->ops->fstat(&s->ctx, &fidp->fs, stbuf);
if (err < 0) {
err = -errno;
}
@@ -90,8 +90,8 @@ int v9fs_co_open(V9fsPDU *pdu, V9fsFidState *fidp, int flags)
v9fs_path_read_lock(s);
v9fs_co_run_in_worker(
{
- fidp->fs.fd = s->ops->open(&s->ctx, &fidp->path, flags);
- if (fidp->fs.fd == -1) {
+ err = s->ops->open(&s->ctx, &fidp->path, flags, &fidp->fs);
+ if (err == -1) {
err = -errno;
} else {
err = 0;
@@ -130,9 +130,9 @@ int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid,
v9fs_path_read_lock(s);
v9fs_co_run_in_worker(
{
- fidp->fs.fd = s->ops->open2(&s->ctx, &fidp->path,
- name->data, flags, &cred);
- if (fidp->fs.fd == -1) {
+ err = s->ops->open2(&s->ctx, &fidp->path,
+ name->data, flags, &cred, &fidp->fs);
+ if (err < 0) {
err = -errno;
} else {
v9fs_path_init(&path);
@@ -141,12 +141,12 @@ int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid,
err = s->ops->lstat(&s->ctx, &path, stbuf);
if (err < 0) {
err = -errno;
- s->ops->close(&s->ctx, fidp->fs.fd);
+ s->ops->close(&s->ctx, &fidp->fs);
} else {
v9fs_path_copy(&fidp->path, &path);
}
} else {
- s->ops->close(&s->ctx, fidp->fs.fd);
+ s->ops->close(&s->ctx, &fidp->fs);
}
v9fs_path_free(&path);
}
@@ -161,7 +161,7 @@ int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid,
return err;
}
-int v9fs_co_close(V9fsPDU *pdu, int fd)
+int v9fs_co_close(V9fsPDU *pdu, V9fsFidOpenState *fs)
{
int err;
V9fsState *s = pdu->s;
@@ -171,7 +171,7 @@ int v9fs_co_close(V9fsPDU *pdu, int fd)
}
v9fs_co_run_in_worker(
{
- err = s->ops->close(&s->ctx, fd);
+ err = s->ops->close(&s->ctx, fs);
if (err < 0) {
err = -errno;
}
@@ -184,16 +184,15 @@ int v9fs_co_close(V9fsPDU *pdu, int fd)
int v9fs_co_fsync(V9fsPDU *pdu, V9fsFidState *fidp, int datasync)
{
- int fd, err;
+ int err;
V9fsState *s = pdu->s;
if (v9fs_request_cancelled(pdu)) {
return -EINTR;
}
- fd = fidp->fs.fd;
v9fs_co_run_in_worker(
{
- err = s->ops->fsync(&s->ctx, fd, datasync);
+ err = s->ops->fsync(&s->ctx, &fidp->fs, datasync);
if (err < 0) {
err = -errno;
}
@@ -226,16 +225,15 @@ int v9fs_co_link(V9fsPDU *pdu, V9fsFidState *oldfid,
int v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp,
struct iovec *iov, int iovcnt, int64_t offset)
{
- int fd, err;
+ int err;
V9fsState *s = pdu->s;
if (v9fs_request_cancelled(pdu)) {
return -EINTR;
}
- fd = fidp->fs.fd;
v9fs_co_run_in_worker(
{
- err = s->ops->pwritev(&s->ctx, fd, iov, iovcnt, offset);
+ err = s->ops->pwritev(&s->ctx, &fidp->fs, iov, iovcnt, offset);
if (err < 0) {
err = -errno;
}
@@ -246,16 +244,15 @@ int v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp,
int v9fs_co_preadv(V9fsPDU *pdu, V9fsFidState *fidp,
struct iovec *iov, int iovcnt, int64_t offset)
{
- int fd, err;
+ int err;
V9fsState *s = pdu->s;
if (v9fs_request_cancelled(pdu)) {
return -EINTR;
}
- fd = fidp->fs.fd;
v9fs_co_run_in_worker(
{
- err = s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset);
+ err = s->ops->preadv(&s->ctx, &fidp->fs, iov, iovcnt, offset);
if (err < 0) {
err = -errno;
}
diff --git a/hw/9pfs/virtio-9p-coth.h b/hw/9pfs/virtio-9p-coth.h
index ca96b9cf2f..c4b74b0221 100644
--- a/hw/9pfs/virtio-9p-coth.h
+++ b/hw/9pfs/virtio-9p-coth.h
@@ -80,7 +80,7 @@ extern int v9fs_co_rename(V9fsPDU *, V9fsPath *, V9fsPath *);
extern int v9fs_co_unlinkat(V9fsPDU *, V9fsPath *, V9fsString *, int flags);
extern int v9fs_co_renameat(V9fsPDU *, V9fsPath *, V9fsString *,
V9fsPath *, V9fsString *);
-extern int v9fs_co_fstat(V9fsPDU *, int, struct stat *);
+extern int v9fs_co_fstat(V9fsPDU *, V9fsFidState *, struct stat *);
extern int v9fs_co_opendir(V9fsPDU *, V9fsFidState *);
extern int v9fs_co_open(V9fsPDU *, V9fsFidState *, int);
extern int v9fs_co_open2(V9fsPDU *, V9fsFidState *, V9fsString *,
@@ -88,8 +88,8 @@ extern int v9fs_co_open2(V9fsPDU *, V9fsFidState *, V9fsString *,
extern int v9fs_co_lsetxattr(V9fsPDU *, V9fsPath *, V9fsString *,
void *, size_t, int);
extern int v9fs_co_lremovexattr(V9fsPDU *, V9fsPath *, V9fsString *);
-extern int v9fs_co_closedir(V9fsPDU *, DIR *);
-extern int v9fs_co_close(V9fsPDU *, int);
+extern int v9fs_co_closedir(V9fsPDU *, V9fsFidOpenState *);
+extern int v9fs_co_close(V9fsPDU *, V9fsFidOpenState *);
extern int v9fs_co_fsync(V9fsPDU *, V9fsFidState *, int);
extern int v9fs_co_symlink(V9fsPDU *, V9fsFidState *, V9fsString *,
const char *, gid_t, struct stat *);
diff --git a/hw/9pfs/virtio-9p-handle.c b/hw/9pfs/virtio-9p-handle.c
index 98809f1642..c38e0e7863 100644
--- a/hw/9pfs/virtio-9p-handle.c
+++ b/hw/9pfs/virtio-9p-handle.c
@@ -133,81 +133,91 @@ static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
return ret;
}
-static int handle_close(FsContext *ctx, int fd)
+static int handle_close(FsContext *ctx, V9fsFidOpenState *fs)
{
- return close(fd);
+ return close(fs->fd);
}
-static int handle_closedir(FsContext *ctx, DIR *dir)
+static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs)
{
- return closedir(dir);
+ return closedir(fs->dir);
}
-static int handle_open(FsContext *ctx, V9fsPath *fs_path, int flags)
+static int handle_open(FsContext *ctx, V9fsPath *fs_path,
+ int flags, V9fsFidOpenState *fs)
{
struct handle_data *data = (struct handle_data *)ctx->private;
- return open_by_handle(data->mountfd, fs_path->data, flags);
+ fs->fd = open_by_handle(data->mountfd, fs_path->data, flags);
+ return fs->fd;
}
-static DIR *handle_opendir(FsContext *ctx, V9fsPath *fs_path)
+static int handle_opendir(FsContext *ctx,
+ V9fsPath *fs_path, V9fsFidOpenState *fs)
{
- int fd;
- fd = handle_open(ctx, fs_path, O_DIRECTORY);
- if (fd < 0) {
- return NULL;
+ int ret;
+ ret = handle_open(ctx, fs_path, O_DIRECTORY, fs);
+ if (ret < 0) {
+ return -1;
}
- return fdopendir(fd);
+ fs->dir = fdopendir(ret);
+ if (!fs->dir) {
+ return -1;
+ }
+ return 0;
}
-static void handle_rewinddir(FsContext *ctx, DIR *dir)
+static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
{
- return rewinddir(dir);
+ return rewinddir(fs->dir);
}
-static off_t handle_telldir(FsContext *ctx, DIR *dir)
+static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs)
{
- return telldir(dir);
+ return telldir(fs->dir);
}
-static int handle_readdir_r(FsContext *ctx, DIR *dir, struct dirent *entry,
+static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+ struct dirent *entry,
struct dirent **result)
{
- return readdir_r(dir, entry, result);
+ return readdir_r(fs->dir, entry, result);
}
-static void handle_seekdir(FsContext *ctx, DIR *dir, off_t off)
+static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
{
- return seekdir(dir, off);
+ return seekdir(fs->dir, off);
}
-static ssize_t handle_preadv(FsContext *ctx, int fd, const struct iovec *iov,
+static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+ const struct iovec *iov,
int iovcnt, off_t offset)
{
#ifdef CONFIG_PREADV
- return preadv(fd, iov, iovcnt, offset);
+ return preadv(fs->fd, iov, iovcnt, offset);
#else
- int err = lseek(fd, offset, SEEK_SET);
+ int err = lseek(fs->fd, offset, SEEK_SET);
if (err == -1) {
return err;
} else {
- return readv(fd, iov, iovcnt);
+ return readv(fs->fd, iov, iovcnt);
}
#endif
}
-static ssize_t handle_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
+static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
+ const struct iovec *iov,
int iovcnt, off_t offset)
{
ssize_t ret;
#ifdef CONFIG_PREADV
- ret = pwritev(fd, iov, iovcnt, offset);
+ ret = pwritev(fs->fd, iov, iovcnt, offset);
#else
- int err = lseek(fd, offset, SEEK_SET);
+ int err = lseek(fs->fd, offset, SEEK_SET);
if (err == -1) {
return err;
} else {
- ret = writev(fd, iov, iovcnt);
+ ret = writev(fs->fd, iov, iovcnt);
}
#endif
#ifdef CONFIG_SYNC_FILE_RANGE
@@ -217,7 +227,7 @@ static ssize_t handle_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
* We want to ensure that we don't leave dirty pages in the cache
* after write when writeout=immediate is sepcified.
*/
- sync_file_range(fd, offset, ret,
+ sync_file_range(fs->fd, offset, ret,
SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
}
#endif
@@ -274,13 +284,14 @@ static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
return ret;
}
-static int handle_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
+static int handle_fstat(FsContext *fs_ctx, V9fsFidOpenState *fs,
+ struct stat *stbuf)
{
- return fstat(fd, stbuf);
+ return fstat(fs->fd, stbuf);
}
static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
- int flags, FsCred *credp)
+ int flags, FsCred *credp, V9fsFidOpenState *fs)
{
int ret;
int dirfd, fd;
@@ -296,6 +307,8 @@ static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
if (ret < 0) {
close(fd);
fd = ret;
+ } else {
+ fs->fd = fd;
}
}
close(dirfd);
@@ -411,12 +424,12 @@ static int handle_remove(FsContext *ctx, const char *path)
return -1;
}
-static int handle_fsync(FsContext *ctx, int fd, int datasync)
+static int handle_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync)
{
if (datasync) {
- return qemu_fdatasync(fd);
+ return qemu_fdatasync(fs->fd);
} else {
- return fsync(fd);
+ return fsync(fs->fd);
}
}
@@ -575,7 +588,8 @@ static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
mode_t st_mode, uint64_t *st_gen)
{
- int err, fd;
+ int err;
+ V9fsFidOpenState fid_open;
/*
* Do not try to open special files like device nodes, fifos etc
@@ -584,12 +598,12 @@ static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
return 0;
}
- fd = handle_open(ctx, path, O_RDONLY);
- if (fd < 0) {
- return fd;
+ err = handle_open(ctx, path, O_RDONLY, &fid_open);
+ if (err < 0) {
+ return err;
}
- err = ioctl(fd, FS_IOC_GETVERSION, st_gen);
- handle_close(ctx, fd);
+ err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
+ handle_close(ctx, &fid_open);
return err;
}
diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c
index d561de88f0..782dc0ab21 100644
--- a/hw/9pfs/virtio-9p-local.c
+++ b/hw/9pfs/virtio-9p-local.c
@@ -156,81 +156,91 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
return tsize;
}
-static int local_close(FsContext *ctx, int fd)
+static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
{
- return close(fd);
+ return close(fs->fd);
}
-static int local_closedir(FsContext *ctx, DIR *dir)
+static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
{
- return closedir(dir);
+ return closedir(fs->dir);
}
-static int local_open(FsContext *ctx, V9fsPath *fs_path, int flags)
+static int local_open(FsContext *ctx, V9fsPath *fs_path,
+ int flags, V9fsFidOpenState *fs)
{
char buffer[PATH_MAX];
char *path = fs_path->data;
- return open(rpath(ctx, path, buffer), flags);
+ fs->fd = open(rpath(ctx, path, buffer), flags);
+ return fs->fd;
}
-static DIR *local_opendir(FsContext *ctx, V9fsPath *fs_path)
+static int local_opendir(FsContext *ctx,
+ V9fsPath *fs_path, V9fsFidOpenState *fs)
{
char buffer[PATH_MAX];
char *path = fs_path->data;
- return opendir(rpath(ctx, path, buffer));
+ fs->dir = opendir(rpath(ctx, path, buffer));
+ if (!fs->dir) {
+ return -1;
+ }
+ return 0;
}
-static void local_rewinddir(FsContext *ctx, DIR *dir)
+static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
{
- return rewinddir(dir);
+ return rewinddir(fs->dir);
}
-static off_t local_telldir(FsContext *ctx, DIR *dir)
+static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
{
- return telldir(dir);
+ return telldir(fs->dir);
}
-static int local_readdir_r(FsContext *ctx, DIR *dir, struct dirent *entry,
+static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+ struct dirent *entry,
struct dirent **result)
{
- return readdir_r(dir, entry, result);
+ return readdir_r(fs->dir, entry, result);
}
-static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
+static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
{
- return seekdir(dir, off);
+ return seekdir(fs->dir, off);
}
-static ssize_t local_preadv(FsContext *ctx, int fd, const struct iovec *iov,
+static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+ const struct iovec *iov,
int iovcnt, off_t offset)
{
#ifdef CONFIG_PREADV
- return preadv(fd, iov, iovcnt, offset);
+ return preadv(fs->fd, iov, iovcnt, offset);
#else
- int err = lseek(fd, offset, SEEK_SET);
+ int err = lseek(fs->fd, offset, SEEK_SET);
if (err == -1) {
return err;
} else {
- return readv(fd, iov, iovcnt);
+ return readv(fs->fd, iov, iovcnt);
}
#endif
}
-static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
+static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
+ const struct iovec *iov,
int iovcnt, off_t offset)
{
ssize_t ret
;
#ifdef CONFIG_PREADV
- ret = pwritev(fd, iov, iovcnt, offset);
+ ret = pwritev(fs->fd, iov, iovcnt, offset);
#else
- int err = lseek(fd, offset, SEEK_SET);
+ int err = lseek(fs->fd, offset, SEEK_SET);
if (err == -1) {
return err;
} else {
- ret = writev(fd, iov, iovcnt);
+ ret = writev(fs->fd, iov, iovcnt);
}
#endif
#ifdef CONFIG_SYNC_FILE_RANGE
@@ -240,7 +250,7 @@ static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
* We want to ensure that we don't leave dirty pages in the cache
* after write when writeout=immediate is sepcified.
*/
- sync_file_range(fd, offset, ret,
+ sync_file_range(fs->fd, offset, ret,
SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
}
#endif
@@ -281,7 +291,7 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
if (err == -1) {
goto out;
}
- local_set_xattr(rpath(fs_ctx, path, buffer), credp);
+ err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
if (err == -1) {
serrno = errno;
goto err_end;
@@ -356,10 +366,11 @@ out:
return err;
}
-static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
+static int local_fstat(FsContext *fs_ctx,
+ V9fsFidOpenState *fs, struct stat *stbuf)
{
int err;
- err = fstat(fd, stbuf);
+ err = fstat(fs->fd, stbuf);
if (err) {
return err;
}
@@ -370,16 +381,20 @@ static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
mode_t tmp_mode;
dev_t tmp_dev;
- if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
+ if (fgetxattr(fs->fd, "user.virtfs.uid",
+ &tmp_uid, sizeof(uid_t)) > 0) {
stbuf->st_uid = tmp_uid;
}
- if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
+ if (fgetxattr(fs->fd, "user.virtfs.gid",
+ &tmp_gid, sizeof(gid_t)) > 0) {
stbuf->st_gid = tmp_gid;
}
- if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
+ if (fgetxattr(fs->fd, "user.virtfs.mode",
+ &tmp_mode, sizeof(mode_t)) > 0) {
stbuf->st_mode = tmp_mode;
}
- if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
+ if (fgetxattr(fs->fd, "user.virtfs.rdev",
+ &tmp_dev, sizeof(dev_t)) > 0) {
stbuf->st_rdev = tmp_dev;
}
}
@@ -387,7 +402,7 @@ static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
}
static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
- int flags, FsCred *credp)
+ int flags, FsCred *credp, V9fsFidOpenState *fs)
{
char *path;
int fd = -1;
@@ -428,6 +443,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
}
}
err = fd;
+ fs->fd = fd;
goto out;
err_end:
@@ -551,15 +567,12 @@ static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
char *path = fs_path->data;
if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
- (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH)) {
- return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid,
- credp->fc_gid);
+ (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+ (fs_ctx->export_flags & V9FS_SM_NONE)) {
+ return lchown(rpath(fs_ctx, path, buffer),
+ credp->fc_uid, credp->fc_gid);
} else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
- } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
- (fs_ctx->export_flags & V9FS_SM_NONE)) {
- return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid,
- credp->fc_gid);
}
return -1;
}
@@ -580,12 +593,12 @@ static int local_remove(FsContext *ctx, const char *path)
return remove(rpath(ctx, path, buffer));
}
-static int local_fsync(FsContext *ctx, int fd, int datasync)
+static int local_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync)
{
if (datasync) {
- return qemu_fdatasync(fd);
+ return qemu_fdatasync(fs->fd);
} else {
- return fsync(fd);
+ return fsync(fs->fd);
}
}
@@ -680,7 +693,9 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
mode_t st_mode, uint64_t *st_gen)
{
- int err, fd;
+ int err;
+ V9fsFidOpenState fid_open;
+
/*
* Do not try to open special files like device nodes, fifos etc
* We can get fd for regular files and directories only
@@ -688,12 +703,12 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
return 0;
}
- fd = local_open(ctx, path, O_RDONLY);
- if (fd < 0) {
- return fd;
+ err = local_open(ctx, path, O_RDONLY, &fid_open);
+ if (err < 0) {
+ return err;
}
- err = ioctl(fd, FS_IOC_GETVERSION, st_gen);
- local_close(ctx, fd);
+ err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
+ local_close(ctx, &fid_open);
return err;
}
diff --git a/hw/9pfs/virtio-9p-synth.c b/hw/9pfs/virtio-9p-synth.c
new file mode 100644
index 0000000000..f573616363
--- /dev/null
+++ b/hw/9pfs/virtio-9p-synth.c
@@ -0,0 +1,571 @@
+/*
+ * Virtio 9p synthetic file system support
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Malahal Naineni <malahal@us.ibm.com>
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw/virtio.h"
+#include "virtio-9p.h"
+#include "virtio-9p-xattr.h"
+#include "fsdev/qemu-fsdev.h"
+#include "virtio-9p-synth.h"
+
+#include <sys/stat.h>
+
+/* Root node for synth file system */
+V9fsSynthNode v9fs_synth_root = {
+ .name = "/",
+ .actual_attr = {
+ .mode = 0555 | S_IFDIR,
+ .nlink = 1,
+ },
+ .attr = &v9fs_synth_root.actual_attr,
+};
+
+static QemuMutex v9fs_synth_mutex;
+static int v9fs_synth_node_count;
+/* set to 1 when the synth fs is ready */
+static int v9fs_synth_fs;
+
+static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode,
+ const char *name,
+ V9fsSynthNodeAttr *attr, int inode)
+{
+ V9fsSynthNode *node;
+
+ /* Add directory type and remove write bits */
+ mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ node = g_malloc0(sizeof(V9fsSynthNode));
+ if (attr) {
+ /* We are adding .. or . entries */
+ node->attr = attr;
+ node->attr->nlink++;
+ } else {
+ node->attr = &node->actual_attr;
+ node->attr->inode = inode;
+ node->attr->nlink = 1;
+ /* We don't allow write to directories */
+ node->attr->mode = mode;
+ node->attr->write = NULL;
+ node->attr->read = NULL;
+ }
+ node->private = node;
+ strncpy(node->name, name, sizeof(node->name));
+ QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
+ return node;
+}
+
+int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
+ const char *name, V9fsSynthNode **result)
+{
+ int ret;
+ V9fsSynthNode *node, *tmp;
+
+ if (!v9fs_synth_fs) {
+ return EAGAIN;
+ }
+ if (!name || (strlen(name) >= NAME_MAX)) {
+ return EINVAL;
+ }
+ if (!parent) {
+ parent = &v9fs_synth_root;
+ }
+ qemu_mutex_lock(&v9fs_synth_mutex);
+ QLIST_FOREACH(tmp, &parent->child, sibling) {
+ if (!strcmp(tmp->name, name)) {
+ ret = EEXIST;
+ goto err_out;
+ }
+ }
+ /* Add the name */
+ node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++);
+ v9fs_add_dir_node(node, parent->attr->mode, "..",
+ parent->attr, parent->attr->inode);
+ v9fs_add_dir_node(node, node->attr->mode, ".",
+ node->attr, node->attr->inode);
+ *result = node;
+ ret = 0;
+err_out:
+ qemu_mutex_unlock(&v9fs_synth_mutex);
+ return ret;
+}
+
+int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
+ const char *name, v9fs_synth_read read,
+ v9fs_synth_write write, void *arg)
+{
+ int ret;
+ V9fsSynthNode *node, *tmp;
+
+ if (!v9fs_synth_fs) {
+ return EAGAIN;
+ }
+ if (!name || (strlen(name) >= NAME_MAX)) {
+ return EINVAL;
+ }
+ if (!parent) {
+ parent = &v9fs_synth_root;
+ }
+
+ qemu_mutex_lock(&v9fs_synth_mutex);
+ QLIST_FOREACH(tmp, &parent->child, sibling) {
+ if (!strcmp(tmp->name, name)) {
+ ret = EEXIST;
+ goto err_out;
+ }
+ }
+ /* Add file type and remove write bits */
+ mode = ((mode & 0777) | S_IFREG);
+ node = g_malloc0(sizeof(V9fsSynthNode));
+ node->attr = &node->actual_attr;
+ node->attr->inode = v9fs_synth_node_count++;
+ node->attr->nlink = 1;
+ node->attr->read = read;
+ node->attr->write = write;
+ node->attr->mode = mode;
+ node->private = arg;
+ strncpy(node->name, name, sizeof(node->name));
+ QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling);
+ ret = 0;
+err_out:
+ qemu_mutex_unlock(&v9fs_synth_mutex);
+ return ret;
+}
+
+static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf)
+{
+ stbuf->st_dev = 0;
+ stbuf->st_ino = node->attr->inode;
+ stbuf->st_mode = node->attr->mode;
+ stbuf->st_nlink = node->attr->nlink;
+ stbuf->st_uid = 0;
+ stbuf->st_gid = 0;
+ stbuf->st_rdev = 0;
+ stbuf->st_size = 0;
+ stbuf->st_blksize = 0;
+ stbuf->st_blocks = 0;
+ stbuf->st_atime = 0;
+ stbuf->st_mtime = 0;
+ stbuf->st_ctime = 0;
+}
+
+static int v9fs_synth_lstat(FsContext *fs_ctx,
+ V9fsPath *fs_path, struct stat *stbuf)
+{
+ V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
+
+ v9fs_synth_fill_statbuf(node, stbuf);
+ return 0;
+}
+
+static int v9fs_synth_fstat(FsContext *fs_ctx,
+ V9fsFidOpenState *fs, struct stat *stbuf)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ v9fs_synth_fill_statbuf(synth_open->node, stbuf);
+ return 0;
+}
+
+static int v9fs_synth_opendir(FsContext *ctx,
+ V9fsPath *fs_path, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open;
+ V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
+
+ synth_open = g_malloc(sizeof(*synth_open));
+ synth_open->node = node;
+ node->open_count++;
+ fs->private = synth_open;
+ return 0;
+}
+
+static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+
+ node->open_count--;
+ g_free(synth_open);
+ fs->private = NULL;
+ return 0;
+}
+
+static off_t v9fs_synth_telldir(FsContext *ctx, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ return synth_open->offset;
+}
+
+static void v9fs_synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ synth_open->offset = off;
+}
+
+static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
+{
+ v9fs_synth_seekdir(ctx, fs, 0);
+}
+
+static void v9fs_synth_direntry(V9fsSynthNode *node,
+ struct dirent *entry, off_t off)
+{
+ strcpy(entry->d_name, node->name);
+ entry->d_ino = node->attr->inode;
+ entry->d_off = off + 1;
+}
+
+static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry,
+ struct dirent **result, off_t off)
+{
+ int i = 0;
+ V9fsSynthNode *node;
+
+ rcu_read_lock();
+ QLIST_FOREACH(node, &dir->child, sibling) {
+ /* This is the off child of the directory */
+ if (i == off) {
+ break;
+ }
+ i++;
+ }
+ rcu_read_unlock();
+ if (!node) {
+ /* end of directory */
+ *result = NULL;
+ return 0;
+ }
+ v9fs_synth_direntry(node, entry, off);
+ *result = entry;
+ return 0;
+}
+
+static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+ struct dirent *entry, struct dirent **result)
+{
+ int ret;
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+ ret = v9fs_synth_get_dentry(node, entry, result, synth_open->offset);
+ if (!ret && *result != NULL) {
+ synth_open->offset++;
+ }
+ return ret;
+}
+
+static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path,
+ int flags, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open;
+ V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
+
+ synth_open = g_malloc(sizeof(*synth_open));
+ synth_open->node = node;
+ node->open_count++;
+ fs->private = synth_open;
+ return 0;
+}
+
+static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path,
+ const char *name, int flags,
+ FsCred *credp, V9fsFidOpenState *fs)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+
+ node->open_count--;
+ g_free(synth_open);
+ fs->private = NULL;
+ return 0;
+}
+
+static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
+ const struct iovec *iov,
+ int iovcnt, off_t offset)
+{
+ int i, count = 0, wcount;
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+ if (!node->attr->write) {
+ errno = EPERM;
+ return -1;
+ }
+ for (i = 0; i < iovcnt; i++) {
+ wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len,
+ offset, node->private);
+ offset += wcount;
+ count += wcount;
+ /* If we wrote less than requested. we are done */
+ if (wcount < iov[i].iov_len) {
+ break;
+ }
+ }
+ return count;
+}
+
+static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+ const struct iovec *iov,
+ int iovcnt, off_t offset)
+{
+ int i, count = 0, rcount;
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+ if (!node->attr->read) {
+ errno = EPERM;
+ return -1;
+ }
+ for (i = 0; i < iovcnt; i++) {
+ rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len,
+ offset, node->private);
+ offset += rcount;
+ count += rcount;
+ /* If we read less than requested. we are done */
+ if (rcount < iov[i].iov_len) {
+ break;
+ }
+ }
+ return count;
+}
+
+static int v9fs_synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int v9fs_synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_mknod(FsContext *fs_ctx, V9fsPath *path,
+ const char *buf, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_mkdir(FsContext *fs_ctx, V9fsPath *path,
+ const char *buf, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static ssize_t v9fs_synth_readlink(FsContext *fs_ctx, V9fsPath *path,
+ char *buf, size_t bufsz)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int v9fs_synth_symlink(FsContext *fs_ctx, const char *oldpath,
+ V9fsPath *newpath, const char *buf, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_link(FsContext *fs_ctx, V9fsPath *oldpath,
+ V9fsPath *newpath, const char *buf)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_rename(FsContext *ctx, const char *oldpath,
+ const char *newpath)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
+ const struct timespec *buf)
+{
+ errno = EPERM;
+ return 0;
+}
+
+static int v9fs_synth_remove(FsContext *ctx, const char *path)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync)
+{
+ errno = ENOSYS;
+ return 0;
+}
+
+static int v9fs_synth_statfs(FsContext *s, V9fsPath *fs_path,
+ struct statfs *stbuf)
+{
+ stbuf->f_type = 0xABCD;
+ stbuf->f_bsize = 512;
+ stbuf->f_blocks = 0;
+ stbuf->f_files = v9fs_synth_node_count;
+ stbuf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+static ssize_t v9fs_synth_lgetxattr(FsContext *ctx, V9fsPath *path,
+ const char *name, void *value, size_t size)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static ssize_t v9fs_synth_llistxattr(FsContext *ctx, V9fsPath *path,
+ void *value, size_t size)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path,
+ const char *name, void *value,
+ size_t size, int flags)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static int v9fs_synth_lremovexattr(FsContext *ctx,
+ V9fsPath *path, const char *name)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
+ const char *name, V9fsPath *target)
+{
+ V9fsSynthNode *node;
+ V9fsSynthNode *dir_node;
+
+ /* "." and ".." are not allowed */
+ if (!strcmp(name, ".") || !strcmp(name, "..")) {
+ errno = EINVAL;
+ return -1;
+
+ }
+ if (!dir_path) {
+ dir_node = &v9fs_synth_root;
+ } else {
+ dir_node = *(V9fsSynthNode **)dir_path->data;
+ }
+ if (!strcmp(name, "/")) {
+ node = dir_node;
+ goto out;
+ }
+ /* search for the name in the childern */
+ rcu_read_lock();
+ QLIST_FOREACH(node, &dir_node->child, sibling) {
+ if (!strcmp(node->name, name)) {
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (!node) {
+ errno = ENOENT;
+ return -1;
+ }
+out:
+ /* Copy the node pointer to fid */
+ target->data = g_malloc(sizeof(void *));
+ memcpy(target->data, &node, sizeof(void *));
+ target->size = sizeof(void *);
+ return 0;
+}
+
+static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir,
+ const char *old_name, V9fsPath *newdir,
+ const char *new_name)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir,
+ const char *name, int flags)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_init(FsContext *ctx)
+{
+ QLIST_INIT(&v9fs_synth_root.child);
+ qemu_mutex_init(&v9fs_synth_mutex);
+
+ /* Add "." and ".." entries for root */
+ v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
+ "..", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
+ v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
+ ".", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
+
+ /* Mark the subsystem is ready for use */
+ v9fs_synth_fs = 1;
+ return 0;
+}
+
+FileOperations synth_ops = {
+ .init = v9fs_synth_init,
+ .lstat = v9fs_synth_lstat,
+ .readlink = v9fs_synth_readlink,
+ .close = v9fs_synth_close,
+ .closedir = v9fs_synth_closedir,
+ .open = v9fs_synth_open,
+ .opendir = v9fs_synth_opendir,
+ .rewinddir = v9fs_synth_rewinddir,
+ .telldir = v9fs_synth_telldir,
+ .readdir_r = v9fs_synth_readdir_r,
+ .seekdir = v9fs_synth_seekdir,
+ .preadv = v9fs_synth_preadv,
+ .pwritev = v9fs_synth_pwritev,
+ .chmod = v9fs_synth_chmod,
+ .mknod = v9fs_synth_mknod,
+ .mkdir = v9fs_synth_mkdir,
+ .fstat = v9fs_synth_fstat,
+ .open2 = v9fs_synth_open2,
+ .symlink = v9fs_synth_symlink,
+ .link = v9fs_synth_link,
+ .truncate = v9fs_synth_truncate,
+ .rename = v9fs_synth_rename,
+ .chown = v9fs_synth_chown,
+ .utimensat = v9fs_synth_utimensat,
+ .remove = v9fs_synth_remove,
+ .fsync = v9fs_synth_fsync,
+ .statfs = v9fs_synth_statfs,
+ .lgetxattr = v9fs_synth_lgetxattr,
+ .llistxattr = v9fs_synth_llistxattr,
+ .lsetxattr = v9fs_synth_lsetxattr,
+ .lremovexattr = v9fs_synth_lremovexattr,
+ .name_to_path = v9fs_synth_name_to_path,
+ .renameat = v9fs_synth_renameat,
+ .unlinkat = v9fs_synth_unlinkat,
+};
diff --git a/hw/9pfs/virtio-9p-synth.h b/hw/9pfs/virtio-9p-synth.h
new file mode 100644
index 0000000000..e03f434633
--- /dev/null
+++ b/hw/9pfs/virtio-9p-synth.h
@@ -0,0 +1,50 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <limits.h>
+
+typedef struct V9fsSynthNode V9fsSynthNode;
+typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset,
+ void *arg);
+typedef ssize_t (*v9fs_synth_write)(void *buf, int len, off_t offset,
+ void *arg);
+typedef struct V9fsSynthNodeAttr {
+ int mode;
+ int inode;
+ int nlink;
+ v9fs_synth_read read;
+ v9fs_synth_write write;
+} V9fsSynthNodeAttr;
+
+struct V9fsSynthNode {
+ QLIST_HEAD(, V9fsSynthNode) child;
+ QLIST_ENTRY(V9fsSynthNode) sibling;
+ char name[NAME_MAX];
+ V9fsSynthNodeAttr *attr;
+ V9fsSynthNodeAttr actual_attr;
+ void *private;
+ int open_count;
+};
+
+typedef struct V9fsSynthOpenState {
+ off_t offset;
+ V9fsSynthNode *node;
+} V9fsSynthOpenState;
+
+extern int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
+ const char *name, V9fsSynthNode **result);
+extern int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
+ const char *name, v9fs_synth_read read,
+ v9fs_synth_write write, void *arg);
diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
index aab3bebcc7..0777ece816 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -455,11 +455,11 @@ static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp)
if (fidp->fid_type == P9_FID_FILE) {
/* If we reclaimed the fd no need to close */
if (fidp->fs.fd != -1) {
- retval = v9fs_co_close(pdu, fidp->fs.fd);
+ retval = v9fs_co_close(pdu, &fidp->fs);
}
} else if (fidp->fid_type == P9_FID_DIR) {
if (fidp->fs.dir != NULL) {
- retval = v9fs_co_closedir(pdu, fidp->fs.dir);
+ retval = v9fs_co_closedir(pdu, &fidp->fs);
}
} else if (fidp->fid_type == P9_FID_XATTR) {
retval = v9fs_xattr_fid_clunk(pdu, fidp);
@@ -567,9 +567,9 @@ void v9fs_reclaim_fd(V9fsPDU *pdu)
f = reclaim_list;
reclaim_list = f->rclm_lst;
if (f->fid_type == P9_FID_FILE) {
- v9fs_co_close(pdu, f->fs_reclaim.fd);
+ v9fs_co_close(pdu, &f->fs_reclaim);
} else if (f->fid_type == P9_FID_DIR) {
- v9fs_co_closedir(pdu, f->fs_reclaim.dir);
+ v9fs_co_closedir(pdu, &f->fs_reclaim);
}
f->rclm_lst = NULL;
/*
@@ -969,7 +969,7 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
if (s->proto_version == V9FS_PROTO_2000L) {
id = P9_RLERROR;
}
- trace_complete_pdu(pdu->tag, pdu->id, err); /* Trace ERROR */
+ trace_v9fs_rerror(pdu->tag, pdu->id, err); /* Trace ERROR */
}
/* fill out the header */
@@ -1271,6 +1271,11 @@ static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len)
dst->size++;
}
+static inline bool is_ro_export(FsContext *ctx)
+{
+ return ctx->export_flags & V9FS_RDONLY;
+}
+
static void v9fs_version(void *opaque)
{
V9fsPDU *pdu = opaque;
@@ -1332,11 +1337,11 @@ static void v9fs_attach(void *opaque)
}
offset += pdu_marshal(pdu, offset, "Q", &qid);
err = offset;
+ trace_v9fs_attach_return(pdu->tag, pdu->id,
+ qid.type, qid.version, qid.path);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_attach_return(pdu->tag, pdu->id,
- qid.type, qid.version, qid.path);
complete_pdu(s, pdu, err);
v9fs_string_free(&uname);
v9fs_string_free(&aname);
@@ -1371,13 +1376,12 @@ static void v9fs_stat(void *opaque)
}
offset += pdu_marshal(pdu, offset, "wS", 0, &v9stat);
err = offset;
+ trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode,
+ v9stat.atime, v9stat.mtime, v9stat.length);
v9fs_stat_free(&v9stat);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode,
- v9stat.atime, v9stat.mtime, v9stat.length);
-
complete_pdu(s, pdu, err);
}
@@ -1421,13 +1425,12 @@ static void v9fs_getattr(void *opaque)
}
retval = offset;
retval += pdu_marshal(pdu, offset, "A", &v9stat_dotl);
-out:
- put_fid(pdu, fidp);
-out_nofid:
trace_v9fs_getattr_return(pdu->tag, pdu->id, v9stat_dotl.st_result_mask,
v9stat_dotl.st_mode, v9stat_dotl.st_uid,
v9stat_dotl.st_gid);
-
+out:
+ put_fid(pdu, fidp);
+out_nofid:
complete_pdu(s, pdu, retval);
}
@@ -1605,6 +1608,7 @@ static void v9fs_walk(void *opaque)
v9fs_path_copy(&newfidp->path, &path);
}
err = v9fs_walk_marshal(pdu, nwnames, qids);
+ trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids);
out:
put_fid(pdu, fidp);
if (newfidp) {
@@ -1613,7 +1617,6 @@ out:
v9fs_path_free(&dpath);
v9fs_path_free(&path);
out_nofid:
- trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids);
complete_pdu(s, pdu, err);
if (nwnames && nwnames <= P9_MAXWELEM) {
for (name_idx = 0; name_idx < nwnames; name_idx++) {
@@ -1648,10 +1651,10 @@ static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path)
static void v9fs_open(void *opaque)
{
int flags;
- int iounit;
int32_t fid;
int32_t mode;
V9fsQID qid;
+ int iounit = 0;
ssize_t err = 0;
size_t offset = 7;
struct stat stbuf;
@@ -1692,6 +1695,14 @@ static void v9fs_open(void *opaque)
} else {
flags = omode_to_uflags(mode);
}
+ if (is_ro_export(&s->ctx)) {
+ if (mode & O_WRONLY || mode & O_RDWR ||
+ mode & O_APPEND || mode & O_TRUNC) {
+ err = -EROFS;
+ goto out;
+ }
+ flags |= O_NOATIME;
+ }
err = v9fs_co_open(pdu, fidp, flags);
if (err < 0) {
goto out;
@@ -1709,11 +1720,11 @@ static void v9fs_open(void *opaque)
offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit);
err = offset;
}
+ trace_v9fs_open_return(pdu->tag, pdu->id,
+ qid.type, qid.version, qid.path, iounit);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_open_return(pdu->tag, pdu->id,
- qid.type, qid.version, qid.path, iounit);
complete_pdu(s, pdu, err);
}
@@ -1759,11 +1770,11 @@ static void v9fs_lcreate(void *opaque)
stat_to_qid(&stbuf, &qid);
offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit);
err = offset;
+ trace_v9fs_lcreate_return(pdu->tag, pdu->id,
+ qid.type, qid.version, qid.path, iounit);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_lcreate_return(pdu->tag, pdu->id,
- qid.type, qid.version, qid.path, iounit);
complete_pdu(pdu->s, pdu, err);
v9fs_string_free(&name);
}
@@ -1978,10 +1989,10 @@ static void v9fs_read(void *opaque)
} else {
err = -EINVAL;
}
+ trace_v9fs_read_return(pdu->tag, pdu->id, count, err);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_read_return(pdu->tag, pdu->id, count, err);
complete_pdu(s, pdu, err);
}
@@ -2090,10 +2101,10 @@ static void v9fs_readdir(void *opaque)
retval = offset;
retval += pdu_marshal(pdu, offset, "d", count);
retval += count;
+ trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval);
complete_pdu(s, pdu, retval);
}
@@ -2202,10 +2213,10 @@ static void v9fs_write(void *opaque)
} while (total < count && len > 0);
offset += pdu_marshal(pdu, offset, "d", total);
err = offset;
+ trace_v9fs_write_return(pdu->tag, pdu->id, total, err);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_write_return(pdu->tag, pdu->id, total, err);
complete_pdu(s, pdu, err);
}
@@ -2362,11 +2373,11 @@ static void v9fs_create(void *opaque)
stat_to_qid(&stbuf, &qid);
offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit);
err = offset;
+ trace_v9fs_create_return(pdu->tag, pdu->id,
+ qid.type, qid.version, qid.path, iounit);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_create_return(pdu->tag, pdu->id,
- qid.type, qid.version, qid.path, iounit);
complete_pdu(pdu->s, pdu, err);
v9fs_string_free(&name);
v9fs_string_free(&extension);
@@ -2401,11 +2412,11 @@ static void v9fs_symlink(void *opaque)
stat_to_qid(&stbuf, &qid);
offset += pdu_marshal(pdu, offset, "Q", &qid);
err = offset;
+ trace_v9fs_symlink_return(pdu->tag, pdu->id,
+ qid.type, qid.version, qid.path);
out:
put_fid(pdu, dfidp);
out_nofid:
- trace_v9fs_symlink_return(pdu->tag, pdu->id,
- qid.type, qid.version, qid.path);
complete_pdu(pdu->s, pdu, err);
v9fs_string_free(&name);
v9fs_string_free(&symname);
@@ -2950,10 +2961,11 @@ static void v9fs_mknod(void *opaque)
stat_to_qid(&stbuf, &qid);
err = offset;
err += pdu_marshal(pdu, offset, "Q", &qid);
+ trace_v9fs_mknod_return(pdu->tag, pdu->id,
+ qid.type, qid.version, qid.path);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_mknod_return(pdu->tag, pdu->id, qid.type, qid.version, qid.path);
complete_pdu(s, pdu, err);
v9fs_string_free(&name);
}
@@ -2997,7 +3009,7 @@ static void v9fs_lock(void *opaque)
err = -ENOENT;
goto out_nofid;
}
- err = v9fs_co_fstat(pdu, fidp->fs.fd, &stbuf);
+ err = v9fs_co_fstat(pdu, fidp, &stbuf);
if (err < 0) {
goto out;
}
@@ -3040,7 +3052,7 @@ static void v9fs_getlock(void *opaque)
err = -ENOENT;
goto out_nofid;
}
- err = v9fs_co_fstat(pdu, fidp->fs.fd, &stbuf);
+ err = v9fs_co_fstat(pdu, fidp, &stbuf);
if (err < 0) {
goto out;
}
@@ -3049,12 +3061,11 @@ static void v9fs_getlock(void *opaque)
glock->start, glock->length, glock->proc_id,
&glock->client_id);
err = offset;
+ trace_v9fs_getlock_return(pdu->tag, pdu->id, glock->type, glock->start,
+ glock->length, glock->proc_id);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_getlock_return(pdu->tag, pdu->id, glock->type, glock->start,
- glock->length, glock->proc_id);
-
complete_pdu(s, pdu, err);
v9fs_string_free(&glock->client_id);
g_free(glock);
@@ -3089,11 +3100,11 @@ static void v9fs_mkdir(void *opaque)
stat_to_qid(&stbuf, &qid);
offset += pdu_marshal(pdu, offset, "Q", &qid);
err = offset;
+ trace_v9fs_mkdir_return(pdu->tag, pdu->id,
+ qid.type, qid.version, qid.path, err);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_mkdir_return(pdu->tag, pdu->id,
- qid.type, qid.version, qid.path, err);
complete_pdu(pdu->s, pdu, err);
v9fs_string_free(&name);
}
@@ -3183,13 +3194,13 @@ static void v9fs_xattrwalk(void *opaque)
offset += pdu_marshal(pdu, offset, "q", size);
err = offset;
}
+ trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size);
out:
put_fid(pdu, file_fidp);
if (xattr_fidp) {
put_fid(pdu, xattr_fidp);
}
out_nofid:
- trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size);
complete_pdu(s, pdu, err);
v9fs_string_free(&name);
}
@@ -3260,11 +3271,11 @@ static void v9fs_readlink(void *opaque)
}
offset += pdu_marshal(pdu, offset, "s", &target);
err = offset;
+ trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data);
v9fs_string_free(&target);
out:
put_fid(pdu, fidp);
out_nofid:
- trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data);
complete_pdu(pdu->s, pdu, err);
}
@@ -3311,6 +3322,39 @@ static void v9fs_op_not_supp(void *opaque)
complete_pdu(pdu->s, pdu, -EOPNOTSUPP);
}
+static void v9fs_fs_ro(void *opaque)
+{
+ V9fsPDU *pdu = opaque;
+ complete_pdu(pdu->s, pdu, -EROFS);
+}
+
+static inline bool is_read_only_op(V9fsPDU *pdu)
+{
+ switch (pdu->id) {
+ case P9_TREADDIR:
+ case P9_TSTATFS:
+ case P9_TGETATTR:
+ case P9_TXATTRWALK:
+ case P9_TLOCK:
+ case P9_TGETLOCK:
+ case P9_TREADLINK:
+ case P9_TVERSION:
+ case P9_TLOPEN:
+ case P9_TATTACH:
+ case P9_TSTAT:
+ case P9_TWALK:
+ case P9_TCLUNK:
+ case P9_TFSYNC:
+ case P9_TOPEN:
+ case P9_TREAD:
+ case P9_TAUTH:
+ case P9_TFLUSH:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
{
Coroutine *co;
@@ -3322,6 +3366,10 @@ static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
} else {
handler = pdu_co_handlers[pdu->id];
}
+
+ if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
+ handler = v9fs_fs_ro;
+ }
co = qemu_coroutine_create(handler);
qemu_coroutine_enter(co, pdu);
}
diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h
index 802f5809d1..7f883563d6 100644
--- a/hw/9pfs/virtio-9p.h
+++ b/hw/9pfs/virtio-9p.h
@@ -204,20 +204,29 @@ typedef struct V9fsXattr
int flags;
} V9fsXattr;
+/*
+ * Filled by fs driver on open and other
+ * calls.
+ */
+union V9fsFidOpenState {
+ int fd;
+ DIR *dir;
+ V9fsXattr xattr;
+ /*
+ * private pointer for fs drivers, that
+ * have its own internal representation of
+ * open files.
+ */
+ void *private;
+};
+
struct V9fsFidState
{
int fid_type;
int32_t fid;
V9fsPath path;
- union {
- int fd;
- DIR *dir;
- V9fsXattr xattr;
- } fs;
- union {
- int fd;
- DIR *dir;
- } fs_reclaim;
+ V9fsFidOpenState fs;
+ V9fsFidOpenState fs_reclaim;
int flags;
int open_flags;
uid_t uid;
diff --git a/hw/acpi_piix4.c b/hw/acpi_piix4.c
index 29f0f76c35..d9075e6611 100644
--- a/hw/acpi_piix4.c
+++ b/hw/acpi_piix4.c
@@ -276,7 +276,7 @@ static void piix4_update_hotplug(PIIX4PMState *s)
s->pci0_hotplug_enable = ~0;
- QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
+ QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev);
PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, qdev);
int slot = PCI_SLOT(pdev->devfn);
@@ -486,7 +486,7 @@ static void pciej_write(void *opaque, uint32_t addr, uint32_t val)
PCIDeviceInfo *info;
int slot = ffs(val) - 1;
- QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
+ QTAILQ_FOREACH_SAFE(qdev, &bus->children, sibling, next) {
dev = DO_UPCAST(PCIDevice, qdev, qdev);
info = container_of(qdev->info, PCIDeviceInfo, qdev);
if (PCI_SLOT(dev->devfn) == slot && !info->no_hotplug) {
diff --git a/hw/audiodev.h b/hw/audiodev.h
index 8e930b21ae..d60c3498ee 100644
--- a/hw/audiodev.h
+++ b/hw/audiodev.h
@@ -11,7 +11,7 @@ int Adlib_init(qemu_irq *pic);
int GUS_init(qemu_irq *pic);
/* ac97.c */
-int ac97_init(PCIBus *buf);
+int ac97_init(PCIBus *bus);
/* cs4231a.c */
int cs4231a_init(qemu_irq *pic);
diff --git a/hw/esp.c b/hw/esp.c
index 697c2c5b80..b698a43fe6 100644
--- a/hw/esp.c
+++ b/hw/esp.c
@@ -217,7 +217,8 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
s->async_len = 0;
}
- if (target >= ESP_MAX_DEVS || !s->bus.devs[target]) {
+ s->current_dev = scsi_device_find(&s->bus, 0, target, 0);
+ if (!s->current_dev) {
// No such drive
s->rregs[ESP_RSTAT] = 0;
s->rregs[ESP_RINTR] = INTR_DC;
@@ -225,7 +226,6 @@ static uint32_t get_cmd(ESPState *s, uint8_t *buf)
esp_raise_irq(s);
return 0;
}
- s->current_dev = s->bus.devs[target];
return dmalen;
}
@@ -233,10 +233,12 @@ static void do_busid_cmd(ESPState *s, uint8_t *buf, uint8_t busid)
{
int32_t datalen;
int lun;
+ SCSIDevice *current_lun;
trace_esp_do_busid_cmd(busid);
lun = busid & 7;
- s->current_req = scsi_req_new(s->current_dev, 0, lun, buf, NULL);
+ current_lun = scsi_device_find(&s->bus, 0, s->current_dev->id, lun);
+ s->current_req = scsi_req_new(current_lun, 0, lun, buf, NULL);
datalen = scsi_req_enqueue(s->current_req);
s->ti_size = datalen;
if (datalen != 0) {
@@ -720,7 +722,11 @@ void esp_init(target_phys_addr_t espaddr, int it_shift,
*dma_enable = qdev_get_gpio_in(dev, 1);
}
-static const struct SCSIBusOps esp_scsi_ops = {
+static const struct SCSIBusInfo esp_scsi_info = {
+ .tcq = false,
+ .max_target = ESP_MAX_DEVS,
+ .max_lun = 7,
+
.transfer_data = esp_transfer_data,
.complete = esp_command_complete,
.cancel = esp_request_cancelled
@@ -740,7 +746,7 @@ static int esp_init1(SysBusDevice *dev)
qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2);
- scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, &esp_scsi_ops);
+ scsi_bus_new(&s->bus, &dev->qdev, &esp_scsi_info);
return scsi_bus_legacy_handle_cmdline(&s->bus);
}
diff --git a/hw/i2c.c b/hw/i2c.c
index 49b9ecb8b6..9bcf3e1d31 100644
--- a/hw/i2c.c
+++ b/hw/i2c.c
@@ -84,7 +84,7 @@ int i2c_start_transfer(i2c_bus *bus, uint8_t address, int recv)
DeviceState *qdev;
i2c_slave *slave = NULL;
- QLIST_FOREACH(qdev, &bus->qbus.children, sibling) {
+ QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) {
i2c_slave *candidate = I2C_SLAVE_FROM_QDEV(qdev);
if (candidate->address == address) {
slave = candidate;
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 1c7e3a00b5..0af201de2f 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -327,7 +327,7 @@ static void ahci_mem_write(void *opaque, target_phys_addr_t addr,
}
if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) {
- DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val);
+ DPRINTF(-1, "(addr 0x%08X), val 0x%08"PRIX64"\n", (unsigned) addr, val);
switch (addr) {
case HOST_CAP: /* R/WO, RO */
@@ -777,7 +777,8 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) |
ncq_fis->sector_count_low;
- DPRINTF(port, "NCQ transfer LBA from %ld to %ld, drive max %ld\n",
+ DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", "
+ "drive max %"PRId64"\n",
ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2,
s->dev[port].port.ifs[0].nb_sectors - 1);
@@ -786,10 +787,12 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
switch(ncq_fis->command) {
case READ_FPDMA_QUEUED:
- DPRINTF(port, "NCQ reading %d sectors from LBA %ld, tag %d\n",
+ DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", "
+ "tag %d\n",
ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag);
- DPRINTF(port, "tag %d aio read %ld\n", ncq_tfs->tag, ncq_tfs->lba);
+ DPRINTF(port, "tag %d aio read %"PRId64"\n",
+ ncq_tfs->tag, ncq_tfs->lba);
bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
(ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE,
@@ -799,10 +802,11 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
ncq_cb, ncq_tfs);
break;
case WRITE_FPDMA_QUEUED:
- DPRINTF(port, "NCQ writing %d sectors to LBA %ld, tag %d\n",
+ DPRINTF(port, "NCQ writing %d sectors to LBA %"PRId64", tag %d\n",
ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag);
- DPRINTF(port, "tag %d aio write %ld\n", ncq_tfs->tag, ncq_tfs->lba);
+ DPRINTF(port, "tag %d aio write %"PRId64"\n",
+ ncq_tfs->tag, ncq_tfs->lba);
bdrv_acct_start(ncq_tfs->drive->port.ifs[0].bs, &ncq_tfs->acct,
(ncq_tfs->sector_count-1) * BDRV_SECTOR_SIZE,
diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c
index 3f909c3a99..90b6729692 100644
--- a/hw/ide/atapi.c
+++ b/hw/ide/atapi.c
@@ -154,10 +154,10 @@ void ide_atapi_io_error(IDEState *s, int ret)
{
/* XXX: handle more errors */
if (ret == -ENOMEDIUM) {
- ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ide_atapi_cmd_error(s, NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
} else {
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
ASC_LOGICAL_BLOCK_OOR);
}
}
@@ -282,7 +282,7 @@ static void ide_atapi_cmd_check_status(IDEState *s)
#ifdef DEBUG_IDE_ATAPI
printf("atapi_cmd_check_status\n");
#endif
- s->error = MC_ERR | (SENSE_UNIT_ATTENTION << 4);
+ s->error = MC_ERR | (UNIT_ATTENTION << 4);
s->status = ERR_STAT;
s->nsector = 0;
ide_set_irq(s->bus);
@@ -354,7 +354,7 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret)
ide_atapi_cmd_read_dma_cb, s);
if (!s->bus->dma->aiocb) {
/* Note: media not present is the most likely case */
- ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ide_atapi_cmd_error(s, NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
goto eot;
}
@@ -505,19 +505,6 @@ static int ide_dvd_read_structure(IDEState *s, int format,
static unsigned int event_status_media(IDEState *s,
uint8_t *buf)
{
- enum media_event_code {
- MEC_NO_CHANGE = 0, /* Status unchanged */
- MEC_EJECT_REQUESTED, /* received a request from user to eject */
- MEC_NEW_MEDIA, /* new media inserted and ready for access */
- MEC_MEDIA_REMOVAL, /* only for media changers */
- MEC_MEDIA_CHANGED, /* only for media changers */
- MEC_BG_FORMAT_COMPLETED, /* MRW or DVD+RW b/g format completed */
- MEC_BG_FORMAT_RESTARTED, /* MRW or DVD+RW b/g format restarted */
- };
- enum media_status {
- MS_TRAY_OPEN = 1,
- MS_MEDIA_PRESENT = 2,
- };
uint8_t event_code, media_status;
media_status = 0;
@@ -564,27 +551,6 @@ static void cmd_get_event_status_notification(IDEState *s,
uint8_t notification_class;
uint8_t supported_events;
} QEMU_PACKED *gesn_event_header;
-
- enum notification_class_request_type {
- NCR_RESERVED1 = 1 << 0,
- NCR_OPERATIONAL_CHANGE = 1 << 1,
- NCR_POWER_MANAGEMENT = 1 << 2,
- NCR_EXTERNAL_REQUEST = 1 << 3,
- NCR_MEDIA = 1 << 4,
- NCR_MULTI_HOST = 1 << 5,
- NCR_DEVICE_BUSY = 1 << 6,
- NCR_RESERVED2 = 1 << 7,
- };
- enum event_notification_class_field {
- ENC_NO_EVENTS = 0,
- ENC_OPERATIONAL_CHANGE,
- ENC_POWER_MANAGEMENT,
- ENC_EXTERNAL_REQUEST,
- ENC_MEDIA,
- ENC_MULTIPLE_HOSTS,
- ENC_DEVICE_BUSY,
- ENC_RESERVED,
- };
unsigned int max_len, used_len;
gesn_cdb = (void *)packet;
@@ -595,7 +561,7 @@ static void cmd_get_event_status_notification(IDEState *s,
/* It is fine by the MMC spec to not support async mode operations */
if (!(gesn_cdb->polled & 0x01)) { /* asynchronous mode */
/* Only polling is supported, asynchronous mode is not. */
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
ASC_INV_FIELD_IN_CMD_PACKET);
return;
}
@@ -606,8 +572,11 @@ static void cmd_get_event_status_notification(IDEState *s,
* These are the supported events.
*
* We currently only support requests of the 'media' type.
+ * Notification class requests and supported event classes are bitmasks,
+ * but they are build from the same values as the "notification class"
+ * field.
*/
- gesn_event_header->supported_events = NCR_MEDIA;
+ gesn_event_header->supported_events = 1 << GESN_MEDIA;
/*
* We use |= below to set the class field; other bits in this byte
@@ -621,8 +590,8 @@ static void cmd_get_event_status_notification(IDEState *s,
* notification_class_request_type enum above specifies the
* priority: upper elements are higher prio than lower ones.
*/
- if (gesn_cdb->class & NCR_MEDIA) {
- gesn_event_header->notification_class |= ENC_MEDIA;
+ if (gesn_cdb->class & (1 << GESN_MEDIA)) {
+ gesn_event_header->notification_class |= GESN_MEDIA;
used_len = event_status_media(s, buf);
} else {
gesn_event_header->notification_class = 0x80; /* No event available */
@@ -643,8 +612,8 @@ static void cmd_request_sense(IDEState *s, uint8_t *buf)
buf[7] = 10;
buf[12] = s->asc;
- if (s->sense_key == SENSE_UNIT_ATTENTION) {
- s->sense_key = SENSE_NONE;
+ if (s->sense_key == UNIT_ATTENTION) {
+ s->sense_key = NO_SENSE;
}
ide_atapi_cmd_reply(s, 18, max_len);
@@ -676,7 +645,7 @@ static void cmd_get_configuration(IDEState *s, uint8_t *buf)
/* only feature 0 is supported */
if (buf[2] != 0 || buf[3] != 0) {
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
ASC_INV_FIELD_IN_CMD_PACKET);
return;
}
@@ -733,7 +702,7 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
switch(action) {
case 0: /* current values */
switch(code) {
- case GPMODE_R_W_ERROR_PAGE: /* error recovery */
+ case MODE_PAGE_R_W_ERROR: /* error recovery */
cpu_to_ube16(&buf[0], 16 + 6);
buf[2] = 0x70;
buf[3] = 0;
@@ -742,8 +711,8 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
buf[6] = 0;
buf[7] = 0;
- buf[8] = 0x01;
- buf[9] = 0x06;
+ buf[8] = MODE_PAGE_R_W_ERROR;
+ buf[9] = 16 - 10;
buf[10] = 0x00;
buf[11] = 0x05;
buf[12] = 0x00;
@@ -752,7 +721,7 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
buf[15] = 0x00;
ide_atapi_cmd_reply(s, 16, max_len);
break;
- case GPMODE_AUDIO_CTL_PAGE:
+ case MODE_PAGE_AUDIO_CTL:
cpu_to_ube16(&buf[0], 24 + 6);
buf[2] = 0x70;
buf[3] = 0;
@@ -761,6 +730,8 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
buf[6] = 0;
buf[7] = 0;
+ buf[8] = MODE_PAGE_AUDIO_CTL;
+ buf[9] = 24 - 10;
/* Fill with CDROM audio volume */
buf[17] = 0;
buf[19] = 0;
@@ -769,7 +740,7 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
ide_atapi_cmd_reply(s, 24, max_len);
break;
- case GPMODE_CAPABILITIES_PAGE:
+ case MODE_PAGE_CAPABILITIES:
cpu_to_ube16(&buf[0], 28 + 6);
buf[2] = 0x70;
buf[3] = 0;
@@ -778,9 +749,9 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
buf[6] = 0;
buf[7] = 0;
- buf[8] = 0x2a;
- buf[9] = 0x12;
- buf[10] = 0x00;
+ buf[8] = MODE_PAGE_CAPABILITIES;
+ buf[9] = 28 - 10;
+ buf[10] = 0x3b; /* read CDR/CDRW/DVDROM/DVDR/DVDRAM */
buf[11] = 0x00;
/* Claim PLAY_AUDIO capability (0x01) since some Linux
@@ -789,14 +760,14 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
buf[13] = 3 << 5;
buf[14] = (1 << 0) | (1 << 3) | (1 << 5);
if (s->tray_locked) {
- buf[6] |= 1 << 1;
+ buf[14] |= 1 << 1;
}
- buf[15] = 0x00;
- cpu_to_ube16(&buf[16], 706);
- buf[18] = 0;
+ buf[15] = 0x00; /* No volume & mute control, no changer */
+ cpu_to_ube16(&buf[16], 704); /* 4x read speed */
+ buf[18] = 0; /* Two volume levels */
buf[19] = 2;
- cpu_to_ube16(&buf[20], 512);
- cpu_to_ube16(&buf[22], 706);
+ cpu_to_ube16(&buf[20], 512); /* 512k buffer */
+ cpu_to_ube16(&buf[22], 704); /* 4x read speed current */
buf[24] = 0;
buf[25] = 0;
buf[26] = 0;
@@ -813,14 +784,14 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf)
goto error_cmd;
default:
case 3: /* saved values */
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
break;
}
return;
error_cmd:
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
}
static void cmd_test_unit_ready(IDEState *s, uint8_t *buf)
@@ -883,7 +854,7 @@ static void cmd_read_cd(IDEState *s, uint8_t* buf)
ide_atapi_cmd_read(s, lba, nb_sectors, 2352);
break;
default:
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
ASC_INV_FIELD_IN_CMD_PACKET);
break;
}
@@ -896,7 +867,7 @@ static void cmd_seek(IDEState *s, uint8_t* buf)
lba = ube32_to_cpu(buf + 2);
if (lba >= total_sectors) {
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
return;
}
@@ -912,7 +883,7 @@ static void cmd_start_stop_unit(IDEState *s, uint8_t* buf)
if (loej) {
if (!start && !s->tray_open && s->tray_locked) {
sense = bdrv_is_inserted(s->bs)
- ? SENSE_NOT_READY : SENSE_ILLEGAL_REQUEST;
+ ? NOT_READY : ILLEGAL_REQUEST;
ide_atapi_cmd_error(s, sense, ASC_MEDIA_REMOVAL_PREVENTED);
return;
}
@@ -971,7 +942,7 @@ static void cmd_read_toc_pma_atip(IDEState *s, uint8_t* buf)
break;
default:
error_cmd:
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
ASC_INV_FIELD_IN_CMD_PACKET);
}
}
@@ -997,11 +968,11 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf)
if (format < 0xff) {
if (media_is_cd(s)) {
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
ASC_INCOMPATIBLE_FORMAT);
return;
} else if (!media_present(s)) {
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
ASC_INV_FIELD_IN_CMD_PACKET);
return;
}
@@ -1017,7 +988,7 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf)
ret = ide_dvd_read_structure(s, format, buf, buf);
if (ret < 0) {
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, -ret);
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST, -ret);
} else {
ide_atapi_cmd_reply(s, ret, max_len);
}
@@ -1034,7 +1005,7 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf)
case 0x90: /* TODO: List of recognized format layers */
case 0xc0: /* TODO: Write protection status */
default:
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST,
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST,
ASC_INV_FIELD_IN_CMD_PACKET);
break;
}
@@ -1106,7 +1077,7 @@ void ide_atapi_cmd(IDEState *s)
* condition response unless a higher priority status, defined by the drive
* here, is pending.
*/
- if (s->sense_key == SENSE_UNIT_ATTENTION &&
+ if (s->sense_key == UNIT_ATTENTION &&
!(atapi_cmd_table[s->io_buffer[0]].flags & ALLOW_UA)) {
ide_atapi_cmd_check_status(s);
return;
@@ -1119,10 +1090,10 @@ void ide_atapi_cmd(IDEState *s)
* states rely on this behavior.
*/
if (!s->tray_open && bdrv_is_inserted(s->bs) && s->cdrom_changed) {
- ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
s->cdrom_changed = 0;
- s->sense_key = SENSE_UNIT_ATTENTION;
+ s->sense_key = UNIT_ATTENTION;
s->asc = ASC_MEDIUM_MAY_HAVE_CHANGED;
return;
}
@@ -1131,7 +1102,7 @@ void ide_atapi_cmd(IDEState *s)
if ((atapi_cmd_table[s->io_buffer[0]].flags & CHECK_READY) &&
(!media_present(s) || !bdrv_is_inserted(s->bs)))
{
- ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ ide_atapi_cmd_error(s, NOT_READY, ASC_MEDIUM_NOT_PRESENT);
return;
}
@@ -1141,5 +1112,5 @@ void ide_atapi_cmd(IDEState *s)
return;
}
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE);
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE);
}
diff --git a/hw/ide/core.c b/hw/ide/core.c
index 280a117fe2..9a2fd30607 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -799,7 +799,7 @@ static void ide_cd_change_cb(void *opaque, bool load)
* First indicate to the guest that a CD has been removed. That's
* done on the next command the guest sends us.
*
- * Then we set SENSE_UNIT_ATTENTION, by which the guest will
+ * Then we set UNIT_ATTENTION, by which the guest will
* detect a new CD in the drive. See ide_atapi_cmd() for details.
*/
s->cdrom_changed = 1;
@@ -2027,7 +2027,7 @@ static int ide_drive_post_load(void *opaque, int version_id)
IDEState *s = opaque;
if (version_id < 3) {
- if (s->sense_key == SENSE_UNIT_ATTENTION &&
+ if (s->sense_key == UNIT_ATTENTION &&
s->asc == ASC_MEDIUM_MAY_HAVE_CHANGED) {
s->cdrom_changed = 1;
}
@@ -2039,7 +2039,7 @@ static int ide_drive_pio_post_load(void *opaque, int version_id)
{
IDEState *s = opaque;
- if (s->end_transfer_fn_idx > ARRAY_SIZE(transfer_end_table)) {
+ if (s->end_transfer_fn_idx >= ARRAY_SIZE(transfer_end_table)) {
return -EINVAL;
}
s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx];
diff --git a/hw/ide/internal.h b/hw/ide/internal.h
index c39dc058f4..00b28dfdbc 100644
--- a/hw/ide/internal.h
+++ b/hw/ide/internal.h
@@ -11,6 +11,7 @@
#include "iorange.h"
#include "dma.h"
#include "sysemu.h"
+#include "hw/scsi-defs.h"
/* debug IDE devices */
//#define DEBUG_IDE
@@ -280,71 +281,6 @@ typedef struct IDEDMAOps IDEDMAOps;
#define GPCMD_GET_MEDIA_STATUS 0xda
#define GPCMD_MODE_SENSE_6 0x1a
-/* Mode page codes for mode sense/set */
-#define GPMODE_R_W_ERROR_PAGE 0x01
-#define GPMODE_WRITE_PARMS_PAGE 0x05
-#define GPMODE_AUDIO_CTL_PAGE 0x0e
-#define GPMODE_POWER_PAGE 0x1a
-#define GPMODE_FAULT_FAIL_PAGE 0x1c
-#define GPMODE_TO_PROTECT_PAGE 0x1d
-#define GPMODE_CAPABILITIES_PAGE 0x2a
-#define GPMODE_ALL_PAGES 0x3f
-/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor
- * of MODE_SENSE_POWER_PAGE */
-#define GPMODE_CDROM_PAGE 0x0d
-
-/*
- * Based on values from <linux/cdrom.h> but extending CD_MINS
- * to the maximum common size allowed by the Orange's Book ATIP
- *
- * 90 and 99 min CDs are also available but using them as the
- * upper limit reduces the effectiveness of the heuristic to
- * detect DVDs burned to less than 25% of their maximum capacity
- */
-
-/* Some generally useful CD-ROM information */
-#define CD_MINS 80 /* max. minutes per CD */
-#define CD_SECS 60 /* seconds per minute */
-#define CD_FRAMES 75 /* frames per second */
-#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */
-#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE)
-#define CD_MAX_SECTORS (CD_MAX_BYTES / 512)
-
-/*
- * The MMC values are not IDE specific and might need to be moved
- * to a common header if they are also needed for the SCSI emulation
- */
-
-/* Profile list from MMC-6 revision 1 table 91 */
-#define MMC_PROFILE_NONE 0x0000
-#define MMC_PROFILE_CD_ROM 0x0008
-#define MMC_PROFILE_CD_R 0x0009
-#define MMC_PROFILE_CD_RW 0x000A
-#define MMC_PROFILE_DVD_ROM 0x0010
-#define MMC_PROFILE_DVD_R_SR 0x0011
-#define MMC_PROFILE_DVD_RAM 0x0012
-#define MMC_PROFILE_DVD_RW_RO 0x0013
-#define MMC_PROFILE_DVD_RW_SR 0x0014
-#define MMC_PROFILE_DVD_R_DL_SR 0x0015
-#define MMC_PROFILE_DVD_R_DL_JR 0x0016
-#define MMC_PROFILE_DVD_RW_DL 0x0017
-#define MMC_PROFILE_DVD_DDR 0x0018
-#define MMC_PROFILE_DVD_PLUS_RW 0x001A
-#define MMC_PROFILE_DVD_PLUS_R 0x001B
-#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A
-#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B
-#define MMC_PROFILE_BD_ROM 0x0040
-#define MMC_PROFILE_BD_R_SRM 0x0041
-#define MMC_PROFILE_BD_R_RRM 0x0042
-#define MMC_PROFILE_BD_RE 0x0043
-#define MMC_PROFILE_HDDVD_ROM 0x0050
-#define MMC_PROFILE_HDDVD_R 0x0051
-#define MMC_PROFILE_HDDVD_RAM 0x0052
-#define MMC_PROFILE_HDDVD_RW 0x0053
-#define MMC_PROFILE_HDDVD_R_DL 0x0058
-#define MMC_PROFILE_HDDVD_RW_DL 0x005A
-#define MMC_PROFILE_INVALID 0xFFFF
-
#define ATAPI_INT_REASON_CD 0x01 /* 0 = data transfer */
#define ATAPI_INT_REASON_IO 0x02 /* 1 = transfer to the host */
#define ATAPI_INT_REASON_REL 0x04
@@ -366,11 +302,6 @@ typedef struct IDEDMAOps IDEDMAOps;
#define CFA_INVALID_ADDRESS 0x21
#define CFA_ADDRESS_OVERFLOW 0x2f
-#define SENSE_NONE 0
-#define SENSE_NOT_READY 2
-#define SENSE_ILLEGAL_REQUEST 5
-#define SENSE_UNIT_ATTENTION 6
-
#define SMART_READ_DATA 0xd0
#define SMART_READ_THRESH 0xd1
#define SMART_ATTR_AUTOSAVE 0xd2
diff --git a/hw/ide/macio.c b/hw/ide/macio.c
index 37b8239b4d..70b33422d2 100644
--- a/hw/ide/macio.c
+++ b/hw/ide/macio.c
@@ -87,7 +87,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
if (!m->aiocb) {
qemu_sglist_destroy(&s->sg);
/* Note: media not present is the most likely case */
- ide_atapi_cmd_error(s, SENSE_NOT_READY,
+ ide_atapi_cmd_error(s, NOT_READY,
ASC_MEDIUM_NOT_PRESENT);
goto done;
}
diff --git a/hw/intel-hda.c b/hw/intel-hda.c
index f97775c235..675b6591e9 100644
--- a/hw/intel-hda.c
+++ b/hw/intel-hda.c
@@ -86,7 +86,7 @@ HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad)
DeviceState *qdev;
HDACodecDevice *cdev;
- QLIST_FOREACH(qdev, &bus->qbus.children, sibling) {
+ QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) {
cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
if (cdev->cad == cad) {
return cdev;
@@ -490,7 +490,7 @@ static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool runn
DeviceState *qdev;
HDACodecDevice *cdev;
- QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
+ QTAILQ_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
if (cdev->info->stream) {
cdev->info->stream(cdev, stream, running, output);
@@ -1114,7 +1114,7 @@ static void intel_hda_reset(DeviceState *dev)
d->wall_base_ns = qemu_get_clock_ns(vm_clock);
/* reset codecs */
- QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
+ QTAILQ_FOREACH(qdev, &d->codecs.qbus.children, sibling) {
cdev = DO_UPCAST(HDACodecDevice, qdev, qdev);
if (qdev->info->reset) {
qdev->info->reset(qdev);
diff --git a/hw/lm4549.c b/hw/lm4549.c
new file mode 100644
index 0000000000..4d5b83125f
--- /dev/null
+++ b/hw/lm4549.c
@@ -0,0 +1,336 @@
+/*
+ * LM4549 Audio Codec Interface
+ *
+ * Copyright (c) 2011
+ * Written by Mathieu Sonet - www.elasticsheep.com
+ *
+ * This code is licenced under the GPL.
+ *
+ * *****************************************************************
+ *
+ * This driver emulates the LM4549 codec.
+ *
+ * It supports only one playback voice and no record voice.
+ */
+
+#include "hw.h"
+#include "audio/audio.h"
+#include "lm4549.h"
+
+#if 0
+#define LM4549_DEBUG 1
+#endif
+
+#if 0
+#define LM4549_DUMP_DAC_INPUT 1
+#endif
+
+#ifdef LM4549_DEBUG
+#define DPRINTF(fmt, ...) \
+do { printf("lm4549: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+#if defined(LM4549_DUMP_DAC_INPUT)
+#include <stdio.h>
+static FILE *fp_dac_input;
+#endif
+
+/* LM4549 register list */
+enum {
+ LM4549_Reset = 0x00,
+ LM4549_Master_Volume = 0x02,
+ LM4549_Line_Out_Volume = 0x04,
+ LM4549_Master_Volume_Mono = 0x06,
+ LM4549_PC_Beep_Volume = 0x0A,
+ LM4549_Phone_Volume = 0x0C,
+ LM4549_Mic_Volume = 0x0E,
+ LM4549_Line_In_Volume = 0x10,
+ LM4549_CD_Volume = 0x12,
+ LM4549_Video_Volume = 0x14,
+ LM4549_Aux_Volume = 0x16,
+ LM4549_PCM_Out_Volume = 0x18,
+ LM4549_Record_Select = 0x1A,
+ LM4549_Record_Gain = 0x1C,
+ LM4549_General_Purpose = 0x20,
+ LM4549_3D_Control = 0x22,
+ LM4549_Powerdown_Ctrl_Stat = 0x26,
+ LM4549_Ext_Audio_ID = 0x28,
+ LM4549_Ext_Audio_Stat_Ctrl = 0x2A,
+ LM4549_PCM_Front_DAC_Rate = 0x2C,
+ LM4549_PCM_ADC_Rate = 0x32,
+ LM4549_Vendor_ID1 = 0x7C,
+ LM4549_Vendor_ID2 = 0x7E
+};
+
+static void lm4549_reset(lm4549_state *s)
+{
+ uint16_t *regfile = s->regfile;
+
+ regfile[LM4549_Reset] = 0x0d50;
+ regfile[LM4549_Master_Volume] = 0x8008;
+ regfile[LM4549_Line_Out_Volume] = 0x8000;
+ regfile[LM4549_Master_Volume_Mono] = 0x8000;
+ regfile[LM4549_PC_Beep_Volume] = 0x0000;
+ regfile[LM4549_Phone_Volume] = 0x8008;
+ regfile[LM4549_Mic_Volume] = 0x8008;
+ regfile[LM4549_Line_In_Volume] = 0x8808;
+ regfile[LM4549_CD_Volume] = 0x8808;
+ regfile[LM4549_Video_Volume] = 0x8808;
+ regfile[LM4549_Aux_Volume] = 0x8808;
+ regfile[LM4549_PCM_Out_Volume] = 0x8808;
+ regfile[LM4549_Record_Select] = 0x0000;
+ regfile[LM4549_Record_Gain] = 0x8000;
+ regfile[LM4549_General_Purpose] = 0x0000;
+ regfile[LM4549_3D_Control] = 0x0101;
+ regfile[LM4549_Powerdown_Ctrl_Stat] = 0x000f;
+ regfile[LM4549_Ext_Audio_ID] = 0x0001;
+ regfile[LM4549_Ext_Audio_Stat_Ctrl] = 0x0000;
+ regfile[LM4549_PCM_Front_DAC_Rate] = 0xbb80;
+ regfile[LM4549_PCM_ADC_Rate] = 0xbb80;
+ regfile[LM4549_Vendor_ID1] = 0x4e53;
+ regfile[LM4549_Vendor_ID2] = 0x4331;
+}
+
+static void lm4549_audio_transfer(lm4549_state *s)
+{
+ uint32_t written_bytes, written_samples;
+ uint32_t i;
+
+ /* Activate the voice */
+ AUD_set_active_out(s->voice, 1);
+ s->voice_is_active = 1;
+
+ /* Try to write the buffer content */
+ written_bytes = AUD_write(s->voice, s->buffer,
+ s->buffer_level * sizeof(uint16_t));
+ written_samples = written_bytes >> 1;
+
+#if defined(LM4549_DUMP_DAC_INPUT)
+ fwrite(s->buffer, sizeof(uint8_t), written_bytes, fp_dac_input);
+#endif
+
+ s->buffer_level -= written_samples;
+
+ if (s->buffer_level > 0) {
+ /* Move the data back to the start of the buffer */
+ for (i = 0; i < s->buffer_level; i++) {
+ s->buffer[i] = s->buffer[i + written_samples];
+ }
+ }
+}
+
+static void lm4549_audio_out_callback(void *opaque, int free)
+{
+ lm4549_state *s = (lm4549_state *)opaque;
+ static uint32_t prev_buffer_level;
+
+#ifdef LM4549_DEBUG
+ int size = AUD_get_buffer_size_out(s->voice);
+ DPRINTF("audio_out_callback size = %i free = %i\n", size, free);
+#endif
+
+ /* Detect that no data are consumed
+ => disable the voice */
+ if (s->buffer_level == prev_buffer_level) {
+ AUD_set_active_out(s->voice, 0);
+ s->voice_is_active = 0;
+ }
+ prev_buffer_level = s->buffer_level;
+
+ /* Check if a buffer transfer is pending */
+ if (s->buffer_level == LM4549_BUFFER_SIZE) {
+ lm4549_audio_transfer(s);
+
+ /* Request more data */
+ if (s->data_req_cb != NULL) {
+ (s->data_req_cb)(s->opaque);
+ }
+ }
+}
+
+uint32_t lm4549_read(lm4549_state *s, target_phys_addr_t offset)
+{
+ uint16_t *regfile = s->regfile;
+ uint32_t value = 0;
+
+ /* Read the stored value */
+ assert(offset < 128);
+ value = regfile[offset];
+
+ DPRINTF("read [0x%02x] = 0x%04x\n", offset, value);
+
+ return value;
+}
+
+void lm4549_write(lm4549_state *s,
+ target_phys_addr_t offset, uint32_t value)
+{
+ uint16_t *regfile = s->regfile;
+
+ assert(offset < 128);
+ DPRINTF("write [0x%02x] = 0x%04x\n", offset, value);
+
+ switch (offset) {
+ case LM4549_Reset:
+ lm4549_reset(s);
+ break;
+
+ case LM4549_PCM_Front_DAC_Rate:
+ regfile[LM4549_PCM_Front_DAC_Rate] = value;
+ DPRINTF("DAC rate change = %i\n", value);
+
+ /* Re-open a voice with the new sample rate */
+ struct audsettings as;
+ as.freq = value;
+ as.nchannels = 2;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out(
+ &s->card,
+ s->voice,
+ "lm4549.out",
+ s,
+ lm4549_audio_out_callback,
+ &as
+ );
+ break;
+
+ case LM4549_Powerdown_Ctrl_Stat:
+ value &= ~0xf;
+ value |= regfile[LM4549_Powerdown_Ctrl_Stat] & 0xf;
+ regfile[LM4549_Powerdown_Ctrl_Stat] = value;
+ break;
+
+ case LM4549_Ext_Audio_ID:
+ case LM4549_Vendor_ID1:
+ case LM4549_Vendor_ID2:
+ DPRINTF("Write to read-only register 0x%x\n", (int)offset);
+ break;
+
+ default:
+ /* Store the new value */
+ regfile[offset] = value;
+ break;
+ }
+}
+
+uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right)
+{
+ /* The left and right samples are in 20-bit resolution.
+ The LM4549 has 18-bit resolution and only uses the bits [19:2].
+ This model supports 16-bit playback.
+ */
+
+ if (s->buffer_level >= LM4549_BUFFER_SIZE) {
+ DPRINTF("write_sample Buffer full\n");
+ return 0;
+ }
+
+ /* Store 16-bit samples in the buffer */
+ s->buffer[s->buffer_level++] = (left >> 4);
+ s->buffer[s->buffer_level++] = (right >> 4);
+
+ if (s->buffer_level == LM4549_BUFFER_SIZE) {
+ /* Trigger the transfer of the buffer to the audio host */
+ lm4549_audio_transfer(s);
+ }
+
+ return 1;
+}
+
+static int lm4549_post_load(void *opaque, int version_id)
+{
+ lm4549_state *s = (lm4549_state *)opaque;
+ uint16_t *regfile = s->regfile;
+
+ /* Re-open a voice with the current sample rate */
+ uint32_t freq = regfile[LM4549_PCM_Front_DAC_Rate];
+
+ DPRINTF("post_load freq = %i\n", freq);
+ DPRINTF("post_load voice_is_active = %i\n", s->voice_is_active);
+
+ struct audsettings as;
+ as.freq = freq;
+ as.nchannels = 2;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out(
+ &s->card,
+ s->voice,
+ "lm4549.out",
+ s,
+ lm4549_audio_out_callback,
+ &as
+ );
+
+ /* Request data */
+ if (s->voice_is_active == 1) {
+ lm4549_audio_out_callback(s, AUD_get_buffer_size_out(s->voice));
+ }
+
+ return 0;
+}
+
+void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque)
+{
+ struct audsettings as;
+
+ /* Store the callback and opaque pointer */
+ s->data_req_cb = data_req_cb;
+ s->opaque = opaque;
+
+ /* Init the registers */
+ lm4549_reset(s);
+
+ /* Register an audio card */
+ AUD_register_card("lm4549", &s->card);
+
+ /* Open a default voice */
+ as.freq = 48000;
+ as.nchannels = 2;
+ as.fmt = AUD_FMT_S16;
+ as.endianness = 0;
+
+ s->voice = AUD_open_out(
+ &s->card,
+ s->voice,
+ "lm4549.out",
+ s,
+ lm4549_audio_out_callback,
+ &as
+ );
+
+ AUD_set_volume_out(s->voice, 0, 255, 255);
+
+ s->voice_is_active = 0;
+
+ /* Reset the input buffer */
+ memset(s->buffer, 0x00, sizeof(s->buffer));
+ s->buffer_level = 0;
+
+#if defined(LM4549_DUMP_DAC_INPUT)
+ fp_dac_input = fopen("lm4549_dac_input.pcm", "wb");
+ if (!fp_dac_input) {
+ hw_error("Unable to open lm4549_dac_input.pcm for writing\n");
+ }
+#endif
+}
+
+const VMStateDescription vmstate_lm4549_state = {
+ .name = "lm4549_state",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .post_load = &lm4549_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(voice_is_active, lm4549_state),
+ VMSTATE_UINT16_ARRAY(regfile, lm4549_state, 128),
+ VMSTATE_UINT16_ARRAY(buffer, lm4549_state, LM4549_BUFFER_SIZE),
+ VMSTATE_UINT32(buffer_level, lm4549_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/hw/lm4549.h b/hw/lm4549.h
new file mode 100644
index 0000000000..70d0ac1750
--- /dev/null
+++ b/hw/lm4549.h
@@ -0,0 +1,43 @@
+/*
+ * LM4549 Audio Codec Interface
+ *
+ * Copyright (c) 2011
+ * Written by Mathieu Sonet - www.elasticsheep.com
+ *
+ * This code is licenced under the GPL.
+ *
+ * *****************************************************************
+ */
+
+#ifndef HW_LM4549_H
+#define HW_LM4549_H
+
+#include "audio/audio.h"
+
+typedef void (*lm4549_callback)(void *opaque);
+
+#define LM4549_BUFFER_SIZE (512 * 2) /* 512 16-bit stereo samples */
+
+
+typedef struct {
+ QEMUSoundCard card;
+ SWVoiceOut *voice;
+ uint32_t voice_is_active;
+
+ uint16_t regfile[128];
+ lm4549_callback data_req_cb;
+ void *opaque;
+
+ uint16_t buffer[LM4549_BUFFER_SIZE];
+ uint32_t buffer_level;
+} lm4549_state;
+
+extern const VMStateDescription vmstate_lm4549_state;
+
+
+void lm4549_init(lm4549_state *s, lm4549_callback data_req, void *opaque);
+uint32_t lm4549_read(lm4549_state *s, target_phys_addr_t offset);
+void lm4549_write(lm4549_state *s, target_phys_addr_t offset, uint32_t value);
+uint32_t lm4549_write_samples(lm4549_state *s, uint32_t left, uint32_t right);
+
+#endif /* #ifndef HW_LM4549_H */
diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c
index e077ec07cc..2984cea633 100644
--- a/hw/lsi53c895a.c
+++ b/hw/lsi53c895a.c
@@ -531,7 +531,7 @@ static void lsi_bad_selection(LSIState *s, uint32_t id)
/* Initiate a SCSI layer data transfer. */
static void lsi_do_dma(LSIState *s, int out)
{
- uint32_t count, id;
+ uint32_t count;
target_phys_addr_t addr;
SCSIDevice *dev;
@@ -542,12 +542,8 @@ static void lsi_do_dma(LSIState *s, int out)
return;
}
- id = (s->current->tag >> 8) & 0xf;
- dev = s->bus.devs[id];
- if (!dev) {
- lsi_bad_selection(s, id);
- return;
- }
+ dev = s->current->req->dev;
+ assert(dev);
count = s->dbc;
if (count > s->current->dma_len)
@@ -771,7 +767,7 @@ static void lsi_do_command(LSIState *s)
s->command_complete = 0;
id = (s->select_tag >> 8) & 0xf;
- dev = s->bus.devs[id];
+ dev = scsi_device_find(&s->bus, 0, id, s->current_lun);
if (!dev) {
lsi_bad_selection(s, id);
return;
@@ -1202,7 +1198,7 @@ again:
}
s->sstat0 |= LSI_SSTAT0_WOA;
s->scntl1 &= ~LSI_SCNTL1_IARB;
- if (id >= LSI_MAX_DEVS || !s->bus.devs[id]) {
+ if (!scsi_device_find(&s->bus, 0, id, 0)) {
lsi_bad_selection(s, id);
break;
}
@@ -1684,13 +1680,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val)
if (val & LSI_SCNTL1_RST) {
if (!(s->sstat0 & LSI_SSTAT0_RST)) {
DeviceState *dev;
- int id;
- for (id = 0; id < s->bus.ndev; id++) {
- if (s->bus.devs[id]) {
- dev = &s->bus.devs[id]->qdev;
- dev->info->reset(dev);
- }
+ QTAILQ_FOREACH(dev, &s->bus.qbus.children, sibling) {
+ dev->info->reset(dev);
}
s->sstat0 |= LSI_SSTAT0_RST;
lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0);
@@ -2091,7 +2083,11 @@ static int lsi_scsi_uninit(PCIDevice *d)
return 0;
}
-static const struct SCSIBusOps lsi_scsi_ops = {
+static const struct SCSIBusInfo lsi_scsi_info = {
+ .tcq = true,
+ .max_target = LSI_MAX_DEVS,
+ .max_lun = 0, /* LUN support is buggy */
+
.transfer_data = lsi_transfer_data,
.complete = lsi_command_complete,
.cancel = lsi_request_cancelled
@@ -2118,7 +2114,7 @@ static int lsi_scsi_init(PCIDevice *dev)
pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ram_io);
QTAILQ_INIT(&s->queue);
- scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, &lsi_scsi_ops);
+ scsi_bus_new(&s->bus, &dev->qdev, &lsi_scsi_info);
if (!dev->qdev.hotplugged) {
return scsi_bus_legacy_handle_cmdline(&s->bus);
}
diff --git a/hw/pci-stub.c b/hw/pci-stub.c
index 1fb105d51c..636171c16f 100644
--- a/hw/pci-stub.c
+++ b/hw/pci-stub.c
@@ -21,20 +21,17 @@
#include "sysemu.h"
#include "monitor.h"
#include "pci.h"
+#include "qmp-commands.h"
-static void pci_error_message(Monitor *mon)
+PciInfoList *qmp_query_pci(Error **errp)
{
- monitor_printf(mon, "PCI devices not supported\n");
+ error_set(errp, QERR_UNSUPPORTED);
+ return NULL;
}
-void do_pci_info(Monitor *mon, QObject **ret_data)
-{
- pci_error_message(mon);
-}
-
-void do_pci_info_print(Monitor *mon, const QObject *data)
+static void pci_error_message(Monitor *mon)
{
- pci_error_message(mon);
+ monitor_printf(mon, "PCI devices not supported\n");
}
int do_pcie_aer_inejct_error(Monitor *mon,
diff --git a/hw/pci.c b/hw/pci.c
index e8cc1b046f..399227fc3d 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -29,8 +29,8 @@
#include "net.h"
#include "sysemu.h"
#include "loader.h"
-#include "qemu-objects.h"
#include "range.h"
+#include "qmp-commands.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
@@ -1164,276 +1164,194 @@ void pci_for_each_device(PCIBus *bus, int bus_num,
}
}
-static void pci_device_print(Monitor *mon, QDict *device)
+static const pci_class_desc *get_class_desc(int class)
{
- QDict *qdict;
- QListEntry *entry;
- uint64_t addr, size;
-
- monitor_printf(mon, " Bus %2" PRId64 ", ", qdict_get_int(device, "bus"));
- monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n",
- qdict_get_int(device, "slot"),
- qdict_get_int(device, "function"));
- monitor_printf(mon, " ");
-
- qdict = qdict_get_qdict(device, "class_info");
- if (qdict_haskey(qdict, "desc")) {
- monitor_printf(mon, "%s", qdict_get_str(qdict, "desc"));
- } else {
- monitor_printf(mon, "Class %04" PRId64, qdict_get_int(qdict, "class"));
- }
-
- qdict = qdict_get_qdict(device, "id");
- monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n",
- qdict_get_int(qdict, "device"),
- qdict_get_int(qdict, "vendor"));
+ const pci_class_desc *desc;
- if (qdict_haskey(device, "irq")) {
- monitor_printf(mon, " IRQ %" PRId64 ".\n",
- qdict_get_int(device, "irq"));
+ desc = pci_class_descriptions;
+ while (desc->desc && class != desc->class) {
+ desc++;
}
- if (qdict_haskey(device, "pci_bridge")) {
- QDict *info;
-
- qdict = qdict_get_qdict(device, "pci_bridge");
-
- info = qdict_get_qdict(qdict, "bus");
- monitor_printf(mon, " BUS %" PRId64 ".\n",
- qdict_get_int(info, "number"));
- monitor_printf(mon, " secondary bus %" PRId64 ".\n",
- qdict_get_int(info, "secondary"));
- monitor_printf(mon, " subordinate bus %" PRId64 ".\n",
- qdict_get_int(info, "subordinate"));
+ return desc;
+}
- info = qdict_get_qdict(qdict, "io_range");
- monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n",
- qdict_get_int(info, "base"),
- qdict_get_int(info, "limit"));
+static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num);
- info = qdict_get_qdict(qdict, "memory_range");
- monitor_printf(mon,
- " memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n",
- qdict_get_int(info, "base"),
- qdict_get_int(info, "limit"));
+static PciMemoryRegionList *qmp_query_pci_regions(const PCIDevice *dev)
+{
+ PciMemoryRegionList *head = NULL, *cur_item = NULL;
+ int i;
- info = qdict_get_qdict(qdict, "prefetchable_range");
- monitor_printf(mon, " prefetchable memory range "
- "[0x%08"PRIx64", 0x%08"PRIx64"]\n",
- qdict_get_int(info, "base"),
- qdict_get_int(info, "limit"));
- }
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ const PCIIORegion *r = &dev->io_regions[i];
+ PciMemoryRegionList *region;
- QLIST_FOREACH_ENTRY(qdict_get_qlist(device, "regions"), entry) {
- qdict = qobject_to_qdict(qlist_entry_obj(entry));
- monitor_printf(mon, " BAR%d: ", (int) qdict_get_int(qdict, "bar"));
+ if (!r->size) {
+ continue;
+ }
- addr = qdict_get_int(qdict, "address");
- size = qdict_get_int(qdict, "size");
+ region = g_malloc0(sizeof(*region));
+ region->value = g_malloc0(sizeof(*region->value));
- if (!strcmp(qdict_get_str(qdict, "type"), "io")) {
- monitor_printf(mon, "I/O at 0x%04"FMT_PCIBUS
- " [0x%04"FMT_PCIBUS"].\n",
- addr, addr + size - 1);
+ if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+ region->value->type = g_strdup("io");
} else {
- monitor_printf(mon, "%d bit%s memory at 0x%08"FMT_PCIBUS
- " [0x%08"FMT_PCIBUS"].\n",
- qdict_get_bool(qdict, "mem_type_64") ? 64 : 32,
- qdict_get_bool(qdict, "prefetch") ?
- " prefetchable" : "", addr, addr + size - 1);
+ region->value->type = g_strdup("memory");
+ region->value->has_prefetch = true;
+ region->value->prefetch = !!(r->type & PCI_BASE_ADDRESS_MEM_PREFETCH);
+ region->value->has_mem_type_64 = true;
+ region->value->mem_type_64 = !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64);
}
- }
- monitor_printf(mon, " id \"%s\"\n", qdict_get_str(device, "qdev_id"));
+ region->value->bar = i;
+ region->value->address = r->addr;
+ region->value->size = r->size;
- if (qdict_haskey(device, "pci_bridge")) {
- qdict = qdict_get_qdict(device, "pci_bridge");
- if (qdict_haskey(qdict, "devices")) {
- QListEntry *dev;
- QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) {
- pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev)));
- }
+ /* XXX: waiting for the qapi to support GSList */
+ if (!cur_item) {
+ head = cur_item = region;
+ } else {
+ cur_item->next = region;
+ cur_item = region;
}
}
-}
-
-void do_pci_info_print(Monitor *mon, const QObject *data)
-{
- QListEntry *bus, *dev;
- QLIST_FOREACH_ENTRY(qobject_to_qlist(data), bus) {
- QDict *qdict = qobject_to_qdict(qlist_entry_obj(bus));
- QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) {
- pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev)));
- }
- }
+ return head;
}
-static QObject *pci_get_dev_class(const PCIDevice *dev)
+static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus,
+ int bus_num)
{
- int class;
- const pci_class_desc *desc;
+ PciBridgeInfo *info;
- class = pci_get_word(dev->config + PCI_CLASS_DEVICE);
- desc = pci_class_descriptions;
- while (desc->desc && class != desc->class)
- desc++;
+ info = g_malloc0(sizeof(*info));
- if (desc->desc) {
- return qobject_from_jsonf("{ 'desc': %s, 'class': %d }",
- desc->desc, class);
- } else {
- return qobject_from_jsonf("{ 'class': %d }", class);
- }
-}
+ info->bus.number = dev->config[PCI_PRIMARY_BUS];
+ info->bus.secondary = dev->config[PCI_SECONDARY_BUS];
+ info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS];
-static QObject *pci_get_dev_id(const PCIDevice *dev)
-{
- return qobject_from_jsonf("{ 'device': %d, 'vendor': %d }",
- pci_get_word(dev->config + PCI_VENDOR_ID),
- pci_get_word(dev->config + PCI_DEVICE_ID));
-}
+ info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range));
+ info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
+ info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
-static QObject *pci_get_regions_list(const PCIDevice *dev)
-{
- int i;
- QList *regions_list;
-
- regions_list = qlist_new();
-
- for (i = 0; i < PCI_NUM_REGIONS; i++) {
- QObject *obj;
- const PCIIORegion *r = &dev->io_regions[i];
+ info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range));
+ info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
+ info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
- if (!r->size) {
- continue;
- }
+ info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range));
+ info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
+ info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
- if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
- obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'io', "
- "'address': %" PRId64 ", "
- "'size': %" PRId64 " }",
- i, r->addr, r->size);
- } else {
- int mem_type_64 = r->type & PCI_BASE_ADDRESS_MEM_TYPE_64;
-
- obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'memory', "
- "'mem_type_64': %i, 'prefetch': %i, "
- "'address': %" PRId64 ", "
- "'size': %" PRId64 " }",
- i, mem_type_64,
- r->type & PCI_BASE_ADDRESS_MEM_PREFETCH,
- r->addr, r->size);
+ if (dev->config[PCI_SECONDARY_BUS] != 0) {
+ PCIBus *child_bus = pci_find_bus(bus, dev->config[PCI_SECONDARY_BUS]);
+ if (child_bus) {
+ info->has_devices = true;
+ info->devices = qmp_query_pci_devices(child_bus, dev->config[PCI_SECONDARY_BUS]);
}
-
- qlist_append_obj(regions_list, obj);
}
- return QOBJECT(regions_list);
+ return info;
}
-static QObject *pci_get_devices_list(PCIBus *bus, int bus_num);
-
-static QObject *pci_get_dev_dict(PCIDevice *dev, PCIBus *bus, int bus_num)
+static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus,
+ int bus_num)
{
+ const pci_class_desc *desc;
+ PciDeviceInfo *info;
uint8_t type;
- QObject *obj;
+ int class;
+
+ info = g_malloc0(sizeof(*info));
+ info->bus = bus_num;
+ info->slot = PCI_SLOT(dev->devfn);
+ info->function = PCI_FUNC(dev->devfn);
- obj = qobject_from_jsonf("{ 'bus': %d, 'slot': %d, 'function': %d," "'class_info': %p, 'id': %p, 'regions': %p,"
- " 'qdev_id': %s }",
- bus_num,
- PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
- pci_get_dev_class(dev), pci_get_dev_id(dev),
- pci_get_regions_list(dev),
- dev->qdev.id ? dev->qdev.id : "");
+ class = pci_get_word(dev->config + PCI_CLASS_DEVICE);
+ info->class_info.class = class;
+ desc = get_class_desc(class);
+ if (desc->desc) {
+ info->class_info.has_desc = true;
+ info->class_info.desc = g_strdup(desc->desc);
+ }
+
+ info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID);
+ info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID);
+ info->regions = qmp_query_pci_regions(dev);
+ info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : "");
if (dev->config[PCI_INTERRUPT_PIN] != 0) {
- QDict *qdict = qobject_to_qdict(obj);
- qdict_put(qdict, "irq", qint_from_int(dev->config[PCI_INTERRUPT_LINE]));
+ info->has_irq = true;
+ info->irq = dev->config[PCI_INTERRUPT_LINE];
}
type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION;
if (type == PCI_HEADER_TYPE_BRIDGE) {
- QDict *qdict;
- QObject *pci_bridge;
-
- pci_bridge = qobject_from_jsonf("{ 'bus': "
- "{ 'number': %d, 'secondary': %d, 'subordinate': %d }, "
- "'io_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, "
- "'memory_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, "
- "'prefetchable_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "} }",
- dev->config[PCI_PRIMARY_BUS], dev->config[PCI_SECONDARY_BUS],
- dev->config[PCI_SUBORDINATE_BUS],
- pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO),
- pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO),
- pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY),
- pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY),
- pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY |
- PCI_BASE_ADDRESS_MEM_PREFETCH),
- pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY |
- PCI_BASE_ADDRESS_MEM_PREFETCH));
-
- if (dev->config[PCI_SECONDARY_BUS] != 0) {
- PCIBus *child_bus = pci_find_bus(bus, dev->config[PCI_SECONDARY_BUS]);
-
- if (child_bus) {
- qdict = qobject_to_qdict(pci_bridge);
- qdict_put_obj(qdict, "devices",
- pci_get_devices_list(child_bus,
- dev->config[PCI_SECONDARY_BUS]));
- }
- }
- qdict = qobject_to_qdict(obj);
- qdict_put_obj(qdict, "pci_bridge", pci_bridge);
+ info->has_pci_bridge = true;
+ info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num);
}
- return obj;
+ return info;
}
-static QObject *pci_get_devices_list(PCIBus *bus, int bus_num)
+static PciDeviceInfoList *qmp_query_pci_devices(PCIBus *bus, int bus_num)
{
- int devfn;
+ PciDeviceInfoList *info, *head = NULL, *cur_item = NULL;
PCIDevice *dev;
- QList *dev_list;
-
- dev_list = qlist_new();
+ int devfn;
for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
dev = bus->devices[devfn];
if (dev) {
- qlist_append_obj(dev_list, pci_get_dev_dict(dev, bus, bus_num));
+ info = g_malloc0(sizeof(*info));
+ info->value = qmp_query_pci_device(dev, bus, bus_num);
+
+ /* XXX: waiting for the qapi to support GSList */
+ if (!cur_item) {
+ head = cur_item = info;
+ } else {
+ cur_item->next = info;
+ cur_item = info;
+ }
}
}
- return QOBJECT(dev_list);
+ return head;
}
-static QObject *pci_get_bus_dict(PCIBus *bus, int bus_num)
+static PciInfo *qmp_query_pci_bus(PCIBus *bus, int bus_num)
{
+ PciInfo *info = NULL;
+
bus = pci_find_bus(bus, bus_num);
if (bus) {
- return qobject_from_jsonf("{ 'bus': %d, 'devices': %p }",
- bus_num, pci_get_devices_list(bus, bus_num));
+ info = g_malloc0(sizeof(*info));
+ info->bus = bus_num;
+ info->devices = qmp_query_pci_devices(bus, bus_num);
}
- return NULL;
+ return info;
}
-void do_pci_info(Monitor *mon, QObject **ret_data)
+PciInfoList *qmp_query_pci(Error **errp)
{
- QList *bus_list;
+ PciInfoList *info, *head = NULL, *cur_item = NULL;
struct PCIHostBus *host;
- bus_list = qlist_new();
-
QLIST_FOREACH(host, &host_buses, next) {
- QObject *obj = pci_get_bus_dict(host->bus, 0);
- if (obj) {
- qlist_append_obj(bus_list, obj);
+ info = g_malloc0(sizeof(*info));
+ info->value = qmp_query_pci_bus(host->bus, 0);
+
+ /* XXX: waiting for the qapi to support GSList */
+ if (!cur_item) {
+ head = cur_item = info;
+ } else {
+ cur_item->next = info;
+ cur_item = info;
}
}
- *ret_data = QOBJECT(bus_list);
+ return head;
}
static const char * const pci_nic_models[] = {
diff --git a/hw/pci.h b/hw/pci.h
index 86a81c8273..98f30f748c 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -2,7 +2,6 @@
#define QEMU_PCI_H
#include "qemu-common.h"
-#include "qobject.h"
#include "qdev.h"
#include "memory.h"
@@ -271,9 +270,6 @@ int pci_parse_devaddr(const char *addr, int *domp, int *busp,
int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp,
unsigned *slotp);
-void do_pci_info_print(Monitor *mon, const QObject *data);
-void do_pci_info(Monitor *mon, QObject **ret_data);
-
void pci_device_deassert_intx(PCIDevice *dev);
static inline void
diff --git a/hw/pl041.c b/hw/pl041.c
new file mode 100644
index 0000000000..efd52ac42f
--- /dev/null
+++ b/hw/pl041.c
@@ -0,0 +1,636 @@
+/*
+ * Arm PrimeCell PL041 Advanced Audio Codec Interface
+ *
+ * Copyright (c) 2011
+ * Written by Mathieu Sonet - www.elasticsheep.com
+ *
+ * This code is licenced under the GPL.
+ *
+ * *****************************************************************
+ *
+ * This driver emulates the ARM AACI interface
+ * connected to a LM4549 codec.
+ *
+ * Limitations:
+ * - Supports only a playback on one channel (Versatile/Vexpress)
+ * - Supports only one TX FIFO in compact-mode or non-compact mode.
+ * - Supports playback of 12, 16, 18 and 20 bits samples.
+ * - Record is not supported.
+ * - The PL041 is hardwired to a LM4549 codec.
+ *
+ */
+
+#include "sysbus.h"
+
+#include "pl041.h"
+#include "lm4549.h"
+
+#if 0
+#define PL041_DEBUG_LEVEL 1
+#endif
+
+#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 1)
+#define DBG_L1(fmt, ...) \
+do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DBG_L1(fmt, ...) \
+do { } while (0)
+#endif
+
+#if defined(PL041_DEBUG_LEVEL) && (PL041_DEBUG_LEVEL >= 2)
+#define DBG_L2(fmt, ...) \
+do { printf("pl041: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DBG_L2(fmt, ...) \
+do { } while (0)
+#endif
+
+
+#define MAX_FIFO_DEPTH (1024)
+#define DEFAULT_FIFO_DEPTH (8)
+
+#define SLOT1_RW (1 << 19)
+
+/* This FIFO only stores 20-bit samples on 32-bit words.
+ So its level is independent of the selected mode */
+typedef struct {
+ uint32_t level;
+ uint32_t data[MAX_FIFO_DEPTH];
+} pl041_fifo;
+
+typedef struct {
+ pl041_fifo tx_fifo;
+ uint8_t tx_enabled;
+ uint8_t tx_compact_mode;
+ uint8_t tx_sample_size;
+
+ pl041_fifo rx_fifo;
+ uint8_t rx_enabled;
+ uint8_t rx_compact_mode;
+ uint8_t rx_sample_size;
+} pl041_channel;
+
+typedef struct {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq irq;
+
+ uint32_t fifo_depth; /* FIFO depth in non-compact mode */
+
+ pl041_regfile regs;
+ pl041_channel fifo1;
+ lm4549_state codec;
+} pl041_state;
+
+
+static const unsigned char pl041_default_id[8] = {
+ 0x41, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1
+};
+
+#if defined(PL041_DEBUG_LEVEL)
+#define REGISTER(name, offset) #name,
+static const char *pl041_regs_name[] = {
+ #include "pl041.hx"
+};
+#undef REGISTER
+#endif
+
+
+#if defined(PL041_DEBUG_LEVEL)
+static const char *get_reg_name(target_phys_addr_t offset)
+{
+ if (offset <= PL041_dr1_7) {
+ return pl041_regs_name[offset >> 2];
+ }
+
+ return "unknown";
+}
+#endif
+
+static uint8_t pl041_compute_periphid3(pl041_state *s)
+{
+ uint8_t id3 = 1; /* One channel */
+
+ /* Add the fifo depth information */
+ switch (s->fifo_depth) {
+ case 8:
+ id3 |= 0 << 3;
+ break;
+ case 32:
+ id3 |= 1 << 3;
+ break;
+ case 64:
+ id3 |= 2 << 3;
+ break;
+ case 128:
+ id3 |= 3 << 3;
+ break;
+ case 256:
+ id3 |= 4 << 3;
+ break;
+ case 512:
+ id3 |= 5 << 3;
+ break;
+ case 1024:
+ id3 |= 6 << 3;
+ break;
+ case 2048:
+ id3 |= 7 << 3;
+ break;
+ }
+
+ return id3;
+}
+
+static void pl041_reset(pl041_state *s)
+{
+ DBG_L1("pl041_reset\n");
+
+ memset(&s->regs, 0x00, sizeof(pl041_regfile));
+
+ s->regs.slfr = SL1TXEMPTY | SL2TXEMPTY | SL12TXEMPTY;
+ s->regs.sr1 = TXFE | RXFE | TXHE;
+ s->regs.isr1 = 0;
+
+ memset(&s->fifo1, 0x00, sizeof(s->fifo1));
+}
+
+
+static void pl041_fifo1_write(pl041_state *s, uint32_t value)
+{
+ pl041_channel *channel = &s->fifo1;
+ pl041_fifo *fifo = &s->fifo1.tx_fifo;
+
+ /* Push the value in the FIFO */
+ if (channel->tx_compact_mode == 0) {
+ /* Non-compact mode */
+
+ if (fifo->level < s->fifo_depth) {
+ /* Pad the value with 0 to obtain a 20-bit sample */
+ switch (channel->tx_sample_size) {
+ case 12:
+ value = (value << 8) & 0xFFFFF;
+ break;
+ case 16:
+ value = (value << 4) & 0xFFFFF;
+ break;
+ case 18:
+ value = (value << 2) & 0xFFFFF;
+ break;
+ case 20:
+ default:
+ break;
+ }
+
+ /* Store the sample in the FIFO */
+ fifo->data[fifo->level++] = value;
+ }
+#if defined(PL041_DEBUG_LEVEL)
+ else {
+ DBG_L1("fifo1 write: overrun\n");
+ }
+#endif
+ } else {
+ /* Compact mode */
+
+ if ((fifo->level + 2) < s->fifo_depth) {
+ uint32_t i = 0;
+ uint32_t sample = 0;
+
+ for (i = 0; i < 2; i++) {
+ sample = value & 0xFFFF;
+ value = value >> 16;
+
+ /* Pad each sample with 0 to obtain a 20-bit sample */
+ switch (channel->tx_sample_size) {
+ case 12:
+ sample = sample << 8;
+ break;
+ case 16:
+ default:
+ sample = sample << 4;
+ break;
+ }
+
+ /* Store the sample in the FIFO */
+ fifo->data[fifo->level++] = sample;
+ }
+ }
+#if defined(PL041_DEBUG_LEVEL)
+ else {
+ DBG_L1("fifo1 write: overrun\n");
+ }
+#endif
+ }
+
+ /* Update the status register */
+ if (fifo->level > 0) {
+ s->regs.sr1 &= ~(TXUNDERRUN | TXFE);
+ }
+
+ if (fifo->level >= (s->fifo_depth / 2)) {
+ s->regs.sr1 &= ~TXHE;
+ }
+
+ if (fifo->level >= s->fifo_depth) {
+ s->regs.sr1 |= TXFF;
+ }
+
+ DBG_L2("fifo1_push sr1 = 0x%08x\n", s->regs.sr1);
+}
+
+static void pl041_fifo1_transmit(pl041_state *s)
+{
+ pl041_channel *channel = &s->fifo1;
+ pl041_fifo *fifo = &s->fifo1.tx_fifo;
+ uint32_t slots = s->regs.txcr1 & TXSLOT_MASK;
+ uint32_t written_samples;
+
+ /* Check if FIFO1 transmit is enabled */
+ if ((channel->tx_enabled) && (slots & (TXSLOT3 | TXSLOT4))) {
+ if (fifo->level >= (s->fifo_depth / 2)) {
+ int i;
+
+ DBG_L1("Transfer FIFO level = %i\n", fifo->level);
+
+ /* Try to transfer the whole FIFO */
+ for (i = 0; i < (fifo->level / 2); i++) {
+ uint32_t left = fifo->data[i * 2];
+ uint32_t right = fifo->data[i * 2 + 1];
+
+ /* Transmit two 20-bit samples to the codec */
+ if (lm4549_write_samples(&s->codec, left, right) == 0) {
+ DBG_L1("Codec buffer full\n");
+ break;
+ }
+ }
+
+ written_samples = i * 2;
+ if (written_samples > 0) {
+ /* Update the FIFO level */
+ fifo->level -= written_samples;
+
+ /* Move back the pending samples to the start of the FIFO */
+ for (i = 0; i < fifo->level; i++) {
+ fifo->data[i] = fifo->data[written_samples + i];
+ }
+
+ /* Update the status register */
+ s->regs.sr1 &= ~TXFF;
+
+ if (fifo->level <= (s->fifo_depth / 2)) {
+ s->regs.sr1 |= TXHE;
+ }
+
+ if (fifo->level == 0) {
+ s->regs.sr1 |= TXFE | TXUNDERRUN;
+ DBG_L1("Empty FIFO\n");
+ }
+ }
+ }
+ }
+}
+
+static void pl041_isr1_update(pl041_state *s)
+{
+ /* Update ISR1 */
+ if (s->regs.sr1 & TXUNDERRUN) {
+ s->regs.isr1 |= URINTR;
+ } else {
+ s->regs.isr1 &= ~URINTR;
+ }
+
+ if (s->regs.sr1 & TXHE) {
+ s->regs.isr1 |= TXINTR;
+ } else {
+ s->regs.isr1 &= ~TXINTR;
+ }
+
+ if (!(s->regs.sr1 & TXBUSY) && (s->regs.sr1 & TXFE)) {
+ s->regs.isr1 |= TXCINTR;
+ } else {
+ s->regs.isr1 &= ~TXCINTR;
+ }
+
+ /* Update the irq state */
+ qemu_set_irq(s->irq, ((s->regs.isr1 & s->regs.ie1) > 0) ? 1 : 0);
+ DBG_L2("Set interrupt sr1 = 0x%08x isr1 = 0x%08x masked = 0x%08x\n",
+ s->regs.sr1, s->regs.isr1, s->regs.isr1 & s->regs.ie1);
+}
+
+static void pl041_request_data(void *opaque)
+{
+ pl041_state *s = (pl041_state *)opaque;
+
+ /* Trigger pending transfers */
+ pl041_fifo1_transmit(s);
+ pl041_isr1_update(s);
+}
+
+static uint64_t pl041_read(void *opaque, target_phys_addr_t offset,
+ unsigned size)
+{
+ pl041_state *s = (pl041_state *)opaque;
+ int value;
+
+ if ((offset >= PL041_periphid0) && (offset <= PL041_pcellid3)) {
+ if (offset == PL041_periphid3) {
+ value = pl041_compute_periphid3(s);
+ } else {
+ value = pl041_default_id[(offset - PL041_periphid0) >> 2];
+ }
+
+ DBG_L1("pl041_read [0x%08x] => 0x%08x\n", offset, value);
+ return value;
+ } else if (offset <= PL041_dr4_7) {
+ value = *((uint32_t *)&s->regs + (offset >> 2));
+ } else {
+ DBG_L1("pl041_read: Reserved offset %x\n", (int)offset);
+ return 0;
+ }
+
+ switch (offset) {
+ case PL041_allints:
+ value = s->regs.isr1 & 0x7F;
+ break;
+ }
+
+ DBG_L1("pl041_read [0x%08x] %s => 0x%08x\n", offset,
+ get_reg_name(offset), value);
+
+ return value;
+}
+
+static void pl041_write(void *opaque, target_phys_addr_t offset,
+ uint64_t value, unsigned size)
+{
+ pl041_state *s = (pl041_state *)opaque;
+ uint16_t control, data;
+ uint32_t result;
+
+ DBG_L1("pl041_write [0x%08x] %s <= 0x%08x\n", offset,
+ get_reg_name(offset), (unsigned int)value);
+
+ /* Write the register */
+ if (offset <= PL041_dr4_7) {
+ *((uint32_t *)&s->regs + (offset >> 2)) = value;
+ } else {
+ DBG_L1("pl041_write: Reserved offset %x\n", (int)offset);
+ return;
+ }
+
+ /* Execute the actions */
+ switch (offset) {
+ case PL041_txcr1:
+ {
+ pl041_channel *channel = &s->fifo1;
+
+ uint32_t txen = s->regs.txcr1 & TXEN;
+ uint32_t tsize = (s->regs.txcr1 & TSIZE_MASK) >> TSIZE_MASK_BIT;
+ uint32_t compact_mode = (s->regs.txcr1 & TXCOMPACT) ? 1 : 0;
+#if defined(PL041_DEBUG_LEVEL)
+ uint32_t slots = (s->regs.txcr1 & TXSLOT_MASK) >> TXSLOT_MASK_BIT;
+ uint32_t txfen = (s->regs.txcr1 & TXFEN) > 0 ? 1 : 0;
+#endif
+
+ DBG_L1("=> txen = %i slots = 0x%01x tsize = %i compact = %i "
+ "txfen = %i\n", txen, slots, tsize, compact_mode, txfen);
+
+ channel->tx_enabled = txen;
+ channel->tx_compact_mode = compact_mode;
+
+ switch (tsize) {
+ case 0:
+ channel->tx_sample_size = 16;
+ break;
+ case 1:
+ channel->tx_sample_size = 18;
+ break;
+ case 2:
+ channel->tx_sample_size = 20;
+ break;
+ case 3:
+ channel->tx_sample_size = 12;
+ break;
+ }
+
+ DBG_L1("TX enabled = %i\n", channel->tx_enabled);
+ DBG_L1("TX compact mode = %i\n", channel->tx_compact_mode);
+ DBG_L1("TX sample width = %i\n", channel->tx_sample_size);
+
+ /* Check if compact mode is allowed with selected tsize */
+ if (channel->tx_compact_mode == 1) {
+ if ((channel->tx_sample_size == 18) ||
+ (channel->tx_sample_size == 20)) {
+ channel->tx_compact_mode = 0;
+ DBG_L1("Compact mode not allowed with 18/20-bit sample size\n");
+ }
+ }
+
+ break;
+ }
+ case PL041_sl1tx:
+ s->regs.slfr &= ~SL1TXEMPTY;
+
+ control = (s->regs.sl1tx >> 12) & 0x7F;
+ data = (s->regs.sl2tx >> 4) & 0xFFFF;
+
+ if ((s->regs.sl1tx & SLOT1_RW) == 0) {
+ /* Write operation */
+ lm4549_write(&s->codec, control, data);
+ } else {
+ /* Read operation */
+ result = lm4549_read(&s->codec, control);
+
+ /* Store the returned value */
+ s->regs.sl1rx = s->regs.sl1tx & ~SLOT1_RW;
+ s->regs.sl2rx = result << 4;
+
+ s->regs.slfr &= ~(SL1RXBUSY | SL2RXBUSY);
+ s->regs.slfr |= SL1RXVALID | SL2RXVALID;
+ }
+ break;
+
+ case PL041_sl2tx:
+ s->regs.sl2tx = value;
+ s->regs.slfr &= ~SL2TXEMPTY;
+ break;
+
+ case PL041_intclr:
+ DBG_L1("=> Clear interrupt intclr = 0x%08x isr1 = 0x%08x\n",
+ s->regs.intclr, s->regs.isr1);
+
+ if (s->regs.intclr & TXUEC1) {
+ s->regs.sr1 &= ~TXUNDERRUN;
+ }
+ break;
+
+ case PL041_maincr:
+ {
+#if defined(PL041_DEBUG_LEVEL)
+ char debug[] = " AACIFE SL1RXEN SL1TXEN";
+ if (!(value & AACIFE)) {
+ debug[0] = '!';
+ }
+ if (!(value & SL1RXEN)) {
+ debug[8] = '!';
+ }
+ if (!(value & SL1TXEN)) {
+ debug[17] = '!';
+ }
+ DBG_L1("%s\n", debug);
+#endif
+
+ if ((s->regs.maincr & AACIFE) == 0) {
+ pl041_reset(s);
+ }
+ break;
+ }
+
+ case PL041_dr1_0:
+ case PL041_dr1_1:
+ case PL041_dr1_2:
+ case PL041_dr1_3:
+ pl041_fifo1_write(s, value);
+ break;
+ }
+
+ /* Transmit the FIFO content */
+ pl041_fifo1_transmit(s);
+
+ /* Update the ISR1 register */
+ pl041_isr1_update(s);
+}
+
+static void pl041_device_reset(DeviceState *d)
+{
+ pl041_state *s = DO_UPCAST(pl041_state, busdev.qdev, d);
+
+ pl041_reset(s);
+}
+
+static const MemoryRegionOps pl041_ops = {
+ .read = pl041_read,
+ .write = pl041_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int pl041_init(SysBusDevice *dev)
+{
+ pl041_state *s = FROM_SYSBUS(pl041_state, dev);
+
+ DBG_L1("pl041_init 0x%08x\n", (uint32_t)s);
+
+ /* Check the device properties */
+ switch (s->fifo_depth) {
+ case 8:
+ case 32:
+ case 64:
+ case 128:
+ case 256:
+ case 512:
+ case 1024:
+ case 2048:
+ break;
+ case 16:
+ default:
+ /* NC FIFO depth of 16 is not allowed because its id bits in
+ AACIPERIPHID3 overlap with the id for the default NC FIFO depth */
+ fprintf(stderr, "pl041: unsupported non-compact fifo depth [%i]\n",
+ s->fifo_depth);
+ return -1;
+ }
+
+ /* Connect the device to the sysbus */
+ memory_region_init_io(&s->iomem, &pl041_ops, s, "pl041", 0x1000);
+ sysbus_init_mmio_region(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+
+ /* Init the codec */
+ lm4549_init(&s->codec, &pl041_request_data, (void *)s);
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_pl041_regfile = {
+ .name = "pl041_regfile",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+#define REGISTER(name, offset) VMSTATE_UINT32(name, pl041_regfile),
+ #include "pl041.hx"
+#undef REGISTER
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pl041_fifo = {
+ .name = "pl041_fifo",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(level, pl041_fifo),
+ VMSTATE_UINT32_ARRAY(data, pl041_fifo, MAX_FIFO_DEPTH),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pl041_channel = {
+ .name = "pl041_channel",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT(tx_fifo, pl041_channel, 0,
+ vmstate_pl041_fifo, pl041_fifo),
+ VMSTATE_UINT8(tx_enabled, pl041_channel),
+ VMSTATE_UINT8(tx_compact_mode, pl041_channel),
+ VMSTATE_UINT8(tx_sample_size, pl041_channel),
+ VMSTATE_STRUCT(rx_fifo, pl041_channel, 0,
+ vmstate_pl041_fifo, pl041_fifo),
+ VMSTATE_UINT8(rx_enabled, pl041_channel),
+ VMSTATE_UINT8(rx_compact_mode, pl041_channel),
+ VMSTATE_UINT8(rx_sample_size, pl041_channel),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_pl041 = {
+ .name = "pl041",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(fifo_depth, pl041_state),
+ VMSTATE_STRUCT(regs, pl041_state, 0,
+ vmstate_pl041_regfile, pl041_regfile),
+ VMSTATE_STRUCT(fifo1, pl041_state, 0,
+ vmstate_pl041_channel, pl041_channel),
+ VMSTATE_STRUCT(codec, pl041_state, 0,
+ vmstate_lm4549_state, lm4549_state),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static SysBusDeviceInfo pl041_device_info = {
+ .init = pl041_init,
+ .qdev.name = "pl041",
+ .qdev.size = sizeof(pl041_state),
+ .qdev.vmsd = &vmstate_pl041,
+ .qdev.reset = pl041_device_reset,
+ .qdev.no_user = 1,
+ .qdev.props = (Property[]) {
+ /* Non-compact FIFO depth property */
+ DEFINE_PROP_UINT32("nc_fifo_depth", pl041_state,
+ fifo_depth, DEFAULT_FIFO_DEPTH),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+};
+
+static void pl041_register_device(void)
+{
+ sysbus_register_withprop(&pl041_device_info);
+}
+
+device_init(pl041_register_device)
diff --git a/hw/pl041.h b/hw/pl041.h
new file mode 100644
index 0000000000..1f224326e5
--- /dev/null
+++ b/hw/pl041.h
@@ -0,0 +1,135 @@
+/*
+ * Arm PrimeCell PL041 Advanced Audio Codec Interface
+ *
+ * Copyright (c) 2011
+ * Written by Mathieu Sonet - www.elasticsheep.com
+ *
+ * This code is licenced under the GPL.
+ *
+ * *****************************************************************
+ */
+
+#ifndef HW_PL041_H
+#define HW_PL041_H
+
+/* Register file */
+#define REGISTER(name, offset) uint32_t name;
+typedef struct {
+ #include "pl041.hx"
+} pl041_regfile;
+#undef REGISTER
+
+/* Register addresses */
+#define REGISTER(name, offset) PL041_##name = offset,
+enum {
+ #include "pl041.hx"
+
+ PL041_periphid0 = 0xFE0,
+ PL041_periphid1 = 0xFE4,
+ PL041_periphid2 = 0xFE8,
+ PL041_periphid3 = 0xFEC,
+ PL041_pcellid0 = 0xFF0,
+ PL041_pcellid1 = 0xFF4,
+ PL041_pcellid2 = 0xFF8,
+ PL041_pcellid3 = 0xFFC,
+};
+#undef REGISTER
+
+/* Register bits */
+
+/* IEx */
+#define TXCIE (1 << 0)
+#define RXTIE (1 << 1)
+#define TXIE (1 << 2)
+#define RXIE (1 << 3)
+#define RXOIE (1 << 4)
+#define TXUIE (1 << 5)
+#define RXTOIE (1 << 6)
+
+/* TXCRx */
+#define TXEN (1 << 0)
+#define TXSLOT1 (1 << 1)
+#define TXSLOT2 (1 << 2)
+#define TXSLOT3 (1 << 3)
+#define TXSLOT4 (1 << 4)
+#define TXCOMPACT (1 << 15)
+#define TXFEN (1 << 16)
+
+#define TXSLOT_MASK_BIT (1)
+#define TXSLOT_MASK (0xFFF << TXSLOT_MASK_BIT)
+
+#define TSIZE_MASK_BIT (13)
+#define TSIZE_MASK (0x3 << TSIZE_MASK_BIT)
+
+#define TSIZE_16BITS (0x0 << TSIZE_MASK_BIT)
+#define TSIZE_18BITS (0x1 << TSIZE_MASK_BIT)
+#define TSIZE_20BITS (0x2 << TSIZE_MASK_BIT)
+#define TSIZE_12BITS (0x3 << TSIZE_MASK_BIT)
+
+/* SRx */
+#define RXFE (1 << 0)
+#define TXFE (1 << 1)
+#define RXHF (1 << 2)
+#define TXHE (1 << 3)
+#define RXFF (1 << 4)
+#define TXFF (1 << 5)
+#define RXBUSY (1 << 6)
+#define TXBUSY (1 << 7)
+#define RXOVERRUN (1 << 8)
+#define TXUNDERRUN (1 << 9)
+#define RXTIMEOUT (1 << 10)
+#define RXTOFE (1 << 11)
+
+/* ISRx */
+#define TXCINTR (1 << 0)
+#define RXTOINTR (1 << 1)
+#define TXINTR (1 << 2)
+#define RXINTR (1 << 3)
+#define ORINTR (1 << 4)
+#define URINTR (1 << 5)
+#define RXTOFEINTR (1 << 6)
+
+/* SLFR */
+#define SL1RXBUSY (1 << 0)
+#define SL1TXBUSY (1 << 1)
+#define SL2RXBUSY (1 << 2)
+#define SL2TXBUSY (1 << 3)
+#define SL12RXBUSY (1 << 4)
+#define SL12TXBUSY (1 << 5)
+#define SL1RXVALID (1 << 6)
+#define SL1TXEMPTY (1 << 7)
+#define SL2RXVALID (1 << 8)
+#define SL2TXEMPTY (1 << 9)
+#define SL12RXVALID (1 << 10)
+#define SL12TXEMPTY (1 << 11)
+#define RAWGPIOINT (1 << 12)
+#define RWIS (1 << 13)
+
+/* MAINCR */
+#define AACIFE (1 << 0)
+#define LOOPBACK (1 << 1)
+#define LOWPOWER (1 << 2)
+#define SL1RXEN (1 << 3)
+#define SL1TXEN (1 << 4)
+#define SL2RXEN (1 << 5)
+#define SL2TXEN (1 << 6)
+#define SL12RXEN (1 << 7)
+#define SL12TXEN (1 << 8)
+#define DMAENABLE (1 << 9)
+
+/* INTCLR */
+#define WISC (1 << 0)
+#define RXOEC1 (1 << 1)
+#define RXOEC2 (1 << 2)
+#define RXOEC3 (1 << 3)
+#define RXOEC4 (1 << 4)
+#define TXUEC1 (1 << 5)
+#define TXUEC2 (1 << 6)
+#define TXUEC3 (1 << 7)
+#define TXUEC4 (1 << 8)
+#define RXTOFEC1 (1 << 9)
+#define RXTOFEC2 (1 << 10)
+#define RXTOFEC3 (1 << 11)
+#define RXTOFEC4 (1 << 12)
+
+#endif /* #ifndef HW_PL041_H */
diff --git a/hw/pl041.hx b/hw/pl041.hx
new file mode 100644
index 0000000000..e972996725
--- /dev/null
+++ b/hw/pl041.hx
@@ -0,0 +1,81 @@
+/*
+ * Arm PrimeCell PL041 Advanced Audio Codec Interface
+ *
+ * Copyright (c) 2011
+ * Written by Mathieu Sonet - www.elasticsheep.com
+ *
+ * This code is licenced under the GPL.
+ *
+ * *****************************************************************
+ */
+
+/* PL041 register file description */
+
+REGISTER( rxcr1, 0x00 )
+REGISTER( txcr1, 0x04 )
+REGISTER( sr1, 0x08 )
+REGISTER( isr1, 0x0C )
+REGISTER( ie1, 0x10 )
+REGISTER( rxcr2, 0x14 )
+REGISTER( txcr2, 0x18 )
+REGISTER( sr2, 0x1C )
+REGISTER( isr2, 0x20 )
+REGISTER( ie2, 0x24 )
+REGISTER( rxcr3, 0x28 )
+REGISTER( txcr3, 0x2C )
+REGISTER( sr3, 0x30 )
+REGISTER( isr3, 0x34 )
+REGISTER( ie3, 0x38 )
+REGISTER( rxcr4, 0x3C )
+REGISTER( txcr4, 0x40 )
+REGISTER( sr4, 0x44 )
+REGISTER( isr4, 0x48 )
+REGISTER( ie4, 0x4C )
+REGISTER( sl1rx, 0x50 )
+REGISTER( sl1tx, 0x54 )
+REGISTER( sl2rx, 0x58 )
+REGISTER( sl2tx, 0x5C )
+REGISTER( sl12rx, 0x60 )
+REGISTER( sl12tx, 0x64 )
+REGISTER( slfr, 0x68 )
+REGISTER( slistat, 0x6C )
+REGISTER( slien, 0x70 )
+REGISTER( intclr, 0x74 )
+REGISTER( maincr, 0x78 )
+REGISTER( reset, 0x7C )
+REGISTER( sync, 0x80 )
+REGISTER( allints, 0x84 )
+REGISTER( mainfr, 0x88 )
+REGISTER( unused, 0x8C )
+REGISTER( dr1_0, 0x90 )
+REGISTER( dr1_1, 0x94 )
+REGISTER( dr1_2, 0x98 )
+REGISTER( dr1_3, 0x9C )
+REGISTER( dr1_4, 0xA0 )
+REGISTER( dr1_5, 0xA4 )
+REGISTER( dr1_6, 0xA8 )
+REGISTER( dr1_7, 0xAC )
+REGISTER( dr2_0, 0xB0 )
+REGISTER( dr2_1, 0xB4 )
+REGISTER( dr2_2, 0xB8 )
+REGISTER( dr2_3, 0xBC )
+REGISTER( dr2_4, 0xC0 )
+REGISTER( dr2_5, 0xC4 )
+REGISTER( dr2_6, 0xC8 )
+REGISTER( dr2_7, 0xCC )
+REGISTER( dr3_0, 0xD0 )
+REGISTER( dr3_1, 0xD4 )
+REGISTER( dr3_2, 0xD8 )
+REGISTER( dr3_3, 0xDC )
+REGISTER( dr3_4, 0xE0 )
+REGISTER( dr3_5, 0xE4 )
+REGISTER( dr3_6, 0xE8 )
+REGISTER( dr3_7, 0xEC )
+REGISTER( dr4_0, 0xF0 )
+REGISTER( dr4_1, 0xF4 )
+REGISTER( dr4_2, 0xF8 )
+REGISTER( dr4_3, 0xFC )
+REGISTER( dr4_4, 0x100 )
+REGISTER( dr4_5, 0x104 )
+REGISTER( dr4_6, 0x108 )
+REGISTER( dr4_7, 0x10C )
diff --git a/hw/qdev.c b/hw/qdev.c
index a223d41cd3..50976dd0c1 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -91,7 +91,7 @@ static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info)
qdev_prop_set_defaults(dev, dev->info->props);
qdev_prop_set_defaults(dev, dev->parent_bus->info->props);
qdev_prop_set_globals(dev);
- QLIST_INSERT_HEAD(&bus->children, dev, sibling);
+ QTAILQ_INSERT_HEAD(&bus->children, dev, sibling);
if (qdev_hotplug) {
assert(bus->allow_hotplug);
dev->hotplugged = 1;
@@ -408,7 +408,7 @@ void qdev_free(DeviceState *dev)
if (dev->opts)
qemu_opts_del(dev->opts);
}
- QLIST_REMOVE(dev, sibling);
+ QTAILQ_REMOVE(&dev->parent_bus->children, dev, sibling);
for (prop = dev->info->props; prop && prop->name; prop++) {
if (prop->info->free) {
prop->info->free(dev, prop);
@@ -510,7 +510,7 @@ int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
}
}
- QLIST_FOREACH(dev, &bus->children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->children, sibling) {
err = qdev_walk_children(dev, devfn, busfn, opaque);
if (err < 0) {
return err;
@@ -560,7 +560,7 @@ static BusState *qbus_find_recursive(BusState *bus, const char *name,
return bus;
}
- QLIST_FOREACH(dev, &bus->children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->children, sibling) {
QLIST_FOREACH(child, &dev->child_bus, sibling) {
ret = qbus_find_recursive(child, name, info);
if (ret) {
@@ -576,7 +576,7 @@ DeviceState *qdev_find_recursive(BusState *bus, const char *id)
DeviceState *dev, *ret;
BusState *child;
- QLIST_FOREACH(dev, &bus->children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->children, sibling) {
if (dev->id && strcmp(dev->id, id) == 0)
return dev;
QLIST_FOREACH(child, &dev->child_bus, sibling) {
@@ -609,7 +609,7 @@ static void qbus_list_dev(BusState *bus)
const char *sep = " ";
error_printf("devices at \"%s\":", bus->name);
- QLIST_FOREACH(dev, &bus->children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->children, sibling) {
error_printf("%s\"%s\"", sep, dev->info->name);
if (dev->id)
error_printf("/\"%s\"", dev->id);
@@ -640,17 +640,17 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem)
* (2) driver name
* (3) driver alias, if present
*/
- QLIST_FOREACH(dev, &bus->children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->children, sibling) {
if (dev->id && strcmp(dev->id, elem) == 0) {
return dev;
}
}
- QLIST_FOREACH(dev, &bus->children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->children, sibling) {
if (strcmp(dev->info->name, elem) == 0) {
return dev;
}
}
- QLIST_FOREACH(dev, &bus->children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->children, sibling) {
if (dev->info->alias && strcmp(dev->info->alias, elem) == 0) {
return dev;
}
@@ -774,7 +774,7 @@ void qbus_create_inplace(BusState *bus, BusInfo *info,
bus->name = buf;
}
- QLIST_INIT(&bus->children);
+ QTAILQ_INIT(&bus->children);
if (parent) {
QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling);
parent->num_child_bus++;
@@ -809,7 +809,7 @@ void qbus_free(BusState *bus)
{
DeviceState *dev;
- while ((dev = QLIST_FIRST(&bus->children)) != NULL) {
+ while ((dev = QTAILQ_FIRST(&bus->children)) != NULL) {
qdev_free(dev);
}
if (bus->parent) {
@@ -878,7 +878,7 @@ static void qbus_print(Monitor *mon, BusState *bus, int indent)
qdev_printf("bus: %s\n", bus->name);
indent += 2;
qdev_printf("type %s\n", bus->info->name);
- QLIST_FOREACH(dev, &bus->children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->children, sibling) {
qdev_print(mon, dev, indent);
}
}
diff --git a/hw/qdev.h b/hw/qdev.h
index aa7ae36187..36a4198c89 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -42,7 +42,7 @@ struct DeviceState {
qemu_irq *gpio_in;
QLIST_HEAD(, BusState) child_bus;
int num_child_bus;
- QLIST_ENTRY(DeviceState) sibling;
+ QTAILQ_ENTRY(DeviceState) sibling;
int instance_id_alias;
int alias_required_for_version;
};
@@ -73,7 +73,7 @@ struct BusState {
const char *name;
int allow_hotplug;
int qdev_allocated;
- QLIST_HEAD(, DeviceState) children;
+ QTAILQ_HEAD(ChildrenHead, DeviceState) children;
QLIST_ENTRY(BusState) sibling;
};
diff --git a/hw/qxl.c b/hw/qxl.c
index 03848edb75..12f71aa56c 100644
--- a/hw/qxl.c
+++ b/hw/qxl.c
@@ -18,8 +18,6 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include <pthread.h>
-
#include "qemu-common.h"
#include "qemu-timer.h"
#include "qemu-queue.h"
@@ -238,6 +236,9 @@ void qxl_spice_reset_image_cache(PCIQXLDevice *qxl)
void qxl_spice_reset_cursor(PCIQXLDevice *qxl)
{
qxl->ssd.worker->reset_cursor(qxl->ssd.worker);
+ qemu_mutex_lock(&qxl->track_lock);
+ qxl->guest_cursor = 0;
+ qemu_mutex_unlock(&qxl->track_lock);
}
@@ -330,6 +331,7 @@ static void init_qxl_ram(PCIQXLDevice *d)
d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC);
d->ram->int_pending = cpu_to_le32(0);
d->ram->int_mask = cpu_to_le32(0);
+ d->ram->update_surface = 0;
SPICE_RING_INIT(&d->ram->cmd_ring);
SPICE_RING_INIT(&d->ram->cursor_ring);
SPICE_RING_INIT(&d->ram->release_ring);
@@ -402,7 +404,9 @@ static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext)
{
QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
if (cmd->type == QXL_CURSOR_SET) {
+ qemu_mutex_lock(&qxl->track_lock);
qxl->guest_cursor = ext->cmd.data;
+ qemu_mutex_unlock(&qxl->track_lock);
}
break;
}
@@ -1067,6 +1071,7 @@ static int qxl_destroy_primary(PCIQXLDevice *d, qxl_async_io async)
d->mode = QXL_MODE_UNDEFINED;
qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
+ qxl_spice_reset_cursor(d);
return 1;
}
@@ -1215,10 +1220,6 @@ async_common:
if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
break;
}
- pthread_yield();
- if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
- break;
- }
d->oom_running = 1;
qxl_spice_oom(d);
d->oom_running = 0;
@@ -1372,7 +1373,7 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events)
if ((old_pending & le_events) == le_events) {
return;
}
- if (pthread_self() == d->main) {
+ if (qemu_thread_is_self(&d->main)) {
qxl_update_irq(d);
} else {
if (write(d->pipe[1], d, 1) != 1) {
@@ -1391,7 +1392,7 @@ static void init_pipe_signaling(PCIQXLDevice *d)
fcntl(d->pipe[1], F_SETFL, O_NONBLOCK);
fcntl(d->pipe[0], F_SETOWN, getpid());
- d->main = pthread_self();
+ qemu_thread_get_self(&d->main);
qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d);
}
@@ -1710,10 +1711,12 @@ static int qxl_post_load(void *opaque, int version)
cmds[out].group_id = MEMSLOT_GROUP_GUEST;
out++;
}
- cmds[out].cmd.data = d->guest_cursor;
- cmds[out].cmd.type = QXL_CMD_CURSOR;
- cmds[out].group_id = MEMSLOT_GROUP_GUEST;
- out++;
+ if (d->guest_cursor) {
+ cmds[out].cmd.data = d->guest_cursor;
+ cmds[out].cmd.type = QXL_CMD_CURSOR;
+ cmds[out].group_id = MEMSLOT_GROUP_GUEST;
+ out++;
+ }
qxl_spice_loadvm_commands(d, cmds, out);
g_free(cmds);
@@ -1787,6 +1790,19 @@ static VMStateDescription qxl_vmstate = {
},
};
+static Property qxl_properties[] = {
+ DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
+ 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size,
+ 64 * 1024 * 1024),
+ DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
+ QXL_DEFAULT_REVISION),
+ DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
+ DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
+ DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static PCIDeviceInfo qxl_info_primary = {
.qdev.name = "qxl-vga",
.qdev.desc = "Spice QXL GPU (primary, vga compatible)",
@@ -1799,18 +1815,7 @@ static PCIDeviceInfo qxl_info_primary = {
.vendor_id = REDHAT_PCI_VENDOR_ID,
.device_id = QXL_DEVICE_ID_STABLE,
.class_id = PCI_CLASS_DISPLAY_VGA,
- .qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
- 64 * 1024 * 1024),
- DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size,
- 64 * 1024 * 1024),
- DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
- QXL_DEFAULT_REVISION),
- DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
- DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
- DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
- DEFINE_PROP_END_OF_LIST(),
- }
+ .qdev.props = qxl_properties,
};
static PCIDeviceInfo qxl_info_secondary = {
@@ -1823,18 +1828,7 @@ static PCIDeviceInfo qxl_info_secondary = {
.vendor_id = REDHAT_PCI_VENDOR_ID,
.device_id = QXL_DEVICE_ID_STABLE,
.class_id = PCI_CLASS_DISPLAY_OTHER,
- .qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size,
- 64 * 1024 * 1024),
- DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size,
- 64 * 1024 * 1024),
- DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision,
- QXL_DEFAULT_REVISION),
- DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0),
- DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0),
- DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0),
- DEFINE_PROP_END_OF_LIST(),
- }
+ .qdev.props = qxl_properties,
};
static void qxl_register(void)
diff --git a/hw/qxl.h b/hw/qxl.h
index 868db813f9..37b2619e55 100644
--- a/hw/qxl.h
+++ b/hw/qxl.h
@@ -4,6 +4,7 @@
#include "hw.h"
#include "pci.h"
#include "vga_int.h"
+#include "qemu-thread.h"
#include "ui/qemu-spice.h"
#include "ui/spice-display.h"
@@ -63,7 +64,7 @@ typedef struct PCIQXLDevice {
QemuMutex track_lock;
/* thread signaling */
- pthread_t main;
+ QemuThread main;
int pipe[2];
/* ram pci bar */
diff --git a/hw/realview.c b/hw/realview.c
index 14281b0f06..9a8e63c8f5 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -125,7 +125,7 @@ static void realview_init(ram_addr_t ram_size,
MemoryRegion *ram_hi = g_new(MemoryRegion, 1);
MemoryRegion *ram_alias = g_new(MemoryRegion, 1);
MemoryRegion *ram_hack = g_new(MemoryRegion, 1);
- DeviceState *dev, *sysctl, *gpio2;
+ DeviceState *dev, *sysctl, *gpio2, *pl041;
SysBusDevice *busdev;
qemu_irq *irqp;
qemu_irq pic[64];
@@ -232,6 +232,12 @@ static void realview_init(ram_addr_t ram_size,
pic[n] = qdev_get_gpio_in(dev, n);
}
+ pl041 = qdev_create(NULL, "pl041");
+ qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
+ qdev_init_nofail(pl041);
+ sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000);
+ sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[19]);
+
sysbus_create_simple("pl050_keyboard", 0x10006000, pic[20]);
sysbus_create_simple("pl050_mouse", 0x10007000, pic[21]);
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index e2f3e32aca..0ce6406b6d 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -274,7 +274,7 @@ VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus,
DeviceState *dev;
int i;
- QLIST_FOREACH(dev, &bus->bus.children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->bus.children, sibling) {
_dev = (VirtIOS390Device *)dev;
for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) {
if (!virtio_queue_get_addr(_dev->vdev, i))
@@ -297,7 +297,7 @@ VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem)
VirtIOS390Device *_dev;
DeviceState *dev;
- QLIST_FOREACH(dev, &bus->bus.children, sibling) {
+ QTAILQ_FOREACH(dev, &bus->bus.children, sibling) {
_dev = (VirtIOS390Device *)dev;
if (_dev->dev_offs == mem) {
return _dev;
diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c
index aca65a16df..e6ebbd594e 100644
--- a/hw/scsi-bus.c
+++ b/hw/scsi-bus.c
@@ -8,6 +8,7 @@
static char *scsibus_get_fw_dev_path(DeviceState *dev);
static int scsi_req_parse(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf);
+static void scsi_req_dequeue(SCSIRequest *req);
static int scsi_build_sense(uint8_t *in_buf, int in_len,
uint8_t *buf, int len, bool fixed);
@@ -16,53 +17,123 @@ static struct BusInfo scsi_bus_info = {
.size = sizeof(SCSIBus),
.get_fw_dev_path = scsibus_get_fw_dev_path,
.props = (Property[]) {
+ DEFINE_PROP_UINT32("channel", SCSIDevice, channel, 0),
DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
- DEFINE_PROP_UINT32("lun", SCSIDevice, lun, 0),
+ DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1),
DEFINE_PROP_END_OF_LIST(),
},
};
static int next_scsi_bus;
/* Create a scsi bus, and attach devices to it. */
-void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
- const SCSIBusOps *ops)
+void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info)
{
qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL);
bus->busnr = next_scsi_bus++;
- bus->tcq = tcq;
- bus->ndev = ndev;
- bus->ops = ops;
+ bus->info = info;
bus->qbus.allow_hotplug = 1;
}
+static void scsi_dma_restart_bh(void *opaque)
+{
+ SCSIDevice *s = opaque;
+ SCSIRequest *req, *next;
+
+ qemu_bh_delete(s->bh);
+ s->bh = NULL;
+
+ QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
+ scsi_req_ref(req);
+ if (req->retry) {
+ req->retry = false;
+ switch (req->cmd.mode) {
+ case SCSI_XFER_FROM_DEV:
+ case SCSI_XFER_TO_DEV:
+ scsi_req_continue(req);
+ break;
+ case SCSI_XFER_NONE:
+ scsi_req_dequeue(req);
+ scsi_req_enqueue(req);
+ break;
+ }
+ }
+ scsi_req_unref(req);
+ }
+}
+
+void scsi_req_retry(SCSIRequest *req)
+{
+ /* No need to save a reference, because scsi_dma_restart_bh just
+ * looks at the request list. */
+ req->retry = true;
+}
+
+static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
+{
+ SCSIDevice *s = opaque;
+
+ if (!running) {
+ return;
+ }
+ if (!s->bh) {
+ s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
+ qemu_bh_schedule(s->bh);
+ }
+}
+
static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
{
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
SCSIDeviceInfo *info = DO_UPCAST(SCSIDeviceInfo, qdev, base);
SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
+ SCSIDevice *d;
int rc = -1;
- if (dev->id == -1) {
- for (dev->id = 0; dev->id < bus->ndev; dev->id++) {
- if (bus->devs[dev->id] == NULL)
- break;
- }
+ if (dev->channel > bus->info->max_channel) {
+ error_report("bad scsi channel id: %d", dev->channel);
+ goto err;
}
- if (dev->id >= bus->ndev) {
+ if (dev->id != -1 && dev->id > bus->info->max_target) {
error_report("bad scsi device id: %d", dev->id);
goto err;
}
- if (bus->devs[dev->id]) {
- qdev_free(&bus->devs[dev->id]->qdev);
+ if (dev->id == -1) {
+ int id = -1;
+ if (dev->lun == -1) {
+ dev->lun = 0;
+ }
+ do {
+ d = scsi_device_find(bus, dev->channel, ++id, dev->lun);
+ } while (d && d->lun == dev->lun && id <= bus->info->max_target);
+ if (id > bus->info->max_target) {
+ error_report("no free target");
+ goto err;
+ }
+ dev->id = id;
+ } else if (dev->lun == -1) {
+ int lun = -1;
+ do {
+ d = scsi_device_find(bus, dev->channel, dev->id, ++lun);
+ } while (d && d->lun == lun && lun < bus->info->max_lun);
+ if (lun > bus->info->max_lun) {
+ error_report("no free lun");
+ goto err;
+ }
+ dev->lun = lun;
+ } else {
+ d = scsi_device_find(bus, dev->channel, dev->id, dev->lun);
+ if (dev->lun == d->lun && dev != d) {
+ qdev_free(&d->qdev);
+ }
}
- bus->devs[dev->id] = dev;
dev->info = info;
QTAILQ_INIT(&dev->requests);
rc = dev->info->init(dev);
- if (rc != 0) {
- bus->devs[dev->id] = NULL;
+ if (rc == 0) {
+ dev->vmsentry = qemu_add_vm_change_state_handler(scsi_dma_restart_cb,
+ dev);
}
err:
@@ -72,13 +143,13 @@ err:
static int scsi_qdev_exit(DeviceState *qdev)
{
SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
- SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
- assert(bus->devs[dev->id] != NULL);
- if (bus->devs[dev->id]->info->destroy) {
- bus->devs[dev->id]->info->destroy(bus->devs[dev->id]);
+ if (dev->vmsentry) {
+ qemu_del_vm_change_state_handler(dev->vmsentry);
+ }
+ if (dev->info->destroy) {
+ dev->info->destroy(dev);
}
- bus->devs[dev->id] = NULL;
return 0;
}
@@ -120,7 +191,7 @@ int scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
int res = 0, unit;
loc_push_none(&loc);
- for (unit = 0; unit < bus->ndev; unit++) {
+ for (unit = 0; unit < bus->info->max_target; unit++) {
dinfo = drive_get(IF_SCSI, bus->busnr, unit);
if (dinfo == NULL) {
continue;
@@ -144,7 +215,7 @@ static int32_t scsi_invalid_command(SCSIRequest *req, uint8_t *buf)
return 0;
}
-struct SCSIReqOps reqops_invalid_opcode = {
+static const struct SCSIReqOps reqops_invalid_opcode = {
.size = sizeof(SCSIRequest),
.send_command = scsi_invalid_command
};
@@ -162,7 +233,7 @@ static int32_t scsi_unit_attention(SCSIRequest *req, uint8_t *buf)
return 0;
}
-struct SCSIReqOps reqops_unit_attention = {
+static const struct SCSIReqOps reqops_unit_attention = {
.size = sizeof(SCSIRequest),
.send_command = scsi_unit_attention
};
@@ -175,7 +246,7 @@ typedef struct SCSITargetReq SCSITargetReq;
struct SCSITargetReq {
SCSIRequest req;
int len;
- uint8_t buf[64];
+ uint8_t buf[2056];
};
static void store_lun(uint8_t *outbuf, int lun)
@@ -190,23 +261,53 @@ static void store_lun(uint8_t *outbuf, int lun)
static bool scsi_target_emulate_report_luns(SCSITargetReq *r)
{
- int len;
+ DeviceState *qdev;
+ int i, len, n;
+ int channel, id;
+ bool found_lun0;
+
if (r->req.cmd.xfer < 16) {
return false;
}
if (r->req.cmd.buf[2] > 2) {
return false;
}
- len = MIN(sizeof r->buf, r->req.cmd.xfer);
+ channel = r->req.dev->channel;
+ id = r->req.dev->id;
+ found_lun0 = false;
+ n = 0;
+ QTAILQ_FOREACH(qdev, &r->req.bus->qbus.children, sibling) {
+ SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
+
+ if (dev->channel == channel && dev->id == id) {
+ if (dev->lun == 0) {
+ found_lun0 = true;
+ }
+ n += 8;
+ }
+ }
+ if (!found_lun0) {
+ n += 8;
+ }
+ len = MIN(n + 8, r->req.cmd.xfer & ~7);
+ if (len > sizeof(r->buf)) {
+ /* TODO: > 256 LUNs? */
+ return false;
+ }
+
memset(r->buf, 0, len);
- if (r->req.dev->lun != 0) {
- r->buf[3] = 16;
- r->len = 24;
- store_lun(&r->buf[16], r->req.dev->lun);
- } else {
- r->buf[3] = 8;
- r->len = 16;
+ stl_be_p(&r->buf, n);
+ i = found_lun0 ? 8 : 16;
+ QTAILQ_FOREACH(qdev, &r->req.bus->qbus.children, sibling) {
+ SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
+
+ if (dev->channel == channel && dev->id == id) {
+ store_lun(&r->buf[i], dev->lun);
+ i += 8;
+ }
}
+ assert(i == n + 8);
+ r->len = len;
return true;
}
@@ -265,7 +366,7 @@ static bool scsi_target_emulate_inquiry(SCSITargetReq *r)
r->buf[2] = 5; /* Version */
r->buf[3] = 2 | 0x10; /* HiSup, response data format */
r->buf[4] = r->len - 5; /* Additional Length = (Len - 1) - 4 */
- r->buf[7] = 0x10 | (r->req.bus->tcq ? 0x02 : 0); /* Sync, TCQ. */
+ r->buf[7] = 0x10 | (r->req.bus->info->tcq ? 0x02 : 0); /* Sync, TCQ. */
memcpy(&r->buf[8], "QEMU ", 8);
memcpy(&r->buf[16], "QEMU TARGET ", 16);
strncpy((char *) &r->buf[32], QEMU_VERSION, 4);
@@ -295,6 +396,13 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf)
r->len = scsi_device_get_sense(r->req.dev, r->buf,
MIN(req->cmd.xfer, sizeof r->buf),
(req->cmd.buf[1] & 1) == 0);
+ if (r->req.dev->sense_is_ua) {
+ if (r->req.dev->info->unit_attention_reported) {
+ r->req.dev->info->unit_attention_reported(req->dev);
+ }
+ r->req.dev->sense_len = 0;
+ r->req.dev->sense_is_ua = false;
+ }
break;
default:
scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED));
@@ -333,7 +441,7 @@ static uint8_t *scsi_target_get_buf(SCSIRequest *req)
return r->buf;
}
-struct SCSIReqOps reqops_target_command = {
+static const struct SCSIReqOps reqops_target_command = {
.size = sizeof(SCSITargetReq),
.send_command = scsi_target_send_command,
.read_data = scsi_target_read_data,
@@ -341,8 +449,8 @@ struct SCSIReqOps reqops_target_command = {
};
-SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag,
- uint32_t lun, void *hba_private)
+SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
+ uint32_t tag, uint32_t lun, void *hba_private)
{
SCSIRequest *req;
@@ -383,7 +491,13 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
(buf[0] != INQUIRY &&
buf[0] != REPORT_LUNS &&
buf[0] != GET_CONFIGURATION &&
- buf[0] != GET_EVENT_STATUS_NOTIFICATION)) {
+ buf[0] != GET_EVENT_STATUS_NOTIFICATION &&
+
+ /*
+ * If we already have a pending unit attention condition,
+ * report this one before triggering another one.
+ */
+ !(buf[0] == REQUEST_SENSE && d->sense_is_ua))) {
req = scsi_req_alloc(&reqops_unit_attention, d, tag, lun,
hba_private);
} else if (lun != d->lun ||
@@ -392,7 +506,7 @@ SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
req = scsi_req_alloc(&reqops_target_command, d, tag, lun,
hba_private);
} else {
- req = d->info->alloc_req(d, tag, lun, hba_private);
+ req = d->info->alloc_req(d, tag, lun, buf, hba_private);
}
}
@@ -479,10 +593,15 @@ int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len)
*
* We assume UA_INTLCK_CTRL to be 00b for HBAs that support autosense, and
* 10b for HBAs that do not support it (do not call scsi_req_get_sense).
- * In the latter case, scsi_req_complete clears unit attention conditions
- * after moving them to the device's sense buffer.
+ * Here we handle unit attention clearing for UA_INTLCK_CTRL == 00b.
*/
- scsi_clear_unit_attention(req);
+ if (req->dev->sense_is_ua) {
+ if (req->dev->info->unit_attention_reported) {
+ req->dev->info->unit_attention_reported(req->dev);
+ }
+ req->dev->sense_len = 0;
+ req->dev->sense_is_ua = false;
+ }
return ret;
}
@@ -522,6 +641,7 @@ int32_t scsi_req_enqueue(SCSIRequest *req)
static void scsi_req_dequeue(SCSIRequest *req)
{
trace_scsi_req_dequeue(req->dev->id, req->lun, req->tag);
+ req->retry = false;
if (req->enqueued) {
QTAILQ_REMOVE(&req->dev->requests, req, next);
req->enqueued = false;
@@ -800,7 +920,7 @@ const struct SCSISense sense_code_SAVING_PARAMS_NOT_SUPPORTED = {
};
/* Illegal request, Incompatible medium installed */
-const struct SCSISense sense_code_INCOMPATIBLE_MEDIUM = {
+const struct SCSISense sense_code_INCOMPATIBLE_FORMAT = {
.key = ILLEGAL_REQUEST, .asc = 0x30, .ascq = 0x00
};
@@ -829,6 +949,11 @@ const struct SCSISense sense_code_RESET = {
.key = UNIT_ATTENTION, .asc = 0x29, .ascq = 0x00
};
+/* Unit attention, No medium */
+const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM = {
+ .key = UNIT_ATTENTION, .asc = 0x3a, .ascq = 0x00
+};
+
/* Unit attention, Medium may have changed */
const struct SCSISense sense_code_MEDIUM_CHANGED = {
.key = UNIT_ATTENTION, .asc = 0x28, .ascq = 0x00
@@ -1038,8 +1163,12 @@ void scsi_req_continue(SCSIRequest *req)
Once it completes, calling scsi_req_continue will restart I/O. */
void scsi_req_data(SCSIRequest *req, int len)
{
- trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
- req->bus->ops->transfer_data(req, len);
+ if (req->io_canceled) {
+ trace_scsi_req_data_canceled(req->dev->id, req->lun, req->tag, len);
+ } else {
+ trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
+ req->bus->info->transfer_data(req, len);
+ }
}
void scsi_req_print(SCSIRequest *req)
@@ -1082,8 +1211,12 @@ void scsi_req_complete(SCSIRequest *req, int status)
if (req->sense_len) {
memcpy(req->dev->sense, req->sense, req->sense_len);
+ req->dev->sense_len = req->sense_len;
+ req->dev->sense_is_ua = (req->ops == &reqops_unit_attention);
+ } else {
+ req->dev->sense_len = 0;
+ req->dev->sense_is_ua = false;
}
- req->dev->sense_len = req->sense_len;
/*
* Unit attention state is now stored in the device's sense buffer
@@ -1094,29 +1227,40 @@ void scsi_req_complete(SCSIRequest *req, int status)
scsi_req_ref(req);
scsi_req_dequeue(req);
- req->bus->ops->complete(req, req->status);
+ req->bus->info->complete(req, req->status);
scsi_req_unref(req);
}
void scsi_req_cancel(SCSIRequest *req)
{
- if (req->ops->cancel_io) {
- req->ops->cancel_io(req);
+ if (!req->enqueued) {
+ return;
}
scsi_req_ref(req);
scsi_req_dequeue(req);
- if (req->bus->ops->cancel) {
- req->bus->ops->cancel(req);
+ req->io_canceled = true;
+ if (req->ops->cancel_io) {
+ req->ops->cancel_io(req);
+ }
+ if (req->bus->info->cancel) {
+ req->bus->info->cancel(req);
}
scsi_req_unref(req);
}
void scsi_req_abort(SCSIRequest *req, int status)
{
+ if (!req->enqueued) {
+ return;
+ }
+ scsi_req_ref(req);
+ scsi_req_dequeue(req);
+ req->io_canceled = true;
if (req->ops->cancel_io) {
req->ops->cancel_io(req);
}
scsi_req_complete(req, status);
+ scsi_req_unref(req);
}
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
@@ -1133,19 +1277,28 @@ void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense)
static char *scsibus_get_fw_dev_path(DeviceState *dev)
{
SCSIDevice *d = DO_UPCAST(SCSIDevice, qdev, dev);
- SCSIBus *bus = scsi_bus_from_device(d);
char path[100];
- int i;
- for (i = 0; i < bus->ndev; i++) {
- if (bus->devs[i] == d) {
- break;
- }
- }
+ snprintf(path, sizeof(path), "%s@%d:%d:%d", qdev_fw_name(dev),
+ d->channel, d->id, d->lun);
- assert(i != bus->ndev);
+ return strdup(path);
+}
- snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), i);
+SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun)
+{
+ DeviceState *qdev;
+ SCSIDevice *target_dev = NULL;
- return strdup(path);
+ QTAILQ_FOREACH_REVERSE(qdev, &bus->qbus.children, ChildrenHead, sibling) {
+ SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
+
+ if (dev->channel == channel && dev->id == id) {
+ if (dev->lun == lun) {
+ return dev;
+ }
+ target_dev = dev;
+ }
+ }
+ return target_dev;
}
diff --git a/hw/scsi-defs.h b/hw/scsi-defs.h
index bfe93922d4..d0a467aab7 100644
--- a/hw/scsi-defs.h
+++ b/hw/scsi-defs.h
@@ -113,6 +113,7 @@
#define READ_12 0xa8
#define WRITE_12 0xaa
#define SERVICE_ACTION_IN_12 0xab
+#define READ_DVD_STRUCTURE 0xad
#define WRITE_VERIFY_12 0xae
#define VERIFY_12 0xaf
#define SEARCH_HIGH_12 0xb0
@@ -122,6 +123,8 @@
#define SEND_VOLUME_TAG 0xb6
#define READ_DEFECT_DATA_12 0xb7
#define SET_CD_SPEED 0xbb
+#define MECHANISM_STATUS 0xbd
+#define READ_CD 0xbe
/*
* SERVICE ACTION IN subcodes
@@ -188,3 +191,90 @@
#define TYPE_INACTIVE 0x20
#define TYPE_NO_LUN 0x7f
+/* Mode page codes for mode sense/set */
+#define MODE_PAGE_R_W_ERROR 0x01
+#define MODE_PAGE_HD_GEOMETRY 0x04
+#define MODE_PAGE_FLEXIBLE_DISK_GEOMETRY 0x05
+#define MODE_PAGE_CACHING 0x08
+#define MODE_PAGE_AUDIO_CTL 0x0e
+#define MODE_PAGE_POWER 0x1a
+#define MODE_PAGE_FAULT_FAIL 0x1c
+#define MODE_PAGE_TO_PROTECT 0x1d
+#define MODE_PAGE_CAPABILITIES 0x2a
+#define MODE_PAGE_ALLS 0x3f
+/* Not in Mt. Fuji, but in ATAPI 2.6 -- depricated now in favor
+ * of MODE_PAGE_SENSE_POWER */
+#define MODE_PAGE_CDROM 0x0d
+
+/* Event notification classes for GET EVENT STATUS NOTIFICATION */
+#define GESN_NO_EVENTS 0
+#define GESN_OPERATIONAL_CHANGE 1
+#define GESN_POWER_MANAGEMENT 2
+#define GESN_EXTERNAL_REQUEST 3
+#define GESN_MEDIA 4
+#define GESN_MULTIPLE_HOSTS 5
+#define GESN_DEVICE_BUSY 6
+
+/* Event codes for MEDIA event status notification */
+#define MEC_NO_CHANGE 0
+#define MEC_EJECT_REQUESTED 1
+#define MEC_NEW_MEDIA 2
+#define MEC_MEDIA_REMOVAL 3 /* only for media changers */
+#define MEC_MEDIA_CHANGED 4 /* only for media changers */
+#define MEC_BG_FORMAT_COMPLETED 5 /* MRW or DVD+RW b/g format completed */
+#define MEC_BG_FORMAT_RESTARTED 6 /* MRW or DVD+RW b/g format restarted */
+
+#define MS_TRAY_OPEN 1
+#define MS_MEDIA_PRESENT 2
+
+/*
+ * Based on values from <linux/cdrom.h> but extending CD_MINS
+ * to the maximum common size allowed by the Orange's Book ATIP
+ *
+ * 90 and 99 min CDs are also available but using them as the
+ * upper limit reduces the effectiveness of the heuristic to
+ * detect DVDs burned to less than 25% of their maximum capacity
+ */
+
+/* Some generally useful CD-ROM information */
+#define CD_MINS 80 /* max. minutes per CD */
+#define CD_SECS 60 /* seconds per minute */
+#define CD_FRAMES 75 /* frames per second */
+#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */
+#define CD_MAX_BYTES (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE)
+#define CD_MAX_SECTORS (CD_MAX_BYTES / 512)
+
+/*
+ * The MMC values are not IDE specific and might need to be moved
+ * to a common header if they are also needed for the SCSI emulation
+ */
+
+/* Profile list from MMC-6 revision 1 table 91 */
+#define MMC_PROFILE_NONE 0x0000
+#define MMC_PROFILE_CD_ROM 0x0008
+#define MMC_PROFILE_CD_R 0x0009
+#define MMC_PROFILE_CD_RW 0x000A
+#define MMC_PROFILE_DVD_ROM 0x0010
+#define MMC_PROFILE_DVD_R_SR 0x0011
+#define MMC_PROFILE_DVD_RAM 0x0012
+#define MMC_PROFILE_DVD_RW_RO 0x0013
+#define MMC_PROFILE_DVD_RW_SR 0x0014
+#define MMC_PROFILE_DVD_R_DL_SR 0x0015
+#define MMC_PROFILE_DVD_R_DL_JR 0x0016
+#define MMC_PROFILE_DVD_RW_DL 0x0017
+#define MMC_PROFILE_DVD_DDR 0x0018
+#define MMC_PROFILE_DVD_PLUS_RW 0x001A
+#define MMC_PROFILE_DVD_PLUS_R 0x001B
+#define MMC_PROFILE_DVD_PLUS_RW_DL 0x002A
+#define MMC_PROFILE_DVD_PLUS_R_DL 0x002B
+#define MMC_PROFILE_BD_ROM 0x0040
+#define MMC_PROFILE_BD_R_SRM 0x0041
+#define MMC_PROFILE_BD_R_RRM 0x0042
+#define MMC_PROFILE_BD_RE 0x0043
+#define MMC_PROFILE_HDDVD_ROM 0x0050
+#define MMC_PROFILE_HDDVD_R 0x0051
+#define MMC_PROFILE_HDDVD_RAM 0x0052
+#define MMC_PROFILE_HDDVD_RW 0x0053
+#define MMC_PROFILE_HDDVD_R_DL 0x0058
+#define MMC_PROFILE_HDDVD_RW_DL 0x005A
+#define MMC_PROFILE_INVALID 0xFFFF
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
index 69095780ac..1c04872af7 100644
--- a/hw/scsi-disk.c
+++ b/hw/scsi-disk.c
@@ -39,15 +39,13 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0)
#include "blockdev.h"
#include "block_int.h"
+#ifdef __linux
+#include <scsi/sg.h>
+#endif
+
#define SCSI_DMA_BUF_SIZE 131072
#define SCSI_MAX_INQUIRY_LEN 256
-#define SCSI_REQ_STATUS_RETRY 0x01
-#define SCSI_REQ_STATUS_RETRY_TYPE_MASK 0x06
-#define SCSI_REQ_STATUS_RETRY_READ 0x00
-#define SCSI_REQ_STATUS_RETRY_WRITE 0x02
-#define SCSI_REQ_STATUS_RETRY_FLUSH 0x04
-
typedef struct SCSIDiskState SCSIDiskState;
typedef struct SCSIDiskReq {
@@ -58,19 +56,15 @@ typedef struct SCSIDiskReq {
uint32_t buflen;
struct iovec iov;
QEMUIOVector qiov;
- uint32_t status;
BlockAcctCookie acct;
} SCSIDiskReq;
struct SCSIDiskState
{
SCSIDevice qdev;
- BlockDriverState *bs;
- /* The qemu block layer uses a fixed 512 byte sector size.
- This is the number of 512 byte blocks in a single scsi sector. */
- int cluster_size;
uint32_t removable;
- uint64_t max_lba;
+ bool media_changed;
+ bool media_event;
QEMUBH *bh;
char *version;
char *serial;
@@ -78,8 +72,7 @@ struct SCSIDiskState
bool tray_locked;
};
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type);
-static int scsi_disk_emulate_command(SCSIDiskReq *r);
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error);
static void scsi_free_request(SCSIRequest *req)
{
@@ -107,6 +100,11 @@ static void scsi_cancel_io(SCSIRequest *req)
DPRINTF("Cancel tag=0x%x\n", req->tag);
if (r->req.aiocb) {
bdrv_aio_cancel(r->req.aiocb);
+
+ /* This reference was left in by scsi_*_data. We take ownership of
+ * it the moment scsi_req_cancel is called, independent of whether
+ * bdrv_aio_cancel completes the request or not. */
+ scsi_req_unref(&r->req);
}
r->req.aiocb = NULL;
}
@@ -117,7 +115,7 @@ static uint32_t scsi_init_iovec(SCSIDiskReq *r)
if (!r->iov.iov_base) {
r->buflen = SCSI_DMA_BUF_SIZE;
- r->iov.iov_base = qemu_blockalign(s->bs, r->buflen);
+ r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
}
r->iov.iov_len = MIN(r->sector_count * 512, r->buflen);
qemu_iovec_init_external(&r->qiov, &r->iov, 1);
@@ -132,12 +130,12 @@ static void scsi_read_complete(void * opaque, int ret)
if (r->req.aiocb != NULL) {
r->req.aiocb = NULL;
- bdrv_acct_done(s->bs, &r->acct);
+ bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
if (ret) {
- if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) {
- return;
+ if (scsi_handle_rw_error(r, -ret)) {
+ goto done;
}
}
@@ -147,6 +145,11 @@ static void scsi_read_complete(void * opaque, int ret)
r->sector += n;
r->sector_count -= n;
scsi_req_data(&r->req, r->qiov.size);
+
+done:
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
}
static void scsi_flush_complete(void * opaque, int ret)
@@ -156,16 +159,21 @@ static void scsi_flush_complete(void * opaque, int ret)
if (r->req.aiocb != NULL) {
r->req.aiocb = NULL;
- bdrv_acct_done(s->bs, &r->acct);
+ bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
if (ret < 0) {
- if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) {
- return;
+ if (scsi_handle_rw_error(r, -ret)) {
+ goto done;
}
}
scsi_req_complete(&r->req, GOOD);
+
+done:
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
}
/* Read more data from scsi device into buffer. */
@@ -191,6 +199,8 @@ static void scsi_read_data(SCSIRequest *req)
/* No data transfer may already be in progress */
assert(r->req.aiocb == NULL);
+ /* The request is used as the AIO opaque value, so add a ref. */
+ scsi_req_ref(&r->req);
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
DPRINTF("Data transfer direction invalid\n");
scsi_read_complete(r, -EINVAL);
@@ -199,38 +209,48 @@ static void scsi_read_data(SCSIRequest *req)
if (s->tray_open) {
scsi_read_complete(r, -ENOMEDIUM);
+ return;
}
+
n = scsi_init_iovec(r);
- bdrv_acct_start(s->bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
- r->req.aiocb = bdrv_aio_readv(s->bs, r->sector, &r->qiov, n,
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_READ);
+ r->req.aiocb = bdrv_aio_readv(s->qdev.conf.bs, r->sector, &r->qiov, n,
scsi_read_complete, r);
if (r->req.aiocb == NULL) {
scsi_read_complete(r, -EIO);
}
}
-static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
+/*
+ * scsi_handle_rw_error has two return values. 0 means that the error
+ * must be ignored, 1 means that the error has been processed and the
+ * caller should not do anything else for this request. Note that
+ * scsi_handle_rw_error always manages its reference counts, independent
+ * of the return value.
+ */
+static int scsi_handle_rw_error(SCSIDiskReq *r, int error)
{
- int is_read = (type == SCSI_REQ_STATUS_RETRY_READ);
+ int is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV);
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
- BlockErrorAction action = bdrv_get_on_error(s->bs, is_read);
+ BlockErrorAction action = bdrv_get_on_error(s->qdev.conf.bs, is_read);
if (action == BLOCK_ERR_IGNORE) {
- bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read);
+ bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read);
return 0;
}
if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC)
|| action == BLOCK_ERR_STOP_ANY) {
- type &= SCSI_REQ_STATUS_RETRY_TYPE_MASK;
- r->status |= SCSI_REQ_STATUS_RETRY | type;
-
- bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read);
+ bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read);
vm_stop(RUN_STATE_IO_ERROR);
- bdrv_iostatus_set_err(s->bs, error);
+ bdrv_iostatus_set_err(s->qdev.conf.bs, error);
+ scsi_req_retry(&r->req);
} else {
switch (error) {
+ case ENOMEDIUM:
+ scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
+ break;
case ENOMEM:
scsi_check_condition(r, SENSE_CODE(TARGET_FAILURE));
break;
@@ -241,7 +261,7 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type)
scsi_check_condition(r, SENSE_CODE(IO_ERROR));
break;
}
- bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read);
+ bdrv_mon_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read);
}
return 1;
}
@@ -254,12 +274,12 @@ static void scsi_write_complete(void * opaque, int ret)
if (r->req.aiocb != NULL) {
r->req.aiocb = NULL;
- bdrv_acct_done(s->bs, &r->acct);
+ bdrv_acct_done(s->qdev.conf.bs, &r->acct);
}
if (ret) {
- if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) {
- return;
+ if (scsi_handle_rw_error(r, -ret)) {
+ goto done;
}
}
@@ -273,6 +293,11 @@ static void scsi_write_complete(void * opaque, int ret)
DPRINTF("Write complete tag=0x%x more=%d\n", r->req.tag, r->qiov.size);
scsi_req_data(&r->req, r->qiov.size);
}
+
+done:
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
}
static void scsi_write_data(SCSIRequest *req)
@@ -284,6 +309,8 @@ static void scsi_write_data(SCSIRequest *req)
/* No data transfer may already be in progress */
assert(r->req.aiocb == NULL);
+ /* The request is used as the AIO opaque value, so add a ref. */
+ scsi_req_ref(&r->req);
if (r->req.cmd.mode != SCSI_XFER_TO_DEV) {
DPRINTF("Data transfer direction invalid\n");
scsi_write_complete(r, -EINVAL);
@@ -294,9 +321,10 @@ static void scsi_write_data(SCSIRequest *req)
if (n) {
if (s->tray_open) {
scsi_write_complete(r, -ENOMEDIUM);
+ return;
}
- bdrv_acct_start(s->bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE);
- r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n,
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, n * BDRV_SECTOR_SIZE, BDRV_ACCT_WRITE);
+ r->req.aiocb = bdrv_aio_writev(s->qdev.conf.bs, r->sector, &r->qiov, n,
scsi_write_complete, r);
if (r->req.aiocb == NULL) {
scsi_write_complete(r, -ENOMEM);
@@ -307,54 +335,6 @@ static void scsi_write_data(SCSIRequest *req)
}
}
-static void scsi_dma_restart_bh(void *opaque)
-{
- SCSIDiskState *s = opaque;
- SCSIRequest *req;
- SCSIDiskReq *r;
-
- qemu_bh_delete(s->bh);
- s->bh = NULL;
-
- QTAILQ_FOREACH(req, &s->qdev.requests, next) {
- r = DO_UPCAST(SCSIDiskReq, req, req);
- if (r->status & SCSI_REQ_STATUS_RETRY) {
- int status = r->status;
- int ret;
-
- r->status &=
- ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK);
-
- switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) {
- case SCSI_REQ_STATUS_RETRY_READ:
- scsi_read_data(&r->req);
- break;
- case SCSI_REQ_STATUS_RETRY_WRITE:
- scsi_write_data(&r->req);
- break;
- case SCSI_REQ_STATUS_RETRY_FLUSH:
- ret = scsi_disk_emulate_command(r);
- if (ret == 0) {
- scsi_req_complete(&r->req, GOOD);
- }
- }
- }
- }
-}
-
-static void scsi_dma_restart_cb(void *opaque, int running, RunState state)
-{
- SCSIDiskState *s = opaque;
-
- if (!running)
- return;
-
- if (!s->bh) {
- s->bh = qemu_bh_new(scsi_dma_restart_bh, s);
- qemu_bh_schedule(s->bh);
- }
-}
-
/* Return a pointer to the data buffer. */
static uint8_t *scsi_get_buf(SCSIRequest *req)
{
@@ -383,11 +363,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
return -1;
}
- if (s->qdev.type == TYPE_ROM) {
- outbuf[buflen++] = 5;
- } else {
- outbuf[buflen++] = 0;
- }
+ outbuf[buflen++] = s->qdev.type & 0x1f;
outbuf[buflen++] = page_code ; // this page
outbuf[buflen++] = 0x00;
@@ -399,8 +375,9 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
"buffer size %zd\n", req->cmd.xfer);
pages = buflen++;
outbuf[buflen++] = 0x00; // list of supported pages (this page)
- if (s->serial)
+ if (s->serial) {
outbuf[buflen++] = 0x80; // unit serial number
+ }
outbuf[buflen++] = 0x83; // device identification
if (s->qdev.type == TYPE_DISK) {
outbuf[buflen++] = 0xb0; // block limits
@@ -419,10 +396,12 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
}
l = strlen(s->serial);
- if (l > req->cmd.xfer)
+ if (l > req->cmd.xfer) {
l = req->cmd.xfer;
- if (l > 20)
+ }
+ if (l > 20) {
l = 20;
+ }
DPRINTF("Inquiry EVPD[Serial number] "
"buffer size %zd\n", req->cmd.xfer);
@@ -435,10 +414,11 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
case 0x83: /* Device identification page, mandatory */
{
int max_len = 255 - 8;
- int id_len = strlen(bdrv_get_device_name(s->bs));
+ int id_len = strlen(bdrv_get_device_name(s->qdev.conf.bs));
- if (id_len > max_len)
+ if (id_len > max_len) {
id_len = max_len;
+ }
DPRINTF("Inquiry EVPD[Device identification] "
"buffer size %zd\n", req->cmd.xfer);
@@ -448,7 +428,7 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
outbuf[buflen++] = 0; // reserved
outbuf[buflen++] = id_len; // length of data following
- memcpy(outbuf+buflen, bdrv_get_device_name(s->bs), id_len);
+ memcpy(outbuf+buflen, bdrv_get_device_name(s->qdev.conf.bs), id_len);
buflen += id_len;
break;
}
@@ -521,17 +501,16 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
}
buflen = req->cmd.xfer;
- if (buflen > SCSI_MAX_INQUIRY_LEN)
+ if (buflen > SCSI_MAX_INQUIRY_LEN) {
buflen = SCSI_MAX_INQUIRY_LEN;
-
+ }
memset(outbuf, 0, buflen);
outbuf[0] = s->qdev.type & 0x1f;
+ outbuf[1] = s->removable ? 0x80 : 0;
if (s->qdev.type == TYPE_ROM) {
- outbuf[1] = 0x80;
memcpy(&outbuf[16], "QEMU CD-ROM ", 16);
} else {
- outbuf[1] = s->removable ? 0x80 : 0;
memcpy(&outbuf[16], "QEMU HARDDISK ", 16);
}
memcpy(&outbuf[8], "QEMU ", 8);
@@ -555,17 +534,250 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf)
}
/* Sync data transfer and TCQ. */
- outbuf[7] = 0x10 | (req->bus->tcq ? 0x02 : 0);
+ outbuf[7] = 0x10 | (req->bus->info->tcq ? 0x02 : 0);
return buflen;
}
+static inline bool media_is_dvd(SCSIDiskState *s)
+{
+ uint64_t nb_sectors;
+ if (s->qdev.type != TYPE_ROM) {
+ return false;
+ }
+ if (!bdrv_is_inserted(s->qdev.conf.bs)) {
+ return false;
+ }
+ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+ return nb_sectors > CD_MAX_SECTORS;
+}
+
+static inline bool media_is_cd(SCSIDiskState *s)
+{
+ uint64_t nb_sectors;
+ if (s->qdev.type != TYPE_ROM) {
+ return false;
+ }
+ if (!bdrv_is_inserted(s->qdev.conf.bs)) {
+ return false;
+ }
+ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+ return nb_sectors <= CD_MAX_SECTORS;
+}
+
+static int scsi_read_dvd_structure(SCSIDiskState *s, SCSIDiskReq *r,
+ uint8_t *outbuf)
+{
+ static const int rds_caps_size[5] = {
+ [0] = 2048 + 4,
+ [1] = 4 + 4,
+ [3] = 188 + 4,
+ [4] = 2048 + 4,
+ };
+
+ uint8_t media = r->req.cmd.buf[1];
+ uint8_t layer = r->req.cmd.buf[6];
+ uint8_t format = r->req.cmd.buf[7];
+ int size = -1;
+
+ if (s->qdev.type != TYPE_ROM) {
+ return -1;
+ }
+ if (media != 0) {
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
+ return -1;
+ }
+
+ if (format != 0xff) {
+ if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
+ scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
+ return -1;
+ }
+ if (media_is_cd(s)) {
+ scsi_check_condition(r, SENSE_CODE(INCOMPATIBLE_FORMAT));
+ return -1;
+ }
+ if (format >= ARRAY_SIZE(rds_caps_size)) {
+ return -1;
+ }
+ size = rds_caps_size[format];
+ memset(outbuf, 0, size);
+ }
+
+ switch (format) {
+ case 0x00: {
+ /* Physical format information */
+ uint64_t nb_sectors;
+ if (layer != 0) {
+ goto fail;
+ }
+ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+
+ outbuf[4] = 1; /* DVD-ROM, part version 1 */
+ outbuf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
+ outbuf[6] = 1; /* one layer, read-only (per MMC-2 spec) */
+ outbuf[7] = 0; /* default densities */
+
+ stl_be_p(&outbuf[12], (nb_sectors >> 2) - 1); /* end sector */
+ stl_be_p(&outbuf[16], (nb_sectors >> 2) - 1); /* l0 end sector */
+ break;
+ }
+
+ case 0x01: /* DVD copyright information, all zeros */
+ break;
+
+ case 0x03: /* BCA information - invalid field for no BCA info */
+ return -1;
+
+ case 0x04: /* DVD disc manufacturing information, all zeros */
+ break;
+
+ case 0xff: { /* List capabilities */
+ int i;
+ size = 4;
+ for (i = 0; i < ARRAY_SIZE(rds_caps_size); i++) {
+ if (!rds_caps_size[i]) {
+ continue;
+ }
+ outbuf[size] = i;
+ outbuf[size + 1] = 0x40; /* Not writable, readable */
+ stw_be_p(&outbuf[size + 2], rds_caps_size[i]);
+ size += 4;
+ }
+ break;
+ }
+
+ default:
+ return -1;
+ }
+
+ /* Size of buffer, not including 2 byte size field */
+ stw_be_p(outbuf, size - 2);
+ return size;
+
+fail:
+ return -1;
+}
+
+static int scsi_event_status_media(SCSIDiskState *s, uint8_t *outbuf)
+{
+ uint8_t event_code, media_status;
+
+ media_status = 0;
+ if (s->tray_open) {
+ media_status = MS_TRAY_OPEN;
+ } else if (bdrv_is_inserted(s->qdev.conf.bs)) {
+ media_status = MS_MEDIA_PRESENT;
+ }
+
+ /* Event notification descriptor */
+ event_code = MEC_NO_CHANGE;
+ if (media_status != MS_TRAY_OPEN && s->media_event) {
+ event_code = MEC_NEW_MEDIA;
+ s->media_event = false;
+ }
+
+ outbuf[0] = event_code;
+ outbuf[1] = media_status;
+
+ /* These fields are reserved, just clear them. */
+ outbuf[2] = 0;
+ outbuf[3] = 0;
+ return 4;
+}
+
+static int scsi_get_event_status_notification(SCSIDiskState *s, SCSIDiskReq *r,
+ uint8_t *outbuf)
+{
+ int size;
+ uint8_t *buf = r->req.cmd.buf;
+ uint8_t notification_class_request = buf[4];
+ if (s->qdev.type != TYPE_ROM) {
+ return -1;
+ }
+ if ((buf[1] & 1) == 0) {
+ /* asynchronous */
+ return -1;
+ }
+
+ size = 4;
+ outbuf[0] = outbuf[1] = 0;
+ outbuf[3] = 1 << GESN_MEDIA; /* supported events */
+ if (notification_class_request & (1 << GESN_MEDIA)) {
+ outbuf[2] = GESN_MEDIA;
+ size += scsi_event_status_media(s, &outbuf[size]);
+ } else {
+ outbuf[2] = 0x80;
+ }
+ stw_be_p(outbuf, size - 4);
+ return size;
+}
+
+static int scsi_get_configuration(SCSIDiskState *s, uint8_t *outbuf)
+{
+ int current;
+
+ if (s->qdev.type != TYPE_ROM) {
+ return -1;
+ }
+ current = media_is_dvd(s) ? MMC_PROFILE_DVD_ROM : MMC_PROFILE_CD_ROM;
+ memset(outbuf, 0, 40);
+ stl_be_p(&outbuf[0], 36); /* Bytes after the data length field */
+ stw_be_p(&outbuf[6], current);
+ /* outbuf[8] - outbuf[19]: Feature 0 - Profile list */
+ outbuf[10] = 0x03; /* persistent, current */
+ outbuf[11] = 8; /* two profiles */
+ stw_be_p(&outbuf[12], MMC_PROFILE_DVD_ROM);
+ outbuf[14] = (current == MMC_PROFILE_DVD_ROM);
+ stw_be_p(&outbuf[16], MMC_PROFILE_CD_ROM);
+ outbuf[18] = (current == MMC_PROFILE_CD_ROM);
+ /* outbuf[20] - outbuf[31]: Feature 1 - Core feature */
+ stw_be_p(&outbuf[20], 1);
+ outbuf[22] = 0x08 | 0x03; /* version 2, persistent, current */
+ outbuf[23] = 8;
+ stl_be_p(&outbuf[24], 1); /* SCSI */
+ outbuf[28] = 1; /* DBE = 1, mandatory */
+ /* outbuf[32] - outbuf[39]: Feature 3 - Removable media feature */
+ stw_be_p(&outbuf[32], 3);
+ outbuf[34] = 0x08 | 0x03; /* version 2, persistent, current */
+ outbuf[35] = 4;
+ outbuf[36] = 0x39; /* tray, load=1, eject=1, unlocked at powerup, lock=1 */
+ /* TODO: Random readable, CD read, DVD read, drive serial number,
+ power management */
+ return 40;
+}
+
+static int scsi_emulate_mechanism_status(SCSIDiskState *s, uint8_t *outbuf)
+{
+ if (s->qdev.type != TYPE_ROM) {
+ return -1;
+ }
+ memset(outbuf, 0, 8);
+ outbuf[5] = 1; /* CD-ROM */
+ return 8;
+}
+
static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
int page_control)
{
- BlockDriverState *bdrv = s->bs;
+ static const int mode_sense_valid[0x3f] = {
+ [MODE_PAGE_HD_GEOMETRY] = (1 << TYPE_DISK),
+ [MODE_PAGE_FLEXIBLE_DISK_GEOMETRY] = (1 << TYPE_DISK),
+ [MODE_PAGE_CACHING] = (1 << TYPE_DISK) | (1 << TYPE_ROM),
+ [MODE_PAGE_R_W_ERROR] = (1 << TYPE_DISK) | (1 << TYPE_ROM),
+ [MODE_PAGE_AUDIO_CTL] = (1 << TYPE_ROM),
+ [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM),
+ };
+
+ BlockDriverState *bdrv = s->qdev.conf.bs;
int cylinders, heads, secs;
uint8_t *p = *p_outbuf;
+ if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) {
+ return -1;
+ }
+
+ p[0] = page;
+
/*
* If Changeable Values are requested, a mask denoting those mode parameters
* that are changeable shall be returned. As we currently don't support
@@ -573,11 +785,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
* The buffer was already menset to zero by the caller of this function.
*/
switch (page) {
- case 4: /* Rigid disk device geometry page. */
- if (s->qdev.type == TYPE_ROM) {
- return -1;
- }
- p[0] = 4;
+ case MODE_PAGE_HD_GEOMETRY:
p[1] = 0x16;
if (page_control == 1) { /* Changeable Values */
break;
@@ -608,11 +816,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
p[21] = 5400 & 0xff;
break;
- case 5: /* Flexible disk device geometry page. */
- if (s->qdev.type == TYPE_ROM) {
- return -1;
- }
- p[0] = 5;
+ case MODE_PAGE_FLEXIBLE_DISK_GEOMETRY:
p[1] = 0x1e;
if (page_control == 1) { /* Changeable Values */
break;
@@ -624,7 +828,7 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs);
p[4] = heads & 0xff;
p[5] = secs & 0xff;
- p[6] = s->cluster_size * 2;
+ p[6] = s->qdev.blocksize >> 8;
p[8] = (cylinders >> 8) & 0xff;
p[9] = cylinders & 0xff;
/* Write precomp start cylinder, disabled */
@@ -650,28 +854,37 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
p[29] = 5400 & 0xff;
break;
- case 8: /* Caching page. */
+ case MODE_PAGE_CACHING:
p[0] = 8;
p[1] = 0x12;
if (page_control == 1) { /* Changeable Values */
break;
}
- if (bdrv_enable_write_cache(s->bs)) {
+ if (bdrv_enable_write_cache(s->qdev.conf.bs)) {
p[2] = 4; /* WCE */
}
break;
- case 0x2a: /* CD Capabilities and Mechanical Status page. */
- if (s->qdev.type != TYPE_ROM) {
- return -1;
+ case MODE_PAGE_R_W_ERROR:
+ p[1] = 10;
+ p[2] = 0x80; /* Automatic Write Reallocation Enabled */
+ if (s->qdev.type == TYPE_ROM) {
+ p[3] = 0x20; /* Read Retry Count */
}
- p[0] = 0x2a;
+ break;
+
+ case MODE_PAGE_AUDIO_CTL:
+ p[1] = 14;
+ break;
+
+ case MODE_PAGE_CAPABILITIES:
p[1] = 0x14;
if (page_control == 1) { /* Changeable Values */
break;
}
- p[2] = 3; // CD-R & CD-RW read
- p[3] = 0; // Writing not supported
+
+ p[2] = 0x3b; /* CD-R & CD-RW read */
+ p[3] = 0; /* Writing not supported */
p[4] = 0x7f; /* Audio, composite, digital out,
mode 2 form 1&2, multi session */
p[5] = 0xff; /* CD DA, DA accurate, RW supported,
@@ -681,17 +894,17 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf,
/* Locking supported, jumper present, eject, tray */
p[7] = 0; /* no volume & mute control, no
changer */
- p[8] = (50 * 176) >> 8; // 50x read speed
+ p[8] = (50 * 176) >> 8; /* 50x read speed */
p[9] = (50 * 176) & 0xff;
- p[10] = 0 >> 8; // No volume
- p[11] = 0 & 0xff;
- p[12] = 2048 >> 8; // 2M buffer
+ p[10] = 2 >> 8; /* Two volume levels */
+ p[11] = 2 & 0xff;
+ p[12] = 2048 >> 8; /* 2M buffer */
p[13] = 2048 & 0xff;
- p[14] = (16 * 176) >> 8; // 16x read speed current
+ p[14] = (16 * 176) >> 8; /* 16x read speed current */
p[15] = (16 * 176) & 0xff;
- p[18] = (16 * 176) >> 8; // 16x write speed
+ p[18] = (16 * 176) >> 8; /* 16x write speed */
p[19] = (16 * 176) & 0xff;
- p[20] = (16 * 176) >> 8; // 16x write speed current
+ p[20] = (16 * 176) >> 8; /* 16x write speed current */
p[21] = (16 * 176) & 0xff;
break;
@@ -719,7 +932,7 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
memset(outbuf, 0, r->req.cmd.xfer);
p = outbuf;
- if (bdrv_is_read_only(s->bs)) {
+ if (bdrv_is_read_only(s->qdev.conf.bs)) {
dev_specific_param = 0x80; /* Readonly. */
} else {
dev_specific_param = 0x00;
@@ -737,23 +950,24 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
p += 8;
}
- bdrv_get_geometry(s->bs, &nb_sectors);
+ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
if (!dbd && nb_sectors) {
if (r->req.cmd.buf[0] == MODE_SENSE) {
outbuf[3] = 8; /* Block descriptor length */
} else { /* MODE_SENSE_10 */
outbuf[7] = 8; /* Block descriptor length */
}
- nb_sectors /= s->cluster_size;
- if (nb_sectors > 0xffffff)
+ nb_sectors /= (s->qdev.blocksize / 512);
+ if (nb_sectors > 0xffffff) {
nb_sectors = 0;
+ }
p[0] = 0; /* media density code */
p[1] = (nb_sectors >> 16) & 0xff;
p[2] = (nb_sectors >> 8) & 0xff;
p[3] = nb_sectors & 0xff;
p[4] = 0; /* reserved */
p[5] = 0; /* bytes 5-7 are the sector size in bytes */
- p[6] = s->cluster_size * 2;
+ p[6] = s->qdev.blocksize >> 8;
p[7] = 0;
p += 8;
}
@@ -787,8 +1001,9 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf)
outbuf[0] = ((buflen - 2) >> 8) & 0xff;
outbuf[1] = (buflen - 2) & 0xff;
}
- if (buflen > r->req.cmd.xfer)
+ if (buflen > r->req.cmd.xfer) {
buflen = r->req.cmd.xfer;
+ }
return buflen;
}
@@ -801,9 +1016,9 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf)
msf = req->cmd.buf[1] & 2;
format = req->cmd.buf[2] & 0xf;
start_track = req->cmd.buf[6];
- bdrv_get_geometry(s->bs, &nb_sectors);
+ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
- nb_sectors /= s->cluster_size;
+ nb_sectors /= s->qdev.blocksize / 512;
switch (format) {
case 0:
toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
@@ -822,8 +1037,9 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf)
default:
return -1;
}
- if (toclen > req->cmd.xfer)
+ if (toclen > req->cmd.xfer) {
toclen = req->cmd.xfer;
+ }
return toclen;
}
@@ -837,12 +1053,12 @@ static int scsi_disk_emulate_start_stop(SCSIDiskReq *r)
if (s->qdev.type == TYPE_ROM && loej) {
if (!start && !s->tray_open && s->tray_locked) {
scsi_check_condition(r,
- bdrv_is_inserted(s->bs)
+ bdrv_is_inserted(s->qdev.conf.bs)
? SENSE_CODE(ILLEGAL_REQ_REMOVAL_PREVENTED)
: SENSE_CODE(NOT_READY_REMOVAL_PREVENTED));
return -1;
}
- bdrv_eject(s->bs, !start);
+ bdrv_eject(s->qdev.conf.bs, !start);
s->tray_open = !start;
}
return 0;
@@ -869,46 +1085,54 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
goto illegal_request;
}
r->buflen = MAX(4096, req->cmd.xfer);
- r->iov.iov_base = qemu_blockalign(s->bs, r->buflen);
+ r->iov.iov_base = qemu_blockalign(s->qdev.conf.bs, r->buflen);
}
outbuf = r->iov.iov_base;
switch (req->cmd.buf[0]) {
case TEST_UNIT_READY:
- if (s->tray_open || !bdrv_is_inserted(s->bs))
+ if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
goto not_ready;
+ }
break;
case INQUIRY:
buflen = scsi_disk_emulate_inquiry(req, outbuf);
- if (buflen < 0)
+ if (buflen < 0) {
goto illegal_request;
+ }
break;
case MODE_SENSE:
case MODE_SENSE_10:
buflen = scsi_disk_emulate_mode_sense(r, outbuf);
- if (buflen < 0)
+ if (buflen < 0) {
goto illegal_request;
+ }
break;
case READ_TOC:
buflen = scsi_disk_emulate_read_toc(req, outbuf);
- if (buflen < 0)
+ if (buflen < 0) {
goto illegal_request;
+ }
break;
case RESERVE:
- if (req->cmd.buf[1] & 1)
+ if (req->cmd.buf[1] & 1) {
goto illegal_request;
+ }
break;
case RESERVE_10:
- if (req->cmd.buf[1] & 3)
+ if (req->cmd.buf[1] & 3) {
goto illegal_request;
+ }
break;
case RELEASE:
- if (req->cmd.buf[1] & 1)
+ if (req->cmd.buf[1] & 1) {
goto illegal_request;
+ }
break;
case RELEASE_10:
- if (req->cmd.buf[1] & 3)
+ if (req->cmd.buf[1] & 3) {
goto illegal_request;
+ }
break;
case START_STOP:
if (scsi_disk_emulate_start_stop(r) < 0) {
@@ -917,52 +1141,78 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
break;
case ALLOW_MEDIUM_REMOVAL:
s->tray_locked = req->cmd.buf[4] & 1;
- bdrv_lock_medium(s->bs, req->cmd.buf[4] & 1);
+ bdrv_lock_medium(s->qdev.conf.bs, req->cmd.buf[4] & 1);
break;
case READ_CAPACITY_10:
/* The normal LEN field for this command is zero. */
memset(outbuf, 0, 8);
- bdrv_get_geometry(s->bs, &nb_sectors);
- if (!nb_sectors)
+ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+ if (!nb_sectors) {
goto not_ready;
- nb_sectors /= s->cluster_size;
+ }
+ if ((req->cmd.buf[8] & 1) == 0 && req->cmd.lba) {
+ goto illegal_request;
+ }
+ nb_sectors /= s->qdev.blocksize / 512;
/* Returned value is the address of the last sector. */
nb_sectors--;
/* Remember the new size for read/write sanity checking. */
- s->max_lba = nb_sectors;
+ s->qdev.max_lba = nb_sectors;
/* Clip to 2TB, instead of returning capacity modulo 2TB. */
- if (nb_sectors > UINT32_MAX)
+ if (nb_sectors > UINT32_MAX) {
nb_sectors = UINT32_MAX;
+ }
outbuf[0] = (nb_sectors >> 24) & 0xff;
outbuf[1] = (nb_sectors >> 16) & 0xff;
outbuf[2] = (nb_sectors >> 8) & 0xff;
outbuf[3] = nb_sectors & 0xff;
outbuf[4] = 0;
outbuf[5] = 0;
- outbuf[6] = s->cluster_size * 2;
+ outbuf[6] = s->qdev.blocksize >> 8;
outbuf[7] = 0;
buflen = 8;
break;
+ case MECHANISM_STATUS:
+ buflen = scsi_emulate_mechanism_status(s, outbuf);
+ if (buflen < 0) {
+ goto illegal_request;
+ }
+ break;
case GET_CONFIGURATION:
- memset(outbuf, 0, 8);
- /* ??? This should probably return much more information. For now
- just return the basic header indicating the CD-ROM profile. */
- outbuf[7] = 8; // CD-ROM
- buflen = 8;
+ buflen = scsi_get_configuration(s, outbuf);
+ if (buflen < 0) {
+ goto illegal_request;
+ }
+ break;
+ case GET_EVENT_STATUS_NOTIFICATION:
+ buflen = scsi_get_event_status_notification(s, r, outbuf);
+ if (buflen < 0) {
+ goto illegal_request;
+ }
+ break;
+ case READ_DVD_STRUCTURE:
+ buflen = scsi_read_dvd_structure(s, r, outbuf);
+ if (buflen < 0) {
+ goto illegal_request;
+ }
break;
case SERVICE_ACTION_IN_16:
/* Service Action In subcommands. */
if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
DPRINTF("SAI READ CAPACITY(16)\n");
memset(outbuf, 0, req->cmd.xfer);
- bdrv_get_geometry(s->bs, &nb_sectors);
- if (!nb_sectors)
+ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+ if (!nb_sectors) {
goto not_ready;
- nb_sectors /= s->cluster_size;
+ }
+ if ((req->cmd.buf[14] & 1) == 0 && req->cmd.lba) {
+ goto illegal_request;
+ }
+ nb_sectors /= s->qdev.blocksize / 512;
/* Returned value is the address of the last sector. */
nb_sectors--;
/* Remember the new size for read/write sanity checking. */
- s->max_lba = nb_sectors;
+ s->qdev.max_lba = nb_sectors;
outbuf[0] = (nb_sectors >> 56) & 0xff;
outbuf[1] = (nb_sectors >> 48) & 0xff;
outbuf[2] = (nb_sectors >> 40) & 0xff;
@@ -973,7 +1223,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
outbuf[7] = nb_sectors & 0xff;
outbuf[8] = 0;
outbuf[9] = 0;
- outbuf[10] = s->cluster_size * 2;
+ outbuf[10] = s->qdev.blocksize >> 8;
outbuf[11] = 0;
outbuf[12] = 0;
outbuf[13] = get_physical_block_exp(&s->qdev.conf);
@@ -998,7 +1248,7 @@ static int scsi_disk_emulate_command(SCSIDiskReq *r)
return buflen;
not_ready:
- if (s->tray_open || !bdrv_is_inserted(s->bs)) {
+ if (s->tray_open || !bdrv_is_inserted(s->qdev.conf.bs)) {
scsi_check_condition(r, SENSE_CODE(NO_MEDIUM));
} else {
scsi_check_condition(r, SENSE_CODE(LUN_NOT_READY));
@@ -1051,7 +1301,10 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
case ALLOW_MEDIUM_REMOVAL:
case READ_CAPACITY_10:
case READ_TOC:
+ case READ_DVD_STRUCTURE:
case GET_CONFIGURATION:
+ case GET_EVENT_STATUS_NOTIFICATION:
+ case MECHANISM_STATUS:
case SERVICE_ACTION_IN_16:
case VERIFY_10:
rc = scsi_disk_emulate_command(r);
@@ -1062,8 +1315,10 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
r->iov.iov_len = rc;
break;
case SYNCHRONIZE_CACHE:
- bdrv_acct_start(s->bs, &r->acct, 0, BDRV_ACCT_FLUSH);
- r->req.aiocb = bdrv_aio_flush(s->bs, scsi_flush_complete, r);
+ /* The request is used as the AIO opaque value, so add a ref. */
+ scsi_req_ref(&r->req);
+ bdrv_acct_start(s->qdev.conf.bs, &r->acct, 0, BDRV_ACCT_FLUSH);
+ r->req.aiocb = bdrv_aio_flush(s->qdev.conf.bs, scsi_flush_complete, r);
if (r->req.aiocb == NULL) {
scsi_flush_complete(r, -EIO);
}
@@ -1074,10 +1329,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
case READ_16:
len = r->req.cmd.xfer / s->qdev.blocksize;
DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len);
- if (r->req.cmd.lba > s->max_lba)
+ if (r->req.cmd.lba > s->qdev.max_lba) {
goto illegal_lba;
- r->sector = r->req.cmd.lba * s->cluster_size;
- r->sector_count = len * s->cluster_size;
+ }
+ r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
+ r->sector_count = len * (s->qdev.blocksize / 512);
break;
case WRITE_6:
case WRITE_10:
@@ -1090,10 +1346,11 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
DPRINTF("Write %s(sector %" PRId64 ", count %d)\n",
(command & 0xe) == 0xe ? "And Verify " : "",
r->req.cmd.lba, len);
- if (r->req.cmd.lba > s->max_lba)
+ if (r->req.cmd.lba > s->qdev.max_lba) {
goto illegal_lba;
- r->sector = r->req.cmd.lba * s->cluster_size;
- r->sector_count = len * s->cluster_size;
+ }
+ r->sector = r->req.cmd.lba * (s->qdev.blocksize / 512);
+ r->sector_count = len * (s->qdev.blocksize / 512);
break;
case MODE_SELECT:
DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer);
@@ -1115,7 +1372,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
case SEEK_10:
DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10,
r->req.cmd.lba);
- if (r->req.cmd.lba > s->max_lba) {
+ if (r->req.cmd.lba > s->qdev.max_lba) {
goto illegal_lba;
}
break;
@@ -1125,7 +1382,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n",
r->req.cmd.lba, len);
- if (r->req.cmd.lba > s->max_lba) {
+ if (r->req.cmd.lba > s->qdev.max_lba) {
goto illegal_lba;
}
@@ -1136,8 +1393,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
goto fail;
}
- rc = bdrv_discard(s->bs, r->req.cmd.lba * s->cluster_size,
- len * s->cluster_size);
+ rc = bdrv_discard(s->qdev.conf.bs,
+ r->req.cmd.lba * (s->qdev.blocksize / 512),
+ len * (s->qdev.blocksize / 512));
if (rc < 0) {
/* XXX: better error code ?*/
goto fail;
@@ -1164,8 +1422,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *buf)
if (r->req.cmd.mode == SCSI_XFER_TO_DEV) {
return -len;
} else {
- if (!r->sector_count)
+ if (!r->sector_count) {
r->sector_count = -1;
+ }
return len;
}
}
@@ -1177,12 +1436,12 @@ static void scsi_disk_reset(DeviceState *dev)
scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET));
- bdrv_get_geometry(s->bs, &nb_sectors);
- nb_sectors /= s->cluster_size;
+ bdrv_get_geometry(s->qdev.conf.bs, &nb_sectors);
+ nb_sectors /= s->qdev.blocksize / 512;
if (nb_sectors) {
nb_sectors--;
}
- s->max_lba = nb_sectors;
+ s->qdev.max_lba = nb_sectors;
}
static void scsi_destroy(SCSIDevice *dev)
@@ -1195,7 +1454,22 @@ static void scsi_destroy(SCSIDevice *dev)
static void scsi_cd_change_media_cb(void *opaque, bool load)
{
- ((SCSIDiskState *)opaque)->tray_open = !load;
+ SCSIDiskState *s = opaque;
+
+ /*
+ * When a CD gets changed, we have to report an ejected state and
+ * then a loaded state to guests so that they detect tray
+ * open/close and media change events. Guests that do not use
+ * GET_EVENT_STATUS_NOTIFICATION to detect such tray open/close
+ * states rely on this behavior.
+ *
+ * media_changed governs the state machine used for unit attention
+ * report. media_event is used by GET EVENT STATUS NOTIFICATION.
+ */
+ s->media_changed = load;
+ s->tray_open = !load;
+ s->qdev.unit_attention = SENSE_CODE(UNIT_ATTENTION_NO_MEDIUM);
+ s->media_event = true;
}
static bool scsi_cd_is_tray_open(void *opaque)
@@ -1214,7 +1488,16 @@ static const BlockDevOps scsi_cd_block_ops = {
.is_medium_locked = scsi_cd_is_medium_locked,
};
-static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type)
+static void scsi_disk_unit_attention_reported(SCSIDevice *dev)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ if (s->media_changed) {
+ s->media_changed = false;
+ s->qdev.unit_attention = SENSE_CODE(MEDIUM_CHANGED);
+ }
+}
+
+static int scsi_initfn(SCSIDevice *dev)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
DriveInfo *dinfo;
@@ -1223,16 +1506,15 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type)
error_report("scsi-disk: drive property not set");
return -1;
}
- s->bs = s->qdev.conf.bs;
- if (scsi_type == TYPE_DISK && !bdrv_is_inserted(s->bs)) {
+ if (!s->removable && !bdrv_is_inserted(s->qdev.conf.bs)) {
error_report("Device needs media, but drive is empty");
return -1;
}
if (!s->serial) {
/* try to fall back to value set with legacy -drive serial=... */
- dinfo = drive_get_by_blockdev(s->bs);
+ dinfo = drive_get_by_blockdev(s->qdev.conf.bs);
if (*dinfo->serial) {
s->serial = g_strdup(dinfo->serial);
}
@@ -1242,56 +1524,55 @@ static int scsi_initfn(SCSIDevice *dev, uint8_t scsi_type)
s->version = g_strdup(QEMU_VERSION);
}
- if (bdrv_is_sg(s->bs)) {
+ if (bdrv_is_sg(s->qdev.conf.bs)) {
error_report("scsi-disk: unwanted /dev/sg*");
return -1;
}
- if (scsi_type == TYPE_ROM) {
- bdrv_set_dev_ops(s->bs, &scsi_cd_block_ops, s);
- s->qdev.blocksize = 2048;
- } else if (scsi_type == TYPE_DISK) {
- s->qdev.blocksize = s->qdev.conf.logical_block_size;
- } else {
- error_report("scsi-disk: Unhandled SCSI type %02x", scsi_type);
- return -1;
+ if (s->removable) {
+ bdrv_set_dev_ops(s->qdev.conf.bs, &scsi_cd_block_ops, s);
}
- s->cluster_size = s->qdev.blocksize / 512;
- bdrv_set_buffer_alignment(s->bs, s->qdev.blocksize);
+ bdrv_set_buffer_alignment(s->qdev.conf.bs, s->qdev.blocksize);
- s->qdev.type = scsi_type;
- qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s);
- bdrv_iostatus_enable(s->bs);
+ bdrv_iostatus_enable(s->qdev.conf.bs);
add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0");
return 0;
}
static int scsi_hd_initfn(SCSIDevice *dev)
{
- return scsi_initfn(dev, TYPE_DISK);
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ s->qdev.blocksize = s->qdev.conf.logical_block_size;
+ s->qdev.type = TYPE_DISK;
+ return scsi_initfn(&s->qdev);
}
static int scsi_cd_initfn(SCSIDevice *dev)
{
- return scsi_initfn(dev, TYPE_ROM);
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ s->qdev.blocksize = 2048;
+ s->qdev.type = TYPE_ROM;
+ s->removable = true;
+ return scsi_initfn(&s->qdev);
}
static int scsi_disk_initfn(SCSIDevice *dev)
{
DriveInfo *dinfo;
- uint8_t scsi_type;
if (!dev->conf.bs) {
- scsi_type = TYPE_DISK; /* will die in scsi_initfn() */
- } else {
- dinfo = drive_get_by_blockdev(dev->conf.bs);
- scsi_type = dinfo->media_cd ? TYPE_ROM : TYPE_DISK;
+ return scsi_initfn(dev); /* ... and die there */
}
- return scsi_initfn(dev, scsi_type);
+ dinfo = drive_get_by_blockdev(dev->conf.bs);
+ if (dinfo->media_cd) {
+ return scsi_cd_initfn(dev);
+ } else {
+ return scsi_hd_initfn(dev);
+ }
}
-static SCSIReqOps scsi_disk_reqops = {
+static const SCSIReqOps scsi_disk_reqops = {
.size = sizeof(SCSIDiskReq),
.free_req = scsi_free_request,
.send_command = scsi_send_command,
@@ -1301,8 +1582,8 @@ static SCSIReqOps scsi_disk_reqops = {
.get_buf = scsi_get_buf,
};
-static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
- uint32_t lun, void *hba_private)
+static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
+ uint8_t *buf, void *hba_private)
{
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
SCSIRequest *req;
@@ -1311,6 +1592,105 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag,
return req;
}
+#ifdef __linux__
+static int get_device_type(SCSIDiskState *s)
+{
+ BlockDriverState *bdrv = s->qdev.conf.bs;
+ uint8_t cmd[16];
+ uint8_t buf[36];
+ uint8_t sensebuf[8];
+ sg_io_hdr_t io_header;
+ int ret;
+
+ memset(cmd, 0, sizeof(cmd));
+ memset(buf, 0, sizeof(buf));
+ cmd[0] = INQUIRY;
+ cmd[4] = sizeof(buf);
+
+ memset(&io_header, 0, sizeof(io_header));
+ io_header.interface_id = 'S';
+ io_header.dxfer_direction = SG_DXFER_FROM_DEV;
+ io_header.dxfer_len = sizeof(buf);
+ io_header.dxferp = buf;
+ io_header.cmdp = cmd;
+ io_header.cmd_len = sizeof(cmd);
+ io_header.mx_sb_len = sizeof(sensebuf);
+ io_header.sbp = sensebuf;
+ io_header.timeout = 6000; /* XXX */
+
+ ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
+ if (ret < 0 || io_header.driver_status || io_header.host_status) {
+ return -1;
+ }
+ s->qdev.type = buf[0];
+ s->removable = (buf[1] & 0x80) != 0;
+ return 0;
+}
+
+static int scsi_block_initfn(SCSIDevice *dev)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
+ int sg_version;
+ int rc;
+
+ if (!s->qdev.conf.bs) {
+ error_report("scsi-block: drive property not set");
+ return -1;
+ }
+
+ /* check we are using a driver managing SG_IO (version 3 and after) */
+ if (bdrv_ioctl(s->qdev.conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
+ sg_version < 30000) {
+ error_report("scsi-block: scsi generic interface too old");
+ return -1;
+ }
+
+ /* get device type from INQUIRY data */
+ rc = get_device_type(s);
+ if (rc < 0) {
+ error_report("scsi-block: INQUIRY failed");
+ return -1;
+ }
+
+ /* Make a guess for the block size, we'll fix it when the guest sends.
+ * READ CAPACITY. If they don't, they likely would assume these sizes
+ * anyway. (TODO: check in /sys).
+ */
+ if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) {
+ s->qdev.blocksize = 2048;
+ } else {
+ s->qdev.blocksize = 512;
+ }
+ return scsi_initfn(&s->qdev);
+}
+
+static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag,
+ uint32_t lun, uint8_t *buf,
+ void *hba_private)
+{
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d);
+
+ switch (buf[0]) {
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ case WRITE_VERIFY_10:
+ case WRITE_VERIFY_12:
+ case WRITE_VERIFY_16:
+ return scsi_req_alloc(&scsi_disk_reqops, &s->qdev, tag, lun,
+ hba_private);
+ }
+
+ return scsi_req_alloc(&scsi_generic_req_ops, &s->qdev, tag, lun,
+ hba_private);
+}
+#endif
+
#define DEFINE_SCSI_DISK_PROPERTIES() \
DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \
DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
@@ -1326,6 +1706,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.init = scsi_hd_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
+ .unit_attention_reported = scsi_disk_unit_attention_reported,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
@@ -1340,10 +1721,26 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.init = scsi_cd_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
+ .unit_attention_reported = scsi_disk_unit_attention_reported,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_END_OF_LIST(),
},
+#ifdef __linux__
+ },{
+ .qdev.name = "scsi-block",
+ .qdev.fw_name = "disk",
+ .qdev.desc = "SCSI block device passthrough",
+ .qdev.size = sizeof(SCSIDiskState),
+ .qdev.reset = scsi_disk_reset,
+ .init = scsi_block_initfn,
+ .destroy = scsi_destroy,
+ .alloc_req = scsi_block_new_request,
+ .qdev.props = (Property[]) {
+ DEFINE_SCSI_DISK_PROPERTIES(),
+ DEFINE_PROP_END_OF_LIST(),
+ },
+#endif
},{
.qdev.name = "scsi-disk", /* legacy -device scsi-disk */
.qdev.fw_name = "disk",
@@ -1353,6 +1750,7 @@ static SCSIDeviceInfo scsi_disk_info[] = {
.init = scsi_disk_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
+ .unit_attention_reported = scsi_disk_unit_attention_reported,
.qdev.props = (Property[]) {
DEFINE_SCSI_DISK_PROPERTIES(),
DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false),
diff --git a/hw/scsi-generic.c b/hw/scsi-generic.c
index 8f6b70df2b..9594cc1276 100644
--- a/hw/scsi-generic.c
+++ b/hw/scsi-generic.c
@@ -39,15 +39,18 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0)
#define SCSI_SENSE_BUF_SIZE 96
-#define SG_ERR_DRIVER_TIMEOUT 0x06
-#define SG_ERR_DRIVER_SENSE 0x08
+#define SG_ERR_DRIVER_TIMEOUT 0x06
+#define SG_ERR_DRIVER_SENSE 0x08
+
+#define SG_ERR_DID_OK 0x00
+#define SG_ERR_DID_NO_CONNECT 0x01
+#define SG_ERR_DID_BUS_BUSY 0x02
+#define SG_ERR_DID_TIME_OUT 0x03
#ifndef MAX_UINT
#define MAX_UINT ((unsigned int)-1)
#endif
-typedef struct SCSIGenericState SCSIGenericState;
-
typedef struct SCSIGenericReq {
SCSIRequest req;
uint8_t *buf;
@@ -56,12 +59,6 @@ typedef struct SCSIGenericReq {
sg_io_hdr_t io_header;
} SCSIGenericReq;
-struct SCSIGenericState
-{
- SCSIDevice qdev;
- BlockDriverState *bs;
-};
-
static void scsi_free_request(SCSIRequest *req)
{
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
@@ -76,8 +73,9 @@ static void scsi_command_complete(void *opaque, int ret)
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
r->req.aiocb = NULL;
- if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE)
+ if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
r->req.sense_len = r->io_header.sb_len_wr;
+ }
if (ret != 0) {
switch (ret) {
@@ -94,9 +92,15 @@ static void scsi_command_complete(void *opaque, int ret)
break;
}
} else {
- if (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT) {
+ if (r->io_header.host_status == SG_ERR_DID_NO_CONNECT ||
+ r->io_header.host_status == SG_ERR_DID_BUS_BUSY ||
+ r->io_header.host_status == SG_ERR_DID_TIME_OUT ||
+ (r->io_header.driver_status & SG_ERR_DRIVER_TIMEOUT)) {
status = BUSY;
BADF("Driver Timeout\n");
+ } else if (r->io_header.host_status) {
+ status = CHECK_CONDITION;
+ scsi_req_build_sense(&r->req, SENSE_CODE(I_T_NEXUS_LOSS));
} else if (r->io_header.status) {
status = r->io_header.status;
} else if (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) {
@@ -109,6 +113,9 @@ static void scsi_command_complete(void *opaque, int ret)
r, r->req.tag, status);
scsi_req_complete(&r->req, status);
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
}
/* Cancel a pending data transfer. */
@@ -119,6 +126,11 @@ static void scsi_cancel_io(SCSIRequest *req)
DPRINTF("Cancel tag=0x%x\n", req->tag);
if (r->req.aiocb) {
bdrv_aio_cancel(r->req.aiocb);
+
+ /* This reference was left in by scsi_*_data. We take ownership of
+ * it independent of whether bdrv_aio_cancel completes the request
+ * or not. */
+ scsi_req_unref(&r->req);
}
r->req.aiocb = NULL;
}
@@ -151,6 +163,7 @@ static int execute_command(BlockDriverState *bdrv,
static void scsi_read_complete(void * opaque, int ret)
{
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+ SCSIDevice *s = r->req.dev;
int len;
r->req.aiocb = NULL;
@@ -166,7 +179,21 @@ static void scsi_read_complete(void * opaque, int ret)
if (len == 0) {
scsi_command_complete(r, 0);
} else {
+ /* Snoop READ CAPACITY output to set the blocksize. */
+ if (r->req.cmd.buf[0] == READ_CAPACITY_10) {
+ s->blocksize = ldl_be_p(&r->buf[4]);
+ s->max_lba = ldl_be_p(&r->buf[0]);
+ } else if (r->req.cmd.buf[0] == SERVICE_ACTION_IN_16 &&
+ (r->req.cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) {
+ s->blocksize = ldl_be_p(&r->buf[8]);
+ s->max_lba = ldq_be_p(&r->buf[0]);
+ }
+ bdrv_set_buffer_alignment(s->conf.bs, s->blocksize);
+
scsi_req_data(&r->req, len);
+ if (!r->req.io_canceled) {
+ scsi_req_unref(&r->req);
+ }
}
}
@@ -174,26 +201,28 @@ static void scsi_read_complete(void * opaque, int ret)
static void scsi_read_data(SCSIRequest *req)
{
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
+ SCSIDevice *s = r->req.dev;
int ret;
DPRINTF("scsi_read_data 0x%x\n", req->tag);
+
+ /* The request is used as the AIO opaque value, so add a ref. */
+ scsi_req_ref(&r->req);
if (r->len == -1) {
scsi_command_complete(r, 0);
return;
}
- ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
+ ret = execute_command(s->conf.bs, r, SG_DXFER_FROM_DEV, scsi_read_complete);
if (ret < 0) {
scsi_command_complete(r, ret);
- return;
}
}
static void scsi_write_complete(void * opaque, int ret)
{
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, r->req.dev);
+ SCSIDevice *s = r->req.dev;
DPRINTF("scsi_write_complete() ret = %d\n", ret);
r->req.aiocb = NULL;
@@ -204,9 +233,9 @@ static void scsi_write_complete(void * opaque, int ret)
}
if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
- s->qdev.type == TYPE_TAPE) {
- s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
- DPRINTF("block size %d\n", s->qdev.blocksize);
+ s->type == TYPE_TAPE) {
+ s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11];
+ DPRINTF("block size %d\n", s->blocksize);
}
scsi_command_complete(r, ret);
@@ -216,8 +245,8 @@ static void scsi_write_complete(void * opaque, int ret)
The transfer may complete asynchronously. */
static void scsi_write_data(SCSIRequest *req)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+ SCSIDevice *s = r->req.dev;
int ret;
DPRINTF("scsi_write_data 0x%x\n", req->tag);
@@ -227,7 +256,9 @@ static void scsi_write_data(SCSIRequest *req)
return;
}
- ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
+ /* The request is used as the AIO opaque value, so add a ref. */
+ scsi_req_ref(&r->req);
+ ret = execute_command(s->conf.bs, r, SG_DXFER_TO_DEV, scsi_write_complete);
if (ret < 0) {
scsi_command_complete(r, ret);
}
@@ -241,19 +272,6 @@ static uint8_t *scsi_get_buf(SCSIRequest *req)
return r->buf;
}
-static void scsi_req_fixup(SCSIRequest *req)
-{
- switch(req->cmd.buf[0]) {
- case REWIND:
- case START_STOP:
- if (req->dev->type == TYPE_TAPE) {
- /* force IMMED, otherwise qemu waits end of command */
- req->cmd.buf[1] = 0x01;
- }
- break;
- }
-}
-
/* Execute a scsi command. Returns the length of the data expected by the
command. This will be Positive for data transfers from the device
(eg. disk reads), negative for transfers to the device (eg. disk writes),
@@ -261,12 +279,10 @@ static void scsi_req_fixup(SCSIRequest *req)
static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, req->dev);
SCSIGenericReq *r = DO_UPCAST(SCSIGenericReq, req, req);
+ SCSIDevice *s = r->req.dev;
int ret;
- scsi_req_fixup(&r->req);
-
DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag,
r->req.cmd.xfer, cmd[0]);
@@ -285,7 +301,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
g_free(r->buf);
r->buflen = 0;
r->buf = NULL;
- ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete);
+ /* The request is used as the AIO opaque value, so add a ref. */
+ scsi_req_ref(&r->req);
+ ret = execute_command(s->conf.bs, r, SG_DXFER_NONE, scsi_command_complete);
if (ret < 0) {
scsi_command_complete(r, ret);
return 0;
@@ -310,36 +328,6 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd)
}
}
-static int get_blocksize(BlockDriverState *bdrv)
-{
- uint8_t cmd[10];
- uint8_t buf[8];
- uint8_t sensebuf[8];
- sg_io_hdr_t io_header;
- int ret;
-
- memset(cmd, 0, sizeof(cmd));
- memset(buf, 0, sizeof(buf));
- cmd[0] = READ_CAPACITY_10;
-
- memset(&io_header, 0, sizeof(io_header));
- io_header.interface_id = 'S';
- io_header.dxfer_direction = SG_DXFER_FROM_DEV;
- io_header.dxfer_len = sizeof(buf);
- io_header.dxferp = buf;
- io_header.cmdp = cmd;
- io_header.cmd_len = sizeof(cmd);
- io_header.mx_sb_len = sizeof(sensebuf);
- io_header.sbp = sensebuf;
- io_header.timeout = 6000; /* XXX */
-
- ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
- if (ret < 0)
- return -1;
-
- return (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
-}
-
static int get_stream_blocksize(BlockDriverState *bdrv)
{
uint8_t cmd[6];
@@ -365,89 +353,92 @@ static int get_stream_blocksize(BlockDriverState *bdrv)
io_header.timeout = 6000; /* XXX */
ret = bdrv_ioctl(bdrv, SG_IO, &io_header);
- if (ret < 0)
+ if (ret < 0 || io_header.driver_status || io_header.host_status) {
return -1;
-
+ }
return (buf[9] << 16) | (buf[10] << 8) | buf[11];
}
static void scsi_generic_reset(DeviceState *dev)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev);
+ SCSIDevice *s = DO_UPCAST(SCSIDevice, qdev, dev);
- scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET));
+ scsi_device_purge_requests(s, SENSE_CODE(RESET));
}
-static void scsi_destroy(SCSIDevice *d)
+static void scsi_destroy(SCSIDevice *s)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d);
-
- scsi_device_purge_requests(&s->qdev, SENSE_CODE(NO_SENSE));
- blockdev_mark_auto_del(s->qdev.conf.bs);
+ scsi_device_purge_requests(s, SENSE_CODE(NO_SENSE));
+ blockdev_mark_auto_del(s->conf.bs);
}
-static int scsi_generic_initfn(SCSIDevice *dev)
+static int scsi_generic_initfn(SCSIDevice *s)
{
- SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, dev);
int sg_version;
struct sg_scsi_id scsiid;
- if (!s->qdev.conf.bs) {
+ if (!s->conf.bs) {
error_report("scsi-generic: drive property not set");
return -1;
}
- s->bs = s->qdev.conf.bs;
/* check we are really using a /dev/sg* file */
- if (!bdrv_is_sg(s->bs)) {
+ if (!bdrv_is_sg(s->conf.bs)) {
error_report("scsi-generic: not /dev/sg*");
return -1;
}
- if (bdrv_get_on_error(s->bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
+ if (bdrv_get_on_error(s->conf.bs, 0) != BLOCK_ERR_STOP_ENOSPC) {
error_report("Device doesn't support drive option werror");
return -1;
}
- if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) {
+ if (bdrv_get_on_error(s->conf.bs, 1) != BLOCK_ERR_REPORT) {
error_report("Device doesn't support drive option rerror");
return -1;
}
/* check we are using a driver managing SG_IO (version 3 and after */
- if (bdrv_ioctl(s->bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
+ if (bdrv_ioctl(s->conf.bs, SG_GET_VERSION_NUM, &sg_version) < 0 ||
sg_version < 30000) {
error_report("scsi-generic: scsi generic interface too old");
return -1;
}
/* get LUN of the /dev/sg? */
- if (bdrv_ioctl(s->bs, SG_GET_SCSI_ID, &scsiid)) {
+ if (bdrv_ioctl(s->conf.bs, SG_GET_SCSI_ID, &scsiid)) {
error_report("scsi-generic: SG_GET_SCSI_ID ioctl failed");
return -1;
}
/* define device state */
- s->qdev.type = scsiid.scsi_type;
- DPRINTF("device type %d\n", s->qdev.type);
- if (s->qdev.type == TYPE_TAPE) {
- s->qdev.blocksize = get_stream_blocksize(s->bs);
- if (s->qdev.blocksize == -1)
- s->qdev.blocksize = 0;
- } else {
- s->qdev.blocksize = get_blocksize(s->bs);
- /* removable media returns 0 if not present */
- if (s->qdev.blocksize <= 0) {
- if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM)
- s->qdev.blocksize = 2048;
- else
- s->qdev.blocksize = 512;
+ s->type = scsiid.scsi_type;
+ DPRINTF("device type %d\n", s->type);
+ switch (s->type) {
+ case TYPE_TAPE:
+ s->blocksize = get_stream_blocksize(s->conf.bs);
+ if (s->blocksize == -1) {
+ s->blocksize = 0;
}
+ break;
+
+ /* Make a guess for block devices, we'll fix it when the guest sends.
+ * READ CAPACITY. If they don't, they likely would assume these sizes
+ * anyway. (TODO: they could also send MODE SENSE).
+ */
+ case TYPE_ROM:
+ case TYPE_WORM:
+ s->blocksize = 2048;
+ break;
+ default:
+ s->blocksize = 512;
+ break;
}
- DPRINTF("block size %d\n", s->qdev.blocksize);
+
+ DPRINTF("block size %d\n", s->blocksize);
return 0;
}
-static SCSIReqOps scsi_generic_req_ops = {
+const SCSIReqOps scsi_generic_req_ops = {
.size = sizeof(SCSIGenericReq),
.free_req = scsi_free_request,
.send_command = scsi_send_command,
@@ -458,7 +449,7 @@ static SCSIReqOps scsi_generic_req_ops = {
};
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
- void *hba_private)
+ uint8_t *buf, void *hba_private)
{
SCSIRequest *req;
@@ -469,13 +460,13 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
static SCSIDeviceInfo scsi_generic_info = {
.qdev.name = "scsi-generic",
.qdev.desc = "pass through generic scsi device (/dev/sg*)",
- .qdev.size = sizeof(SCSIGenericState),
+ .qdev.size = sizeof(SCSIDevice),
.qdev.reset = scsi_generic_reset,
.init = scsi_generic_initfn,
.destroy = scsi_destroy,
.alloc_req = scsi_new_request,
.qdev.props = (Property[]) {
- DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf),
+ DEFINE_BLOCK_PROPERTIES(SCSIDevice, conf),
DEFINE_PROP_END_OF_LIST(),
},
};
diff --git a/hw/scsi.h b/hw/scsi.h
index e8dcabfa28..ff8fdd0962 100644
--- a/hw/scsi.h
+++ b/hw/scsi.h
@@ -3,13 +3,14 @@
#include "qdev.h"
#include "block.h"
+#include "sysemu.h"
#define MAX_SCSI_DEVS 255
#define SCSI_CMD_BUF_SIZE 16
typedef struct SCSIBus SCSIBus;
-typedef struct SCSIBusOps SCSIBusOps;
+typedef struct SCSIBusInfo SCSIBusInfo;
typedef struct SCSICommand SCSICommand;
typedef struct SCSIDevice SCSIDevice;
typedef struct SCSIDeviceInfo SCSIDeviceInfo;
@@ -41,7 +42,7 @@ struct SCSICommand {
struct SCSIRequest {
SCSIBus *bus;
SCSIDevice *dev;
- SCSIReqOps *ops;
+ const SCSIReqOps *ops;
uint32_t refcount;
uint32_t tag;
uint32_t lun;
@@ -51,6 +52,8 @@ struct SCSIRequest {
uint8_t sense[SCSI_SENSE_BUF_SIZE];
uint32_t sense_len;
bool enqueued;
+ bool io_canceled;
+ bool retry;
void *hba_private;
QTAILQ_ENTRY(SCSIRequest) next;
};
@@ -58,16 +61,21 @@ struct SCSIRequest {
struct SCSIDevice
{
DeviceState qdev;
+ VMChangeStateEntry *vmsentry;
+ QEMUBH *bh;
uint32_t id;
BlockConf conf;
SCSIDeviceInfo *info;
SCSISense unit_attention;
+ bool sense_is_ua;
uint8_t sense[SCSI_SENSE_BUF_SIZE];
uint32_t sense_len;
QTAILQ_HEAD(, SCSIRequest) requests;
+ uint32_t channel;
uint32_t lun;
int blocksize;
int type;
+ uint64_t max_lba;
};
/* cdrom.c */
@@ -91,11 +99,13 @@ struct SCSIDeviceInfo {
scsi_qdev_initfn init;
void (*destroy)(SCSIDevice *s);
SCSIRequest *(*alloc_req)(SCSIDevice *s, uint32_t tag, uint32_t lun,
- void *hba_private);
- SCSIReqOps reqops;
+ uint8_t *buf, void *hba_private);
+ void (*unit_attention_reported)(SCSIDevice *s);
};
-struct SCSIBusOps {
+struct SCSIBusInfo {
+ int tcq;
+ int max_channel, max_target, max_lun;
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
void (*complete)(SCSIRequest *req, uint32_t arg);
void (*cancel)(SCSIRequest *req);
@@ -106,14 +116,10 @@ struct SCSIBus {
int busnr;
SCSISense unit_attention;
- int tcq, ndev;
- const SCSIBusOps *ops;
-
- SCSIDevice *devs[MAX_SCSI_DEVS];
+ const SCSIBusInfo *info;
};
-void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
- const SCSIBusOps *ops);
+void scsi_bus_new(SCSIBus *bus, DeviceState *host, const SCSIBusInfo *info);
void scsi_qdev_register(SCSIDeviceInfo *info);
static inline SCSIBus *scsi_bus_from_device(SCSIDevice *d)
@@ -159,6 +165,8 @@ extern const struct SCSISense sense_code_IO_ERROR;
extern const struct SCSISense sense_code_I_T_NEXUS_LOSS;
/* Command aborted, Logical Unit failure */
extern const struct SCSISense sense_code_LUN_FAILURE;
+/* LUN not ready, Medium not present */
+extern const struct SCSISense sense_code_UNIT_ATTENTION_NO_MEDIUM;
/* Unit attention, Power on, reset or bus device reset occurred */
extern const struct SCSISense sense_code_RESET;
/* Unit attention, Medium may have changed*/
@@ -172,8 +180,8 @@ extern const struct SCSISense sense_code_DEVICE_INTERNAL_RESET;
int scsi_sense_valid(SCSISense sense);
-SCSIRequest *scsi_req_alloc(SCSIReqOps *reqops, SCSIDevice *d, uint32_t tag,
- uint32_t lun, void *hba_private);
+SCSIRequest *scsi_req_alloc(const SCSIReqOps *reqops, SCSIDevice *d,
+ uint32_t tag, uint32_t lun, void *hba_private);
SCSIRequest *scsi_req_new(SCSIDevice *d, uint32_t tag, uint32_t lun,
uint8_t *buf, void *hba_private);
int32_t scsi_req_enqueue(SCSIRequest *req);
@@ -190,7 +198,12 @@ uint8_t *scsi_req_get_buf(SCSIRequest *req);
int scsi_req_get_sense(SCSIRequest *req, uint8_t *buf, int len);
void scsi_req_abort(SCSIRequest *req, int status);
void scsi_req_cancel(SCSIRequest *req);
+void scsi_req_retry(SCSIRequest *req);
void scsi_device_purge_requests(SCSIDevice *sdev, SCSISense sense);
int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed);
+SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun);
+
+/* scsi-generic.c. */
+extern const SCSIReqOps scsi_generic_req_ops;
#endif
diff --git a/hw/spapr_vio.c b/hw/spapr_vio.c
index 35818e18f1..977603f81e 100644
--- a/hw/spapr_vio.c
+++ b/hw/spapr_vio.c
@@ -63,7 +63,7 @@ VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg)
DeviceState *qdev;
VIOsPAPRDevice *dev = NULL;
- QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+ QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) {
dev = (VIOsPAPRDevice *)qdev;
if (dev->reg == reg) {
break;
@@ -588,7 +588,7 @@ static void rtas_quiesce(sPAPREnvironment *spapr, uint32_t token,
return;
}
- QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+ QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) {
dev = (VIOsPAPRDevice *)qdev;
spapr_vio_quiesce_one(dev);
}
@@ -726,7 +726,7 @@ int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt)
DeviceState *qdev;
int ret = 0;
- QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+ QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) {
VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev;
ret = vio_make_devnode(dev, fdt);
diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c
index e8426d7c5e..00e2d2d5d3 100644
--- a/hw/spapr_vscsi.c
+++ b/hw/spapr_vscsi.c
@@ -129,11 +129,38 @@ static void vscsi_put_req(vscsi_req *req)
req->active = 0;
}
-static void vscsi_decode_id_lun(uint64_t srp_lun, int *id, int *lun)
+static SCSIDevice *vscsi_device_find(SCSIBus *bus, uint64_t srp_lun, int *lun)
{
- /* XXX Figure that one out properly ! This is crackpot */
- *id = (srp_lun >> 56) & 0x7f;
- *lun = (srp_lun >> 48) & 0xff;
+ int channel = 0, id = 0;
+
+retry:
+ switch (srp_lun >> 62) {
+ case 0:
+ if ((srp_lun >> 56) != 0) {
+ channel = (srp_lun >> 56) & 0x3f;
+ id = (srp_lun >> 48) & 0xff;
+ srp_lun <<= 16;
+ goto retry;
+ }
+ *lun = (srp_lun >> 48) & 0xff;
+ break;
+
+ case 1:
+ *lun = (srp_lun >> 48) & 0x3fff;
+ break;
+ case 2:
+ channel = (srp_lun >> 53) & 0x7;
+ id = (srp_lun >> 56) & 0x3f;
+ *lun = (srp_lun >> 48) & 0x1f;
+ break;
+ case 3:
+ *lun = -1;
+ return NULL;
+ default:
+ abort();
+ }
+
+ return scsi_device_find(bus, channel, id, *lun);
}
static int vscsi_send_iu(VSCSIState *s, vscsi_req *req,
@@ -582,14 +609,11 @@ static int vscsi_queue_cmd(VSCSIState *s, vscsi_req *req)
{
union srp_iu *srp = &req->iu.srp;
SCSIDevice *sdev;
- int n, id, lun;
+ int n, lun;
- vscsi_decode_id_lun(be64_to_cpu(srp->cmd.lun), &id, &lun);
-
- /* Qemu vs. linux issue with LUNs to be sorted out ... */
- sdev = (id < 8 && lun < 16) ? s->bus.devs[id] : NULL;
+ sdev = vscsi_device_find(&s->bus, be64_to_cpu(srp->cmd.lun), &lun);
if (!sdev) {
- dprintf("VSCSI: Command for id %d with no drive\n", id);
+ dprintf("VSCSI: Command for lun %08" PRIx64 " with no drive\n", be64_to_cpu(srp->cmd.lun));
if (srp->cmd.cdb[0] == INQUIRY) {
vscsi_inquiry_no_target(s, req);
} else {
@@ -862,7 +886,12 @@ static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data)
return 0;
}
-static const struct SCSIBusOps vscsi_scsi_ops = {
+static const struct SCSIBusInfo vscsi_scsi_info = {
+ .tcq = true,
+ .max_channel = 7, /* logical unit addressing format */
+ .max_target = 63,
+ .max_lun = 31,
+
.transfer_data = vscsi_transfer_data,
.complete = vscsi_command_complete,
.cancel = vscsi_request_cancelled
@@ -883,8 +912,7 @@ static int spapr_vscsi_init(VIOsPAPRDevice *dev)
dev->crq.SendFunc = vscsi_do_crq;
- scsi_bus_new(&s->bus, &dev->qdev, 1, VSCSI_REQ_LIMIT,
- &vscsi_scsi_ops);
+ scsi_bus_new(&s->bus, &dev->qdev, &vscsi_scsi_info);
if (!dev->qdev.hotplugged) {
scsi_bus_legacy_handle_cmdline(&s->bus);
}
diff --git a/hw/ssi.c b/hw/ssi.c
index 3f4c5f9f06..9842fe7472 100644
--- a/hw/ssi.c
+++ b/hw/ssi.c
@@ -25,8 +25,8 @@ static int ssi_slave_init(DeviceState *dev, DeviceInfo *base_info)
SSIBus *bus;
bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev));
- if (QLIST_FIRST(&bus->qbus.children) != dev
- || QLIST_NEXT(dev, sibling) != NULL) {
+ if (QTAILQ_FIRST(&bus->qbus.children) != dev
+ || QTAILQ_NEXT(dev, sibling) != NULL) {
hw_error("Too many devices on SSI bus");
}
@@ -61,7 +61,7 @@ uint32_t ssi_transfer(SSIBus *bus, uint32_t val)
{
DeviceState *dev;
SSISlave *slave;
- dev = QLIST_FIRST(&bus->qbus.children);
+ dev = QTAILQ_FIRST(&bus->qbus.children);
if (!dev) {
return 0;
}
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
index 08d2d2ac77..1a0815a136 100644
--- a/hw/usb-msd.c
+++ b/hw/usb-msd.c
@@ -495,7 +495,11 @@ static void usb_msd_password_cb(void *opaque, int err)
qdev_unplug(&s->dev.qdev);
}
-static const struct SCSIBusOps usb_msd_scsi_ops = {
+static const struct SCSIBusInfo usb_msd_scsi_info = {
+ .tcq = false,
+ .max_target = 0,
+ .max_lun = 0,
+
.transfer_data = usb_msd_transfer_data,
.complete = usb_msd_command_complete,
.cancel = usb_msd_request_cancelled
@@ -536,7 +540,7 @@ static int usb_msd_initfn(USBDevice *dev)
}
usb_desc_init(dev);
- scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops);
+ scsi_bus_new(&s->bus, &s->dev.qdev, &usb_msd_scsi_info);
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
if (!s->scsi_dev) {
return -1;
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index 68402cc479..6370600bb3 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -182,6 +182,7 @@ static void versatile_init(ram_addr_t ram_size,
qemu_irq sic[32];
DeviceState *dev, *sysctl;
SysBusDevice *busdev;
+ DeviceState *pl041;
PCIBus *pci_bus;
NICInfo *nd;
int n;
@@ -273,6 +274,13 @@ static void versatile_init(ram_addr_t ram_size,
/* Add PL031 Real Time Clock. */
sysbus_create_simple("pl031", 0x101e8000, pic[10]);
+ /* Add PL041 AACI Interface to the LM4549 codec */
+ pl041 = qdev_create(NULL, "pl041");
+ qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
+ qdev_init_nofail(pl041);
+ sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000);
+ sysbus_connect_irq(sysbus_from_qdev(pl041), 0, sic[24]);
+
/* Memory map for Versatile/PB: */
/* 0x10000000 System registers. */
/* 0x10001000 PCI controller config registers. */
diff --git a/hw/vexpress.c b/hw/vexpress.c
index c9766dd0c4..0940a26d73 100644
--- a/hw/vexpress.c
+++ b/hw/vexpress.c
@@ -41,7 +41,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
{
CPUState *env = NULL;
ram_addr_t ram_offset, vram_offset, sram_offset;
- DeviceState *dev, *sysctl;
+ DeviceState *dev, *sysctl, *pl041;
SysBusDevice *busdev;
qemu_irq *irqp;
qemu_irq pic[64];
@@ -118,6 +118,11 @@ static void vexpress_a9_init(ram_addr_t ram_size,
/* 0x10001000 SP810 system control */
/* 0x10002000 serial bus PCI */
/* 0x10004000 PL041 audio */
+ pl041 = qdev_create(NULL, "pl041");
+ qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
+ qdev_init_nofail(pl041);
+ sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000);
+ sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]);
dev = sysbus_create_varargs("pl181", 0x10005000, pic[9], pic[10], NULL);
/* Wire up MMC card detect and read-only signals */
diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c
index 5f8f4bdb9f..e24a2bf1f3 100644
--- a/hw/virtio-balloon.c
+++ b/hw/virtio-balloon.c
@@ -18,22 +18,14 @@
#include "virtio.h"
#include "pc.h"
#include "cpu.h"
-#include "monitor.h"
#include "balloon.h"
#include "virtio-balloon.h"
#include "kvm.h"
-#include "qlist.h"
-#include "qint.h"
-#include "qstring.h"
#if defined(__linux__)
#include <sys/mman.h>
#endif
-/* Disable guest-provided stats by now (https://bugzilla.redhat.com/show_bug.cgi?id=623903) */
-#define ENABLE_GUEST_STATS 0
-
-
typedef struct VirtIOBalloon
{
VirtIODevice vdev;
@@ -43,8 +35,6 @@ typedef struct VirtIOBalloon
uint64_t stats[VIRTIO_BALLOON_S_NR];
VirtQueueElement stats_vq_elem;
size_t stats_vq_offset;
- MonitorCompletion *stats_callback;
- void *stats_opaque_callback_data;
DeviceState *qdev;
} VirtIOBalloon;
@@ -76,31 +66,6 @@ static inline void reset_stats(VirtIOBalloon *dev)
for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1);
}
-static void stat_put(QDict *dict, const char *label, uint64_t val)
-{
- if (val != -1)
- qdict_put(dict, label, qint_from_int(val));
-}
-
-static QObject *get_stats_qobject(VirtIOBalloon *dev)
-{
- QDict *dict = qdict_new();
- uint64_t actual = ram_size - ((uint64_t) dev->actual <<
- VIRTIO_BALLOON_PFN_SHIFT);
-
- stat_put(dict, "actual", actual);
-#if ENABLE_GUEST_STATS
- stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]);
- stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]);
- stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]);
- stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]);
- stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]);
- stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]);
-#endif
-
- return QOBJECT(dict);
-}
-
static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBalloon *s = to_virtio_balloon(vdev);
@@ -131,20 +96,6 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
}
}
-static void complete_stats_request(VirtIOBalloon *vb)
-{
- QObject *stats;
-
- if (!vb->stats_opaque_callback_data)
- return;
-
- stats = get_stats_qobject(vb);
- vb->stats_callback(vb->stats_opaque_callback_data, stats);
- qobject_decref(stats);
- vb->stats_opaque_callback_data = NULL;
- vb->stats_callback = NULL;
-}
-
static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev);
@@ -172,8 +123,6 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
s->stats[tag] = val;
}
s->stats_vq_offset = offset;
-
- complete_stats_request(s);
}
static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data)
@@ -202,32 +151,33 @@ static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f)
return f;
}
-static void virtio_balloon_stat(void *opaque, MonitorCompletion cb,
- void *cb_data)
+static void virtio_balloon_stat(void *opaque, BalloonInfo *info)
{
VirtIOBalloon *dev = opaque;
- /* For now, only allow one request at a time. This restriction can be
- * removed later by queueing callback and data pairs.
+#if 0
+ /* Disable guest-provided stats for now. For more details please check:
+ * https://bugzilla.redhat.com/show_bug.cgi?id=623903
+ *
+ * If you do enable it (which is probably not going to happen as we
+ * need a new command for it), remember that you also need to fill the
+ * appropriate members of the BalloonInfo structure so that the stats
+ * are returned to the client.
*/
- if (dev->stats_callback != NULL) {
- return;
- }
- dev->stats_callback = cb;
- dev->stats_opaque_callback_data = cb_data;
-
- if (ENABLE_GUEST_STATS
- && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) {
+ if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) {
virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset);
virtio_notify(&dev->vdev, dev->svq);
return;
}
+#endif
/* Stats are not supported. Clear out any stale values that might
* have been set by a more featureful guest kernel.
*/
reset_stats(dev);
- complete_stats_request(dev);
+
+ info->actual = ram_size - ((uint64_t) dev->actual <<
+ VIRTIO_BALLOON_PFN_SHIFT);
}
static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
diff --git a/input.c b/input.c
index e2f7c92a71..9ade63f648 100644
--- a/input.c
+++ b/input.c
@@ -26,7 +26,8 @@
#include "net.h"
#include "monitor.h"
#include "console.h"
-#include "qjson.h"
+#include "error.h"
+#include "qmp-commands.h"
static QEMUPutKBDEvent *qemu_put_kbd_event;
static void *qemu_put_kbd_event_opaque;
@@ -211,60 +212,27 @@ int kbd_mouse_has_absolute(void)
return 0;
}
-static void info_mice_iter(QObject *data, void *opaque)
-{
- QDict *mouse;
- Monitor *mon = opaque;
-
- mouse = qobject_to_qdict(data);
- monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n",
- (qdict_get_bool(mouse, "current") ? '*' : ' '),
- qdict_get_int(mouse, "index"), qdict_get_str(mouse, "name"),
- qdict_get_bool(mouse, "absolute") ? " (absolute)" : "");
-}
-
-void do_info_mice_print(Monitor *mon, const QObject *data)
-{
- QList *mice_list;
-
- mice_list = qobject_to_qlist(data);
- if (qlist_empty(mice_list)) {
- monitor_printf(mon, "No mouse devices connected\n");
- return;
- }
-
- qlist_iter(mice_list, info_mice_iter, mon);
-}
-
-void do_info_mice(Monitor *mon, QObject **ret_data)
+MouseInfoList *qmp_query_mice(Error **errp)
{
+ MouseInfoList *mice_list = NULL;
QEMUPutMouseEntry *cursor;
- QList *mice_list;
- int current;
-
- mice_list = qlist_new();
+ bool current = true;
- if (QTAILQ_EMPTY(&mouse_handlers)) {
- goto out;
- }
+ QTAILQ_FOREACH(cursor, &mouse_handlers, node) {
+ MouseInfoList *info = g_malloc0(sizeof(*info));
+ info->value = g_malloc0(sizeof(*info->value));
+ info->value->name = g_strdup(cursor->qemu_put_mouse_event_name);
+ info->value->index = cursor->index;
+ info->value->absolute = !!cursor->qemu_put_mouse_event_absolute;
+ info->value->current = current;
- current = QTAILQ_FIRST(&mouse_handlers)->index;
+ current = false;
- QTAILQ_FOREACH(cursor, &mouse_handlers, node) {
- QObject *obj;
- obj = qobject_from_jsonf("{ 'name': %s,"
- " 'index': %d,"
- " 'current': %i,"
- " 'absolute': %i }",
- cursor->qemu_put_mouse_event_name,
- cursor->index,
- cursor->index == current,
- !!cursor->qemu_put_mouse_event_absolute);
- qlist_append_obj(mice_list, obj);
+ info->next = mice_list;
+ mice_list = info;
}
-out:
- *ret_data = QOBJECT(mice_list);
+ return mice_list;
}
void do_mouse_set(Monitor *mon, const QDict *qdict)
diff --git a/kvm-all.c b/kvm-all.c
index e7faf5cba9..c09ddf7ac5 100644
--- a/kvm-all.c
+++ b/kvm-all.c
@@ -64,6 +64,7 @@ struct KVMState
int vmfd;
int coalesced_mmio;
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
+ bool coalesced_flush_in_progress;
int broken_set_mem_region;
int migration_log;
int vcpu_events;
@@ -876,6 +877,13 @@ static int kvm_handle_internal_error(CPUState *env, struct kvm_run *run)
void kvm_flush_coalesced_mmio_buffer(void)
{
KVMState *s = kvm_state;
+
+ if (s->coalesced_flush_in_progress) {
+ return;
+ }
+
+ s->coalesced_flush_in_progress = true;
+
if (s->coalesced_mmio_ring) {
struct kvm_coalesced_mmio_ring *ring = s->coalesced_mmio_ring;
while (ring->first != ring->last) {
@@ -888,6 +896,8 @@ void kvm_flush_coalesced_mmio_buffer(void)
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
}
}
+
+ s->coalesced_flush_in_progress = false;
}
static void do_kvm_cpu_synchronize_state(void *_env)
diff --git a/libcacard/cac.c b/libcacard/cac.c
index f4b0b1b057..927a4ca296 100644
--- a/libcacard/cac.c
+++ b/libcacard/cac.c
@@ -266,7 +266,8 @@ static void
cac_delete_pki_applet_private(VCardAppletPrivate *applet_private)
{
CACPKIAppletData *pki_applet_data = NULL;
- if (pki_applet_data == NULL) {
+
+ if (applet_private == NULL) {
return;
}
pki_applet_data = &(applet_private->u.pki_data);
diff --git a/libcacard/card_7816.c b/libcacard/card_7816.c
index 9fd59d4a5f..6fe27d5631 100644
--- a/libcacard/card_7816.c
+++ b/libcacard/card_7816.c
@@ -754,7 +754,7 @@ vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
return vcard7816_vm_process_apdu(card, apdu, response);
case VCARD_DIRECT:
/* if we are type direct, then the applet should handle everything */
- assert("VCARD_DIRECT: applet failure");
+ assert(!"VCARD_DIRECT: applet failure");
break;
}
*response =
diff --git a/libcacard/vscclient.c b/libcacard/vscclient.c
index 2191f6038c..e317a25faf 100644
--- a/libcacard/vscclient.c
+++ b/libcacard/vscclient.c
@@ -357,6 +357,7 @@ connect_to_qemu(
if (sock < 0) {
/* Error */
fprintf(stderr, "Error opening socket!\n");
+ return -1;
}
memset(&hints, 0, sizeof(struct addrinfo));
@@ -370,13 +371,13 @@ connect_to_qemu(
if (ret != 0) {
/* Error */
fprintf(stderr, "getaddrinfo failed\n");
- return 5;
+ return -1;
}
if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) {
/* Error */
fprintf(stderr, "Could not connect\n");
- return 5;
+ return -1;
}
if (verbose) {
printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader));
@@ -505,6 +506,10 @@ main(
qemu_host = strdup(argv[argc - 2]);
qemu_port = strdup(argv[argc - 1]);
sock = connect_to_qemu(qemu_host, qemu_port);
+ if (sock == -1) {
+ fprintf(stderr, "error opening socket, exiting.\n");
+ exit(5);
+ }
qemu_mutex_init(&write_lock);
qemu_mutex_init(&pending_reader_lock);
diff --git a/linux-user/signal.c b/linux-user/signal.c
index e4addcdd53..78e3380702 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -2296,12 +2296,14 @@ void sparc64_set_context(CPUSPARCState *env)
*/
err |= __get_user(env->fprs, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fprs));
{
- uint32_t *src, *dst;
- src = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs;
- dst = env->fpr;
- /* XXX: check that the CPU storage is the same as user context */
- for (i = 0; i < 64; i++, dst++, src++)
- err |= __get_user(*dst, src);
+ uint32_t *src = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs;
+ for (i = 0; i < 64; i++, src++) {
+ if (i & 1) {
+ err |= __get_user(env->fpr[i/2].l.lower, src);
+ } else {
+ err |= __get_user(env->fpr[i/2].l.upper, src);
+ }
+ }
}
err |= __get_user(env->fsr,
&(ucp->tuc_mcontext.mc_fpregs.mcfpu_fsr));
@@ -2390,12 +2392,14 @@ void sparc64_get_context(CPUSPARCState *env)
err |= __put_user(i7, &(mcp->mc_i7));
{
- uint32_t *src, *dst;
- src = env->fpr;
- dst = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs;
- /* XXX: check that the CPU storage is the same as user context */
- for (i = 0; i < 64; i++, dst++, src++)
- err |= __put_user(*src, dst);
+ uint32_t *dst = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs;
+ for (i = 0; i < 64; i++, dst++) {
+ if (i & 1) {
+ err |= __put_user(env->fpr[i/2].l.lower, dst);
+ } else {
+ err |= __put_user(env->fpr[i/2].l.upper, dst);
+ }
+ }
}
err |= __put_user(env->fsr, &(mcp->mc_fpregs.mcfpu_fsr));
err |= __put_user(env->gsr, &(mcp->mc_fpregs.mcfpu_gsr));
diff --git a/migration.c b/migration.c
index bdca72e008..3b4abbde64 100644
--- a/migration.c
+++ b/migration.c
@@ -19,7 +19,7 @@
#include "block.h"
#include "qemu_socket.h"
#include "block-migration.h"
-#include "qemu-objects.h"
+#include "qmp-commands.h"
//#define DEBUG_MIGRATION
@@ -107,53 +107,9 @@ uint64_t migrate_max_downtime(void)
return max_downtime;
}
-static void migrate_print_status(Monitor *mon, const char *name,
- const QDict *status_dict)
+MigrationInfo *qmp_query_migrate(Error **errp)
{
- QDict *qdict;
-
- qdict = qobject_to_qdict(qdict_get(status_dict, name));
-
- monitor_printf(mon, "transferred %s: %" PRIu64 " kbytes\n", name,
- qdict_get_int(qdict, "transferred") >> 10);
- monitor_printf(mon, "remaining %s: %" PRIu64 " kbytes\n", name,
- qdict_get_int(qdict, "remaining") >> 10);
- monitor_printf(mon, "total %s: %" PRIu64 " kbytes\n", name,
- qdict_get_int(qdict, "total") >> 10);
-}
-
-void do_info_migrate_print(Monitor *mon, const QObject *data)
-{
- QDict *qdict;
-
- qdict = qobject_to_qdict(data);
-
- monitor_printf(mon, "Migration status: %s\n",
- qdict_get_str(qdict, "status"));
-
- if (qdict_haskey(qdict, "ram")) {
- migrate_print_status(mon, "ram", qdict);
- }
-
- if (qdict_haskey(qdict, "disk")) {
- migrate_print_status(mon, "disk", qdict);
- }
-}
-
-static void migrate_put_status(QDict *qdict, const char *name,
- uint64_t trans, uint64_t rem, uint64_t total)
-{
- QObject *obj;
-
- obj = qobject_from_jsonf("{ 'transferred': %" PRId64 ", "
- "'remaining': %" PRId64 ", "
- "'total': %" PRId64 " }", trans, rem, total);
- qdict_put_obj(qdict, name, obj);
-}
-
-void do_info_migrate(Monitor *mon, QObject **ret_data)
-{
- QDict *qdict;
+ MigrationInfo *info = g_malloc0(sizeof(*info));
MigrationState *s = migrate_get_current();
switch (s->state) {
@@ -161,30 +117,38 @@ void do_info_migrate(Monitor *mon, QObject **ret_data)
/* no migration has happened ever */
break;
case MIG_STATE_ACTIVE:
- qdict = qdict_new();
- qdict_put(qdict, "status", qstring_from_str("active"));
+ info->has_status = true;
+ info->status = g_strdup("active");
- migrate_put_status(qdict, "ram", ram_bytes_transferred(),
- ram_bytes_remaining(), ram_bytes_total());
+ info->has_ram = true;
+ info->ram = g_malloc0(sizeof(*info->ram));
+ info->ram->transferred = ram_bytes_transferred();
+ info->ram->remaining = ram_bytes_remaining();
+ info->ram->total = ram_bytes_total();
if (blk_mig_active()) {
- migrate_put_status(qdict, "disk", blk_mig_bytes_transferred(),
- blk_mig_bytes_remaining(),
- blk_mig_bytes_total());
+ info->has_disk = true;
+ info->disk = g_malloc0(sizeof(*info->disk));
+ info->disk->transferred = blk_mig_bytes_transferred();
+ info->disk->remaining = blk_mig_bytes_remaining();
+ info->disk->total = blk_mig_bytes_total();
}
-
- *ret_data = QOBJECT(qdict);
break;
case MIG_STATE_COMPLETED:
- *ret_data = qobject_from_jsonf("{ 'status': 'completed' }");
+ info->has_status = true;
+ info->status = g_strdup("completed");
break;
case MIG_STATE_ERROR:
- *ret_data = qobject_from_jsonf("{ 'status': 'failed' }");
+ info->has_status = true;
+ info->status = g_strdup("failed");
break;
case MIG_STATE_CANCELLED:
- *ret_data = qobject_from_jsonf("{ 'status': 'cancelled' }");
+ info->has_status = true;
+ info->status = g_strdup("cancelled");
break;
}
+
+ return info;
}
/* shared migration helpers */
@@ -372,11 +336,22 @@ void remove_migration_state_change_notifier(Notifier *notify)
notifier_list_remove(&migration_state_notifiers, notify);
}
+bool migration_is_active(MigrationState *s)
+{
+ return s->state == MIG_STATE_ACTIVE;
+}
+
bool migration_has_finished(MigrationState *s)
{
return s->state == MIG_STATE_COMPLETED;
}
+bool migration_has_failed(MigrationState *s)
+{
+ return (s->state == MIG_STATE_CANCELLED ||
+ s->state == MIG_STATE_ERROR);
+}
+
void migrate_fd_connect(MigrationState *s)
{
int ret;
diff --git a/migration.h b/migration.h
index a1f80d0728..1b8ee58530 100644
--- a/migration.h
+++ b/migration.h
@@ -76,7 +76,9 @@ void migrate_fd_connect(MigrationState *s);
void add_migration_state_change_notifier(Notifier *notify);
void remove_migration_state_change_notifier(Notifier *notify);
+bool migration_is_active(MigrationState *);
bool migration_has_finished(MigrationState *);
+bool migration_has_failed(MigrationState *);
uint64_t ram_bytes_remaining(void);
uint64_t ram_bytes_transferred(void);
diff --git a/monitor.c b/monitor.c
index ffda0feb84..7c2a9b95f8 100644
--- a/monitor.c
+++ b/monitor.c
@@ -123,8 +123,6 @@ typedef struct mon_cmd_t {
void (*user_print)(Monitor *mon, const QObject *data);
union {
void (*info)(Monitor *mon);
- void (*info_new)(Monitor *mon, QObject **ret_data);
- int (*info_async)(Monitor *mon, MonitorCompletion *cb, void *opaque);
void (*cmd)(Monitor *mon, const QDict *qdict);
int (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data);
int (*cmd_async)(Monitor *mon, const QDict *params,
@@ -205,7 +203,6 @@ static const mon_cmd_t mon_cmds[];
static const mon_cmd_t info_cmds[];
static const mon_cmd_t qmp_cmds[];
-static const mon_cmd_t qmp_query_cmds[];
Monitor *cur_mon;
Monitor *default_mon;
@@ -514,7 +511,6 @@ static int do_qmp_capabilities(Monitor *mon, const QDict *params,
return 0;
}
-static int mon_set_cpu(int cpu_index);
static void handle_user_command(Monitor *mon, const char *cmdline);
static int do_hmp_passthrough(Monitor *mon, const QDict *params,
@@ -532,7 +528,7 @@ static int do_hmp_passthrough(Monitor *mon, const QDict *params,
cur_mon = &hmp;
if (qdict_haskey(params, "cpu-index")) {
- ret = mon_set_cpu(qdict_get_int(params, "cpu-index"));
+ ret = monitor_set_cpu(qdict_get_int(params, "cpu-index"));
if (ret < 0) {
cur_mon = old_mon;
qerror_report(QERR_INVALID_PARAMETER_VALUE, "cpu-index", "a CPU number");
@@ -664,11 +660,6 @@ static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon);
}
-static void qmp_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
-{
- cmd->mhandler.info_async(mon, qmp_monitor_complete, mon);
-}
-
static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
const QDict *params)
{
@@ -686,21 +677,6 @@ static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
}
}
-static void user_async_info_handler(Monitor *mon, const mon_cmd_t *cmd)
-{
- int ret;
-
- MonitorCompletionData *cb_data = g_malloc(sizeof(*cb_data));
- cb_data->mon = mon;
- cb_data->user_print = cmd->user_print;
- monitor_suspend(mon);
- ret = cmd->mhandler.info_async(mon, user_monitor_complete, cb_data);
- if (ret < 0) {
- monitor_resume(mon);
- g_free(cb_data);
- }
-}
-
static void do_info(Monitor *mon, const QDict *qdict)
{
const mon_cmd_t *cmd;
@@ -719,52 +695,23 @@ static void do_info(Monitor *mon, const QDict *qdict)
goto help;
}
- if (handler_is_async(cmd)) {
- user_async_info_handler(mon, cmd);
- } else if (handler_is_qobject(cmd)) {
- QObject *info_data = NULL;
-
- cmd->mhandler.info_new(mon, &info_data);
- if (info_data) {
- cmd->user_print(mon, info_data);
- qobject_decref(info_data);
- }
- } else {
- cmd->mhandler.info(mon);
- }
-
+ cmd->mhandler.info(mon);
return;
help:
help_cmd(mon, "info");
}
-static CommandInfoList *alloc_cmd_entry(const char *cmd_name)
-{
- CommandInfoList *info;
-
- info = g_malloc0(sizeof(*info));
- info->value = g_malloc0(sizeof(*info->value));
- info->value->name = g_strdup(cmd_name);
-
- return info;
-}
-
CommandInfoList *qmp_query_commands(Error **errp)
{
CommandInfoList *info, *cmd_list = NULL;
const mon_cmd_t *cmd;
for (cmd = qmp_cmds; cmd->name != NULL; cmd++) {
- info = alloc_cmd_entry(cmd->name);
- info->next = cmd_list;
- cmd_list = info;
- }
+ info = g_malloc0(sizeof(*info));
+ info->value = g_malloc0(sizeof(*info->value));
+ info->value->name = g_strdup(cmd->name);
- for (cmd = qmp_query_cmds; cmd->name != NULL; cmd++) {
- char buf[128];
- snprintf(buf, sizeof(buf), "query-%s", cmd->name);
- info = alloc_cmd_entry(buf);
info->next = cmd_list;
cmd_list = info;
}
@@ -772,8 +719,8 @@ CommandInfoList *qmp_query_commands(Error **errp)
return cmd_list;
}
-/* get the current CPU defined by the user */
-static int mon_set_cpu(int cpu_index)
+/* set the current CPU defined by the user */
+int monitor_set_cpu(int cpu_index)
{
CPUState *env;
@@ -789,12 +736,17 @@ static int mon_set_cpu(int cpu_index)
static CPUState *mon_get_cpu(void)
{
if (!cur_mon->mon_cpu) {
- mon_set_cpu(0);
+ monitor_set_cpu(0);
}
cpu_synchronize_state(cur_mon->mon_cpu);
return cur_mon->mon_cpu;
}
+int monitor_get_cpu_index(void)
+{
+ return mon_get_cpu()->cpu_index;
+}
+
static void do_info_registers(Monitor *mon)
{
CPUState *env;
@@ -808,107 +760,6 @@ static void do_info_registers(Monitor *mon)
#endif
}
-static void print_cpu_iter(QObject *obj, void *opaque)
-{
- QDict *cpu;
- int active = ' ';
- Monitor *mon = opaque;
-
- assert(qobject_type(obj) == QTYPE_QDICT);
- cpu = qobject_to_qdict(obj);
-
- if (qdict_get_bool(cpu, "current")) {
- active = '*';
- }
-
- monitor_printf(mon, "%c CPU #%d: ", active, (int)qdict_get_int(cpu, "CPU"));
-
-#if defined(TARGET_I386)
- monitor_printf(mon, "pc=0x" TARGET_FMT_lx,
- (target_ulong) qdict_get_int(cpu, "pc"));
-#elif defined(TARGET_PPC)
- monitor_printf(mon, "nip=0x" TARGET_FMT_lx,
- (target_long) qdict_get_int(cpu, "nip"));
-#elif defined(TARGET_SPARC)
- monitor_printf(mon, "pc=0x" TARGET_FMT_lx,
- (target_long) qdict_get_int(cpu, "pc"));
- monitor_printf(mon, "npc=0x" TARGET_FMT_lx,
- (target_long) qdict_get_int(cpu, "npc"));
-#elif defined(TARGET_MIPS)
- monitor_printf(mon, "PC=0x" TARGET_FMT_lx,
- (target_long) qdict_get_int(cpu, "PC"));
-#endif
-
- if (qdict_get_bool(cpu, "halted")) {
- monitor_printf(mon, " (halted)");
- }
-
- monitor_printf(mon, " thread_id=%" PRId64 " ",
- qdict_get_int(cpu, "thread_id"));
-
- monitor_printf(mon, "\n");
-}
-
-static void monitor_print_cpus(Monitor *mon, const QObject *data)
-{
- QList *cpu_list;
-
- assert(qobject_type(data) == QTYPE_QLIST);
- cpu_list = qobject_to_qlist(data);
- qlist_iter(cpu_list, print_cpu_iter, mon);
-}
-
-static void do_info_cpus(Monitor *mon, QObject **ret_data)
-{
- CPUState *env;
- QList *cpu_list;
-
- cpu_list = qlist_new();
-
- /* just to set the default cpu if not already done */
- mon_get_cpu();
-
- for(env = first_cpu; env != NULL; env = env->next_cpu) {
- QDict *cpu;
- QObject *obj;
-
- cpu_synchronize_state(env);
-
- obj = qobject_from_jsonf("{ 'CPU': %d, 'current': %i, 'halted': %i }",
- env->cpu_index, env == mon->mon_cpu,
- env->halted);
-
- cpu = qobject_to_qdict(obj);
-
-#if defined(TARGET_I386)
- qdict_put(cpu, "pc", qint_from_int(env->eip + env->segs[R_CS].base));
-#elif defined(TARGET_PPC)
- qdict_put(cpu, "nip", qint_from_int(env->nip));
-#elif defined(TARGET_SPARC)
- qdict_put(cpu, "pc", qint_from_int(env->pc));
- qdict_put(cpu, "npc", qint_from_int(env->npc));
-#elif defined(TARGET_MIPS)
- qdict_put(cpu, "PC", qint_from_int(env->active_tc.PC));
-#endif
- qdict_put(cpu, "thread_id", qint_from_int(env->thread_id));
-
- qlist_append(cpu_list, cpu);
- }
-
- *ret_data = QOBJECT(cpu_list);
-}
-
-static int do_cpu_set(Monitor *mon, const QDict *qdict, QObject **ret_data)
-{
- int index = qdict_get_int(qdict, "index");
- if (mon_set_cpu(index) < 0) {
- qerror_report(QERR_INVALID_PARAMETER_VALUE, "index",
- "a CPU number");
- return -1;
- }
- return 0;
-}
-
static void do_info_jit(Monitor *mon)
{
dump_exec_info((FILE *)mon, monitor_fprintf);
@@ -1153,7 +1004,8 @@ static int add_graphics_client(Monitor *mon, const QDict *qdict, QObject **ret_d
return -1;
}
-static int client_migrate_info(Monitor *mon, const QDict *qdict, QObject **ret_data)
+static int client_migrate_info(Monitor *mon, const QDict *qdict,
+ MonitorCompletion cb, void *opaque)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *hostname = qdict_get_str(qdict, "hostname");
@@ -1168,7 +1020,8 @@ static int client_migrate_info(Monitor *mon, const QDict *qdict, QObject **ret_d
return -1;
}
- ret = qemu_spice_migrate_info(hostname, port, tls_port, subject);
+ ret = qemu_spice_migrate_info(hostname, port, tls_port, subject,
+ cb, opaque);
if (ret != 0) {
qerror_report(QERR_UNDEFINED_ERROR);
return -1;
@@ -2771,16 +2624,14 @@ static const mon_cmd_t info_cmds[] = {
.args_type = "",
.params = "",
.help = "show the block devices",
- .user_print = bdrv_info_print,
- .mhandler.info_new = bdrv_info,
+ .mhandler.info = hmp_info_block,
},
{
.name = "blockstats",
.args_type = "",
.params = "",
.help = "show block device statistics",
- .user_print = bdrv_stats_print,
- .mhandler.info_new = bdrv_info_stats,
+ .mhandler.info = hmp_info_blockstats,
},
{
.name = "registers",
@@ -2794,8 +2645,7 @@ static const mon_cmd_t info_cmds[] = {
.args_type = "",
.params = "",
.help = "show infos for each CPU",
- .user_print = monitor_print_cpus,
- .mhandler.info_new = do_info_cpus,
+ .mhandler.info = hmp_info_cpus,
},
{
.name = "history",
@@ -2838,8 +2688,7 @@ static const mon_cmd_t info_cmds[] = {
.args_type = "",
.params = "",
.help = "show PCI info",
- .user_print = do_pci_info_print,
- .mhandler.info_new = do_pci_info,
+ .mhandler.info = hmp_info_pci,
},
#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) || \
defined(TARGET_PPC)
@@ -2942,16 +2791,14 @@ static const mon_cmd_t info_cmds[] = {
.args_type = "",
.params = "",
.help = "show which guest mouse is receiving events",
- .user_print = do_info_mice_print,
- .mhandler.info_new = do_info_mice,
+ .mhandler.info = hmp_info_mice,
},
{
.name = "vnc",
.args_type = "",
.params = "",
.help = "show the vnc server status",
- .user_print = do_info_vnc_print,
- .mhandler.info_new = do_info_vnc,
+ .mhandler.info = hmp_info_vnc,
},
#if defined(CONFIG_SPICE)
{
@@ -2959,8 +2806,7 @@ static const mon_cmd_t info_cmds[] = {
.args_type = "",
.params = "",
.help = "show the spice server status",
- .user_print = do_info_spice_print,
- .mhandler.info_new = do_info_spice,
+ .mhandler.info = hmp_info_spice,
},
#endif
{
@@ -3000,17 +2846,14 @@ static const mon_cmd_t info_cmds[] = {
.args_type = "",
.params = "",
.help = "show migration status",
- .user_print = do_info_migrate_print,
- .mhandler.info_new = do_info_migrate,
+ .mhandler.info = hmp_info_migrate,
},
{
.name = "balloon",
.args_type = "",
.params = "",
.help = "show balloon information",
- .user_print = monitor_print_balloon,
- .mhandler.info_async = do_info_balloon,
- .flags = MONITOR_CMD_ASYNC,
+ .mhandler.info = hmp_info_balloon,
},
{
.name = "qtree",
@@ -3059,85 +2902,6 @@ static const mon_cmd_t qmp_cmds[] = {
{ /* NULL */ },
};
-static const mon_cmd_t qmp_query_cmds[] = {
- {
- .name = "block",
- .args_type = "",
- .params = "",
- .help = "show the block devices",
- .user_print = bdrv_info_print,
- .mhandler.info_new = bdrv_info,
- },
- {
- .name = "blockstats",
- .args_type = "",
- .params = "",
- .help = "show block device statistics",
- .user_print = bdrv_stats_print,
- .mhandler.info_new = bdrv_info_stats,
- },
- {
- .name = "cpus",
- .args_type = "",
- .params = "",
- .help = "show infos for each CPU",
- .user_print = monitor_print_cpus,
- .mhandler.info_new = do_info_cpus,
- },
- {
- .name = "pci",
- .args_type = "",
- .params = "",
- .help = "show PCI info",
- .user_print = do_pci_info_print,
- .mhandler.info_new = do_pci_info,
- },
- {
- .name = "mice",
- .args_type = "",
- .params = "",
- .help = "show which guest mouse is receiving events",
- .user_print = do_info_mice_print,
- .mhandler.info_new = do_info_mice,
- },
- {
- .name = "vnc",
- .args_type = "",
- .params = "",
- .help = "show the vnc server status",
- .user_print = do_info_vnc_print,
- .mhandler.info_new = do_info_vnc,
- },
-#if defined(CONFIG_SPICE)
- {
- .name = "spice",
- .args_type = "",
- .params = "",
- .help = "show the spice server status",
- .user_print = do_info_spice_print,
- .mhandler.info_new = do_info_spice,
- },
-#endif
- {
- .name = "migrate",
- .args_type = "",
- .params = "",
- .help = "show migration status",
- .user_print = do_info_migrate_print,
- .mhandler.info_new = do_info_migrate,
- },
- {
- .name = "balloon",
- .args_type = "",
- .params = "",
- .help = "show balloon information",
- .user_print = monitor_print_balloon,
- .mhandler.info_async = do_info_balloon,
- .flags = MONITOR_CMD_ASYNC,
- },
- { /* NULL */ },
-};
-
/*******************************************************************/
static const char *pch;
@@ -3471,55 +3235,55 @@ static const MonitorDef monitor_defs[] = {
#endif
{ "tbr", offsetof(CPUState, tbr) },
{ "fsr", offsetof(CPUState, fsr) },
- { "f0", offsetof(CPUState, fpr[0]) },
- { "f1", offsetof(CPUState, fpr[1]) },
- { "f2", offsetof(CPUState, fpr[2]) },
- { "f3", offsetof(CPUState, fpr[3]) },
- { "f4", offsetof(CPUState, fpr[4]) },
- { "f5", offsetof(CPUState, fpr[5]) },
- { "f6", offsetof(CPUState, fpr[6]) },
- { "f7", offsetof(CPUState, fpr[7]) },
- { "f8", offsetof(CPUState, fpr[8]) },
- { "f9", offsetof(CPUState, fpr[9]) },
- { "f10", offsetof(CPUState, fpr[10]) },
- { "f11", offsetof(CPUState, fpr[11]) },
- { "f12", offsetof(CPUState, fpr[12]) },
- { "f13", offsetof(CPUState, fpr[13]) },
- { "f14", offsetof(CPUState, fpr[14]) },
- { "f15", offsetof(CPUState, fpr[15]) },
- { "f16", offsetof(CPUState, fpr[16]) },
- { "f17", offsetof(CPUState, fpr[17]) },
- { "f18", offsetof(CPUState, fpr[18]) },
- { "f19", offsetof(CPUState, fpr[19]) },
- { "f20", offsetof(CPUState, fpr[20]) },
- { "f21", offsetof(CPUState, fpr[21]) },
- { "f22", offsetof(CPUState, fpr[22]) },
- { "f23", offsetof(CPUState, fpr[23]) },
- { "f24", offsetof(CPUState, fpr[24]) },
- { "f25", offsetof(CPUState, fpr[25]) },
- { "f26", offsetof(CPUState, fpr[26]) },
- { "f27", offsetof(CPUState, fpr[27]) },
- { "f28", offsetof(CPUState, fpr[28]) },
- { "f29", offsetof(CPUState, fpr[29]) },
- { "f30", offsetof(CPUState, fpr[30]) },
- { "f31", offsetof(CPUState, fpr[31]) },
+ { "f0", offsetof(CPUState, fpr[0].l.upper) },
+ { "f1", offsetof(CPUState, fpr[0].l.lower) },
+ { "f2", offsetof(CPUState, fpr[1].l.upper) },
+ { "f3", offsetof(CPUState, fpr[1].l.lower) },
+ { "f4", offsetof(CPUState, fpr[2].l.upper) },
+ { "f5", offsetof(CPUState, fpr[2].l.lower) },
+ { "f6", offsetof(CPUState, fpr[3].l.upper) },
+ { "f7", offsetof(CPUState, fpr[3].l.lower) },
+ { "f8", offsetof(CPUState, fpr[4].l.upper) },
+ { "f9", offsetof(CPUState, fpr[4].l.lower) },
+ { "f10", offsetof(CPUState, fpr[5].l.upper) },
+ { "f11", offsetof(CPUState, fpr[5].l.lower) },
+ { "f12", offsetof(CPUState, fpr[6].l.upper) },
+ { "f13", offsetof(CPUState, fpr[6].l.lower) },
+ { "f14", offsetof(CPUState, fpr[7].l.upper) },
+ { "f15", offsetof(CPUState, fpr[7].l.lower) },
+ { "f16", offsetof(CPUState, fpr[8].l.upper) },
+ { "f17", offsetof(CPUState, fpr[8].l.lower) },
+ { "f18", offsetof(CPUState, fpr[9].l.upper) },
+ { "f19", offsetof(CPUState, fpr[9].l.lower) },
+ { "f20", offsetof(CPUState, fpr[10].l.upper) },
+ { "f21", offsetof(CPUState, fpr[10].l.lower) },
+ { "f22", offsetof(CPUState, fpr[11].l.upper) },
+ { "f23", offsetof(CPUState, fpr[11].l.lower) },
+ { "f24", offsetof(CPUState, fpr[12].l.upper) },
+ { "f25", offsetof(CPUState, fpr[12].l.lower) },
+ { "f26", offsetof(CPUState, fpr[13].l.upper) },
+ { "f27", offsetof(CPUState, fpr[13].l.lower) },
+ { "f28", offsetof(CPUState, fpr[14].l.upper) },
+ { "f29", offsetof(CPUState, fpr[14].l.lower) },
+ { "f30", offsetof(CPUState, fpr[15].l.upper) },
+ { "f31", offsetof(CPUState, fpr[15].l.lower) },
#ifdef TARGET_SPARC64
- { "f32", offsetof(CPUState, fpr[32]) },
- { "f34", offsetof(CPUState, fpr[34]) },
- { "f36", offsetof(CPUState, fpr[36]) },
- { "f38", offsetof(CPUState, fpr[38]) },
- { "f40", offsetof(CPUState, fpr[40]) },
- { "f42", offsetof(CPUState, fpr[42]) },
- { "f44", offsetof(CPUState, fpr[44]) },
- { "f46", offsetof(CPUState, fpr[46]) },
- { "f48", offsetof(CPUState, fpr[48]) },
- { "f50", offsetof(CPUState, fpr[50]) },
- { "f52", offsetof(CPUState, fpr[52]) },
- { "f54", offsetof(CPUState, fpr[54]) },
- { "f56", offsetof(CPUState, fpr[56]) },
- { "f58", offsetof(CPUState, fpr[58]) },
- { "f60", offsetof(CPUState, fpr[60]) },
- { "f62", offsetof(CPUState, fpr[62]) },
+ { "f32", offsetof(CPUState, fpr[16]) },
+ { "f34", offsetof(CPUState, fpr[17]) },
+ { "f36", offsetof(CPUState, fpr[18]) },
+ { "f38", offsetof(CPUState, fpr[19]) },
+ { "f40", offsetof(CPUState, fpr[20]) },
+ { "f42", offsetof(CPUState, fpr[21]) },
+ { "f44", offsetof(CPUState, fpr[22]) },
+ { "f46", offsetof(CPUState, fpr[23]) },
+ { "f48", offsetof(CPUState, fpr[24]) },
+ { "f50", offsetof(CPUState, fpr[25]) },
+ { "f52", offsetof(CPUState, fpr[26]) },
+ { "f54", offsetof(CPUState, fpr[27]) },
+ { "f56", offsetof(CPUState, fpr[28]) },
+ { "f58", offsetof(CPUState, fpr[29]) },
+ { "f60", offsetof(CPUState, fpr[30]) },
+ { "f62", offsetof(CPUState, fpr[31]) },
{ "asi", offsetof(CPUState, asi) },
{ "pstate", offsetof(CPUState, pstate) },
{ "cansave", offsetof(CPUState, cansave) },
@@ -3932,11 +3696,6 @@ static const mon_cmd_t *monitor_find_command(const char *cmdname)
return search_dispatch_table(mon_cmds, cmdname);
}
-static const mon_cmd_t *qmp_find_query_cmd(const char *info_item)
-{
- return search_dispatch_table(qmp_query_cmds, info_item);
-}
-
static const mon_cmd_t *qmp_find_cmd(const char *cmdname)
{
return search_dispatch_table(qmp_cmds, cmdname);
@@ -4860,22 +4619,6 @@ static QDict *qmp_check_input_obj(QObject *input_obj)
return input_dict;
}
-static void qmp_call_query_cmd(Monitor *mon, const mon_cmd_t *cmd)
-{
- QObject *ret_data = NULL;
-
- if (handler_is_async(cmd)) {
- qmp_async_info_handler(mon, cmd);
- if (monitor_has_error(mon)) {
- monitor_protocol_emitter(mon, NULL);
- }
- } else {
- cmd->mhandler.info_new(mon, &ret_data);
- monitor_protocol_emitter(mon, ret_data);
- qobject_decref(ret_data);
- }
-}
-
static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd,
const QDict *params)
{
@@ -4896,10 +4639,9 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
QObject *obj;
QDict *input, *args;
const mon_cmd_t *cmd;
+ const char *cmd_name;
Monitor *mon = cur_mon;
- const char *cmd_name, *query_cmd;
- query_cmd = NULL;
args = input = NULL;
obj = json_parser_parse(tokens, NULL);
@@ -4926,9 +4668,6 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
}
cmd = qmp_find_cmd(cmd_name);
- if (!cmd && strstart(cmd_name, "query-", &query_cmd)) {
- cmd = qmp_find_query_cmd(query_cmd);
- }
if (!cmd) {
qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name);
goto err_out;
@@ -4947,9 +4686,7 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
goto err_out;
}
- if (query_cmd) {
- qmp_call_query_cmd(mon, cmd);
- } else if (handler_is_async(cmd)) {
+ if (handler_is_async(cmd)) {
err = qmp_async_cmd_handler(mon, cmd, args);
if (err) {
/* emit the error response */
diff --git a/monitor.h b/monitor.h
index 4f2d328db5..e76795f1f3 100644
--- a/monitor.h
+++ b/monitor.h
@@ -57,6 +57,8 @@ void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
void monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void monitor_print_filename(Monitor *mon, const char *filename);
void monitor_flush(Monitor *mon);
+int monitor_set_cpu(int cpu_index);
+int monitor_get_cpu_index(void);
typedef void (MonitorCompletion)(void *opaque, QObject *ret_data);
diff --git a/qapi-schema.json b/qapi-schema.json
index 5922c4a920..cb1ba776df 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -226,6 +226,611 @@
{ 'command': 'query-commands', 'returns': ['CommandInfo'] }
##
+# @MigrationStats
+#
+# Detailed migration status.
+#
+# @transferred: amount of bytes already transferred to the target VM
+#
+# @remaining: amount of bytes remaining to be transferred to the target VM
+#
+# @total: total amount of bytes involved in the migration process
+#
+# Since: 0.14.0.
+##
+{ 'type': 'MigrationStats',
+ 'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' } }
+
+##
+# @MigrationInfo
+#
+# Information about current migration process.
+#
+# @status: #optional string describing the current migration status.
+# As of 0.14.0 this can be 'active', 'completed', 'failed' or
+# 'cancelled'. If this field is not returned, no migration process
+# has been initiated
+#
+# @ram: #optional @MigrationStats containing detailed migration status,
+# only returned if status is 'active'
+#
+# @disk: #optional @MigrationStats containing detailed disk migration
+# status, only returned if status is 'active' and it is a block
+# migration
+#
+# Since: 0.14.0
+##
+{ 'type': 'MigrationInfo',
+ 'data': {'*status': 'str', '*ram': 'MigrationStats',
+ '*disk': 'MigrationStats'} }
+
+##
+# @query-migrate
+#
+# Returns information about current migration process.
+#
+# Returns: @MigrationInfo
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-migrate', 'returns': 'MigrationInfo' }
+
+##
+# @MouseInfo:
+#
+# Information about a mouse device.
+#
+# @name: the name of the mouse device
+#
+# @index: the index of the mouse device
+#
+# @current: true if this device is currently receiving mouse events
+#
+# @absolute: true if this device supports absolute coordinates as input
+#
+# Since: 0.14.0
+##
+{ 'type': 'MouseInfo',
+ 'data': {'name': 'str', 'index': 'int', 'current': 'bool',
+ 'absolute': 'bool'} }
+
+##
+# @query-mice:
+#
+# Returns information about each active mouse device
+#
+# Returns: a list of @MouseInfo for each device
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-mice', 'returns': ['MouseInfo'] }
+
+##
+# @CpuInfo:
+#
+# Information about a virtual CPU
+#
+# @CPU: the index of the virtual CPU
+#
+# @current: this only exists for backwards compatible and should be ignored
+#
+# @halted: true if the virtual CPU is in the halt state. Halt usually refers
+# to a processor specific low power mode.
+#
+# @pc: #optional If the target is i386 or x86_64, this is the 64-bit instruction
+# pointer.
+# If the target is Sparc, this is the PC component of the
+# instruction pointer.
+#
+# @nip: #optional If the target is PPC, the instruction pointer
+#
+# @npc: #optional If the target is Sparc, the NPC component of the instruction
+# pointer
+#
+# @PC: #optional If the target is MIPS, the instruction pointer
+#
+# @thread_id: ID of the underlying host thread
+#
+# Since: 0.14.0
+#
+# Notes: @halted is a transient state that changes frequently. By the time the
+# data is sent to the client, the guest may no longer be halted.
+##
+{ 'type': 'CpuInfo',
+ 'data': {'CPU': 'int', 'current': 'bool', 'halted': 'bool', '*pc': 'int',
+ '*nip': 'int', '*npc': 'int', '*PC': 'int', 'thread_id': 'int'} }
+
+##
+# @query-cpus:
+#
+# Returns a list of information about each virtual CPU.
+#
+# Returns: a list of @CpuInfo for each virtual CPU
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-cpus', 'returns': ['CpuInfo'] }
+
+##
+# @BlockDeviceInfo:
+#
+# Information about the backing device for a block device.
+#
+# @file: the filename of the backing device
+#
+# @ro: true if the backing device was open read-only
+#
+# @drv: the name of the block format used to open the backing device. As of
+# 0.14.0 this can be: 'blkdebug', 'bochs', 'cloop', 'cow', 'dmg',
+# 'file', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device',
+# 'host_floppy', 'http', 'https', 'nbd', 'parallels', 'qcow',
+# 'qcow2', 'raw', 'tftp', 'vdi', 'vmdk', 'vpc', 'vvfat'
+#
+# @backing_file: #optional the name of the backing file (for copy-on-write)
+#
+# @encrypted: true if the backing device is encrypted
+#
+# Since: 0.14.0
+#
+# Notes: This interface is only found in @BlockInfo.
+##
+{ 'type': 'BlockDeviceInfo',
+ 'data': { 'file': 'str', 'ro': 'bool', 'drv': 'str',
+ '*backing_file': 'str', 'encrypted': 'bool' } }
+
+##
+# @BlockDeviceIoStatus:
+#
+# An enumeration of block device I/O status.
+#
+# @ok: The last I/O operation has succeeded
+#
+# @failed: The last I/O operation has failed
+#
+# @nospace: The last I/O operation has failed due to a no-space condition
+#
+# Since: 1.0
+##
+{ 'enum': 'BlockDeviceIoStatus', 'data': [ 'ok', 'failed', 'nospace' ] }
+
+##
+# @BlockInfo:
+#
+# Block device information. This structure describes a virtual device and
+# the backing device associated with it.
+#
+# @device: The device name associated with the virtual device.
+#
+# @type: This field is returned only for compatibility reasons, it should
+# not be used (always returns 'unknown')
+#
+# @removable: True if the device supports removable media.
+#
+# @locked: True if the guest has locked this device from having its media
+# removed
+#
+# @tray_open: #optional True if the device has a tray and it is open
+# (only present if removable is true)
+#
+# @io-status: #optional @BlockDeviceIoStatus. Only present if the device
+# supports it and the VM is configured to stop on errors
+#
+# @inserted: #optional @BlockDeviceInfo describing the device if media is
+# present
+#
+# Since: 0.14.0
+##
+{ 'type': 'BlockInfo',
+ 'data': {'device': 'str', 'type': 'str', 'removable': 'bool',
+ 'locked': 'bool', '*inserted': 'BlockDeviceInfo',
+ '*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus'} }
+
+##
+# @query-block:
+#
+# Get a list of BlockInfo for all virtual block devices.
+#
+# Returns: a list of @BlockInfo describing each virtual block device
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-block', 'returns': ['BlockInfo'] }
+
+##
+# @BlockDeviceStats:
+#
+# Statistics of a virtual block device or a block backing device.
+#
+# @rd_bytes: The number of bytes read by the device.
+#
+# @wr_bytes: The number of bytes written by the device.
+#
+# @rd_operations: The number of read operations performed by the device.
+#
+# @wr_operations: The number of write operations performed by the device.
+#
+# @flush_operations: The number of cache flush operations performed by the
+# device (since 0.15.0)
+#
+# @flush_total_time_ns: Total time spend on cache flushes in nano-seconds
+# (since 0.15.0).
+#
+# @wr_total_time_ns: Total time spend on writes in nano-seconds (since 0.15.0).
+#
+# @rd_total_time_ns: Total_time_spend on reads in nano-seconds (since 0.15.0).
+#
+# @wr_highest_offset: The offset after the greatest byte written to the
+# device. The intended use of this information is for
+# growable sparse files (like qcow2) that are used on top
+# of a physical device.
+#
+# Since: 0.14.0
+##
+{ 'type': 'BlockDeviceStats',
+ 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'rd_operations': 'int',
+ 'wr_operations': 'int', 'flush_operations': 'int',
+ 'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int',
+ 'rd_total_time_ns': 'int', 'wr_highest_offset': 'int' } }
+
+##
+# @BlockStats:
+#
+# Statistics of a virtual block device or a block backing device.
+#
+# @device: #optional If the stats are for a virtual block device, the name
+# corresponding to the virtual block device.
+#
+# @stats: A @BlockDeviceStats for the device.
+#
+# @parent: #optional This may point to the backing block device if this is a
+# a virtual block device. If it's a backing block, this will point
+# to the backing file is one is present.
+#
+# Since: 0.14.0
+##
+{ 'type': 'BlockStats',
+ 'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
+ '*parent': 'BlockStats'} }
+
+##
+# @query-blockstats:
+#
+# Query the @BlockStats for all virtual block devices.
+#
+# Returns: A list of @BlockStats for each virtual block devices.
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-blockstats', 'returns': ['BlockStats'] }
+
+##
+# @VncClientInfo:
+#
+# Information about a connected VNC client.
+#
+# @host: The host name of the client. QEMU tries to resolve this to a DNS name
+# when possible.
+#
+# @family: 'ipv6' if the client is connected via IPv6 and TCP
+# 'ipv4' if the client is connected via IPv4 and TCP
+# 'unix' if the client is connected via a unix domain socket
+# 'unknown' otherwise
+#
+# @service: The service name of the client's port. This may depends on the
+# host system's service database so symbolic names should not be
+# relied on.
+#
+# @x509_dname: #optional If x509 authentication is in use, the Distinguished
+# Name of the client.
+#
+# @sasl_username: #optional If SASL authentication is in use, the SASL username
+# used for authentication.
+#
+# Since: 0.14.0
+##
+{ 'type': 'VncClientInfo',
+ 'data': {'host': 'str', 'family': 'str', 'service': 'str',
+ '*x509_dname': 'str', '*sasl_username': 'str'} }
+
+##
+# @VncInfo:
+#
+# Information about the VNC session.
+#
+# @enabled: true if the VNC server is enabled, false otherwise
+#
+# @host: #optional The hostname the VNC server is bound to. This depends on
+# the name resolution on the host and may be an IP address.
+#
+# @family: #optional 'ipv6' if the host is listening for IPv6 connections
+# 'ipv4' if the host is listening for IPv4 connections
+# 'unix' if the host is listening on a unix domain socket
+# 'unknown' otherwise
+#
+# @service: #optional The service name of the server's port. This may depends
+# on the host system's service database so symbolic names should not
+# be relied on.
+#
+# @auth: #optional the current authentication type used by the server
+# 'none' if no authentication is being used
+# 'vnc' if VNC authentication is being used
+# 'vencrypt+plain' if VEncrypt is used with plain text authentication
+# 'vencrypt+tls+none' if VEncrypt is used with TLS and no authentication
+# 'vencrypt+tls+vnc' if VEncrypt is used with TLS and VNC authentication
+# 'vencrypt+tls+plain' if VEncrypt is used with TLS and plain text auth
+# 'vencrypt+x509+none' if VEncrypt is used with x509 and no auth
+# 'vencrypt+x509+vnc' if VEncrypt is used with x509 and VNC auth
+# 'vencrypt+x509+plain' if VEncrypt is used with x509 and plain text auth
+# 'vencrypt+tls+sasl' if VEncrypt is used with TLS and SASL auth
+# 'vencrypt+x509+sasl' if VEncrypt is used with x509 and SASL auth
+#
+# @clients: a list of @VncClientInfo of all currently connected clients
+#
+# Since: 0.14.0
+##
+{ 'type': 'VncInfo',
+ 'data': {'enabled': 'bool', '*host': 'str', '*family': 'str',
+ '*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} }
+
+##
+# @query-vnc:
+#
+# Returns information about the current VNC server
+#
+# Returns: @VncInfo
+# If VNC support is not compiled in, FeatureDisabled
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-vnc', 'returns': 'VncInfo' }
+
+##
+# @SpiceChannel
+#
+# Information about a SPICE client channel.
+#
+# @host: The host name of the client. QEMU tries to resolve this to a DNS name
+# when possible.
+#
+# @family: 'ipv6' if the client is connected via IPv6 and TCP
+# 'ipv4' if the client is connected via IPv4 and TCP
+# 'unix' if the client is connected via a unix domain socket
+# 'unknown' otherwise
+#
+# @port: The client's port number.
+#
+# @connection-id: SPICE connection id number. All channels with the same id
+# belong to the same SPICE session.
+#
+# @connection-type: SPICE channel type number. "1" is the main control channel,
+# filter for this one if you want track spice sessions only
+#
+# @channel-id: SPICE channel ID number. Usually "0", might be different needed
+# when multiple channels of the same type exist, such as multiple
+# display channels in a multihead setup
+#
+# @tls: true if the channel is encrypted, false otherwise.
+#
+# Since: 0.14.0
+##
+{ 'type': 'SpiceChannel',
+ 'data': {'host': 'str', 'family': 'str', 'port': 'str',
+ 'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int',
+ 'tls': 'bool'} }
+
+##
+# @SpiceInfo
+#
+# Information about the SPICE session.
+#
+# @enabled: true if the SPICE server is enabled, false otherwise
+#
+# @host: #optional The hostname the SPICE server is bound to. This depends on
+# the name resolution on the host and may be an IP address.
+#
+# @port: #optional The SPICE server's port number.
+#
+# @compiled-version: #optional SPICE server version.
+#
+# @tls-port: #optional The SPICE server's TLS port number.
+#
+# @auth: #optional the current authentication type used by the server
+# 'none' if no authentication is being used
+# 'spice' (TODO: describe)
+#
+# @channels: a list of @SpiceChannel for each active spice channel
+#
+# Since: 0.14.0
+##
+{ 'type': 'SpiceInfo',
+ 'data': {'enabled': 'bool', '*host': 'str', '*port': 'int',
+ '*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str',
+ '*channels': ['SpiceChannel']} }
+
+##
+# @query-spice
+#
+# Returns information about the current SPICE server
+#
+# Returns: @SpiceInfo
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-spice', 'returns': 'SpiceInfo' }
+
+##
+# @BalloonInfo:
+#
+# Information about the guest balloon device.
+#
+# @actual: the number of bytes the balloon currently contains
+#
+# @mem_swapped_in: #optional number of pages swapped in within the guest
+#
+# @mem_swapped_out: #optional number of pages swapped out within the guest
+#
+# @major_page_faults: #optional number of major page faults within the guest
+#
+# @minor_page_faults: #optional number of minor page faults within the guest
+#
+# @free_mem: #optional amount of memory (in bytes) free in the guest
+#
+# @total_mem: #optional amount of memory (in bytes) visible to the guest
+#
+# Since: 0.14.0
+#
+# Notes: all current versions of QEMU do not fill out optional information in
+# this structure.
+##
+{ 'type': 'BalloonInfo',
+ 'data': {'actual': 'int', '*mem_swapped_in': 'int',
+ '*mem_swapped_out': 'int', '*major_page_faults': 'int',
+ '*minor_page_faults': 'int', '*free_mem': 'int',
+ '*total_mem': 'int'} }
+
+##
+# @query-balloon:
+#
+# Return information about the balloon device.
+#
+# Returns: @BalloonInfo on success
+# If the balloon driver is enabled but not functional because the KVM
+# kernel module cannot support it, KvmMissingCap
+# If no balloon device is present, DeviceNotActive
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-balloon', 'returns': 'BalloonInfo' }
+
+##
+# @PciMemoryRange:
+#
+# A PCI device memory region
+#
+# @base: the starting address (guest physical)
+#
+# @limit: the ending address (guest physical)
+#
+# Since: 0.14.0
+##
+{ 'type': 'PciMemoryRange', 'data': {'base': 'int', 'limit': 'int'} }
+
+##
+# @PciMemoryRegion
+#
+# Information about a PCI device I/O region.
+#
+# @bar: the index of the Base Address Register for this region
+#
+# @type: 'io' if the region is a PIO region
+# 'memory' if the region is a MMIO region
+#
+# @prefetch: #optional if @type is 'memory', true if the memory is prefetchable
+#
+# @mem_type_64: #optional if @type is 'memory', true if the BAR is 64-bit
+#
+# Since: 0.14.0
+##
+{ 'type': 'PciMemoryRegion',
+ 'data': {'bar': 'int', 'type': 'str', 'address': 'int', 'size': 'int',
+ '*prefetch': 'bool', '*mem_type_64': 'bool' } }
+
+##
+# @PciBridgeInfo:
+#
+# Information about a PCI Bridge device
+#
+# @bus.number: primary bus interface number. This should be the number of the
+# bus the device resides on.
+#
+# @bus.secondary: secondary bus interface number. This is the number of the
+# main bus for the bridge
+#
+# @bus.subordinate: This is the highest number bus that resides below the
+# bridge.
+#
+# @bus.io_range: The PIO range for all devices on this bridge
+#
+# @bus.memory_range: The MMIO range for all devices on this bridge
+#
+# @bus.prefetchable_range: The range of prefetchable MMIO for all devices on
+# this bridge
+#
+# @devices: a list of @PciDeviceInfo for each device on this bridge
+#
+# Since: 0.14.0
+##
+{ 'type': 'PciBridgeInfo',
+ 'data': {'bus': { 'number': 'int', 'secondary': 'int', 'subordinate': 'int',
+ 'io_range': 'PciMemoryRange',
+ 'memory_range': 'PciMemoryRange',
+ 'prefetchable_range': 'PciMemoryRange' },
+ '*devices': ['PciDeviceInfo']} }
+
+##
+# @PciDeviceInfo:
+#
+# Information about a PCI device
+#
+# @bus: the bus number of the device
+#
+# @slot: the slot the device is located in
+#
+# @function: the function of the slot used by the device
+#
+# @class_info.desc: #optional a string description of the device's class
+#
+# @class_info.class: the class code of the device
+#
+# @id.device: the PCI device id
+#
+# @id.vendor: the PCI vendor id
+#
+# @irq: #optional if an IRQ is assigned to the device, the IRQ number
+#
+# @qdev_id: the device name of the PCI device
+#
+# @pci_bridge: if the device is a PCI bridge, the bridge information
+#
+# @regions: a list of the PCI I/O regions associated with the device
+#
+# Notes: the contents of @class_info.desc are not stable and should only be
+# treated as informational.
+#
+# Since: 0.14.0
+##
+{ 'type': 'PciDeviceInfo',
+ 'data': {'bus': 'int', 'slot': 'int', 'function': 'int',
+ 'class_info': {'*desc': 'str', 'class': 'int'},
+ 'id': {'device': 'int', 'vendor': 'int'},
+ '*irq': 'int', 'qdev_id': 'str', '*pci_bridge': 'PciBridgeInfo',
+ 'regions': ['PciMemoryRegion']} }
+
+##
+# @PciInfo:
+#
+# Information about a PCI bus
+#
+# @bus: the bus index
+#
+# @devices: a list of devices on this bus
+#
+# Since: 0.14.0
+##
+{ 'type': 'PciInfo', 'data': {'bus': 'int', 'devices': ['PciDeviceInfo']} }
+
+##
+# @query-pci:
+#
+# Return information about the PCI bus topology of the guest.
+#
+# Returns: a list of @PciInfo for each PCI bus
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-pci', 'returns': ['PciInfo'] }
+
+##
# @quit:
#
# This command will cause the QEMU process to exit gracefully. While every
@@ -271,3 +876,14 @@
# prompting the user in some way.
##
{ 'command': 'system_powerdown' }
+
+##
+# @cpu:
+#
+# This command is a nop that is only provided for the purposes of compatibility.
+#
+# Since: 0.14.0
+#
+# Notes: Do not use this command.
+##
+{ 'command': 'cpu', 'data': {'index': 'int'} }
diff --git a/qemu-config.c b/qemu-config.c
index 90b6b3e85b..597d7e10b1 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -180,7 +180,11 @@ QemuOptsList qemu_fsdev_opts = {
}, {
.name = "writeout",
.type = QEMU_OPT_STRING,
+ }, {
+ .name = "readonly",
+ .type = QEMU_OPT_BOOL,
},
+
{ /*End of list */ }
},
};
@@ -205,6 +209,9 @@ QemuOptsList qemu_virtfs_opts = {
}, {
.name = "writeout",
.type = QEMU_OPT_STRING,
+ }, {
+ .name = "readonly",
+ .type = QEMU_OPT_BOOL,
},
{ /*End of list */ }
diff --git a/qemu-doc.texi b/qemu-doc.texi
index ad19b73f15..149e9bd28b 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -227,7 +227,7 @@ QEMU uses YM3812 emulation by Tatsuyuki Satoh.
QEMU uses GUS emulation (GUSEMU32 @url{http://www.deinmeister.de/gusemu/})
by Tibor "TS" Schütz.
-Not that, by default, GUS shares IRQ(7) with parallel ports and so
+Note that, by default, GUS shares IRQ(7) with parallel ports and so
qemu must be told to not have parallel ports to have working GUS
@example
diff --git a/qemu-io.c b/qemu-io.c
index c45a4138b2..5af887e057 100644
--- a/qemu-io.c
+++ b/qemu-io.c
@@ -1582,7 +1582,7 @@ static const cmdinfo_t map_cmd = {
static int close_f(int argc, char **argv)
{
- bdrv_close(bs);
+ bdrv_delete(bs);
bs = NULL;
return 0;
}
@@ -1611,6 +1611,7 @@ static int openfile(char *name, int flags, int growable)
if (bdrv_open(bs, name, flags, NULL) < 0) {
fprintf(stderr, "%s: can't open device %s\n", progname, name);
+ bdrv_delete(bs);
bs = NULL;
return 1;
}
@@ -1834,7 +1835,7 @@ int main(int argc, char **argv)
qemu_aio_flush();
if (bs) {
- bdrv_close(bs);
+ bdrv_delete(bs);
}
return 0;
}
diff --git a/qemu-option.c b/qemu-option.c
index 105d760a8a..f97a758a95 100644
--- a/qemu-option.c
+++ b/qemu-option.c
@@ -168,7 +168,7 @@ QEMUOptionParameter *get_option_parameter(QEMUOptionParameter *list,
return NULL;
}
-static int parse_option_bool(const char *name, const char *value, int *ret)
+static int parse_option_bool(const char *name, const char *value, bool *ret)
{
if (value != NULL) {
if (!strcmp(value, "on")) {
@@ -258,7 +258,7 @@ static int parse_option_size(const char *name, const char *value, uint64_t *ret)
int set_option_parameter(QEMUOptionParameter *list, const char *name,
const char *value)
{
- int flag;
+ bool flag;
// Find a matching parameter
list = get_option_parameter(list, name);
@@ -508,7 +508,7 @@ struct QemuOpt {
const QemuOptDesc *desc;
union {
- int boolean;
+ bool boolean;
uint64_t uint;
} value;
@@ -542,7 +542,7 @@ const char *qemu_opt_get(QemuOpts *opts, const char *name)
return opt ? opt->str : NULL;
}
-int qemu_opt_get_bool(QemuOpts *opts, const char *name, int defval)
+bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval)
{
QemuOpt *opt = qemu_opt_find(opts, name);
@@ -636,6 +636,37 @@ int qemu_opt_set(QemuOpts *opts, const char *name, const char *value)
return 0;
}
+int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val)
+{
+ QemuOpt *opt;
+ const QemuOptDesc *desc = opts->list->desc;
+ int i;
+
+ for (i = 0; desc[i].name != NULL; i++) {
+ if (strcmp(desc[i].name, name) == 0) {
+ break;
+ }
+ }
+ if (desc[i].name == NULL) {
+ if (i == 0) {
+ /* empty list -> allow any */;
+ } else {
+ qerror_report(QERR_INVALID_PARAMETER, name);
+ return -1;
+ }
+ }
+
+ opt = g_malloc0(sizeof(*opt));
+ opt->name = g_strdup(name);
+ opt->opts = opts;
+ QTAILQ_INSERT_TAIL(&opts->head, opt, next);
+ if (desc[i].name != NULL) {
+ opt->desc = desc+i;
+ }
+ opt->value.boolean = !!val;
+ return 0;
+}
+
int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
int abort_on_failure)
{
diff --git a/qemu-option.h b/qemu-option.h
index b515813891..07958e4e90 100644
--- a/qemu-option.h
+++ b/qemu-option.h
@@ -105,10 +105,11 @@ struct QemuOptsList {
};
const char *qemu_opt_get(QemuOpts *opts, const char *name);
-int qemu_opt_get_bool(QemuOpts *opts, const char *name, int defval);
+bool qemu_opt_get_bool(QemuOpts *opts, const char *name, bool defval);
uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval);
uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval);
int qemu_opt_set(QemuOpts *opts, const char *name, const char *value);
+int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val);
typedef int (*qemu_opt_loopfunc)(const char *name, const char *value, void *opaque);
int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
int abort_on_failure);
diff --git a/qemu-options.hx b/qemu-options.hx
index 5d2a7765e8..681eaf198e 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -148,6 +148,9 @@ Define a new drive. Valid options are:
This option defines which disk image (@pxref{disk_images}) to use with
this drive. If the filename contains comma, you must double it
(for instance, "file=my,,file" to use file "my,file").
+
+Special files such as iSCSI devices can be specified using protocol
+specific URLs. See the section for "Device URL Syntax" for more information.
@item if=@var{interface}
This option defines on which type on interface the drive is connected.
Available types are: ide, scsi, sd, mtd, floppy, pflash, virtio.
@@ -528,12 +531,12 @@ DEFHEADING(File system options:)
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
"-fsdev fsdriver,id=id,path=path,[security_model={mapped|passthrough|none}]\n"
- " [,writeout=immediate]\n",
+ " [,writeout=immediate][,readonly]\n",
QEMU_ARCH_ALL)
STEXI
-@item -fsdev @var{fsdriver},id=@var{id},path=@var{path},[security_model=@var{security_model}][,writeout=@var{writeout}]
+@item -fsdev @var{fsdriver},id=@var{id},path=@var{path},[security_model=@var{security_model}][,writeout=@var{writeout}][,readonly]
@findex -fsdev
Define a new file system device. Valid options are:
@table @option
@@ -563,6 +566,9 @@ This is an optional argument. The only supported value is "immediate".
This means that host page cache will be used to read and write data but
write notification will be sent to the guest only when the data has been
reported as written by the storage subsystem.
+@item readonly
+Enables exporting 9p share as a readonly mount for guests. By default
+read-write access is given.
@end table
-fsdev option is used along with -device driver "virtio-9p-pci".
@@ -583,12 +589,12 @@ DEFHEADING(Virtual File system pass-through options:)
DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs,
"-virtfs local,path=path,mount_tag=tag,security_model=[mapped|passthrough|none]\n"
- " [,writeout=immediate]\n",
+ " [,writeout=immediate][,readonly]\n",
QEMU_ARCH_ALL)
STEXI
-@item -virtfs @var{fsdriver},path=@var{path},mount_tag=@var{mount_tag},security_model=@var{security_model}[,writeout=@var{writeout}]
+@item -virtfs @var{fsdriver},path=@var{path},mount_tag=@var{mount_tag},security_model=@var{security_model}[,writeout=@var{writeout}][,readonly]
@findex -virtfs
The general form of a Virtual File system pass-through options are:
@@ -619,9 +625,21 @@ This is an optional argument. The only supported value is "immediate".
This means that host page cache will be used to read and write data but
write notification will be sent to the guest only when the data has been
reported as written by the storage subsystem.
+@item readonly
+Enables exporting 9p share as a readonly mount for guests. By default
+read-write access is given.
@end table
ETEXI
+DEF("virtfs_synth", 0, QEMU_OPTION_virtfs_synth,
+ "-virtfs_synth Create synthetic file system image\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -virtfs_synth
+@findex -virtfs_synth
+Create synthetic file system image
+ETEXI
+
DEFHEADING()
DEF("name", HAS_ARG, QEMU_OPTION_name,
@@ -1718,6 +1736,93 @@ ETEXI
DEFHEADING()
+STEXI
+DEFHEADING(Device URL Syntax:)
+
+In addition to using normal file images for the emulated storage devices,
+QEMU can also use networked resources such as iSCSI devices. These are
+specified using a special URL syntax.
+
+@table @option
+@item iSCSI
+iSCSI support allows QEMU to access iSCSI resources directly and use as
+images for the guest storage. Both disk and cdrom images are supported.
+
+Syntax for specifying iSCSI LUNs is
+``iscsi://<target-ip>[:<port>]/<target-iqn>/<lun>''
+
+Example (without authentication):
+@example
+qemu -cdrom iscsi://192.0.2.1/iqn.2001-04.com.example/2 \
+--drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1
+@end example
+
+Example (CHAP username/password via URL):
+@example
+qemu --drive file=iscsi://user%password@@192.0.2.1/iqn.2001-04.com.example/1
+@end example
+
+Example (CHAP username/password via environment variables):
+@example
+LIBISCSI_CHAP_USERNAME="user" \
+LIBISCSI_CHAP_PASSWORD="password" \
+qemu --drive file=iscsi://192.0.2.1/iqn.2001-04.com.example/1
+@end example
+
+iSCSI support is an optional feature of QEMU and only available when
+compiled and linked against libiscsi.
+
+@item NBD
+QEMU supports NBD (Network Block Devices) both using TCP protocol as well
+as Unix Domain Sockets.
+
+Syntax for specifying a NBD device using TCP
+``nbd:<server-ip>:<port>[:exportname=<export>]''
+
+Syntax for specifying a NBD device using Unix Domain Sockets
+``nbd:unix:<domain-socket>[:exportname=<export>]''
+
+
+Example for TCP
+@example
+qemu --drive file=nbd:192.0.2.1:30000
+@end example
+
+Example for Unix Domain Sockets
+@example
+qemu --drive file=nbd:unix:/tmp/nbd-socket
+@end example
+
+@item Sheepdog
+Sheepdog is a distributed storage system for QEMU.
+QEMU supports using either local sheepdog devices or remote networked
+devices.
+
+Syntax for specifying a sheepdog device
+@table @list
+``sheepdog:<vdiname>''
+
+``sheepdog:<vdiname>:<snapid>''
+
+``sheepdog:<vdiname>:<tag>''
+
+``sheepdog:<host>:<port>:<vdiname>''
+
+``sheepdog:<host>:<port>:<vdiname>:<snapid>''
+
+``sheepdog:<host>:<port>:<vdiname>:<tag>''
+@end table
+
+Example
+@example
+qemu --drive file=sheepdog:192.0.2.1:30000:MyVirtualMachine
+@end example
+
+See also @url{http://http://www.osrg.net/sheepdog/}.
+
+@end table
+ETEXI
+
DEFHEADING(Bluetooth(R) options:)
DEF("bt", HAS_ARG, QEMU_OPTION_bt, \
diff --git a/qemu-queue.h b/qemu-queue.h
index 1d077458ce..22142305a6 100644
--- a/qemu-queue.h
+++ b/qemu-queue.h
@@ -76,6 +76,8 @@
* For details on the use of these macros, see the queue(3) manual page.
*/
+#include "qemu-barrier.h" /* for smp_wmb() */
+
/*
* List definitions.
*/
@@ -122,6 +124,17 @@ struct { \
(elm)->field.le_prev = &(head)->lh_first; \
} while (/*CONSTCOND*/0)
+#define QLIST_INSERT_HEAD_RCU(head, elm, field) do { \
+ (elm)->field.le_prev = &(head)->lh_first; \
+ (elm)->field.le_next = (head)->lh_first; \
+ smp_wmb(); /* fill elm before linking it */ \
+ if ((head)->lh_first != NULL) { \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next; \
+ } \
+ (head)->lh_first = (elm); \
+ smp_wmb(); \
+} while (/* CONSTCOND*/0)
+
#define QLIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
diff --git a/qemu-thread.h b/qemu-thread.h
index 0a73d50524..e008b60028 100644
--- a/qemu-thread.h
+++ b/qemu-thread.h
@@ -19,6 +19,9 @@ void qemu_mutex_lock(QemuMutex *mutex);
int qemu_mutex_trylock(QemuMutex *mutex);
void qemu_mutex_unlock(QemuMutex *mutex);
+#define rcu_read_lock() do { } while (0)
+#define rcu_read_unlock() do { } while (0)
+
void qemu_cond_init(QemuCond *cond);
void qemu_cond_destroy(QemuCond *cond);
diff --git a/qerror.c b/qerror.c
index 68998d4bab..4b48b39660 100644
--- a/qerror.c
+++ b/qerror.c
@@ -117,6 +117,10 @@ static const QErrorStringTable qerror_table[] = {
.desc = "No file descriptor supplied via SCM_RIGHTS",
},
{
+ .error_fmt = QERR_FEATURE_DISABLED,
+ .desc = "The feature '%(name)' is not enabled",
+ },
+ {
.error_fmt = QERR_INVALID_BLOCK_FORMAT,
.desc = "Invalid block format '%(name)'",
},
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 4328e8b86c..97975a5207 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -331,10 +331,7 @@ EQMP
{
.name = "cpu",
.args_type = "index:i",
- .params = "index",
- .help = "set the default CPU",
- .user_print = monitor_user_noop,
- .mhandler.cmd_new = do_cpu_set,
+ .mhandler.cmd_new = qmp_marshal_input_cpu,
},
SQMP
@@ -569,7 +566,8 @@ EQMP
.params = "protocol hostname port tls-port cert-subject",
.help = "send migration info to spice/vnc client",
.user_print = monitor_user_noop,
- .mhandler.cmd_new = client_migrate_info,
+ .mhandler.cmd_async = client_migrate_info,
+ .flags = MONITOR_CMD_ASYNC,
},
SQMP
@@ -1201,6 +1199,12 @@ Example:
EQMP
+ {
+ .name = "query-block",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_block,
+ },
+
SQMP
query-blockstats
----------------
@@ -1308,6 +1312,12 @@ Example:
EQMP
+ {
+ .name = "query-blockstats",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_blockstats,
+ },
+
SQMP
query-cpus
----------
@@ -1350,6 +1360,12 @@ Example:
EQMP
+ {
+ .name = "query-cpus",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_cpus,
+ },
+
SQMP
query-pci
---------
@@ -1561,6 +1577,12 @@ Note: This example has been shortened as the real response is too long.
EQMP
+ {
+ .name = "query-pci",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_pci,
+ },
+
SQMP
query-kvm
---------
@@ -1663,6 +1685,12 @@ Example:
EQMP
+ {
+ .name = "query-mice",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_mice,
+ },
+
SQMP
query-vnc
---------
@@ -1720,6 +1748,12 @@ Example:
EQMP
+ {
+ .name = "query-vnc",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_vnc,
+ },
+
SQMP
query-spice
-----------
@@ -1790,6 +1824,14 @@ Example:
EQMP
+#if defined(CONFIG_SPICE)
+ {
+ .name = "query-spice",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_spice,
+ },
+#endif
+
SQMP
query-name
----------
@@ -1913,6 +1955,12 @@ Examples:
EQMP
+ {
+ .name = "query-migrate",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_migrate,
+ },
+
SQMP
query-balloon
-------------
@@ -1948,3 +1996,8 @@ Example:
EQMP
+ {
+ .name = "query-balloon",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_balloon,
+ },
diff --git a/qmp.c b/qmp.c
index bf58b05a9a..511dd624b5 100644
--- a/qmp.c
+++ b/qmp.c
@@ -90,3 +90,30 @@ void qmp_system_powerdown(Error **erp)
{
qemu_system_powerdown_request();
}
+
+void qmp_cpu(int64_t index, Error **errp)
+{
+ /* Just do nothing */
+}
+
+#ifndef CONFIG_VNC
+/* If VNC support is enabled, the "true" query-vnc command is
+ defined in the VNC subsystem */
+VncInfo *qmp_query_vnc(Error **errp)
+{
+ error_set(errp, QERR_FEATURE_DISABLED, "vnc");
+ return NULL;
+};
+#endif
+
+#ifndef CONFIG_SPICE
+/* If SPICE support is enabled, the "true" query-spice command is
+ defined in the SPICE subsystem. Also note that we use a small
+ trick to maintain query-spice's original behavior, which is not
+ to be available in the namespace if SPICE is not compiled in */
+SpiceInfo *qmp_query_spice(Error **errp)
+{
+ error_set(errp, QERR_COMMAND_NOT_FOUND, "query-spice");
+ return NULL;
+};
+#endif
diff --git a/scripts/analyse-9p-simpletrace.py b/scripts/analyse-9p-simpletrace.py
index 4358d6b594..b6d58fde96 100755
--- a/scripts/analyse-9p-simpletrace.py
+++ b/scripts/analyse-9p-simpletrace.py
@@ -7,11 +7,11 @@
import simpletrace
class VirtFSRequestTracker(simpletrace.Analyzer):
- def begin(self):
- print "Pretty printing 9p simpletrace log ..."
+ def begin(self):
+ print "Pretty printing 9p simpletrace log ..."
- def complete_pdu(self, tag, id, err):
- print "ERROR (tag =", tag, ", id =", id, ",err =", err, ")"
+ def v9fs_rerror(self, tag, id, err):
+ print "RERROR (tag =", tag, ", id =", id, ",err =", err, ")"
def v9fs_version(self, tag, id, msize, version):
print "TVERSION (tag =", tag, ", msize =", msize, ", version =", version, ")"
@@ -22,121 +22,121 @@ class VirtFSRequestTracker(simpletrace.Analyzer):
def v9fs_attach(self, tag, id, fid, afid, uname, aname):
print "TATTACH (tag =", tag, ", fid =", fid, ", afid =", afid, ", uname =", uname, ", aname =", aname, ")"
- def v9fs_attach_return(self, tag, id, type, verison, path):
- print "RATTACH (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})"
+ def v9fs_attach_return(self, tag, id, type, version, path):
+ print "RATTACH (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})"
- def v9fs_stat(self, tag, id, fid):
- print "TSTAT (tag =", tag, ", fid =", fid, ")"
+ def v9fs_stat(self, tag, id, fid):
+ print "TSTAT (tag =", tag, ", fid =", fid, ")"
- def v9fs_stat_return(self, tag, id, mode, atime, mtime, length):
- print "RSTAT (tag =", tag, ", mode =", mode, ", atime =", atime, ", mtime =", mtime, ", length =", length, ")"
+ def v9fs_stat_return(self, tag, id, mode, atime, mtime, length):
+ print "RSTAT (tag =", tag, ", mode =", mode, ", atime =", atime, ", mtime =", mtime, ", length =", length, ")"
- def v9fs_getattr(self, tag, id, fid, request_mask):
- print "TGETATTR (tag =", tag, ", fid =", fid, ", request_mask =", hex(request_mask), ")"
+ def v9fs_getattr(self, tag, id, fid, request_mask):
+ print "TGETATTR (tag =", tag, ", fid =", fid, ", request_mask =", hex(request_mask), ")"
- def v9fs_getattr_return(self, tag, id, result_mask, mode, uid, gid):
- print "RGETATTR (tag =", tag, ", result_mask =", hex(result_mask), ", mode =", oct(mode), ", uid =", uid, ", gid =", gid, ")"
+ def v9fs_getattr_return(self, tag, id, result_mask, mode, uid, gid):
+ print "RGETATTR (tag =", tag, ", result_mask =", hex(result_mask), ", mode =", oct(mode), ", uid =", uid, ", gid =", gid, ")"
- def v9fs_walk(self, tag, id, fid, newfid, nwnames):
- print "TWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", nwnames =", nwnames, ")"
+ def v9fs_walk(self, tag, id, fid, newfid, nwnames):
+ print "TWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", nwnames =", nwnames, ")"
- def v9fs_walk_return(self, tag, id, nwnames, qids):
- print "RWALK (tag =", tag, ", nwnames =", nwnames, ", qids =", hex(qids), ")"
+ def v9fs_walk_return(self, tag, id, nwnames, qids):
+ print "RWALK (tag =", tag, ", nwnames =", nwnames, ", qids =", hex(qids), ")"
- def v9fs_open(self, tag, id, fid, mode):
- print "TOPEN (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ")"
+ def v9fs_open(self, tag, id, fid, mode):
+ print "TOPEN (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ")"
- def v9fs_open_return(self, tag, id, type, version, path, iounit):
- print "ROPEN (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")"
+ def v9fs_open_return(self, tag, id, type, version, path, iounit):
+ print "ROPEN (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")"
- def v9fs_lcreate(self, tag, id, dfid, flags, mode, gid):
- print "TLCREATE (tag =", tag, ", dfid =", dfid, ", flags =", oct(flags), ", mode =", oct(mode), ", gid =", gid, ")"
+ def v9fs_lcreate(self, tag, id, dfid, flags, mode, gid):
+ print "TLCREATE (tag =", tag, ", dfid =", dfid, ", flags =", oct(flags), ", mode =", oct(mode), ", gid =", gid, ")"
- def v9fs_lcreate_return(self, id, type, version, path, iounit):
- print "RLCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")"
+ def v9fs_lcreate_return(self, tag, id, type, version, path, iounit):
+ print "RLCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")"
- def v9fs_fsync(self, tag, id, fid, datasync):
- print "TFSYNC (tag =", tag, ", fid =", fid, ", datasync =", datasync, ")"
+ def v9fs_fsync(self, tag, id, fid, datasync):
+ print "TFSYNC (tag =", tag, ", fid =", fid, ", datasync =", datasync, ")"
- def v9fs_clunk(self, tag, id, fid):
- print "TCLUNK (tag =", tag, ", fid =", fid, ")"
+ def v9fs_clunk(self, tag, id, fid):
+ print "TCLUNK (tag =", tag, ", fid =", fid, ")"
- def v9fs_read(self, tag, id, fid, off, max_count):
- print "TREAD (tag =", tag, ", fid =", fid, ", off =", off, ", max_count =", max_count, ")"
+ def v9fs_read(self, tag, id, fid, off, max_count):
+ print "TREAD (tag =", tag, ", fid =", fid, ", off =", off, ", max_count =", max_count, ")"
- def v9fs_read_return(self, tag, id, count, err):
- print "RREAD (tag =", tag, ", count =", count, ", err =", err, ")"
+ def v9fs_read_return(self, tag, id, count, err):
+ print "RREAD (tag =", tag, ", count =", count, ", err =", err, ")"
- def v9fs_readdir(self, tag, id, fid, offset, max_count):
- print "TREADDIR (tag =", tag, ", fid =", fid, ", offset =", offset, ", max_count =", max_count, ")"
+ def v9fs_readdir(self, tag, id, fid, offset, max_count):
+ print "TREADDIR (tag =", tag, ", fid =", fid, ", offset =", offset, ", max_count =", max_count, ")"
- def v9fs_readdir_return(self, tag, id, count, retval):
- print "RREADDIR (tag =", tag, ", count =", count, ", retval =", retval, ")"
+ def v9fs_readdir_return(self, tag, id, count, retval):
+ print "RREADDIR (tag =", tag, ", count =", count, ", retval =", retval, ")"
- def v9fs_write(self, tag, id, fid, off, count, cnt):
- print "TWRITE (tag =", tag, ", fid =", fid, ", off =", off, ", count =", count, ", cnt =", cnt, ")"
+ def v9fs_write(self, tag, id, fid, off, count, cnt):
+ print "TWRITE (tag =", tag, ", fid =", fid, ", off =", off, ", count =", count, ", cnt =", cnt, ")"
- def v9fs_write_return(self, tag, id, total, err):
- print "RWRITE (tag =", tag, ", total =", total, ", err =", err, ")"
+ def v9fs_write_return(self, tag, id, total, err):
+ print "RWRITE (tag =", tag, ", total =", total, ", err =", err, ")"
- def v9fs_create(self, tag, id, fid, perm, name, mode):
- print "TCREATE (tag =", tag, ", fid =", fid, ", perm =", oct(perm), ", name =", name, ", mode =", oct(mode), ")"
+ def v9fs_create(self, tag, id, fid, name, perm, mode):
+ print "TCREATE (tag =", tag, ", fid =", fid, ", perm =", oct(perm), ", name =", name, ", mode =", oct(mode), ")"
- def v9fs_create_return(self, tag, id, type, verison, path, iounit):
- print "RCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")"
+ def v9fs_create_return(self, tag, id, type, version, path, iounit):
+ print "RCREATE (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, iounit =", iounit, ")"
- def v9fs_symlink(self, tag, id, fid, name, symname, gid):
- print "TSYMLINK (tag =", tag, ", fid =", fid, ", name =", name, ", symname =", symname, ", gid =", gid, ")"
+ def v9fs_symlink(self, tag, id, fid, name, symname, gid):
+ print "TSYMLINK (tag =", tag, ", fid =", fid, ", name =", name, ", symname =", symname, ", gid =", gid, ")"
- def v9fs_symlink_return(self, tag, id, type, version, path):
- print "RSYMLINK (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})"
+ def v9fs_symlink_return(self, tag, id, type, version, path):
+ print "RSYMLINK (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "})"
- def v9fs_flush(self, tag, id, flush_tag):
- print "TFLUSH (tag =", tag, ", flush_tag =", flush_tag, ")"
+ def v9fs_flush(self, tag, id, flush_tag):
+ print "TFLUSH (tag =", tag, ", flush_tag =", flush_tag, ")"
- def v9fs_link(self, tag, id, dfid, oldfid, name):
- print "TLINK (tag =", tag, ", dfid =", dfid, ", oldfid =", oldfid, ", name =", name, ")"
+ def v9fs_link(self, tag, id, dfid, oldfid, name):
+ print "TLINK (tag =", tag, ", dfid =", dfid, ", oldfid =", oldfid, ", name =", name, ")"
- def v9fs_remove(self, tag, id, fid):
- print "TREMOVE (tag =", tag, ", fid =", fid, ")"
+ def v9fs_remove(self, tag, id, fid):
+ print "TREMOVE (tag =", tag, ", fid =", fid, ")"
- def v9fs_wstat(self, tag, id, fid, mode, atime, mtime):
- print "TWSTAT (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", atime =", atime, "mtime =", mtime, ")"
+ def v9fs_wstat(self, tag, id, fid, mode, atime, mtime):
+ print "TWSTAT (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", atime =", atime, "mtime =", mtime, ")"
- def v9fs_mknod(self, tag, id, fid, mode, major, minor):
- print "TMKNOD (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", major =", major, ", minor =", minor, ")"
+ def v9fs_mknod(self, tag, id, fid, mode, major, minor):
+ print "TMKNOD (tag =", tag, ", fid =", fid, ", mode =", oct(mode), ", major =", major, ", minor =", minor, ")"
- def v9fs_lock(self, tag, id, fid, type, start, length):
- print "TLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")"
+ def v9fs_lock(self, tag, id, fid, type, start, length):
+ print "TLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")"
- def v9fs_lock_return(self, tag, id, status):
- print "RLOCK (tag =", tag, ", status =", status, ")"
+ def v9fs_lock_return(self, tag, id, status):
+ print "RLOCK (tag =", tag, ", status =", status, ")"
- def v9fs_getlock(self, tag, id, fid, type, start, length):
- print "TGETLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")"
+ def v9fs_getlock(self, tag, id, fid, type, start, length):
+ print "TGETLOCK (tag =", tag, ", fid =", fid, "type =", type, ", start =", start, ", length =", length, ")"
- def v9fs_getlock_return(self, tag, id, type, start, length, proc_id):
- print "RGETLOCK (tag =", tag, "type =", type, ", start =", start, ", length =", length, ", proc_id =", proc_id, ")"
+ def v9fs_getlock_return(self, tag, id, type, start, length, proc_id):
+ print "RGETLOCK (tag =", tag, "type =", type, ", start =", start, ", length =", length, ", proc_id =", proc_id, ")"
- def v9fs_mkdir(self, tag, id, fid, name, mode, gid):
- print "TMKDIR (tag =", tag, ", fid =", fid, ", name =", name, ", mode =", mode, ", gid =", gid, ")"
+ def v9fs_mkdir(self, tag, id, fid, name, mode, gid):
+ print "TMKDIR (tag =", tag, ", fid =", fid, ", name =", name, ", mode =", mode, ", gid =", gid, ")"
- def v9fs_mkdir_return(self, tag, id, type, version, path, err):
- print "RMKDIR (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, err =", err, ")"
+ def v9fs_mkdir_return(self, tag, id, type, version, path, err):
+ print "RMKDIR (tag =", tag, ", qid={type =", type, ", version =", version, ", path =", path, "}, err =", err, ")"
- def v9fs_xattrwalk(self, tag, id, fid, newfid, name):
- print "TXATTRWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", xattr name =", name, ")"
+ def v9fs_xattrwalk(self, tag, id, fid, newfid, name):
+ print "TXATTRWALK (tag =", tag, ", fid =", fid, ", newfid =", newfid, ", xattr name =", name, ")"
- def v9fs_xattrwalk_return(self, tag, id, size):
- print "RXATTRWALK (tag =", tag, ", xattrsize =", size, ")"
+ def v9fs_xattrwalk_return(self, tag, id, size):
+ print "RXATTRWALK (tag =", tag, ", xattrsize =", size, ")"
- def v9fs_xattrcreate(self, tag, id, fid, name, size, flags):
- print "TXATTRCREATE (tag =", tag, ", fid =", fid, ", name =", name, ", xattrsize =", size, ", flags =", flags, ")"
+ def v9fs_xattrcreate(self, tag, id, fid, name, size, flags):
+ print "TXATTRCREATE (tag =", tag, ", fid =", fid, ", name =", name, ", xattrsize =", size, ", flags =", flags, ")"
- def v9fs_readlink(self, tag, id, fid):
- print "TREADLINK (tag =", tag, ", fid =", fid, ")"
+ def v9fs_readlink(self, tag, id, fid):
+ print "TREADLINK (tag =", tag, ", fid =", fid, ")"
- def v9fs_readlink_return(self, tag, id, target):
- print "RREADLINK (tag =", tag, ", target =", target, ")"
+ def v9fs_readlink_return(self, tag, id, target):
+ print "RREADLINK (tag =", tag, ", target =", target, ")"
simpletrace.run(VirtFSRequestTracker())
diff --git a/scripts/kvm/kvm_stat b/scripts/kvm/kvm_stat
new file mode 100755
index 0000000000..56d2bd7f21
--- /dev/null
+++ b/scripts/kvm/kvm_stat
@@ -0,0 +1,480 @@
+#!/usr/bin/python
+#
+# top-like utility for displaying kvm statistics
+#
+# Copyright 2006-2008 Qumranet Technologies
+# Copyright 2008-2011 Red Hat, Inc.
+#
+# Authors:
+# Avi Kivity <avi@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+
+import curses
+import sys, os, time, optparse
+
+class DebugfsProvider(object):
+ def __init__(self):
+ self.base = '/sys/kernel/debug/kvm'
+ self._fields = os.listdir(self.base)
+ def fields(self):
+ return self._fields
+ def select(self, fields):
+ self._fields = fields
+ def read(self):
+ def val(key):
+ return int(file(self.base + '/' + key).read())
+ return dict([(key, val(key)) for key in self._fields])
+
+vmx_exit_reasons = {
+ 0: 'EXCEPTION_NMI',
+ 1: 'EXTERNAL_INTERRUPT',
+ 2: 'TRIPLE_FAULT',
+ 7: 'PENDING_INTERRUPT',
+ 8: 'NMI_WINDOW',
+ 9: 'TASK_SWITCH',
+ 10: 'CPUID',
+ 12: 'HLT',
+ 14: 'INVLPG',
+ 15: 'RDPMC',
+ 16: 'RDTSC',
+ 18: 'VMCALL',
+ 19: 'VMCLEAR',
+ 20: 'VMLAUNCH',
+ 21: 'VMPTRLD',
+ 22: 'VMPTRST',
+ 23: 'VMREAD',
+ 24: 'VMRESUME',
+ 25: 'VMWRITE',
+ 26: 'VMOFF',
+ 27: 'VMON',
+ 28: 'CR_ACCESS',
+ 29: 'DR_ACCESS',
+ 30: 'IO_INSTRUCTION',
+ 31: 'MSR_READ',
+ 32: 'MSR_WRITE',
+ 33: 'INVALID_STATE',
+ 36: 'MWAIT_INSTRUCTION',
+ 39: 'MONITOR_INSTRUCTION',
+ 40: 'PAUSE_INSTRUCTION',
+ 41: 'MCE_DURING_VMENTRY',
+ 43: 'TPR_BELOW_THRESHOLD',
+ 44: 'APIC_ACCESS',
+ 48: 'EPT_VIOLATION',
+ 49: 'EPT_MISCONFIG',
+ 54: 'WBINVD',
+ 55: 'XSETBV',
+}
+
+svm_exit_reasons = {
+ 0x000: 'READ_CR0',
+ 0x003: 'READ_CR3',
+ 0x004: 'READ_CR4',
+ 0x008: 'READ_CR8',
+ 0x010: 'WRITE_CR0',
+ 0x013: 'WRITE_CR3',
+ 0x014: 'WRITE_CR4',
+ 0x018: 'WRITE_CR8',
+ 0x020: 'READ_DR0',
+ 0x021: 'READ_DR1',
+ 0x022: 'READ_DR2',
+ 0x023: 'READ_DR3',
+ 0x024: 'READ_DR4',
+ 0x025: 'READ_DR5',
+ 0x026: 'READ_DR6',
+ 0x027: 'READ_DR7',
+ 0x030: 'WRITE_DR0',
+ 0x031: 'WRITE_DR1',
+ 0x032: 'WRITE_DR2',
+ 0x033: 'WRITE_DR3',
+ 0x034: 'WRITE_DR4',
+ 0x035: 'WRITE_DR5',
+ 0x036: 'WRITE_DR6',
+ 0x037: 'WRITE_DR7',
+ 0x040: 'EXCP_BASE',
+ 0x060: 'INTR',
+ 0x061: 'NMI',
+ 0x062: 'SMI',
+ 0x063: 'INIT',
+ 0x064: 'VINTR',
+ 0x065: 'CR0_SEL_WRITE',
+ 0x066: 'IDTR_READ',
+ 0x067: 'GDTR_READ',
+ 0x068: 'LDTR_READ',
+ 0x069: 'TR_READ',
+ 0x06a: 'IDTR_WRITE',
+ 0x06b: 'GDTR_WRITE',
+ 0x06c: 'LDTR_WRITE',
+ 0x06d: 'TR_WRITE',
+ 0x06e: 'RDTSC',
+ 0x06f: 'RDPMC',
+ 0x070: 'PUSHF',
+ 0x071: 'POPF',
+ 0x072: 'CPUID',
+ 0x073: 'RSM',
+ 0x074: 'IRET',
+ 0x075: 'SWINT',
+ 0x076: 'INVD',
+ 0x077: 'PAUSE',
+ 0x078: 'HLT',
+ 0x079: 'INVLPG',
+ 0x07a: 'INVLPGA',
+ 0x07b: 'IOIO',
+ 0x07c: 'MSR',
+ 0x07d: 'TASK_SWITCH',
+ 0x07e: 'FERR_FREEZE',
+ 0x07f: 'SHUTDOWN',
+ 0x080: 'VMRUN',
+ 0x081: 'VMMCALL',
+ 0x082: 'VMLOAD',
+ 0x083: 'VMSAVE',
+ 0x084: 'STGI',
+ 0x085: 'CLGI',
+ 0x086: 'SKINIT',
+ 0x087: 'RDTSCP',
+ 0x088: 'ICEBP',
+ 0x089: 'WBINVD',
+ 0x08a: 'MONITOR',
+ 0x08b: 'MWAIT',
+ 0x08c: 'MWAIT_COND',
+ 0x400: 'NPF',
+}
+
+vendor_exit_reasons = {
+ 'vmx': vmx_exit_reasons,
+ 'svm': svm_exit_reasons,
+}
+
+exit_reasons = None
+
+for line in file('/proc/cpuinfo').readlines():
+ if line.startswith('flags'):
+ for flag in line.split():
+ if flag in vendor_exit_reasons:
+ exit_reasons = vendor_exit_reasons[flag]
+
+filters = {
+ 'kvm_exit': ('exit_reason', exit_reasons)
+}
+
+def invert(d):
+ return dict((x[1], x[0]) for x in d.iteritems())
+
+for f in filters:
+ filters[f] = (filters[f][0], invert(filters[f][1]))
+
+import ctypes, struct, array
+
+libc = ctypes.CDLL('libc.so.6')
+syscall = libc.syscall
+class perf_event_attr(ctypes.Structure):
+ _fields_ = [('type', ctypes.c_uint32),
+ ('size', ctypes.c_uint32),
+ ('config', ctypes.c_uint64),
+ ('sample_freq', ctypes.c_uint64),
+ ('sample_type', ctypes.c_uint64),
+ ('read_format', ctypes.c_uint64),
+ ('flags', ctypes.c_uint64),
+ ('wakeup_events', ctypes.c_uint32),
+ ('bp_type', ctypes.c_uint32),
+ ('bp_addr', ctypes.c_uint64),
+ ('bp_len', ctypes.c_uint64),
+ ]
+def _perf_event_open(attr, pid, cpu, group_fd, flags):
+ return syscall(298, ctypes.pointer(attr), ctypes.c_int(pid),
+ ctypes.c_int(cpu), ctypes.c_int(group_fd),
+ ctypes.c_long(flags))
+
+PERF_TYPE_HARDWARE = 0
+PERF_TYPE_SOFTWARE = 1
+PERF_TYPE_TRACEPOINT = 2
+PERF_TYPE_HW_CACHE = 3
+PERF_TYPE_RAW = 4
+PERF_TYPE_BREAKPOINT = 5
+
+PERF_SAMPLE_IP = 1 << 0
+PERF_SAMPLE_TID = 1 << 1
+PERF_SAMPLE_TIME = 1 << 2
+PERF_SAMPLE_ADDR = 1 << 3
+PERF_SAMPLE_READ = 1 << 4
+PERF_SAMPLE_CALLCHAIN = 1 << 5
+PERF_SAMPLE_ID = 1 << 6
+PERF_SAMPLE_CPU = 1 << 7
+PERF_SAMPLE_PERIOD = 1 << 8
+PERF_SAMPLE_STREAM_ID = 1 << 9
+PERF_SAMPLE_RAW = 1 << 10
+
+PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0
+PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1
+PERF_FORMAT_ID = 1 << 2
+PERF_FORMAT_GROUP = 1 << 3
+
+import re
+
+sys_tracing = '/sys/kernel/debug/tracing'
+
+class Group(object):
+ def __init__(self, cpu):
+ self.events = []
+ self.group_leader = None
+ self.cpu = cpu
+ def add_event(self, name, event_set, tracepoint, filter = None):
+ self.events.append(Event(group = self,
+ name = name, event_set = event_set,
+ tracepoint = tracepoint, filter = filter))
+ if len(self.events) == 1:
+ self.file = os.fdopen(self.events[0].fd)
+ def read(self):
+ bytes = 8 * (1 + len(self.events))
+ fmt = 'xxxxxxxx' + 'q' * len(self.events)
+ return dict(zip([event.name for event in self.events],
+ struct.unpack(fmt, self.file.read(bytes))))
+
+class Event(object):
+ def __init__(self, group, name, event_set, tracepoint, filter = None):
+ self.name = name
+ attr = perf_event_attr()
+ attr.type = PERF_TYPE_TRACEPOINT
+ attr.size = ctypes.sizeof(attr)
+ id_path = os.path.join(sys_tracing, 'events', event_set,
+ tracepoint, 'id')
+ id = int(file(id_path).read())
+ attr.config = id
+ attr.sample_type = (PERF_SAMPLE_RAW
+ | PERF_SAMPLE_TIME
+ | PERF_SAMPLE_CPU)
+ attr.sample_period = 1
+ attr.read_format = PERF_FORMAT_GROUP
+ group_leader = -1
+ if group.events:
+ group_leader = group.events[0].fd
+ fd = _perf_event_open(attr, -1, group.cpu, group_leader, 0)
+ if fd == -1:
+ raise Exception('perf_event_open failed')
+ if filter:
+ import fcntl
+ fcntl.ioctl(fd, 0x40082406, filter)
+ self.fd = fd
+ def enable(self):
+ import fcntl
+ fcntl.ioctl(self.fd, 0x00002400, 0)
+ def disable(self):
+ import fcntl
+ fcntl.ioctl(self.fd, 0x00002401, 0)
+
+class TracepointProvider(object):
+ def __init__(self):
+ path = os.path.join(sys_tracing, 'events', 'kvm')
+ fields = [f
+ for f in os.listdir(path)
+ if os.path.isdir(os.path.join(path, f))]
+ extra = []
+ for f in fields:
+ if f in filters:
+ subfield, values = filters[f]
+ for name, number in values.iteritems():
+ extra.append(f + '(' + name + ')')
+ fields += extra
+ self._setup(fields)
+ self.select(fields)
+ def fields(self):
+ return self._fields
+ def _setup(self, _fields):
+ self._fields = _fields
+ cpure = r'cpu([0-9]+)'
+ self.cpus = [int(re.match(cpure, x).group(1))
+ for x in os.listdir('/sys/devices/system/cpu')
+ if re.match(cpure, x)]
+ import resource
+ nfiles = len(self.cpus) * 1000
+ resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles))
+ events = []
+ self.group_leaders = []
+ for cpu in self.cpus:
+ group = Group(cpu)
+ for name in _fields:
+ tracepoint = name
+ filter = None
+ m = re.match(r'(.*)\((.*)\)', name)
+ if m:
+ tracepoint, sub = m.groups()
+ filter = '%s==%d\0' % (filters[tracepoint][0],
+ filters[tracepoint][1][sub])
+ event = group.add_event(name, event_set = 'kvm',
+ tracepoint = tracepoint,
+ filter = filter)
+ self.group_leaders.append(group)
+ def select(self, fields):
+ for group in self.group_leaders:
+ for event in group.events:
+ if event.name in fields:
+ event.enable()
+ else:
+ event.disable()
+ def read(self):
+ from collections import defaultdict
+ ret = defaultdict(int)
+ for group in self.group_leaders:
+ for name, val in group.read().iteritems():
+ ret[name] += val
+ return ret
+
+class Stats:
+ def __init__(self, provider, fields = None):
+ self.provider = provider
+ self.fields_filter = fields
+ self._update()
+ def _update(self):
+ def wanted(key):
+ import re
+ if not self.fields_filter:
+ return True
+ return re.match(self.fields_filter, key) is not None
+ self.values = dict([(key, None)
+ for key in provider.fields()
+ if wanted(key)])
+ self.provider.select(self.values.keys())
+ def set_fields_filter(self, fields_filter):
+ self.fields_filter = fields_filter
+ self._update()
+ def get(self):
+ new = self.provider.read()
+ for key in self.provider.fields():
+ oldval = self.values.get(key, (0, 0))
+ newval = new[key]
+ newdelta = None
+ if oldval is not None:
+ newdelta = newval - oldval[0]
+ self.values[key] = (newval, newdelta)
+ return self.values
+
+if not os.access('/sys/kernel/debug', os.F_OK):
+ print 'Please enable CONFIG_DEBUG_FS in your kernel'
+ sys.exit(1)
+if not os.access('/sys/kernel/debug/kvm', os.F_OK):
+ print "Please mount debugfs ('mount -t debugfs debugfs /sys/kernel/debug')"
+ print "and ensure the kvm modules are loaded"
+ sys.exit(1)
+
+label_width = 40
+number_width = 10
+
+def tui(screen, stats):
+ curses.use_default_colors()
+ curses.noecho()
+ drilldown = False
+ fields_filter = stats.fields_filter
+ def update_drilldown():
+ if not fields_filter:
+ if drilldown:
+ stats.set_fields_filter(None)
+ else:
+ stats.set_fields_filter(r'^[^\(]*$')
+ update_drilldown()
+ def refresh(sleeptime):
+ screen.erase()
+ screen.addstr(0, 0, 'kvm statistics')
+ row = 2
+ s = stats.get()
+ def sortkey(x):
+ if s[x][1]:
+ return (-s[x][1], -s[x][0])
+ else:
+ return (0, -s[x][0])
+ for key in sorted(s.keys(), key = sortkey):
+ if row >= screen.getmaxyx()[0]:
+ break
+ values = s[key]
+ if not values[0] and not values[1]:
+ break
+ col = 1
+ screen.addstr(row, col, key)
+ col += label_width
+ screen.addstr(row, col, '%10d' % (values[0],))
+ col += number_width
+ if values[1] is not None:
+ screen.addstr(row, col, '%8d' % (values[1] / sleeptime,))
+ row += 1
+ screen.refresh()
+
+ sleeptime = 0.25
+ while True:
+ refresh(sleeptime)
+ curses.halfdelay(int(sleeptime * 10))
+ sleeptime = 3
+ try:
+ c = screen.getkey()
+ if c == 'x':
+ drilldown = not drilldown
+ update_drilldown()
+ if c == 'q':
+ break
+ except KeyboardInterrupt:
+ break
+ except curses.error:
+ continue
+
+def batch(stats):
+ s = stats.get()
+ time.sleep(1)
+ s = stats.get()
+ for key in sorted(s.keys()):
+ values = s[key]
+ print '%-22s%10d%10d' % (key, values[0], values[1])
+
+def log(stats):
+ keys = sorted(stats.get().iterkeys())
+ def banner():
+ for k in keys:
+ print '%10s' % k[0:9],
+ print
+ def statline():
+ s = stats.get()
+ for k in keys:
+ print ' %9d' % s[k][1],
+ print
+ line = 0
+ banner_repeat = 20
+ while True:
+ time.sleep(1)
+ if line % banner_repeat == 0:
+ banner()
+ statline()
+ line += 1
+
+options = optparse.OptionParser()
+options.add_option('-1', '--once', '--batch',
+ action = 'store_true',
+ default = False,
+ dest = 'once',
+ help = 'run in batch mode for one second',
+ )
+options.add_option('-l', '--log',
+ action = 'store_true',
+ default = False,
+ dest = 'log',
+ help = 'run in logging mode (like vmstat)',
+ )
+options.add_option('-f', '--fields',
+ action = 'store',
+ default = None,
+ dest = 'fields',
+ help = 'fields to display (regex)',
+ )
+(options, args) = options.parse_args(sys.argv)
+
+try:
+ provider = TracepointProvider()
+except:
+ provider = DebugfsProvider()
+
+stats = Stats(provider, fields = options.fields)
+
+if options.log:
+ log(stats)
+elif not options.once:
+ import curses.wrapper
+ curses.wrapper(tui, stats)
+else:
+ batch(stats)
diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap
new file mode 100755
index 0000000000..a74ce71917
--- /dev/null
+++ b/scripts/kvm/vmxcap
@@ -0,0 +1,224 @@
+#!/usr/bin/python
+#
+# tool for querying VMX capabilities
+#
+# Copyright 2009-2010 Red Hat, Inc.
+#
+# Authors:
+# Avi Kivity <avi@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+
+MSR_IA32_VMX_BASIC = 0x480
+MSR_IA32_VMX_PINBASED_CTLS = 0x481
+MSR_IA32_VMX_PROCBASED_CTLS = 0x482
+MSR_IA32_VMX_EXIT_CTLS = 0x483
+MSR_IA32_VMX_ENTRY_CTLS = 0x484
+MSR_IA32_VMX_MISC_CTLS = 0x485
+MSR_IA32_VMX_PROCBASED_CTLS2 = 0x48B
+MSR_IA32_VMX_EPT_VPID_CAP = 0x48C
+MSR_IA32_VMX_TRUE_PINBASED_CTLS = 0x48D
+MSR_IA32_VMX_TRUE_PROCBASED_CTLS = 0x48E
+MSR_IA32_VMX_TRUE_EXIT_CTLS = 0x48F
+MSR_IA32_VMX_TRUE_ENTRY_CTLS = 0x490
+
+class msr(object):
+ def __init__(self):
+ try:
+ self.f = file('/dev/cpu/0/msr')
+ except:
+ self.f = file('/dev/msr0')
+ def read(self, index, default = None):
+ import struct
+ self.f.seek(index)
+ try:
+ return struct.unpack('Q', self.f.read(8))[0]
+ except:
+ return default
+
+class Control(object):
+ def __init__(self, name, bits, cap_msr, true_cap_msr = None):
+ self.name = name
+ self.bits = bits
+ self.cap_msr = cap_msr
+ self.true_cap_msr = true_cap_msr
+ def read2(self, nr):
+ m = msr()
+ val = m.read(nr, 0)
+ return (val & 0xffffffff, val >> 32)
+ def show(self):
+ print self.name
+ mbz, mb1 = self.read2(self.cap_msr)
+ tmbz, tmb1 = 0, 0
+ if self.true_cap_msr:
+ tmbz, tmb1 = self.read2(self.true_cap_msr)
+ for bit in sorted(self.bits.keys()):
+ zero = not (mbz & (1 << bit))
+ one = mb1 & (1 << bit)
+ true_zero = not (tmbz & (1 << bit))
+ true_one = tmb1 & (1 << bit)
+ s= '?'
+ if (self.true_cap_msr and true_zero and true_one
+ and one and not zero):
+ s = 'default'
+ elif zero and not one:
+ s = 'no'
+ elif one and not zero:
+ s = 'forced'
+ elif one and zero:
+ s = 'yes'
+ print ' %-40s %s' % (self.bits[bit], s)
+
+class Misc(object):
+ def __init__(self, name, bits, msr):
+ self.name = name
+ self.bits = bits
+ self.msr = msr
+ def show(self):
+ print self.name
+ value = msr().read(self.msr, 0)
+ def first_bit(key):
+ if type(key) is tuple:
+ return key[0]
+ else:
+ return key
+ for bits in sorted(self.bits.keys(), key = first_bit):
+ if type(bits) is tuple:
+ lo, hi = bits
+ fmt = int
+ else:
+ lo = hi = bits
+ def fmt(x):
+ return { True: 'yes', False: 'no' }[x]
+ v = (value >> lo) & ((1 << (hi - lo + 1)) - 1)
+ print ' %-40s %s' % (self.bits[bits], fmt(v))
+
+controls = [
+ Control(
+ name = 'pin-based controls',
+ bits = {
+ 0: 'External interrupt exiting',
+ 3: 'NMI exiting',
+ 5: 'Virtual NMIs',
+ 6: 'Activate VMX-preemption timer',
+ },
+ cap_msr = MSR_IA32_VMX_PINBASED_CTLS,
+ true_cap_msr = MSR_IA32_VMX_TRUE_PINBASED_CTLS,
+ ),
+
+ Control(
+ name = 'primary processor-based controls',
+ bits = {
+ 2: 'Interrupt window exiting',
+ 3: 'Use TSC offsetting',
+ 7: 'HLT exiting',
+ 9: 'INVLPG exiting',
+ 10: 'MWAIT exiting',
+ 11: 'RDPMC exiting',
+ 12: 'RDTSC exiting',
+ 15: 'CR3-load exiting',
+ 16: 'CR3-store exiting',
+ 19: 'CR8-load exiting',
+ 20: 'CR8-store exiting',
+ 21: 'Use TPR shadow',
+ 22: 'NMI-window exiting',
+ 23: 'MOV-DR exiting',
+ 24: 'Unconditional I/O exiting',
+ 25: 'Use I/O bitmaps',
+ 27: 'Monitor trap flag',
+ 28: 'Use MSR bitmaps',
+ 29: 'MONITOR exiting',
+ 30: 'PAUSE exiting',
+ 31: 'Activate secondary control',
+ },
+ cap_msr = MSR_IA32_VMX_PROCBASED_CTLS,
+ true_cap_msr = MSR_IA32_VMX_TRUE_PROCBASED_CTLS,
+ ),
+
+ Control(
+ name = 'secondary processor-based controls',
+ bits = {
+ 0: 'Virtualize APIC accesses',
+ 1: 'Enable EPT',
+ 2: 'Descriptor-table exiting',
+ 4: 'Virtualize x2APIC mode',
+ 5: 'Enable VPID',
+ 6: 'WBINVD exiting',
+ 7: 'Unrestricted guest',
+ 10: 'PAUSE-loop exiting',
+ },
+ cap_msr = MSR_IA32_VMX_PROCBASED_CTLS2,
+ ),
+
+ Control(
+ name = 'VM-Exit controls',
+ bits = {
+ 2: 'Save debug controls',
+ 9: 'Host address-space size',
+ 12: 'Load IA32_PERF_GLOBAL_CTRL',
+ 15: 'Acknowledge interrupt on exit',
+ 18: 'Save IA32_PAT',
+ 19: 'Load IA32_PAT',
+ 20: 'Save IA32_EFER',
+ 21: 'Load IA32_EFER',
+ 22: 'Save VMX-preemption timer value',
+ },
+ cap_msr = MSR_IA32_VMX_EXIT_CTLS,
+ true_cap_msr = MSR_IA32_VMX_TRUE_EXIT_CTLS,
+ ),
+
+ Control(
+ name = 'VM-Entry controls',
+ bits = {
+ 2: 'Load debug controls',
+ 9: 'IA-64 mode guest',
+ 10: 'Entry to SMM',
+ 11: 'Deactivate dual-monitor treatment',
+ 13: 'Load IA32_PERF_GLOBAL_CTRL',
+ 14: 'Load IA32_PAT',
+ 15: 'Load IA32_EFER',
+ },
+ cap_msr = MSR_IA32_VMX_ENTRY_CTLS,
+ true_cap_msr = MSR_IA32_VMX_TRUE_ENTRY_CTLS,
+ ),
+
+ Misc(
+ name = 'Miscellaneous data',
+ bits = {
+ (0,4): 'VMX-preemption timer scale (log2)',
+ 5: 'Store EFER.LMA into IA-32e mode guest control',
+ 6: 'HLT activity state',
+ 7: 'Shutdown activity state',
+ 8: 'Wait-for-SIPI activity state',
+ (16,24): 'Number of CR3-target values',
+ (25,27): 'MSR-load/store count recommenation',
+ (32,62): 'MSEG revision identifier',
+ },
+ msr = MSR_IA32_VMX_MISC_CTLS,
+ ),
+
+ Misc(
+ name = 'VPID and EPT capabilities',
+ bits = {
+ 0: 'Execute-only EPT translations',
+ 6: 'Page-walk length 4',
+ 8: 'Paging-structure memory type UC',
+ 14: 'Paging-structure memory type WB',
+ 16: '2MB EPT pages',
+ 17: '1GB EPT pages',
+ 20: 'INVEPT supported',
+ 25: 'Single-context INVEPT',
+ 26: 'All-context INVEPT',
+ 32: 'INVVPID supported',
+ 40: 'Individual-address INVVPID',
+ 41: 'Single-context INVVPID',
+ 42: 'All-context INVVPID',
+ 43: 'Single-context-retaining-globals INVVPID',
+ },
+ msr = MSR_IA32_VMX_EPT_VPID_CAP,
+ ),
+ ]
+
+for c in controls:
+ c.show()
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index c947ba4208..f7def16662 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -62,7 +62,9 @@ def gen_sync_call(name, args, ret_type, indent=0):
name=c_var(name), args=arglist, retval=retval).rstrip()
if ret_type:
ret += "\n" + mcgen(''''
-%(marshal_output_call)s
+if (!error_is_set(errp)) {
+ %(marshal_output_call)s
+}
''',
marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip()
pop_indent(indent)
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index a973f2e20c..a08ce9d873 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -300,6 +300,10 @@
#define MSR_IA32_PERF_STATUS 0x198
+#define MSR_IA32_MISC_ENABLE 0x1a0
+/* Indicates good rep/movs microcode on some processors: */
+#define MSR_IA32_MISC_ENABLE_DEFAULT 1
+
#define MSR_MTRRphysBase(reg) (0x200 + 2 * (reg))
#define MSR_MTRRphysMask(reg) (0x200 + 2 * (reg) + 1)
@@ -691,6 +695,7 @@ typedef struct CPUX86State {
uint64_t tsc_deadline;
uint64_t mcg_status;
+ uint64_t msr_ia32_misc_enable;
/* exception/interrupt handling */
int error_code;
@@ -949,7 +954,7 @@ uint64_t cpu_get_tsc(CPUX86State *env);
#define cpu_list_id x86_cpu_list
#define cpudef_setup x86_cpudef_setup
-#define CPU_SAVE_VERSION 13
+#define CPU_SAVE_VERSION 12
/* MMU modes definitions */
#define MMU_MODE0_SUFFIX _kernel
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 5df40d4661..6c6a1675df 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -98,6 +98,7 @@ void cpu_reset(CPUX86State *env)
env->mxcsr = 0x1f80;
env->pat = 0x0007040600070406ULL;
+ env->msr_ia32_misc_enable = MSR_IA32_MISC_ENABLE_DEFAULT;
memset(env->dr, 0, sizeof(env->dr));
env->dr[6] = DR6_FIXED_1;
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 90a6ffba02..ddd115c53c 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -61,6 +61,7 @@ static bool has_msr_star;
static bool has_msr_hsave_pa;
static bool has_msr_tsc_deadline;
static bool has_msr_async_pf_en;
+static bool has_msr_misc_enable;
static int lm_capable_kernel;
static struct kvm_cpuid2 *try_get_cpuid(KVMState *s, int max)
@@ -573,6 +574,10 @@ static int kvm_get_supported_msrs(KVMState *s)
has_msr_tsc_deadline = true;
continue;
}
+ if (kvm_msr_list->indices[i] == MSR_IA32_MISC_ENABLE) {
+ has_msr_misc_enable = true;
+ continue;
+ }
}
}
@@ -889,6 +894,10 @@ static int kvm_put_msrs(CPUState *env, int level)
if (has_msr_tsc_deadline) {
kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSCDEADLINE, env->tsc_deadline);
}
+ if (has_msr_misc_enable) {
+ kvm_msr_entry_set(&msrs[n++], MSR_IA32_MISC_ENABLE,
+ env->msr_ia32_misc_enable);
+ }
#ifdef TARGET_X86_64
if (lm_capable_kernel) {
kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar);
@@ -1138,6 +1147,9 @@ static int kvm_get_msrs(CPUState *env)
if (has_msr_tsc_deadline) {
msrs[n++].index = MSR_IA32_TSCDEADLINE;
}
+ if (has_msr_misc_enable) {
+ msrs[n++].index = MSR_IA32_MISC_ENABLE;
+ }
if (!env->tsc_valid) {
msrs[n++].index = MSR_IA32_TSC;
@@ -1224,6 +1236,9 @@ static int kvm_get_msrs(CPUState *env)
case MSR_MCG_CTL:
env->mcg_ctl = msrs[i].data;
break;
+ case MSR_IA32_MISC_ENABLE:
+ env->msr_ia32_misc_enable = msrs[i].data;
+ break;
default:
if (msrs[i].index >= MSR_MC0_CTL &&
msrs[i].index < MSR_MC0_CTL + (env->mcg_cap & 0xff) * 4) {
diff --git a/target-i386/machine.c b/target-i386/machine.c
index 25fa97de4a..d6e98ff37b 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -310,6 +310,42 @@ static const VMStateDescription vmstate_fpop_ip_dp = {
}
};
+static bool tscdeadline_needed(void *opaque)
+{
+ CPUState *env = opaque;
+
+ return env->tsc_deadline != 0;
+}
+
+static const VMStateDescription vmstate_msr_tscdeadline = {
+ .name = "cpu/msr_tscdeadline",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(tsc_deadline, CPUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static bool misc_enable_needed(void *opaque)
+{
+ CPUState *env = opaque;
+
+ return env->msr_ia32_misc_enable != MSR_IA32_MISC_ENABLE_DEFAULT;
+}
+
+static const VMStateDescription vmstate_msr_ia32_misc_enable = {
+ .name = "cpu/msr_ia32_misc_enable",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField []) {
+ VMSTATE_UINT64(msr_ia32_misc_enable, CPUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static const VMStateDescription vmstate_cpu = {
.name = "cpu",
.version_id = CPU_SAVE_VERSION,
@@ -410,7 +446,6 @@ static const VMStateDescription vmstate_cpu = {
VMSTATE_UINT64_V(xcr0, CPUState, 12),
VMSTATE_UINT64_V(xstate_bv, CPUState, 12),
VMSTATE_YMMH_REGS_VARS(ymmh_regs, CPUState, CPU_NB_REGS, 12),
- VMSTATE_UINT64_V(tsc_deadline, CPUState, 13),
VMSTATE_END_OF_LIST()
/* The above list is not sorted /wrt version numbers, watch out! */
},
@@ -421,6 +456,12 @@ static const VMStateDescription vmstate_cpu = {
} , {
.vmsd = &vmstate_fpop_ip_dp,
.needed = fpop_ip_dp_needed,
+ }, {
+ .vmsd = &vmstate_msr_tscdeadline,
+ .needed = tscdeadline_needed,
+ }, {
+ .vmsd = &vmstate_msr_ia32_misc_enable,
+ .needed = misc_enable_needed,
} , {
/* empty */
}
diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c
index 3bb5a919ce..c89e4a49db 100644
--- a/target-i386/op_helper.c
+++ b/target-i386/op_helper.c
@@ -3280,6 +3280,9 @@ void helper_wrmsr(void)
case MSR_TSC_AUX:
env->tsc_aux = val;
break;
+ case MSR_IA32_MISC_ENABLE:
+ env->msr_ia32_misc_enable = val;
+ break;
default:
if ((uint32_t)ECX >= MSR_MC0_CTL
&& (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
@@ -3413,6 +3416,9 @@ void helper_rdmsr(void)
case MSR_MCG_STATUS:
val = env->mcg_status;
break;
+ case MSR_IA32_MISC_ENABLE:
+ val = env->msr_ia32_misc_enable;
+ break;
default:
if ((uint32_t)ECX >= MSR_MC0_CTL
&& (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h
index 25b4f1a1dc..38a707466c 100644
--- a/target-sparc/cpu.h
+++ b/target-sparc/cpu.h
@@ -3,16 +3,17 @@
#include "config.h"
#include "qemu-common.h"
+#include "bswap.h"
#if !defined(TARGET_SPARC64)
#define TARGET_LONG_BITS 32
-#define TARGET_FPREGS 32
+#define TARGET_DPREGS 16
#define TARGET_PAGE_BITS 12 /* 4k */
#define TARGET_PHYS_ADDR_SPACE_BITS 36
#define TARGET_VIRT_ADDR_SPACE_BITS 32
#else
#define TARGET_LONG_BITS 64
-#define TARGET_FPREGS 64
+#define TARGET_DPREGS 32
#define TARGET_PAGE_BITS 13 /* 8k */
#define TARGET_PHYS_ADDR_SPACE_BITS 41
# ifdef TARGET_ABI32
@@ -395,7 +396,7 @@ typedef struct CPUSPARCState {
uint32_t psr; /* processor state register */
target_ulong fsr; /* FPU state register */
- float32 fpr[TARGET_FPREGS]; /* floating point registers */
+ CPU_DoubleU fpr[TARGET_DPREGS]; /* floating point registers */
uint32_t cwp; /* index of current register window (extracted
from PSR) */
#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
@@ -463,7 +464,6 @@ typedef struct CPUSPARCState {
uint64_t prom_addr;
#endif
/* temporary float registers */
- float64 dt0, dt1;
float128 qt0, qt1;
float_status fp_status;
#if defined(TARGET_SPARC64)
diff --git a/target-sparc/cpu_init.c b/target-sparc/cpu_init.c
index 6954800af0..c7269b54a8 100644
--- a/target-sparc/cpu_init.c
+++ b/target-sparc/cpu_init.c
@@ -813,11 +813,11 @@ void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
}
}
cpu_fprintf(f, "\nFloating Point Registers:\n");
- for (i = 0; i < TARGET_FPREGS; i++) {
+ for (i = 0; i < TARGET_DPREGS; i++) {
if ((i & 3) == 0) {
- cpu_fprintf(f, "%%f%02d:", i);
+ cpu_fprintf(f, "%%f%02d:", i * 2);
}
- cpu_fprintf(f, " %016f", *(float *)&env->fpr[i]);
+ cpu_fprintf(f, " %016" PRIx64, env->fpr[i].ll);
if ((i & 3) == 3) {
cpu_fprintf(f, "\n");
}
diff --git a/target-sparc/fop_helper.c b/target-sparc/fop_helper.c
index 23502f3020..c7a2512117 100644
--- a/target-sparc/fop_helper.c
+++ b/target-sparc/fop_helper.c
@@ -20,26 +20,74 @@
#include "cpu.h"
#include "helper.h"
-#define DT0 (env->dt0)
-#define DT1 (env->dt1)
#define QT0 (env->qt0)
#define QT1 (env->qt1)
+static void check_ieee_exceptions(CPUState *env)
+{
+ target_ulong status;
+
+ status = get_float_exception_flags(&env->fp_status);
+ if (status) {
+ /* Copy IEEE 754 flags into FSR */
+ if (status & float_flag_invalid) {
+ env->fsr |= FSR_NVC;
+ }
+ if (status & float_flag_overflow) {
+ env->fsr |= FSR_OFC;
+ }
+ if (status & float_flag_underflow) {
+ env->fsr |= FSR_UFC;
+ }
+ if (status & float_flag_divbyzero) {
+ env->fsr |= FSR_DZC;
+ }
+ if (status & float_flag_inexact) {
+ env->fsr |= FSR_NXC;
+ }
+
+ if ((env->fsr & FSR_CEXC_MASK) & ((env->fsr & FSR_TEM_MASK) >> 23)) {
+ /* Unmasked exception, generate a trap */
+ env->fsr |= FSR_FTT_IEEE_EXCP;
+ helper_raise_exception(env, TT_FP_EXCP);
+ } else {
+ /* Accumulate exceptions */
+ env->fsr |= (env->fsr & FSR_CEXC_MASK) << 5;
+ }
+ }
+}
+
+static inline void clear_float_exceptions(CPUState *env)
+{
+ set_float_exception_flags(0, &env->fp_status);
+}
+
#define F_HELPER(name, p) void helper_f##name##p(CPUState *env)
#define F_BINOP(name) \
- float32 helper_f ## name ## s (CPUState * env, float32 src1,\
+ float32 helper_f ## name ## s (CPUState *env, float32 src1, \
float32 src2) \
{ \
- return float32_ ## name (src1, src2, &env->fp_status); \
+ float32 ret; \
+ clear_float_exceptions(env); \
+ ret = float32_ ## name (src1, src2, &env->fp_status); \
+ check_ieee_exceptions(env); \
+ return ret; \
} \
- F_HELPER(name, d) \
+ float64 helper_f ## name ## d (CPUState * env, float64 src1,\
+ float64 src2) \
{ \
- DT0 = float64_ ## name (DT0, DT1, &env->fp_status); \
+ float64 ret; \
+ clear_float_exceptions(env); \
+ ret = float64_ ## name (src1, src2, &env->fp_status); \
+ check_ieee_exceptions(env); \
+ return ret; \
} \
F_HELPER(name, q) \
{ \
+ clear_float_exceptions(env); \
QT0 = float128_ ## name (QT0, QT1, &env->fp_status); \
+ check_ieee_exceptions(env); \
}
F_BINOP(add);
@@ -48,18 +96,24 @@ F_BINOP(mul);
F_BINOP(div);
#undef F_BINOP
-void helper_fsmuld(CPUState *env, float32 src1, float32 src2)
+float64 helper_fsmuld(CPUState *env, float32 src1, float32 src2)
{
- DT0 = float64_mul(float32_to_float64(src1, &env->fp_status),
+ float64 ret;
+ clear_float_exceptions(env);
+ ret = float64_mul(float32_to_float64(src1, &env->fp_status),
float32_to_float64(src2, &env->fp_status),
&env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-void helper_fdmulq(CPUState *env)
+void helper_fdmulq(CPUState *env, float64 src1, float64 src2)
{
- QT0 = float128_mul(float64_to_float128(DT0, &env->fp_status),
- float64_to_float128(DT1, &env->fp_status),
+ clear_float_exceptions(env);
+ QT0 = float128_mul(float64_to_float128(src1, &env->fp_status),
+ float64_to_float128(src2, &env->fp_status),
&env->fp_status);
+ check_ieee_exceptions(env);
}
float32 helper_fnegs(float32 src)
@@ -68,9 +122,9 @@ float32 helper_fnegs(float32 src)
}
#ifdef TARGET_SPARC64
-F_HELPER(neg, d)
+float64 helper_fnegd(float64 src)
{
- DT0 = float64_chs(DT1);
+ return float64_chs(src);
}
F_HELPER(neg, q)
@@ -82,98 +136,158 @@ F_HELPER(neg, q)
/* Integer to float conversion. */
float32 helper_fitos(CPUState *env, int32_t src)
{
- return int32_to_float32(src, &env->fp_status);
+ /* Inexact error possible converting int to float. */
+ float32 ret;
+ clear_float_exceptions(env);
+ ret = int32_to_float32(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-void helper_fitod(CPUState *env, int32_t src)
+float64 helper_fitod(CPUState *env, int32_t src)
{
- DT0 = int32_to_float64(src, &env->fp_status);
+ /* No possible exceptions converting int to double. */
+ return int32_to_float64(src, &env->fp_status);
}
void helper_fitoq(CPUState *env, int32_t src)
{
+ /* No possible exceptions converting int to long double. */
QT0 = int32_to_float128(src, &env->fp_status);
}
#ifdef TARGET_SPARC64
-float32 helper_fxtos(CPUState *env)
+float32 helper_fxtos(CPUState *env, int64_t src)
{
- return int64_to_float32(*((int64_t *)&DT1), &env->fp_status);
+ float32 ret;
+ clear_float_exceptions(env);
+ ret = int64_to_float32(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-F_HELPER(xto, d)
+float64 helper_fxtod(CPUState *env, int64_t src)
{
- DT0 = int64_to_float64(*((int64_t *)&DT1), &env->fp_status);
+ float64 ret;
+ clear_float_exceptions(env);
+ ret = int64_to_float64(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-F_HELPER(xto, q)
+void helper_fxtoq(CPUState *env, int64_t src)
{
- QT0 = int64_to_float128(*((int64_t *)&DT1), &env->fp_status);
+ /* No possible exceptions converting long long to long double. */
+ QT0 = int64_to_float128(src, &env->fp_status);
}
#endif
#undef F_HELPER
/* floating point conversion */
-float32 helper_fdtos(CPUState *env)
+float32 helper_fdtos(CPUState *env, float64 src)
{
- return float64_to_float32(DT1, &env->fp_status);
+ float32 ret;
+ clear_float_exceptions(env);
+ ret = float64_to_float32(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-void helper_fstod(CPUState *env, float32 src)
+float64 helper_fstod(CPUState *env, float32 src)
{
- DT0 = float32_to_float64(src, &env->fp_status);
+ float64 ret;
+ clear_float_exceptions(env);
+ ret = float32_to_float64(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
float32 helper_fqtos(CPUState *env)
{
- return float128_to_float32(QT1, &env->fp_status);
+ float32 ret;
+ clear_float_exceptions(env);
+ ret = float128_to_float32(QT1, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
void helper_fstoq(CPUState *env, float32 src)
{
+ clear_float_exceptions(env);
QT0 = float32_to_float128(src, &env->fp_status);
+ check_ieee_exceptions(env);
}
-void helper_fqtod(CPUState *env)
+float64 helper_fqtod(CPUState *env)
{
- DT0 = float128_to_float64(QT1, &env->fp_status);
+ float64 ret;
+ clear_float_exceptions(env);
+ ret = float128_to_float64(QT1, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-void helper_fdtoq(CPUState *env)
+void helper_fdtoq(CPUState *env, float64 src)
{
- QT0 = float64_to_float128(DT1, &env->fp_status);
+ clear_float_exceptions(env);
+ QT0 = float64_to_float128(src, &env->fp_status);
+ check_ieee_exceptions(env);
}
/* Float to integer conversion. */
int32_t helper_fstoi(CPUState *env, float32 src)
{
- return float32_to_int32_round_to_zero(src, &env->fp_status);
+ int32_t ret;
+ clear_float_exceptions(env);
+ ret = float32_to_int32_round_to_zero(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-int32_t helper_fdtoi(CPUState *env)
+int32_t helper_fdtoi(CPUState *env, float64 src)
{
- return float64_to_int32_round_to_zero(DT1, &env->fp_status);
+ int32_t ret;
+ clear_float_exceptions(env);
+ ret = float64_to_int32_round_to_zero(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
int32_t helper_fqtoi(CPUState *env)
{
- return float128_to_int32_round_to_zero(QT1, &env->fp_status);
+ int32_t ret;
+ clear_float_exceptions(env);
+ ret = float128_to_int32_round_to_zero(QT1, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
#ifdef TARGET_SPARC64
-void helper_fstox(CPUState *env, float32 src)
+int64_t helper_fstox(CPUState *env, float32 src)
{
- *((int64_t *)&DT0) = float32_to_int64_round_to_zero(src, &env->fp_status);
+ int64_t ret;
+ clear_float_exceptions(env);
+ ret = float32_to_int64_round_to_zero(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-void helper_fdtox(CPUState *env)
+int64_t helper_fdtox(CPUState *env, float64 src)
{
- *((int64_t *)&DT0) = float64_to_int64_round_to_zero(DT1, &env->fp_status);
+ int64_t ret;
+ clear_float_exceptions(env);
+ ret = float64_to_int64_round_to_zero(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-void helper_fqtox(CPUState *env)
+int64_t helper_fqtox(CPUState *env)
{
- *((int64_t *)&DT0) = float128_to_int64_round_to_zero(QT1, &env->fp_status);
+ int64_t ret;
+ clear_float_exceptions(env);
+ ret = float128_to_int64_round_to_zero(QT1, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
#endif
@@ -183,9 +297,9 @@ float32 helper_fabss(float32 src)
}
#ifdef TARGET_SPARC64
-void helper_fabsd(CPUState *env)
+float64 helper_fabsd(float64 src)
{
- DT0 = float64_abs(DT1);
+ return float64_abs(src);
}
void helper_fabsq(CPUState *env)
@@ -196,17 +310,27 @@ void helper_fabsq(CPUState *env)
float32 helper_fsqrts(CPUState *env, float32 src)
{
- return float32_sqrt(src, &env->fp_status);
+ float32 ret;
+ clear_float_exceptions(env);
+ ret = float32_sqrt(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
-void helper_fsqrtd(CPUState *env)
+float64 helper_fsqrtd(CPUState *env, float64 src)
{
- DT0 = float64_sqrt(DT1, &env->fp_status);
+ float64 ret;
+ clear_float_exceptions(env);
+ ret = float64_sqrt(src, &env->fp_status);
+ check_ieee_exceptions(env);
+ return ret;
}
void helper_fsqrtq(CPUState *env)
{
+ clear_float_exceptions(env);
QT0 = float128_sqrt(QT1, &env->fp_status);
+ check_ieee_exceptions(env);
}
#define GEN_FCMP(name, size, reg1, reg2, FS, E) \
@@ -245,8 +369,8 @@ void helper_fsqrtq(CPUState *env)
break; \
} \
}
-#define GEN_FCMPS(name, size, FS, E) \
- void glue(helper_, name)(CPUState *env, float32 src1, float32 src2) \
+#define GEN_FCMP_T(name, size, FS, E) \
+ void glue(helper_, name)(CPUState *env, size src1, size src2) \
{ \
env->fsr &= FSR_FTT_NMASK; \
if (E && (glue(size, _is_any_nan)(src1) || \
@@ -282,80 +406,42 @@ void helper_fsqrtq(CPUState *env)
} \
}
-GEN_FCMPS(fcmps, float32, 0, 0);
-GEN_FCMP(fcmpd, float64, DT0, DT1, 0, 0);
+GEN_FCMP_T(fcmps, float32, 0, 0);
+GEN_FCMP_T(fcmpd, float64, 0, 0);
-GEN_FCMPS(fcmpes, float32, 0, 1);
-GEN_FCMP(fcmped, float64, DT0, DT1, 0, 1);
+GEN_FCMP_T(fcmpes, float32, 0, 1);
+GEN_FCMP_T(fcmped, float64, 0, 1);
GEN_FCMP(fcmpq, float128, QT0, QT1, 0, 0);
GEN_FCMP(fcmpeq, float128, QT0, QT1, 0, 1);
#ifdef TARGET_SPARC64
-GEN_FCMPS(fcmps_fcc1, float32, 22, 0);
-GEN_FCMP(fcmpd_fcc1, float64, DT0, DT1, 22, 0);
+GEN_FCMP_T(fcmps_fcc1, float32, 22, 0);
+GEN_FCMP_T(fcmpd_fcc1, float64, 22, 0);
GEN_FCMP(fcmpq_fcc1, float128, QT0, QT1, 22, 0);
-GEN_FCMPS(fcmps_fcc2, float32, 24, 0);
-GEN_FCMP(fcmpd_fcc2, float64, DT0, DT1, 24, 0);
+GEN_FCMP_T(fcmps_fcc2, float32, 24, 0);
+GEN_FCMP_T(fcmpd_fcc2, float64, 24, 0);
GEN_FCMP(fcmpq_fcc2, float128, QT0, QT1, 24, 0);
-GEN_FCMPS(fcmps_fcc3, float32, 26, 0);
-GEN_FCMP(fcmpd_fcc3, float64, DT0, DT1, 26, 0);
+GEN_FCMP_T(fcmps_fcc3, float32, 26, 0);
+GEN_FCMP_T(fcmpd_fcc3, float64, 26, 0);
GEN_FCMP(fcmpq_fcc3, float128, QT0, QT1, 26, 0);
-GEN_FCMPS(fcmpes_fcc1, float32, 22, 1);
-GEN_FCMP(fcmped_fcc1, float64, DT0, DT1, 22, 1);
+GEN_FCMP_T(fcmpes_fcc1, float32, 22, 1);
+GEN_FCMP_T(fcmped_fcc1, float64, 22, 1);
GEN_FCMP(fcmpeq_fcc1, float128, QT0, QT1, 22, 1);
-GEN_FCMPS(fcmpes_fcc2, float32, 24, 1);
-GEN_FCMP(fcmped_fcc2, float64, DT0, DT1, 24, 1);
+GEN_FCMP_T(fcmpes_fcc2, float32, 24, 1);
+GEN_FCMP_T(fcmped_fcc2, float64, 24, 1);
GEN_FCMP(fcmpeq_fcc2, float128, QT0, QT1, 24, 1);
-GEN_FCMPS(fcmpes_fcc3, float32, 26, 1);
-GEN_FCMP(fcmped_fcc3, float64, DT0, DT1, 26, 1);
+GEN_FCMP_T(fcmpes_fcc3, float32, 26, 1);
+GEN_FCMP_T(fcmped_fcc3, float64, 26, 1);
GEN_FCMP(fcmpeq_fcc3, float128, QT0, QT1, 26, 1);
#endif
-#undef GEN_FCMPS
-
-void helper_check_ieee_exceptions(CPUState *env)
-{
- target_ulong status;
-
- status = get_float_exception_flags(&env->fp_status);
- if (status) {
- /* Copy IEEE 754 flags into FSR */
- if (status & float_flag_invalid) {
- env->fsr |= FSR_NVC;
- }
- if (status & float_flag_overflow) {
- env->fsr |= FSR_OFC;
- }
- if (status & float_flag_underflow) {
- env->fsr |= FSR_UFC;
- }
- if (status & float_flag_divbyzero) {
- env->fsr |= FSR_DZC;
- }
- if (status & float_flag_inexact) {
- env->fsr |= FSR_NXC;
- }
-
- if ((env->fsr & FSR_CEXC_MASK) & ((env->fsr & FSR_TEM_MASK) >> 23)) {
- /* Unmasked exception, generate a trap */
- env->fsr |= FSR_FTT_IEEE_EXCP;
- helper_raise_exception(env, TT_FP_EXCP);
- } else {
- /* Accumulate exceptions */
- env->fsr |= (env->fsr & FSR_CEXC_MASK) << 5;
- }
- }
-}
-
-void helper_clear_float_exceptions(CPUState *env)
-{
- set_float_exception_flags(0, &env->fp_status);
-}
+#undef GEN_FCMP_T
+#undef GEN_FCMP
static inline void set_fsr(CPUState *env)
{
diff --git a/target-sparc/helper.h b/target-sparc/helper.h
index 615ddefa92..faaf8dc7ad 100644
--- a/target-sparc/helper.h
+++ b/target-sparc/helper.h
@@ -16,8 +16,7 @@ DEF_HELPER_1(rdccr, tl, env)
DEF_HELPER_2(wrccr, void, env, tl)
DEF_HELPER_1(rdcwp, tl, env)
DEF_HELPER_2(wrcwp, void, env, tl)
-DEF_HELPER_3(array8, tl, env, tl, tl)
-DEF_HELPER_3(alignaddr, tl, env, tl, tl)
+DEF_HELPER_FLAGS_2(array8, TCG_CALL_CONST | TCG_CALL_PURE, tl, tl, tl)
DEF_HELPER_1(popc, tl, tl)
DEF_HELPER_3(ldda_asi, void, tl, int, int)
DEF_HELPER_4(ldf_asi, void, tl, int, int, int)
@@ -39,8 +38,6 @@ DEF_HELPER_3(udiv, tl, env, tl, tl)
DEF_HELPER_3(udiv_cc, tl, env, tl, tl)
DEF_HELPER_3(sdiv, tl, env, tl, tl)
DEF_HELPER_3(sdiv_cc, tl, env, tl, tl)
-DEF_HELPER_2(stdf, void, tl, int)
-DEF_HELPER_2(lddf, void, tl, int)
DEF_HELPER_2(ldqf, void, tl, int)
DEF_HELPER_2(stqf, void, tl, int)
#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
@@ -48,33 +45,31 @@ DEF_HELPER_4(ld_asi, i64, tl, int, int, int)
DEF_HELPER_4(st_asi, void, tl, i64, int, int)
#endif
DEF_HELPER_2(ldfsr, void, env, i32)
-DEF_HELPER_1(check_ieee_exceptions, void, env)
-DEF_HELPER_1(clear_float_exceptions, void, env)
-DEF_HELPER_1(fabss, f32, f32)
+DEF_HELPER_FLAGS_1(fabss, TCG_CALL_CONST | TCG_CALL_PURE, f32, f32)
DEF_HELPER_2(fsqrts, f32, env, f32)
-DEF_HELPER_1(fsqrtd, void, env)
+DEF_HELPER_2(fsqrtd, f64, env, f64)
DEF_HELPER_3(fcmps, void, env, f32, f32)
-DEF_HELPER_1(fcmpd, void, env)
+DEF_HELPER_3(fcmpd, void, env, f64, f64)
DEF_HELPER_3(fcmpes, void, env, f32, f32)
-DEF_HELPER_1(fcmped, void, env)
+DEF_HELPER_3(fcmped, void, env, f64, f64)
DEF_HELPER_1(fsqrtq, void, env)
DEF_HELPER_1(fcmpq, void, env)
DEF_HELPER_1(fcmpeq, void, env)
#ifdef TARGET_SPARC64
DEF_HELPER_2(ldxfsr, void, env, i64)
-DEF_HELPER_1(fabsd, void, env)
+DEF_HELPER_FLAGS_1(fabsd, TCG_CALL_CONST | TCG_CALL_PURE, f64, f64)
DEF_HELPER_3(fcmps_fcc1, void, env, f32, f32)
DEF_HELPER_3(fcmps_fcc2, void, env, f32, f32)
DEF_HELPER_3(fcmps_fcc3, void, env, f32, f32)
-DEF_HELPER_1(fcmpd_fcc1, void, env)
-DEF_HELPER_1(fcmpd_fcc2, void, env)
-DEF_HELPER_1(fcmpd_fcc3, void, env)
+DEF_HELPER_3(fcmpd_fcc1, void, env, f64, f64)
+DEF_HELPER_3(fcmpd_fcc2, void, env, f64, f64)
+DEF_HELPER_3(fcmpd_fcc3, void, env, f64, f64)
DEF_HELPER_3(fcmpes_fcc1, void, env, f32, f32)
DEF_HELPER_3(fcmpes_fcc2, void, env, f32, f32)
DEF_HELPER_3(fcmpes_fcc3, void, env, f32, f32)
-DEF_HELPER_1(fcmped_fcc1, void, env)
-DEF_HELPER_1(fcmped_fcc2, void, env)
-DEF_HELPER_1(fcmped_fcc3, void, env)
+DEF_HELPER_3(fcmped_fcc1, void, env, f64, f64)
+DEF_HELPER_3(fcmped_fcc2, void, env, f64, f64)
+DEF_HELPER_3(fcmped_fcc3, void, env, f64, f64)
DEF_HELPER_1(fabsq, void, env)
DEF_HELPER_1(fcmpq_fcc1, void, env)
DEF_HELPER_1(fcmpq_fcc2, void, env)
@@ -86,77 +81,88 @@ DEF_HELPER_1(fcmpeq_fcc3, void, env)
DEF_HELPER_2(raise_exception, void, env, int)
DEF_HELPER_0(shutdown, void)
#define F_HELPER_0_1(name) DEF_HELPER_1(f ## name, void, env)
-#define F_HELPER_DQ_0_1(name) \
- F_HELPER_0_1(name ## d); \
- F_HELPER_0_1(name ## q)
-F_HELPER_DQ_0_1(add);
-F_HELPER_DQ_0_1(sub);
-F_HELPER_DQ_0_1(mul);
-F_HELPER_DQ_0_1(div);
+DEF_HELPER_3(faddd, f64, env, f64, f64)
+DEF_HELPER_3(fsubd, f64, env, f64, f64)
+DEF_HELPER_3(fmuld, f64, env, f64, f64)
+DEF_HELPER_3(fdivd, f64, env, f64, f64)
+F_HELPER_0_1(addq)
+F_HELPER_0_1(subq)
+F_HELPER_0_1(mulq)
+F_HELPER_0_1(divq)
DEF_HELPER_3(fadds, f32, env, f32, f32)
DEF_HELPER_3(fsubs, f32, env, f32, f32)
DEF_HELPER_3(fmuls, f32, env, f32, f32)
DEF_HELPER_3(fdivs, f32, env, f32, f32)
-DEF_HELPER_3(fsmuld, void, env, f32, f32)
-F_HELPER_0_1(dmulq);
+DEF_HELPER_3(fsmuld, f64, env, f32, f32)
+DEF_HELPER_3(fdmulq, void, env, f64, f64);
-DEF_HELPER_1(fnegs, f32, f32)
-DEF_HELPER_2(fitod, void, env, s32)
+DEF_HELPER_FLAGS_1(fnegs, TCG_CALL_CONST | TCG_CALL_PURE, f32, f32)
+DEF_HELPER_2(fitod, f64, env, s32)
DEF_HELPER_2(fitoq, void, env, s32)
DEF_HELPER_2(fitos, f32, env, s32)
#ifdef TARGET_SPARC64
-DEF_HELPER_1(fnegd, void, env)
+DEF_HELPER_FLAGS_1(fnegd, TCG_CALL_CONST | TCG_CALL_PURE, f64, f64)
DEF_HELPER_1(fnegq, void, env)
-DEF_HELPER_1(fxtos, i32, env)
-F_HELPER_DQ_0_1(xto);
+DEF_HELPER_2(fxtos, f32, env, s64)
+DEF_HELPER_2(fxtod, f64, env, s64)
+DEF_HELPER_2(fxtoq, void, env, s64)
#endif
-DEF_HELPER_1(fdtos, f32, env)
-DEF_HELPER_2(fstod, void, env, f32)
+DEF_HELPER_2(fdtos, f32, env, f64)
+DEF_HELPER_2(fstod, f64, env, f32)
DEF_HELPER_1(fqtos, f32, env)
DEF_HELPER_2(fstoq, void, env, f32)
-F_HELPER_0_1(qtod);
-F_HELPER_0_1(dtoq);
+DEF_HELPER_1(fqtod, f64, env)
+DEF_HELPER_2(fdtoq, void, env, f64)
DEF_HELPER_2(fstoi, s32, env, f32)
-DEF_HELPER_1(fdtoi, s32, env)
+DEF_HELPER_2(fdtoi, s32, env, f64)
DEF_HELPER_1(fqtoi, s32, env)
#ifdef TARGET_SPARC64
-DEF_HELPER_2(fstox, void, env, i32)
-F_HELPER_0_1(dtox);
-F_HELPER_0_1(qtox);
-F_HELPER_0_1(aligndata);
+DEF_HELPER_2(fstox, s64, env, f32)
+DEF_HELPER_2(fdtox, s64, env, f64)
+DEF_HELPER_1(fqtox, s64, env)
-F_HELPER_0_1(pmerge);
-F_HELPER_0_1(mul8x16);
-F_HELPER_0_1(mul8x16al);
-F_HELPER_0_1(mul8x16au);
-F_HELPER_0_1(mul8sux16);
-F_HELPER_0_1(mul8ulx16);
-F_HELPER_0_1(muld8sux16);
-F_HELPER_0_1(muld8ulx16);
-F_HELPER_0_1(expand);
-#define VIS_HELPER(name) \
- F_HELPER_0_1(name##16); \
- DEF_HELPER_3(f ## name ## 16s, i32, env, i32, i32) \
- F_HELPER_0_1(name##32); \
- DEF_HELPER_3(f ## name ## 32s, i32, env, i32, i32)
+DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8x16al, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8x16au, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fmuld8ulx16, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fexpand, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64)
+DEF_HELPER_FLAGS_3(pdist, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64, i64)
+DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64, i64)
+DEF_HELPER_FLAGS_2(fpackfix, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64, i64)
+DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64, i64)
+#define VIS_HELPER(name) \
+ DEF_HELPER_FLAGS_2(f ## name ## 16, TCG_CALL_CONST | TCG_CALL_PURE, \
+ i64, i64, i64) \
+ DEF_HELPER_FLAGS_2(f ## name ## 16s, TCG_CALL_CONST | TCG_CALL_PURE, \
+ i32, i32, i32) \
+ DEF_HELPER_FLAGS_2(f ## name ## 32, TCG_CALL_CONST | TCG_CALL_PURE, \
+ i64, i64, i64) \
+ DEF_HELPER_FLAGS_2(f ## name ## 32s, TCG_CALL_CONST | TCG_CALL_PURE, \
+ i32, i32, i32)
VIS_HELPER(padd);
VIS_HELPER(psub);
-#define VIS_CMPHELPER(name) \
- DEF_HELPER_1(f##name##16, i64, env); \
- DEF_HELPER_1(f##name##32, i64, env)
+#define VIS_CMPHELPER(name) \
+ DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_CONST | TCG_CALL_PURE, \
+ i64, i64, i64) \
+ DEF_HELPER_FLAGS_2(f##name##32, TCG_CALL_CONST | TCG_CALL_PURE, \
+ i64, i64, i64)
VIS_CMPHELPER(cmpgt);
VIS_CMPHELPER(cmpeq);
VIS_CMPHELPER(cmple);
VIS_CMPHELPER(cmpne);
#endif
#undef F_HELPER_0_1
-#undef F_HELPER_DQ_0_1
#undef VIS_HELPER
#undef VIS_CMPHELPER
DEF_HELPER_1(compute_psr, void, env);
diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c
index 1fb3996fdb..b59707ecd2 100644
--- a/target-sparc/ldst_helper.c
+++ b/target-sparc/ldst_helper.c
@@ -66,8 +66,6 @@
#endif
#endif
-#define DT0 (env->dt0)
-#define DT1 (env->dt1)
#define QT0 (env->qt0)
#define QT1 (env->qt1)
@@ -2047,7 +2045,7 @@ void helper_ldda_asi(target_ulong addr, int asi, int rd)
void helper_ldf_asi(target_ulong addr, int asi, int size, int rd)
{
unsigned int i;
- CPU_DoubleU u;
+ target_ulong val;
helper_check_align(addr, 3);
addr = asi_address_mask(env, asi, addr);
@@ -2062,13 +2060,11 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd)
return;
}
helper_check_align(addr, 0x3f);
- for (i = 0; i < 16; i++) {
- *(uint32_t *)&env->fpr[rd++] = helper_ld_asi(addr, asi & 0x8f, 4,
- 0);
- addr += 4;
+ for (i = 0; i < 8; i++, rd += 2, addr += 8) {
+ env->fpr[rd/2].ll = helper_ld_asi(addr, asi & 0x8f, 8, 0);
}
-
return;
+
case 0x16: /* UA2007 Block load primary, user privilege */
case 0x17: /* UA2007 Block load secondary, user privilege */
case 0x1e: /* UA2007 Block load primary LE, user privilege */
@@ -2082,13 +2078,11 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd)
return;
}
helper_check_align(addr, 0x3f);
- for (i = 0; i < 16; i++) {
- *(uint32_t *)&env->fpr[rd++] = helper_ld_asi(addr, asi & 0x19, 4,
- 0);
- addr += 4;
+ for (i = 0; i < 8; i++, rd += 2, addr += 4) {
+ env->fpr[rd/2].ll = helper_ld_asi(addr, asi & 0x19, 8, 0);
}
-
return;
+
default:
break;
}
@@ -2096,20 +2090,19 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd)
switch (size) {
default:
case 4:
- *((uint32_t *)&env->fpr[rd]) = helper_ld_asi(addr, asi, size, 0);
+ val = helper_ld_asi(addr, asi, size, 0);
+ if (rd & 1) {
+ env->fpr[rd/2].l.lower = val;
+ } else {
+ env->fpr[rd/2].l.upper = val;
+ }
break;
case 8:
- u.ll = helper_ld_asi(addr, asi, size, 0);
- *((uint32_t *)&env->fpr[rd++]) = u.l.upper;
- *((uint32_t *)&env->fpr[rd++]) = u.l.lower;
+ env->fpr[rd/2].ll = helper_ld_asi(addr, asi, size, 0);
break;
case 16:
- u.ll = helper_ld_asi(addr, asi, 8, 0);
- *((uint32_t *)&env->fpr[rd++]) = u.l.upper;
- *((uint32_t *)&env->fpr[rd++]) = u.l.lower;
- u.ll = helper_ld_asi(addr + 8, asi, 8, 0);
- *((uint32_t *)&env->fpr[rd++]) = u.l.upper;
- *((uint32_t *)&env->fpr[rd++]) = u.l.lower;
+ env->fpr[rd/2].ll = helper_ld_asi(addr, asi, 8, 0);
+ env->fpr[rd/2 + 1].ll = helper_ld_asi(addr + 8, asi, 8, 0);
break;
}
}
@@ -2117,8 +2110,7 @@ void helper_ldf_asi(target_ulong addr, int asi, int size, int rd)
void helper_stf_asi(target_ulong addr, int asi, int size, int rd)
{
unsigned int i;
- target_ulong val = 0;
- CPU_DoubleU u;
+ target_ulong val;
helper_check_align(addr, 3);
addr = asi_address_mask(env, asi, addr);
@@ -2135,10 +2127,8 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd)
return;
}
helper_check_align(addr, 0x3f);
- for (i = 0; i < 16; i++) {
- val = *(uint32_t *)&env->fpr[rd++];
- helper_st_asi(addr, val, asi & 0x8f, 4);
- addr += 4;
+ for (i = 0; i < 8; i++, rd += 2, addr += 8) {
+ helper_st_asi(addr, env->fpr[rd/2].ll, asi & 0x8f, 8);
}
return;
@@ -2155,10 +2145,8 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd)
return;
}
helper_check_align(addr, 0x3f);
- for (i = 0; i < 16; i++) {
- val = *(uint32_t *)&env->fpr[rd++];
- helper_st_asi(addr, val, asi & 0x19, 4);
- addr += 4;
+ for (i = 0; i < 8; i++, rd += 2, addr += 8) {
+ helper_st_asi(addr, env->fpr[rd/2].ll, asi & 0x19, 8);
}
return;
@@ -2169,20 +2157,19 @@ void helper_stf_asi(target_ulong addr, int asi, int size, int rd)
switch (size) {
default:
case 4:
- helper_st_asi(addr, *(uint32_t *)&env->fpr[rd], asi, size);
+ if (rd & 1) {
+ val = env->fpr[rd/2].l.lower;
+ } else {
+ val = env->fpr[rd/2].l.upper;
+ }
+ helper_st_asi(addr, val, asi, size);
break;
case 8:
- u.l.upper = *(uint32_t *)&env->fpr[rd++];
- u.l.lower = *(uint32_t *)&env->fpr[rd++];
- helper_st_asi(addr, u.ll, asi, size);
+ helper_st_asi(addr, env->fpr[rd/2].ll, asi, size);
break;
case 16:
- u.l.upper = *(uint32_t *)&env->fpr[rd++];
- u.l.lower = *(uint32_t *)&env->fpr[rd++];
- helper_st_asi(addr, u.ll, asi, 8);
- u.l.upper = *(uint32_t *)&env->fpr[rd++];
- u.l.lower = *(uint32_t *)&env->fpr[rd++];
- helper_st_asi(addr + 8, u.ll, asi, 8);
+ helper_st_asi(addr, env->fpr[rd/2].ll, asi, 8);
+ helper_st_asi(addr + 8, env->fpr[rd/2 + 1].ll, asi, 8);
break;
}
}
@@ -2214,56 +2201,6 @@ target_ulong helper_casx_asi(target_ulong addr, target_ulong val1,
}
#endif /* TARGET_SPARC64 */
-void helper_stdf(target_ulong addr, int mem_idx)
-{
- helper_check_align(addr, 7);
-#if !defined(CONFIG_USER_ONLY)
- switch (mem_idx) {
- case MMU_USER_IDX:
- stfq_user(addr, DT0);
- break;
- case MMU_KERNEL_IDX:
- stfq_kernel(addr, DT0);
- break;
-#ifdef TARGET_SPARC64
- case MMU_HYPV_IDX:
- stfq_hypv(addr, DT0);
- break;
-#endif
- default:
- DPRINTF_MMU("helper_stdf: need to check MMU idx %d\n", mem_idx);
- break;
- }
-#else
- stfq_raw(address_mask(env, addr), DT0);
-#endif
-}
-
-void helper_lddf(target_ulong addr, int mem_idx)
-{
- helper_check_align(addr, 7);
-#if !defined(CONFIG_USER_ONLY)
- switch (mem_idx) {
- case MMU_USER_IDX:
- DT0 = ldfq_user(addr);
- break;
- case MMU_KERNEL_IDX:
- DT0 = ldfq_kernel(addr);
- break;
-#ifdef TARGET_SPARC64
- case MMU_HYPV_IDX:
- DT0 = ldfq_hypv(addr);
- break;
-#endif
- default:
- DPRINTF_MMU("helper_lddf: need to check MMU idx %d\n", mem_idx);
- break;
- }
-#else
- DT0 = ldfq_raw(address_mask(env, addr));
-#endif
-}
-
void helper_ldqf(target_ulong addr, int mem_idx)
{
/* XXX add 128 bit load */
diff --git a/target-sparc/machine.c b/target-sparc/machine.c
index 56ae0412cd..235b088a45 100644
--- a/target-sparc/machine.c
+++ b/target-sparc/machine.c
@@ -21,13 +21,9 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_betls(f, &env->regbase[i]);
/* FPU */
- for(i = 0; i < TARGET_FPREGS; i++) {
- union {
- float32 f;
- uint32_t i;
- } u;
- u.f = env->fpr[i];
- qemu_put_be32(f, u.i);
+ for (i = 0; i < TARGET_DPREGS; i++) {
+ qemu_put_be32(f, env->fpr[i].l.upper);
+ qemu_put_be32(f, env->fpr[i].l.lower);
}
qemu_put_betls(f, &env->pc);
@@ -128,13 +124,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
qemu_get_betls(f, &env->regbase[i]);
/* FPU */
- for(i = 0; i < TARGET_FPREGS; i++) {
- union {
- float32 f;
- uint32_t i;
- } u;
- u.i = qemu_get_be32(f);
- env->fpr[i] = u.f;
+ for (i = 0; i < TARGET_DPREGS; i++) {
+ env->fpr[i].l.upper = qemu_get_be32(f);
+ env->fpr[i].l.lower = qemu_get_be32(f);
}
qemu_get_betls(f, &env->pc);
diff --git a/target-sparc/translate.c b/target-sparc/translate.c
index 383fd9ce26..93185402fd 100644
--- a/target-sparc/translate.c
+++ b/target-sparc/translate.c
@@ -63,7 +63,7 @@ static TCGv cpu_tmp0;
static TCGv_i32 cpu_tmp32;
static TCGv_i64 cpu_tmp64;
/* Floating point registers */
-static TCGv_i32 cpu_fpr[TARGET_FPREGS];
+static TCGv_i64 cpu_fpr[TARGET_DPREGS];
static target_ulong gen_opc_npc[OPC_BUF_SIZE];
static target_ulong gen_opc_jump_pc[2];
@@ -82,6 +82,8 @@ typedef struct DisasContext {
uint32_t cc_op; /* current CC operation */
struct TranslationBlock *tb;
sparc_def_t *def;
+ TCGv_i32 t32[3];
+ int n_t32;
} DisasContext;
// This function uses non-native bit order
@@ -114,67 +116,116 @@ static int sign_extend(int x, int len)
#define IS_IMM (insn & (1<<13))
+static inline void gen_update_fprs_dirty(int rd)
+{
+#if defined(TARGET_SPARC64)
+ tcg_gen_ori_i32(cpu_fprs, cpu_fprs, (rd < 32) ? 1 : 2);
+#endif
+}
+
/* floating point registers moves */
-static void gen_op_load_fpr_DT0(unsigned int src)
+static TCGv_i32 gen_load_fpr_F(DisasContext *dc, unsigned int src)
+{
+#if TCG_TARGET_REG_BITS == 32
+ if (src & 1) {
+ return TCGV_LOW(cpu_fpr[src / 2]);
+ } else {
+ return TCGV_HIGH(cpu_fpr[src / 2]);
+ }
+#else
+ if (src & 1) {
+ return MAKE_TCGV_I32(GET_TCGV_I64(cpu_fpr[src / 2]));
+ } else {
+ TCGv_i32 ret = tcg_temp_local_new_i32();
+ TCGv_i64 t = tcg_temp_new_i64();
+
+ tcg_gen_shri_i64(t, cpu_fpr[src / 2], 32);
+ tcg_gen_trunc_i64_i32(ret, t);
+ tcg_temp_free_i64(t);
+
+ dc->t32[dc->n_t32++] = ret;
+ assert(dc->n_t32 <= ARRAY_SIZE(dc->t32));
+
+ return ret;
+ }
+#endif
+}
+
+static void gen_store_fpr_F(DisasContext *dc, unsigned int dst, TCGv_i32 v)
+{
+#if TCG_TARGET_REG_BITS == 32
+ if (dst & 1) {
+ tcg_gen_mov_i32(TCGV_LOW(cpu_fpr[dst / 2]), v);
+ } else {
+ tcg_gen_mov_i32(TCGV_HIGH(cpu_fpr[dst / 2]), v);
+ }
+#else
+ TCGv_i64 t = MAKE_TCGV_I64(GET_TCGV_I32(v));
+ tcg_gen_deposit_i64(cpu_fpr[dst / 2], cpu_fpr[dst / 2], t,
+ (dst & 1 ? 0 : 32), 32);
+#endif
+ gen_update_fprs_dirty(dst);
+}
+
+static TCGv_i32 gen_dest_fpr_F(void)
+{
+ return cpu_tmp32;
+}
+
+static TCGv_i64 gen_load_fpr_D(DisasContext *dc, unsigned int src)
{
- tcg_gen_st_i32(cpu_fpr[src], cpu_env, offsetof(CPUSPARCState, dt0) +
- offsetof(CPU_DoubleU, l.upper));
- tcg_gen_st_i32(cpu_fpr[src + 1], cpu_env, offsetof(CPUSPARCState, dt0) +
- offsetof(CPU_DoubleU, l.lower));
+ src = DFPREG(src);
+ return cpu_fpr[src / 2];
}
-static void gen_op_load_fpr_DT1(unsigned int src)
+static void gen_store_fpr_D(DisasContext *dc, unsigned int dst, TCGv_i64 v)
{
- tcg_gen_st_i32(cpu_fpr[src], cpu_env, offsetof(CPUSPARCState, dt1) +
- offsetof(CPU_DoubleU, l.upper));
- tcg_gen_st_i32(cpu_fpr[src + 1], cpu_env, offsetof(CPUSPARCState, dt1) +
- offsetof(CPU_DoubleU, l.lower));
+ dst = DFPREG(dst);
+ tcg_gen_mov_i64(cpu_fpr[dst / 2], v);
+ gen_update_fprs_dirty(dst);
}
-static void gen_op_store_DT0_fpr(unsigned int dst)
+static TCGv_i64 gen_dest_fpr_D(void)
{
- tcg_gen_ld_i32(cpu_fpr[dst], cpu_env, offsetof(CPUSPARCState, dt0) +
- offsetof(CPU_DoubleU, l.upper));
- tcg_gen_ld_i32(cpu_fpr[dst + 1], cpu_env, offsetof(CPUSPARCState, dt0) +
- offsetof(CPU_DoubleU, l.lower));
+ return cpu_tmp64;
}
static void gen_op_load_fpr_QT0(unsigned int src)
{
- tcg_gen_st_i32(cpu_fpr[src], cpu_env, offsetof(CPUSPARCState, qt0) +
- offsetof(CPU_QuadU, l.upmost));
- tcg_gen_st_i32(cpu_fpr[src + 1], cpu_env, offsetof(CPUSPARCState, qt0) +
- offsetof(CPU_QuadU, l.upper));
- tcg_gen_st_i32(cpu_fpr[src + 2], cpu_env, offsetof(CPUSPARCState, qt0) +
- offsetof(CPU_QuadU, l.lower));
- tcg_gen_st_i32(cpu_fpr[src + 3], cpu_env, offsetof(CPUSPARCState, qt0) +
- offsetof(CPU_QuadU, l.lowest));
+ tcg_gen_st_i64(cpu_fpr[src / 2], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, ll.upper));
+ tcg_gen_st_i64(cpu_fpr[src/2 + 1], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, ll.lower));
}
static void gen_op_load_fpr_QT1(unsigned int src)
{
- tcg_gen_st_i32(cpu_fpr[src], cpu_env, offsetof(CPUSPARCState, qt1) +
- offsetof(CPU_QuadU, l.upmost));
- tcg_gen_st_i32(cpu_fpr[src + 1], cpu_env, offsetof(CPUSPARCState, qt1) +
- offsetof(CPU_QuadU, l.upper));
- tcg_gen_st_i32(cpu_fpr[src + 2], cpu_env, offsetof(CPUSPARCState, qt1) +
- offsetof(CPU_QuadU, l.lower));
- tcg_gen_st_i32(cpu_fpr[src + 3], cpu_env, offsetof(CPUSPARCState, qt1) +
- offsetof(CPU_QuadU, l.lowest));
+ tcg_gen_st_i64(cpu_fpr[src / 2], cpu_env, offsetof(CPUSPARCState, qt1) +
+ offsetof(CPU_QuadU, ll.upper));
+ tcg_gen_st_i64(cpu_fpr[src/2 + 1], cpu_env, offsetof(CPUSPARCState, qt1) +
+ offsetof(CPU_QuadU, ll.lower));
}
static void gen_op_store_QT0_fpr(unsigned int dst)
{
- tcg_gen_ld_i32(cpu_fpr[dst], cpu_env, offsetof(CPUSPARCState, qt0) +
- offsetof(CPU_QuadU, l.upmost));
- tcg_gen_ld_i32(cpu_fpr[dst + 1], cpu_env, offsetof(CPUSPARCState, qt0) +
- offsetof(CPU_QuadU, l.upper));
- tcg_gen_ld_i32(cpu_fpr[dst + 2], cpu_env, offsetof(CPUSPARCState, qt0) +
- offsetof(CPU_QuadU, l.lower));
- tcg_gen_ld_i32(cpu_fpr[dst + 3], cpu_env, offsetof(CPUSPARCState, qt0) +
- offsetof(CPU_QuadU, l.lowest));
+ tcg_gen_ld_i64(cpu_fpr[dst / 2], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, ll.upper));
+ tcg_gen_ld_i64(cpu_fpr[dst/2 + 1], cpu_env, offsetof(CPUSPARCState, qt0) +
+ offsetof(CPU_QuadU, ll.lower));
}
+#ifdef TARGET_SPARC64
+static void gen_move_Q(unsigned int rd, unsigned int rs)
+{
+ rd = QFPREG(rd);
+ rs = QFPREG(rs);
+
+ tcg_gen_mov_i64(cpu_fpr[rd / 2], cpu_fpr[rs / 2]);
+ tcg_gen_mov_i64(cpu_fpr[rd / 2 + 1], cpu_fpr[rs / 2 + 1]);
+ gen_update_fprs_dirty(rd);
+}
+#endif
+
/* moves */
#ifdef CONFIG_USER_ONLY
#define supervisor(dc) 0
@@ -1419,20 +1470,20 @@ static inline void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2)
}
}
-static inline void gen_op_fcmpd(int fccno)
+static inline void gen_op_fcmpd(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2)
{
switch (fccno) {
case 0:
- gen_helper_fcmpd(cpu_env);
+ gen_helper_fcmpd(cpu_env, r_rs1, r_rs2);
break;
case 1:
- gen_helper_fcmpd_fcc1(cpu_env);
+ gen_helper_fcmpd_fcc1(cpu_env, r_rs1, r_rs2);
break;
case 2:
- gen_helper_fcmpd_fcc2(cpu_env);
+ gen_helper_fcmpd_fcc2(cpu_env, r_rs1, r_rs2);
break;
case 3:
- gen_helper_fcmpd_fcc3(cpu_env);
+ gen_helper_fcmpd_fcc3(cpu_env, r_rs1, r_rs2);
break;
}
}
@@ -1473,20 +1524,20 @@ static inline void gen_op_fcmpes(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2)
}
}
-static inline void gen_op_fcmped(int fccno)
+static inline void gen_op_fcmped(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2)
{
switch (fccno) {
case 0:
- gen_helper_fcmped(cpu_env);
+ gen_helper_fcmped(cpu_env, r_rs1, r_rs2);
break;
case 1:
- gen_helper_fcmped_fcc1(cpu_env);
+ gen_helper_fcmped_fcc1(cpu_env, r_rs1, r_rs2);
break;
case 2:
- gen_helper_fcmped_fcc2(cpu_env);
+ gen_helper_fcmped_fcc2(cpu_env, r_rs1, r_rs2);
break;
case 3:
- gen_helper_fcmped_fcc3(cpu_env);
+ gen_helper_fcmped_fcc3(cpu_env, r_rs1, r_rs2);
break;
}
}
@@ -1516,9 +1567,9 @@ static inline void gen_op_fcmps(int fccno, TCGv r_rs1, TCGv r_rs2)
gen_helper_fcmps(cpu_env, r_rs1, r_rs2);
}
-static inline void gen_op_fcmpd(int fccno)
+static inline void gen_op_fcmpd(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2)
{
- gen_helper_fcmpd(cpu_env);
+ gen_helper_fcmpd(cpu_env, r_rs1, r_rs2);
}
static inline void gen_op_fcmpq(int fccno)
@@ -1531,9 +1582,9 @@ static inline void gen_op_fcmpes(int fccno, TCGv r_rs1, TCGv r_rs2)
gen_helper_fcmpes(cpu_env, r_rs1, r_rs2);
}
-static inline void gen_op_fcmped(int fccno)
+static inline void gen_op_fcmped(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2)
{
- gen_helper_fcmped(cpu_env);
+ gen_helper_fcmped(cpu_env, r_rs1, r_rs2);
}
static inline void gen_op_fcmpeq(int fccno)
@@ -1570,21 +1621,313 @@ static int gen_trap_ifnofpu(DisasContext *dc, TCGv r_cond)
return 0;
}
-static inline void gen_update_fprs_dirty(int rd)
+static inline void gen_op_clear_ieee_excp_and_FTT(void)
{
-#if defined(TARGET_SPARC64)
- tcg_gen_ori_i32(cpu_fprs, cpu_fprs, (rd < 32) ? 1 : 2);
+ tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK);
+}
+
+static inline void gen_fop_FF(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32))
+{
+ TCGv_i32 dst, src;
+
+ src = gen_load_fpr_F(dc, rs);
+ dst = gen_dest_fpr_F();
+
+ gen(dst, cpu_env, src);
+
+ gen_store_fpr_F(dc, rd, dst);
+}
+
+static inline void gen_ne_fop_FF(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_i32, TCGv_i32))
+{
+ TCGv_i32 dst, src;
+
+ src = gen_load_fpr_F(dc, rs);
+ dst = gen_dest_fpr_F();
+
+ gen(dst, src);
+
+ gen_store_fpr_F(dc, rd, dst);
+}
+
+static inline void gen_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2,
+ void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32))
+{
+ TCGv_i32 dst, src1, src2;
+
+ src1 = gen_load_fpr_F(dc, rs1);
+ src2 = gen_load_fpr_F(dc, rs2);
+ dst = gen_dest_fpr_F();
+
+ gen(dst, cpu_env, src1, src2);
+
+ gen_store_fpr_F(dc, rd, dst);
+}
+
+#ifdef TARGET_SPARC64
+static inline void gen_ne_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2,
+ void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32))
+{
+ TCGv_i32 dst, src1, src2;
+
+ src1 = gen_load_fpr_F(dc, rs1);
+ src2 = gen_load_fpr_F(dc, rs2);
+ dst = gen_dest_fpr_F();
+
+ gen(dst, src1, src2);
+
+ gen_store_fpr_F(dc, rd, dst);
+}
#endif
+
+static inline void gen_fop_DD(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64))
+{
+ TCGv_i64 dst, src;
+
+ src = gen_load_fpr_D(dc, rs);
+ dst = gen_dest_fpr_D();
+
+ gen(dst, cpu_env, src);
+
+ gen_store_fpr_D(dc, rd, dst);
}
-static inline void gen_op_clear_ieee_excp_and_FTT(void)
+#ifdef TARGET_SPARC64
+static inline void gen_ne_fop_DD(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_i64, TCGv_i64))
{
- tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK);
+ TCGv_i64 dst, src;
+
+ src = gen_load_fpr_D(dc, rs);
+ dst = gen_dest_fpr_D();
+
+ gen(dst, src);
+
+ gen_store_fpr_D(dc, rd, dst);
}
+#endif
-static inline void gen_clear_float_exceptions(void)
+static inline void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2,
+ void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i64))
{
- gen_helper_clear_float_exceptions(cpu_env);
+ TCGv_i64 dst, src1, src2;
+
+ src1 = gen_load_fpr_D(dc, rs1);
+ src2 = gen_load_fpr_D(dc, rs2);
+ dst = gen_dest_fpr_D();
+
+ gen(dst, cpu_env, src1, src2);
+
+ gen_store_fpr_D(dc, rd, dst);
+}
+
+#ifdef TARGET_SPARC64
+static inline void gen_ne_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2,
+ void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64))
+{
+ TCGv_i64 dst, src1, src2;
+
+ src1 = gen_load_fpr_D(dc, rs1);
+ src2 = gen_load_fpr_D(dc, rs2);
+ dst = gen_dest_fpr_D();
+
+ gen(dst, src1, src2);
+
+ gen_store_fpr_D(dc, rd, dst);
+}
+
+static inline void gen_gsr_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2,
+ void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64))
+{
+ TCGv_i64 dst, src1, src2;
+
+ src1 = gen_load_fpr_D(dc, rs1);
+ src2 = gen_load_fpr_D(dc, rs2);
+ dst = gen_dest_fpr_D();
+
+ gen(dst, cpu_gsr, src1, src2);
+
+ gen_store_fpr_D(dc, rd, dst);
+}
+
+static inline void gen_ne_fop_DDDD(DisasContext *dc, int rd, int rs1, int rs2,
+ void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64))
+{
+ TCGv_i64 dst, src0, src1, src2;
+
+ src1 = gen_load_fpr_D(dc, rs1);
+ src2 = gen_load_fpr_D(dc, rs2);
+ src0 = gen_load_fpr_D(dc, rd);
+ dst = gen_dest_fpr_D();
+
+ gen(dst, src0, src1, src2);
+
+ gen_store_fpr_D(dc, rd, dst);
+}
+#endif
+
+static inline void gen_fop_QQ(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_ptr))
+{
+ gen_op_load_fpr_QT1(QFPREG(rs));
+
+ gen(cpu_env);
+
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
+}
+
+#ifdef TARGET_SPARC64
+static inline void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_ptr))
+{
+ gen_op_load_fpr_QT1(QFPREG(rs));
+
+ gen(cpu_env);
+
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
+}
+#endif
+
+static inline void gen_fop_QQQ(DisasContext *dc, int rd, int rs1, int rs2,
+ void (*gen)(TCGv_ptr))
+{
+ gen_op_load_fpr_QT0(QFPREG(rs1));
+ gen_op_load_fpr_QT1(QFPREG(rs2));
+
+ gen(cpu_env);
+
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
+}
+
+static inline void gen_fop_DFF(DisasContext *dc, int rd, int rs1, int rs2,
+ void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32, TCGv_i32))
+{
+ TCGv_i64 dst;
+ TCGv_i32 src1, src2;
+
+ src1 = gen_load_fpr_F(dc, rs1);
+ src2 = gen_load_fpr_F(dc, rs2);
+ dst = gen_dest_fpr_D();
+
+ gen(dst, cpu_env, src1, src2);
+
+ gen_store_fpr_D(dc, rd, dst);
+}
+
+static inline void gen_fop_QDD(DisasContext *dc, int rd, int rs1, int rs2,
+ void (*gen)(TCGv_ptr, TCGv_i64, TCGv_i64))
+{
+ TCGv_i64 src1, src2;
+
+ src1 = gen_load_fpr_D(dc, rs1);
+ src2 = gen_load_fpr_D(dc, rs2);
+
+ gen(cpu_env, src1, src2);
+
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
+}
+
+#ifdef TARGET_SPARC64
+static inline void gen_fop_DF(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32))
+{
+ TCGv_i64 dst;
+ TCGv_i32 src;
+
+ src = gen_load_fpr_F(dc, rs);
+ dst = gen_dest_fpr_D();
+
+ gen(dst, cpu_env, src);
+
+ gen_store_fpr_D(dc, rd, dst);
+}
+#endif
+
+static inline void gen_ne_fop_DF(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32))
+{
+ TCGv_i64 dst;
+ TCGv_i32 src;
+
+ src = gen_load_fpr_F(dc, rs);
+ dst = gen_dest_fpr_D();
+
+ gen(dst, cpu_env, src);
+
+ gen_store_fpr_D(dc, rd, dst);
+}
+
+static inline void gen_fop_FD(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i64))
+{
+ TCGv_i32 dst;
+ TCGv_i64 src;
+
+ src = gen_load_fpr_D(dc, rs);
+ dst = gen_dest_fpr_F();
+
+ gen(dst, cpu_env, src);
+
+ gen_store_fpr_F(dc, rd, dst);
+}
+
+static inline void gen_fop_FQ(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_i32, TCGv_ptr))
+{
+ TCGv_i32 dst;
+
+ gen_op_load_fpr_QT1(QFPREG(rs));
+ dst = gen_dest_fpr_F();
+
+ gen(dst, cpu_env);
+
+ gen_store_fpr_F(dc, rd, dst);
+}
+
+static inline void gen_fop_DQ(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_i64, TCGv_ptr))
+{
+ TCGv_i64 dst;
+
+ gen_op_load_fpr_QT1(QFPREG(rs));
+ dst = gen_dest_fpr_D();
+
+ gen(dst, cpu_env);
+
+ gen_store_fpr_D(dc, rd, dst);
+}
+
+static inline void gen_ne_fop_QF(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_ptr, TCGv_i32))
+{
+ TCGv_i32 src;
+
+ src = gen_load_fpr_F(dc, rs);
+
+ gen(cpu_env, src);
+
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
+}
+
+static inline void gen_ne_fop_QD(DisasContext *dc, int rd, int rs,
+ void (*gen)(TCGv_ptr, TCGv_i64))
+{
+ TCGv_i64 src;
+
+ src = gen_load_fpr_D(dc, rs);
+
+ gen(cpu_env, src);
+
+ gen_op_store_QT0_fpr(QFPREG(rd));
+ gen_update_fprs_dirty(QFPREG(rd));
}
/* asi moves */
@@ -1878,6 +2221,148 @@ static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_ptr cpu_env)
tcg_temp_free_i32(r_tl);
}
+
+static void gen_edge(DisasContext *dc, TCGv dst, TCGv s1, TCGv s2,
+ int width, bool cc, bool left)
+{
+ TCGv lo1, lo2, t1, t2;
+ uint64_t amask, tabl, tabr;
+ int shift, imask, omask;
+
+ if (cc) {
+ tcg_gen_mov_tl(cpu_cc_src, s1);
+ tcg_gen_mov_tl(cpu_cc_src2, s2);
+ tcg_gen_sub_tl(cpu_cc_dst, s1, s2);
+ tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB);
+ dc->cc_op = CC_OP_SUB;
+ }
+
+ /* Theory of operation: there are two tables, left and right (not to
+ be confused with the left and right versions of the opcode). These
+ are indexed by the low 3 bits of the inputs. To make things "easy",
+ these tables are loaded into two constants, TABL and TABR below.
+ The operation index = (input & imask) << shift calculates the index
+ into the constant, while val = (table >> index) & omask calculates
+ the value we're looking for. */
+ switch (width) {
+ case 8:
+ imask = 0x7;
+ shift = 3;
+ omask = 0xff;
+ if (left) {
+ tabl = 0x80c0e0f0f8fcfeffULL;
+ tabr = 0xff7f3f1f0f070301ULL;
+ } else {
+ tabl = 0x0103070f1f3f7fffULL;
+ tabr = 0xfffefcf8f0e0c080ULL;
+ }
+ break;
+ case 16:
+ imask = 0x6;
+ shift = 1;
+ omask = 0xf;
+ if (left) {
+ tabl = 0x8cef;
+ tabr = 0xf731;
+ } else {
+ tabl = 0x137f;
+ tabr = 0xfec8;
+ }
+ break;
+ case 32:
+ imask = 0x4;
+ shift = 0;
+ omask = 0x3;
+ if (left) {
+ tabl = (2 << 2) | 3;
+ tabr = (3 << 2) | 1;
+ } else {
+ tabl = (1 << 2) | 3;
+ tabr = (3 << 2) | 2;
+ }
+ break;
+ default:
+ abort();
+ }
+
+ lo1 = tcg_temp_new();
+ lo2 = tcg_temp_new();
+ tcg_gen_andi_tl(lo1, s1, imask);
+ tcg_gen_andi_tl(lo2, s2, imask);
+ tcg_gen_shli_tl(lo1, lo1, shift);
+ tcg_gen_shli_tl(lo2, lo2, shift);
+
+ t1 = tcg_const_tl(tabl);
+ t2 = tcg_const_tl(tabr);
+ tcg_gen_shr_tl(lo1, t1, lo1);
+ tcg_gen_shr_tl(lo2, t2, lo2);
+ tcg_gen_andi_tl(dst, lo1, omask);
+ tcg_gen_andi_tl(lo2, lo2, omask);
+
+ amask = -8;
+ if (AM_CHECK(dc)) {
+ amask &= 0xffffffffULL;
+ }
+ tcg_gen_andi_tl(s1, s1, amask);
+ tcg_gen_andi_tl(s2, s2, amask);
+
+ /* We want to compute
+ dst = (s1 == s2 ? lo1 : lo1 & lo2).
+ We've already done dst = lo1, so this reduces to
+ dst &= (s1 == s2 ? -1 : lo2)
+ Which we perform by
+ lo2 |= -(s1 == s2)
+ dst &= lo2
+ */
+ tcg_gen_setcond_tl(TCG_COND_EQ, t1, s1, s2);
+ tcg_gen_neg_tl(t1, t1);
+ tcg_gen_or_tl(lo2, lo2, t1);
+ tcg_gen_and_tl(dst, dst, lo2);
+
+ tcg_temp_free(lo1);
+ tcg_temp_free(lo2);
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+}
+
+static void gen_alignaddr(TCGv dst, TCGv s1, TCGv s2, bool left)
+{
+ TCGv tmp = tcg_temp_new();
+
+ tcg_gen_add_tl(tmp, s1, s2);
+ tcg_gen_andi_tl(dst, tmp, -8);
+ if (left) {
+ tcg_gen_neg_tl(tmp, tmp);
+ }
+ tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3);
+
+ tcg_temp_free(tmp);
+}
+
+static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2)
+{
+ TCGv t1, t2, shift;
+
+ t1 = tcg_temp_new();
+ t2 = tcg_temp_new();
+ shift = tcg_temp_new();
+
+ tcg_gen_andi_tl(shift, gsr, 7);
+ tcg_gen_shli_tl(shift, shift, 3);
+ tcg_gen_shl_tl(t1, s1, shift);
+
+ /* A shift of 64 does not produce 0 in TCG. Divide this into a
+ shift of (up to 63) followed by a constant shift of 1. */
+ tcg_gen_xori_tl(shift, shift, 63);
+ tcg_gen_shr_tl(t2, s2, shift);
+ tcg_gen_shri_tl(t2, t2, 1);
+
+ tcg_gen_or_tl(dst, t1, t2);
+
+ tcg_temp_free(t1);
+ tcg_temp_free(t2);
+ tcg_temp_free(shift);
+}
#endif
#define CHECK_IU_FEATURE(dc, FEATURE) \
@@ -1892,6 +2377,8 @@ static void disas_sparc_insn(DisasContext * dc)
{
unsigned int insn, opc, rs1, rs2, rd;
TCGv cpu_src1, cpu_src2, cpu_tmp1, cpu_tmp2;
+ TCGv_i32 cpu_src1_32, cpu_src2_32, cpu_dst_32;
+ TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64;
target_long simm;
if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP)))
@@ -2369,350 +2856,162 @@ static void disas_sparc_insn(DisasContext * dc)
save_state(dc, cpu_cond);
switch (xop) {
case 0x1: /* fmovs */
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ cpu_src1_32 = gen_load_fpr_F(dc, rs2);
+ gen_store_fpr_F(dc, rd, cpu_src1_32);
break;
case 0x5: /* fnegs */
- gen_helper_fnegs(cpu_fpr[rd], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FF(dc, rd, rs2, gen_helper_fnegs);
break;
case 0x9: /* fabss */
- gen_helper_fabss(cpu_fpr[rd], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FF(dc, rd, rs2, gen_helper_fabss);
break;
case 0x29: /* fsqrts */
CHECK_FPU_FEATURE(dc, FSQRT);
- gen_clear_float_exceptions();
- gen_helper_fsqrts(cpu_tmp32, cpu_env, cpu_fpr[rs2]);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FF(dc, rd, rs2, gen_helper_fsqrts);
break;
case 0x2a: /* fsqrtd */
CHECK_FPU_FEATURE(dc, FSQRT);
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fsqrtd(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DD(dc, rd, rs2, gen_helper_fsqrtd);
break;
case 0x2b: /* fsqrtq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fsqrtq(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_fop_QQ(dc, rd, rs2, gen_helper_fsqrtq);
break;
case 0x41: /* fadds */
- gen_clear_float_exceptions();
- gen_helper_fadds(cpu_tmp32, cpu_env, cpu_fpr[rs1],
- cpu_fpr[rs2]);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fadds);
break;
case 0x42: /* faddd */
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_faddd(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_faddd);
break;
case 0x43: /* faddq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT0(QFPREG(rs1));
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_faddq(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_faddq);
break;
case 0x45: /* fsubs */
- gen_clear_float_exceptions();
- gen_helper_fsubs(cpu_tmp32, cpu_env, cpu_fpr[rs1],
- cpu_fpr[rs2]);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fsubs);
break;
case 0x46: /* fsubd */
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fsubd(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fsubd);
break;
case 0x47: /* fsubq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT0(QFPREG(rs1));
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fsubq(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fsubq);
break;
case 0x49: /* fmuls */
CHECK_FPU_FEATURE(dc, FMUL);
- gen_clear_float_exceptions();
- gen_helper_fmuls(cpu_tmp32, cpu_env, cpu_fpr[rs1],
- cpu_fpr[rs2]);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fmuls);
break;
case 0x4a: /* fmuld */
CHECK_FPU_FEATURE(dc, FMUL);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fmuld(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld);
break;
case 0x4b: /* fmulq */
CHECK_FPU_FEATURE(dc, FLOAT128);
CHECK_FPU_FEATURE(dc, FMUL);
- gen_op_load_fpr_QT0(QFPREG(rs1));
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fmulq(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fmulq);
break;
case 0x4d: /* fdivs */
- gen_clear_float_exceptions();
- gen_helper_fdivs(cpu_tmp32, cpu_env, cpu_fpr[rs1],
- cpu_fpr[rs2]);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fdivs);
break;
case 0x4e: /* fdivd */
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fdivd(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fdivd);
break;
case 0x4f: /* fdivq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT0(QFPREG(rs1));
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fdivq(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fdivq);
break;
case 0x69: /* fsmuld */
CHECK_FPU_FEATURE(dc, FSMULD);
- gen_clear_float_exceptions();
- gen_helper_fsmuld(cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DFF(dc, rd, rs1, rs2, gen_helper_fsmuld);
break;
case 0x6e: /* fdmulq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fdmulq(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_fop_QDD(dc, rd, rs1, rs2, gen_helper_fdmulq);
break;
case 0xc4: /* fitos */
- gen_clear_float_exceptions();
- gen_helper_fitos(cpu_tmp32, cpu_env, cpu_fpr[rs2]);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FF(dc, rd, rs2, gen_helper_fitos);
break;
case 0xc6: /* fdtos */
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fdtos(cpu_tmp32, cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FD(dc, rd, rs2, gen_helper_fdtos);
break;
case 0xc7: /* fqtos */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fqtos(cpu_tmp32, cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FQ(dc, rd, rs2, gen_helper_fqtos);
break;
case 0xc8: /* fitod */
- gen_helper_fitod(cpu_env, cpu_fpr[rs2]);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DF(dc, rd, rs2, gen_helper_fitod);
break;
case 0xc9: /* fstod */
- gen_helper_fstod(cpu_env, cpu_fpr[rs2]);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DF(dc, rd, rs2, gen_helper_fstod);
break;
case 0xcb: /* fqtod */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fqtod(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DQ(dc, rd, rs2, gen_helper_fqtod);
break;
case 0xcc: /* fitoq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_helper_fitoq(cpu_env, cpu_fpr[rs2]);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_ne_fop_QF(dc, rd, rs2, gen_helper_fitoq);
break;
case 0xcd: /* fstoq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_helper_fstoq(cpu_env, cpu_fpr[rs2]);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_ne_fop_QF(dc, rd, rs2, gen_helper_fstoq);
break;
case 0xce: /* fdtoq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fdtoq(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_ne_fop_QD(dc, rd, rs2, gen_helper_fdtoq);
break;
case 0xd1: /* fstoi */
- gen_clear_float_exceptions();
- gen_helper_fstoi(cpu_tmp32, cpu_env, cpu_fpr[rs2]);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FF(dc, rd, rs2, gen_helper_fstoi);
break;
case 0xd2: /* fdtoi */
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fdtoi(cpu_tmp32, cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FD(dc, rd, rs2, gen_helper_fdtoi);
break;
case 0xd3: /* fqtoi */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fqtoi(cpu_tmp32, cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FQ(dc, rd, rs2, gen_helper_fqtoi);
break;
#ifdef TARGET_SPARC64
case 0x2: /* V9 fmovd */
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]);
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ cpu_src1_64 = gen_load_fpr_D(dc, rs2);
+ gen_store_fpr_D(dc, rd, cpu_src1_64);
break;
case 0x3: /* V9 fmovq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd)], cpu_fpr[QFPREG(rs2)]);
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1],
- cpu_fpr[QFPREG(rs2) + 1]);
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2],
- cpu_fpr[QFPREG(rs2) + 2]);
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3],
- cpu_fpr[QFPREG(rs2) + 3]);
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_move_Q(rd, rs2);
break;
case 0x6: /* V9 fnegd */
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fnegd(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DD(dc, rd, rs2, gen_helper_fnegd);
break;
case 0x7: /* V9 fnegq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_helper_fnegq(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fnegq);
break;
case 0xa: /* V9 fabsd */
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fabsd(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DD(dc, rd, rs2, gen_helper_fabsd);
break;
case 0xb: /* V9 fabsq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_helper_fabsq(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fabsq);
break;
case 0x81: /* V9 fstox */
- gen_clear_float_exceptions();
- gen_helper_fstox(cpu_env, cpu_fpr[rs2]);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DF(dc, rd, rs2, gen_helper_fstox);
break;
case 0x82: /* V9 fdtox */
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fdtox(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DD(dc, rd, rs2, gen_helper_fdtox);
break;
case 0x83: /* V9 fqtox */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_QT1(QFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fqtox(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DQ(dc, rd, rs2, gen_helper_fqtox);
break;
case 0x84: /* V9 fxtos */
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fxtos(cpu_tmp32, cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_tmp32);
- gen_update_fprs_dirty(rd);
+ gen_fop_FD(dc, rd, rs2, gen_helper_fxtos);
break;
case 0x88: /* V9 fxtod */
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fxtod(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_fop_DD(dc, rd, rs2, gen_helper_fxtod);
break;
case 0x8c: /* V9 fxtoq */
CHECK_FPU_FEATURE(dc, FLOAT128);
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_clear_float_exceptions();
- gen_helper_fxtoq(cpu_env);
- gen_helper_check_ieee_exceptions(cpu_env);
- gen_op_store_QT0_fpr(QFPREG(rd));
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_ne_fop_QD(dc, rd, rs2, gen_helper_fxtoq);
break;
#endif
default:
@@ -2738,8 +3037,8 @@ static void disas_sparc_insn(DisasContext * dc)
cpu_src1 = get_src1(insn, cpu_src1);
tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond], cpu_src1,
0, l1);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ cpu_src1_32 = gen_load_fpr_F(dc, rs2);
+ gen_store_fpr_F(dc, rd, cpu_src1_32);
gen_set_label(l1);
break;
} else if ((xop & 0x11f) == 0x006) { // V9 fmovdr
@@ -2750,9 +3049,8 @@ static void disas_sparc_insn(DisasContext * dc)
cpu_src1 = get_src1(insn, cpu_src1);
tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond], cpu_src1,
0, l1);
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]);
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ cpu_src1_64 = gen_load_fpr_D(dc, rs2);
+ gen_store_fpr_D(dc, rd, cpu_src1_64);
gen_set_label(l1);
break;
} else if ((xop & 0x11f) == 0x007) { // V9 fmovqr
@@ -2764,11 +3062,7 @@ static void disas_sparc_insn(DisasContext * dc)
cpu_src1 = get_src1(insn, cpu_src1);
tcg_gen_brcondi_tl(gen_tcg_cond_reg[cond], cpu_src1,
0, l1);
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd)], cpu_fpr[QFPREG(rs2)]);
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1], cpu_fpr[QFPREG(rs2) + 1]);
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2], cpu_fpr[QFPREG(rs2) + 2]);
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], cpu_fpr[QFPREG(rs2) + 3]);
- gen_update_fprs_dirty(QFPREG(rd));
+ gen_move_Q(rd, rs2);
gen_set_label(l1);
break;
}
@@ -2786,8 +3080,8 @@ static void disas_sparc_insn(DisasContext * dc)
gen_fcond(r_cond, fcc, cond); \
tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
0, l1); \
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); \
- gen_update_fprs_dirty(rd); \
+ cpu_src1_32 = gen_load_fpr_F(dc, rs2); \
+ gen_store_fpr_F(dc, rd, cpu_src1_32); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2802,11 +3096,8 @@ static void disas_sparc_insn(DisasContext * dc)
gen_fcond(r_cond, fcc, cond); \
tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
0, l1); \
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], \
- cpu_fpr[DFPREG(rs2)]); \
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], \
- cpu_fpr[DFPREG(rs2) + 1]); \
- gen_update_fprs_dirty(DFPREG(rd)); \
+ cpu_src1_64 = gen_load_fpr_D(dc, rs2); \
+ gen_store_fpr_D(dc, rd, cpu_src1_64); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2821,15 +3112,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_fcond(r_cond, fcc, cond); \
tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
0, l1); \
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd)], \
- cpu_fpr[QFPREG(rs2)]); \
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1], \
- cpu_fpr[QFPREG(rs2) + 1]); \
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2], \
- cpu_fpr[QFPREG(rs2) + 2]); \
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], \
- cpu_fpr[QFPREG(rs2) + 3]); \
- gen_update_fprs_dirty(QFPREG(rd)); \
+ gen_move_Q(rd, rs2); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2887,8 +3170,8 @@ static void disas_sparc_insn(DisasContext * dc)
gen_cond(r_cond, icc, cond, dc); \
tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
0, l1); \
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); \
- gen_update_fprs_dirty(rd); \
+ cpu_src1_32 = gen_load_fpr_F(dc, rs2); \
+ gen_store_fpr_F(dc, rd, cpu_src1_32); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2903,10 +3186,8 @@ static void disas_sparc_insn(DisasContext * dc)
gen_cond(r_cond, icc, cond, dc); \
tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
0, l1); \
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], \
- cpu_fpr[DFPREG(rs2)]); \
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1], \
- cpu_fpr[DFPREG(rs2) + 1]); \
+ cpu_src1_64 = gen_load_fpr_D(dc, rs2); \
+ gen_store_fpr_D(dc, rd, cpu_src1_64); \
gen_update_fprs_dirty(DFPREG(rd)); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
@@ -2922,15 +3203,7 @@ static void disas_sparc_insn(DisasContext * dc)
gen_cond(r_cond, icc, cond, dc); \
tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, \
0, l1); \
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd)], \
- cpu_fpr[QFPREG(rs2)]); \
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 1], \
- cpu_fpr[QFPREG(rs2) + 1]); \
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 2], \
- cpu_fpr[QFPREG(rs2) + 2]); \
- tcg_gen_mov_i32(cpu_fpr[QFPREG(rd) + 3], \
- cpu_fpr[QFPREG(rs2) + 3]); \
- gen_update_fprs_dirty(QFPREG(rd)); \
+ gen_move_Q(rd, rs2); \
gen_set_label(l1); \
tcg_temp_free(r_cond); \
}
@@ -2960,12 +3233,14 @@ static void disas_sparc_insn(DisasContext * dc)
#undef FMOVQCC
#endif
case 0x51: /* fcmps, V9 %fcc */
- gen_op_fcmps(rd & 3, cpu_fpr[rs1], cpu_fpr[rs2]);
+ cpu_src1_32 = gen_load_fpr_F(dc, rs1);
+ cpu_src2_32 = gen_load_fpr_F(dc, rs2);
+ gen_op_fcmps(rd & 3, cpu_src1_32, cpu_src2_32);
break;
case 0x52: /* fcmpd, V9 %fcc */
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_op_fcmpd(rd & 3);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_op_fcmpd(rd & 3, cpu_src1_64, cpu_src2_64);
break;
case 0x53: /* fcmpq, V9 %fcc */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -2974,12 +3249,14 @@ static void disas_sparc_insn(DisasContext * dc)
gen_op_fcmpq(rd & 3);
break;
case 0x55: /* fcmpes, V9 %fcc */
- gen_op_fcmpes(rd & 3, cpu_fpr[rs1], cpu_fpr[rs2]);
+ cpu_src1_32 = gen_load_fpr_F(dc, rs1);
+ cpu_src2_32 = gen_load_fpr_F(dc, rs2);
+ gen_op_fcmpes(rd & 3, cpu_src1_32, cpu_src2_32);
break;
case 0x56: /* fcmped, V9 %fcc */
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_op_fcmped(rd & 3);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_op_fcmped(rd & 3, cpu_src1_64, cpu_src2_64);
break;
case 0x57: /* fcmpeq, V9 %fcc */
CHECK_FPU_FEATURE(dc, FLOAT128);
@@ -3819,31 +4096,101 @@ static void disas_sparc_insn(DisasContext * dc)
switch (opf) {
case 0x000: /* VIS I edge8cc */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 1, 0);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x001: /* VIS II edge8n */
+ CHECK_FPU_FEATURE(dc, VIS2);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 0, 0);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x002: /* VIS I edge8lcc */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 1, 1);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x003: /* VIS II edge8ln */
+ CHECK_FPU_FEATURE(dc, VIS2);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 0, 1);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x004: /* VIS I edge16cc */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 1, 0);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x005: /* VIS II edge16n */
+ CHECK_FPU_FEATURE(dc, VIS2);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 0, 0);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x006: /* VIS I edge16lcc */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 1, 1);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x007: /* VIS II edge16ln */
+ CHECK_FPU_FEATURE(dc, VIS2);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 0, 1);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x008: /* VIS I edge32cc */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 1, 0);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x009: /* VIS II edge32n */
+ CHECK_FPU_FEATURE(dc, VIS2);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 0, 0);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x00a: /* VIS I edge32lcc */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 1, 1);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x00b: /* VIS II edge32ln */
- // XXX
- goto illegal_insn;
+ CHECK_FPU_FEATURE(dc, VIS2);
+ gen_movl_reg_TN(rs1, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 0, 1);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x010: /* VIS I array8 */
CHECK_FPU_FEATURE(dc, VIS1);
cpu_src1 = get_src1(insn, cpu_src1);
gen_movl_reg_TN(rs2, cpu_src2);
- gen_helper_array8(cpu_dst, cpu_env, cpu_src1, cpu_src2);
+ gen_helper_array8(cpu_dst, cpu_src1, cpu_src2);
gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x012: /* VIS I array16 */
CHECK_FPU_FEATURE(dc, VIS1);
cpu_src1 = get_src1(insn, cpu_src1);
gen_movl_reg_TN(rs2, cpu_src2);
- gen_helper_array8(cpu_dst, cpu_env, cpu_src1, cpu_src2);
+ gen_helper_array8(cpu_dst, cpu_src1, cpu_src2);
tcg_gen_shli_i64(cpu_dst, cpu_dst, 1);
gen_movl_TN_reg(rd, cpu_dst);
break;
@@ -3851,7 +4198,7 @@ static void disas_sparc_insn(DisasContext * dc)
CHECK_FPU_FEATURE(dc, VIS1);
cpu_src1 = get_src1(insn, cpu_src1);
gen_movl_reg_TN(rs2, cpu_src2);
- gen_helper_array8(cpu_dst, cpu_env, cpu_src1, cpu_src2);
+ gen_helper_array8(cpu_dst, cpu_src1, cpu_src2);
tcg_gen_shli_i64(cpu_dst, cpu_dst, 2);
gen_movl_TN_reg(rd, cpu_dst);
break;
@@ -3859,424 +4206,317 @@ static void disas_sparc_insn(DisasContext * dc)
CHECK_FPU_FEATURE(dc, VIS1);
cpu_src1 = get_src1(insn, cpu_src1);
gen_movl_reg_TN(rs2, cpu_src2);
- gen_helper_alignaddr(cpu_dst, cpu_env, cpu_src1, cpu_src2);
+ gen_alignaddr(cpu_dst, cpu_src1, cpu_src2, 0);
gen_movl_TN_reg(rd, cpu_dst);
break;
- case 0x019: /* VIS II bmask */
case 0x01a: /* VIS I alignaddrl */
- // XXX
- goto illegal_insn;
+ CHECK_FPU_FEATURE(dc, VIS1);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ gen_movl_reg_TN(rs2, cpu_src2);
+ gen_alignaddr(cpu_dst, cpu_src1, cpu_src2, 1);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
+ case 0x019: /* VIS II bmask */
+ CHECK_FPU_FEATURE(dc, VIS2);
+ cpu_src1 = get_src1(insn, cpu_src1);
+ cpu_src2 = get_src1(insn, cpu_src2);
+ tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2);
+ tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, cpu_dst, 32, 32);
+ gen_movl_TN_reg(rd, cpu_dst);
+ break;
case 0x020: /* VIS I fcmple16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmple16(cpu_dst, cpu_env);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_helper_fcmple16(cpu_dst, cpu_src1_64, cpu_src2_64);
gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x022: /* VIS I fcmpne16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpne16(cpu_dst, cpu_env);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_helper_fcmpne16(cpu_dst, cpu_src1_64, cpu_src2_64);
gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x024: /* VIS I fcmple32 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmple32(cpu_dst, cpu_env);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_helper_fcmple32(cpu_dst, cpu_src1_64, cpu_src2_64);
gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x026: /* VIS I fcmpne32 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpne32(cpu_dst, cpu_env);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_helper_fcmpne32(cpu_dst, cpu_src1_64, cpu_src2_64);
gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x028: /* VIS I fcmpgt16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpgt16(cpu_dst, cpu_env);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_helper_fcmpgt16(cpu_dst, cpu_src1_64, cpu_src2_64);
gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x02a: /* VIS I fcmpeq16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpeq16(cpu_dst, cpu_env);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_helper_fcmpeq16(cpu_dst, cpu_src1_64, cpu_src2_64);
gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x02c: /* VIS I fcmpgt32 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpgt32(cpu_dst, cpu_env);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_helper_fcmpgt32(cpu_dst, cpu_src1_64, cpu_src2_64);
gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x02e: /* VIS I fcmpeq32 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fcmpeq32(cpu_dst, cpu_env);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ cpu_src2_64 = gen_load_fpr_D(dc, rs2);
+ gen_helper_fcmpeq32(cpu_dst, cpu_src1_64, cpu_src2_64);
gen_movl_TN_reg(rd, cpu_dst);
break;
case 0x031: /* VIS I fmul8x16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fmul8x16(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16);
break;
case 0x033: /* VIS I fmul8x16au */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fmul8x16au(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16au);
break;
case 0x035: /* VIS I fmul8x16al */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fmul8x16al(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16al);
break;
case 0x036: /* VIS I fmul8sux16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fmul8sux16(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8sux16);
break;
case 0x037: /* VIS I fmul8ulx16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fmul8ulx16(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8ulx16);
break;
case 0x038: /* VIS I fmuld8sux16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fmuld8sux16(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld8sux16);
break;
case 0x039: /* VIS I fmuld8ulx16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fmuld8ulx16(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld8ulx16);
break;
case 0x03a: /* VIS I fpack32 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpack32);
+ break;
case 0x03b: /* VIS I fpack16 */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs2);
+ cpu_dst_32 = gen_dest_fpr_F();
+ gen_helper_fpack16(cpu_dst_32, cpu_gsr, cpu_src1_64);
+ gen_store_fpr_F(dc, rd, cpu_dst_32);
+ break;
case 0x03d: /* VIS I fpackfix */
+ CHECK_FPU_FEATURE(dc, VIS1);
+ cpu_src1_64 = gen_load_fpr_D(dc, rs2);
+ cpu_dst_32 = gen_dest_fpr_F();
+ gen_helper_fpackfix(cpu_dst_32, cpu_gsr, cpu_src1_64);
+ gen_store_fpr_F(dc, rd, cpu_dst_32);
+ break;
case 0x03e: /* VIS I pdist */
- // XXX
- goto illegal_insn;
+ CHECK_FPU_FEATURE(dc, VIS1);
+ gen_ne_fop_DDDD(dc, rd, rs1, rs2, gen_helper_pdist);
+ break;
case 0x048: /* VIS I faligndata */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_faligndata(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_faligndata);
break;
case 0x04b: /* VIS I fpmerge */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fpmerge(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpmerge);
break;
case 0x04c: /* VIS II bshuffle */
- // XXX
- goto illegal_insn;
+ CHECK_FPU_FEATURE(dc, VIS2);
+ gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_bshuffle);
+ break;
case 0x04d: /* VIS I fexpand */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fexpand(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fexpand);
break;
case 0x050: /* VIS I fpadd16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fpadd16(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpadd16);
break;
case 0x051: /* VIS I fpadd16s */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_helper_fpadd16s(cpu_fpr[rd], cpu_env,
- cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, gen_helper_fpadd16s);
break;
case 0x052: /* VIS I fpadd32 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fpadd32(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpadd32);
break;
case 0x053: /* VIS I fpadd32s */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_helper_fpadd32s(cpu_fpr[rd], cpu_env,
- cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_add_i32);
break;
case 0x054: /* VIS I fpsub16 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fpsub16(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpsub16);
break;
case 0x055: /* VIS I fpsub16s */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_helper_fpsub16s(cpu_fpr[rd], cpu_env,
- cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, gen_helper_fpsub16s);
break;
case 0x056: /* VIS I fpsub32 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs1));
- gen_op_load_fpr_DT1(DFPREG(rs2));
- gen_helper_fpsub32(cpu_env);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpsub32);
break;
case 0x057: /* VIS I fpsub32s */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_helper_fpsub32s(cpu_fpr[rd], cpu_env,
- cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_sub_i32);
break;
case 0x060: /* VIS I fzero */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_movi_i32(cpu_fpr[DFPREG(rd)], 0);
- tcg_gen_movi_i32(cpu_fpr[DFPREG(rd) + 1], 0);
- gen_update_fprs_dirty(DFPREG(rd));
+ cpu_dst_64 = gen_dest_fpr_D();
+ tcg_gen_movi_i64(cpu_dst_64, 0);
+ gen_store_fpr_D(dc, rd, cpu_dst_64);
break;
case 0x061: /* VIS I fzeros */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_movi_i32(cpu_fpr[rd], 0);
- gen_update_fprs_dirty(rd);
+ cpu_dst_32 = gen_dest_fpr_F();
+ tcg_gen_movi_i32(cpu_dst_32, 0);
+ gen_store_fpr_F(dc, rd, cpu_dst_32);
break;
case 0x062: /* VIS I fnor */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_nor_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
- cpu_fpr[DFPREG(rs2)]);
- tcg_gen_nor_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs1) + 1],
- cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nor_i64);
break;
case 0x063: /* VIS I fnors */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_nor_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_nor_i32);
break;
case 0x064: /* VIS I fandnot2 */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_andc_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
- cpu_fpr[DFPREG(rs2)]);
- tcg_gen_andc_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs1) + 1],
- cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_andc_i64);
break;
case 0x065: /* VIS I fandnot2s */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_andc_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_andc_i32);
break;
case 0x066: /* VIS I fnot2 */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_not_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)]);
- tcg_gen_not_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DD(dc, rd, rs2, tcg_gen_not_i64);
break;
case 0x067: /* VIS I fnot2s */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_not_i32(cpu_fpr[rd], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FF(dc, rd, rs2, tcg_gen_not_i32);
break;
case 0x068: /* VIS I fandnot1 */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_andc_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)],
- cpu_fpr[DFPREG(rs1)]);
- tcg_gen_andc_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs2) + 1],
- cpu_fpr[DFPREG(rs1) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_andc_i64);
break;
case 0x069: /* VIS I fandnot1s */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_andc_i32(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs2, rs1, tcg_gen_andc_i32);
break;
case 0x06a: /* VIS I fnot1 */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_not_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)]);
- tcg_gen_not_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs1) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DD(dc, rd, rs1, tcg_gen_not_i64);
break;
case 0x06b: /* VIS I fnot1s */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_not_i32(cpu_fpr[rd], cpu_fpr[rs1]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FF(dc, rd, rs1, tcg_gen_not_i32);
break;
case 0x06c: /* VIS I fxor */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_xor_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
- cpu_fpr[DFPREG(rs2)]);
- tcg_gen_xor_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs1) + 1],
- cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_xor_i64);
break;
case 0x06d: /* VIS I fxors */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_xor_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_xor_i32);
break;
case 0x06e: /* VIS I fnand */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_nand_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
- cpu_fpr[DFPREG(rs2)]);
- tcg_gen_nand_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs1) + 1],
- cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nand_i64);
break;
case 0x06f: /* VIS I fnands */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_nand_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_nand_i32);
break;
case 0x070: /* VIS I fand */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_and_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
- cpu_fpr[DFPREG(rs2)]);
- tcg_gen_and_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs1) + 1],
- cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_and_i64);
break;
case 0x071: /* VIS I fands */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_and_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_and_i32);
break;
case 0x072: /* VIS I fxnor */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[DFPREG(rs2)], -1);
- tcg_gen_xor_i32(cpu_fpr[DFPREG(rd)], cpu_tmp32,
- cpu_fpr[DFPREG(rs1)]);
- tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[DFPREG(rs2) + 1], -1);
- tcg_gen_xor_i32(cpu_fpr[DFPREG(rd) + 1], cpu_tmp32,
- cpu_fpr[DFPREG(rs1) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_eqv_i64);
break;
case 0x073: /* VIS I fxnors */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_xori_i32(cpu_tmp32, cpu_fpr[rs2], -1);
- tcg_gen_xor_i32(cpu_fpr[rd], cpu_tmp32, cpu_fpr[rs1]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_eqv_i32);
break;
case 0x074: /* VIS I fsrc1 */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)]);
- tcg_gen_mov_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs1) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ cpu_src1_64 = gen_load_fpr_D(dc, rs1);
+ gen_store_fpr_D(dc, rd, cpu_src1_64);
break;
case 0x075: /* VIS I fsrc1s */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs1]);
- gen_update_fprs_dirty(rd);
+ cpu_src1_32 = gen_load_fpr_F(dc, rs1);
+ gen_store_fpr_F(dc, rd, cpu_src1_32);
break;
case 0x076: /* VIS I fornot2 */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_orc_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
- cpu_fpr[DFPREG(rs2)]);
- tcg_gen_orc_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs1) + 1],
- cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_orc_i64);
break;
case 0x077: /* VIS I fornot2s */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_orc_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_orc_i32);
break;
case 0x078: /* VIS I fsrc2 */
CHECK_FPU_FEATURE(dc, VIS1);
- gen_op_load_fpr_DT0(DFPREG(rs2));
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
+ cpu_src1_64 = gen_load_fpr_D(dc, rs2);
+ gen_store_fpr_D(dc, rd, cpu_src1_64);
break;
case 0x079: /* VIS I fsrc2s */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ cpu_src1_32 = gen_load_fpr_F(dc, rs2);
+ gen_store_fpr_F(dc, rd, cpu_src1_32);
break;
case 0x07a: /* VIS I fornot1 */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_orc_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs2)],
- cpu_fpr[DFPREG(rs1)]);
- tcg_gen_orc_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs2) + 1],
- cpu_fpr[DFPREG(rs1) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_orc_i64);
break;
case 0x07b: /* VIS I fornot1s */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_orc_i32(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs2, rs1, tcg_gen_orc_i32);
break;
case 0x07c: /* VIS I for */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_or_i32(cpu_fpr[DFPREG(rd)], cpu_fpr[DFPREG(rs1)],
- cpu_fpr[DFPREG(rs2)]);
- tcg_gen_or_i32(cpu_fpr[DFPREG(rd) + 1],
- cpu_fpr[DFPREG(rs1) + 1],
- cpu_fpr[DFPREG(rs2) + 1]);
- gen_update_fprs_dirty(DFPREG(rd));
+ gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_or_i64);
break;
case 0x07d: /* VIS I fors */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_or_i32(cpu_fpr[rd], cpu_fpr[rs1], cpu_fpr[rs2]);
- gen_update_fprs_dirty(rd);
+ gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_or_i32);
break;
case 0x07e: /* VIS I fone */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_movi_i32(cpu_fpr[DFPREG(rd)], -1);
- tcg_gen_movi_i32(cpu_fpr[DFPREG(rd) + 1], -1);
- gen_update_fprs_dirty(DFPREG(rd));
+ cpu_dst_64 = gen_dest_fpr_D();
+ tcg_gen_movi_i64(cpu_dst_64, -1);
+ gen_store_fpr_D(dc, rd, cpu_dst_64);
break;
case 0x07f: /* VIS I fones */
CHECK_FPU_FEATURE(dc, VIS1);
- tcg_gen_movi_i32(cpu_fpr[rd], -1);
- gen_update_fprs_dirty(rd);
+ cpu_dst_32 = gen_dest_fpr_F();
+ tcg_gen_movi_i32(cpu_dst_32, -1);
+ gen_store_fpr_F(dc, rd, cpu_dst_32);
break;
case 0x080: /* VIS I shutdown */
case 0x081: /* VIS II siam */
@@ -4659,8 +4899,9 @@ static void disas_sparc_insn(DisasContext * dc)
case 0x20: /* ldf, load fpreg */
gen_address_mask(dc, cpu_addr);
tcg_gen_qemu_ld32u(cpu_tmp0, cpu_addr, dc->mem_idx);
- tcg_gen_trunc_tl_i32(cpu_fpr[rd], cpu_tmp0);
- gen_update_fprs_dirty(rd);
+ cpu_dst_32 = gen_dest_fpr_F();
+ tcg_gen_trunc_tl_i32(cpu_dst_32, cpu_tmp0);
+ gen_store_fpr_F(dc, rd, cpu_dst_32);
break;
case 0x21: /* ldfsr, V9 ldxfsr */
#ifdef TARGET_SPARC64
@@ -4694,16 +4935,10 @@ static void disas_sparc_insn(DisasContext * dc)
}
break;
case 0x23: /* lddf, load double fpreg */
- {
- TCGv_i32 r_const;
-
- r_const = tcg_const_i32(dc->mem_idx);
- gen_address_mask(dc, cpu_addr);
- gen_helper_lddf(cpu_addr, r_const);
- tcg_temp_free_i32(r_const);
- gen_op_store_DT0_fpr(DFPREG(rd));
- gen_update_fprs_dirty(DFPREG(rd));
- }
+ gen_address_mask(dc, cpu_addr);
+ cpu_dst_64 = gen_dest_fpr_D();
+ tcg_gen_qemu_ld64(cpu_dst_64, cpu_addr, dc->mem_idx);
+ gen_store_fpr_D(dc, rd, cpu_dst_64);
break;
default:
goto illegal_insn;
@@ -4810,7 +5045,8 @@ static void disas_sparc_insn(DisasContext * dc)
switch (xop) {
case 0x24: /* stf, store fpreg */
gen_address_mask(dc, cpu_addr);
- tcg_gen_ext_i32_tl(cpu_tmp0, cpu_fpr[rd]);
+ cpu_src1_32 = gen_load_fpr_F(dc, rd);
+ tcg_gen_ext_i32_tl(cpu_tmp0, cpu_src1_32);
tcg_gen_qemu_st32(cpu_tmp0, cpu_addr, dc->mem_idx);
break;
case 0x25: /* stfsr, V9 stxfsr */
@@ -4853,15 +5089,9 @@ static void disas_sparc_insn(DisasContext * dc)
#endif
#endif
case 0x27: /* stdf, store double fpreg */
- {
- TCGv_i32 r_const;
-
- gen_op_load_fpr_DT0(DFPREG(rd));
- r_const = tcg_const_i32(dc->mem_idx);
- gen_address_mask(dc, cpu_addr);
- gen_helper_stdf(cpu_addr, r_const);
- tcg_temp_free_i32(r_const);
- }
+ gen_address_mask(dc, cpu_addr);
+ cpu_src1_64 = gen_load_fpr_D(dc, rd);
+ tcg_gen_qemu_st64(cpu_src1_64, cpu_addr, dc->mem_idx);
break;
default:
goto illegal_insn;
@@ -4996,6 +5226,13 @@ static void disas_sparc_insn(DisasContext * dc)
egress:
tcg_temp_free(cpu_tmp1);
tcg_temp_free(cpu_tmp2);
+ if (dc->n_t32 != 0) {
+ int i;
+ for (i = dc->n_t32 - 1; i >= 0; --i) {
+ tcg_temp_free_i32(dc->t32[i]);
+ }
+ dc->n_t32 = 0;
+ }
}
static inline void gen_intermediate_code_internal(TranslationBlock * tb,
@@ -5095,6 +5332,7 @@ static inline void gen_intermediate_code_internal(TranslationBlock * tb,
tcg_temp_free_i64(cpu_tmp64);
tcg_temp_free_i32(cpu_tmp32);
tcg_temp_free(cpu_tmp0);
+
if (tb->cflags & CF_LAST_IO)
gen_io_end();
if (!dc->is_br) {
@@ -5159,15 +5397,11 @@ void gen_intermediate_code_init(CPUSPARCState *env)
"g6",
"g7",
};
- static const char * const fregnames[64] = {
- "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
- "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
- "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
- "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
- "f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39",
- "f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47",
- "f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55",
- "f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63",
+ static const char * const fregnames[32] = {
+ "f0", "f2", "f4", "f6", "f8", "f10", "f12", "f14",
+ "f16", "f18", "f20", "f22", "f24", "f26", "f28", "f30",
+ "f32", "f34", "f36", "f38", "f40", "f42", "f44", "f46",
+ "f48", "f50", "f52", "f54", "f56", "f58", "f60", "f62",
};
/* init various static tables */
@@ -5237,14 +5471,16 @@ void gen_intermediate_code_init(CPUSPARCState *env)
cpu_tbr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, tbr),
"tbr");
#endif
- for (i = 1; i < 8; i++)
+ for (i = 1; i < 8; i++) {
cpu_gregs[i] = tcg_global_mem_new(TCG_AREG0,
offsetof(CPUState, gregs[i]),
gregnames[i]);
- for (i = 0; i < TARGET_FPREGS; i++)
- cpu_fpr[i] = tcg_global_mem_new_i32(TCG_AREG0,
+ }
+ for (i = 0; i < TARGET_DPREGS; i++) {
+ cpu_fpr[i] = tcg_global_mem_new_i64(TCG_AREG0,
offsetof(CPUState, fpr[i]),
fregnames[i]);
+ }
/* register helpers */
diff --git a/target-sparc/vis_helper.c b/target-sparc/vis_helper.c
index a22c10bb43..a992c293af 100644
--- a/target-sparc/vis_helper.c
+++ b/target-sparc/vis_helper.c
@@ -20,11 +20,6 @@
#include "cpu.h"
#include "helper.h"
-#define DT0 (env->dt0)
-#define DT1 (env->dt1)
-#define QT0 (env->qt0)
-#define QT1 (env->qt1)
-
/* This function uses non-native bit order */
#define GET_FIELD(X, FROM, TO) \
((X) >> (63 - (TO)) & ((1ULL << ((TO) - (FROM) + 1)) - 1))
@@ -33,8 +28,7 @@
#define GET_FIELD_SP(X, FROM, TO) \
GET_FIELD(X, 63 - (TO), 63 - (FROM))
-target_ulong helper_array8(CPUState *env, target_ulong pixel_addr,
- target_ulong cubesize)
+target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize)
{
return (GET_FIELD_SP(pixel_addr, 60, 63) << (17 + 2 * cubesize)) |
(GET_FIELD_SP(pixel_addr, 39, 39 + cubesize - 1) << (17 + cubesize)) |
@@ -47,29 +41,6 @@ target_ulong helper_array8(CPUState *env, target_ulong pixel_addr,
GET_FIELD_SP(pixel_addr, 11, 12);
}
-target_ulong helper_alignaddr(CPUState *env, target_ulong addr,
- target_ulong offset)
-{
- uint64_t tmp;
-
- tmp = addr + offset;
- env->gsr &= ~7ULL;
- env->gsr |= tmp & 7ULL;
- return tmp & ~7ULL;
-}
-
-void helper_faligndata(CPUState *env)
-{
- uint64_t tmp;
-
- tmp = (*((uint64_t *)&DT0)) << ((env->gsr & 7) * 8);
- /* on many architectures a shift of 64 does nothing */
- if ((env->gsr & 7) != 0) {
- tmp |= (*((uint64_t *)&DT1)) >> (64 - (env->gsr & 7) * 8);
- }
- *((uint64_t *)&DT0) = tmp;
-}
-
#ifdef HOST_WORDS_BIGENDIAN
#define VIS_B64(n) b[7 - (n)]
#define VIS_W64(n) w[3 - (n)]
@@ -102,12 +73,12 @@ typedef union {
float32 f;
} VIS32;
-void helper_fpmerge(CPUState *env)
+uint64_t helper_fpmerge(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
- s.d = DT0;
- d.d = DT1;
+ s.ll = src1;
+ d.ll = src2;
/* Reverse calculation order to handle overlap */
d.VIS_B64(7) = s.VIS_B64(3);
@@ -119,16 +90,16 @@ void helper_fpmerge(CPUState *env)
d.VIS_B64(1) = s.VIS_B64(0);
/* d.VIS_B64(0) = d.VIS_B64(0); */
- DT0 = d.d;
+ return d.ll;
}
-void helper_fmul8x16(CPUState *env)
+uint64_t helper_fmul8x16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
- s.d = DT0;
- d.d = DT1;
+ s.ll = src1;
+ d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B64(r); \
@@ -143,16 +114,16 @@ void helper_fmul8x16(CPUState *env)
PMUL(3);
#undef PMUL
- DT0 = d.d;
+ return d.ll;
}
-void helper_fmul8x16al(CPUState *env)
+uint64_t helper_fmul8x16al(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
- s.d = DT0;
- d.d = DT1;
+ s.ll = src1;
+ d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(1) * (int32_t)s.VIS_B64(r); \
@@ -167,16 +138,16 @@ void helper_fmul8x16al(CPUState *env)
PMUL(3);
#undef PMUL
- DT0 = d.d;
+ return d.ll;
}
-void helper_fmul8x16au(CPUState *env)
+uint64_t helper_fmul8x16au(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
- s.d = DT0;
- d.d = DT1;
+ s.ll = src1;
+ d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(0) * (int32_t)s.VIS_B64(r); \
@@ -191,16 +162,16 @@ void helper_fmul8x16au(CPUState *env)
PMUL(3);
#undef PMUL
- DT0 = d.d;
+ return d.ll;
}
-void helper_fmul8sux16(CPUState *env)
+uint64_t helper_fmul8sux16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
- s.d = DT0;
- d.d = DT1;
+ s.ll = src1;
+ d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
@@ -215,16 +186,16 @@ void helper_fmul8sux16(CPUState *env)
PMUL(3);
#undef PMUL
- DT0 = d.d;
+ return d.ll;
}
-void helper_fmul8ulx16(CPUState *env)
+uint64_t helper_fmul8ulx16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
- s.d = DT0;
- d.d = DT1;
+ s.ll = src1;
+ d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
@@ -239,16 +210,16 @@ void helper_fmul8ulx16(CPUState *env)
PMUL(3);
#undef PMUL
- DT0 = d.d;
+ return d.ll;
}
-void helper_fmuld8sux16(CPUState *env)
+uint64_t helper_fmuld8sux16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
- s.d = DT0;
- d.d = DT1;
+ s.ll = src1;
+ d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
@@ -262,16 +233,16 @@ void helper_fmuld8sux16(CPUState *env)
PMUL(0);
#undef PMUL
- DT0 = d.d;
+ return d.ll;
}
-void helper_fmuld8ulx16(CPUState *env)
+uint64_t helper_fmuld8ulx16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
- s.d = DT0;
- d.d = DT1;
+ s.ll = src1;
+ d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
@@ -285,42 +256,41 @@ void helper_fmuld8ulx16(CPUState *env)
PMUL(0);
#undef PMUL
- DT0 = d.d;
+ return d.ll;
}
-void helper_fexpand(CPUState *env)
+uint64_t helper_fexpand(uint64_t src1, uint64_t src2)
{
VIS32 s;
VIS64 d;
- s.l = (uint32_t)(*(uint64_t *)&DT0 & 0xffffffff);
- d.d = DT1;
+ s.l = (uint32_t)src1;
+ d.ll = src2;
d.VIS_W64(0) = s.VIS_B32(0) << 4;
d.VIS_W64(1) = s.VIS_B32(1) << 4;
d.VIS_W64(2) = s.VIS_B32(2) << 4;
d.VIS_W64(3) = s.VIS_B32(3) << 4;
- DT0 = d.d;
+ return d.ll;
}
#define VIS_HELPER(name, F) \
- void name##16(CPUState *env) \
+ uint64_t name##16(uint64_t src1, uint64_t src2) \
{ \
VIS64 s, d; \
\
- s.d = DT0; \
- d.d = DT1; \
+ s.ll = src1; \
+ d.ll = src2; \
\
d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0)); \
d.VIS_W64(1) = F(d.VIS_W64(1), s.VIS_W64(1)); \
d.VIS_W64(2) = F(d.VIS_W64(2), s.VIS_W64(2)); \
d.VIS_W64(3) = F(d.VIS_W64(3), s.VIS_W64(3)); \
\
- DT0 = d.d; \
+ return d.ll; \
} \
\
- uint32_t name##16s(CPUState *env, uint32_t src1, \
- uint32_t src2) \
+ uint32_t name##16s(uint32_t src1, uint32_t src2) \
{ \
VIS32 s, d; \
\
@@ -333,21 +303,20 @@ void helper_fexpand(CPUState *env)
return d.l; \
} \
\
- void name##32(CPUState *env) \
+ uint64_t name##32(uint64_t src1, uint64_t src2) \
{ \
VIS64 s, d; \
\
- s.d = DT0; \
- d.d = DT1; \
+ s.ll = src1; \
+ d.ll = src2; \
\
d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0)); \
d.VIS_L64(1) = F(d.VIS_L64(1), s.VIS_L64(1)); \
\
- DT0 = d.d; \
+ return d.ll; \
} \
\
- uint32_t name##32s(CPUState *env, uint32_t src1, \
- uint32_t src2) \
+ uint32_t name##32s(uint32_t src1, uint32_t src2) \
{ \
VIS32 s, d; \
\
@@ -365,12 +334,12 @@ VIS_HELPER(helper_fpadd, FADD)
VIS_HELPER(helper_fpsub, FSUB)
#define VIS_CMPHELPER(name, F) \
- uint64_t name##16(CPUState *env) \
+ uint64_t name##16(uint64_t src1, uint64_t src2) \
{ \
VIS64 s, d; \
\
- s.d = DT0; \
- d.d = DT1; \
+ s.ll = src1; \
+ d.ll = src2; \
\
d.VIS_W64(0) = F(s.VIS_W64(0), d.VIS_W64(0)) ? 1 : 0; \
d.VIS_W64(0) |= F(s.VIS_W64(1), d.VIS_W64(1)) ? 2 : 0; \
@@ -381,12 +350,12 @@ VIS_HELPER(helper_fpsub, FSUB)
return d.ll; \
} \
\
- uint64_t name##32(CPUState *env) \
+ uint64_t name##32(uint64_t src1, uint64_t src2) \
{ \
VIS64 s, d; \
\
- s.d = DT0; \
- d.d = DT1; \
+ s.ll = src1; \
+ d.ll = src2; \
\
d.VIS_L64(0) = F(s.VIS_L64(0), d.VIS_L64(0)) ? 1 : 0; \
d.VIS_L64(0) |= F(s.VIS_L64(1), d.VIS_L64(1)) ? 2 : 0; \
@@ -404,3 +373,117 @@ VIS_CMPHELPER(helper_fcmpgt, FCMPGT)
VIS_CMPHELPER(helper_fcmpeq, FCMPEQ)
VIS_CMPHELPER(helper_fcmple, FCMPLE)
VIS_CMPHELPER(helper_fcmpne, FCMPNE)
+
+uint64_t helper_pdist(uint64_t sum, uint64_t src1, uint64_t src2)
+{
+ int i;
+ for (i = 0; i < 8; i++) {
+ int s1, s2;
+
+ s1 = (src1 >> (56 - (i * 8))) & 0xff;
+ s2 = (src2 >> (56 - (i * 8))) & 0xff;
+
+ /* Absolute value of difference. */
+ s1 -= s2;
+ if (s1 < 0) {
+ s1 = -s1;
+ }
+
+ sum += s1;
+ }
+
+ return sum;
+}
+
+uint32_t helper_fpack16(uint64_t gsr, uint64_t rs2)
+{
+ int scale = (gsr >> 3) & 0xf;
+ uint32_t ret = 0;
+ int byte;
+
+ for (byte = 0; byte < 4; byte++) {
+ uint32_t val;
+ int16_t src = rs2 >> (byte * 16);
+ int32_t scaled = src << scale;
+ int32_t from_fixed = scaled >> 7;
+
+ val = (from_fixed < 0 ? 0 :
+ from_fixed > 255 ? 255 : from_fixed);
+
+ ret |= val << (8 * byte);
+ }
+
+ return ret;
+}
+
+uint64_t helper_fpack32(uint64_t gsr, uint64_t rs1, uint64_t rs2)
+{
+ int scale = (gsr >> 3) & 0x1f;
+ uint64_t ret = 0;
+ int word;
+
+ ret = (rs1 << 8) & ~(0x000000ff000000ffULL);
+ for (word = 0; word < 2; word++) {
+ uint64_t val;
+ int32_t src = rs2 >> (word * 32);
+ int64_t scaled = (int64_t)src << scale;
+ int64_t from_fixed = scaled >> 23;
+
+ val = (from_fixed < 0 ? 0 :
+ (from_fixed > 255) ? 255 : from_fixed);
+
+ ret |= val << (32 * word);
+ }
+
+ return ret;
+}
+
+uint32_t helper_fpackfix(uint64_t gsr, uint64_t rs2)
+{
+ int scale = (gsr >> 3) & 0x1f;
+ uint32_t ret = 0;
+ int word;
+
+ for (word = 0; word < 2; word++) {
+ uint32_t val;
+ int32_t src = rs2 >> (word * 32);
+ int64_t scaled = src << scale;
+ int64_t from_fixed = scaled >> 16;
+
+ val = (from_fixed < -32768 ? -32768 :
+ from_fixed > 32767 ? 32767 : from_fixed);
+
+ ret |= (val & 0xffff) << (word * 16);
+ }
+
+ return ret;
+}
+
+uint64 helper_bshuffle(uint64_t gsr, uint64_t src1, uint64_t src2)
+{
+ union {
+ uint64_t ll[2];
+ uint8_t b[16];
+ } s;
+ VIS64 r;
+ uint32_t i, mask, host;
+
+ /* Set up S such that we can index across all of the bytes. */
+#ifdef HOST_WORDS_BIGENDIAN
+ s.ll[0] = src1;
+ s.ll[1] = src2;
+ host = 0;
+#else
+ s.ll[1] = src1;
+ s.ll[0] = src2;
+ host = 15;
+#endif
+ mask = gsr >> 32;
+
+ for (i = 0; i < 8; ++i) {
+ unsigned e = (mask >> (28 - i*4)) & 0xf;
+ r.VIS_B64(i) = s.b[e ^ host];
+ }
+
+ return r.ll;
+}
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
index fea5983669..24ec7fc128 100644
--- a/tcg/tcg-op.h
+++ b/tcg/tcg-op.h
@@ -2045,38 +2045,75 @@ static inline void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1,
TCGv_i32 arg2, unsigned int ofs,
unsigned int len)
{
+ uint32_t mask;
+ TCGv_i32 t1;
+
+ if (ofs == 0 && len == 32) {
+ tcg_gen_mov_i32(ret, arg2);
+ return;
+ }
if (TCG_TARGET_HAS_deposit_i32 && TCG_TARGET_deposit_i32_valid(ofs, len)) {
tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len);
- } else {
- uint32_t mask = (1u << len) - 1;
- TCGv_i32 t1 = tcg_temp_new_i32 ();
+ return;
+ }
+
+ mask = (1u << len) - 1;
+ t1 = tcg_temp_new_i32();
+ if (ofs + len < 32) {
tcg_gen_andi_i32(t1, arg2, mask);
tcg_gen_shli_i32(t1, t1, ofs);
- tcg_gen_andi_i32(ret, arg1, ~(mask << ofs));
- tcg_gen_or_i32(ret, ret, t1);
-
- tcg_temp_free_i32(t1);
+ } else {
+ tcg_gen_shli_i32(t1, arg2, ofs);
}
+ tcg_gen_andi_i32(ret, arg1, ~(mask << ofs));
+ tcg_gen_or_i32(ret, ret, t1);
+
+ tcg_temp_free_i32(t1);
}
static inline void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1,
TCGv_i64 arg2, unsigned int ofs,
unsigned int len)
{
+ uint64_t mask;
+ TCGv_i64 t1;
+
+ if (ofs == 0 && len == 64) {
+ tcg_gen_mov_i64(ret, arg2);
+ return;
+ }
if (TCG_TARGET_HAS_deposit_i64 && TCG_TARGET_deposit_i64_valid(ofs, len)) {
tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len);
- } else {
- uint64_t mask = (1ull << len) - 1;
- TCGv_i64 t1 = tcg_temp_new_i64 ();
+ return;
+ }
+#if TCG_TARGET_REG_BITS == 32
+ if (ofs >= 32) {
+ tcg_gen_deposit_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1),
+ TCGV_LOW(arg2), ofs - 32, len);
+ return;
+ }
+ if (ofs + len <= 32) {
+ tcg_gen_deposit_i32(TCGV_LOW(ret), TCGV_LOW(arg1),
+ TCGV_LOW(arg2), ofs, len);
+ return;
+ }
+#endif
+
+ mask = (1ull << len) - 1;
+ t1 = tcg_temp_new_i64();
+
+ if (ofs + len < 64) {
tcg_gen_andi_i64(t1, arg2, mask);
tcg_gen_shli_i64(t1, t1, ofs);
- tcg_gen_andi_i64(ret, arg1, ~(mask << ofs));
- tcg_gen_or_i64(ret, ret, t1);
-
- tcg_temp_free_i64(t1);
+ } else {
+ tcg_gen_shli_i64(t1, arg2, ofs);
}
+ tcg_gen_andi_i64(ret, arg1, ~(mask << ofs));
+ tcg_gen_or_i64(ret, ret, t1);
+
+ tcg_temp_free_i64(t1);
}
/***************************************/
diff --git a/trace-events b/trace-events
index 820b1d6ee6..962caca598 100644
--- a/trace-events
+++ b/trace-events
@@ -278,6 +278,7 @@ usb_host_claim_port(int bus, int hub, int port) "bus %d, hub addr %d, port %d"
# hw/scsi-bus.c
scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d"
scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
+scsi_req_data_canceled(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d"
scsi_req_dequeue(int target, int lun, int tag) "target %d lun %d tag %d"
scsi_req_continue(int target, int lun, int tag) "target %d lun %d tag %d"
scsi_req_parsed(int target, int lun, int tag, int cmd, int mode, int xfer) "target %d lun %d tag %d command %d dir %d length %d"
@@ -505,6 +506,12 @@ escc_sunkbd_event_out(int ch) "Translated keycode %2.2x"
escc_kbd_command(int val) "Command %d"
escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=%01x"
+# block/iscsi.c
+iscsi_aio_write10_cb(void *iscsi, int status, void *acb, int canceled) "iscsi %p status %d acb %p canceled %d"
+iscsi_aio_writev(void *iscsi, int64_t sector_num, int nb_sectors, void *opaque, void *acb) "iscsi %p sector_num %"PRId64" nb_sectors %d opaque %p acb %p"
+iscsi_aio_read10_cb(void *iscsi, int status, void *acb, int canceled) "iscsi %p status %d acb %p canceled %d"
+iscsi_aio_readv(void *iscsi, int64_t sector_num, int nb_sectors, void *opaque, void *acb) "iscsi %p sector_num %"PRId64" nb_sectors %d opaque %p acb %p"
+
# hw/esp.c
esp_raise_irq(void) "Raise IRQ"
esp_lower_irq(void) "Lower IRQ"
@@ -555,7 +562,7 @@ open_eth_desc_read(uint32_t addr, uint32_t v) "DESC[%04x] -> %08x"
open_eth_desc_write(uint32_t addr, uint32_t v) "DESC[%04x] <- %08x"
# hw/9pfs/virtio-9p.c
-complete_pdu(uint16_t tag, uint8_t id, int err) "tag %d id %d err %d"
+v9fs_rerror(uint16_t tag, uint8_t id, int err) "tag %d id %d err %d"
v9fs_version(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s"
v9fs_version_return(uint16_t tag, uint8_t id, int32_t msize, char* version) "tag %d id %d msize %d version %s"
v9fs_attach(uint16_t tag, uint8_t id, int32_t fid, int32_t afid, char* uname, char* aname) "tag %u id %u fid %d afid %d uname %s aname %s"
diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h
index f34be69f52..c35b29c1f6 100644
--- a/ui/qemu-spice.h
+++ b/ui/qemu-spice.h
@@ -25,6 +25,7 @@
#include "qemu-option.h"
#include "qemu-config.h"
#include "qemu-char.h"
+#include "monitor.h"
extern int using_spice;
@@ -37,7 +38,8 @@ int qemu_spice_set_passwd(const char *passwd,
bool fail_if_connected, bool disconnect_if_connected);
int qemu_spice_set_pw_expire(time_t expires);
int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
- const char *subject);
+ const char *subject,
+ MonitorCompletion cb, void *opaque);
void do_info_spice_print(Monitor *mon, const QObject *data);
void do_info_spice(Monitor *mon, QObject **ret_data);
@@ -45,6 +47,7 @@ void do_info_spice(Monitor *mon, QObject **ret_data);
int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr);
#else /* CONFIG_SPICE */
+#include "monitor.h"
#define using_spice 0
static inline int qemu_spice_set_passwd(const char *passwd,
@@ -57,8 +60,13 @@ static inline int qemu_spice_set_pw_expire(time_t expires)
{
return -1;
}
-static inline int qemu_spice_migrate_info(const char *h, int p, int t, const char *s)
-{ return -1; }
+static inline int qemu_spice_migrate_info(const char *h, int p, int t,
+ const char *s,
+ MonitorCompletion cb, void *opaque)
+{
+ cb(opaque, NULL);
+ return -1;
+}
#endif /* CONFIG_SPICE */
diff --git a/ui/spice-core.c b/ui/spice-core.c
index b33366e5d7..5639c6f531 100644
--- a/ui/spice-core.c
+++ b/ui/spice-core.c
@@ -19,14 +19,15 @@
#include <spice-experimental.h>
#include <netdb.h>
-#include <pthread.h>
#include "qemu-common.h"
#include "qemu-spice.h"
+#include "qemu-thread.h"
#include "qemu-timer.h"
#include "qemu-queue.h"
#include "qemu-x509.h"
#include "qemu_socket.h"
+#include "qmp-commands.h"
#include "qint.h"
#include "qbool.h"
#include "qstring.h"
@@ -45,7 +46,7 @@ static char *auth_passwd;
static time_t auth_expires = TIME_MAX;
int using_spice = 0;
-static pthread_t me;
+static QemuThread me;
struct SpiceTimer {
QEMUTimer *timer;
@@ -133,7 +134,7 @@ static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *
static void watch_remove(SpiceWatch *watch)
{
- watch_update_mask(watch, 0);
+ qemu_set_fd_handler(watch->fd, NULL, NULL, NULL);
QTAILQ_REMOVE(&watches, watch, next);
g_free(watch);
}
@@ -194,22 +195,6 @@ static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info)
qdict_put(dict, "tls", qbool_from_int(tls));
}
-static QList *channel_list_get(void)
-{
- ChannelList *item;
- QList *list;
- QDict *dict;
-
- list = qlist_new();
- QTAILQ_FOREACH(item, &channel_list, link) {
- dict = qdict_new();
- add_addr_info(dict, &item->info->paddr, item->info->plen);
- add_channel_info(dict, item->info);
- qlist_append(list, dict);
- }
- return list;
-}
-
static void channel_event(int event, SpiceChannelEventInfo *info)
{
static const int qevent[] = {
@@ -229,7 +214,7 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
* thread and grab the iothread lock if so before calling qemu
* functions.
*/
- bool need_lock = !pthread_equal(me, pthread_self());
+ bool need_lock = !qemu_thread_is_self(&me);
if (need_lock) {
qemu_mutex_lock_iothread();
}
@@ -288,6 +273,38 @@ static SpiceCoreInterface core_interface = {
#endif
};
+#ifdef SPICE_INTERFACE_MIGRATION
+typedef struct SpiceMigration {
+ SpiceMigrateInstance sin;
+ struct {
+ MonitorCompletion *cb;
+ void *opaque;
+ } connect_complete;
+} SpiceMigration;
+
+static void migrate_connect_complete_cb(SpiceMigrateInstance *sin);
+
+static const SpiceMigrateInterface migrate_interface = {
+ .base.type = SPICE_INTERFACE_MIGRATION,
+ .base.description = "migration",
+ .base.major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
+ .base.minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
+ .migrate_connect_complete = migrate_connect_complete_cb,
+ .migrate_end_complete = NULL,
+};
+
+static SpiceMigration spice_migrate;
+
+static void migrate_connect_complete_cb(SpiceMigrateInstance *sin)
+{
+ SpiceMigration *sm = container_of(sin, SpiceMigration, sin);
+ if (sm->connect_complete.cb) {
+ sm->connect_complete.cb(sm->connect_complete.opaque, NULL);
+ }
+ sm->connect_complete.cb = NULL;
+}
+#endif
+
/* config string parsing */
static int name2enum(const char *string, const char *table[], int entries)
@@ -351,116 +368,129 @@ static const char *wan_compression_names[] = {
/* functions for the rest of qemu */
-static void info_spice_iter(QObject *obj, void *opaque)
+static SpiceChannelList *qmp_query_spice_channels(void)
{
- QDict *client;
- Monitor *mon = opaque;
-
- client = qobject_to_qdict(obj);
- monitor_printf(mon, "Channel:\n");
- monitor_printf(mon, " address: %s:%s%s\n",
- qdict_get_str(client, "host"),
- qdict_get_str(client, "port"),
- qdict_get_bool(client, "tls") ? " [tls]" : "");
- monitor_printf(mon, " session: %" PRId64 "\n",
- qdict_get_int(client, "connection-id"));
- monitor_printf(mon, " channel: %d:%d\n",
- (int)qdict_get_int(client, "channel-type"),
- (int)qdict_get_int(client, "channel-id"));
-}
-
-void do_info_spice_print(Monitor *mon, const QObject *data)
-{
- QDict *server;
- QList *channels;
- const char *host;
- int port;
-
- server = qobject_to_qdict(data);
- if (qdict_get_bool(server, "enabled") == 0) {
- monitor_printf(mon, "Server: disabled\n");
- return;
- }
+ SpiceChannelList *cur_item = NULL, *head = NULL;
+ ChannelList *item;
- monitor_printf(mon, "Server:\n");
- host = qdict_get_str(server, "host");
- port = qdict_get_try_int(server, "port", -1);
- if (port != -1) {
- monitor_printf(mon, " address: %s:%d\n", host, port);
- }
- port = qdict_get_try_int(server, "tls-port", -1);
- if (port != -1) {
- monitor_printf(mon, " address: %s:%d [tls]\n", host, port);
+ QTAILQ_FOREACH(item, &channel_list, link) {
+ SpiceChannelList *chan;
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+
+ chan = g_malloc0(sizeof(*chan));
+ chan->value = g_malloc0(sizeof(*chan->value));
+
+ getnameinfo(&item->info->paddr, item->info->plen,
+ host, sizeof(host), port, sizeof(port),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ chan->value->host = g_strdup(host);
+ chan->value->port = g_strdup(port);
+ chan->value->family = g_strdup(inet_strfamily(item->info->paddr.sa_family));
+
+ chan->value->connection_id = item->info->connection_id;
+ chan->value->channel_type = item->info->type;
+ chan->value->channel_id = item->info->id;
+ chan->value->tls = item->info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
+
+ /* XXX: waiting for the qapi to support GSList */
+ if (!cur_item) {
+ head = cur_item = chan;
+ } else {
+ cur_item->next = chan;
+ cur_item = chan;
+ }
}
- monitor_printf(mon, " auth: %s\n", qdict_get_str(server, "auth"));
- monitor_printf(mon, " compiled: %s\n",
- qdict_get_str(server, "compiled-version"));
- channels = qdict_get_qlist(server, "channels");
- if (qlist_empty(channels)) {
- monitor_printf(mon, "Channels: none\n");
- } else {
- qlist_iter(channels, info_spice_iter, mon);
- }
+ return head;
}
-void do_info_spice(Monitor *mon, QObject **ret_data)
+SpiceInfo *qmp_query_spice(Error **errp)
{
QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
- QDict *server;
- QList *clist;
- const char *addr;
int port, tls_port;
+ const char *addr;
+ SpiceInfo *info;
char version_string[20]; /* 12 = |255.255.255\0| is the max */
- if (!spice_server) {
- *ret_data = qobject_from_jsonf("{ 'enabled': false }");
- return;
+ info = g_malloc0(sizeof(*info));
+
+ if (!spice_server || !opts) {
+ info->enabled = false;
+ return info;
}
+ info->enabled = true;
+
addr = qemu_opt_get(opts, "addr");
port = qemu_opt_get_number(opts, "port", 0);
tls_port = qemu_opt_get_number(opts, "tls-port", 0);
- clist = channel_list_get();
- server = qdict_new();
- qdict_put(server, "enabled", qbool_from_int(true));
- qdict_put(server, "auth", qstring_from_str(auth));
- qdict_put(server, "host", qstring_from_str(addr ? addr : "0.0.0.0"));
+ info->has_auth = true;
+ info->auth = g_strdup(auth);
+
+ info->has_host = true;
+ info->host = g_strdup(addr ? addr : "0.0.0.0");
+
+ info->has_compiled_version = true;
snprintf(version_string, sizeof(version_string), "%d.%d.%d",
(SPICE_SERVER_VERSION & 0xff0000) >> 16,
(SPICE_SERVER_VERSION & 0xff00) >> 8,
SPICE_SERVER_VERSION & 0xff);
- qdict_put(server, "compiled-version", qstring_from_str(version_string));
+ info->compiled_version = g_strdup(version_string);
+
if (port) {
- qdict_put(server, "port", qint_from_int(port));
+ info->has_port = true;
+ info->port = port;
}
if (tls_port) {
- qdict_put(server, "tls-port", qint_from_int(tls_port));
- }
- if (clist) {
- qdict_put(server, "channels", clist);
+ info->has_tls_port = true;
+ info->tls_port = tls_port;
}
- *ret_data = QOBJECT(server);
+ /* for compatibility with the original command */
+ info->has_channels = true;
+ info->channels = qmp_query_spice_channels();
+
+ return info;
}
static void migration_state_notifier(Notifier *notifier, void *data)
{
MigrationState *s = data;
- if (migration_has_finished(s)) {
+ if (migration_is_active(s)) {
+#ifdef SPICE_INTERFACE_MIGRATION
+ spice_server_migrate_start(spice_server);
+#endif
+ } else if (migration_has_finished(s)) {
#if SPICE_SERVER_VERSION >= 0x000701 /* 0.7.1 */
+#ifndef SPICE_INTERFACE_MIGRATION
spice_server_migrate_switch(spice_server);
+#else
+ spice_server_migrate_end(spice_server, true);
+ } else if (migration_has_failed(s)) {
+ spice_server_migrate_end(spice_server, false);
+#endif
#endif
}
}
int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
- const char *subject)
+ const char *subject,
+ MonitorCompletion *cb, void *opaque)
{
- return spice_server_migrate_info(spice_server, hostname,
- port, tls_port, subject);
+ int ret;
+#ifdef SPICE_INTERFACE_MIGRATION
+ spice_migrate.connect_complete.cb = cb;
+ spice_migrate.connect_complete.opaque = opaque;
+ ret = spice_server_migrate_connect(spice_server, hostname,
+ port, tls_port, subject);
+#else
+ ret = spice_server_migrate_info(spice_server, hostname,
+ port, tls_port, subject);
+ cb(opaque, NULL);
+#endif
+ return ret;
}
static int add_channel(const char *name, const char *value, void *opaque)
@@ -503,7 +533,7 @@ void qemu_spice_init(void)
spice_image_compression_t compression;
spice_wan_compression_t wan_compr;
- me = pthread_self();
+ qemu_thread_get_self(&me);
if (!opts) {
return;
@@ -650,6 +680,11 @@ void qemu_spice_init(void)
migration_state.notify = migration_state_notifier;
add_migration_state_change_notifier(&migration_state);
+#ifdef SPICE_INTERFACE_MIGRATION
+ spice_migrate.sin.base.sif = &migrate_interface.base;
+ spice_migrate.connect_complete.cb = NULL;
+ qemu_spice_add_interface(&spice_migrate.sin.base);
+#endif
qemu_spice_input_init();
qemu_spice_audio_init();
diff --git a/ui/vnc.c b/ui/vnc.c
index fc3a612a35..32d4cb70cd 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -31,6 +31,7 @@
#include "qemu-timer.h"
#include "acl.h"
#include "qemu-objects.h"
+#include "qmp-commands.h"
#define VNC_REFRESH_INTERVAL_BASE 30
#define VNC_REFRESH_INTERVAL_INC 50
@@ -274,80 +275,110 @@ static void vnc_qmp_event(VncState *vs, MonitorEvent event)
qobject_decref(data);
}
-static void info_vnc_iter(QObject *obj, void *opaque)
+static VncClientInfo *qmp_query_vnc_client(const VncState *client)
{
- QDict *client;
- Monitor *mon = opaque;
+ struct sockaddr_storage sa;
+ socklen_t salen = sizeof(sa);
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ VncClientInfo *info;
+
+ if (getpeername(client->csock, (struct sockaddr *)&sa, &salen) < 0) {
+ return NULL;
+ }
+
+ if (getnameinfo((struct sockaddr *)&sa, salen,
+ host, sizeof(host),
+ serv, sizeof(serv),
+ NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+ return NULL;
+ }
- client = qobject_to_qdict(obj);
- monitor_printf(mon, "Client:\n");
- monitor_printf(mon, " address: %s:%s\n",
- qdict_get_str(client, "host"),
- qdict_get_str(client, "service"));
+ info = g_malloc0(sizeof(*info));
+ info->host = g_strdup(host);
+ info->service = g_strdup(serv);
+ info->family = g_strdup(inet_strfamily(sa.ss_family));
#ifdef CONFIG_VNC_TLS
- monitor_printf(mon, " x509_dname: %s\n",
- qdict_haskey(client, "x509_dname") ?
- qdict_get_str(client, "x509_dname") : "none");
+ if (client->tls.session && client->tls.dname) {
+ info->has_x509_dname = true;
+ info->x509_dname = g_strdup(client->tls.dname);
+ }
#endif
#ifdef CONFIG_VNC_SASL
- monitor_printf(mon, " username: %s\n",
- qdict_haskey(client, "sasl_username") ?
- qdict_get_str(client, "sasl_username") : "none");
-#endif
-}
-
-void do_info_vnc_print(Monitor *mon, const QObject *data)
-{
- QDict *server;
- QList *clients;
-
- server = qobject_to_qdict(data);
- if (qdict_get_bool(server, "enabled") == 0) {
- monitor_printf(mon, "Server: disabled\n");
- return;
+ if (client->sasl.conn && client->sasl.username) {
+ info->has_sasl_username = true;
+ info->sasl_username = g_strdup(client->sasl.username);
}
+#endif
- monitor_printf(mon, "Server:\n");
- monitor_printf(mon, " address: %s:%s\n",
- qdict_get_str(server, "host"),
- qdict_get_str(server, "service"));
- monitor_printf(mon, " auth: %s\n", qdict_get_str(server, "auth"));
-
- clients = qdict_get_qlist(server, "clients");
- if (qlist_empty(clients)) {
- monitor_printf(mon, "Client: none\n");
- } else {
- qlist_iter(clients, info_vnc_iter, mon);
- }
+ return info;
}
-void do_info_vnc(Monitor *mon, QObject **ret_data)
+VncInfo *qmp_query_vnc(Error **errp)
{
+ VncInfo *info = g_malloc0(sizeof(*info));
+
if (vnc_display == NULL || vnc_display->display == NULL) {
- *ret_data = qobject_from_jsonf("{ 'enabled': false }");
+ info->enabled = false;
} else {
- QList *clist;
+ VncClientInfoList *cur_item = NULL;
+ struct sockaddr_storage sa;
+ socklen_t salen = sizeof(sa);
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
VncState *client;
- clist = qlist_new();
+ info->enabled = true;
+
+ /* for compatibility with the original command */
+ info->has_clients = true;
+
QTAILQ_FOREACH(client, &vnc_display->clients, next) {
- if (client->info) {
- /* incref so that it's not freed by upper layers */
- qobject_incref(client->info);
- qlist_append_obj(clist, client->info);
+ VncClientInfoList *cinfo = g_malloc0(sizeof(*info));
+ cinfo->value = qmp_query_vnc_client(client);
+
+ /* XXX: waiting for the qapi to support GSList */
+ if (!cur_item) {
+ info->clients = cur_item = cinfo;
+ } else {
+ cur_item->next = cinfo;
+ cur_item = cinfo;
}
}
- *ret_data = qobject_from_jsonf("{ 'enabled': true, 'clients': %p }",
- QOBJECT(clist));
- assert(*ret_data != NULL);
+ if (getsockname(vnc_display->lsock, (struct sockaddr *)&sa,
+ &salen) == -1) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ goto out_error;
+ }
- if (vnc_server_info_put(qobject_to_qdict(*ret_data)) < 0) {
- qobject_decref(*ret_data);
- *ret_data = NULL;
+ if (getnameinfo((struct sockaddr *)&sa, salen,
+ host, sizeof(host),
+ serv, sizeof(serv),
+ NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
+ error_set(errp, QERR_UNDEFINED_ERROR);
+ goto out_error;
}
+
+ info->has_host = true;
+ info->host = g_strdup(host);
+
+ info->has_service = true;
+ info->service = g_strdup(serv);
+
+ info->has_family = true;
+ info->family = g_strdup(inet_strfamily(sa.ss_family));
+
+ info->has_auth = true;
+ info->auth = g_strdup(vnc_auth_name(vnc_display));
}
+
+ return info;
+
+out_error:
+ qapi_free_VncInfo(info);
+ return NULL;
}
/* TODO
diff --git a/vl.c b/vl.c
index 1ddb17bfd9..b56e0cb378 100644
--- a/vl.c
+++ b/vl.c
@@ -143,9 +143,9 @@ int main(int argc, char **argv)
#include "audio/audio.h"
#include "migration.h"
#include "kvm.h"
+#include "qjson.h"
#include "qemu-option.h"
#include "qemu-config.h"
-#include "qemu-objects.h"
#include "qemu-options.h"
#include "qmp-commands.h"
#include "main-loop.h"
@@ -2707,6 +2707,8 @@ int main(int argc, char **argv, char **envp)
qemu_opt_set(fsdev, "security_model",
qemu_opt_get(opts, "security_model"));
+ qemu_opt_set_bool(fsdev, "readonly",
+ qemu_opt_get_bool(opts, "readonly", 0));
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
qemu_opt_set(device, "driver", "virtio-9p-pci");
qemu_opt_set(device, "fsdev",
@@ -2715,6 +2717,24 @@ int main(int argc, char **argv, char **envp)
qemu_opt_get(opts, "mount_tag"));
break;
}
+ case QEMU_OPTION_virtfs_synth: {
+ QemuOpts *fsdev;
+ QemuOpts *device;
+
+ fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", 1);
+ if (!fsdev) {
+ fprintf(stderr, "duplicate option: %s\n", "virtfs_synth");
+ exit(1);
+ }
+ qemu_opt_set(fsdev, "fsdriver", "synth");
+ qemu_opt_set(fsdev, "path", "/"); /* ignored */
+
+ device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
+ qemu_opt_set(device, "driver", "virtio-9p-pci");
+ qemu_opt_set(device, "fsdev", "v_synth");
+ qemu_opt_set(device, "mount_tag", "v_synth");
+ break;
+ }
case QEMU_OPTION_serial:
add_device_config(DEV_SERIAL, optarg);
default_serial = 0;