*-linux-user
*-bsd-user
libdis*
+libhw
libhw32
libhw64
libuser
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
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=libhw libuser libdis libdis-user
clean:
# avoid old build problems by removing potentially incorrect old files
include ../config-host.mak
include ../config-all-devices.mak
-include config.mak
include $(SRC_PATH)/rules.mak
.PHONY: all
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
--- /dev/null
+#!/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:])
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
error = socket.error
- def connect(self):
+ def connect(self, negotiate=True):
"""
Connect to the QMP Monitor and perform capabilities negotiation.
@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):
"""
def close(self):
self.__sock.close()
self.__sockfile.close()
+
+ timeout = socket.timeout
+
+ def settimeout(self, timeout):
+ self.__sock.settimeout(timeout)
# 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
case "$target_arch2" in
i386)
- target_phys_bits=64
;;
x86_64)
TARGET_BASE_ARCH=i386
target_long_alignment=8
;;
alpha)
- target_phys_bits=64
target_long_alignment=8
target_nptl="yes"
;;
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
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"
;;
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"
;;
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"
;;
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)
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)
;;
xtensa|xtensaeb)
TARGET_ARCH=xtensa
- target_phys_bits=32
;;
*)
echo "Unsupported target CPU"
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"
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
+ echo "HWDIR=../libhw" >> $config_target_mak
+ echo "subdir-$target: subdir-libhw" >> $config_host_mak
if test "$smartcard_nss" = "yes" ; then
echo "subdir-$target: subdir-libcacard" >> $config_host_mak
fi
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=libhw
+symlink "$source_path/Makefile.hw" "$d/Makefile"
d=libuser
symlink "$source_path/Makefile.user" "$d/Makefile"
};
/* 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
DMAContext *dma;
};
-#if defined(TARGET_PHYS_ADDR_BITS)
+#ifndef CONFIG_USER_ONLY
/*
* When an IOMMU is present, bus addresses become distinct from
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;
#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;
}
}
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;
else {
a <<= shiftCount;
}
- return roundAndPackFloat32( 1 > 0, 0x9C - shiftCount, a STATUS_VAR );
+ return roundAndPackFloat32(0, 0x9C - shiftCount, a STATUS_VAR);
}
}
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;
#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,
},
@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.
{
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");
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)
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, '-');
}
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;
}
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)
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
#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
{
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;
}
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);
#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
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)
}
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);
}
} 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:
// 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) {
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;
}
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 */
#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
#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,
} 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;
typedef struct XHCISlot {
bool enabled;
dma_addr_t ctx;
- unsigned int port;
+ USBPort *uport;
unsigned int devaddr;
XHCIEPContext * eps[31];
} XHCISlot;
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;
}
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;
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);
}
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;
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;
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) {
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 */
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));
/* 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);
}
}
XHCIState *xhci = ptr;
uint32_t ret;
- if (reg >= 0x400) {
- return xhci_port_read(xhci, reg - 0x400);
- }
-
switch (reg) {
case 0x00: /* USBCMD */
ret = xhci->usbcmd;
{
XHCIState *xhci = ptr;
- if (reg >= 0x400) {
- xhci_port_write(xhci, reg - 0x400, val);
- return;
- }
-
trace_usb_xhci_oper_write(reg, val);
switch (reg) {
.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,
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,
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) {
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);
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,
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);
{
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;
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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 */
{
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;
len += ret;
- virtqueue_push(vq, &elem, len);
+ virtqueue_push(vq, &elem, 0);
virtio_notify(&n->vdev, vq);
if (++num_packets >= n->tx_burst) {
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) ||
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)
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++)
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;
}
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);
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;
}
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);
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])) {
}
}
- 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;
}
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);
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) +
# 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;
* 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);
--- /dev/null
+/*
+ * 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 */
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;
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)
{
}
}
-int monitor_get_fd(Monitor *mon, const char *fdname)
+int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
{
mon_fd_t *monfd;
return fd;
}
+ error_setg(errp, "File descriptor named '%s' has not been found", fdname);
return -1;
}
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 {
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");
}
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)
'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap',
'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:
#
# @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',
# @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
# 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.
#
-# 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
+# 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
#
# @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
#
{ 'command': 'dump-guest-memory',
'data': { 'paging': 'bool', 'protocol': 'str', '*begin': 'int',
'*length': 'int' } }
+
##
# @netdev_add:
#
'props', 'undo', 'front', 'copy', 'open', 'paste', 'find', 'cut',
'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
#
##
{ 'command': 'send-key',
- 'data': { 'keys': ['QKeyCode'], '*hold-time': 'int' } }
+ 'data': { 'keys': ['KeyValue'], '*hold-time': 'int' } }
##
# @screendump:
{
.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
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);
+}
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
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))
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',
# 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
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"
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)
"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 */,
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 {
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);
}
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 | \
/* 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:
#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
*/
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,
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,
/* 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,
.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"
.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,
},
{
/* 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",
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)
#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, ",");
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")) {
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;
}
(*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)
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;
/* 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 {
/* 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 */
#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)
#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 */
#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)
#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)
#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)
#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" */
#define CPU_NB_REGS CPU_NB_REGS32
#endif
-#define NB_MMU_MODES 2
+#define NB_MMU_MODES 3
typedef enum TPRAccess {
TPR_ACCESS_READ,
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];
/* 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
*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);
#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;
}
/* 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) {
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) {
/* 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) {
}
/* 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) {
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 */
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)
* 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;
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);
}
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);
}
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;
}
/* 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;
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;
}
#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
#define TARGET_PRIuPHYS PRIu64
#define TARGET_PRIxPHYS PRIx64
#define TARGET_PRIXPHYS PRIX64
-#endif
-#endif
#endif