diff options
author | Marcelo Tosatti <mtosatti@redhat.com> | 2012-10-11 05:19:58 -0300 |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2012-10-11 05:19:58 -0300 |
commit | caaef9b163a4696f686d91f9f2767e9c6ab446d6 (patch) | |
tree | 33acd5bd1bc8c34ffe36af2a7ab160018d80250f | |
parent | 487a26af87644923656e98a40f7801ec2f459b14 (diff) | |
parent | c9159fe9aa9abe24115ea4d16127179e9cb07e22 (diff) |
Merge commit 'c9159fe9aa9abe24115ea4d16127179e9cb07e22' into upstream-merge
* commit 'c9159fe9aa9abe24115ea4d16127179e9cb07e22': (83 commits)
Remove libhw
rtc: implement century byte
rtc: map CMOS index 0x37 to 0x32 on read and writes
rtc: fix overflow in mktimegm
qtest: implement QTEST_STOP
qemu-barrier: Fix compiler version check for future gcc versions
doc: update HACKING wrt strncpy/pstrcpy
hw/r2d: add comment: this strncpy use is ok
qcow2: mark this file's sole strncpy use as justified
acpi: remove strzcpy (strncpy-identical) function; just use strncpy
libcacard/vcard_emul_nss: use pstrcpy in place of strncpy
qemu-ga: prefer pstrcpy: consistently NUL-terminate ifreq.ifr_name
vscsi: avoid unwarranted strncpy
virtio-9p: avoid unwarranted uses of strncpy
bt: replace fragile snprintf use and unwarranted strncpy
ui/vnc: simplify and avoid strncpy
linux-user: remove two unchecked uses of strdup
ppc: avoid buffer overrun: use pstrcpy, not strncpy
os-posix: avoid buffer overrun
lm32: avoid buffer overrun
...
Conflicts:
hw/Makefile.objs
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
113 files changed, 4286 insertions, 854 deletions
diff --git a/.gitignore b/.gitignore index 824c0d24d..bd6ba1c71 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,6 @@ trace-dtrace.dtrace *-linux-user *-bsd-user libdis* -libhw32 -libhw64 libuser linux-headers/asm qapi-generated @@ -91,10 +91,11 @@ emulators. 4. String manipulation -Do not use the strncpy function. According to the man page, it does -*not* guarantee a NULL-terminated buffer, which makes it extremely dangerous -to use. Instead, use functionally equivalent function: -void pstrcpy(char *buf, int buf_size, const char *str) +Do not use the strncpy function. As mentioned in the man page, it does *not* +guarantee a NULL-terminated buffer, which makes it extremely dangerous to use. +It also zeros trailing destination bytes out to the specified length. Instead, +use this similar function when possible, but note its different signature: +void pstrcpy(char *dest, int dest_buf_size, const char *src) Don't use strcat because it can't check for buffer overflows, but: char *pstrcat(char *buf, int buf_size, const char *s) diff --git a/MAINTAINERS b/MAINTAINERS index 25733fc86..29aac4fce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -474,6 +474,11 @@ M: Gerd Hoffmann <kraxel@redhat.com> S: Maintained F: hw/usb* +VFIO +M: Alex Williamson <alex.williamson@redhat.com> +S: Supported +F: hw/vfio* + vhost M: Michael S. Tsirkin <mst@redhat.com> S: Supported @@ -214,7 +214,7 @@ $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(tools-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) -QEMULIBS=libhw32 libhw64 libuser libdis libdis-user +QEMULIBS=libuser libdis libdis-user clean: # avoid old build problems by removing potentially incorrect old files diff --git a/Makefile.hw b/Makefile.hw deleted file mode 100644 index 59f5b4835..000000000 --- a/Makefile.hw +++ /dev/null @@ -1,23 +0,0 @@ -# Makefile for qemu target independent devices. - -include ../config-host.mak -include ../config-all-devices.mak -include config.mak -include $(SRC_PATH)/rules.mak - -.PHONY: all - -$(call set-vpath, $(SRC_PATH)) - -QEMU_CFLAGS+=-I.. -QEMU_CFLAGS += -I$(SRC_PATH)/include - -include $(SRC_PATH)/Makefile.objs - -all: $(hw-obj-y) -# Dummy command so that make thinks it has done something - @true - -clean: - rm -f $(addsuffix *.o, $(sort $(dir $(hw-obj-y)))) - rm -f $(addsuffix *.d, $(sort $(dir $(hw-obj-y)))) diff --git a/Makefile.objs b/Makefile.objs index b1f3e2254..74b35422c 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -90,10 +90,13 @@ common-obj-y += hw/ common-obj-y += ui/ common-obj-y += bt-host.o bt-vhci.o +common-obj-y += dma-helpers.o common-obj-y += iov.o acl.o common-obj-$(CONFIG_POSIX) += compatfd.o common-obj-y += notify.o event_notifier.o common-obj-y += qemu-timer.o qemu-timer-common.o +common-obj-y += qtest.o +common-obj-y += vl.o common-obj-$(CONFIG_SLIRP) += slirp/ @@ -116,11 +119,6 @@ user-obj-y += $(trace-obj-y) user-obj-y += qom/ ###################################################################### -# libhw - -hw-obj-y = vl.o dma-helpers.o qtest.o hw/ - -###################################################################### # libdis # NOTE: the disassembler code is only needed for debugging @@ -240,7 +238,6 @@ vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) QEMU_CFLAGS+=$(GLIB_CFLAGS) nested-vars += \ - hw-obj-y \ qga-obj-y \ block-obj-y \ qom-obj-y \ diff --git a/Makefile.target b/Makefile.target index d9d54b8dd..3822bc5ac 100644 --- a/Makefile.target +++ b/Makefile.target @@ -4,9 +4,6 @@ include ../config-host.mak include config-devices.mak include config-target.mak include $(SRC_PATH)/rules.mak -ifneq ($(HWDIR),) -include $(HWDIR)/config.mak -endif $(call set-vpath, $(SRC_PATH)) ifdef CONFIG_LINUX @@ -157,7 +154,6 @@ all-obj-y += $(addprefix ../, $(universal-obj-y)) ifdef CONFIG_SOFTMMU all-obj-y += $(addprefix ../, $(common-obj-y)) all-obj-y += $(addprefix ../libdis/, $(libdis-y)) -all-obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y)) all-obj-y += $(addprefix ../, $(trace-obj-y)) else all-obj-y += $(addprefix ../libuser/, $(user-obj-y)) diff --git a/QMP/qemu-ga-client b/QMP/qemu-ga-client new file mode 100755 index 000000000..46676c375 --- /dev/null +++ b/QMP/qemu-ga-client @@ -0,0 +1,299 @@ +#!/usr/bin/python + +# QEMU Guest Agent Client +# +# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# Usage: +# +# Start QEMU with: +# +# # qemu [...] -chardev socket,path=/tmp/qga.sock,server,nowait,id=qga0 \ +# -device virtio-serial -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 +# +# Run the script: +# +# $ qemu-ga-client --address=/tmp/qga.sock <command> [args...] +# +# or +# +# $ export QGA_CLIENT_ADDRESS=/tmp/qga.sock +# $ qemu-ga-client <command> [args...] +# +# For example: +# +# $ qemu-ga-client cat /etc/resolv.conf +# # Generated by NetworkManager +# nameserver 10.0.2.3 +# $ qemu-ga-client fsfreeze status +# thawed +# $ qemu-ga-client fsfreeze freeze +# 2 filesystems frozen +# +# See also: http://wiki.qemu.org/Features/QAPI/GuestAgent +# + +import base64 +import random + +import qmp + + +class QemuGuestAgent(qmp.QEMUMonitorProtocol): + def __getattr__(self, name): + def wrapper(**kwds): + return self.command('guest-' + name.replace('_', '-'), **kwds) + return wrapper + + +class QemuGuestAgentClient: + error = QemuGuestAgent.error + + def __init__(self, address): + self.qga = QemuGuestAgent(address) + self.qga.connect(negotiate=False) + + def sync(self, timeout=3): + # Avoid being blocked forever + if not self.ping(timeout): + raise EnvironmentError('Agent seems not alive') + uid = random.randint(0, (1 << 32) - 1) + while True: + ret = self.qga.sync(id=uid) + if isinstance(ret, int) and int(ret) == uid: + break + + def __file_read_all(self, handle): + eof = False + data = '' + while not eof: + ret = self.qga.file_read(handle=handle, count=1024) + _data = base64.b64decode(ret['buf-b64']) + data += _data + eof = ret['eof'] + return data + + def read(self, path): + handle = self.qga.file_open(path=path) + try: + data = self.__file_read_all(handle) + finally: + self.qga.file_close(handle=handle) + return data + + def info(self): + info = self.qga.info() + + msgs = [] + msgs.append('version: ' + info['version']) + msgs.append('supported_commands:') + enabled = [c['name'] for c in info['supported_commands'] if c['enabled']] + msgs.append('\tenabled: ' + ', '.join(enabled)) + disabled = [c['name'] for c in info['supported_commands'] if not c['enabled']] + msgs.append('\tdisabled: ' + ', '.join(disabled)) + + return '\n'.join(msgs) + + def __gen_ipv4_netmask(self, prefixlen): + mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2) + return '.'.join([str(mask >> 24), + str((mask >> 16) & 0xff), + str((mask >> 8) & 0xff), + str(mask & 0xff)]) + + def ifconfig(self): + nifs = self.qga.network_get_interfaces() + + msgs = [] + for nif in nifs: + msgs.append(nif['name'] + ':') + if 'ip-addresses' in nif: + for ipaddr in nif['ip-addresses']: + if ipaddr['ip-address-type'] == 'ipv4': + addr = ipaddr['ip-address'] + mask = self.__gen_ipv4_netmask(int(ipaddr['prefix'])) + msgs.append("\tinet %s netmask %s" % (addr, mask)) + elif ipaddr['ip-address-type'] == 'ipv6': + addr = ipaddr['ip-address'] + prefix = ipaddr['prefix'] + msgs.append("\tinet6 %s prefixlen %s" % (addr, prefix)) + if nif['hardware-address'] != '00:00:00:00:00:00': + msgs.append("\tether " + nif['hardware-address']) + + return '\n'.join(msgs) + + def ping(self, timeout): + self.qga.settimeout(timeout) + try: + self.qga.ping() + except self.qga.timeout: + return False + return True + + def fsfreeze(self, cmd): + if cmd not in ['status', 'freeze', 'thaw']: + raise StandardError('Invalid command: ' + cmd) + + return getattr(self.qga, 'fsfreeze' + '_' + cmd)() + + def fstrim(self, minimum=0): + return getattr(self.qga, 'fstrim')(minimum=minimum) + + def suspend(self, mode): + if mode not in ['disk', 'ram', 'hybrid']: + raise StandardError('Invalid mode: ' + mode) + + try: + getattr(self.qga, 'suspend' + '_' + mode)() + # On error exception will raise + except self.qga.timeout: + # On success command will timed out + return + + def shutdown(self, mode='powerdown'): + if mode not in ['powerdown', 'halt', 'reboot']: + raise StandardError('Invalid mode: ' + mode) + + try: + self.qga.shutdown(mode=mode) + except self.qga.timeout: + return + + +def _cmd_cat(client, args): + if len(args) != 1: + print('Invalid argument') + print('Usage: cat <file>') + sys.exit(1) + print(client.read(args[0])) + + +def _cmd_fsfreeze(client, args): + usage = 'Usage: fsfreeze status|freeze|thaw' + if len(args) != 1: + print('Invalid argument') + print(usage) + sys.exit(1) + if args[0] not in ['status', 'freeze', 'thaw']: + print('Invalid command: ' + args[0]) + print(usage) + sys.exit(1) + cmd = args[0] + ret = client.fsfreeze(cmd) + if cmd == 'status': + print(ret) + elif cmd == 'freeze': + print("%d filesystems frozen" % ret) + else: + print("%d filesystems thawed" % ret) + + +def _cmd_fstrim(client, args): + if len(args) == 0: + minimum = 0 + else: + minimum = int(args[0]) + print(client.fstrim(minimum)) + + +def _cmd_ifconfig(client, args): + print(client.ifconfig()) + + +def _cmd_info(client, args): + print(client.info()) + + +def _cmd_ping(client, args): + if len(args) == 0: + timeout = 3 + else: + timeout = float(args[0]) + alive = client.ping(timeout) + if not alive: + print("Not responded in %s sec" % args[0]) + sys.exit(1) + + +def _cmd_suspend(client, args): + usage = 'Usage: suspend disk|ram|hybrid' + if len(args) != 1: + print('Less argument') + print(usage) + sys.exit(1) + if args[0] not in ['disk', 'ram', 'hybrid']: + print('Invalid command: ' + args[0]) + print(usage) + sys.exit(1) + client.suspend(args[0]) + + +def _cmd_shutdown(client, args): + client.shutdown() +_cmd_powerdown = _cmd_shutdown + + +def _cmd_halt(client, args): + client.shutdown('halt') + + +def _cmd_reboot(client, args): + client.shutdown('reboot') + + +commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m] + + +def main(address, cmd, args): + if not os.path.exists(address): + print('%s not found' % address) + sys.exit(1) + + if cmd not in commands: + print('Invalid command: ' + cmd) + print('Available commands: ' + ', '.join(commands)) + sys.exit(1) + + try: + client = QemuGuestAgentClient(address) + except QemuGuestAgent.error, e: + import errno + + print(e) + if e.errno == errno.ECONNREFUSED: + print('Hint: qemu is not running?') + sys.exit(1) + + if cmd != 'ping': + client.sync() + + globals()['_cmd_' + cmd](client, args) + + +if __name__ == '__main__': + import sys + import os + import optparse + + address = os.environ['QGA_CLIENT_ADDRESS'] if 'QGA_CLIENT_ADDRESS' in os.environ else None + + usage = "%prog [--address=<unix_path>|<ipv4_address>] <command> [args...]\n" + usage += '<command>: ' + ', '.join(commands) + parser = optparse.OptionParser(usage=usage) + parser.add_option('--address', action='store', type='string', + default=address, help='Specify a ip:port pair or a unix socket path') + options, args = parser.parse_args() + + address = options.address + if address is None: + parser.error('address is not specified') + sys.exit(1) + + if len(args) == 0: + parser.error('Less argument') + sys.exit(1) + + main(address, args[0], args[1:]) diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt index 449102042..987c5756b 100644 --- a/QMP/qmp-events.txt +++ b/QMP/qmp-events.txt @@ -96,6 +96,28 @@ Example: "speed": 0 }, "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } +BLOCK_JOB_ERROR +--------------- + +Emitted when a block job encounters an error. + +Data: + +- "device": device name (json-string) +- "operation": I/O operation (json-string, "read" or "write") +- "action": action that has been taken, it's one of the following (json-string): + "ignore": error has been ignored, the job may fail later + "report": error will be reported and the job canceled + "stop": error caused job to be paused + +Example: + +{ "event": "BLOCK_JOB_ERROR", + "data": { "device": "ide0-hd1", + "operation": "write", + "action": "stop" }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } + DEVICE_TRAY_MOVED ----------------- diff --git a/QMP/qmp.py b/QMP/qmp.py index 36ecc1dfa..33c7d36d9 100644 --- a/QMP/qmp.py +++ b/QMP/qmp.py @@ -49,7 +49,6 @@ class QEMUMonitorProtocol: return socket.socket(family, socket.SOCK_STREAM) def __negotiate_capabilities(self): - self.__sockfile = self.__sock.makefile() greeting = self.__json_read() if greeting is None or not greeting.has_key('QMP'): raise QMPConnectError @@ -73,7 +72,7 @@ class QEMUMonitorProtocol: error = socket.error - def connect(self): + def connect(self, negotiate=True): """ Connect to the QMP Monitor and perform capabilities negotiation. @@ -83,7 +82,9 @@ class QEMUMonitorProtocol: @raise QMPCapabilitiesError if fails to negotiate capabilities """ self.__sock.connect(self.__address) - return self.__negotiate_capabilities() + self.__sockfile = self.__sock.makefile() + if negotiate: + return self.__negotiate_capabilities() def accept(self): """ @@ -161,3 +162,8 @@ class QEMUMonitorProtocol: def close(self): self.__sock.close() self.__sockfile.close() + + timeout = socket.timeout + + def settimeout(self, timeout): + self.__sock.settimeout(timeout) @@ -29,6 +29,7 @@ #include "blockjob.h" #include "module.h" #include "qjson.h" +#include "sysemu.h" #include "qemu-coroutine.h" #include "qmp-commands.h" #include "qemu-timer.h" @@ -1387,7 +1388,8 @@ void bdrv_set_dev_ops(BlockDriverState *bs, const BlockDevOps *ops, } void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv, - BlockErrorAction action, int is_read) + enum MonitorEvent ev, + BlockErrorAction action, bool is_read) { QObject *data; const char *action_str; @@ -1410,7 +1412,7 @@ void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv, bdrv->device_name, action_str, is_read ? "read" : "write"); - monitor_protocol_event(QEVENT_BLOCK_IO_ERROR, data); + monitor_protocol_event(ev, data); qobject_decref(data); } @@ -1504,7 +1506,7 @@ int bdrv_commit(BlockDriverState *bs) int n, ro, open_flags; int ret = 0; uint8_t *buf; - char filename[1024]; + char filename[PATH_MAX]; if (!drv) return -ENOMEDIUM; @@ -1518,7 +1520,8 @@ int bdrv_commit(BlockDriverState *bs) } ro = bs->backing_hd->read_only; - strncpy(filename, bs->backing_hd->filename, sizeof(filename)); + /* Use pstrcpy (not strncpy): filename must be NUL-terminated. */ + pstrcpy(filename, sizeof(filename), bs->backing_hd->filename); open_flags = bs->backing_hd->open_flags; if (ro) { @@ -2481,11 +2484,44 @@ void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error, bs->on_write_error = on_write_error; } -BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, int is_read) +BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, bool is_read) { return is_read ? bs->on_read_error : bs->on_write_error; } +BlockErrorAction bdrv_get_error_action(BlockDriverState *bs, bool is_read, int error) +{ + BlockdevOnError on_err = is_read ? bs->on_read_error : bs->on_write_error; + + switch (on_err) { + case BLOCKDEV_ON_ERROR_ENOSPC: + return (error == ENOSPC) ? BDRV_ACTION_STOP : BDRV_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_STOP: + return BDRV_ACTION_STOP; + case BLOCKDEV_ON_ERROR_REPORT: + return BDRV_ACTION_REPORT; + case BLOCKDEV_ON_ERROR_IGNORE: + return BDRV_ACTION_IGNORE; + default: + abort(); + } +} + +/* This is done by device models because, while the block layer knows + * about the error, it does not know whether an operation comes from + * the device or the block layer (from a job, for example). + */ +void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action, + bool is_read, int error) +{ + assert(error >= 0); + bdrv_emit_qmp_error_event(bs, QEVENT_BLOCK_IO_ERROR, action, is_read); + if (action == BDRV_ACTION_STOP) { + vm_stop(RUN_STATE_IO_ERROR); + bdrv_iostatus_set_err(bs, error); + } +} + int bdrv_is_read_only(BlockDriverState *bs) { return bs->read_only; @@ -4226,14 +4262,10 @@ void bdrv_iostatus_reset(BlockDriverState *bs) } } -/* XXX: Today this is set by device models because it makes the implementation - quite simple. However, the block layer knows about the error, so it's - 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 == BLOCK_DEVICE_IO_STATUS_OK) { - assert(error >= 0); + assert(bdrv_iostatus_is_enabled(bs)); + if (bs->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { bs->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : BLOCK_DEVICE_IO_STATUS_FAILED; } @@ -108,8 +108,6 @@ void bdrv_iostatus_reset(BlockDriverState *bs); void bdrv_iostatus_disable(BlockDriverState *bs); bool bdrv_iostatus_is_enabled(const BlockDriverState *bs); void bdrv_iostatus_set_err(BlockDriverState *bs, int error); -void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv, - BlockErrorAction action, int is_read); void bdrv_info_print(Monitor *mon, const QObject *data); void bdrv_info(Monitor *mon, QObject **ret_data); void bdrv_stats_print(Monitor *mon, const QObject *data); @@ -281,7 +279,10 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error, BlockdevOnError on_write_error); -BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, int is_read); +BlockdevOnError bdrv_get_on_error(BlockDriverState *bs, bool is_read); +BlockErrorAction bdrv_get_error_action(BlockDriverState *bs, bool is_read, int error); +void bdrv_error_action(BlockDriverState *bs, BlockErrorAction action, + bool is_read, int error); int bdrv_is_read_only(BlockDriverState *bs); int bdrv_is_sg(BlockDriverState *bs); int bdrv_enable_write_cache(BlockDriverState *bs); diff --git a/block/blkdebug.c b/block/blkdebug.c index 59dcea065..1206d5256 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -28,6 +28,7 @@ typedef struct BDRVBlkdebugState { int state; + int new_state; QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; QSIMPLEQ_HEAD(, BlkdebugRule) active_rules; } BDRVBlkdebugState; @@ -403,12 +404,12 @@ static void blkdebug_close(BlockDriverState *bs) } static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, - int old_state, bool injected) + bool injected) { BDRVBlkdebugState *s = bs->opaque; /* Only process rules for the current state */ - if (rule->state && rule->state != old_state) { + if (rule->state && rule->state != s->state) { return injected; } @@ -423,7 +424,7 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, break; case ACTION_SET_STATE: - s->state = rule->options.set_state.new_state; + s->new_state = rule->options.set_state.new_state; break; } return injected; @@ -433,15 +434,16 @@ static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) { BDRVBlkdebugState *s = bs->opaque; struct BlkdebugRule *rule; - int old_state = s->state; bool injected; assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); injected = false; + s->new_state = s->state; QLIST_FOREACH(rule, &s->rules[event], next) { - injected = process_rule(bs, rule, old_state, injected); + injected = process_rule(bs, rule, injected); } + s->state = s->new_state; } static int64_t blkdebug_getlength(BlockDriverState *bs) diff --git a/block/qcow2.c b/block/qcow2.c index aa5e603cd..c1ff31f48 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1096,6 +1096,7 @@ int qcow2_update_header(BlockDriverState *bs) goto fail; } + /* Using strncpy is ok here, since buf is not NUL-terminated. */ strncpy(buf, bs->backing_file, buflen); header->backing_file_offset = cpu_to_be64(buf - ((char*) header)); diff --git a/block/sheepdog.c b/block/sheepdog.c index 4742f8ae6..f35ff5bbe 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -866,14 +866,14 @@ static int parse_vdiname(BDRVSheepdogState *s, const char *filename, s->port = 0; } - strncpy(vdi, p, SD_MAX_VDI_LEN); + pstrcpy(vdi, SD_MAX_VDI_LEN, p); p = strchr(vdi, ':'); if (p) { *p++ = '\0'; *snapid = strtoul(p, NULL, 10); if (*snapid == 0) { - strncpy(tag, p, SD_MAX_VDI_TAG_LEN); + pstrcpy(tag, SD_MAX_VDI_TAG_LEN, p); } } else { *snapid = CURRENT_VDI_ID; /* search current vdi */ @@ -900,7 +900,10 @@ static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid, return fd; } - memset(buf, 0, sizeof(buf)); + /* This pair of strncpy calls ensures that the buffer is zero-filled, + * which is desirable since we'll soon be sending those bytes, and + * don't want the send_req to read uninitialized data. + */ strncpy(buf, filename, SD_MAX_VDI_LEN); strncpy(buf + SD_MAX_VDI_LEN, tag, SD_MAX_VDI_TAG_LEN); @@ -1149,7 +1152,7 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags) s->max_dirty_data_idx = 0; bs->total_sectors = s->inode.vdi_size / SECTOR_SIZE; - strncpy(s->name, vdi, sizeof(s->name)); + pstrcpy(s->name, sizeof(s->name), vdi); qemu_co_mutex_init(&s->lock); g_free(buf); return 0; @@ -1177,8 +1180,11 @@ static int do_sd_create(char *filename, int64_t vdi_size, return fd; } + /* FIXME: would it be better to fail (e.g., return -EIO) when filename + * does not fit in buf? For now, just truncate and avoid buffer overrun. + */ memset(buf, 0, sizeof(buf)); - strncpy(buf, filename, SD_MAX_VDI_LEN); + pstrcpy(buf, sizeof(buf), filename); memset(&hdr, 0, sizeof(hdr)); hdr.opcode = SD_OP_NEW_VDI; @@ -1752,6 +1758,9 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) s->inode.vm_state_size = sn_info->vm_state_size; s->inode.vm_clock_nsec = sn_info->vm_clock_nsec; + /* It appears that inode.tag does not require a NUL terminator, + * which means this use of strncpy is ok. + */ strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag)); /* we don't need to update entire object */ datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); @@ -1811,13 +1820,13 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) memcpy(old_s, s, sizeof(BDRVSheepdogState)); - memset(vdi, 0, sizeof(vdi)); - strncpy(vdi, s->name, sizeof(vdi)); + pstrcpy(vdi, sizeof(vdi), s->name); - memset(tag, 0, sizeof(tag)); snapid = strtoul(snapshot_id, NULL, 10); - if (!snapid) { - strncpy(tag, s->name, sizeof(tag)); + if (snapid) { + tag[0] = 0; + } else { + pstrcpy(tag, sizeof(tag), s->name); } ret = find_vdi_name(s, vdi, snapid, tag, &vid, 1); @@ -1946,8 +1955,9 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), "%u", inode.snap_id); - strncpy(sn_tab[found].name, inode.tag, - MIN(sizeof(sn_tab[found].name), sizeof(inode.tag))); + pstrcpy(sn_tab[found].name, + MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)), + inode.tag); found++; } } diff --git a/block/stream.c b/block/stream.c index 57e4be7c6..792665276 100644 --- a/block/stream.c +++ b/block/stream.c @@ -31,6 +31,7 @@ typedef struct StreamBlockJob { BlockJob common; RateLimit limit; BlockDriverState *base; + BlockdevOnError on_error; char backing_file_id[1024]; } StreamBlockJob; @@ -78,6 +79,7 @@ static void coroutine_fn stream_run(void *opaque) BlockDriverState *bs = s->common.bs; BlockDriverState *base = s->base; int64_t sector_num, end; + int error = 0; int ret = 0; int n = 0; void *buf; @@ -142,7 +144,19 @@ wait: ret = stream_populate(bs, sector_num, n, buf); } if (ret < 0) { - break; + BlockErrorAction action = + block_job_error_action(&s->common, s->common.bs, s->on_error, + true, -ret); + if (action == BDRV_ACTION_STOP) { + n = 0; + continue; + } + if (error == 0) { + error = ret; + } + if (action == BDRV_ACTION_REPORT) { + break; + } } ret = 0; @@ -154,6 +168,9 @@ wait: bdrv_disable_copy_on_read(bs); } + /* Do not remove the backing file if an error was there but ignored. */ + ret = error; + if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) { const char *base_id = NULL, *base_fmt = NULL; if (base) { @@ -189,11 +206,19 @@ static BlockJobType stream_job_type = { void stream_start(BlockDriverState *bs, BlockDriverState *base, const char *base_id, int64_t speed, + BlockdevOnError on_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp) { StreamBlockJob *s; + if ((on_error == BLOCKDEV_ON_ERROR_STOP || + on_error == BLOCKDEV_ON_ERROR_ENOSPC) && + !bdrv_iostatus_is_enabled(bs)) { + error_set(errp, QERR_INVALID_PARAMETER, "on-error"); + return; + } + s = block_job_create(&stream_job_type, bs, speed, cb, opaque, errp); if (!s) { return; @@ -204,6 +229,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base, pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id); } + s->on_error = on_error; s->common.co = qemu_coroutine_create(stream_run); trace_stream_start(bs, base, s, s->common.co, opaque); qemu_coroutine_enter(s->common.co, s); diff --git a/block/vmdk.c b/block/vmdk.c index f2e861b07..1a80e5a24 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1408,8 +1408,7 @@ static int relative_path(char *dest, int dest_size, return -1; } if (path_is_absolute(target)) { - dest[dest_size - 1] = '\0'; - strncpy(dest, target, dest_size - 1); + pstrcpy(dest, dest_size, target); return 0; } while (base[i] == target[i]) { diff --git a/block_int.h b/block_int.h index 615aafca8..f4bae0440 100644 --- a/block_int.h +++ b/block_int.h @@ -31,6 +31,7 @@ #include "qemu-timer.h" #include "qapi-types.h" #include "qerror.h" +#include "monitor.h" #define BLOCK_FLAG_ENCRYPT 1 #define BLOCK_FLAG_COMPAT6 4 @@ -286,6 +287,9 @@ void bdrv_set_io_limits(BlockDriverState *bs, #ifdef _WIN32 int is_windows_drive(const char *filename); #endif +void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv, + enum MonitorEvent ev, + BlockErrorAction action, bool is_read); /** * stream_start: @@ -295,6 +299,7 @@ int is_windows_drive(const char *filename); * @base_id: The file name that will be written to @bs as the new * backing file if the job completes. Ignored if @base is %NULL. * @speed: The maximum speed, in bytes per second, or 0 for unlimited. + * @on_error: The action to take upon error. * @cb: Completion function for the job. * @opaque: Opaque pointer value passed to @cb. * @errp: Error object. @@ -306,7 +311,7 @@ int is_windows_drive(const char *filename); * @base_id in the written image and to @base in the live BlockDriverState. */ void stream_start(BlockDriverState *bs, BlockDriverState *base, - const char *base_id, int64_t speed, + const char *base_id, int64_t speed, BlockdevOnError on_error, BlockDriverCompletionFunc *cb, void *opaque, Error **errp); diff --git a/blockdev.c b/blockdev.c index ae9c137f8..1db9efecf 100644 --- a/blockdev.c +++ b/blockdev.c @@ -238,7 +238,7 @@ static void drive_put_ref_bh_schedule(DriveInfo *dinfo) qemu_bh_schedule(s->bh); } -static int parse_block_error_action(const char *buf, int is_read) +static int parse_block_error_action(const char *buf, bool is_read) { if (!strcmp(buf, "ignore")) { return BLOCKDEV_ON_ERROR_IGNORE; @@ -1102,13 +1102,18 @@ static void block_job_cb(void *opaque, int ret) } void qmp_block_stream(const char *device, bool has_base, - const char *base, bool has_speed, - int64_t speed, Error **errp) + const char *base, bool has_speed, int64_t speed, + bool has_on_error, BlockdevOnError on_error, + Error **errp) { BlockDriverState *bs; BlockDriverState *base_bs = NULL; Error *local_err = NULL; + if (!has_on_error) { + on_error = BLOCKDEV_ON_ERROR_REPORT; + } + bs = bdrv_find(device); if (!bs) { error_set(errp, QERR_DEVICE_NOT_FOUND, device); @@ -1124,7 +1129,7 @@ void qmp_block_stream(const char *device, bool has_base, } stream_start(bs, base_bs, base, has_speed ? speed : 0, - block_job_cb, bs, &local_err); + on_error, block_job_cb, bs, &local_err); if (error_is_set(&local_err)) { error_propagate(errp, local_err); return; diff --git a/blockjob.c b/blockjob.c index 8219f7397..f55f55a19 100644 --- a/blockjob.c +++ b/blockjob.c @@ -112,6 +112,7 @@ bool block_job_is_paused(BlockJob *job) void block_job_resume(BlockJob *job) { job->paused = false; + block_job_iostatus_reset(job); if (job->co && !job->busy) { qemu_coroutine_enter(job->co, NULL); } @@ -128,6 +129,11 @@ bool block_job_is_cancelled(BlockJob *job) return job->cancelled; } +void block_job_iostatus_reset(BlockJob *job) +{ + job->iostatus = BLOCK_DEVICE_IO_STATUS_OK; +} + struct BlockCancelData { BlockJob *job; BlockDriverCompletionFunc *cb; @@ -189,12 +195,55 @@ void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns) BlockJobInfo *block_job_query(BlockJob *job) { BlockJobInfo *info = g_new0(BlockJobInfo, 1); - info->type = g_strdup(job->job_type->job_type); - info->device = g_strdup(bdrv_get_device_name(job->bs)); - info->len = job->len; - info->busy = job->busy; - info->paused = job->paused; - info->offset = job->offset; - info->speed = job->speed; + info->type = g_strdup(job->job_type->job_type); + info->device = g_strdup(bdrv_get_device_name(job->bs)); + info->len = job->len; + info->busy = job->busy; + info->paused = job->paused; + info->offset = job->offset; + info->speed = job->speed; + info->io_status = job->iostatus; return info; } + +static void block_job_iostatus_set_err(BlockJob *job, int error) +{ + if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) { + job->iostatus = error == ENOSPC ? BLOCK_DEVICE_IO_STATUS_NOSPACE : + BLOCK_DEVICE_IO_STATUS_FAILED; + } +} + + +BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, + BlockdevOnError on_err, + int is_read, int error) +{ + BlockErrorAction action; + + switch (on_err) { + case BLOCKDEV_ON_ERROR_ENOSPC: + action = (error == ENOSPC) ? BDRV_ACTION_STOP : BDRV_ACTION_REPORT; + break; + case BLOCKDEV_ON_ERROR_STOP: + action = BDRV_ACTION_STOP; + break; + case BLOCKDEV_ON_ERROR_REPORT: + action = BDRV_ACTION_REPORT; + break; + case BLOCKDEV_ON_ERROR_IGNORE: + action = BDRV_ACTION_IGNORE; + break; + default: + abort(); + } + bdrv_emit_qmp_error_event(job->bs, QEVENT_BLOCK_JOB_ERROR, action, is_read); + if (action == BDRV_ACTION_STOP) { + block_job_pause(job); + block_job_iostatus_set_err(job, error); + if (bs != job->bs) { + bdrv_iostatus_set_err(bs, error); + } + } + return action; +} diff --git a/blockjob.h b/blockjob.h index ece5afa75..930cc3c46 100644 --- a/blockjob.h +++ b/blockjob.h @@ -82,6 +82,9 @@ struct BlockJob { */ bool busy; + /** Status that is published by the query-block-jobs QMP API */ + BlockDeviceIoStatus iostatus; + /** Offset that is published by the query-block-jobs QMP API */ int64_t offset; @@ -215,4 +218,26 @@ bool block_job_is_paused(BlockJob *job); */ int block_job_cancel_sync(BlockJob *job); +/** + * block_job_iostatus_reset: + * @job: The job whose I/O status should be reset. + * + * Reset I/O status on @job. + */ +void block_job_iostatus_reset(BlockJob *job); + +/** + * block_job_error_action: + * @job: The job to signal an error for. + * @bs: The block device on which to set an I/O error. + * @on_err: The error action setting. + * @is_read: Whether the operation was a read. + * @error: The error that was reported. + * + * Report an I/O error for a block job and possibly stop the VM. Return the + * action that was selected based on @on_err and @error. + */ +BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, + BlockdevOnError on_err, + int is_read, int error); #endif @@ -2787,10 +2787,10 @@ fi # check for usbredirparser for usb network redirection support if test "$usb_redir" != "no" ; then - if $pkg_config --atleast-version=0.5 libusbredirparser >/dev/null 2>&1 ; then + if $pkg_config --atleast-version=0.5 libusbredirparser-0.5 >/dev/null 2>&1 ; then usb_redir="yes" - usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null) - usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null) + usb_redir_cflags=$($pkg_config --cflags libusbredirparser-0.5 2>/dev/null) + usb_redir_libs=$($pkg_config --libs libusbredirparser-0.5 2>/dev/null) QEMU_CFLAGS="$QEMU_CFLAGS $usb_redir_cflags" libs_softmmu="$libs_softmmu $usb_redir_libs" else @@ -3738,7 +3738,6 @@ TARGET_ABI_DIR="" case "$target_arch2" in i386) - target_phys_bits=64 ;; x86_64) TARGET_BASE_ARCH=i386 @@ -3746,7 +3745,6 @@ case "$target_arch2" in target_long_alignment=8 ;; alpha) - target_phys_bits=64 target_long_alignment=8 target_nptl="yes" ;; @@ -3755,22 +3753,18 @@ case "$target_arch2" in bflt="yes" target_nptl="yes" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" - target_phys_bits=64 target_llong_alignment=4 target_libs_softmmu="$fdt_libs" ;; cris) target_nptl="yes" - target_phys_bits=32 ;; lm32) - target_phys_bits=32 target_libs_softmmu="$opengl_libs" ;; m68k) bflt="yes" gdb_xml_files="cf-core.xml cf-fp.xml" - target_phys_bits=32 target_int_alignment=2 target_long_alignment=2 target_llong_alignment=2 @@ -3779,36 +3773,30 @@ case "$target_arch2" in TARGET_ARCH=microblaze bflt="yes" target_nptl="yes" - target_phys_bits=32 target_libs_softmmu="$fdt_libs" ;; mips|mipsel) TARGET_ARCH=mips echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak target_nptl="yes" - target_phys_bits=64 ;; mipsn32|mipsn32el) TARGET_ARCH=mipsn32 TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak - target_phys_bits=64 ;; mips64|mips64el) TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak - target_phys_bits=64 target_long_alignment=8 ;; or32) TARGET_ARCH=openrisc TARGET_BASE_ARCH=openrisc - target_phys_bits=32 ;; ppc) gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_phys_bits=64 target_nptl="yes" target_libs_softmmu="$fdt_libs" ;; @@ -3816,7 +3804,6 @@ case "$target_arch2" in TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_phys_bits=64 target_nptl="yes" target_libs_softmmu="$fdt_libs" ;; @@ -3824,7 +3811,6 @@ case "$target_arch2" in TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_phys_bits=64 target_long_alignment=8 target_libs_softmmu="$fdt_libs" ;; @@ -3834,21 +3820,17 @@ case "$target_arch2" in TARGET_ABI_DIR=ppc echo "TARGET_ABI32=y" >> $config_target_mak gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_phys_bits=64 target_libs_softmmu="$fdt_libs" ;; sh4|sh4eb) TARGET_ARCH=sh4 bflt="yes" target_nptl="yes" - target_phys_bits=32 ;; sparc) - target_phys_bits=64 ;; sparc64) TARGET_BASE_ARCH=sparc - target_phys_bits=64 target_long_alignment=8 ;; sparc32plus) @@ -3856,11 +3838,9 @@ case "$target_arch2" in TARGET_BASE_ARCH=sparc TARGET_ABI_DIR=sparc echo "TARGET_ABI32=y" >> $config_target_mak - target_phys_bits=64 ;; s390x) target_nptl="yes" - target_phys_bits=64 target_long_alignment=8 ;; unicore32) @@ -3868,7 +3848,6 @@ case "$target_arch2" in ;; xtensa|xtensaeb) TARGET_ARCH=xtensa - target_phys_bits=32 ;; *) echo "Unsupported target CPU" @@ -3903,7 +3882,6 @@ echo "TARGET_ABI_DIR=$TARGET_ABI_DIR" >> $config_target_mak case "$target_arch2" in i386|x86_64) if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then - target_phys_bits=64 echo "CONFIG_XEN=y" >> $config_target_mak if test "$xen_pci_passthrough" = yes; then echo "CONFIG_XEN_PCI_PASSTHROUGH=y" >> "$config_target_mak" @@ -3944,11 +3922,8 @@ if test "$target_bigendian" = "yes" ; then echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak fi if test "$target_softmmu" = "yes" ; then - echo "TARGET_PHYS_ADDR_BITS=$target_phys_bits" >> $config_target_mak echo "CONFIG_SOFTMMU=y" >> $config_target_mak echo "LIBS+=$libs_softmmu $target_libs_softmmu" >> $config_target_mak - echo "HWDIR=../libhw$target_phys_bits" >> $config_target_mak - echo "subdir-$target: subdir-libhw$target_phys_bits" >> $config_host_mak if test "$smartcard_nss" = "yes" ; then echo "subdir-$target: subdir-libcacard" >> $config_host_mak fi @@ -4190,12 +4165,6 @@ for rom in seabios vgabios ; do echo "LD=$ld" >> $config_mak done -for hwlib in 32 64; do - d=libhw$hwlib - symlink "$source_path/Makefile.hw" "$d/Makefile" - echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak -done - d=libuser symlink "$source_path/Makefile.user" "$d/Makefile" diff --git a/cpu-common.h b/cpu-common.h index 85548de5e..c0d27afd8 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -21,7 +21,7 @@ enum device_endian { }; /* address in the RAM (different from a physical address) */ -#if defined(CONFIG_XEN_BACKEND) && TARGET_PHYS_ADDR_BITS == 64 +#if defined(CONFIG_XEN_BACKEND) typedef uint64_t ram_addr_t; # define RAM_ADDR_MAX UINT64_MAX # define RAM_ADDR_FMT "%" PRIx64 @@ -115,7 +115,7 @@ time_t mktimegm(struct tm *tm) m += 12; y--; } - t = 86400 * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + + t = 86400ULL * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 719469); t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec; return t; @@ -31,7 +31,7 @@ struct QEMUSGList { DMAContext *dma; }; -#if defined(TARGET_PHYS_ADDR_BITS) +#ifndef CONFIG_USER_ONLY /* * When an IOMMU is present, bus addresses become distinct from @@ -100,18 +100,11 @@ static void dump_error(DumpState *s, const char *reason) static int fd_write_vmcore(void *buf, size_t size, void *opaque) { DumpState *s = opaque; - int fd = s->fd; - size_t writen_size; + size_t written_size; - /* The fd may be passed from user, and it can be non-blocked */ - while (size) { - writen_size = qemu_write_full(fd, buf, size); - if (writen_size != size && errno != EAGAIN) { - return -1; - } - - buf += writen_size; - size -= writen_size; + written_size = qemu_write_full(s->fd, buf, size); + if (written_size != size) { + return -1; } return 0; @@ -836,9 +829,8 @@ void qmp_dump_guest_memory(bool paging, const char *file, bool has_begin, #if !defined(WIN32) if (strstart(file, "fd:", &p)) { - fd = monitor_get_fd(cur_mon, p); + fd = monitor_get_fd(cur_mon, p, errp); if (fd == -1) { - error_set(errp, QERR_FD_NOT_FOUND, p); return; } } diff --git a/exec-obsolete.h b/exec-obsolete.h index c09925610..286e2f75e 100644 --- a/exec-obsolete.h +++ b/exec-obsolete.h @@ -24,6 +24,7 @@ #endif #ifndef CONFIG_USER_ONLY +#include "hw/xen.h" ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr); @@ -111,6 +112,7 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, for (addr = start; addr < end; addr += TARGET_PAGE_SIZE) { cpu_physical_memory_set_dirty_flags(addr, dirty_flags); } + xen_modified_memory(addr, length); } static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start, @@ -3417,6 +3417,19 @@ int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr, } #else + +static void invalidate_and_set_dirty(target_phys_addr_t addr, + target_phys_addr_t length) +{ + if (!cpu_physical_memory_is_dirty(addr)) { + /* invalidate code */ + tb_invalidate_phys_page_range(addr, addr + length, 0); + /* set dirty bit */ + cpu_physical_memory_set_dirty_flags(addr, (0xff & ~CODE_DIRTY_FLAG)); + } + xen_modified_memory(addr, length); +} + void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, int len, int is_write) { @@ -3462,13 +3475,7 @@ void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, /* RAM case */ ptr = qemu_get_ram_ptr(addr1); memcpy(ptr, buf, l); - if (!cpu_physical_memory_is_dirty(addr1)) { - /* invalidate code */ - tb_invalidate_phys_page_range(addr1, addr1 + l, 0); - /* set dirty bit */ - cpu_physical_memory_set_dirty_flags( - addr1, (0xff & ~CODE_DIRTY_FLAG)); - } + invalidate_and_set_dirty(addr1, l); qemu_put_ram_ptr(ptr); } } else { @@ -3534,13 +3541,7 @@ void cpu_physical_memory_write_rom(target_phys_addr_t addr, /* ROM/RAM case */ ptr = qemu_get_ram_ptr(addr1); memcpy(ptr, buf, l); - if (!cpu_physical_memory_is_dirty(addr1)) { - /* invalidate code */ - tb_invalidate_phys_page_range(addr1, addr1 + l, 0); - /* set dirty bit */ - cpu_physical_memory_set_dirty_flags( - addr1, (0xff & ~CODE_DIRTY_FLAG)); - } + invalidate_and_set_dirty(addr1, l); qemu_put_ram_ptr(ptr); } len -= l; @@ -3666,13 +3667,7 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, l = TARGET_PAGE_SIZE; if (l > access_len) l = access_len; - if (!cpu_physical_memory_is_dirty(addr1)) { - /* invalidate code */ - tb_invalidate_phys_page_range(addr1, addr1 + l, 0); - /* set dirty bit */ - cpu_physical_memory_set_dirty_flags( - addr1, (0xff & ~CODE_DIRTY_FLAG)); - } + invalidate_and_set_dirty(addr1, l); addr1 += l; access_len -= l; } @@ -3978,13 +3973,7 @@ static inline void stl_phys_internal(target_phys_addr_t addr, uint32_t val, stl_p(ptr, val); break; } - if (!cpu_physical_memory_is_dirty(addr1)) { - /* invalidate code */ - tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); - /* set dirty bit */ - cpu_physical_memory_set_dirty_flags(addr1, - (0xff & ~CODE_DIRTY_FLAG)); - } + invalidate_and_set_dirty(addr1, 4); } } @@ -4051,13 +4040,7 @@ static inline void stw_phys_internal(target_phys_addr_t addr, uint32_t val, stw_p(ptr, val); break; } - if (!cpu_physical_memory_is_dirty(addr1)) { - /* invalidate code */ - tb_invalidate_phys_page_range(addr1, addr1 + 2, 0); - /* set dirty bit */ - cpu_physical_memory_set_dirty_flags(addr1, - (0xff & ~CODE_DIRTY_FLAG)); - } + invalidate_and_set_dirty(addr1, 2); } } diff --git a/fpu/softfloat.c b/fpu/softfloat.c index b29256a8e..841314686 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1238,7 +1238,7 @@ float32 uint64_to_float32( uint64 a STATUS_PARAM ) if ( a == 0 ) return float32_zero; shiftCount = countLeadingZeros64( a ) - 40; if ( 0 <= shiftCount ) { - return packFloat32( 1 > 0, 0x95 - shiftCount, a<<shiftCount ); + return packFloat32(0, 0x95 - shiftCount, a<<shiftCount); } else { shiftCount += 7; @@ -1248,7 +1248,7 @@ float32 uint64_to_float32( uint64 a STATUS_PARAM ) else { a <<= shiftCount; } - return roundAndPackFloat32( 1 > 0, 0x9C - shiftCount, a STATUS_VAR ); + return roundAndPackFloat32(0, 0x9C - shiftCount, a STATUS_VAR); } } @@ -3007,7 +3007,7 @@ float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM) if (aSig) { return commonNaNToFloat32(float16ToCommonNaN(a STATUS_VAR) STATUS_VAR); } - return packFloat32(aSign, 0xff, aSig << 13); + return packFloat32(aSign, 0xff, 0); } if (aExp == 0) { int8 shiftCount; diff --git a/hmp-commands.hx b/hmp-commands.hx index c6aa564a6..698e11420 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -943,12 +943,11 @@ ETEXI #if defined(CONFIG_HAVE_CORE_DUMP) { .name = "dump-guest-memory", - .args_type = "paging:-p,protocol:s,begin:i?,length:i?", - .params = "[-p] protocol [begin] [length]", + .args_type = "paging:-p,filename:F,begin:i?,length:i?", + .params = "[-p] filename [begin] [length]", .help = "dump guest memory to file" "\n\t\t\t begin(optional): the starting physical address" "\n\t\t\t length(optional): the memory size, in bytes", - .user_print = monitor_user_noop, .mhandler.cmd = hmp_dump_guest_memory, }, @@ -958,8 +957,7 @@ STEXI @findex dump-guest-memory Dump guest memory to @var{protocol}. The file can be processed with crash or gdb. - protocol: destination file(started with "file:") or destination file - descriptor (started with "fd:") + filename: dump file name paging: do paging to get guest's memory mapping begin: the starting physical address. It's optional, and should be specified with length together. @@ -930,7 +930,8 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) int64_t speed = qdict_get_try_int(qdict, "speed", 0); qmp_block_stream(device, base != NULL, base, - qdict_haskey(qdict, "speed"), speed, &error); + qdict_haskey(qdict, "speed"), speed, + BLOCKDEV_ON_ERROR_REPORT, true, &error); hmp_handle_error(mon, &error); } @@ -1063,11 +1064,12 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) { Error *errp = NULL; int paging = qdict_get_try_bool(qdict, "paging", 0); - const char *file = qdict_get_str(qdict, "protocol"); + const char *file = qdict_get_str(qdict, "filename"); bool has_begin = qdict_haskey(qdict, "begin"); bool has_length = qdict_haskey(qdict, "length"); int64_t begin = 0; int64_t length = 0; + char *prot; if (has_begin) { begin = qdict_get_int(qdict, "begin"); @@ -1076,9 +1078,12 @@ void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict) length = qdict_get_int(qdict, "length"); } - qmp_dump_guest_memory(paging, file, has_begin, begin, has_length, length, + prot = g_strconcat("file:", file, NULL); + + qmp_dump_guest_memory(paging, prot, has_begin, begin, has_length, length, &errp); hmp_handle_error(mon, &errp); + g_free(prot); } void hmp_netdev_add(Monitor *mon, const QDict *qdict) @@ -1130,13 +1135,13 @@ void hmp_closefd(Monitor *mon, const QDict *qdict) void hmp_send_key(Monitor *mon, const QDict *qdict) { const char *keys = qdict_get_str(qdict, "keys"); - QKeyCodeList *keylist, *head = NULL, *tmp = NULL; + KeyValueList *keylist, *head = NULL, *tmp = NULL; int has_hold_time = qdict_haskey(qdict, "hold-time"); int hold_time = qdict_get_try_int(qdict, "hold-time", -1); Error *err = NULL; char keyname_buf[16]; char *separator; - int keyname_len, idx; + int keyname_len; while (1) { separator = strchr(keys, '-'); @@ -1150,15 +1155,8 @@ void hmp_send_key(Monitor *mon, const QDict *qdict) } keyname_buf[keyname_len] = 0; - idx = index_from_key(keyname_buf); - if (idx == Q_KEY_CODE_MAX) { - monitor_printf(mon, "invalid parameter: %s\n", keyname_buf); - break; - } - keylist = g_malloc0(sizeof(*keylist)); - keylist->value = idx; - keylist->next = NULL; + keylist->value = g_malloc0(sizeof(*keylist->value)); if (!head) { head = keylist; @@ -1168,17 +1166,39 @@ void hmp_send_key(Monitor *mon, const QDict *qdict) } tmp = keylist; + if (strstart(keyname_buf, "0x", NULL)) { + char *endp; + int value = strtoul(keyname_buf, &endp, 0); + if (*endp != '\0') { + goto err_out; + } + keylist->value->kind = KEY_VALUE_KIND_NUMBER; + keylist->value->number = value; + } else { + int idx = index_from_key(keyname_buf); + if (idx == Q_KEY_CODE_MAX) { + goto err_out; + } + keylist->value->kind = KEY_VALUE_KIND_QCODE; + keylist->value->qcode = idx; + } + if (!separator) { break; } keys = separator + 1; } - if (idx != Q_KEY_CODE_MAX) { - qmp_send_key(head, has_hold_time, hold_time, &err); - } + qmp_send_key(head, has_hold_time, hold_time, &err); hmp_handle_error(mon, &err); - qapi_free_QKeyCodeList(head); + +out: + qapi_free_KeyValueList(head); + return; + +err_out: + monitor_printf(mon, "invalid parameter: %s\n", keyname_buf); + goto out; } void hmp_screen_dump(Monitor *mon, const QDict *qdict) diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs index 972df2405..1e9b595cb 100644 --- a/hw/9pfs/Makefile.objs +++ b/hw/9pfs/Makefile.objs @@ -1,9 +1,9 @@ -hw-obj-y = virtio-9p.o -hw-obj-y += virtio-9p-local.o virtio-9p-xattr.o -hw-obj-y += virtio-9p-xattr-user.o virtio-9p-posix-acl.o -hw-obj-y += virtio-9p-coth.o cofs.o codir.o cofile.o -hw-obj-y += coxattr.o virtio-9p-synth.o -hw-obj-$(CONFIG_OPEN_BY_HANDLE) += virtio-9p-handle.o -hw-obj-y += virtio-9p-proxy.o +common-obj-y = virtio-9p.o +common-obj-y += virtio-9p-local.o virtio-9p-xattr.o +common-obj-y += virtio-9p-xattr-user.o virtio-9p-posix-acl.o +common-obj-y += virtio-9p-coth.o cofs.o codir.o cofile.o +common-obj-y += coxattr.o virtio-9p-synth.o +common-obj-$(CONFIG_OPEN_BY_HANDLE) += virtio-9p-handle.o +common-obj-y += virtio-9p-proxy.o obj-y += virtio-9p-device.o diff --git a/hw/9pfs/virtio-9p-posix-acl.c b/hw/9pfs/virtio-9p-posix-acl.c index a1948e3af..c064017b1 100644 --- a/hw/9pfs/virtio-9p-posix-acl.c +++ b/hw/9pfs/virtio-9p-posix-acl.c @@ -44,7 +44,8 @@ static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path, return -1; } - strncpy(value, ACL_ACCESS, len); + /* len includes the trailing NUL */ + memcpy(value, ACL_ACCESS, len); return 0; } @@ -95,7 +96,8 @@ static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path, return -1; } - strncpy(value, ACL_DEFAULT, len); + /* len includes the trailing NUL */ + memcpy(value, ACL_ACCESS, len); return 0; } diff --git a/hw/9pfs/virtio-9p-synth.c b/hw/9pfs/virtio-9p-synth.c index 92e0b09d3..e95a856d2 100644 --- a/hw/9pfs/virtio-9p-synth.c +++ b/hw/9pfs/virtio-9p-synth.c @@ -58,7 +58,7 @@ static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode, node->attr->read = NULL; } node->private = node; - strncpy(node->name, name, sizeof(node->name)); + pstrcpy(node->name, sizeof(node->name), name); QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); return node; } @@ -132,7 +132,7 @@ int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode, node->attr->write = write; node->attr->mode = mode; node->private = arg; - strncpy(node->name, name, sizeof(node->name)); + pstrcpy(node->name, sizeof(node->name), name); QLIST_INSERT_HEAD_RCU(&parent->child, node, sibling); ret = 0; err_out: diff --git a/hw/9pfs/virtio-9p-xattr-user.c b/hw/9pfs/virtio-9p-xattr-user.c index 5044a3e5a..5bb602007 100644 --- a/hw/9pfs/virtio-9p-xattr-user.c +++ b/hw/9pfs/virtio-9p-xattr-user.c @@ -61,7 +61,8 @@ static ssize_t mp_user_listxattr(FsContext *ctx, const char *path, return -1; } - strncpy(value, name, name_size); + /* name_size includes the trailing NUL. */ + memcpy(value, name, name_size); return name_size; } diff --git a/hw/9pfs/virtio-9p-xattr.c b/hw/9pfs/virtio-9p-xattr.c index 7f08f6e17..a83960676 100644 --- a/hw/9pfs/virtio-9p-xattr.c +++ b/hw/9pfs/virtio-9p-xattr.c @@ -53,7 +53,8 @@ ssize_t pt_listxattr(FsContext *ctx, const char *path, return -1; } - strncpy(value, name, name_size); + /* no need for strncpy: name_size is strlen(name)+1 */ + memcpy(value, name, name_size); return name_size; } diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 850b87b02..24392f568 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -1,141 +1,141 @@ -hw-obj-y = usb/ ide/ -hw-obj-y += loader.o -hw-obj-$(CONFIG_VIRTIO) += virtio-console.o -hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o -hw-obj-y += fw_cfg.o -hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o -hw-obj-$(CONFIG_PCI) += msix.o msi.o -hw-obj-$(CONFIG_PCI) += shpc.o -hw-obj-$(CONFIG_PCI) += slotid_cap.o -hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o -hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o -hw-obj-y += watchdog.o -hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o -hw-obj-$(CONFIG_ECC) += ecc.o -hw-obj-$(CONFIG_NAND) += nand.o -hw-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o -hw-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o - -hw-obj-$(CONFIG_M48T59) += m48t59.o -hw-obj-$(CONFIG_ESCC) += escc.o -hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o - -hw-obj-$(CONFIG_SERIAL) += serial.o -hw-obj-$(CONFIG_PARALLEL) += parallel.o -hw-obj-$(CONFIG_I8254) += i8254_common.o i8254.o -hw-obj-$(CONFIG_PCSPK) += pcspk.o -hw-obj-$(CONFIG_PCKBD) += pckbd.o -hw-obj-$(CONFIG_FDC) += fdc.o -# needs fixes for cpu hotplug, so moved to Makefile.target: -# hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o -hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o -hw-obj-$(CONFIG_DMA) += dma.o -hw-obj-$(CONFIG_I82374) += i82374.o -hw-obj-$(CONFIG_HPET) += hpet.o -hw-obj-$(CONFIG_APPLESMC) += applesmc.o -hw-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o -hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o -hw-obj-$(CONFIG_I8259) += i8259_common.o i8259.o +common-obj-y = usb/ ide/ +common-obj-y += loader.o +common-obj-$(CONFIG_VIRTIO) += virtio-console.o +common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o +common-obj-y += fw_cfg.o +common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o +common-obj-$(CONFIG_PCI) += msix.o msi.o +common-obj-$(CONFIG_PCI) += shpc.o +common-obj-$(CONFIG_PCI) += slotid_cap.o +common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o +common-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o +common-obj-y += watchdog.o +common-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o +common-obj-$(CONFIG_ECC) += ecc.o +common-obj-$(CONFIG_NAND) += nand.o +common-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o +common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o + +common-obj-$(CONFIG_M48T59) += m48t59.o +common-obj-$(CONFIG_ESCC) += escc.o +common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o + +common-obj-$(CONFIG_SERIAL) += serial.o +common-obj-$(CONFIG_PARALLEL) += parallel.o +common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o +common-obj-$(CONFIG_PCSPK) += pcspk.o +common-obj-$(CONFIG_PCKBD) += pckbd.o +common-obj-$(CONFIG_FDC) += fdc.o +# qemu-kvm: acpi_piix4.c uses TARGET_I386 define +#common-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o +common-obj-$(CONFIG_APM) += pm_smbus.o apm.o +common-obj-$(CONFIG_DMA) += dma.o +common-obj-$(CONFIG_I82374) += i82374.o +common-obj-$(CONFIG_HPET) += hpet.o +common-obj-$(CONFIG_APPLESMC) += applesmc.o +common-obj-$(CONFIG_SMARTCARD) += ccid-card-passthru.o +common-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o +common-obj-$(CONFIG_I8259) += i8259_common.o i8259.o # PPC devices -hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o -hw-obj-$(CONFIG_I82378) += i82378.o +common-obj-$(CONFIG_PREP_PCI) += prep_pci.o +common-obj-$(CONFIG_I82378) += i82378.o # Mac shared devices -hw-obj-$(CONFIG_MACIO) += macio.o -hw-obj-$(CONFIG_CUDA) += cuda.o -hw-obj-$(CONFIG_ADB) += adb.o -hw-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o -hw-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o +common-obj-$(CONFIG_MACIO) += macio.o +common-obj-$(CONFIG_CUDA) += cuda.o +common-obj-$(CONFIG_ADB) += adb.o +common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o +common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o # OldWorld PowerMac -hw-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o -hw-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o +common-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o +common-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o # NewWorld PowerMac -hw-obj-$(CONFIG_UNIN_PCI) += unin_pci.o -hw-obj-$(CONFIG_DEC_PCI) += dec_pci.o +common-obj-$(CONFIG_UNIN_PCI) += unin_pci.o +common-obj-$(CONFIG_DEC_PCI) += dec_pci.o # PowerPC E500 boards -hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o +common-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o # MIPS devices -hw-obj-$(CONFIG_PIIX4) += piix4.o -hw-obj-$(CONFIG_G364FB) += g364fb.o -hw-obj-$(CONFIG_JAZZ_LED) += jazz_led.o +common-obj-$(CONFIG_PIIX4) += piix4.o +common-obj-$(CONFIG_G364FB) += g364fb.o +common-obj-$(CONFIG_JAZZ_LED) += jazz_led.o # Xilinx devices -hw-obj-$(CONFIG_XILINX) += xilinx_intc.o -hw-obj-$(CONFIG_XILINX) += xilinx_timer.o -hw-obj-$(CONFIG_XILINX) += xilinx_uartlite.o -hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o -hw-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o -hw-obj-$(CONFIG_XILINX_AXI) += stream.o +common-obj-$(CONFIG_XILINX) += xilinx_intc.o +common-obj-$(CONFIG_XILINX) += xilinx_timer.o +common-obj-$(CONFIG_XILINX) += xilinx_uartlite.o +common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o +common-obj-$(CONFIG_XILINX_AXI) += xilinx_axienet.o +common-obj-$(CONFIG_XILINX_AXI) += stream.o # PKUnity SoC devices -hw-obj-$(CONFIG_PUV3) += puv3_intc.o -hw-obj-$(CONFIG_PUV3) += puv3_ost.o -hw-obj-$(CONFIG_PUV3) += puv3_gpio.o -hw-obj-$(CONFIG_PUV3) += puv3_pm.o -hw-obj-$(CONFIG_PUV3) += puv3_dma.o +common-obj-$(CONFIG_PUV3) += puv3_intc.o +common-obj-$(CONFIG_PUV3) += puv3_ost.o +common-obj-$(CONFIG_PUV3) += puv3_gpio.o +common-obj-$(CONFIG_PUV3) += puv3_pm.o +common-obj-$(CONFIG_PUV3) += puv3_dma.o # ARM devices -hw-obj-$(CONFIG_ARM_TIMER) += arm_timer.o -hw-obj-$(CONFIG_PL011) += pl011.o -hw-obj-$(CONFIG_PL022) += pl022.o -hw-obj-$(CONFIG_PL031) += pl031.o -hw-obj-$(CONFIG_PL041) += pl041.o lm4549.o -hw-obj-$(CONFIG_PL050) += pl050.o -hw-obj-$(CONFIG_PL061) += pl061.o -hw-obj-$(CONFIG_PL080) += pl080.o -hw-obj-$(CONFIG_PL110) += pl110.o -hw-obj-$(CONFIG_PL181) += pl181.o -hw-obj-$(CONFIG_PL190) += pl190.o -hw-obj-$(CONFIG_PL310) += arm_l2x0.o -hw-obj-$(CONFIG_VERSATILE_PCI) += versatile_pci.o -hw-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o -hw-obj-$(CONFIG_CADENCE) += cadence_uart.o -hw-obj-$(CONFIG_CADENCE) += cadence_ttc.o -hw-obj-$(CONFIG_CADENCE) += cadence_gem.o -hw-obj-$(CONFIG_XGMAC) += xgmac.o +common-obj-$(CONFIG_ARM_TIMER) += arm_timer.o +common-obj-$(CONFIG_PL011) += pl011.o +common-obj-$(CONFIG_PL022) += pl022.o +common-obj-$(CONFIG_PL031) += pl031.o +common-obj-$(CONFIG_PL041) += pl041.o lm4549.o +common-obj-$(CONFIG_PL050) += pl050.o +common-obj-$(CONFIG_PL061) += pl061.o +common-obj-$(CONFIG_PL080) += pl080.o +common-obj-$(CONFIG_PL110) += pl110.o +common-obj-$(CONFIG_PL181) += pl181.o +common-obj-$(CONFIG_PL190) += pl190.o +common-obj-$(CONFIG_PL310) += arm_l2x0.o +common-obj-$(CONFIG_VERSATILE_PCI) += versatile_pci.o +common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o +common-obj-$(CONFIG_CADENCE) += cadence_uart.o +common-obj-$(CONFIG_CADENCE) += cadence_ttc.o +common-obj-$(CONFIG_CADENCE) += cadence_gem.o +common-obj-$(CONFIG_XGMAC) += xgmac.o # PCI watchdog devices -hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o +common-obj-$(CONFIG_PCI) += wdt_i6300esb.o -hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o +common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o # PCI network cards -hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o -hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o -hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o -hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o -hw-obj-$(CONFIG_E1000_PCI) += e1000.o -hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o - -hw-obj-$(CONFIG_SMC91C111) += smc91c111.o -hw-obj-$(CONFIG_LAN9118) += lan9118.o -hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o -hw-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o +common-obj-$(CONFIG_NE2000_PCI) += ne2000.o +common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o +common-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o +common-obj-$(CONFIG_PCNET_COMMON) += pcnet.o +common-obj-$(CONFIG_E1000_PCI) += e1000.o +common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o + +common-obj-$(CONFIG_SMC91C111) += smc91c111.o +common-obj-$(CONFIG_LAN9118) += lan9118.o +common-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o +common-obj-$(CONFIG_OPENCORES_ETH) += opencores_eth.o # SCSI layer -hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o -hw-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o -hw-obj-$(CONFIG_ESP) += esp.o -hw-obj-$(CONFIG_ESP_PCI) += esp-pci.o +common-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o +common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o +common-obj-$(CONFIG_ESP) += esp.o +common-obj-$(CONFIG_ESP_PCI) += esp-pci.o -hw-obj-y += sysbus.o isa-bus.o -hw-obj-y += qdev-addr.o +common-obj-y += sysbus.o isa-bus.o +common-obj-y += qdev-addr.o # VGA -hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o -hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o -hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o -hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o -hw-obj-$(CONFIG_VMMOUSE) += vmmouse.o -hw-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o +common-obj-$(CONFIG_VGA_PCI) += vga-pci.o +common-obj-$(CONFIG_VGA_ISA) += vga-isa.o +common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o +common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o +common-obj-$(CONFIG_VMMOUSE) += vmmouse.o +common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o -hw-obj-$(CONFIG_RC4030) += rc4030.o -hw-obj-$(CONFIG_DP8393X) += dp8393x.o -hw-obj-$(CONFIG_DS1225Y) += ds1225y.o -hw-obj-$(CONFIG_MIPSNET) += mipsnet.o +common-obj-$(CONFIG_RC4030) += rc4030.o +common-obj-$(CONFIG_DP8393X) += dp8393x.o +common-obj-$(CONFIG_DS1225Y) += ds1225y.o +common-obj-$(CONFIG_MIPSNET) += mipsnet.o -hw-obj-y += null-machine.o +common-obj-y += null-machine.o # Sound sound-obj-y = @@ -149,9 +149,9 @@ sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o $(obj)/adlib.o $(obj)/fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0 -hw-obj-$(CONFIG_SOUND) += $(sound-obj-y) +common-obj-$(CONFIG_SOUND) += $(sound-obj-y) -hw-obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/ +common-obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/ common-obj-y += usb/ common-obj-y += irq.o @@ -199,7 +199,8 @@ obj-$(CONFIG_VGA) += vga.o obj-$(CONFIG_SOFTMMU) += device-hotplug.o obj-$(CONFIG_XEN) += xen_domainbuild.o xen_machine_pv.o -# Inter-VM PCI shared memory +# Inter-VM PCI shared memory & VFIO PCI device assignment ifeq ($(CONFIG_PCI), y) obj-$(CONFIG_KVM) += ivshmem.o +obj-$(CONFIG_LINUX) += vfio_pci.o endif @@ -61,18 +61,6 @@ static int acpi_checksum(const uint8_t *data, int len) return (-sum) & 0xff; } -/* like strncpy() but zero-fills the tail of destination */ -static void strzcpy(char *dst, const char *src, size_t size) -{ - size_t len = strlen(src); - if (len >= size) { - len = size; - } else { - memset(dst + len, 0, size - len); - } - memcpy(dst, src, len); -} - /* XXX fixme: this function uses obsolete argument parsing interface */ int acpi_table_add(const char *t) { @@ -157,7 +145,8 @@ int acpi_table_add(const char *t) hdr._length = cpu_to_le16(len); if (get_param_value(buf, sizeof(buf), "sig", t)) { - strzcpy(hdr.sig, buf, sizeof(hdr.sig)); + /* strncpy is justified: the field need not be NUL-terminated. */ + strncpy(hdr.sig, buf, sizeof(hdr.sig)); ++changed; } @@ -187,12 +176,14 @@ int acpi_table_add(const char *t) } if (get_param_value(buf, sizeof(buf), "oem_id", t)) { - strzcpy(hdr.oem_id, buf, sizeof(hdr.oem_id)); + /* strncpy is justified: the field need not be NUL-terminated. */ + strncpy(hdr.oem_id, buf, sizeof(hdr.oem_id)); ++changed; } if (get_param_value(buf, sizeof(buf), "oem_table_id", t)) { - strzcpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id)); + /* strncpy is justified: the field need not be NUL-terminated. */ + strncpy(hdr.oem_table_id, buf, sizeof(hdr.oem_table_id)); ++changed; } @@ -207,7 +198,8 @@ int acpi_table_add(const char *t) } if (get_param_value(buf, sizeof(buf), "asl_compiler_id", t)) { - strzcpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id)); + /* strncpy is justified: the field need not be NUL-terminated. */ + strncpy(hdr.asl_compiler_id, buf, sizeof(hdr.asl_compiler_id)); ++changed; } diff --git a/hw/bt-hci.c b/hw/bt-hci.c index a3a7fb49e..47f9a4e49 100644 --- a/hw/bt-hci.c +++ b/hw/bt-hci.c @@ -943,7 +943,6 @@ static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr) { struct bt_device_s *slave; evt_remote_name_req_complete params; - int len; for (slave = hci->device.net->slave; slave; slave = slave->next) if (slave->page_scan && !bacmp(&slave->bd_addr, bdaddr)) @@ -955,9 +954,7 @@ static int bt_hci_name_req(struct bt_hci_s *hci, bdaddr_t *bdaddr) params.status = HCI_SUCCESS; bacpy(¶ms.bdaddr, &slave->bd_addr); - len = snprintf(params.name, sizeof(params.name), - "%s", slave->lmp_name ?: ""); - memset(params.name + len, 0, sizeof(params.name) - len); + pstrcpy(params.name, sizeof(params.name), slave->lmp_name ?: ""); bt_hci_event(hci, EVT_REMOTE_NAME_REQ_COMPLETE, ¶ms, EVT_REMOTE_NAME_REQ_COMPLETE_SIZE); @@ -1388,7 +1385,7 @@ static inline void bt_hci_event_complete_read_local_name(struct bt_hci_s *hci) params.status = HCI_SUCCESS; memset(params.name, 0, sizeof(params.name)); if (hci->device.lmp_name) - strncpy(params.name, hci->device.lmp_name, sizeof(params.name)); + pstrcpy(params.name, sizeof(params.name), hci->device.lmp_name); bt_hci_event_complete(hci, ¶ms, READ_LOCAL_NAME_RP_SIZE); } @@ -4,7 +4,7 @@ #include "qemu-common.h" -#if defined(TARGET_PHYS_ADDR_BITS) && !defined(NEED_CPU_H) +#if !defined(CONFIG_USER_ONLY) && !defined(NEED_CPU_H) #include "cpu-common.h" #endif diff --git a/hw/ide/Makefile.objs b/hw/ide/Makefile.objs index cf718dd01..5c8c22aad 100644 --- a/hw/ide/Makefile.objs +++ b/hw/ide/Makefile.objs @@ -1,10 +1,10 @@ -hw-obj-$(CONFIG_IDE_CORE) += core.o atapi.o -hw-obj-$(CONFIG_IDE_QDEV) += qdev.o -hw-obj-$(CONFIG_IDE_PCI) += pci.o -hw-obj-$(CONFIG_IDE_ISA) += isa.o -hw-obj-$(CONFIG_IDE_PIIX) += piix.o -hw-obj-$(CONFIG_IDE_CMD646) += cmd646.o -hw-obj-$(CONFIG_IDE_MACIO) += macio.o -hw-obj-$(CONFIG_IDE_VIA) += via.o -hw-obj-$(CONFIG_AHCI) += ahci.o -hw-obj-$(CONFIG_AHCI) += ich.o +common-obj-$(CONFIG_IDE_CORE) += core.o atapi.o +common-obj-$(CONFIG_IDE_QDEV) += qdev.o +common-obj-$(CONFIG_IDE_PCI) += pci.o +common-obj-$(CONFIG_IDE_ISA) += isa.o +common-obj-$(CONFIG_IDE_PIIX) += piix.o +common-obj-$(CONFIG_IDE_CMD646) += cmd646.o +common-obj-$(CONFIG_IDE_MACIO) += macio.o +common-obj-$(CONFIG_IDE_VIA) += via.o +common-obj-$(CONFIG_AHCI) += ahci.o +common-obj-$(CONFIG_AHCI) += ich.o diff --git a/hw/ide/core.c b/hw/ide/core.c index 2620e87ae..d683a8cc8 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -556,32 +556,22 @@ void ide_dma_error(IDEState *s) static int ide_handle_rw_error(IDEState *s, int error, int op) { - int is_read = (op & BM_STATUS_RETRY_READ); - BlockdevOnError action = bdrv_get_on_error(s->bs, is_read); + bool is_read = (op & BM_STATUS_RETRY_READ) != 0; + BlockErrorAction action = bdrv_get_error_action(s->bs, is_read, error); - if (action == BLOCKDEV_ON_ERROR_IGNORE) { - bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_IGNORE, is_read); - return 0; - } - - if ((error == ENOSPC && action == BLOCKDEV_ON_ERROR_ENOSPC) - || action == BLOCKDEV_ON_ERROR_STOP) { + if (action == BDRV_ACTION_STOP) { s->bus->dma->ops->set_unit(s->bus->dma, s->unit); s->bus->error_status = op; - bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_STOP, is_read); - vm_stop(RUN_STATE_IO_ERROR); - bdrv_iostatus_set_err(s->bs, error); - } else { + } else if (action == BDRV_ACTION_REPORT) { if (op & BM_STATUS_DMA_RETRY) { dma_buf_commit(s); ide_dma_error(s); } else { ide_rw_error(s); } - bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_REPORT, is_read); } - - return 1; + bdrv_error_action(s->bs, action, is_read, error); + return action != BDRV_ACTION_IGNORE; } void ide_dma_cb(void *opaque, int ret) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 88c0942e3..644533f77 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -188,7 +188,7 @@ static void bmdma_restart_bh(void *opaque) { BMDMAState *bm = opaque; IDEBus *bus = bm->bus; - int is_read; + bool is_read; int error_status; qemu_bh_delete(bm->bh); @@ -198,7 +198,7 @@ static void bmdma_restart_bh(void *opaque) return; } - is_read = !!(bus->error_status & BM_STATUS_RETRY_READ); + is_read = (bus->error_status & BM_STATUS_RETRY_READ) != 0; /* The error status must be cleared before resubmitting the request: The * request may fail again, and this case can only be distinguished if the diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 5ea9b8f4b..f2e4ea420 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -60,7 +60,7 @@ static char *idebus_get_fw_dev_path(DeviceState *dev) snprintf(path, sizeof(path), "%s@%d", qdev_fw_name(dev), ((IDEBus*)dev->parent_bus)->bus_id); - return strdup(path); + return g_strdup(path); } static int ide_qdev_init(DeviceState *qdev) diff --git a/hw/intel-hda.c b/hw/intel-hda.c index 127e81888..d8e1b23a6 100644 --- a/hw/intel-hda.c +++ b/hw/intel-hda.c @@ -210,13 +210,7 @@ static target_phys_addr_t intel_hda_addr(uint32_t lbase, uint32_t ubase) { target_phys_addr_t addr; -#if TARGET_PHYS_ADDR_BITS == 32 - addr = lbase; -#else - addr = ubase; - addr <<= 32; - addr |= lbase; -#endif + addr = ((uint64_t)ubase << 32) | lbase; return addr; } diff --git a/hw/isa-bus.c b/hw/isa-bus.c index f9b237387..47c93d37b 100644 --- a/hw/isa-bus.c +++ b/hw/isa-bus.c @@ -236,7 +236,7 @@ static char *isabus_get_fw_dev_path(DeviceState *dev) snprintf(path + off, sizeof(path) - off, "@%04x", d->ioport_id); } - return strdup(path); + return g_strdup(path); } MemoryRegion *isa_address_space(ISADevice *dev) diff --git a/hw/kvm/pci-assign.c b/hw/kvm/pci-assign.c index 05b93d9a5..7a0998c51 100644 --- a/hw/kvm/pci-assign.c +++ b/hw/kvm/pci-assign.c @@ -579,15 +579,9 @@ static int get_real_device(AssignedDevice *pci_dev, uint16_t r_seg, snprintf(name, sizeof(name), "%sconfig", dir); if (pci_dev->configfd_name && *pci_dev->configfd_name) { - if (qemu_isdigit(pci_dev->configfd_name[0])) { - dev->config_fd = strtol(pci_dev->configfd_name, NULL, 0); - } else { - dev->config_fd = monitor_get_fd(cur_mon, pci_dev->configfd_name); - if (dev->config_fd < 0) { - error_report("%s: (%s) unkown", __func__, - pci_dev->configfd_name); - return 1; - } + dev->config_fd = monitor_handle_fd_param(cur_mon, pci_dev->configfd_name); + if (dev->config_fd < 0) { + return 1; } } else { dev->config_fd = open(name, O_RDWR); diff --git a/hw/lm32_hwsetup.h b/hw/lm32_hwsetup.h index 8fc285efc..70dc61f9e 100644 --- a/hw/lm32_hwsetup.h +++ b/hw/lm32_hwsetup.h @@ -96,7 +96,7 @@ static inline void hwsetup_add_tag(HWSetup *hw, enum hwsetup_tag t) static inline void hwsetup_add_str(HWSetup *hw, const char *str) { - strncpy(hw->ptr, str, 31); /* make sure last byte is zero */ + pstrcpy(hw->ptr, 32, str); hw->ptr += 32; } diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c index d63554f89..332a77d4d 100644 --- a/hw/mc146818rtc.c +++ b/hw/mc146818rtc.c @@ -399,6 +399,10 @@ static void cmos_ioport_write(void *opaque, uint32_t addr, uint32_t data) s->cmos_data[s->cmos_index] = data; check_update_timer(s); break; + case RTC_IBM_PS2_CENTURY_BYTE: + s->cmos_index = RTC_CENTURY; + /* fall through */ + case RTC_CENTURY: case RTC_SECONDS: case RTC_MINUTES: case RTC_HOURS: @@ -515,7 +519,9 @@ static void rtc_get_time(RTCState *s, struct tm *tm) tm->tm_wday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_WEEK]) - 1; tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]); tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1; - tm->tm_year = rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year - 1900; + tm->tm_year = + rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year + + rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; } static void rtc_set_time(RTCState *s) @@ -548,10 +554,9 @@ static void rtc_set_cmos(RTCState *s, const struct tm *tm) s->cmos_data[RTC_DAY_OF_WEEK] = rtc_to_bcd(s, tm->tm_wday + 1); s->cmos_data[RTC_DAY_OF_MONTH] = rtc_to_bcd(s, tm->tm_mday); s->cmos_data[RTC_MONTH] = rtc_to_bcd(s, tm->tm_mon + 1); - year = (tm->tm_year - s->base_year) % 100; - if (year < 0) - year += 100; - s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year); + year = tm->tm_year + 1900 - s->base_year; + s->cmos_data[RTC_YEAR] = rtc_to_bcd(s, year % 100); + s->cmos_data[RTC_CENTURY] = rtc_to_bcd(s, year / 100); } static void rtc_update_time(RTCState *s) @@ -598,6 +603,10 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) return 0xff; } else { switch(s->cmos_index) { + case RTC_IBM_PS2_CENTURY_BYTE: + s->cmos_index = RTC_CENTURY; + /* fall through */ + case RTC_CENTURY: case RTC_SECONDS: case RTC_MINUTES: case RTC_HOURS: @@ -661,15 +670,10 @@ void rtc_set_memory(ISADevice *dev, int addr, int val) s->cmos_data[addr] = val; } -/* PC cmos mappings */ -#define REG_IBM_CENTURY_BYTE 0x32 -#define REG_IBM_PS2_CENTURY_BYTE 0x37 - static void rtc_set_date_from_host(ISADevice *dev) { RTCState *s = DO_UPCAST(RTCState, dev, dev); struct tm tm; - int val; qemu_get_timedate(&tm, 0); @@ -679,10 +683,6 @@ static void rtc_set_date_from_host(ISADevice *dev) /* set the CMOS date */ rtc_set_cmos(s, &tm); - - val = rtc_to_bcd(s, (tm.tm_year / 100) + 19); - rtc_set_memory(dev, REG_IBM_CENTURY_BYTE, val); - rtc_set_memory(dev, REG_IBM_PS2_CENTURY_BYTE, val); } static int rtc_post_load(void *opaque, int version_id) @@ -807,6 +807,18 @@ static int rtc_initfn(ISADevice *dev) s->cmos_data[RTC_REG_C] = 0x00; s->cmos_data[RTC_REG_D] = 0x80; + /* This is for historical reasons. The default base year qdev property + * was set to 2000 for most machine types before the century byte was + * implemented. + * + * This if statement means that the century byte will be always 0 + * (at least until 2079...) for base_year = 1980, but will be set + * correctly for base_year = 2000. + */ + if (s->base_year == 2000) { + s->base_year = 0; + } + rtc_set_date_from_host(dev); #ifdef TARGET_I386 diff --git a/hw/mc146818rtc_regs.h b/hw/mc146818rtc_regs.h index fc10076ec..ccdee42b3 100644 --- a/hw/mc146818rtc_regs.h +++ b/hw/mc146818rtc_regs.h @@ -44,6 +44,10 @@ #define RTC_REG_C 12 #define RTC_REG_D 13 +/* PC cmos mappings */ +#define RTC_CENTURY 0x32 +#define RTC_IBM_PS2_CENTURY_BYTE 0x37 + #define REG_A_UIP 0x80 #define REG_B_SET 0x80 @@ -1962,7 +1962,7 @@ static char *pcibus_get_fw_dev_path(DeviceState *dev) PCI_SLOT(d->devfn)); if (PCI_FUNC(d->devfn)) snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn)); - return strdup(path); + return g_strdup(path); } static char *pcibus_get_dev_path(DeviceState *dev) @@ -520,7 +520,7 @@ char* qdev_get_fw_dev_path(DeviceState *dev) path[l-1] = '\0'; - return strdup(path); + return g_strdup(path); } char *qdev_get_dev_path(DeviceState *dev) @@ -332,6 +332,8 @@ static void r2d_init(ram_addr_t ram_size, } if (kernel_cmdline) { + /* I see no evidence that this .kernel_cmdline buffer requires + NUL-termination, so using strncpy should be ok. */ strncpy(boot_params.kernel_cmdline, kernel_cmdline, sizeof(boot_params.kernel_cmdline)); } diff --git a/hw/rtl8139.c b/hw/rtl8139.c index 844f1b8c3..b7c82ee02 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -774,11 +774,7 @@ static void rtl8139_write_buffer(RTL8139State *s, const void *buf, int size) #define MIN_BUF_SIZE 60 static inline dma_addr_t rtl8139_addr64(uint32_t low, uint32_t high) { -#if TARGET_PHYS_ADDR_BITS > 32 - return low | ((target_phys_addr_t)high << 32); -#else - return low; -#endif + return low | ((uint64_t)high << 32); } /* Workaround for buggy guest driver such as linux who allocates rx diff --git a/hw/scsi-bus.c b/hw/scsi-bus.c index 058d3b237..dfb263121 100644 --- a/hw/scsi-bus.c +++ b/hw/scsi-bus.c @@ -1723,12 +1723,8 @@ static char *scsibus_get_dev_path(DeviceState *dev) static char *scsibus_get_fw_dev_path(DeviceState *dev) { SCSIDevice *d = SCSI_DEVICE(dev); - char path[100]; - - snprintf(path, sizeof(path), "channel@%x/%s@%x,%x", d->channel, - qdev_fw_name(dev), d->id, d->lun); - - return strdup(path); + return g_strdup_printf("channel@%x/%s@%x,%x", d->channel, + qdev_fw_name(dev), d->id, d->lun); } SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun) diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index c295326e9..99bb02ebf 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -386,23 +386,11 @@ static void scsi_read_data(SCSIRequest *req) */ static int scsi_handle_rw_error(SCSIDiskReq *r, int error) { - int is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV); + bool is_read = (r->req.cmd.xfer == SCSI_XFER_FROM_DEV); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - BlockdevOnError action = bdrv_get_on_error(s->qdev.conf.bs, is_read); + BlockErrorAction action = bdrv_get_error_action(s->qdev.conf.bs, is_read, error); - if (action == BLOCKDEV_ON_ERROR_IGNORE) { - bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_IGNORE, is_read); - return 0; - } - - if ((error == ENOSPC && action == BLOCKDEV_ON_ERROR_ENOSPC) - || action == BLOCKDEV_ON_ERROR_STOP) { - - bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_STOP, is_read); - vm_stop(RUN_STATE_IO_ERROR); - bdrv_iostatus_set_err(s->qdev.conf.bs, error); - scsi_req_retry(&r->req); - } else { + if (action == BDRV_ACTION_REPORT) { switch (error) { case ENOMEDIUM: scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); @@ -417,9 +405,12 @@ static int scsi_handle_rw_error(SCSIDiskReq *r, int error) scsi_check_condition(r, SENSE_CODE(IO_ERROR)); break; } - bdrv_emit_qmp_error_event(s->qdev.conf.bs, BDRV_ACTION_REPORT, is_read); } - return 1; + bdrv_error_action(s->qdev.conf.bs, action, is_read, error); + if (action == BDRV_ACTION_STOP) { + scsi_req_retry(&r->req); + } + return action != BDRV_ACTION_IGNORE; } static void scsi_write_complete(void * opaque, int ret) diff --git a/hw/spapr_vscsi.c b/hw/spapr_vscsi.c index 3cf5844e0..e3d4b237f 100644 --- a/hw/spapr_vscsi.c +++ b/hw/spapr_vscsi.c @@ -737,7 +737,7 @@ static int vscsi_send_adapter_info(VSCSIState *s, vscsi_req *req) #endif memset(&info, 0, sizeof(info)); strcpy(info.srp_version, SRP_VERSION); - strncpy(info.partition_name, "qemu", sizeof("qemu")); + memcpy(info.partition_name, "qemu", sizeof("qemu")); info.partition_number = cpu_to_be32(0); info.mad_version = cpu_to_be32(1); info.os_type = cpu_to_be32(2); diff --git a/hw/sysbus.c b/hw/sysbus.c index 9d8b1eaf7..c1738403d 100644 --- a/hw/sysbus.c +++ b/hw/sysbus.c @@ -211,7 +211,7 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev) snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]); } - return strdup(path); + return g_strdup(path); } void sysbus_add_memory(SysBusDevice *dev, target_phys_addr_t addr, diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 4225136d0..6425c1ff7 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -1,11 +1,11 @@ -hw-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o -hw-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o -hw-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o -hw-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o -hw-obj-y += libhw.o +common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o +common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o +common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o +common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o +common-obj-y += libhw.o -hw-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o -hw-obj-$(CONFIG_USB_REDIR) += redirect.o +common-obj-$(CONFIG_SMARTCARD) += dev-smartcard-reader.o +common-obj-$(CONFIG_USB_REDIR) += redirect.o common-obj-y += core.o bus.o desc.o dev-hub.o common-obj-y += host-$(HOST_USB).o dev-bluetooth.o diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index 6a5da8413..8bdb806b9 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -373,6 +373,7 @@ struct EHCIQueue { uint32_t seen; uint64_t ts; int async; + int transact_ctr; /* cached data from guest - needs to be flushed * when guest removes an entry (doorbell, handshake sequence) @@ -1837,6 +1838,11 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async) } q->qh = qh; + q->transact_ctr = get_field(q->qh.epcap, QH_EPCAP_MULT); + if (q->transact_ctr == 0) { /* Guest bug in some versions of windows */ + q->transact_ctr = 4; + } + if (q->dev == NULL) { q->dev = ehci_find_device(q->ehci, devaddr); } @@ -2014,11 +2020,8 @@ static int ehci_state_fetchqtd(EHCIQueue *q) } else if (p != NULL) { switch (p->async) { case EHCI_ASYNC_NONE: - /* Should never happen packet should at least be initialized */ - assert(0); - break; case EHCI_ASYNC_INITIALIZED: - /* Previously nacked packet (likely interrupt ep) */ + /* Not yet executed (MULT), or previously nacked (int) packet */ ehci_set_state(q->ehci, q->async, EST_EXECUTE); break; case EHCI_ASYNC_INFLIGHT: @@ -2107,15 +2110,12 @@ static int ehci_state_execute(EHCIQueue *q) // TODO verify enough time remains in the uframe as in 4.4.1.1 // TODO write back ptr to async list when done or out of time - // TODO Windows does not seem to ever set the MULT field - if (!q->async) { - int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT); - if (!transactCtr) { - ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = 1; - goto out; - } + /* 4.10.3, bottom of page 82, go horizontal on transaction counter == 0 */ + if (!q->async && q->transact_ctr == 0) { + ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); + again = 1; + goto out; } if (q->async) { @@ -2132,7 +2132,11 @@ static int ehci_state_execute(EHCIQueue *q) trace_usb_ehci_packet_action(p->queue, p, "async"); p->async = EHCI_ASYNC_INFLIGHT; ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); - again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1; + if (q->async) { + again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1; + } else { + again = 1; + } goto out; } @@ -2152,13 +2156,9 @@ static int ehci_state_executing(EHCIQueue *q) ehci_execute_complete(q); - // 4.10.3 - if (!q->async) { - int transactCtr = get_field(q->qh.epcap, QH_EPCAP_MULT); - transactCtr--; - set_field(&q->qh.epcap, transactCtr, QH_EPCAP_MULT); - // 4.10.3, bottom of page 82, should exit this state when transaction - // counter decrements to 0 + /* 4.10.3 */ + if (!q->async && q->transact_ctr > 0) { + q->transact_ctr--; } /* 4.10.5 */ diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index e0ca69044..e79a8724c 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -37,12 +37,12 @@ #define FIXME() do { fprintf(stderr, "FIXME %s:%d\n", \ __func__, __LINE__); abort(); } while (0) -#define MAXPORTS_2 8 -#define MAXPORTS_3 8 +#define MAXPORTS_2 15 +#define MAXPORTS_3 15 #define MAXPORTS (MAXPORTS_2+MAXPORTS_3) -#define MAXSLOTS MAXPORTS -#define MAXINTRS MAXPORTS +#define MAXSLOTS 64 +#define MAXINTRS 16 #define TD_QUEUE 24 @@ -285,6 +285,8 @@ typedef enum TRBCCode { #define SLOT_CONTEXT_ENTRIES_MASK 0x1f #define SLOT_CONTEXT_ENTRIES_SHIFT 27 +typedef struct XHCIState XHCIState; + typedef enum EPType { ET_INVALID = 0, ET_ISO_OUT, @@ -303,15 +305,15 @@ typedef struct XHCIRing { } XHCIRing; typedef struct XHCIPort { + XHCIState *xhci; uint32_t portsc; uint32_t portnr; USBPort *uport; uint32_t speedmask; + char name[16]; + MemoryRegion mem; } XHCIPort; -struct XHCIState; -typedef struct XHCIState XHCIState; - typedef struct XHCITransfer { XHCIState *xhci; USBPacket packet; @@ -363,7 +365,7 @@ typedef struct XHCIEPContext { typedef struct XHCISlot { bool enabled; dma_addr_t ctx; - unsigned int port; + USBPort *uport; unsigned int devaddr; XHCIEPContext * eps[31]; } XHCISlot; @@ -1230,7 +1232,7 @@ static TRBCCode xhci_reset_ep(XHCIState *xhci, unsigned int slotid, ep |= 0x80; } - dev = xhci->ports[xhci->slots[slotid-1].port-1].uport->dev; + dev = xhci->slots[slotid-1].uport->dev; if (!dev) { return CC_USB_TRANSACTION_ERROR; } @@ -1412,18 +1414,9 @@ static void xhci_stall_ep(XHCITransfer *xfer) static int xhci_submit(XHCIState *xhci, XHCITransfer *xfer, XHCIEPContext *epctx); -static USBDevice *xhci_find_device(XHCIPort *port, uint8_t addr) -{ - if (!(port->portsc & PORTSC_PED)) { - return NULL; - } - return usb_find_device(port->uport, addr); -} - static int xhci_setup_packet(XHCITransfer *xfer) { XHCIState *xhci = xfer->xhci; - XHCIPort *port; USBDevice *dev; USBEndpoint *ep; int dir; @@ -1434,13 +1427,12 @@ static int xhci_setup_packet(XHCITransfer *xfer) ep = xfer->packet.ep; dev = ep->dev; } else { - port = &xhci->ports[xhci->slots[xfer->slotid-1].port-1]; - dev = xhci_find_device(port, xhci->slots[xfer->slotid-1].devaddr); - if (!dev) { - fprintf(stderr, "xhci: slot %d port %d has no device\n", - xfer->slotid, xhci->slots[xfer->slotid-1].port); + if (!xhci->slots[xfer->slotid-1].uport) { + fprintf(stderr, "xhci: slot %d has no device\n", + xfer->slotid); return -1; } + dev = xhci->slots[xfer->slotid-1].uport->dev; ep = usb_ep_get(dev, dir, xfer->epid >> 1); } @@ -1772,7 +1764,7 @@ static TRBCCode xhci_enable_slot(XHCIState *xhci, unsigned int slotid) trace_usb_xhci_slot_enable(slotid); assert(slotid >= 1 && slotid <= MAXSLOTS); xhci->slots[slotid-1].enabled = 1; - xhci->slots[slotid-1].port = 0; + xhci->slots[slotid-1].uport = NULL; memset(xhci->slots[slotid-1].eps, 0, sizeof(XHCIEPContext*)*31); return CC_SUCCESS; @@ -1795,17 +1787,42 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid) return CC_SUCCESS; } +static USBPort *xhci_lookup_uport(XHCIState *xhci, uint32_t *slot_ctx) +{ + USBPort *uport; + char path[32]; + int i, pos, port; + + port = (slot_ctx[1]>>16) & 0xFF; + port = xhci->ports[port-1].uport->index+1; + pos = snprintf(path, sizeof(path), "%d", port); + for (i = 0; i < 5; i++) { + port = (slot_ctx[0] >> 4*i) & 0x0f; + if (!port) { + break; + } + pos += snprintf(path + pos, sizeof(path) - pos, ".%d", port); + } + + QTAILQ_FOREACH(uport, &xhci->bus.used, next) { + if (strcmp(uport->path, path) == 0) { + return uport; + } + } + return NULL; +} + static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, uint64_t pictx, bool bsr) { XHCISlot *slot; + USBPort *uport; USBDevice *dev; dma_addr_t ictx, octx, dcbaap; uint64_t poctx; uint32_t ictl_ctx[2]; uint32_t slot_ctx[4]; uint32_t ep0_ctx[5]; - unsigned int port; int i; TRBCCode res; @@ -1837,27 +1854,28 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, DPRINTF("xhci: input ep0 context: %08x %08x %08x %08x %08x\n", ep0_ctx[0], ep0_ctx[1], ep0_ctx[2], ep0_ctx[3], ep0_ctx[4]); - port = (slot_ctx[1]>>16) & 0xFF; - dev = xhci->ports[port-1].uport->dev; - - if (port < 1 || port > xhci->numports) { - fprintf(stderr, "xhci: bad port %d\n", port); + uport = xhci_lookup_uport(xhci, slot_ctx); + if (uport == NULL) { + fprintf(stderr, "xhci: port not found\n"); return CC_TRB_ERROR; - } else if (!dev) { - fprintf(stderr, "xhci: port %d not connected\n", port); + } + + dev = uport->dev; + if (!dev) { + fprintf(stderr, "xhci: port %s not connected\n", uport->path); return CC_USB_TRANSACTION_ERROR; } for (i = 0; i < MAXSLOTS; i++) { - if (xhci->slots[i].port == port) { - fprintf(stderr, "xhci: port %d already assigned to slot %d\n", - port, i+1); + if (xhci->slots[i].uport == uport) { + fprintf(stderr, "xhci: port %s already assigned to slot %d\n", + uport->path, i+1); return CC_TRB_ERROR; } } slot = &xhci->slots[slotid-1]; - slot->port = port; + slot->uport = uport; slot->ctx = octx; if (bsr) { @@ -2414,20 +2432,14 @@ static uint64_t xhci_cap_read(void *ptr, target_phys_addr_t reg, unsigned size) return ret; } -static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg) +static uint64_t xhci_port_read(void *ptr, target_phys_addr_t reg, unsigned size) { - uint32_t port = reg >> 4; + XHCIPort *port = ptr; uint32_t ret; - if (port >= xhci->numports) { - fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port); - ret = 0; - goto out; - } - - switch (reg & 0xf) { + switch (reg) { case 0x00: /* PORTSC */ - ret = xhci->ports[port].portsc; + ret = port->portsc; break; case 0x04: /* PORTPMSC */ case 0x08: /* PORTLI */ @@ -2436,30 +2448,25 @@ static uint32_t xhci_port_read(XHCIState *xhci, uint32_t reg) case 0x0c: /* reserved */ default: fprintf(stderr, "xhci_port_read (port %d): reg 0x%x unimplemented\n", - port, reg); + port->portnr, (uint32_t)reg); ret = 0; } -out: - trace_usb_xhci_port_read(port, reg & 0x0f, ret); + trace_usb_xhci_port_read(port->portnr, reg, ret); return ret; } -static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) +static void xhci_port_write(void *ptr, target_phys_addr_t reg, + uint64_t val, unsigned size) { - uint32_t port = reg >> 4; + XHCIPort *port = ptr; uint32_t portsc; - trace_usb_xhci_port_write(port, reg & 0x0f, val); - - if (port >= xhci->numports) { - fprintf(stderr, "xhci_port_read: port %d out of bounds\n", port); - return; - } + trace_usb_xhci_port_write(port->portnr, reg, val); - switch (reg & 0xf) { + switch (reg) { case 0x00: /* PORTSC */ - portsc = xhci->ports[port].portsc; + portsc = port->portsc; /* write-1-to-clear bits*/ portsc &= ~(val & (PORTSC_CSC|PORTSC_PEC|PORTSC_WRC|PORTSC_OCC| PORTSC_PRC|PORTSC_PLC|PORTSC_CEC)); @@ -2474,16 +2481,16 @@ static void xhci_port_write(XHCIState *xhci, uint32_t reg, uint32_t val) /* write-1-to-start bits */ if (val & PORTSC_PR) { DPRINTF("xhci: port %d reset\n", port); - usb_device_reset(xhci->ports[port].uport->dev); + usb_device_reset(port->uport->dev); portsc |= PORTSC_PRC | PORTSC_PED; } - xhci->ports[port].portsc = portsc; + port->portsc = portsc; break; case 0x04: /* PORTPMSC */ case 0x08: /* PORTLI */ default: fprintf(stderr, "xhci_port_write (port %d): reg 0x%x unimplemented\n", - port, reg); + port->portnr, (uint32_t)reg); } } @@ -2492,10 +2499,6 @@ static uint64_t xhci_oper_read(void *ptr, target_phys_addr_t reg, unsigned size) XHCIState *xhci = ptr; uint32_t ret; - if (reg >= 0x400) { - return xhci_port_read(xhci, reg - 0x400); - } - switch (reg) { case 0x00: /* USBCMD */ ret = xhci->usbcmd; @@ -2538,11 +2541,6 @@ static void xhci_oper_write(void *ptr, target_phys_addr_t reg, { XHCIState *xhci = ptr; - if (reg >= 0x400) { - xhci_port_write(xhci, reg - 0x400, val); - return; - } - trace_usb_xhci_oper_write(reg, val); switch (reg) { @@ -2761,6 +2759,14 @@ static const MemoryRegionOps xhci_oper_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static const MemoryRegionOps xhci_port_ops = { + .read = xhci_port_read, + .write = xhci_port_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static const MemoryRegionOps xhci_runtime_ops = { .read = xhci_runtime_read, .write = xhci_runtime_write, @@ -2821,12 +2827,20 @@ static void xhci_complete(USBPort *port, USBPacket *packet) xhci_kick_ep(xfer->xhci, xfer->slotid, xfer->epid); } -static void xhci_child_detach(USBPort *port, USBDevice *child) +static void xhci_child_detach(USBPort *uport, USBDevice *child) { - FIXME(); + USBBus *bus = usb_bus_from_device(child); + XHCIState *xhci = container_of(bus, XHCIState, bus); + int i; + + for (i = 0; i < MAXSLOTS; i++) { + if (xhci->slots[i].uport == uport) { + xhci->slots[i].uport = NULL; + } + } } -static USBPortOps xhci_port_ops = { +static USBPortOps xhci_uport_ops = { .attach = xhci_attach, .detach = xhci_detach, .wakeup = xhci_wakeup, @@ -2906,6 +2920,7 @@ static void usb_xhci_init(XHCIState *xhci, DeviceState *dev) USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL | USB_SPEED_MASK_HIGH; + snprintf(port->name, sizeof(port->name), "usb2 port #%d", i+1); speedmask |= port->speedmask; } if (i < xhci->numports_3) { @@ -2913,16 +2928,17 @@ static void usb_xhci_init(XHCIState *xhci, DeviceState *dev) port->portnr = i + 1 + xhci->numports_2; port->uport = &xhci->uports[i]; port->speedmask = USB_SPEED_MASK_SUPER; + snprintf(port->name, sizeof(port->name), "usb3 port #%d", i+1); speedmask |= port->speedmask; } usb_register_port(&xhci->bus, &xhci->uports[i], xhci, i, - &xhci_port_ops, speedmask); + &xhci_uport_ops, speedmask); } } static int usb_xhci_initfn(struct PCIDevice *dev) { - int ret; + int i, ret; XHCIState *xhci = DO_UPCAST(XHCIState, pci_dev, dev); @@ -2941,7 +2957,7 @@ static int usb_xhci_initfn(struct PCIDevice *dev) memory_region_init_io(&xhci->mem_cap, &xhci_cap_ops, xhci, "capabilities", LEN_CAP); memory_region_init_io(&xhci->mem_oper, &xhci_oper_ops, xhci, - "operational", 0x400 + 0x10 * xhci->numports); + "operational", 0x400); memory_region_init_io(&xhci->mem_runtime, &xhci_runtime_ops, xhci, "runtime", LEN_RUNTIME); memory_region_init_io(&xhci->mem_doorbell, &xhci_doorbell_ops, xhci, @@ -2952,6 +2968,15 @@ static int usb_xhci_initfn(struct PCIDevice *dev) memory_region_add_subregion(&xhci->mem, OFF_RUNTIME, &xhci->mem_runtime); memory_region_add_subregion(&xhci->mem, OFF_DOORBELL, &xhci->mem_doorbell); + for (i = 0; i < xhci->numports; i++) { + XHCIPort *port = &xhci->ports[i]; + uint32_t offset = OFF_OPER + 0x400 + 0x10 * i; + port->xhci = xhci; + memory_region_init_io(&port->mem, &xhci_port_ops, port, + port->name, 0x10); + memory_region_add_subregion(&xhci->mem, offset, &port->mem); + } + pci_register_bar(&xhci->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64, &xhci->mem); diff --git a/hw/usb/libhw.c b/hw/usb/libhw.c index c0de30ea8..703e2d213 100644 --- a/hw/usb/libhw.c +++ b/hw/usb/libhw.c @@ -28,19 +28,25 @@ int usb_packet_map(USBPacket *p, QEMUSGList *sgl) { DMADirection dir = (p->pid == USB_TOKEN_IN) ? DMA_DIRECTION_FROM_DEVICE : DMA_DIRECTION_TO_DEVICE; - dma_addr_t len; void *mem; int i; for (i = 0; i < sgl->nsg; i++) { - len = sgl->sg[i].len; - mem = dma_memory_map(sgl->dma, sgl->sg[i].base, &len, dir); - if (!mem) { - goto err; - } - qemu_iovec_add(&p->iov, mem, len); - if (len != sgl->sg[i].len) { - goto err; + dma_addr_t base = sgl->sg[i].base; + dma_addr_t len = sgl->sg[i].len; + + while (len) { + dma_addr_t xlen = len; + mem = dma_memory_map(sgl->dma, sgl->sg[i].base, &xlen, dir); + if (!mem) { + goto err; + } + if (xlen > len) { + xlen = len; + } + qemu_iovec_add(&p->iov, mem, xlen); + len -= xlen; + base += xlen; } } return 0; diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c new file mode 100644 index 000000000..a1eeced8f --- /dev/null +++ b/hw/vfio_pci.c @@ -0,0 +1,1864 @@ +/* + * vfio based device assignment support + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Alex Williamson <alex.williamson@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Based on qemu-kvm device-assignment: + * Adapted for KVM by Qumranet. + * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) + * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) + * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) + * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) + * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) + */ + +#include <dirent.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <linux/vfio.h> + +#include "config.h" +#include "event_notifier.h" +#include "exec-memory.h" +#include "kvm.h" +#include "memory.h" +#include "msi.h" +#include "msix.h" +#include "qemu-error.h" +#include "range.h" +#include "vfio_pci_int.h" + +/* #define DEBUG_VFIO */ +#ifdef DEBUG_VFIO +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "vfio: " fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +#define MSIX_CAP_LENGTH 12 + +static QLIST_HEAD(, VFIOContainer) + container_list = QLIST_HEAD_INITIALIZER(container_list); + +static QLIST_HEAD(, VFIOGroup) + group_list = QLIST_HEAD_INITIALIZER(group_list); + +static void vfio_disable_interrupts(VFIODevice *vdev); +static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); +static void vfio_mmap_set_enabled(VFIODevice *vdev, bool enabled); + +/* + * Common VFIO interrupt disable + */ +static void vfio_disable_irqindex(VFIODevice *vdev, int index) +{ + struct vfio_irq_set irq_set = { + .argsz = sizeof(irq_set), + .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER, + .index = index, + .start = 0, + .count = 0, + }; + + ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); + + vdev->interrupt = VFIO_INT_NONE; +} + +/* + * INTx + */ +static void vfio_unmask_intx(VFIODevice *vdev) +{ + struct vfio_irq_set irq_set = { + .argsz = sizeof(irq_set), + .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK, + .index = VFIO_PCI_INTX_IRQ_INDEX, + .start = 0, + .count = 1, + }; + + ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); +} + +static void vfio_intx_interrupt(void *opaque) +{ + VFIODevice *vdev = opaque; + + if (!event_notifier_test_and_clear(&vdev->intx.interrupt)) { + return; + } + + DPRINTF("%s(%04x:%02x:%02x.%x) Pin %c\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function, + 'A' + vdev->intx.pin); + + vdev->intx.pending = true; + qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 1); +} + +static void vfio_eoi(VFIODevice *vdev) +{ + if (!vdev->intx.pending) { + return; + } + + DPRINTF("%s(%04x:%02x:%02x.%x) EOI\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); + + vdev->intx.pending = false; + qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0); + vfio_unmask_intx(vdev); +} + +typedef struct QEMU_PACKED VFIOIRQSetFD { + struct vfio_irq_set irq_set; + int32_t fd; +} VFIOIRQSetFD; + +static int vfio_enable_intx(VFIODevice *vdev) +{ + VFIOIRQSetFD irq_set_fd = { + .irq_set = { + .argsz = sizeof(irq_set_fd), + .flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER, + .index = VFIO_PCI_INTX_IRQ_INDEX, + .start = 0, + .count = 1, + }, + }; + uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); + int ret; + + if (vdev->intx.disabled || !pin) { + return 0; + } + + vfio_disable_interrupts(vdev); + + vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */ + ret = event_notifier_init(&vdev->intx.interrupt, 0); + if (ret) { + error_report("vfio: Error: event_notifier_init failed\n"); + return ret; + } + + irq_set_fd.fd = event_notifier_get_fd(&vdev->intx.interrupt); + qemu_set_fd_handler(irq_set_fd.fd, vfio_intx_interrupt, NULL, vdev); + + if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd)) { + error_report("vfio: Error: Failed to setup INTx fd: %m\n"); + return -errno; + } + + /* + * Disable mmaps so we can trap on BAR accesses. We interpret any + * access as a response to an interrupt and unmask the physical + * device. The device will re-assert if the interrupt is still + * pending. We'll likely retrigger on the host multiple times per + * guest interrupt, but without EOI notification it's better than + * nothing. Acceleration paths through KVM will avoid this. + */ + vfio_mmap_set_enabled(vdev, false); + + vdev->interrupt = VFIO_INT_INTx; + + DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); + + return 0; +} + +static void vfio_disable_intx(VFIODevice *vdev) +{ + int fd; + + vfio_disable_irqindex(vdev, VFIO_PCI_INTX_IRQ_INDEX); + vdev->intx.pending = false; + qemu_set_irq(vdev->pdev.irq[vdev->intx.pin], 0); + vfio_mmap_set_enabled(vdev, true); + + fd = event_notifier_get_fd(&vdev->intx.interrupt); + qemu_set_fd_handler(fd, NULL, NULL, vdev); + event_notifier_cleanup(&vdev->intx.interrupt); + + vdev->interrupt = VFIO_INT_NONE; + + DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); +} + +/* + * MSI/X + */ +static void vfio_msi_interrupt(void *opaque) +{ + VFIOMSIVector *vector = opaque; + VFIODevice *vdev = vector->vdev; + int nr = vector - vdev->msi_vectors; + + if (!event_notifier_test_and_clear(&vector->interrupt)) { + return; + } + + DPRINTF("%s(%04x:%02x:%02x.%x) vector %d\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, nr); + + if (vdev->interrupt == VFIO_INT_MSIX) { + msix_notify(&vdev->pdev, nr); + } else if (vdev->interrupt == VFIO_INT_MSI) { + msi_notify(&vdev->pdev, nr); + } else { + error_report("vfio: MSI interrupt receieved, but not enabled?\n"); + } +} + +static int vfio_enable_vectors(VFIODevice *vdev, bool msix) +{ + struct vfio_irq_set *irq_set; + int ret = 0, i, argsz; + int32_t *fds; + + argsz = sizeof(*irq_set) + (vdev->nr_vectors * sizeof(*fds)); + + irq_set = g_malloc0(argsz); + irq_set->argsz = argsz; + irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER; + irq_set->index = msix ? VFIO_PCI_MSIX_IRQ_INDEX : VFIO_PCI_MSI_IRQ_INDEX; + irq_set->start = 0; + irq_set->count = vdev->nr_vectors; + fds = (int32_t *)&irq_set->data; + + for (i = 0; i < vdev->nr_vectors; i++) { + if (!vdev->msi_vectors[i].use) { + fds[i] = -1; + continue; + } + + fds[i] = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt); + } + + ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set); + + g_free(irq_set); + + if (!ret) { + vdev->interrupt = msix ? VFIO_INT_MSIX : VFIO_INT_MSI; + } + + return ret; +} + +static int vfio_msix_vector_use(PCIDevice *pdev, + unsigned int nr, MSIMessage msg) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOMSIVector *vector; + int ret; + + DPRINTF("%s(%04x:%02x:%02x.%x) vector %d used\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, nr); + + if (vdev->interrupt != VFIO_INT_MSIX) { + vfio_disable_interrupts(vdev); + } + + if (!vdev->msi_vectors) { + vdev->msi_vectors = g_malloc0(vdev->msix->entries * + sizeof(VFIOMSIVector)); + } + + vector = &vdev->msi_vectors[nr]; + vector->vdev = vdev; + vector->use = true; + + msix_vector_use(pdev, nr); + + if (event_notifier_init(&vector->interrupt, 0)) { + error_report("vfio: Error: event_notifier_init failed\n"); + } + + /* + * Attempt to enable route through KVM irqchip, + * default to userspace handling if unavailable. + */ + vector->virq = kvm_irqchip_add_msi_route(kvm_state, msg); + if (vector->virq < 0 || + kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt, + vector->virq) < 0) { + if (vector->virq >= 0) { + kvm_irqchip_release_virq(kvm_state, vector->virq); + vector->virq = -1; + } + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + vfio_msi_interrupt, NULL, vector); + } + + /* + * We don't want to have the host allocate all possible MSI vectors + * for a device if they're not in use, so we shutdown and incrementally + * increase them as needed. + */ + if (vdev->nr_vectors < nr + 1) { + int i; + + vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX); + vdev->nr_vectors = nr + 1; + ret = vfio_enable_vectors(vdev, true); + if (ret) { + error_report("vfio: failed to enable vectors, %d\n", ret); + } + + /* We don't know if we've missed interrupts in the interim... */ + for (i = 0; i < vdev->msix->entries; i++) { + if (vdev->msi_vectors[i].use) { + msix_notify(&vdev->pdev, i); + } + } + } else { + VFIOIRQSetFD irq_set_fd = { + .irq_set = { + .argsz = sizeof(irq_set_fd), + .flags = VFIO_IRQ_SET_DATA_EVENTFD | + VFIO_IRQ_SET_ACTION_TRIGGER, + .index = VFIO_PCI_MSIX_IRQ_INDEX, + .start = nr, + .count = 1, + }, + .fd = event_notifier_get_fd(&vector->interrupt), + }; + ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd); + if (ret) { + error_report("vfio: failed to modify vector, %d\n", ret); + } + + /* + * If we were connected to the hardware PBA we could skip this, + * until then, a spurious interrupt is better than starvation. + */ + msix_notify(&vdev->pdev, nr); + } + + return 0; +} + +static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOMSIVector *vector = &vdev->msi_vectors[nr]; + VFIOIRQSetFD irq_set_fd = { + .irq_set = { + .argsz = sizeof(irq_set_fd), + .flags = VFIO_IRQ_SET_DATA_EVENTFD | + VFIO_IRQ_SET_ACTION_TRIGGER, + .index = VFIO_PCI_MSIX_IRQ_INDEX, + .start = nr, + .count = 1, + }, + .fd = -1, + }; + + DPRINTF("%s(%04x:%02x:%02x.%x) vector %d released\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, nr); + + /* + * XXX What's the right thing to do here? This turns off the interrupt + * completely, but do we really just want to switch the interrupt to + * bouncing through userspace and let msix.c drop it? Not sure. + */ + msix_vector_unuse(pdev, nr); + ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set_fd); + + if (vector->virq < 0) { + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + NULL, NULL, NULL); + } else { + kvm_irqchip_remove_irqfd_notifier(kvm_state, &vector->interrupt, + vector->virq); + kvm_irqchip_release_virq(kvm_state, vector->virq); + vector->virq = -1; + } + + event_notifier_cleanup(&vector->interrupt); + vector->use = false; +} + +/* TODO This should move to msi.c */ +static MSIMessage msi_get_msg(PCIDevice *pdev, unsigned int vector) +{ + uint16_t flags = pci_get_word(pdev->config + pdev->msi_cap + PCI_MSI_FLAGS); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + MSIMessage msg; + + if (msi64bit) { + msg.address = pci_get_quad(pdev->config + + pdev->msi_cap + PCI_MSI_ADDRESS_LO); + } else { + msg.address = pci_get_long(pdev->config + + pdev->msi_cap + PCI_MSI_ADDRESS_LO); + } + + msg.data = pci_get_word(pdev->config + pdev->msi_cap + + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32)); + msg.data += vector; + + return msg; +} + +/* So should this */ +static void msi_set_qsize(PCIDevice *pdev, uint8_t size) +{ + uint8_t *config = pdev->config + pdev->msi_cap; + uint16_t flags; + + flags = pci_get_word(config + PCI_MSI_FLAGS); + flags = le16_to_cpu(flags); + flags &= ~PCI_MSI_FLAGS_QSIZE; + flags |= (size & 0x7) << 4; + flags = cpu_to_le16(flags); + pci_set_word(config + PCI_MSI_FLAGS, flags); +} + +static void vfio_enable_msi(VFIODevice *vdev) +{ + int ret, i; + + vfio_disable_interrupts(vdev); + + vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev); +retry: + vdev->msi_vectors = g_malloc0(vdev->nr_vectors * sizeof(VFIOMSIVector)); + + for (i = 0; i < vdev->nr_vectors; i++) { + MSIMessage msg; + VFIOMSIVector *vector = &vdev->msi_vectors[i]; + + vector->vdev = vdev; + vector->use = true; + + if (event_notifier_init(&vector->interrupt, 0)) { + error_report("vfio: Error: event_notifier_init failed\n"); + } + + msg = msi_get_msg(&vdev->pdev, i); + + /* + * Attempt to enable route through KVM irqchip, + * default to userspace handling if unavailable. + */ + vector->virq = kvm_irqchip_add_msi_route(kvm_state, msg); + if (vector->virq < 0 || + kvm_irqchip_add_irqfd_notifier(kvm_state, &vector->interrupt, + vector->virq) < 0) { + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + vfio_msi_interrupt, NULL, vector); + } + } + + ret = vfio_enable_vectors(vdev, false); + if (ret) { + if (ret < 0) { + error_report("vfio: Error: Failed to setup MSI fds: %m\n"); + } else if (ret != vdev->nr_vectors) { + error_report("vfio: Error: Failed to enable %d " + "MSI vectors, retry with %d\n", vdev->nr_vectors, ret); + } + + for (i = 0; i < vdev->nr_vectors; i++) { + VFIOMSIVector *vector = &vdev->msi_vectors[i]; + if (vector->virq >= 0) { + kvm_irqchip_remove_irqfd_notifier(kvm_state, &vector->interrupt, + vector->virq); + kvm_irqchip_release_virq(kvm_state, vector->virq); + vector->virq = -1; + } else { + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + NULL, NULL, NULL); + } + event_notifier_cleanup(&vector->interrupt); + } + + g_free(vdev->msi_vectors); + + if (ret > 0 && ret != vdev->nr_vectors) { + vdev->nr_vectors = ret; + goto retry; + } + vdev->nr_vectors = 0; + + return; + } + + msi_set_qsize(&vdev->pdev, vdev->nr_vectors); + + DPRINTF("%s(%04x:%02x:%02x.%x) Enabled %d MSI vectors\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, vdev->nr_vectors); +} + +static void vfio_disable_msi_x(VFIODevice *vdev, bool msix) +{ + int i; + + vfio_disable_irqindex(vdev, msix ? VFIO_PCI_MSIX_IRQ_INDEX : + VFIO_PCI_MSI_IRQ_INDEX); + + for (i = 0; i < vdev->nr_vectors; i++) { + VFIOMSIVector *vector = &vdev->msi_vectors[i]; + + if (!vector->use) { + continue; + } + + if (vector->virq >= 0) { + kvm_irqchip_remove_irqfd_notifier(kvm_state, + &vector->interrupt, vector->virq); + kvm_irqchip_release_virq(kvm_state, vector->virq); + vector->virq = -1; + } else { + qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), + NULL, NULL, NULL); + } + + if (msix) { + msix_vector_unuse(&vdev->pdev, i); + } + + event_notifier_cleanup(&vector->interrupt); + } + + g_free(vdev->msi_vectors); + vdev->msi_vectors = NULL; + vdev->nr_vectors = 0; + + if (!msix) { + msi_set_qsize(&vdev->pdev, 0); /* Actually still means 1 vector */ + } + + DPRINTF("%s(%04x:%02x:%02x.%x, msi%s)\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, msix ? "x" : ""); + + vfio_enable_intx(vdev); +} + +/* + * IO Port/MMIO - Beware of the endians, VFIO is always little endian + */ +static void vfio_bar_write(void *opaque, target_phys_addr_t addr, + uint64_t data, unsigned size) +{ + VFIOBAR *bar = opaque; + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + } buf; + + switch (size) { + case 1: + buf.byte = data; + break; + case 2: + buf.word = cpu_to_le16(data); + break; + case 4: + buf.dword = cpu_to_le32(data); + break; + default: + hw_error("vfio: unsupported write size, %d bytes\n", size); + break; + } + + if (pwrite(bar->fd, &buf, size, bar->fd_offset + addr) != size) { + error_report("%s(,0x%"TARGET_PRIxPHYS", 0x%"PRIx64", %d) failed: %m\n", + __func__, addr, data, size); + } + + DPRINTF("%s(BAR%d+0x%"TARGET_PRIxPHYS", 0x%"PRIx64", %d)\n", + __func__, bar->nr, addr, data, size); + + /* + * A read or write to a BAR always signals an INTx EOI. This will + * do nothing if not pending (including not in INTx mode). We assume + * that a BAR access is in response to an interrupt and that BAR + * accesses will service the interrupt. Unfortunately, we don't know + * which access will service the interrupt, so we're potentially + * getting quite a few host interrupts per guest interrupt. + */ + vfio_eoi(DO_UPCAST(VFIODevice, bars[bar->nr], bar)); +} + +static uint64_t vfio_bar_read(void *opaque, + target_phys_addr_t addr, unsigned size) +{ + VFIOBAR *bar = opaque; + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + } buf; + uint64_t data = 0; + + if (pread(bar->fd, &buf, size, bar->fd_offset + addr) != size) { + error_report("%s(,0x%"TARGET_PRIxPHYS", %d) failed: %m\n", + __func__, addr, size); + return (uint64_t)-1; + } + + switch (size) { + case 1: + data = buf.byte; + break; + case 2: + data = le16_to_cpu(buf.word); + break; + case 4: + data = le32_to_cpu(buf.dword); + break; + default: + hw_error("vfio: unsupported read size, %d bytes\n", size); + break; + } + + DPRINTF("%s(BAR%d+0x%"TARGET_PRIxPHYS", %d) = 0x%"PRIx64"\n", + __func__, bar->nr, addr, size, data); + + /* Same as write above */ + vfio_eoi(DO_UPCAST(VFIODevice, bars[bar->nr], bar)); + + return data; +} + +static const MemoryRegionOps vfio_bar_ops = { + .read = vfio_bar_read, + .write = vfio_bar_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +/* + * PCI config space + */ +static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + uint32_t val = 0; + + /* + * We only need QEMU PCI config support for the ROM BAR, the MSI and MSIX + * capabilities, and the multifunction bit below. We let VFIO handle + * virtualizing everything else. Performance is not a concern here. + */ + if (ranges_overlap(addr, len, PCI_ROM_ADDRESS, 4) || + (pdev->cap_present & QEMU_PCI_CAP_MSIX && + ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) || + (pdev->cap_present & QEMU_PCI_CAP_MSI && + ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size))) { + + val = pci_default_read_config(pdev, addr, len); + } else { + if (pread(vdev->fd, &val, len, vdev->config_offset + addr) != len) { + error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m\n", + __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr, len); + return -errno; + } + val = le32_to_cpu(val); + } + + /* Multifunction bit is virualized in QEMU */ + if (unlikely(ranges_overlap(addr, len, PCI_HEADER_TYPE, 1))) { + uint32_t mask = PCI_HEADER_TYPE_MULTI_FUNCTION; + + if (len == 4) { + mask <<= 16; + } + + if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + val |= mask; + } else { + val &= ~mask; + } + } + + DPRINTF("%s(%04x:%02x:%02x.%x, @0x%x, len=0x%x) %x\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, addr, len, val); + + return val; +} + +static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, + uint32_t val, int len) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + uint32_t val_le = cpu_to_le32(val); + + DPRINTF("%s(%04x:%02x:%02x.%x, @0x%x, 0x%x, len=0x%x)\n", __func__, + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, addr, val, len); + + /* Write everything to VFIO, let it filter out what we can't write */ + if (pwrite(vdev->fd, &val_le, len, vdev->config_offset + addr) != len) { + error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x, 0x%x) failed: %m\n", + __func__, vdev->host.domain, vdev->host.bus, + vdev->host.slot, vdev->host.function, addr, val, len); + } + + /* Write standard header bits to emulation */ + if (addr < PCI_CONFIG_HEADER_SIZE) { + pci_default_write_config(pdev, addr, val, len); + return; + } + + /* MSI/MSI-X Enabling/Disabling */ + if (pdev->cap_present & QEMU_PCI_CAP_MSI && + ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size)) { + int is_enabled, was_enabled = msi_enabled(pdev); + + pci_default_write_config(pdev, addr, val, len); + + is_enabled = msi_enabled(pdev); + + if (!was_enabled && is_enabled) { + vfio_enable_msi(vdev); + } else if (was_enabled && !is_enabled) { + vfio_disable_msi_x(vdev, false); + } + } + + if (pdev->cap_present & QEMU_PCI_CAP_MSIX && + ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) { + int is_enabled, was_enabled = msix_enabled(pdev); + + pci_default_write_config(pdev, addr, val, len); + + is_enabled = msix_enabled(pdev); + + if (!was_enabled && is_enabled) { + /* vfio_msix_vector_use handles this automatically */ + } else if (was_enabled && !is_enabled) { + vfio_disable_msi_x(vdev, true); + } + } +} + +/* + * DMA - Mapping and unmapping for the "type1" IOMMU interface used on x86 + */ +static int vfio_dma_map(VFIOContainer *container, target_phys_addr_t iova, + ram_addr_t size, void *vaddr, bool readonly) +{ + struct vfio_iommu_type1_dma_map map = { + .argsz = sizeof(map), + .flags = VFIO_DMA_MAP_FLAG_READ, + .vaddr = (__u64)(intptr_t)vaddr, + .iova = iova, + .size = size, + }; + + if (!readonly) { + map.flags |= VFIO_DMA_MAP_FLAG_WRITE; + } + + if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map)) { + DPRINTF("VFIO_MAP_DMA: %d\n", -errno); + return -errno; + } + + return 0; +} + +static int vfio_dma_unmap(VFIOContainer *container, + target_phys_addr_t iova, ram_addr_t size) +{ + struct vfio_iommu_type1_dma_unmap unmap = { + .argsz = sizeof(unmap), + .flags = 0, + .iova = iova, + .size = size, + }; + + if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { + DPRINTF("VFIO_UNMAP_DMA: %d\n", -errno); + return -errno; + } + + return 0; +} + +static void vfio_listener_dummy1(MemoryListener *listener) +{ + /* We don't do batching (begin/commit) or care about logging */ +} + +static void vfio_listener_dummy2(MemoryListener *listener, + MemoryRegionSection *section) +{ + /* We don't do logging or care about nops */ +} + +static void vfio_listener_dummy3(MemoryListener *listener, + MemoryRegionSection *section, + bool match_data, uint64_t data, + EventNotifier *e) +{ + /* We don't care about eventfds */ +} + +static bool vfio_listener_skipped_section(MemoryRegionSection *section) +{ + return !memory_region_is_ram(section->mr); +} + +static void vfio_listener_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + iommu_data.listener); + target_phys_addr_t iova, end; + void *vaddr; + int ret; + + if (vfio_listener_skipped_section(section)) { + DPRINTF("vfio: SKIPPING region_add %"TARGET_PRIxPHYS" - %"PRIx64"\n", + section->offset_within_address_space, + section->offset_within_address_space + section->size - 1); + return; + } + + if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != + (section->offset_within_region & ~TARGET_PAGE_MASK))) { + error_report("%s received unaligned region\n", __func__); + return; + } + + iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + end = (section->offset_within_address_space + section->size) & + TARGET_PAGE_MASK; + + if (iova >= end) { + return; + } + + vaddr = memory_region_get_ram_ptr(section->mr) + + section->offset_within_region + + (iova - section->offset_within_address_space); + + DPRINTF("vfio: region_add %"TARGET_PRIxPHYS" - %"TARGET_PRIxPHYS" [%p]\n", + iova, end - 1, vaddr); + + ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly); + if (ret) { + error_report("vfio_dma_map(%p, 0x%"TARGET_PRIxPHYS", " + "0x%"TARGET_PRIxPHYS", %p) = %d (%m)\n", + container, iova, end - iova, vaddr, ret); + } +} + +static void vfio_listener_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + iommu_data.listener); + target_phys_addr_t iova, end; + int ret; + + if (vfio_listener_skipped_section(section)) { + DPRINTF("vfio: SKIPPING region_del %"TARGET_PRIxPHYS" - %"PRIx64"\n", + section->offset_within_address_space, + section->offset_within_address_space + section->size - 1); + return; + } + + if (unlikely((section->offset_within_address_space & ~TARGET_PAGE_MASK) != + (section->offset_within_region & ~TARGET_PAGE_MASK))) { + error_report("%s received unaligned region\n", __func__); + return; + } + + iova = TARGET_PAGE_ALIGN(section->offset_within_address_space); + end = (section->offset_within_address_space + section->size) & + TARGET_PAGE_MASK; + + if (iova >= end) { + return; + } + + DPRINTF("vfio: region_del %"TARGET_PRIxPHYS" - %"TARGET_PRIxPHYS"\n", + iova, end - 1); + + ret = vfio_dma_unmap(container, iova, end - iova); + if (ret) { + error_report("vfio_dma_unmap(%p, 0x%"TARGET_PRIxPHYS", " + "0x%"TARGET_PRIxPHYS") = %d (%m)\n", + container, iova, end - iova, ret); + } +} + +static MemoryListener vfio_memory_listener = { + .begin = vfio_listener_dummy1, + .commit = vfio_listener_dummy1, + .region_add = vfio_listener_region_add, + .region_del = vfio_listener_region_del, + .region_nop = vfio_listener_dummy2, + .log_start = vfio_listener_dummy2, + .log_stop = vfio_listener_dummy2, + .log_sync = vfio_listener_dummy2, + .log_global_start = vfio_listener_dummy1, + .log_global_stop = vfio_listener_dummy1, + .eventfd_add = vfio_listener_dummy3, + .eventfd_del = vfio_listener_dummy3, +}; + +static void vfio_listener_release(VFIOContainer *container) +{ + memory_listener_unregister(&container->iommu_data.listener); +} + +/* + * Interrupt setup + */ +static void vfio_disable_interrupts(VFIODevice *vdev) +{ + switch (vdev->interrupt) { + case VFIO_INT_INTx: + vfio_disable_intx(vdev); + break; + case VFIO_INT_MSI: + vfio_disable_msi_x(vdev, false); + break; + case VFIO_INT_MSIX: + vfio_disable_msi_x(vdev, true); + break; + } +} + +static int vfio_setup_msi(VFIODevice *vdev, int pos) +{ + uint16_t ctrl; + bool msi_64bit, msi_maskbit; + int ret, entries; + + /* + * TODO: don't peek into msi_supported, let msi_init fail and + * check for ENOTSUP + */ + if (!msi_supported) { + return 0; + } + + if (pread(vdev->fd, &ctrl, sizeof(ctrl), + vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { + return -errno; + } + ctrl = le16_to_cpu(ctrl); + + msi_64bit = !!(ctrl & PCI_MSI_FLAGS_64BIT); + msi_maskbit = !!(ctrl & PCI_MSI_FLAGS_MASKBIT); + entries = 1 << ((ctrl & PCI_MSI_FLAGS_QMASK) >> 1); + + DPRINTF("%04x:%02x:%02x.%x PCI MSI CAP @0x%x\n", vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function, pos); + + ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit); + if (ret < 0) { + error_report("vfio: msi_init failed\n"); + return ret; + } + vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0); + + return 0; +} + +/* + * We don't have any control over how pci_add_capability() inserts + * capabilities into the chain. In order to setup MSI-X we need a + * MemoryRegion for the BAR. In order to setup the BAR and not + * attempt to mmap the MSI-X table area, which VFIO won't allow, we + * need to first look for where the MSI-X table lives. So we + * unfortunately split MSI-X setup across two functions. + */ +static int vfio_early_setup_msix(VFIODevice *vdev) +{ + uint8_t pos; + uint16_t ctrl; + uint32_t table, pba; + + pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX); + if (!pos) { + return 0; + } + + if (pread(vdev->fd, &ctrl, sizeof(ctrl), + vdev->config_offset + pos + PCI_CAP_FLAGS) != sizeof(ctrl)) { + return -errno; + } + + if (pread(vdev->fd, &table, sizeof(table), + vdev->config_offset + pos + PCI_MSIX_TABLE) != sizeof(table)) { + return -errno; + } + + if (pread(vdev->fd, &pba, sizeof(pba), + vdev->config_offset + pos + PCI_MSIX_PBA) != sizeof(pba)) { + return -errno; + } + + ctrl = le16_to_cpu(ctrl); + table = le32_to_cpu(table); + pba = le32_to_cpu(pba); + + vdev->msix = g_malloc0(sizeof(*(vdev->msix))); + vdev->msix->table_bar = table & PCI_MSIX_FLAGS_BIRMASK; + vdev->msix->table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; + vdev->msix->pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK; + vdev->msix->pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; + vdev->msix->entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; + + DPRINTF("%04x:%02x:%02x.%x " + "PCI MSI-X CAP @0x%x, BAR %d, offset 0x%x, entries %d\n", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, pos, vdev->msix->table_bar, + vdev->msix->table_offset, vdev->msix->entries); + + return 0; +} + +static int vfio_setup_msix(VFIODevice *vdev, int pos) +{ + int ret; + + /* + * TODO: don't peek into msi_supported, let msix_init fail and + * check for ENOTSUP + */ + if (!msi_supported) { + return 0; + } + + ret = msix_init(&vdev->pdev, vdev->msix->entries, + &vdev->bars[vdev->msix->table_bar].mem, + vdev->msix->table_bar, vdev->msix->table_offset, + &vdev->bars[vdev->msix->pba_bar].mem, + vdev->msix->pba_bar, vdev->msix->pba_offset, pos); + if (ret < 0) { + error_report("vfio: msix_init failed\n"); + return ret; + } + + ret = msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, + vfio_msix_vector_release); + if (ret) { + error_report("vfio: msix_set_vector_notifiers failed %d\n", ret); + msix_uninit(&vdev->pdev, &vdev->bars[vdev->msix->table_bar].mem, + &vdev->bars[vdev->msix->pba_bar].mem); + return ret; + } + + return 0; +} + +static void vfio_teardown_msi(VFIODevice *vdev) +{ + msi_uninit(&vdev->pdev); + + if (vdev->msix) { + /* FIXME: Why can't unset just silently do nothing?? */ + if (vdev->pdev.msix_vector_use_notifier && + vdev->pdev.msix_vector_release_notifier) { + msix_unset_vector_notifiers(&vdev->pdev); + } + + msix_uninit(&vdev->pdev, &vdev->bars[vdev->msix->table_bar].mem, + &vdev->bars[vdev->msix->pba_bar].mem); + } +} + +/* + * Resource setup + */ +static void vfio_mmap_set_enabled(VFIODevice *vdev, bool enabled) +{ + int i; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + VFIOBAR *bar = &vdev->bars[i]; + + if (!bar->size) { + continue; + } + + memory_region_set_enabled(&bar->mmap_mem, enabled); + if (vdev->msix && vdev->msix->table_bar == i) { + memory_region_set_enabled(&vdev->msix->mmap_mem, enabled); + } + } +} + +static void vfio_unmap_bar(VFIODevice *vdev, int nr) +{ + VFIOBAR *bar = &vdev->bars[nr]; + + if (!bar->size) { + return; + } + + memory_region_del_subregion(&bar->mem, &bar->mmap_mem); + munmap(bar->mmap, memory_region_size(&bar->mmap_mem)); + + if (vdev->msix && vdev->msix->table_bar == nr) { + memory_region_del_subregion(&bar->mem, &vdev->msix->mmap_mem); + munmap(vdev->msix->mmap, memory_region_size(&vdev->msix->mmap_mem)); + } + + memory_region_destroy(&bar->mem); +} + +static int vfio_mmap_bar(VFIOBAR *bar, MemoryRegion *mem, MemoryRegion *submem, + void **map, size_t size, off_t offset, + const char *name) +{ + int ret = 0; + + if (size && bar->flags & VFIO_REGION_INFO_FLAG_MMAP) { + int prot = 0; + + if (bar->flags & VFIO_REGION_INFO_FLAG_READ) { + prot |= PROT_READ; + } + + if (bar->flags & VFIO_REGION_INFO_FLAG_WRITE) { + prot |= PROT_WRITE; + } + + *map = mmap(NULL, size, prot, MAP_SHARED, + bar->fd, bar->fd_offset + offset); + if (*map == MAP_FAILED) { + *map = NULL; + ret = -errno; + goto empty_region; + } + + memory_region_init_ram_ptr(submem, name, size, *map); + } else { +empty_region: + /* Create a zero sized sub-region to make cleanup easy. */ + memory_region_init(submem, name, 0); + } + + memory_region_add_subregion(mem, offset, submem); + + return ret; +} + +static void vfio_map_bar(VFIODevice *vdev, int nr) +{ + VFIOBAR *bar = &vdev->bars[nr]; + unsigned size = bar->size; + char name[64]; + uint32_t pci_bar; + uint8_t type; + int ret; + + /* Skip both unimplemented BARs and the upper half of 64bit BARS. */ + if (!size) { + return; + } + + snprintf(name, sizeof(name), "VFIO %04x:%02x:%02x.%x BAR %d", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function, nr); + + /* Determine what type of BAR this is for registration */ + ret = pread(vdev->fd, &pci_bar, sizeof(pci_bar), + vdev->config_offset + PCI_BASE_ADDRESS_0 + (4 * nr)); + if (ret != sizeof(pci_bar)) { + error_report("vfio: Failed to read BAR %d (%m)\n", nr); + return; + } + + pci_bar = le32_to_cpu(pci_bar); + type = pci_bar & (pci_bar & PCI_BASE_ADDRESS_SPACE_IO ? + ~PCI_BASE_ADDRESS_IO_MASK : ~PCI_BASE_ADDRESS_MEM_MASK); + + /* A "slow" read/write mapping underlies all BARs */ + memory_region_init_io(&bar->mem, &vfio_bar_ops, bar, name, size); + pci_register_bar(&vdev->pdev, nr, type, &bar->mem); + + /* + * We can't mmap areas overlapping the MSIX vector table, so we + * potentially insert a direct-mapped subregion before and after it. + */ + if (vdev->msix && vdev->msix->table_bar == nr) { + size = vdev->msix->table_offset & TARGET_PAGE_MASK; + } + + strncat(name, " mmap", sizeof(name) - strlen(name) - 1); + if (vfio_mmap_bar(bar, &bar->mem, + &bar->mmap_mem, &bar->mmap, size, 0, name)) { + error_report("%s unsupported. Performance may be slow\n", name); + } + + if (vdev->msix && vdev->msix->table_bar == nr) { + unsigned start; + + start = TARGET_PAGE_ALIGN(vdev->msix->table_offset + + (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE)); + + size = start < bar->size ? bar->size - start : 0; + strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1); + /* VFIOMSIXInfo contains another MemoryRegion for this mapping */ + if (vfio_mmap_bar(bar, &bar->mem, &vdev->msix->mmap_mem, + &vdev->msix->mmap, size, start, name)) { + error_report("%s unsupported. Performance may be slow\n", name); + } + } +} + +static void vfio_map_bars(VFIODevice *vdev) +{ + int i; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + vfio_map_bar(vdev, i); + } +} + +static void vfio_unmap_bars(VFIODevice *vdev) +{ + int i; + + for (i = 0; i < PCI_ROM_SLOT; i++) { + vfio_unmap_bar(vdev, i); + } +} + +/* + * General setup + */ +static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos) +{ + uint8_t tmp, next = 0xff; + + for (tmp = pdev->config[PCI_CAPABILITY_LIST]; tmp; + tmp = pdev->config[tmp + 1]) { + if (tmp > pos && tmp < next) { + next = tmp; + } + } + + return next - pos; +} + +static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos) +{ + PCIDevice *pdev = &vdev->pdev; + uint8_t cap_id, next, size; + int ret; + + cap_id = pdev->config[pos]; + next = pdev->config[pos + 1]; + + /* + * If it becomes important to configure capabilities to their actual + * size, use this as the default when it's something we don't recognize. + * Since QEMU doesn't actually handle many of the config accesses, + * exact size doesn't seem worthwhile. + */ + size = vfio_std_cap_max_size(pdev, pos); + + /* + * pci_add_capability always inserts the new capability at the head + * of the chain. Therefore to end up with a chain that matches the + * physical device, we insert from the end by making this recursive. + * This is also why we pre-caclulate size above as cached config space + * will be changed as we unwind the stack. + */ + if (next) { + ret = vfio_add_std_cap(vdev, next); + if (ret) { + return ret; + } + } else { + pdev->config[PCI_CAPABILITY_LIST] = 0; /* Begin the rebuild */ + } + + switch (cap_id) { + case PCI_CAP_ID_MSI: + ret = vfio_setup_msi(vdev, pos); + break; + case PCI_CAP_ID_MSIX: + ret = vfio_setup_msix(vdev, pos); + break; + default: + ret = pci_add_capability(pdev, cap_id, pos, size); + break; + } + + if (ret < 0) { + error_report("vfio: %04x:%02x:%02x.%x Error adding PCI capability " + "0x%x[0x%x]@0x%x: %d\n", vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function, + cap_id, size, pos, ret); + return ret; + } + + return 0; +} + +static int vfio_add_capabilities(VFIODevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + + if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) || + !pdev->config[PCI_CAPABILITY_LIST]) { + return 0; /* Nothing to add */ + } + + return vfio_add_std_cap(vdev, pdev->config[PCI_CAPABILITY_LIST]); +} + +static int vfio_load_rom(VFIODevice *vdev) +{ + uint64_t size = vdev->rom_size; + char name[32]; + off_t off = 0, voff = vdev->rom_offset; + ssize_t bytes; + void *ptr; + + /* If loading ROM from file, pci handles it */ + if (vdev->pdev.romfile || !vdev->pdev.rom_bar || !size) { + return 0; + } + + DPRINTF("%s(%04x:%02x:%02x.%x)\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); + + snprintf(name, sizeof(name), "vfio[%04x:%02x:%02x.%x].rom", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); + memory_region_init_ram(&vdev->pdev.rom, name, size); + ptr = memory_region_get_ram_ptr(&vdev->pdev.rom); + memset(ptr, 0xff, size); + + while (size) { + bytes = pread(vdev->fd, ptr + off, size, voff + off); + if (bytes == 0) { + break; /* expect that we could get back less than the ROM BAR */ + } else if (bytes > 0) { + off += bytes; + size -= bytes; + } else { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + error_report("vfio: Error reading device ROM: %m\n"); + memory_region_destroy(&vdev->pdev.rom); + return -errno; + } + } + + pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, 0, &vdev->pdev.rom); + vdev->pdev.has_rom = true; + return 0; +} + +static int vfio_connect_container(VFIOGroup *group) +{ + VFIOContainer *container; + int ret, fd; + + if (group->container) { + return 0; + } + + QLIST_FOREACH(container, &container_list, next) { + if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { + group->container = container; + QLIST_INSERT_HEAD(&container->group_list, group, container_next); + return 0; + } + } + + fd = qemu_open("/dev/vfio/vfio", O_RDWR); + if (fd < 0) { + error_report("vfio: failed to open /dev/vfio/vfio: %m\n"); + return -errno; + } + + ret = ioctl(fd, VFIO_GET_API_VERSION); + if (ret != VFIO_API_VERSION) { + error_report("vfio: supported vfio version: %d, " + "reported version: %d\n", VFIO_API_VERSION, ret); + close(fd); + return -EINVAL; + } + + container = g_malloc0(sizeof(*container)); + container->fd = fd; + + if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) { + ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd); + if (ret) { + error_report("vfio: failed to set group container: %m\n"); + g_free(container); + close(fd); + return -errno; + } + + ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU); + if (ret) { + error_report("vfio: failed to set iommu for container: %m\n"); + g_free(container); + close(fd); + return -errno; + } + + container->iommu_data.listener = vfio_memory_listener; + container->iommu_data.release = vfio_listener_release; + + memory_listener_register(&container->iommu_data.listener, + get_system_memory()); + } else { + error_report("vfio: No available IOMMU models\n"); + g_free(container); + close(fd); + return -EINVAL; + } + + QLIST_INIT(&container->group_list); + QLIST_INSERT_HEAD(&container_list, container, next); + + group->container = container; + QLIST_INSERT_HEAD(&container->group_list, group, container_next); + + return 0; +} + +static void vfio_disconnect_container(VFIOGroup *group) +{ + VFIOContainer *container = group->container; + + if (ioctl(group->fd, VFIO_GROUP_UNSET_CONTAINER, &container->fd)) { + error_report("vfio: error disconnecting group %d from container\n", + group->groupid); + } + + QLIST_REMOVE(group, container_next); + group->container = NULL; + + if (QLIST_EMPTY(&container->group_list)) { + if (container->iommu_data.release) { + container->iommu_data.release(container); + } + QLIST_REMOVE(container, next); + DPRINTF("vfio_disconnect_container: close container->fd\n"); + close(container->fd); + g_free(container); + } +} + +static VFIOGroup *vfio_get_group(int groupid) +{ + VFIOGroup *group; + char path[32]; + struct vfio_group_status status = { .argsz = sizeof(status) }; + + QLIST_FOREACH(group, &group_list, next) { + if (group->groupid == groupid) { + return group; + } + } + + group = g_malloc0(sizeof(*group)); + + snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); + group->fd = qemu_open(path, O_RDWR); + if (group->fd < 0) { + error_report("vfio: error opening %s: %m\n", path); + g_free(group); + return NULL; + } + + if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) { + error_report("vfio: error getting group status: %m\n"); + close(group->fd); + g_free(group); + return NULL; + } + + if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) { + error_report("vfio: error, group %d is not viable, please ensure " + "all devices within the iommu_group are bound to their " + "vfio bus driver.\n", groupid); + close(group->fd); + g_free(group); + return NULL; + } + + group->groupid = groupid; + QLIST_INIT(&group->device_list); + + if (vfio_connect_container(group)) { + error_report("vfio: failed to setup container for group %d\n", groupid); + close(group->fd); + g_free(group); + return NULL; + } + + QLIST_INSERT_HEAD(&group_list, group, next); + + return group; +} + +static void vfio_put_group(VFIOGroup *group) +{ + if (!QLIST_EMPTY(&group->device_list)) { + return; + } + + vfio_disconnect_container(group); + QLIST_REMOVE(group, next); + DPRINTF("vfio_put_group: close group->fd\n"); + close(group->fd); + g_free(group); +} + +static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev) +{ + struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; + struct vfio_region_info reg_info = { .argsz = sizeof(reg_info) }; + int ret, i; + + ret = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name); + if (ret < 0) { + error_report("vfio: error getting device %s from group %d: %m\n", + name, group->groupid); + error_report("Verify all devices in group %d are bound to vfio-pci " + "or pci-stub and not already in use\n", group->groupid); + return ret; + } + + vdev->fd = ret; + vdev->group = group; + QLIST_INSERT_HEAD(&group->device_list, vdev, next); + + /* Sanity check device */ + ret = ioctl(vdev->fd, VFIO_DEVICE_GET_INFO, &dev_info); + if (ret) { + error_report("vfio: error getting device info: %m\n"); + goto error; + } + + DPRINTF("Device %s flags: %u, regions: %u, irgs: %u\n", name, + dev_info.flags, dev_info.num_regions, dev_info.num_irqs); + + if (!(dev_info.flags & VFIO_DEVICE_FLAGS_PCI)) { + error_report("vfio: Um, this isn't a PCI device\n"); + goto error; + } + + vdev->reset_works = !!(dev_info.flags & VFIO_DEVICE_FLAGS_RESET); + if (!vdev->reset_works) { + error_report("Warning, device %s does not support reset\n", name); + } + + if (dev_info.num_regions != VFIO_PCI_NUM_REGIONS) { + error_report("vfio: unexpected number of io regions %u\n", + dev_info.num_regions); + goto error; + } + + if (dev_info.num_irqs != VFIO_PCI_NUM_IRQS) { + error_report("vfio: unexpected number of irqs %u\n", dev_info.num_irqs); + goto error; + } + + for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) { + reg_info.index = i; + + ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + if (ret) { + error_report("vfio: Error getting region %d info: %m\n", i); + goto error; + } + + DPRINTF("Device %s region %d:\n", name, i); + DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n", + (unsigned long)reg_info.size, (unsigned long)reg_info.offset, + (unsigned long)reg_info.flags); + + vdev->bars[i].flags = reg_info.flags; + vdev->bars[i].size = reg_info.size; + vdev->bars[i].fd_offset = reg_info.offset; + vdev->bars[i].fd = vdev->fd; + vdev->bars[i].nr = i; + } + + reg_info.index = VFIO_PCI_ROM_REGION_INDEX; + + ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + if (ret) { + error_report("vfio: Error getting ROM info: %m\n"); + goto error; + } + + DPRINTF("Device %s ROM:\n", name); + DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n", + (unsigned long)reg_info.size, (unsigned long)reg_info.offset, + (unsigned long)reg_info.flags); + + vdev->rom_size = reg_info.size; + vdev->rom_offset = reg_info.offset; + + reg_info.index = VFIO_PCI_CONFIG_REGION_INDEX; + + ret = ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, ®_info); + if (ret) { + error_report("vfio: Error getting config info: %m\n"); + goto error; + } + + DPRINTF("Device %s config:\n", name); + DPRINTF(" size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n", + (unsigned long)reg_info.size, (unsigned long)reg_info.offset, + (unsigned long)reg_info.flags); + + vdev->config_size = reg_info.size; + vdev->config_offset = reg_info.offset; + +error: + if (ret) { + QLIST_REMOVE(vdev, next); + vdev->group = NULL; + close(vdev->fd); + } + return ret; +} + +static void vfio_put_device(VFIODevice *vdev) +{ + QLIST_REMOVE(vdev, next); + vdev->group = NULL; + DPRINTF("vfio_put_device: close vdev->fd\n"); + close(vdev->fd); + if (vdev->msix) { + g_free(vdev->msix); + vdev->msix = NULL; + } +} + +static int vfio_initfn(PCIDevice *pdev) +{ + VFIODevice *pvdev, *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOGroup *group; + char path[PATH_MAX], iommu_group_path[PATH_MAX], *group_name; + ssize_t len; + struct stat st; + int groupid; + int ret; + + /* Check that the host device exists */ + snprintf(path, sizeof(path), + "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); + if (stat(path, &st) < 0) { + error_report("vfio: error: no such host device: %s\n", path); + return -errno; + } + + strncat(path, "iommu_group", sizeof(path) - strlen(path) - 1); + + len = readlink(path, iommu_group_path, PATH_MAX); + if (len <= 0) { + error_report("vfio: error no iommu_group for device\n"); + return -errno; + } + + iommu_group_path[len] = 0; + group_name = basename(iommu_group_path); + + if (sscanf(group_name, "%d", &groupid) != 1) { + error_report("vfio: error reading %s: %m\n", path); + return -errno; + } + + DPRINTF("%s(%04x:%02x:%02x.%x) group %d\n", __func__, vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function, groupid); + + group = vfio_get_group(groupid); + if (!group) { + error_report("vfio: failed to get group %d\n", groupid); + return -ENOENT; + } + + snprintf(path, sizeof(path), "%04x:%02x:%02x.%01x", + vdev->host.domain, vdev->host.bus, vdev->host.slot, + vdev->host.function); + + QLIST_FOREACH(pvdev, &group->device_list, next) { + if (pvdev->host.domain == vdev->host.domain && + pvdev->host.bus == vdev->host.bus && + pvdev->host.slot == vdev->host.slot && + pvdev->host.function == vdev->host.function) { + + error_report("vfio: error: device %s is already attached\n", path); + vfio_put_group(group); + return -EBUSY; + } + } + + ret = vfio_get_device(group, path, vdev); + if (ret) { + error_report("vfio: failed to get device %s\n", path); + vfio_put_group(group); + return ret; + } + + /* Get a copy of config space */ + ret = pread(vdev->fd, vdev->pdev.config, + MIN(pci_config_size(&vdev->pdev), vdev->config_size), + vdev->config_offset); + if (ret < (int)MIN(pci_config_size(&vdev->pdev), vdev->config_size)) { + ret = ret < 0 ? -errno : -EFAULT; + error_report("vfio: Failed to read device config space\n"); + goto out_put; + } + + /* + * Clear host resource mapping info. If we choose not to register a + * BAR, such as might be the case with the option ROM, we can get + * confusing, unwritable, residual addresses from the host here. + */ + memset(&vdev->pdev.config[PCI_BASE_ADDRESS_0], 0, 24); + memset(&vdev->pdev.config[PCI_ROM_ADDRESS], 0, 4); + + vfio_load_rom(vdev); + + ret = vfio_early_setup_msix(vdev); + if (ret) { + goto out_put; + } + + vfio_map_bars(vdev); + + ret = vfio_add_capabilities(vdev); + if (ret) { + goto out_teardown; + } + + if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { + if (vdev->intx.intx && strcmp(vdev->intx.intx, "off")) { + error_report("vfio: Unknown option x-intx=%s, " + "valid options: \"off\".\n", vdev->intx.intx); + ret = -EINVAL; + goto out_teardown; + } + + if (vdev->intx.intx && !strcmp(vdev->intx.intx, "off")) { + vdev->intx.disabled = true; + } + + ret = vfio_enable_intx(vdev); + if (ret) { + goto out_teardown; + } + } + + return 0; + +out_teardown: + pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + vfio_teardown_msi(vdev); + vfio_unmap_bars(vdev); +out_put: + vfio_put_device(vdev); + vfio_put_group(group); + return ret; +} + +static void vfio_exitfn(PCIDevice *pdev) +{ + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + VFIOGroup *group = vdev->group; + + pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + vfio_disable_interrupts(vdev); + vfio_teardown_msi(vdev); + vfio_unmap_bars(vdev); + vfio_put_device(vdev); + vfio_put_group(group); +} + +static void vfio_pci_reset(DeviceState *dev) +{ + PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev); + VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev); + + if (!vdev->reset_works) { + return; + } + + if (ioctl(vdev->fd, VFIO_DEVICE_RESET)) { + error_report("vfio: Error unable to reset physical device " + "(%04x:%02x:%02x.%x): %m\n", vdev->host.domain, + vdev->host.bus, vdev->host.slot, vdev->host.function); + } +} + +static Property vfio_pci_dev_properties[] = { + DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIODevice, host), + DEFINE_PROP_STRING("x-intx", VFIODevice, intx.intx), + /* + * TODO - support passed fds... is this necessary? + * DEFINE_PROP_STRING("vfiofd", VFIODevice, vfiofd_name), + * DEFINE_PROP_STRING("vfiogroupfd, VFIODevice, vfiogroupfd_name), + */ + DEFINE_PROP_END_OF_LIST(), +}; + + +static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); + + dc->reset = vfio_pci_reset; + dc->props = vfio_pci_dev_properties; + pdc->init = vfio_initfn; + pdc->exit = vfio_exitfn; + pdc->config_read = vfio_pci_read_config; + pdc->config_write = vfio_pci_write_config; +} + +static const TypeInfo vfio_pci_dev_info = { + .name = "vfio-pci", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(VFIODevice), + .class_init = vfio_pci_dev_class_init, +}; + +static void register_vfio_pci_dev_type(void) +{ + type_register_static(&vfio_pci_dev_info); +} + +type_init(register_vfio_pci_dev_type) diff --git a/hw/vfio_pci_int.h b/hw/vfio_pci_int.h new file mode 100644 index 000000000..3812d8d7f --- /dev/null +++ b/hw/vfio_pci_int.h @@ -0,0 +1,114 @@ +/* + * vfio based device assignment support + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Alex Williamson <alex.williamson@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#ifndef HW_VFIO_PCI_INT_H +#define HW_VFIO_PCI_INT_H + +#include "qemu-common.h" +#include "qemu-queue.h" +#include "pci.h" +#include "event_notifier.h" + +typedef struct VFIOBAR { + off_t fd_offset; /* offset of BAR within device fd */ + int fd; /* device fd, allows us to pass VFIOBAR as opaque data */ + MemoryRegion mem; /* slow, read/write access */ + MemoryRegion mmap_mem; /* direct mapped access */ + void *mmap; + size_t size; + uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ + uint8_t nr; /* cache the BAR number for debug */ +} VFIOBAR; + +typedef struct VFIOINTx { + bool pending; /* interrupt pending */ + bool kvm_accel; /* set when QEMU bypass through KVM enabled */ + uint8_t pin; /* which pin to pull for qemu_set_irq */ + EventNotifier interrupt; /* eventfd triggered on interrupt */ + EventNotifier unmask; /* eventfd for unmask on QEMU bypass */ + PCIINTxRoute route; /* routing info for QEMU bypass */ + bool disabled; + char *intx; +} VFIOINTx; + +struct VFIODevice; + +typedef struct VFIOMSIVector { + EventNotifier interrupt; /* eventfd triggered on interrupt */ + struct VFIODevice *vdev; /* back pointer to device */ + int virq; /* KVM irqchip route for QEMU bypass */ + bool use; +} VFIOMSIVector; + +enum { + VFIO_INT_NONE = 0, + VFIO_INT_INTx = 1, + VFIO_INT_MSI = 2, + VFIO_INT_MSIX = 3, +}; + +struct VFIOGroup; + +typedef struct VFIOContainer { + int fd; /* /dev/vfio/vfio, empowered by the attached groups */ + struct { + /* enable abstraction to support various iommu backends */ + union { + MemoryListener listener; /* Used by type1 iommu */ + }; + void (*release)(struct VFIOContainer *); + } iommu_data; + QLIST_HEAD(, VFIOGroup) group_list; + QLIST_ENTRY(VFIOContainer) next; +} VFIOContainer; + +/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */ +typedef struct VFIOMSIXInfo { + uint8_t table_bar; + uint8_t pba_bar; + uint16_t entries; + uint32_t table_offset; + uint32_t pba_offset; + MemoryRegion mmap_mem; + void *mmap; +} VFIOMSIXInfo; + +typedef struct VFIODevice { + PCIDevice pdev; + int fd; + VFIOINTx intx; + unsigned int config_size; + off_t config_offset; /* Offset of config space region within device fd */ + unsigned int rom_size; + off_t rom_offset; /* Offset of ROM region within device fd */ + int msi_cap_size; + VFIOMSIVector *msi_vectors; + VFIOMSIXInfo *msix; + int nr_vectors; /* Number of MSI/MSIX vectors currently in use */ + int interrupt; /* Current interrupt type */ + VFIOBAR bars[PCI_NUM_REGIONS - 1]; /* No ROM */ + PCIHostDeviceAddress host; + QLIST_ENTRY(VFIODevice) next; + struct VFIOGroup *group; + bool reset_works; +} VFIODevice; + +typedef struct VFIOGroup { + int fd; + int groupid; + VFIOContainer *container; + QLIST_HEAD(, VFIODevice) device_list; + QLIST_ENTRY(VFIOGroup) next; + QLIST_ENTRY(VFIOGroup) container_next; +} VFIOGroup; + +#endif /* HW_VFIO_PCI_INT_H */ diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index f178fa86c..e25cc9647 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -64,31 +64,22 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, int status) } static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, - int is_read) + bool is_read) { - BlockdevOnError action = bdrv_get_on_error(req->dev->bs, is_read); + BlockErrorAction action = bdrv_get_error_action(req->dev->bs, is_read, error); VirtIOBlock *s = req->dev; - if (action == BLOCKDEV_ON_ERROR_IGNORE) { - bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_IGNORE, is_read); - return 0; - } - - if ((error == ENOSPC && action == BLOCKDEV_ON_ERROR_ENOSPC) - || action == BLOCKDEV_ON_ERROR_STOP) { + if (action == BDRV_ACTION_STOP) { req->next = s->rq; s->rq = req; - bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_STOP, is_read); - vm_stop(RUN_STATE_IO_ERROR); - bdrv_iostatus_set_err(s->bs, error); - } else { + } else if (action == BDRV_ACTION_REPORT) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); bdrv_acct_done(s->bs, &req->acct); g_free(req); - bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_REPORT, is_read); } - return 1; + bdrv_error_action(s->bs, action, is_read, error); + return action != BDRV_ACTION_IGNORE; } static void virtio_blk_rw_complete(void *opaque, int ret) @@ -98,7 +89,7 @@ static void virtio_blk_rw_complete(void *opaque, int ret) trace_virtio_blk_rw_complete(req, ret); if (ret) { - int is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT); + bool is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT); if (virtio_blk_handle_rw_error(req, -ret, is_read)) return; } diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 649074329..247d7bef5 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -690,7 +690,7 @@ static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - virtqueue_push(n->tx_vq, &n->async_tx.elem, n->async_tx.len); + virtqueue_push(n->tx_vq, &n->async_tx.elem, 0); virtio_notify(&n->vdev, n->tx_vq); n->async_tx.elem.out_num = n->async_tx.len = 0; @@ -754,7 +754,7 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) len += ret; - virtqueue_push(vq, &elem, len); + virtqueue_push(vq, &elem, 0); virtio_notify(&n->vdev, vq); if (++num_packets >= n->tx_burst) { diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 82073f5dc..d20bd8bf7 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -287,6 +287,7 @@ ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, size_t virtio_serial_guest_ready(VirtIOSerialPort *port) { VirtQueue *vq = port->ivq; + unsigned int bytes; if (!virtio_queue_ready(vq) || !(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || @@ -296,14 +297,8 @@ size_t virtio_serial_guest_ready(VirtIOSerialPort *port) if (use_multiport(port->vser) && !port->guest_connected) { return 0; } - - if (virtqueue_avail_bytes(vq, 4096, 0)) { - return 4096; - } - if (virtqueue_avail_bytes(vq, 1, 0)) { - return 1; - } - return 0; + virtqueue_get_avail_bytes(vq, &bytes, NULL); + return bytes; } static void flush_queued_data_bh(void *opaque) diff --git a/hw/virtio.c b/hw/virtio.c index 209c76375..6821092df 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -241,7 +241,7 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, elem->in_sg[i].iov_len, 1, size); - offset += elem->in_sg[i].iov_len; + offset += size; } for (i = 0; i < elem->out_num; i++) @@ -335,10 +335,11 @@ static unsigned virtqueue_next_desc(target_phys_addr_t desc_pa, return next; } -int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes) +void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + unsigned int *out_bytes) { unsigned int idx; - int total_bufs, in_total, out_total; + unsigned int total_bufs, in_total, out_total; idx = vq->last_avail_idx; @@ -380,13 +381,9 @@ int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes) } if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) { - if (in_bytes > 0 && - (in_total += vring_desc_len(desc_pa, i)) >= in_bytes) - return 1; + in_total += vring_desc_len(desc_pa, i); } else { - if (out_bytes > 0 && - (out_total += vring_desc_len(desc_pa, i)) >= out_bytes) - return 1; + out_total += vring_desc_len(desc_pa, i); } } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max); @@ -395,7 +392,24 @@ int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes) else total_bufs++; } + if (in_bytes) { + *in_bytes = in_total; + } + if (out_bytes) { + *out_bytes = out_total; + } +} +int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, + unsigned int out_bytes) +{ + unsigned int in_total, out_total; + + virtqueue_get_avail_bytes(vq, &in_total, &out_total); + if ((in_bytes && in_bytes < in_total) + || (out_bytes && out_bytes < out_total)) { + return 1; + } return 0; } diff --git a/hw/virtio.h b/hw/virtio.h index 7a4f56452..80de3757e 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -147,7 +147,10 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr, size_t num_sg, int is_write); int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem); -int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes); +int virtqueue_avail_bytes(VirtQueue *vq, unsigned int in_bytes, + unsigned int out_bytes); +void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, + unsigned int *out_bytes); void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); @@ -48,6 +48,7 @@ void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); struct MemoryRegion; void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, struct MemoryRegion *mr); +void xen_modified_memory(ram_addr_t start, ram_addr_t length); #endif struct MemoryRegion; diff --git a/hw/xen_platform.c b/hw/xen_platform.c index 0d6c2ff8c..956dbfeae 100644 --- a/hw/xen_platform.c +++ b/hw/xen_platform.c @@ -85,8 +85,10 @@ static void log_writeb(PCIXenPlatformState *s, char val) static void unplug_nic(PCIBus *b, PCIDevice *d, void *o) { + /* We have to ignore passthrough devices */ if (pci_get_word(d->config + PCI_CLASS_DEVICE) == - PCI_CLASS_NETWORK_ETHERNET) { + PCI_CLASS_NETWORK_ETHERNET + && strcmp(d->name, "xen-pci-passthrough") != 0) { qdev_free(&d->qdev); } } @@ -98,8 +100,10 @@ static void pci_unplug_nics(PCIBus *bus) static void unplug_disks(PCIBus *b, PCIDevice *d, void *o) { + /* We have to ignore passthrough devices */ if (pci_get_word(d->config + PCI_CLASS_DEVICE) == - PCI_CLASS_STORAGE_IDE) { + PCI_CLASS_STORAGE_IDE + && strcmp(d->name, "xen-pci-passthrough") != 0) { qdev_unplug(&(d->qdev), NULL); } } diff --git a/hw/xen_pt.c b/hw/xen_pt.c index 307119a12..838bcea4d 100644 --- a/hw/xen_pt.c +++ b/hw/xen_pt.c @@ -410,14 +410,17 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) if (r->type & XEN_HOST_PCI_REGION_TYPE_PREFETCH) { type |= PCI_BASE_ADDRESS_MEM_PREFETCH; } + if (r->type & XEN_HOST_PCI_REGION_TYPE_MEM_64) { + type |= PCI_BASE_ADDRESS_MEM_TYPE_64; + } } memory_region_init_io(&s->bar[i], &ops, &s->dev, "xen-pci-pt-bar", r->size); pci_register_bar(&s->dev, i, type, &s->bar[i]); - XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%08"PRIx64 - " base_addr=0x%08"PRIx64" type: %#x)\n", + XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%lx"PRIx64 + " base_addr=0x%lx"PRIx64" type: %#x)\n", i, r->size, r->base_addr, type); } diff --git a/hw/xen_pt_config_init.c b/hw/xen_pt_config_init.c index e524a4094..0a5f82cb8 100644 --- a/hw/xen_pt_config_init.c +++ b/hw/xen_pt_config_init.c @@ -342,6 +342,23 @@ static int xen_pt_cmd_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, #define XEN_PT_BAR_IO_RO_MASK 0x00000003 /* BAR ReadOnly mask(I/O) */ #define XEN_PT_BAR_IO_EMU_MASK 0xFFFFFFFC /* BAR emul mask(I/O) */ +static bool is_64bit_bar(PCIIORegion *r) +{ + return !!(r->type & PCI_BASE_ADDRESS_MEM_TYPE_64); +} + +static uint64_t xen_pt_get_bar_size(PCIIORegion *r) +{ + if (is_64bit_bar(r)) { + uint64_t size64; + size64 = (r + 1)->size; + size64 <<= 32; + size64 += r->size; + return size64; + } + return r->size; +} + static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s, XenPTRegInfo *reg) { @@ -366,7 +383,7 @@ static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s, /* check unused BAR */ r = &d->io_regions[index]; - if (r->size == 0) { + if (!xen_pt_get_bar_size(r)) { return XEN_PT_BAR_FLAG_UNUSED; } @@ -481,7 +498,12 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, switch (s->bases[index].bar_flag) { case XEN_PT_BAR_FLAG_MEM: bar_emu_mask = XEN_PT_BAR_MEM_EMU_MASK; - bar_ro_mask = XEN_PT_BAR_MEM_RO_MASK | (r_size - 1); + if (!r_size) { + /* low 32 bits mask for 64 bit bars */ + bar_ro_mask = XEN_PT_BAR_ALLF; + } else { + bar_ro_mask = XEN_PT_BAR_MEM_RO_MASK | (r_size - 1); + } break; case XEN_PT_BAR_FLAG_IO: bar_emu_mask = XEN_PT_BAR_IO_EMU_MASK; @@ -489,7 +511,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, break; case XEN_PT_BAR_FLAG_UPPER: bar_emu_mask = XEN_PT_BAR_ALLF; - bar_ro_mask = 0; /* all upper 32bit are R/W */ + bar_ro_mask = r_size ? r_size - 1 : 0; break; default: break; @@ -501,22 +523,13 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, /* check whether we need to update the virtual region address or not */ switch (s->bases[index].bar_flag) { + case XEN_PT_BAR_FLAG_UPPER: case XEN_PT_BAR_FLAG_MEM: /* nothing to do */ break; case XEN_PT_BAR_FLAG_IO: /* nothing to do */ break; - case XEN_PT_BAR_FLAG_UPPER: - if (cfg_entry->data) { - if (cfg_entry->data != (XEN_PT_BAR_ALLF & ~bar_ro_mask)) { - XEN_PT_WARN(d, "Guest attempt to set high MMIO Base Address. " - "Ignore mapping. " - "(offset: 0x%02x, high address: 0x%08x)\n", - reg->offset, cfg_entry->data); - } - } - break; default: break; } @@ -186,8 +186,7 @@ static const int key_defs[] = { int index_from_key(const char *key) { - int i, keycode; - char *endp; + int i; for (i = 0; QKeyCode_lookup[i] != NULL; i++) { if (!strcmp(key, QKeyCode_lookup[i])) { @@ -195,17 +194,6 @@ int index_from_key(const char *key) } } - if (strstart(key, "0x", NULL)) { - keycode = strtoul(key, &endp, 0); - if (*endp == '\0' && keycode >= 0x01 && keycode <= 0xff) { - for (i = 0; i < Q_KEY_CODE_MAX; i++) { - if (keycode == key_defs[i]) { - break; - } - } - } - } - /* Return Q_KEY_CODE_MAX if the key is invalid */ return i; } @@ -224,30 +212,46 @@ int index_from_keycode(int code) return i; } -static QKeyCodeList *keycodes; +static int *keycodes; +static int keycodes_size; static QEMUTimer *key_timer; +static int keycode_from_keyvalue(const KeyValue *value) +{ + if (value->kind == KEY_VALUE_KIND_QCODE) { + return key_defs[value->qcode]; + } else { + assert(value->kind == KEY_VALUE_KIND_NUMBER); + return value->number; + } +} + +static void free_keycodes(void) +{ + g_free(keycodes); + keycodes = NULL; + keycodes_size = 0; +} + static void release_keys(void *opaque) { - int keycode; - QKeyCodeList *p; + int i; - for (p = keycodes; p != NULL; p = p->next) { - keycode = key_defs[p->value]; - if (keycode & 0x80) { + for (i = 0; i < keycodes_size; i++) { + if (keycodes[i] & 0x80) { kbd_put_keycode(0xe0); } - kbd_put_keycode(keycode | 0x80); + kbd_put_keycode(keycodes[i]| 0x80); } - qapi_free_QKeyCodeList(keycodes); - keycodes = NULL; + + free_keycodes(); } -void qmp_send_key(QKeyCodeList *keys, bool has_hold_time, int64_t hold_time, +void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time, Error **errp) { int keycode; - QKeyCodeList *p, *keylist, *head = NULL, *tmp = NULL; + KeyValueList *p; if (!key_timer) { key_timer = qemu_new_timer_ns(vm_clock, release_keys, NULL); @@ -257,31 +261,28 @@ void qmp_send_key(QKeyCodeList *keys, bool has_hold_time, int64_t hold_time, qemu_del_timer(key_timer); release_keys(NULL); } + if (!has_hold_time) { hold_time = 100; } for (p = keys; p != NULL; p = p->next) { - keylist = g_malloc0(sizeof(*keylist)); - keylist->value = p->value; - keylist->next = NULL; - - if (!head) { - head = keylist; - } - if (tmp) { - tmp->next = keylist; + /* key down events */ + keycode = keycode_from_keyvalue(p->value); + if (keycode < 0x01 || keycode > 0xff) { + error_setg(errp, "invalid hex keycode 0x%x\n", keycode); + free_keycodes(); + return; } - tmp = keylist; - /* key down events */ - keycode = key_defs[p->value]; if (keycode & 0x80) { kbd_put_keycode(0xe0); } kbd_put_keycode(keycode & 0x7f); + + keycodes = g_realloc(keycodes, sizeof(int) * (keycodes_size + 1)); + keycodes[keycodes_size++] = keycode; } - keycodes = head; /* delayed key up events */ qemu_mod_timer(key_timer, qemu_get_clock_ns(vm_clock) + @@ -26,7 +26,7 @@ # include <sys/socket.h> #endif -size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, +size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, size_t offset, const void *buf, size_t bytes) { size_t done; @@ -36,7 +36,7 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); * such "large" value is -1 (sinice size_t is unsigned), * so specifying `-1' as `bytes' means 'up to the end of iovec'. */ -size_t iov_from_buf(struct iovec *iov, unsigned int iov_cnt, +size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, size_t offset, const void *buf, size_t bytes); size_t iov_to_buf(const struct iovec *iov, const unsigned int iov_cnt, size_t offset, void *buf, size_t bytes); diff --git a/libcacard/Makefile b/libcacard/Makefile index 63990b700..487f43489 100644 --- a/libcacard/Makefile +++ b/libcacard/Makefile @@ -14,6 +14,9 @@ QEMU_CFLAGS+=-I../ libcacard.lib-y=$(patsubst %.o,%.lo,$(libcacard-y)) +vscclient: $(libcacard-y) $(QEMU_OBJS) vscclient.o cutils.o + $(call quiet-command,$(CC) -o $@ $^ $(libcacard_libs) $(LIBS)," LINK $@") + clean: rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ vscclient *.lo */*.lo .libs/* */.libs/* *.la */*.la *.pc rm -Rf .libs */.libs diff --git a/libcacard/vcard_emul_nss.c b/libcacard/vcard_emul_nss.c index 802cae3a2..e1cae5bc5 100644 --- a/libcacard/vcard_emul_nss.c +++ b/libcacard/vcard_emul_nss.c @@ -1169,8 +1169,7 @@ vcard_emul_options(const char *args) NEXT_TOKEN(vname) NEXT_TOKEN(type_params) type_params_length = MIN(type_params_length, sizeof(type_str)-1); - strncpy(type_str, type_params, type_params_length); - type_str[type_params_length] = 0; + pstrcpy(type_str, type_params_length, type_params); type = vcard_emul_type_from_string(type_str); NEXT_TOKEN(type_params) diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h new file mode 100644 index 000000000..f787b727a --- /dev/null +++ b/linux-headers/linux/vfio.h @@ -0,0 +1,368 @@ +/* + * VFIO API definition + * + * Copyright (C) 2012 Red Hat, Inc. All rights reserved. + * Author: Alex Williamson <alex.williamson@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef VFIO_H +#define VFIO_H + +#include <linux/types.h> +#include <linux/ioctl.h> + +#define VFIO_API_VERSION 0 + + +/* Kernel & User level defines for VFIO IOCTLs. */ + +/* Extensions */ + +#define VFIO_TYPE1_IOMMU 1 + +/* + * The IOCTL interface is designed for extensibility by embedding the + * structure length (argsz) and flags into structures passed between + * kernel and userspace. We therefore use the _IO() macro for these + * defines to avoid implicitly embedding a size into the ioctl request. + * As structure fields are added, argsz will increase to match and flag + * bits will be defined to indicate additional fields with valid data. + * It's *always* the caller's responsibility to indicate the size of + * the structure passed by setting argsz appropriately. + */ + +#define VFIO_TYPE (';') +#define VFIO_BASE 100 + +/* -------- IOCTLs for VFIO file descriptor (/dev/vfio/vfio) -------- */ + +/** + * VFIO_GET_API_VERSION - _IO(VFIO_TYPE, VFIO_BASE + 0) + * + * Report the version of the VFIO API. This allows us to bump the entire + * API version should we later need to add or change features in incompatible + * ways. + * Return: VFIO_API_VERSION + * Availability: Always + */ +#define VFIO_GET_API_VERSION _IO(VFIO_TYPE, VFIO_BASE + 0) + +/** + * VFIO_CHECK_EXTENSION - _IOW(VFIO_TYPE, VFIO_BASE + 1, __u32) + * + * Check whether an extension is supported. + * Return: 0 if not supported, 1 (or some other positive integer) if supported. + * Availability: Always + */ +#define VFIO_CHECK_EXTENSION _IO(VFIO_TYPE, VFIO_BASE + 1) + +/** + * VFIO_SET_IOMMU - _IOW(VFIO_TYPE, VFIO_BASE + 2, __s32) + * + * Set the iommu to the given type. The type must be supported by an + * iommu driver as verified by calling CHECK_EXTENSION using the same + * type. A group must be set to this file descriptor before this + * ioctl is available. The IOMMU interfaces enabled by this call are + * specific to the value set. + * Return: 0 on success, -errno on failure + * Availability: When VFIO group attached + */ +#define VFIO_SET_IOMMU _IO(VFIO_TYPE, VFIO_BASE + 2) + +/* -------- IOCTLs for GROUP file descriptors (/dev/vfio/$GROUP) -------- */ + +/** + * VFIO_GROUP_GET_STATUS - _IOR(VFIO_TYPE, VFIO_BASE + 3, + * struct vfio_group_status) + * + * Retrieve information about the group. Fills in provided + * struct vfio_group_info. Caller sets argsz. + * Return: 0 on succes, -errno on failure. + * Availability: Always + */ +struct vfio_group_status { + __u32 argsz; + __u32 flags; +#define VFIO_GROUP_FLAGS_VIABLE (1 << 0) +#define VFIO_GROUP_FLAGS_CONTAINER_SET (1 << 1) +}; +#define VFIO_GROUP_GET_STATUS _IO(VFIO_TYPE, VFIO_BASE + 3) + +/** + * VFIO_GROUP_SET_CONTAINER - _IOW(VFIO_TYPE, VFIO_BASE + 4, __s32) + * + * Set the container for the VFIO group to the open VFIO file + * descriptor provided. Groups may only belong to a single + * container. Containers may, at their discretion, support multiple + * groups. Only when a container is set are all of the interfaces + * of the VFIO file descriptor and the VFIO group file descriptor + * available to the user. + * Return: 0 on success, -errno on failure. + * Availability: Always + */ +#define VFIO_GROUP_SET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 4) + +/** + * VFIO_GROUP_UNSET_CONTAINER - _IO(VFIO_TYPE, VFIO_BASE + 5) + * + * Remove the group from the attached container. This is the + * opposite of the SET_CONTAINER call and returns the group to + * an initial state. All device file descriptors must be released + * prior to calling this interface. When removing the last group + * from a container, the IOMMU will be disabled and all state lost, + * effectively also returning the VFIO file descriptor to an initial + * state. + * Return: 0 on success, -errno on failure. + * Availability: When attached to container + */ +#define VFIO_GROUP_UNSET_CONTAINER _IO(VFIO_TYPE, VFIO_BASE + 5) + +/** + * VFIO_GROUP_GET_DEVICE_FD - _IOW(VFIO_TYPE, VFIO_BASE + 6, char) + * + * Return a new file descriptor for the device object described by + * the provided string. The string should match a device listed in + * the devices subdirectory of the IOMMU group sysfs entry. The + * group containing the device must already be added to this context. + * Return: new file descriptor on success, -errno on failure. + * Availability: When attached to container + */ +#define VFIO_GROUP_GET_DEVICE_FD _IO(VFIO_TYPE, VFIO_BASE + 6) + +/* --------------- IOCTLs for DEVICE file descriptors --------------- */ + +/** + * VFIO_DEVICE_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 7, + * struct vfio_device_info) + * + * Retrieve information about the device. Fills in provided + * struct vfio_device_info. Caller sets argsz. + * Return: 0 on success, -errno on failure. + */ +struct vfio_device_info { + __u32 argsz; + __u32 flags; +#define VFIO_DEVICE_FLAGS_RESET (1 << 0) /* Device supports reset */ +#define VFIO_DEVICE_FLAGS_PCI (1 << 1) /* vfio-pci device */ + __u32 num_regions; /* Max region index + 1 */ + __u32 num_irqs; /* Max IRQ index + 1 */ +}; +#define VFIO_DEVICE_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 7) + +/** + * VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8, + * struct vfio_region_info) + * + * Retrieve information about a device region. Caller provides + * struct vfio_region_info with index value set. Caller sets argsz. + * Implementation of region mapping is bus driver specific. This is + * intended to describe MMIO, I/O port, as well as bus specific + * regions (ex. PCI config space). Zero sized regions may be used + * to describe unimplemented regions (ex. unimplemented PCI BARs). + * Return: 0 on success, -errno on failure. + */ +struct vfio_region_info { + __u32 argsz; + __u32 flags; +#define VFIO_REGION_INFO_FLAG_READ (1 << 0) /* Region supports read */ +#define VFIO_REGION_INFO_FLAG_WRITE (1 << 1) /* Region supports write */ +#define VFIO_REGION_INFO_FLAG_MMAP (1 << 2) /* Region supports mmap */ + __u32 index; /* Region index */ + __u32 resv; /* Reserved for alignment */ + __u64 size; /* Region size (bytes) */ + __u64 offset; /* Region offset from start of device fd */ +}; +#define VFIO_DEVICE_GET_REGION_INFO _IO(VFIO_TYPE, VFIO_BASE + 8) + +/** + * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9, + * struct vfio_irq_info) + * + * Retrieve information about a device IRQ. Caller provides + * struct vfio_irq_info with index value set. Caller sets argsz. + * Implementation of IRQ mapping is bus driver specific. Indexes + * using multiple IRQs are primarily intended to support MSI-like + * interrupt blocks. Zero count irq blocks may be used to describe + * unimplemented interrupt types. + * + * The EVENTFD flag indicates the interrupt index supports eventfd based + * signaling. + * + * The MASKABLE flags indicates the index supports MASK and UNMASK + * actions described below. + * + * AUTOMASKED indicates that after signaling, the interrupt line is + * automatically masked by VFIO and the user needs to unmask the line + * to receive new interrupts. This is primarily intended to distinguish + * level triggered interrupts. + * + * The NORESIZE flag indicates that the interrupt lines within the index + * are setup as a set and new subindexes cannot be enabled without first + * disabling the entire index. This is used for interrupts like PCI MSI + * and MSI-X where the driver may only use a subset of the available + * indexes, but VFIO needs to enable a specific number of vectors + * upfront. In the case of MSI-X, where the user can enable MSI-X and + * then add and unmask vectors, it's up to userspace to make the decision + * whether to allocate the maximum supported number of vectors or tear + * down setup and incrementally increase the vectors as each is enabled. + */ +struct vfio_irq_info { + __u32 argsz; + __u32 flags; +#define VFIO_IRQ_INFO_EVENTFD (1 << 0) +#define VFIO_IRQ_INFO_MASKABLE (1 << 1) +#define VFIO_IRQ_INFO_AUTOMASKED (1 << 2) +#define VFIO_IRQ_INFO_NORESIZE (1 << 3) + __u32 index; /* IRQ index */ + __u32 count; /* Number of IRQs within this index */ +}; +#define VFIO_DEVICE_GET_IRQ_INFO _IO(VFIO_TYPE, VFIO_BASE + 9) + +/** + * VFIO_DEVICE_SET_IRQS - _IOW(VFIO_TYPE, VFIO_BASE + 10, struct vfio_irq_set) + * + * Set signaling, masking, and unmasking of interrupts. Caller provides + * struct vfio_irq_set with all fields set. 'start' and 'count' indicate + * the range of subindexes being specified. + * + * The DATA flags specify the type of data provided. If DATA_NONE, the + * operation performs the specified action immediately on the specified + * interrupt(s). For example, to unmask AUTOMASKED interrupt [0,0]: + * flags = (DATA_NONE|ACTION_UNMASK), index = 0, start = 0, count = 1. + * + * DATA_BOOL allows sparse support for the same on arrays of interrupts. + * For example, to mask interrupts [0,1] and [0,3] (but not [0,2]): + * flags = (DATA_BOOL|ACTION_MASK), index = 0, start = 1, count = 3, + * data = {1,0,1} + * + * DATA_EVENTFD binds the specified ACTION to the provided __s32 eventfd. + * A value of -1 can be used to either de-assign interrupts if already + * assigned or skip un-assigned interrupts. For example, to set an eventfd + * to be trigger for interrupts [0,0] and [0,2]: + * flags = (DATA_EVENTFD|ACTION_TRIGGER), index = 0, start = 0, count = 3, + * data = {fd1, -1, fd2} + * If index [0,1] is previously set, two count = 1 ioctls calls would be + * required to set [0,0] and [0,2] without changing [0,1]. + * + * Once a signaling mechanism is set, DATA_BOOL or DATA_NONE can be used + * with ACTION_TRIGGER to perform kernel level interrupt loopback testing + * from userspace (ie. simulate hardware triggering). + * + * Setting of an event triggering mechanism to userspace for ACTION_TRIGGER + * enables the interrupt index for the device. Individual subindex interrupts + * can be disabled using the -1 value for DATA_EVENTFD or the index can be + * disabled as a whole with: flags = (DATA_NONE|ACTION_TRIGGER), count = 0. + * + * Note that ACTION_[UN]MASK specify user->kernel signaling (irqfds) while + * ACTION_TRIGGER specifies kernel->user signaling. + */ +struct vfio_irq_set { + __u32 argsz; + __u32 flags; +#define VFIO_IRQ_SET_DATA_NONE (1 << 0) /* Data not present */ +#define VFIO_IRQ_SET_DATA_BOOL (1 << 1) /* Data is bool (u8) */ +#define VFIO_IRQ_SET_DATA_EVENTFD (1 << 2) /* Data is eventfd (s32) */ +#define VFIO_IRQ_SET_ACTION_MASK (1 << 3) /* Mask interrupt */ +#define VFIO_IRQ_SET_ACTION_UNMASK (1 << 4) /* Unmask interrupt */ +#define VFIO_IRQ_SET_ACTION_TRIGGER (1 << 5) /* Trigger interrupt */ + __u32 index; + __u32 start; + __u32 count; + __u8 data[]; +}; +#define VFIO_DEVICE_SET_IRQS _IO(VFIO_TYPE, VFIO_BASE + 10) + +#define VFIO_IRQ_SET_DATA_TYPE_MASK (VFIO_IRQ_SET_DATA_NONE | \ + VFIO_IRQ_SET_DATA_BOOL | \ + VFIO_IRQ_SET_DATA_EVENTFD) +#define VFIO_IRQ_SET_ACTION_TYPE_MASK (VFIO_IRQ_SET_ACTION_MASK | \ + VFIO_IRQ_SET_ACTION_UNMASK | \ + VFIO_IRQ_SET_ACTION_TRIGGER) +/** + * VFIO_DEVICE_RESET - _IO(VFIO_TYPE, VFIO_BASE + 11) + * + * Reset a device. + */ +#define VFIO_DEVICE_RESET _IO(VFIO_TYPE, VFIO_BASE + 11) + +/* + * The VFIO-PCI bus driver makes use of the following fixed region and + * IRQ index mapping. Unimplemented regions return a size of zero. + * Unimplemented IRQ types return a count of zero. + */ + +enum { + VFIO_PCI_BAR0_REGION_INDEX, + VFIO_PCI_BAR1_REGION_INDEX, + VFIO_PCI_BAR2_REGION_INDEX, + VFIO_PCI_BAR3_REGION_INDEX, + VFIO_PCI_BAR4_REGION_INDEX, + VFIO_PCI_BAR5_REGION_INDEX, + VFIO_PCI_ROM_REGION_INDEX, + VFIO_PCI_CONFIG_REGION_INDEX, + VFIO_PCI_NUM_REGIONS +}; + +enum { + VFIO_PCI_INTX_IRQ_INDEX, + VFIO_PCI_MSI_IRQ_INDEX, + VFIO_PCI_MSIX_IRQ_INDEX, + VFIO_PCI_NUM_IRQS +}; + +/* -------- API for Type1 VFIO IOMMU -------- */ + +/** + * VFIO_IOMMU_GET_INFO - _IOR(VFIO_TYPE, VFIO_BASE + 12, struct vfio_iommu_info) + * + * Retrieve information about the IOMMU object. Fills in provided + * struct vfio_iommu_info. Caller sets argsz. + * + * XXX Should we do these by CHECK_EXTENSION too? + */ +struct vfio_iommu_type1_info { + __u32 argsz; + __u32 flags; +#define VFIO_IOMMU_INFO_PGSIZES (1 << 0) /* supported page sizes info */ + __u64 iova_pgsizes; /* Bitmap of supported page sizes */ +}; + +#define VFIO_IOMMU_GET_INFO _IO(VFIO_TYPE, VFIO_BASE + 12) + +/** + * VFIO_IOMMU_MAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 13, struct vfio_dma_map) + * + * Map process virtual addresses to IO virtual addresses using the + * provided struct vfio_dma_map. Caller sets argsz. READ &/ WRITE required. + */ +struct vfio_iommu_type1_dma_map { + __u32 argsz; + __u32 flags; +#define VFIO_DMA_MAP_FLAG_READ (1 << 0) /* readable from device */ +#define VFIO_DMA_MAP_FLAG_WRITE (1 << 1) /* writable from device */ + __u64 vaddr; /* Process virtual address */ + __u64 iova; /* IO virtual address */ + __u64 size; /* Size of mapping (bytes) */ +}; + +#define VFIO_IOMMU_MAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 13) + +/** + * VFIO_IOMMU_UNMAP_DMA - _IOW(VFIO_TYPE, VFIO_BASE + 14, struct vfio_dma_unmap) + * + * Unmap IO virtual addresses using the provided struct vfio_dma_unmap. + * Caller sets argsz. + */ +struct vfio_iommu_type1_dma_unmap { + __u32 argsz; + __u32 flags; + __u64 iova; /* IO virtual address */ + __u64 size; /* Size of mapping (bytes) */ +}; + +#define VFIO_IOMMU_UNMAP_DMA _IO(VFIO_TYPE, VFIO_BASE + 14) + +#endif /* VFIO_H */ diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 819fdd515..1d8bcb4e7 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2442,7 +2442,7 @@ static void fill_prstatus(struct target_elf_prstatus *prstatus, static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts) { - char *filename, *base_filename; + char *base_filename; unsigned int i, len; (void) memset(psinfo, 0, sizeof (*psinfo)); @@ -2464,13 +2464,15 @@ static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts) psinfo->pr_uid = getuid(); psinfo->pr_gid = getgid(); - filename = strdup(ts->bprm->filename); - base_filename = strdup(basename(filename)); + base_filename = g_path_get_basename(ts->bprm->filename); + /* + * Using strncpy here is fine: at max-length, + * this field is not NUL-terminated. + */ (void) strncpy(psinfo->pr_fname, base_filename, sizeof(psinfo->pr_fname)); - free(base_filename); - free(filename); + g_free(base_filename); bswap_psinfo(psinfo); return (0); } diff --git a/migration-fd.c b/migration-fd.c index 50138edb3..73351678e 100644 --- a/migration-fd.c +++ b/migration-fd.c @@ -75,7 +75,7 @@ static int fd_close(MigrationState *s) int fd_start_outgoing_migration(MigrationState *s, const char *fdname) { - s->fd = monitor_get_fd(cur_mon, fdname); + s->fd = monitor_get_fd(cur_mon, fdname, NULL); if (s->fd == -1) { DPRINTF("fd_migration: invalid file descriptor identifier\n"); goto err_after_get_fd; @@ -450,6 +450,7 @@ static const char *monitor_event_names[] = { [QEVENT_SPICE_DISCONNECTED] = "SPICE_DISCONNECTED", [QEVENT_BLOCK_JOB_COMPLETED] = "BLOCK_JOB_COMPLETED", [QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED", + [QEVENT_BLOCK_JOB_ERROR] = "BLOCK_JOB_ERROR", [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED", [QEVENT_SUSPEND] = "SUSPEND", [QEVENT_SUSPEND_DISK] = "SUSPEND_DISK", @@ -965,45 +966,6 @@ static void do_trace_print_events(Monitor *mon) trace_print_events((FILE *)mon, &monitor_fprintf); } -static int add_graphics_client(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - const char *protocol = qdict_get_str(qdict, "protocol"); - const char *fdname = qdict_get_str(qdict, "fdname"); - CharDriverState *s; - - if (strcmp(protocol, "spice") == 0) { - int fd = monitor_get_fd(mon, fdname); - int skipauth = qdict_get_try_bool(qdict, "skipauth", 0); - int tls = qdict_get_try_bool(qdict, "tls", 0); - if (!using_spice) { - /* correct one? spice isn't a device ,,, */ - qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice"); - return -1; - } - if (qemu_spice_display_add_client(fd, skipauth, tls) < 0) { - close(fd); - } - return 0; -#ifdef CONFIG_VNC - } else if (strcmp(protocol, "vnc") == 0) { - int fd = monitor_get_fd(mon, fdname); - int skipauth = qdict_get_try_bool(qdict, "skipauth", 0); - vnc_display_add_client(NULL, fd, skipauth); - return 0; -#endif - } else if ((s = qemu_chr_find(protocol)) != NULL) { - int fd = monitor_get_fd(mon, fdname); - if (qemu_chr_add_client(s, fd) < 0) { - qerror_report(QERR_ADD_CLIENT_FAILED); - return -1; - } - return 0; - } - - qerror_report(QERR_INVALID_PARAMETER, "protocol"); - return -1; -} - static int client_migrate_info(Monitor *mon, const QDict *qdict, MonitorCompletion cb, void *opaque) { @@ -2139,7 +2101,7 @@ static void do_loadvm(Monitor *mon, const QDict *qdict) } } -int monitor_get_fd(Monitor *mon, const char *fdname) +int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) { mon_fd_t *monfd; @@ -2160,6 +2122,7 @@ int monitor_get_fd(Monitor *mon, const char *fdname) return fd; } + error_setg(errp, "File descriptor named '%s' has not been found", fdname); return -1; } @@ -2431,12 +2394,14 @@ int monitor_fdset_dup_fd_remove(int dup_fd) int monitor_handle_fd_param(Monitor *mon, const char *fdname) { int fd; + Error *local_err = NULL; if (!qemu_isdigit(fdname[0]) && mon) { - fd = monitor_get_fd(mon, fdname); + fd = monitor_get_fd(mon, fdname, &local_err); if (fd == -1) { - error_report("No file descriptor named %s found", fdname); + qerror_report_err(local_err); + error_free(local_err); return -1; } } else { @@ -3280,11 +3245,7 @@ static int64_t expr_unary(Monitor *mon) break; default: errno = 0; -#if TARGET_PHYS_ADDR_BITS > 32 n = strtoull(pch, &p, 0); -#else - n = strtoul(pch, &p, 0); -#endif if (errno == ERANGE) { expr_error(mon, "number too large"); } @@ -38,6 +38,7 @@ typedef enum MonitorEvent { QEVENT_SPICE_DISCONNECTED, QEVENT_BLOCK_JOB_COMPLETED, QEVENT_BLOCK_JOB_CANCELLED, + QEVENT_BLOCK_JOB_ERROR, QEVENT_DEVICE_TRAY_MOVED, QEVENT_SUSPEND, QEVENT_SUSPEND_DISK, @@ -66,7 +67,7 @@ int monitor_read_block_device_key(Monitor *mon, const char *device, BlockDriverCompletionFunc *completion_cb, void *opaque); -int monitor_get_fd(Monitor *mon, const char *fdname); +int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp); int monitor_handle_fd_param(Monitor *mon, const char *fdname); void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) diff --git a/os-posix.c b/os-posix.c index eabccb8fe..f855abb09 100644 --- a/os-posix.c +++ b/os-posix.c @@ -148,8 +148,7 @@ void os_set_proc_name(const char *s) char name[16]; if (!s) return; - name[sizeof(name) - 1] = 0; - strncpy(name, s, sizeof(name)); + pstrcpy(name, sizeof(name), s); /* Could rewrite argv[0] too, but that's a bit more complicated. This simple way is enough for `top'. */ if (prctl(PR_SET_NAME, name)) { diff --git a/qapi-schema.json b/qapi-schema.json index a7264135a..f9dbdae69 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -33,6 +33,31 @@ 'MigrationExpected' ] } ## +# @add_client +# +# Allow client connections for VNC, Spice and socket based +# character devices to be passed in to QEMU via SCM_RIGHTS. +# +# @protocol: protocol name. Valid names are "vnc", "spice" or the +# name of a character device (eg. from -chardev id=XXXX) +# +# @fdname: file descriptor name previously passed via 'getfd' command +# +# @skipauth: #optional whether to skip authentication. Only applies +# to "vnc" and "spice" protocols +# +# @tls: #optional whether to perform TLS. Only applies to the "spice" +# protocol +# +# Returns: nothing on success. +# +# Since: 0.14.0 +## +{ 'command': 'add_client', + 'data': { 'protocol': 'str', 'fdname': 'str', '*skipauth': 'bool', + '*tls': 'bool' } } + +## # @NameInfo: # # Guest name information. @@ -1131,11 +1156,14 @@ # # @speed: the rate limit, bytes per second # +# @io-status: the status of the job (since 1.3) +# # Since: 1.1 ## { 'type': 'BlockJobInfo', 'data': {'type': 'str', 'device': 'str', 'len': 'int', - 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int'} } + 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', + 'io-status': 'BlockDeviceIoStatus'} } ## # @query-block-jobs: @@ -1403,7 +1431,7 @@ # @format: #optional the format of the snapshot image, default is 'qcow2'. # # @mode: #optional whether and how QEMU should create a new image, default is -# 'absolute-paths'. +# 'absolute-paths'. ## { 'type': 'BlockdevSnapshot', 'data': { 'device': 'str', 'snapshot-file': 'str', '*format': 'str', @@ -1457,7 +1485,7 @@ # @format: #optional the format of the snapshot image, default is 'qcow2'. # # @mode: #optional whether and how QEMU should create a new image, default is -# 'absolute-paths'. +# 'absolute-paths'. # # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound @@ -1866,13 +1894,18 @@ # # @speed: #optional the maximum speed, in bytes per second # +# @on-error: #optional the action to take on an error (default report). +# 'stop' and 'enospc' can only be used if the block device +# supports io-status (see BlockInfo). Since 1.3. +# # Returns: Nothing on success # If @device does not exist, DeviceNotFound # # Since: 1.1 ## -{ 'command': 'block-stream', 'data': { 'device': 'str', '*base': 'str', - '*speed': 'int' } } +{ 'command': 'block-stream', + 'data': { 'device': 'str', '*base': 'str', '*speed': 'int', + '*on-error': 'BlockdevOnError' } } ## # @block-job-set-speed: @@ -1958,6 +1991,8 @@ # operation. It is an error to call this command if no operation is in # progress. Resuming an already running job is not an error. # +# This command also clears the error status of the job. +# # @device: the device name # # Returns: Nothing on success @@ -2063,6 +2098,19 @@ { 'command': 'xen-save-devices-state', 'data': {'filename': 'str'} } ## +# @xen-set-global-dirty-log +# +# Enable or disable the global dirty log mode. +# +# @enable: true to enable, false to disable. +# +# Returns: nothing +# +# Since: 1.3 +## +{ 'command': 'xen-set-global-dirty-log', 'data': { 'enable': 'bool' } } + +## # @device_del: # # Remove a device from a guest @@ -2089,26 +2137,33 @@ # supported on i386 and x86_64. # # @paging: if true, do paging to get guest's memory mapping. This allows -# using gdb to process the core file. However, setting @paging to false -# may be desirable because of two reasons: +# using gdb to process the core file. +# +# IMPORTANT: this option can make QEMU allocate several gigabytes +# of RAM. This can happen for a large guest, or a +# malicious guest pretending to be large. +# +# Also, paging=true has the following limitations: # -# 1. The guest may be in a catastrophic state or can have corrupted -# memory, which cannot be trusted -# 2. The guest can be in real-mode even if paging is enabled. For example, -# the guest uses ACPI to sleep, and ACPI sleep state goes in real-mode +# 1. The guest may be in a catastrophic state or can have corrupted +# memory, which cannot be trusted +# 2. The guest can be in real-mode even if paging is enabled. For +# example, the guest uses ACPI to sleep, and ACPI sleep state +# goes in real-mode # # @protocol: the filename or file descriptor of the vmcore. The supported -# protocols are: +# protocols are: # -# 1. file: the protocol starts with "file:", and the following string is -# the file's path. -# 2. fd: the protocol starts with "fd:", and the following string is the -# fd's name. +# 1. file: the protocol starts with "file:", and the following +# string is the file's path. +# 2. fd: the protocol starts with "fd:", and the following string +# is the fd's name. # # @begin: #optional if specified, the starting physical address. # # @length: #optional if specified, the memory size, in bytes. If you don't -# want to dump all guest's memory, please specify the start @begin and @length +# want to dump all guest's memory, please specify the start @begin +# and @length # # Returns: nothing on success # @@ -2117,6 +2172,7 @@ { 'command': 'dump-guest-memory', 'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int', '*length': 'int' } } + ## # @netdev_add: # @@ -2695,12 +2751,26 @@ 'lf', 'help', 'meta_l', 'meta_r', 'compose' ] } ## +# @KeyValue +# +# Represents a keyboard key. +# +# Since: 1.3.0 +## +{ 'union': 'KeyValue', + 'data': { + 'number': 'int', + 'qcode': 'QKeyCode' } } + +## # @send-key: # # Send keys to guest. # -# @keys: key sequence. 'keys' is the name of the key. Use a JSON array to -# press several keys simultaneously. +# @keys: An array of @KeyValue elements. All @KeyValues in this array are +# simultaneously sent to the guest. A @KeyValue.number value is sent +# directly to the guest, while @KeyValue.qcode must be a valid +# @QKeyCode value # # @hold-time: #optional time to delay key up events, milliseconds. Defaults # to 100 @@ -2712,7 +2782,7 @@ # ## { 'command': 'send-key', - 'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } } + 'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } } ## # @screendump: diff --git a/qemu-barrier.h b/qemu-barrier.h index 7e1119781..16f09429c 100644 --- a/qemu-barrier.h +++ b/qemu-barrier.h @@ -19,7 +19,7 @@ * mfence on 32 bit as well, e.g. if built with -march=pentium-m. * However, on i386, there seem to be known bugs as recently as 4.3. * */ -#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 +#if QEMU_GNUC_PREREQ(4, 4) #define smp_mb() __sync_synchronize() #else #define smp_mb() asm volatile("lock; addl $0,0(%%esp) " ::: "memory") diff --git a/qemu-tool.c b/qemu-tool.c index 18205baba..f2f98138c 100644 --- a/qemu-tool.c +++ b/qemu-tool.c @@ -19,6 +19,7 @@ #include "qemu-log.h" #include "migration.h" #include "main-loop.h" +#include "sysemu.h" #include "qemu_socket.h" #include "slirp/libslirp.h" @@ -37,6 +38,11 @@ const char *qemu_get_vm_name(void) Monitor *cur_mon; +void vm_stop(RunState state) +{ + abort(); +} + int monitor_cur_is_qmp(void) { return 0; diff --git a/qga/commands-posix.c b/qga/commands-posix.c index ce9042123..b9f357cb9 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -828,7 +828,7 @@ GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp) } memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, info->value->name, IF_NAMESIZE); + pstrcpy(ifr.ifr_name, IF_NAMESIZE, info->value->name); if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) { snprintf(err_msg, sizeof(err_msg), "failed to get MAC address of %s: %s", diff --git a/qmp-commands.hx b/qmp-commands.hx index 71d7c25f5..2f8477e2a 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -493,6 +493,30 @@ Example: EQMP { + .name = "xen-set-global-dirty-log", + .args_type = "enable:b", + .mhandler.cmd_new = qmp_marshal_input_xen_set_global_dirty_log, + }, + +SQMP +xen-set-global-dirty-log +------- + +Enable or disable the global dirty log mode. + +Arguments: + +- "enable": Enable it or disable it. + +Example: + +-> { "execute": "xen-set-global-dirty-log", + "arguments": { "enable": true } } +<- { "return": {} } + +EQMP + + { .name = "migrate", .args_type = "detach:-d,blk:-b,inc:-i,uri:s", .mhandler.cmd_new = qmp_marshal_input_migrate, @@ -787,7 +811,7 @@ EQMP { .name = "block-stream", - .args_type = "device:B,base:s?,speed:o?", + .args_type = "device:B,base:s?,speed:o?,on-error:s?", .mhandler.cmd_new = qmp_marshal_input_block_stream, }, @@ -1247,10 +1271,7 @@ EQMP { .name = "add_client", .args_type = "protocol:s,fdname:s,skipauth:b?,tls:b?", - .params = "protocol fdname skipauth tls", - .help = "add a graphics client", - .user_print = monitor_user_noop, - .mhandler.cmd_new = add_graphics_client, + .mhandler.cmd_new = qmp_marshal_input_add_client, }, SQMP @@ -479,3 +479,46 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return arch_query_cpu_definitions(errp); } +void qmp_add_client(const char *protocol, const char *fdname, + bool has_skipauth, bool skipauth, bool has_tls, bool tls, + Error **errp) +{ + CharDriverState *s; + int fd; + + fd = monitor_get_fd(cur_mon, fdname, errp); + if (fd < 0) { + return; + } + + if (strcmp(protocol, "spice") == 0) { + if (!using_spice) { + error_set(errp, QERR_DEVICE_NOT_ACTIVE, "spice"); + close(fd); + return; + } + skipauth = has_skipauth ? skipauth : false; + tls = has_tls ? tls : false; + if (qemu_spice_display_add_client(fd, skipauth, tls) < 0) { + error_setg(errp, "spice failed to add client"); + close(fd); + } + return; +#ifdef CONFIG_VNC + } else if (strcmp(protocol, "vnc") == 0) { + skipauth = has_skipauth ? skipauth : false; + vnc_display_add_client(NULL, fd, skipauth); + return; +#endif + } else if ((s = qemu_chr_find(protocol)) != NULL) { + if (qemu_chr_add_client(s, fd) < 0) { + error_setg(errp, "failed to add client"); + close(fd); + return; + } + return; + } + + error_setg(errp, "protocol '%s' is invalid", protocol); + close(fd); +} diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 49ef569a2..1b8483495 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -91,9 +91,9 @@ const char *%(name)s_lookup[] = { def generate_enum_name(name): if name.isupper(): - return c_fun(name) + return c_fun(name, False) new_name = '' - for c in c_fun(name): + for c in c_fun(name, False): if c.isupper(): new_name += '_' new_name += c diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index e2093e894..a360de719 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -173,7 +173,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error ** break; ''', abbrev = de_camel_case(name).upper(), - enum = c_fun(de_camel_case(key)).upper(), + enum = c_fun(de_camel_case(key),False).upper(), c_type=members[key], c_name=c_fun(key)) diff --git a/scripts/qapi.py b/scripts/qapi.py index 122b4cb6d..afc5f32ae 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -141,7 +141,7 @@ def camel_case(name): new_name += ch.lower() return new_name -def c_var(name): +def c_var(name, protect=True): # ANSI X3J11/88-090, 3.1.1 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double', 'else', 'enum', 'extern', 'float', @@ -156,12 +156,14 @@ def c_var(name): # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html # excluding _.* gcc_words = set(['asm', 'typeof']) - if name in c89_words | c99_words | c11_words | gcc_words: + # namespace pollution: + polluted_words = set(['unix']) + if protect and (name in c89_words | c99_words | c11_words | gcc_words | polluted_words): return "q_" + name return name.replace('-', '_').lstrip("*") -def c_fun(name): - return c_var(name).replace('.', '_') +def c_fun(name, protect=True): + return c_var(name, protect).replace('.', '_') def c_list_type(name): return '%sList' % name diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index 53a6f8765..67be2efec 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -57,7 +57,7 @@ done rm -rf "$output/linux-headers/linux" mkdir -p "$output/linux-headers/linux" -for header in kvm.h kvm_para.h vhost.h virtio_config.h virtio_ring.h; do +for header in kvm.h kvm_para.h vfio.h vhost.h virtio_config.h virtio_ring.h; do cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux" done rm -rf "$output/linux-headers/asm-generic" diff --git a/target-i386/cc_helper.c b/target-i386/cc_helper.c index 07892f904..9422003f2 100644 --- a/target-i386/cc_helper.c +++ b/target-i386/cc_helper.c @@ -353,6 +353,16 @@ void helper_sti(CPUX86State *env) env->eflags |= IF_MASK; } +void helper_clac(CPUX86State *env) +{ + env->eflags &= ~AC_MASK; +} + +void helper_stac(CPUX86State *env) +{ + env->eflags |= AC_MASK; +} + #if 0 /* vm86plus instructions */ void helper_cli_vm(CPUX86State *env) diff --git a/target-i386/cpu.c b/target-i386/cpu.c index fd4fe2898..bb1e44eb0 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -61,15 +61,19 @@ static const char *ext_feature_name[] = { "tsc-deadline", "aes", "xsave", "osxsave", "avx", NULL, NULL, "hypervisor", }; +/* Feature names that are already defined on feature_name[] but are set on + * CPUID[8000_0001].EDX on AMD CPUs don't have their names on + * ext2_feature_name[]. They are copied automatically to cpuid_ext2_features + * if and only if CPU vendor is AMD. + */ static const char *ext2_feature_name[] = { - "fpu", "vme", "de", "pse", - "tsc", "msr", "pae", "mce", - "cx8" /* AMD CMPXCHG8B */, "apic", NULL, "syscall", - "mtrr", "pge", "mca", "cmov", - "pat", "pse36", NULL, NULL /* Linux mp */, - "nx|xd", NULL, "mmxext", "mmx", - "fxsr", "fxsr_opt|ffxsr", "pdpe1gb" /* AMD Page1GB */, "rdtscp", - NULL, "lm|i64", "3dnowext", "3dnow", + NULL /* fpu */, NULL /* vme */, NULL /* de */, NULL /* pse */, + NULL /* tsc */, NULL /* msr */, NULL /* pae */, NULL /* mce */, + NULL /* cx8 */ /* AMD CMPXCHG8B */, NULL /* apic */, NULL, "syscall", + NULL /* mtrr */, NULL /* pge */, NULL /* mca */, NULL /* cmov */, + NULL /* pat */, NULL /* pse36 */, NULL, NULL /* Linux mp */, + "nx|xd", NULL, "mmxext", NULL /* mmx */, + NULL /* fxsr */, "fxsr_opt|ffxsr", "pdpe1gb" /* AMD Page1GB */, "rdtscp", }; static const char *ext3_feature_name[] = { "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, @@ -100,6 +104,13 @@ static const char *svm_feature_name[] = { NULL, NULL, NULL, NULL, }; +static const char *cpuid_7_0_ebx_feature_name[] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "smep", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "smap", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + /* collects per-function cpuid data */ typedef struct model_features_t { @@ -215,14 +226,17 @@ static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features, uint32_t *ext2_features, uint32_t *ext3_features, uint32_t *kvm_features, - uint32_t *svm_features) + uint32_t *svm_features, + uint32_t *cpuid_7_0_ebx_features) { if (!lookup_feature(features, flagname, NULL, feature_name) && !lookup_feature(ext_features, flagname, NULL, ext_feature_name) && !lookup_feature(ext2_features, flagname, NULL, ext2_feature_name) && !lookup_feature(ext3_features, flagname, NULL, ext3_feature_name) && !lookup_feature(kvm_features, flagname, NULL, kvm_feature_name) && - !lookup_feature(svm_features, flagname, NULL, svm_feature_name)) + !lookup_feature(svm_features, flagname, NULL, svm_feature_name) && + !lookup_feature(cpuid_7_0_ebx_features, flagname, NULL, + cpuid_7_0_ebx_feature_name)) fprintf(stderr, "CPU feature %s not found\n", flagname); } @@ -258,7 +272,6 @@ typedef struct x86_def_t { CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_PGE | CPUID_CMOV | \ CPUID_PAT | CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | \ CPUID_PAE | CPUID_SEP | CPUID_APIC) -#define EXT2_FEATURE_MASK 0x0183F3FF #define TCG_FEATURES (CPUID_FP87 | CPUID_PSE | CPUID_TSC | CPUID_MSR | \ CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | CPUID_SEP | \ @@ -276,7 +289,7 @@ typedef struct x86_def_t { /* missing: CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_XSAVE */ -#define TCG_EXT2_FEATURES ((TCG_FEATURES & EXT2_FEATURE_MASK) | \ +#define TCG_EXT2_FEATURES ((TCG_FEATURES & CPUID_EXT2_AMD_ALIASES) | \ CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_RDTSCP | \ CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT) /* missing: @@ -284,6 +297,7 @@ typedef struct x86_def_t { #define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A) #define TCG_SVM_FEATURES 0 +#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP) /* maintains list of cpu model definitions */ @@ -305,7 +319,7 @@ static x86_def_t builtin_x86_defs[] = { CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, .ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, @@ -325,7 +339,7 @@ static x86_def_t builtin_x86_defs[] = { CPUID_PSE36 | CPUID_VME | CPUID_HT, .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_MMXEXT | CPUID_EXT2_FFXSR | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP, @@ -373,7 +387,7 @@ static x86_def_t builtin_x86_defs[] = { /* Missing: CPUID_EXT_POPCNT, CPUID_EXT_MONITOR */ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16, /* Missing: CPUID_EXT2_PDPE1GB, CPUID_EXT2_RDTSCP */ - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, /* Missing: CPUID_EXT3_LAHF_LM, CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC, CPUID_EXT3_CR8LEG, CPUID_EXT3_ABM, CPUID_EXT3_SSE4A, @@ -402,7 +416,7 @@ static x86_def_t builtin_x86_defs[] = { .features = PPRO_FEATURES | CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, .ext_features = CPUID_EXT_SSE3, - .ext2_features = PPRO_FEATURES & EXT2_FEATURE_MASK, + .ext2_features = PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES, .ext3_features = 0, .xlevel = 0x80000008, .model_id = "Common 32-bit KVM processor" @@ -467,8 +481,10 @@ static x86_def_t builtin_x86_defs[] = { .family = 6, .model = 2, .stepping = 3, - .features = PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | CPUID_MCA, - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, + .features = PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | + CPUID_MCA, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | + CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, .xlevel = 0x80000008, }, { @@ -484,7 +500,8 @@ static x86_def_t builtin_x86_defs[] = { /* Some CPUs got no CPUID_SEP */ .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | CPUID_EXT_DSCPL | CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR, - .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | CPUID_EXT2_NX, + .ext2_features = (PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) | + CPUID_EXT2_NX, .ext3_features = CPUID_EXT3_LAHF_LM, .xlevel = 0x8000000A, .model_id = "Intel(R) Atom(TM) CPU N270 @ 1.60GHz", @@ -1091,10 +1108,12 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) uint32_t plus_features = 0, plus_ext_features = 0; uint32_t plus_ext2_features = 0, plus_ext3_features = 0; uint32_t plus_kvm_features = 0, plus_svm_features = 0; + uint32_t plus_7_0_ebx_features = 0; /* Features to be removed */ uint32_t minus_features = 0, minus_ext_features = 0; uint32_t minus_ext2_features = 0, minus_ext3_features = 0; uint32_t minus_kvm_features = 0, minus_svm_features = 0; + uint32_t minus_7_0_ebx_features = 0; uint32_t numvalue; for (def = x86_defs; def; def = def->next) @@ -1121,8 +1140,8 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) #endif add_flagname_to_bitmaps("hypervisor", &plus_features, - &plus_ext_features, &plus_ext2_features, &plus_ext3_features, - &plus_kvm_features, &plus_svm_features); + &plus_ext_features, &plus_ext2_features, &plus_ext3_features, + &plus_kvm_features, &plus_svm_features, &plus_7_0_ebx_features); featurestr = strtok(NULL, ","); @@ -1132,12 +1151,12 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) add_flagname_to_bitmaps(featurestr + 1, &plus_features, &plus_ext_features, &plus_ext2_features, &plus_ext3_features, &plus_kvm_features, - &plus_svm_features); + &plus_svm_features, &plus_7_0_ebx_features); } else if (featurestr[0] == '-') { add_flagname_to_bitmaps(featurestr + 1, &minus_features, &minus_ext_features, &minus_ext2_features, &minus_ext3_features, &minus_kvm_features, - &minus_svm_features); + &minus_svm_features, &minus_7_0_ebx_features); } else if ((val = strchr(featurestr, '='))) { *val = 0; val++; if (!strcmp(featurestr, "family")) { @@ -1243,16 +1262,21 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) x86_cpu_def->ext3_features |= plus_ext3_features; x86_cpu_def->kvm_features |= plus_kvm_features; x86_cpu_def->svm_features |= plus_svm_features; + x86_cpu_def->cpuid_7_0_ebx_features |= plus_7_0_ebx_features; x86_cpu_def->features &= ~minus_features; x86_cpu_def->ext_features &= ~minus_ext_features; x86_cpu_def->ext2_features &= ~minus_ext2_features; x86_cpu_def->ext3_features &= ~minus_ext3_features; x86_cpu_def->kvm_features &= ~minus_kvm_features; x86_cpu_def->svm_features &= ~minus_svm_features; + x86_cpu_def->cpuid_7_0_ebx_features &= ~minus_7_0_ebx_features; if (check_cpuid) { if (check_features_against_host(x86_cpu_def) && enforce_cpuid) goto error; } + if (x86_cpu_def->cpuid_7_0_ebx_features && x86_cpu_def->level < 7) { + x86_cpu_def->level = 7; + } g_free(s); return 0; @@ -1306,13 +1330,13 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) } (*cpu_fprintf)(f, "\nRecognized CPUID flags:\n"); listflags(buf, sizeof(buf), (uint32_t)~0, feature_name, 1); - (*cpu_fprintf)(f, " f_edx: %s\n", buf); + (*cpu_fprintf)(f, " %s\n", buf); listflags(buf, sizeof(buf), (uint32_t)~0, ext_feature_name, 1); - (*cpu_fprintf)(f, " f_ecx: %s\n", buf); + (*cpu_fprintf)(f, " %s\n", buf); listflags(buf, sizeof(buf), (uint32_t)~0, ext2_feature_name, 1); - (*cpu_fprintf)(f, " extf_edx: %s\n", buf); + (*cpu_fprintf)(f, " %s\n", buf); listflags(buf, sizeof(buf), (uint32_t)~0, ext3_feature_name, 1); - (*cpu_fprintf)(f, " extf_ecx: %s\n", buf); + (*cpu_fprintf)(f, " %s\n", buf); } CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) @@ -1368,10 +1392,21 @@ int cpu_x86_register(X86CPU *cpu, const char *cpu_model) env->cpuid_kvm_features = def->kvm_features; env->cpuid_svm_features = def->svm_features; env->cpuid_ext4_features = def->ext4_features; - env->cpuid_7_0_ebx = def->cpuid_7_0_ebx_features; + env->cpuid_7_0_ebx_features = def->cpuid_7_0_ebx_features; env->cpuid_xlevel2 = def->xlevel2; object_property_set_int(OBJECT(cpu), (int64_t)def->tsc_khz * 1000, "tsc-frequency", &error); + + /* On AMD CPUs, some CPUID[8000_0001].EDX bits must match the bits on + * CPUID[1].EDX. + */ + if (env->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && + env->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && + env->cpuid_vendor3 == CPUID_VENDOR_AMD_3) { + env->cpuid_ext2_features &= ~CPUID_EXT2_AMD_ALIASES; + env->cpuid_ext2_features |= (def->features & CPUID_EXT2_AMD_ALIASES); + } + if (!kvm_enabled()) { env->cpuid_features &= TCG_FEATURES; env->cpuid_ext_features &= TCG_EXT_FEATURES; @@ -1545,7 +1580,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, /* Structured Extended Feature Flags Enumeration Leaf */ if (count == 0) { *eax = 0; /* Maximum ECX value for sub-leaves */ - *ebx = env->cpuid_7_0_ebx; /* Feature flags */ + *ebx = env->cpuid_7_0_ebx_features; /* Feature flags */ *ecx = 0; /* Reserved */ *edx = 0; /* Reserved */ } else { diff --git a/target-i386/cpu.h b/target-i386/cpu.h index f78535bcc..d28d155a0 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -123,8 +123,8 @@ /* hidden flags - used internally by qemu to represent additional cpu states. Only the CPL, INHIBIT_IRQ, SMM and SVMI are not - redundant. We avoid using the IOPL_MASK, TF_MASK and VM_MASK bit - position to ease oring with eflags. */ + redundant. We avoid using the IOPL_MASK, TF_MASK, VM_MASK and AC_MASK + bit positions to ease oring with eflags. */ /* current cpl */ #define HF_CPL_SHIFT 0 /* true if soft mmu is being used */ @@ -147,10 +147,12 @@ #define HF_CS64_SHIFT 15 /* only used on x86_64: 64 bit code segment */ #define HF_RF_SHIFT 16 /* must be same as eflags */ #define HF_VM_SHIFT 17 /* must be same as eflags */ +#define HF_AC_SHIFT 18 /* must be same as eflags */ #define HF_SMM_SHIFT 19 /* CPU in SMM mode */ #define HF_SVME_SHIFT 20 /* SVME enabled (copy of EFER.SVME) */ #define HF_SVMI_SHIFT 21 /* SVM intercepts are active */ #define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */ +#define HF_SMAP_SHIFT 23 /* CR4.SMAP */ #define HF_CPL_MASK (3 << HF_CPL_SHIFT) #define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT) @@ -168,10 +170,12 @@ #define HF_CS64_MASK (1 << HF_CS64_SHIFT) #define HF_RF_MASK (1 << HF_RF_SHIFT) #define HF_VM_MASK (1 << HF_VM_SHIFT) +#define HF_AC_MASK (1 << HF_AC_SHIFT) #define HF_SMM_MASK (1 << HF_SMM_SHIFT) #define HF_SVME_MASK (1 << HF_SVME_SHIFT) #define HF_SVMI_MASK (1 << HF_SVMI_SHIFT) #define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT) +#define HF_SMAP_MASK (1 << HF_SMAP_SHIFT) /* hflags2 */ @@ -210,6 +214,13 @@ #define CR4_OSFXSR_SHIFT 9 #define CR4_OSFXSR_MASK (1 << CR4_OSFXSR_SHIFT) #define CR4_OSXMMEXCPT_MASK (1 << 10) +#define CR4_VMXE_MASK (1 << 13) +#define CR4_SMXE_MASK (1 << 14) +#define CR4_FSGSBASE_MASK (1 << 16) +#define CR4_PCIDE_MASK (1 << 17) +#define CR4_OSXSAVE_MASK (1 << 18) +#define CR4_SMEP_MASK (1 << 20) +#define CR4_SMAP_MASK (1 << 21) #define DR6_BD (1 << 13) #define DR6_BS (1 << 14) @@ -409,6 +420,7 @@ #define CPUID_EXT_HYPERVISOR (1 << 31) #define CPUID_EXT2_FPU (1 << 0) +#define CPUID_EXT2_VME (1 << 1) #define CPUID_EXT2_DE (1 << 2) #define CPUID_EXT2_PSE (1 << 3) #define CPUID_EXT2_TSC (1 << 4) @@ -436,6 +448,17 @@ #define CPUID_EXT2_3DNOWEXT (1 << 30) #define CPUID_EXT2_3DNOW (1 << 31) +/* CPUID[8000_0001].EDX bits that are aliase of CPUID[1].EDX bits on AMD CPUs */ +#define CPUID_EXT2_AMD_ALIASES (CPUID_EXT2_FPU | CPUID_EXT2_VME | \ + CPUID_EXT2_DE | CPUID_EXT2_PSE | \ + CPUID_EXT2_TSC | CPUID_EXT2_MSR | \ + CPUID_EXT2_PAE | CPUID_EXT2_MCE | \ + CPUID_EXT2_CX8 | CPUID_EXT2_APIC | \ + CPUID_EXT2_MTRR | CPUID_EXT2_PGE | \ + CPUID_EXT2_MCA | CPUID_EXT2_CMOV | \ + CPUID_EXT2_PAT | CPUID_EXT2_PSE36 | \ + CPUID_EXT2_MMX | CPUID_EXT2_FXSR) + #define CPUID_EXT3_LAHF_LM (1 << 0) #define CPUID_EXT3_CMP_LEG (1 << 1) #define CPUID_EXT3_SVM (1 << 2) @@ -462,6 +485,9 @@ #define CPUID_SVM_PAUSEFILTER (1 << 10) #define CPUID_SVM_PFTHRESHOLD (1 << 12) +#define CPUID_7_0_EBX_SMEP (1 << 7) +#define CPUID_7_0_EBX_SMAP (1 << 20) + #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ #define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */ #define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */ @@ -637,7 +663,7 @@ typedef struct { #define CPU_NB_REGS CPU_NB_REGS32 #endif -#define NB_MMU_MODES 2 +#define NB_MMU_MODES 3 typedef enum TPRAccess { TPR_ACCESS_READ, @@ -767,7 +793,7 @@ typedef struct CPUX86State { uint32_t cpuid_xlevel2; uint32_t cpuid_ext4_features; /* Flags from CPUID[EAX=7,ECX=0].EBX */ - uint32_t cpuid_7_0_ebx; + uint32_t cpuid_7_0_ebx_features; /* MTRRs */ uint64_t mtrr_fixed[11]; @@ -1007,10 +1033,15 @@ static inline CPUX86State *cpu_init(const char *cpu_model) /* MMU modes definitions */ #define MMU_MODE0_SUFFIX _kernel #define MMU_MODE1_SUFFIX _user -#define MMU_USER_IDX 1 +#define MMU_MODE2_SUFFIX _ksmap /* Kernel with SMAP override */ +#define MMU_KERNEL_IDX 0 +#define MMU_USER_IDX 1 +#define MMU_KSMAP_IDX 2 static inline int cpu_mmu_index (CPUX86State *env) { - return (env->hflags & HF_CPL_MASK) == 3 ? 1 : 0; + return (env->hflags & HF_CPL_MASK) == 3 ? MMU_USER_IDX : + ((env->hflags & HF_SMAP_MASK) && (env->eflags & AC_MASK)) + ? MMU_KSMAP_IDX : MMU_KERNEL_IDX; } #undef EAX @@ -1096,7 +1127,7 @@ static inline void cpu_get_tb_cpu_state(CPUX86State *env, target_ulong *pc, *cs_base = env->segs[R_CS].base; *pc = *cs_base + env->eip; *flags = env->hflags | - (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK)); + (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK)); } void do_cpu_init(X86CPU *cpu); diff --git a/target-i386/helper.c b/target-i386/helper.c index 8a5da3d7c..c635667d6 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -443,17 +443,27 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) #if defined(DEBUG_MMU) printf("CR4 update: CR4=%08x\n", (uint32_t)env->cr[4]); #endif - if ((new_cr4 & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK)) != - (env->cr[4] & (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK))) { + if ((new_cr4 ^ env->cr[4]) & + (CR4_PGE_MASK | CR4_PAE_MASK | CR4_PSE_MASK | + CR4_SMEP_MASK | CR4_SMAP_MASK)) { tlb_flush(env, 1); } /* SSE handling */ - if (!(env->cpuid_features & CPUID_SSE)) + if (!(env->cpuid_features & CPUID_SSE)) { new_cr4 &= ~CR4_OSFXSR_MASK; - if (new_cr4 & CR4_OSFXSR_MASK) + } + env->hflags &= ~HF_OSFXSR_MASK; + if (new_cr4 & CR4_OSFXSR_MASK) { env->hflags |= HF_OSFXSR_MASK; - else - env->hflags &= ~HF_OSFXSR_MASK; + } + + if (!(env->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP)) { + new_cr4 &= ~CR4_SMAP_MASK; + } + env->hflags &= ~HF_SMAP_MASK; + if (new_cr4 & CR4_SMAP_MASK) { + env->hflags |= HF_SMAP_MASK; + } env->cr[4] = new_cr4; } @@ -591,17 +601,38 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, /* 2 MB page */ page_size = 2048 * 1024; ptep ^= PG_NX_MASK; - if ((ptep & PG_NX_MASK) && is_write1 == 2) + if ((ptep & PG_NX_MASK) && is_write1 == 2) { goto do_fault_protect; - if (is_user) { - if (!(ptep & PG_USER_MASK)) + } + switch (mmu_idx) { + case MMU_USER_IDX: + if (!(ptep & PG_USER_MASK)) { goto do_fault_protect; - if (is_write && !(ptep & PG_RW_MASK)) + } + if (is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; - } else { + } + break; + + case MMU_KERNEL_IDX: + if (is_write1 != 2 && (env->cr[4] & CR4_SMAP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + /* fall through */ + case MMU_KSMAP_IDX: + if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } if ((env->cr[0] & CR0_WP_MASK) && - is_write && !(ptep & PG_RW_MASK)) + is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; + } + break; + + default: /* cannot happen */ + break; } is_dirty = is_write && !(pde & PG_DIRTY_MASK); if (!(pde & PG_ACCESSED_MASK) || is_dirty) { @@ -635,15 +666,35 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, ptep ^= PG_NX_MASK; if ((ptep & PG_NX_MASK) && is_write1 == 2) goto do_fault_protect; - if (is_user) { - if (!(ptep & PG_USER_MASK)) + switch (mmu_idx) { + case MMU_USER_IDX: + if (!(ptep & PG_USER_MASK)) { goto do_fault_protect; - if (is_write && !(ptep & PG_RW_MASK)) + } + if (is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; - } else { + } + break; + + case MMU_KERNEL_IDX: + if (is_write1 != 2 && (env->cr[4] & CR4_SMAP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + /* fall through */ + case MMU_KSMAP_IDX: + if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } if ((env->cr[0] & CR0_WP_MASK) && - is_write && !(ptep & PG_RW_MASK)) + is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; + } + break; + + default: /* cannot happen */ + break; } is_dirty = is_write && !(pte & PG_DIRTY_MASK); if (!(pte & PG_ACCESSED_MASK) || is_dirty) { @@ -670,15 +721,35 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, /* if PSE bit is set, then we use a 4MB page */ if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { page_size = 4096 * 1024; - if (is_user) { - if (!(pde & PG_USER_MASK)) + switch (mmu_idx) { + case MMU_USER_IDX: + if (!(pde & PG_USER_MASK)) { goto do_fault_protect; - if (is_write && !(pde & PG_RW_MASK)) + } + if (is_write && !(pde & PG_RW_MASK)) { goto do_fault_protect; - } else { + } + break; + + case MMU_KERNEL_IDX: + if (is_write1 != 2 && (env->cr[4] & CR4_SMAP_MASK) && + (pde & PG_USER_MASK)) { + goto do_fault_protect; + } + /* fall through */ + case MMU_KSMAP_IDX: + if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && + (pde & PG_USER_MASK)) { + goto do_fault_protect; + } if ((env->cr[0] & CR0_WP_MASK) && - is_write && !(pde & PG_RW_MASK)) + is_write && !(pde & PG_RW_MASK)) { goto do_fault_protect; + } + break; + + default: /* cannot happen */ + break; } is_dirty = is_write && !(pde & PG_DIRTY_MASK); if (!(pde & PG_ACCESSED_MASK) || is_dirty) { @@ -707,15 +778,35 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, } /* combine pde and pte user and rw protections */ ptep = pte & pde; - if (is_user) { - if (!(ptep & PG_USER_MASK)) + switch (mmu_idx) { + case MMU_USER_IDX: + if (!(ptep & PG_USER_MASK)) { goto do_fault_protect; - if (is_write && !(ptep & PG_RW_MASK)) + } + if (is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; - } else { + } + break; + + case MMU_KERNEL_IDX: + if (is_write1 != 2 && (env->cr[4] & CR4_SMAP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + /* fall through */ + case MMU_KSMAP_IDX: + if (is_write1 == 2 && (env->cr[4] & CR4_SMEP_MASK) && + (ptep & PG_USER_MASK)) { + goto do_fault_protect; + } if ((env->cr[0] & CR0_WP_MASK) && - is_write && !(ptep & PG_RW_MASK)) + is_write && !(ptep & PG_RW_MASK)) { goto do_fault_protect; + } + break; + + default: /* cannot happen */ + break; } is_dirty = is_write && !(pte & PG_DIRTY_MASK); if (!(pte & PG_ACCESSED_MASK) || is_dirty) { @@ -762,8 +853,9 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, if (is_user) error_code |= PG_ERROR_U_MASK; if (is_write1 == 2 && - (env->efer & MSR_EFER_NXE) && - (env->cr[4] & CR4_PAE_MASK)) + (((env->efer & MSR_EFER_NXE) && + (env->cr[4] & CR4_PAE_MASK)) || + (env->cr[4] & CR4_SMEP_MASK))) error_code |= PG_ERROR_I_D_MASK; if (env->intercept_exceptions & (1 << EXCP0E_PAGE)) { /* cr2 is not modified in case of exceptions */ diff --git a/target-i386/helper.h b/target-i386/helper.h index ab6af638e..93850ceec 100644 --- a/target-i386/helper.h +++ b/target-i386/helper.h @@ -67,6 +67,8 @@ DEF_HELPER_3(raise_interrupt, void, env, int, int) DEF_HELPER_2(raise_exception, void, env, int) DEF_HELPER_1(cli, void, env) DEF_HELPER_1(sti, void, env) +DEF_HELPER_1(clac, void, env) +DEF_HELPER_1(stac, void, env) DEF_HELPER_1(set_inhibit_irq, void, env) DEF_HELPER_1(reset_inhibit_irq, void, env) DEF_HELPER_3(boundw, void, env, tl, int) diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 6790180b0..5b18383d8 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -165,7 +165,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, * so add missing bits according to the AMD spec: */ cpuid_1_edx = kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX); - ret |= cpuid_1_edx & 0x183f7ff; + ret |= cpuid_1_edx & CPUID_EXT2_AMD_ALIASES; break; } break; diff --git a/target-i386/translate.c b/target-i386/translate.c index 323869d87..0a7e4e348 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -107,6 +107,7 @@ typedef struct DisasContext { int cpuid_ext_features; int cpuid_ext2_features; int cpuid_ext3_features; + int cpuid_7_0_ebx_features; } DisasContext; static void gen_eob(DisasContext *s); @@ -6556,7 +6557,7 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) } gen_pop_update(s); s->cc_op = CC_OP_EFLAGS; - /* abort translation because TF flag may change */ + /* abort translation because TF/AC flag may change */ gen_jmp_im(s->pc - s->cs_base); gen_eob(s); } @@ -7206,6 +7207,24 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start) gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); gen_eob(s); break; + case 2: /* clac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || + s->cpl != 0) { + goto illegal_op; + } + gen_helper_clac(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + break; + case 3: /* stac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || + s->cpl != 0) { + goto illegal_op; + } + gen_helper_stac(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + break; default: goto illegal_op; } @@ -7901,15 +7920,13 @@ static inline void gen_intermediate_code_internal(CPUX86State *env, /* select memory access functions */ dc->mem_index = 0; if (flags & HF_SOFTMMU_MASK) { - if (dc->cpl == 3) - dc->mem_index = 2 * 4; - else - dc->mem_index = 1 * 4; + dc->mem_index = (cpu_mmu_index(env) + 1) << 2; } dc->cpuid_features = env->cpuid_features; dc->cpuid_ext_features = env->cpuid_ext_features; dc->cpuid_ext2_features = env->cpuid_ext2_features; dc->cpuid_ext3_features = env->cpuid_ext3_features; + dc->cpuid_7_0_ebx_features = env->cpuid_7_0_ebx_features; #ifdef TARGET_X86_64 dc->lma = (flags >> HF_LMA_SHIFT) & 1; dc->code64 = (flags >> HF_CS64_SHIFT) & 1; diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index a31d278a5..7f6e4e0b8 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -795,7 +795,7 @@ static int read_cpuinfo(const char *field, char *value, int len) break; } if (!strncmp(line, field, field_len)) { - strncpy(value, line, len); + pstrcpy(value, len, line); ret = 0; break; } diff --git a/target-ppc/mmu_helper.c b/target-ppc/mmu_helper.c index d2664acef..532b114ae 100644 --- a/target-ppc/mmu_helper.c +++ b/target-ppc/mmu_helper.c @@ -1032,12 +1032,10 @@ static int ppcemb_tlb_check(CPUPPCState *env, ppcemb_tlb_t *tlb, return -1; } *raddrp = (tlb->RPN & mask) | (address & ~mask); -#if (TARGET_PHYS_ADDR_BITS >= 36) if (ext) { /* Extend the physical address to 36 bits */ - *raddrp |= (target_phys_addr_t)(tlb->RPN & 0xF) << 32; + *raddrp |= (uint64_t)(tlb->RPN & 0xF) << 32; } -#endif return 0; } diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c index f7c004c7d..eb9f0e725 100644 --- a/target-sparc/cpu.c +++ b/target-sparc/cpu.c @@ -643,7 +643,7 @@ static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model) { unsigned int i; const sparc_def_t *def = NULL; - char *s = strdup(cpu_model); + char *s = g_strdup(cpu_model); char *featurestr, *name = strtok(s, ","); uint32_t plus_features = 0; uint32_t minus_features = 0; @@ -735,7 +735,7 @@ static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model) #ifdef DEBUG_FEATURES print_features(stderr, fprintf, cpu_def->features, NULL); #endif - free(s); + g_free(s); return 0; error: diff --git a/targphys.h b/targphys.h index bd4938fc0..08cade909 100644 --- a/targphys.h +++ b/targphys.h @@ -3,25 +3,10 @@ #ifndef TARGPHYS_H #define TARGPHYS_H -#ifdef TARGET_PHYS_ADDR_BITS +#define TARGET_PHYS_ADDR_BITS 64 /* target_phys_addr_t is the type of a physical address (its size can be different from 'target_ulong'). */ -#if TARGET_PHYS_ADDR_BITS == 32 -typedef uint32_t target_phys_addr_t; -#define TARGET_PHYS_ADDR_MAX UINT32_MAX -#define TARGET_FMT_plx "%08x" -/* Format strings for printing target_phys_addr_t types. - * These are recommended over the less flexible TARGET_FMT_plx, - * which is retained for the benefit of existing code. - */ -#define TARGET_PRIdPHYS PRId32 -#define TARGET_PRIiPHYS PRIi32 -#define TARGET_PRIoPHYS PRIo32 -#define TARGET_PRIuPHYS PRIu32 -#define TARGET_PRIxPHYS PRIx32 -#define TARGET_PRIXPHYS PRIX32 -#elif TARGET_PHYS_ADDR_BITS == 64 typedef uint64_t target_phys_addr_t; #define TARGET_PHYS_ADDR_MAX UINT64_MAX #define TARGET_FMT_plx "%016" PRIx64 @@ -31,7 +16,5 @@ typedef uint64_t target_phys_addr_t; #define TARGET_PRIuPHYS PRIu64 #define TARGET_PRIxPHYS PRIx64 #define TARGET_PRIXPHYS PRIX64 -#endif -#endif #endif diff --git a/tests/libqtest.c b/tests/libqtest.c index 02d039218..71b84c12d 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -85,6 +85,22 @@ static int socket_accept(int sock) return ret; } +static pid_t qtest_qemu_pid(QTestState *s) +{ + FILE *f; + char buffer[1024]; + pid_t pid = -1; + + f = fopen(s->pid_file, "r"); + if (f) { + if (fgets(buffer, sizeof(buffer), f)) { + pid = atoi(buffer); + } + } + fclose(f); + return pid; +} + QTestState *qtest_init(const char *extra_args) { QTestState *s; @@ -136,25 +152,21 @@ QTestState *qtest_init(const char *extra_args) qtest_qmp(s, ""); qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }"); + if (getenv("QTEST_STOP")) { + kill(qtest_qemu_pid(s), SIGSTOP); + } + return s; } void qtest_quit(QTestState *s) { - FILE *f; - char buffer[1024]; - - f = fopen(s->pid_file, "r"); - if (f) { - if (fgets(buffer, sizeof(buffer), f)) { - pid_t pid = atoi(buffer); - int status = 0; - - kill(pid, SIGTERM); - waitpid(pid, &status, 0); - } + int status; - fclose(f); + pid_t pid = qtest_qemu_pid(s); + if (pid != -1) { + kill(pid, SIGTERM); + waitpid(pid, &status, 0); } unlink(s->pid_file); diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index dfacdf11a..dd4ef1199 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -195,6 +195,226 @@ class TestSmallerBackingFile(ImageStreamingTestCase): self.assert_no_active_streams() self.vm.shutdown() +class TestErrors(ImageStreamingTestCase): + image_len = 2 * 1024 * 1024 # MB + + # this should match STREAM_BUFFER_SIZE/512 in block/stream.c + STREAM_BUFFER_SIZE = 512 * 1024 + + def create_blkdebug_file(self, name, event, errno): + file = open(name, 'w') + file.write(''' +[inject-error] +state = "1" +event = "%s" +errno = "%d" +immediately = "off" +once = "on" +sector = "%d" + +[set-state] +state = "1" +event = "%s" +new_state = "2" + +[set-state] +state = "2" +event = "%s" +new_state = "1" +''' % (event, errno, self.STREAM_BUFFER_SIZE / 512, event, event)) + file.close() + +class TestEIO(TestErrors): + def setUp(self): + self.blkdebug_file = backing_img + ".blkdebug" + self.create_image(backing_img, TestErrors.image_len) + self.create_blkdebug_file(self.blkdebug_file, "read_aio", 5) + qemu_img('create', '-f', iotests.imgfmt, + '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' + % (self.blkdebug_file, backing_img), + test_img) + self.vm = iotests.VM().add_drive(test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + os.remove(backing_img) + os.remove(self.blkdebug_file) + + def test_report(self): + self.assert_no_active_streams() + + result = self.vm.qmp('block-stream', device='drive0') + self.assert_qmp(result, 'return', {}) + + completed = False + error = False + while not completed: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_ERROR': + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'read') + error = True + elif event['event'] == 'BLOCK_JOB_COMPLETED': + self.assertTrue(error, 'job completed unexpectedly') + self.assert_qmp(event, 'data/type', 'stream') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/error', 'Input/output error') + self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True + + self.assert_no_active_streams() + self.vm.shutdown() + + def test_ignore(self): + self.assert_no_active_streams() + + result = self.vm.qmp('block-stream', device='drive0', on_error='ignore') + self.assert_qmp(result, 'return', {}) + + error = False + completed = False + while not completed: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_ERROR': + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'read') + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/paused', False) + error = True + elif event['event'] == 'BLOCK_JOB_COMPLETED': + self.assertTrue(error, 'job completed unexpectedly') + self.assert_qmp(event, 'data/type', 'stream') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/error', 'Input/output error') + self.assert_qmp(event, 'data/offset', self.image_len) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True + + self.assert_no_active_streams() + self.vm.shutdown() + + def test_stop(self): + self.assert_no_active_streams() + + result = self.vm.qmp('block-stream', device='drive0', on_error='stop') + self.assert_qmp(result, 'return', {}) + + error = False + completed = False + while not completed: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_ERROR': + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'read') + + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/paused', True) + self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) + self.assert_qmp(result, 'return[0]/io-status', 'failed') + + result = self.vm.qmp('block-job-resume', device='drive0') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/paused', False) + self.assert_qmp(result, 'return[0]/io-status', 'ok') + error = True + elif event['event'] == 'BLOCK_JOB_COMPLETED': + self.assertTrue(error, 'job completed unexpectedly') + self.assert_qmp(event, 'data/type', 'stream') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp_absent(event, 'data/error') + self.assert_qmp(event, 'data/offset', self.image_len) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True + + self.assert_no_active_streams() + self.vm.shutdown() + + def test_enospc(self): + self.assert_no_active_streams() + + result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') + self.assert_qmp(result, 'return', {}) + + completed = False + error = False + while not completed: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_ERROR': + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'read') + error = True + elif event['event'] == 'BLOCK_JOB_COMPLETED': + self.assertTrue(error, 'job completed unexpectedly') + self.assert_qmp(event, 'data/type', 'stream') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/error', 'Input/output error') + self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True + + self.assert_no_active_streams() + self.vm.shutdown() + +class TestENOSPC(TestErrors): + def setUp(self): + self.blkdebug_file = backing_img + ".blkdebug" + self.create_image(backing_img, TestErrors.image_len) + self.create_blkdebug_file(self.blkdebug_file, "read_aio", 28) + qemu_img('create', '-f', iotests.imgfmt, + '-o', 'backing_file=blkdebug:%s:%s,backing_fmt=raw' + % (self.blkdebug_file, backing_img), + test_img) + self.vm = iotests.VM().add_drive(test_img) + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + os.remove(test_img) + os.remove(backing_img) + os.remove(self.blkdebug_file) + + def test_enospc(self): + self.assert_no_active_streams() + + result = self.vm.qmp('block-stream', device='drive0', on_error='enospc') + self.assert_qmp(result, 'return', {}) + + error = False + completed = False + while not completed: + for event in self.vm.get_qmp_events(wait=True): + if event['event'] == 'BLOCK_JOB_ERROR': + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp(event, 'data/operation', 'read') + + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/paused', True) + self.assert_qmp(result, 'return[0]/offset', self.STREAM_BUFFER_SIZE) + self.assert_qmp(result, 'return[0]/io-status', 'nospace') + + result = self.vm.qmp('block-job-resume', device='drive0') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('query-block-jobs') + self.assert_qmp(result, 'return[0]/paused', False) + self.assert_qmp(result, 'return[0]/io-status', 'ok') + error = True + elif event['event'] == 'BLOCK_JOB_COMPLETED': + self.assertTrue(error, 'job completed unexpectedly') + self.assert_qmp(event, 'data/type', 'stream') + self.assert_qmp(event, 'data/device', 'drive0') + self.assert_qmp_absent(event, 'data/error') + self.assert_qmp(event, 'data/offset', self.image_len) + self.assert_qmp(event, 'data/len', self.image_len) + completed = True + + self.assert_no_active_streams() + self.vm.shutdown() class TestStreamStop(ImageStreamingTestCase): image_len = 8 * 1024 * 1024 * 1024 # GB diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out index 594c16f49..fa16b5cce 100644 --- a/tests/qemu-iotests/030.out +++ b/tests/qemu-iotests/030.out @@ -1,5 +1,5 @@ -........ +............. ---------------------------------------------------------------------- -Ran 8 tests +Ran 13 tests OK diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index e05b1d640..3c60b2d16 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -19,6 +19,7 @@ import os import re import subprocess +import string import unittest import sys; sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'QMP')) import qmp @@ -96,9 +97,14 @@ class VM(object): os.remove(self._qemu_log_path) self._popen = None + underscore_to_dash = string.maketrans('_', '-') def qmp(self, cmd, **args): '''Invoke a QMP command and return the result dict''' - return self._qmp.cmd(cmd, args=args) + qmp_args = dict() + for k in args.keys(): + qmp_args[k.translate(self.underscore_to_dash)] = args[k] + + return self._qmp.cmd(cmd, args=qmp_args) def get_qmp_events(self, wait=False): '''Poll for queued QMP events and return a list of dicts''' @@ -132,6 +138,13 @@ class QMPTestCase(unittest.TestCase): self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d))) return d + def assert_qmp_absent(self, d, path): + try: + result = self.dictpath(d, path) + except AssertionError: + return + self.fail('path "%s" has value "%s"' % (path, str(result))) + def assert_qmp(self, d, path, value): '''Assert that the value for a specific path in a QMP dict matches''' result = self.dictpath(d, path) diff --git a/tests/rtc-test.c b/tests/rtc-test.c index f23ac3a6c..7fdc94a3d 100644 --- a/tests/rtc-test.c +++ b/tests/rtc-test.c @@ -179,6 +179,77 @@ static void check_time(int wiggle) static int wiggle = 2; +static void set_year_20xx(void) +{ + /* Set BCD mode */ + cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM); + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_YEAR, 0x11); + cmos_write(RTC_CENTURY, 0x20); + cmos_write(RTC_MONTH, 0x02); + cmos_write(RTC_DAY_OF_MONTH, 0x02); + cmos_write(RTC_HOURS, 0x02); + cmos_write(RTC_MINUTES, 0x04); + cmos_write(RTC_SECONDS, 0x58); + cmos_write(RTC_REG_A, 0x26); + + g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); + g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); + g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11); + g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); + + /* Set a date in 2080 to ensure there is no year-2038 overflow. */ + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_YEAR, 0x80); + cmos_write(RTC_REG_A, 0x26); + + g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); + g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); + g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80); + g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); + + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_YEAR, 0x11); + cmos_write(RTC_REG_A, 0x26); + + g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); + g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); + g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x11); + g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x20); +} + +static void set_year_1980(void) +{ + /* Set BCD mode */ + cmos_write(RTC_REG_B, cmos_read(RTC_REG_B) & ~REG_B_DM); + cmos_write(RTC_REG_A, 0x76); + cmos_write(RTC_YEAR, 0x80); + cmos_write(RTC_CENTURY, 0x19); + cmos_write(RTC_MONTH, 0x02); + cmos_write(RTC_DAY_OF_MONTH, 0x02); + cmos_write(RTC_HOURS, 0x02); + cmos_write(RTC_MINUTES, 0x04); + cmos_write(RTC_SECONDS, 0x58); + cmos_write(RTC_REG_A, 0x26); + + g_assert_cmpint(cmos_read(RTC_HOURS), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MINUTES), ==, 0x04); + g_assert_cmpint(cmos_read(RTC_SECONDS), >=, 0x58); + g_assert_cmpint(cmos_read(RTC_DAY_OF_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_MONTH), ==, 0x02); + g_assert_cmpint(cmos_read(RTC_YEAR), ==, 0x80); + g_assert_cmpint(cmos_read(RTC_CENTURY), ==, 0x19); +} + static void bcd_check_time(void) { /* Set BCD mode */ @@ -269,6 +340,8 @@ int main(int argc, char **argv) qtest_add_func("/rtc/bcd/check-time", bcd_check_time); qtest_add_func("/rtc/dec/check-time", dec_check_time); qtest_add_func("/rtc/alarm-time", alarm_time); + qtest_add_func("/rtc/set-year/20xx", set_year_20xx); + qtest_add_func("/rtc/set-year/1980", set_year_1980); qtest_add_func("/rtc/fuzz-registers", fuzz_registers); ret = g_test_run(); diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 8fba7702c..bfdcb46ef 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -432,9 +432,7 @@ static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len) { - char *mechname = g_malloc(len + 1); - strncpy(mechname, (char*)data, len); - mechname[len] = '\0'; + char *mechname = g_strndup((const char *) data, len); VNC_DEBUG("Got client mechname '%s' check against '%s'\n", mechname, vs->sasl.mechlist); @@ -14,6 +14,7 @@ #include "hw/pc.h" #include "hw/xen_common.h" #include "hw/xen_backend.h" +#include "qmp-commands.h" #include "range.h" #include "xen-mapcache.h" @@ -36,6 +37,7 @@ static MemoryRegion ram_memory, ram_640k, ram_lo, ram_hi; static MemoryRegion *framebuffer; +static bool xen_in_migration; /* Compatibility with older version */ #if __XEN_LATEST_INTERFACE_VERSION__ < 0x0003020a @@ -505,7 +507,8 @@ static void xen_sync_dirty_bitmap(XenIOState *state, bitmap); if (rc < 0) { if (rc != -ENODATA) { - fprintf(stderr, "xen: track_dirty_vram failed (0x" TARGET_FMT_plx + memory_region_set_dirty(framebuffer, 0, size); + DPRINTF("xen: track_dirty_vram failed (0x" TARGET_FMT_plx ", 0x" TARGET_FMT_plx "): %s\n", start_addr, start_addr + size, strerror(-rc)); } @@ -552,10 +555,14 @@ static void xen_log_sync(MemoryListener *listener, MemoryRegionSection *section) static void xen_log_global_start(MemoryListener *listener) { + if (xen_enabled()) { + xen_in_migration = true; + } } static void xen_log_global_stop(MemoryListener *listener) { + xen_in_migration = false; } static void xen_eventfd_add(MemoryListener *listener, @@ -588,6 +595,15 @@ static MemoryListener xen_memory_listener = { .priority = 10, }; +void qmp_xen_set_global_dirty_log(bool enable, Error **errp) +{ + if (enable) { + memory_global_dirty_log_start(); + } else { + memory_global_dirty_log_stop(); + } +} + /* VCPU Operations, MMIO, IO ring ... */ static void xen_reset_vcpu(void *opaque) @@ -1213,3 +1229,24 @@ void xen_shutdown_fatal_error(const char *fmt, ...) /* destroy the domain */ qemu_system_shutdown_request(); } + +void xen_modified_memory(ram_addr_t start, ram_addr_t length) +{ + if (unlikely(xen_in_migration)) { + int rc; + ram_addr_t start_pfn, nb_pages; + + if (length == 0) { + length = TARGET_PAGE_SIZE; + } + start_pfn = start >> TARGET_PAGE_BITS; + nb_pages = ((start + length + TARGET_PAGE_SIZE - 1) >> TARGET_PAGE_BITS) + - start_pfn; + rc = xc_hvm_modified_memory(xen_xc, xen_domid, start_pfn, nb_pages); + if (rc) { + fprintf(stderr, + "%s failed for "RAM_ADDR_FMT" ("RAM_ADDR_FMT"): %i, %s\n", + __func__, start, nb_pages, rc, strerror(-rc)); + } + } +} diff --git a/xen-stub.c b/xen-stub.c index 8ff2b79ac..921439263 100644 --- a/xen-stub.c +++ b/xen-stub.c @@ -11,6 +11,7 @@ #include "qemu-common.h" #include "hw/xen.h" #include "memory.h" +#include "qmp-commands.h" void xenstore_store_pv_console_info(int i, CharDriverState *chr) { @@ -54,3 +55,11 @@ int xen_init(void) void xen_register_framebuffer(MemoryRegion *mr) { } + +void qmp_xen_set_global_dirty_log(bool enable, Error **errp) +{ +} + +void xen_modified_memory(ram_addr_t start, ram_addr_t length) +{ +} |