libhw32
libhw64
libuser
+linux-headers/asm
qapi-generated
qemu-doc.html
qemu-tech.html
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/
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
-------
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
$(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
# 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)
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
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
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
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)
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
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
*/
#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;
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;
}
/**
#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);
#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>
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;
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
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);
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;
BlockDriver *drv)
{
int ret;
+ char tmp_filename[PATH_MAX];
if (flags & BDRV_O_SNAPSHOT) {
BlockDriverState *bs1;
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
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)
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,
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));
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;
}
}
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;
}
}
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);
--- /dev/null
+/*
+ * 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);
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));
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;
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;
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)
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);
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) {
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);
*/
#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)
/* 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
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)
/* 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);
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;
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;
/* 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;
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;
}
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;
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) {
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;
}
/* 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;
}
}
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),
#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
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;
opengl=""
zlib="yes"
guest_agent="yes"
+libiscsi=""
# parse CC options first
for opt do
;;
--enable-spice) spice="yes"
;;
+ --disable-libiscsi) libiscsi="no"
+ ;;
+ --enable-libiscsi) libiscsi="yes"
+ ;;
--enable-profiler) profiler="yes"
;;
--enable-cocoa)
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"
bswap_h=yes
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
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
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
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
#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)
{
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 */
#include "gdbstub.h"
#include "dma.h"
#include "kvm.h"
+#include "qmp-commands.h"
#include "qemu-thread.h"
#include "cpus.h"
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;
+}
int retval;
if (!basename) {
+ g_free(dupname);
return -1;
}
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];
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
#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
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
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 */
* 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;
char *data;
} V9fsPath;
+typedef union V9fsFidOpenState V9fsFidOpenState;
+
void cred_init(FsCred *);
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);
static FsDriverTable FsDrivers[] = {
{ .name = "local", .ops = &local_ops},
{ .name = "handle", .ops = &handle_ops},
+ { .name = "synth", .ops = &synth_ops},
};
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");
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;
FsDriverEntry *get_fsdev_fsentry(char *id);
extern FileOperations local_ops;
extern FileOperations handle_ops;
+extern FileOperations synth_ops;
#endif
#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) {
#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);
#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) {
#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;
.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
.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
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);
{
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");
+ }
+}
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
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 {
}
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;
}
}
v9fs_co_run_in_worker(
{
- s->ops->seekdir(&s->ctx, fidp->fs.dir, offset);
+ s->ops->seekdir(&s->ctx, &fidp->fs, offset);
});
}
}
v9fs_co_run_in_worker(
{
- s->ops->rewinddir(&s->ctx, fidp->fs.dir);
+ s->ops->rewinddir(&s->ctx, &fidp->fs);
});
}
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;
return err;
}
-int v9fs_co_closedir(V9fsPDU *pdu, DIR *dir)
+int v9fs_co_closedir(V9fsPDU *pdu, V9fsFidOpenState *fs)
{
int err;
V9fsState *s = pdu->s;
}
v9fs_co_run_in_worker(
{
- err = s->ops->closedir(&s->ctx, dir);
+ err = s->ops->closedir(&s->ctx, fs);
if (err < 0) {
err = -errno;
}
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;
}
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;
}
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;
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);
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);
}
return err;
}
-int v9fs_co_close(V9fsPDU *pdu, int fd)
+int v9fs_co_close(V9fsPDU *pdu, V9fsFidOpenState *fs)
{
int err;
V9fsState *s = pdu->s;
}
v9fs_co_run_in_worker(
{
- err = s->ops->close(&s->ctx, fd);
+ err = s->ops->close(&s->ctx, fs);
if (err < 0) {
err = -errno;
}
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;
}
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;
}
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;
}
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 *,
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 *);
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
* 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
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;
if (ret < 0) {
close(fd);
fd = ret;
+ } else {
+ fs->fd = fd;
}
}
close(dirfd);
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);
}
}
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
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;
}
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
* 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
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;
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;
}
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;
}
}
}
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;
}
}
err = fd;
+ fs->fd = fd;
goto out;
err_end:
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;
}
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);
}
}
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
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;
}
--- /dev/null
+/*
+ * 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,
+};
--- /dev/null
+/*
+ * 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);
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);
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;
/*
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 */
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;
}
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);
}
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);
}
}
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);
}
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) {
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++) {
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;
} 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;
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);
}
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);
}
} 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);
}
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);
}
} 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);
}
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);
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);
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);
}
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;
}
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;
}
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);
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);
}
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);
}
}
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);
}
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;
} 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);
}
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;
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);
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) {
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);
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;
esp_raise_irq(s);
return 0;
}
- s->current_dev = s->bus.devs[target];
return dmalen;
}
{
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) {
*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
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);
}
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;
}
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 */
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);
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,
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,
{
/* 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);
}
}
#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);
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;
}
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;
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;
/* 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;
}
* 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
* 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 */
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);
/* 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;
}
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;
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;
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;
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;
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;
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
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;
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)
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;
}
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;
}
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;
}
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);
}
}
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;
}
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);
}
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;
}
* 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;
* 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;
}
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;
}
return;
}
- ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE);
+ ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE);
}
* 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;
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;
}
{
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];
#include "iorange.h"
#include "dma.h"
#include "sysemu.h"
+#include "hw/scsi-defs.h"
/* debug IDE devices */
//#define DEBUG_IDE
#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
#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
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;
}
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;
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);
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);
--- /dev/null
+/*
+ * 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()
+ }
+};
--- /dev/null
+/*
+ * 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 */
/* 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;
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)
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;
}
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;
}
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);
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
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);
}
#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,
#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
}
}
-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[] = {
#define QEMU_PCI_H
#include "qemu-common.h"
-#include "qobject.h"
#include "qdev.h"
#include "memory.h"
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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 )
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;
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);
}
}
- 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;
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) {
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) {
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);
* (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;
}
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++;
{
DeviceState *dev;
- while ((dev = QLIST_FIRST(&bus->children)) != NULL) {
+ while ((dev = QTAILQ_FIRST(&bus->children)) != NULL) {
qdev_free(dev);
}
if (bus->parent) {
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);
}
}
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;
};
const char *name;
int allow_hotplug;
int qdev_allocated;
- QLIST_HEAD(, DeviceState) children;
+ QTAILQ_HEAD(ChildrenHead, DeviceState) children;
QLIST_ENTRY(BusState) sibling;
};
* 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"
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);
}
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);
{
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;
}
d->mode = QXL_MODE_UNDEFINED;
qemu_spice_destroy_primary_surface(&d->ssd, 0, async);
+ qxl_spice_reset_cursor(d);
return 1;
}
qxl_update_irq(d);
break;
case QXL_IO_NOTIFY_OOM:
- if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
- break;
- }
- pthread_yield();
if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) {
break;
}
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) {
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);
}
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);
},
};
+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)",
.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 = {
.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)
#include "hw.h"
#include "pci.h"
#include "vga_int.h"
+#include "qemu-thread.h"
#include "ui/qemu-spice.h"
#include "ui/spice-display.h"
QemuMutex track_lock;
/* thread signaling */
- pthread_t main;
+ QemuThread main;
int pipe[2];
/* ram pci bar */
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];
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]);
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))
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;
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);
.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:
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;
}
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;
return 0;
}
-struct SCSIReqOps reqops_invalid_opcode = {
+static const struct SCSIReqOps reqops_invalid_opcode = {
.size = sizeof(SCSIRequest),
.send_command = scsi_invalid_command
};
return 0;
}
-struct SCSIReqOps reqops_unit_attention = {
+static const struct SCSIReqOps reqops_unit_attention = {
.size = sizeof(SCSIRequest),
.send_command = scsi_unit_attention
};
struct SCSITargetReq {
SCSIRequest req;
int len;
- uint8_t buf[64];
+ uint8_t buf[2056];
};
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;
}
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);
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));
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,
};
-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;
(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 ||
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);
}
}
*
* 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;
}
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;
};
/* 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
};
.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
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)
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
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)
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;
}
#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
#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
#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
#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 {
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;
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)
{
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;
}
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);
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;
}
}
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)
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. */
/* 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);
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;
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;
}
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;
}
}
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)
/* 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);
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);
}
}
-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)
{
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;
"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
}
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);
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);
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;
}
}
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);
}
/* 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
* 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;
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;
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 */
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,
/* 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;
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;
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;
}
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;
}
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);
default:
return -1;
}
- if (toclen > req->cmd.xfer)
+ if (toclen > req->cmd.xfer) {
toclen = req->cmd.xfer;
+ }
return toclen;
}
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;
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) {
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;
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);
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));
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);
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);
}
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:
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);
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;
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;
}
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;
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;
}
}
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)
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)
.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;
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);
}
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,
.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;
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), \
.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),
.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",
.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),
#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;
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);
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) {
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) {
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. */
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;
}
static void scsi_read_complete(void * opaque, int ret)
{
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
+ SCSIDevice *s = r->req.dev;
int len;
r->req.aiocb = NULL;
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);
+ }
}
}
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;
}
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);
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);
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);
}
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),
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]);
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;
}
}
-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];
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,
};
static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun,
- void *hba_private)
+ uint8_t *buf, void *hba_private)
{
SCSIRequest *req;
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(),
},
};
#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;
struct SCSIRequest {
SCSIBus *bus;
SCSIDevice *dev;
- SCSIReqOps *ops;
+ const SCSIReqOps *ops;
uint32_t refcount;
uint32_t tag;
uint32_t lun;
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;
};
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 */
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);
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)
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*/
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);
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
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;
return;
}
- QLIST_FOREACH(qdev, &bus->bus.children, sibling) {
+ QTAILQ_FOREACH(qdev, &bus->bus.children, sibling) {
dev = (VIOsPAPRDevice *)qdev;
spapr_vio_quiesce_one(dev);
}
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);
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,
{
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 {
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
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);
}
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");
}
{
DeviceState *dev;
SSISlave *slave;
- dev = QLIST_FIRST(&bus->qbus.children);
+ dev = QTAILQ_FIRST(&bus->qbus.children);
if (!dev) {
return 0;
}
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
}
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;
qemu_irq sic[32];
DeviceState *dev, *sysctl;
SysBusDevice *busdev;
+ DeviceState *pl041;
PCIBus *pci_bus;
NICInfo *nd;
int n;
/* 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. */
{
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];
/* 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 */
#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;
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;
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);
}
}
-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);
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)
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)
#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;
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)
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;
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) {
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
}
}
+
+ s->coalesced_flush_in_progress = false;
}
static void do_kvm_cpu_synchronize_state(void *_env)
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);
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 =
if (sock < 0) {
/* Error */
fprintf(stderr, "Error opening socket!\n");
+ return -1;
}
memset(&hints, 0, sizeof(struct addrinfo));
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));
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);
*/
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));
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));
#include "block.h"
#include "qemu_socket.h"
#include "block-migration.h"
-#include "qemu-objects.h"
+#include "qmp-commands.h"
//#define DEBUG_MIGRATION
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) {
/* 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 */
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;
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);
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,
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;
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,
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");
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)
{
}
}
-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;
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;
}
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;
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;
#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);
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");
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;
.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",
.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",
.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)
.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)
{
.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
{
.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",
{ /* 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;
#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) },
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);
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)
{
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);
}
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;
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 */
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);
##
{ '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:
#
# 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'} }
}, {
.name = "writeout",
.type = QEMU_OPT_STRING,
+ }, {
+ .name = "readonly",
+ .type = QEMU_OPT_BOOL,
},
+
{ /*End of list */ }
},
};
}, {
.name = "writeout",
.type = QEMU_OPT_STRING,
+ }, {
+ .name = "readonly",
+ .type = QEMU_OPT_BOOL,
},
{ /*End of list */ }
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
static int close_f(int argc, char **argv)
{
- bdrv_close(bs);
+ bdrv_delete(bs);
bs = NULL;
return 0;
}
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;
}
qemu_aio_flush();
if (bs) {
- bdrv_close(bs);
+ bdrv_delete(bs);
}
return 0;
}
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")) {
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);
const QemuOptDesc *desc;
union {
- int boolean;
+ bool boolean;
uint64_t uint;
} value;
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);
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)
{
};
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);
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.
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
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".
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:
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,
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, \
* For details on the use of these macros, see the queue(3) manual page.
*/
+#include "qemu-barrier.h" /* for smp_wmb() */
+
/*
* List definitions.
*/
(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 = \
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);
.error_fmt = QERR_FD_NOT_SUPPLIED,
.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)'",
{
.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
.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
EQMP
+ {
+ .name = "query-block",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_block,
+ },
+
SQMP
query-blockstats
----------------
EQMP
+ {
+ .name = "query-blockstats",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_blockstats,
+ },
+
SQMP
query-cpus
----------
EQMP
+ {
+ .name = "query-cpus",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_cpus,
+ },
+
SQMP
query-pci
---------
EQMP
+ {
+ .name = "query-pci",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_pci,
+ },
+
SQMP
query-kvm
---------
EQMP
+ {
+ .name = "query-mice",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_mice,
+ },
+
SQMP
query-vnc
---------
EQMP
+ {
+ .name = "query-vnc",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_vnc,
+ },
+
SQMP
query-spice
-----------
EQMP
+#if defined(CONFIG_SPICE)
+ {
+ .name = "query-spice",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_spice,
+ },
+#endif
+
SQMP
query-name
----------
EQMP
+ {
+ .name = "query-migrate",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_migrate,
+ },
+
SQMP
query-balloon
-------------
EQMP
+ {
+ .name = "query-balloon",
+ .args_type = "",
+ .mhandler.cmd_new = qmp_marshal_input_query_balloon,
+ },
{
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
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, ")"
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())
--- /dev/null
+#!/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)
--- /dev/null
+#!/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()
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)
#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)
uint64_t tsc_deadline;
uint64_t mcg_status;
+ uint64_t msr_ia32_misc_enable;
/* exception/interrupt handling */
int error_code;
#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
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;
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)
has_msr_tsc_deadline = true;
continue;
}
+ if (kvm_msr_list->indices[i] == MSR_IA32_MISC_ENABLE) {
+ has_msr_misc_enable = true;
+ continue;
+ }
}
}
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);
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;
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) {
}
};
+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,
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! */
},
} , {
.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 */
}
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)) {
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)) {
#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
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)
uint64_t prom_addr;
#endif
/* temporary float registers */
- float64 dt0, dt1;
float128 qt0, qt1;
float_status fp_status;
#if defined(TARGET_SPARC64)
}
}
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");
}
#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);
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)
}
#ifdef TARGET_SPARC64
-F_HELPER(neg, d)
+float64 helper_fnegd(float64 src)
{
- DT0 = float64_chs(DT1);
+ return float64_chs(src);
}
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
}
#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)
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) \
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) || \
} \
}
-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)
{
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)
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)
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)
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);
#endif
#endif
-#define DT0 (env->dt0)
-#define DT1 (env->dt1)
#define QT0 (env->qt0)
#define QT1 (env->qt1)
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);
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 */
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;
}
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;
}
}
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);
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;
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;
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;
}
}
}
#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 */
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);
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);
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];
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
#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
}
}
-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;
}
}
}
}
-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;
}
}
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)
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)
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 */
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) \
{
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)))
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:
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
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
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;
}
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); \
}
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); \
}
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); \
}
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); \
}
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); \
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); \
}
#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);
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);
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;
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;
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 */
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
}
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;
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 */
#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;
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,
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) {
"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 */
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 */
#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))
#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)) |
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)]
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);
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); \
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); \
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); \
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); \
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)); \
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); \
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)); \
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; \
\
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; \
\
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; \
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; \
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;
+}
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);
}
/***************************************/
# 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"
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"
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"
#include "qemu-option.h"
#include "qemu-config.h"
#include "qemu-char.h"
+#include "monitor.h"
extern int using_spice;
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);
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,
{
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 */
#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"
static time_t auth_expires = TIME_MAX;
int using_spice = 0;
-static pthread_t me;
+static QemuThread me;
struct SpiceTimer {
QEMUTimer *timer;
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);
}
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[] = {
* 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();
}
#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)
/* 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)
spice_image_compression_t compression;
spice_wan_compression_t wan_compr;
- me = pthread_self();
+ qemu_thread_get_self(&me);
if (!opts) {
return;
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();
#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
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
#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"
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",
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;