Compare commits
46 Commits
pull-usb-8
...
pull-input
Author | SHA1 | Date | |
---|---|---|---|
|
2082bac151 | ||
|
90525fe279 | ||
|
3503206a90 | ||
|
2deb4acc7c | ||
|
5a165668e7 | ||
|
2e377f1730 | ||
|
be1a717624 | ||
|
e00fcfeab3 | ||
|
278073ba29 | ||
|
82ea61c6da | ||
|
1673e89e93 | ||
|
3257fc8383 | ||
|
36f5db59cf | ||
|
b52991537c | ||
|
4006617552 | ||
|
cf864569cd | ||
|
f2335791fd | ||
|
363f59d9e4 | ||
|
bdef972474 | ||
|
50ef467923 | ||
|
f72b49398f | ||
|
55d492d760 | ||
|
5e70018b00 | ||
|
0688448b71 | ||
|
3df3e0a587 | ||
|
279a35ab4a | ||
|
7532d3cbf1 | ||
|
12e1129b80 | ||
|
078c44f48e | ||
|
4cb47d281a | ||
|
c13959c745 | ||
|
675036e4da | ||
|
bb9cd2ee99 | ||
|
2df5fee2db | ||
|
b122c3b6d0 | ||
|
6262bbd363 | ||
|
f25391c2a6 | ||
|
3cb0e25c4b | ||
|
6376f95223 | ||
|
543f7bef13 | ||
|
29f2601aa6 | ||
|
443422fde7 | ||
|
b20e61e0d5 | ||
|
a1904e48c4 | ||
|
75e347d66a | ||
|
ebee92b4fe |
1
block.c
1
block.c
@@ -1228,6 +1228,7 @@ int bdrv_open_image(BlockDriverState **pbs, const char *filename,
|
|||||||
bdref_key);
|
bdref_key);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
|
QDECREF(image_options);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -475,6 +475,7 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
|
|||||||
case QTYPE_QERROR: {
|
case QTYPE_QERROR: {
|
||||||
QString *value = qerror_human((QError *)obj);
|
QString *value = qerror_human((QError *)obj);
|
||||||
func_fprintf(f, "%s", qstring_get_str(value));
|
func_fprintf(f, "%s", qstring_get_str(value));
|
||||||
|
QDECREF(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QTYPE_NONE:
|
case QTYPE_NONE:
|
||||||
|
@@ -1308,6 +1308,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
|||||||
options = qdict_clone_shallow(bs->options);
|
options = qdict_clone_shallow(bs->options);
|
||||||
|
|
||||||
ret = qcow2_open(bs, options, flags, &local_err);
|
ret = qcow2_open(bs, options, flags, &local_err);
|
||||||
|
QDECREF(options);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_setg(errp, "Could not reopen qcow2 layer: %s",
|
error_setg(errp, "Could not reopen qcow2 layer: %s",
|
||||||
error_get_pretty(local_err));
|
error_get_pretty(local_err));
|
||||||
@@ -1318,8 +1319,6 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDECREF(options);
|
|
||||||
|
|
||||||
if (crypt_method) {
|
if (crypt_method) {
|
||||||
s->crypt_method = crypt_method;
|
s->crypt_method = crypt_method;
|
||||||
memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key));
|
memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key));
|
||||||
|
@@ -1192,7 +1192,7 @@ again:
|
|||||||
if (size == 0)
|
if (size == 0)
|
||||||
#endif
|
#endif
|
||||||
#if defined(__APPLE__) && defined(__MACH__)
|
#if defined(__APPLE__) && defined(__MACH__)
|
||||||
size = LONG_LONG_MAX;
|
size = LLONG_MAX;
|
||||||
#else
|
#else
|
||||||
size = lseek(fd, 0LL, SEEK_END);
|
size = lseek(fd, 0LL, SEEK_END);
|
||||||
#endif
|
#endif
|
||||||
|
@@ -2176,6 +2176,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||||||
strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag));
|
strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag));
|
||||||
/* we don't need to update entire object */
|
/* we don't need to update entire object */
|
||||||
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
|
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
|
||||||
|
inode = g_malloc(datalen);
|
||||||
|
|
||||||
/* refresh inode. */
|
/* refresh inode. */
|
||||||
fd = connect_to_sdog(s, &local_err);
|
fd = connect_to_sdog(s, &local_err);
|
||||||
@@ -2202,8 +2203,6 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
inode = (SheepdogInode *)g_malloc(datalen);
|
|
||||||
|
|
||||||
ret = read_object(fd, (char *)inode, vid_to_vdi_oid(new_vid),
|
ret = read_object(fd, (char *)inode, vid_to_vdi_oid(new_vid),
|
||||||
s->inode.nr_copies, datalen, 0, s->cache_flags);
|
s->inode.nr_copies, datalen, 0, s->cache_flags);
|
||||||
|
|
||||||
@@ -2217,6 +2216,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
|||||||
s->inode.name, s->inode.snap_id, s->inode.vdi_id);
|
s->inode.name, s->inode.snap_id, s->inode.vdi_id);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
g_free(inode);
|
||||||
closesocket(fd);
|
closesocket(fd);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@@ -1534,7 +1534,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
|
|||||||
int ret, i;
|
int ret, i;
|
||||||
BlockDriverState *bs = NULL;
|
BlockDriverState *bs = NULL;
|
||||||
VMDK4Header header;
|
VMDK4Header header;
|
||||||
Error *local_err;
|
Error *local_err = NULL;
|
||||||
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
|
uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
|
||||||
uint32_t *gd_buf = NULL;
|
uint32_t *gd_buf = NULL;
|
||||||
int gd_buf_size;
|
int gd_buf_size;
|
||||||
@@ -1700,7 +1700,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
{
|
{
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
BlockDriverState *new_bs = NULL;
|
BlockDriverState *new_bs = NULL;
|
||||||
Error *local_err;
|
Error *local_err = NULL;
|
||||||
char *desc = NULL;
|
char *desc = NULL;
|
||||||
int64_t total_size = 0, filesize;
|
int64_t total_size = 0, filesize;
|
||||||
const char *adapter_type = NULL;
|
const char *adapter_type = NULL;
|
||||||
@@ -1881,7 +1881,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
} else {
|
} else {
|
||||||
ret = bdrv_create_file(filename, options, &local_err);
|
ret = bdrv_create_file(filename, options, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not create image file");
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1889,7 +1889,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
|
|||||||
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
ret = bdrv_open(&new_bs, filename, NULL, NULL,
|
||||||
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_setg_errno(errp, -ret, "Could not write description");
|
error_propagate(errp, local_err);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len);
|
ret = bdrv_pwrite(new_bs, desc_offset, desc, desc_len);
|
||||||
|
@@ -787,7 +787,9 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
|
|||||||
s->current_mapping->path=buffer;
|
s->current_mapping->path=buffer;
|
||||||
s->current_mapping->read_only =
|
s->current_mapping->read_only =
|
||||||
(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
|
(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
|
||||||
}
|
} else {
|
||||||
|
g_free(buffer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
|
|
||||||
@@ -1866,7 +1868,7 @@ static int check_directory_consistency(BDRVVVFATState *s,
|
|||||||
|
|
||||||
if (s->used_clusters[cluster_num] & USED_ANY) {
|
if (s->used_clusters[cluster_num] & USED_ANY) {
|
||||||
fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
|
fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
|
||||||
return 0;
|
goto fail;
|
||||||
}
|
}
|
||||||
s->used_clusters[cluster_num] = USED_DIRECTORY;
|
s->used_clusters[cluster_num] = USED_DIRECTORY;
|
||||||
|
|
||||||
@@ -2929,6 +2931,7 @@ static int enable_write_target(BDRVVVFATState *s, Error **errp)
|
|||||||
set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
|
set_option_parameter(options, BLOCK_OPT_BACKING_FILE, "fat:");
|
||||||
|
|
||||||
ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, errp);
|
ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, errp);
|
||||||
|
free_option_parameters(options);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
@@ -351,7 +351,7 @@ static DriveInfo *blockdev_init(const char *file, QDict *bs_opts,
|
|||||||
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
|
opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);
|
||||||
if (error) {
|
if (error) {
|
||||||
error_propagate(errp, error);
|
error_propagate(errp, error);
|
||||||
return NULL;
|
goto err_no_opts;
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_opts_absorb_qdict(opts, bs_opts, &error);
|
qemu_opts_absorb_qdict(opts, bs_opts, &error);
|
||||||
@@ -564,8 +564,9 @@ bdrv_new_err:
|
|||||||
g_free(dinfo->id);
|
g_free(dinfo->id);
|
||||||
g_free(dinfo);
|
g_free(dinfo);
|
||||||
early_err:
|
early_err:
|
||||||
QDECREF(bs_opts);
|
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
|
err_no_opts:
|
||||||
|
QDECREF(bs_opts);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -939,6 +940,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
|
|
||||||
/* Actual block device init: Functionality shared with blockdev-add */
|
/* Actual block device init: Functionality shared with blockdev-add */
|
||||||
dinfo = blockdev_init(filename, bs_opts, &local_err);
|
dinfo = blockdev_init(filename, bs_opts, &local_err);
|
||||||
|
bs_opts = NULL;
|
||||||
if (dinfo == NULL) {
|
if (dinfo == NULL) {
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_report("%s", error_get_pretty(local_err));
|
error_report("%s", error_get_pretty(local_err));
|
||||||
@@ -976,6 +978,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
|
|
||||||
fail:
|
fail:
|
||||||
qemu_opts_del(legacy_opts);
|
qemu_opts_del(legacy_opts);
|
||||||
|
QDECREF(bs_opts);
|
||||||
return dinfo;
|
return dinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,16 +6,20 @@ host side
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
First you must compile qemu with a user interface supporting
|
First you must compile qemu with a user interface supporting
|
||||||
multihead/multiseat and input event routing. Right now this list is
|
multihead/multiseat and input event routing. Right now this
|
||||||
pretty short: sdl2.
|
list includes sdl2 and gtk (both 2+3):
|
||||||
|
|
||||||
./configure --enable-sdl --with-sdlabi=2.0
|
./configure --enable-sdl --with-sdlabi=2.0
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
./configure --enable-gtk
|
||||||
|
|
||||||
|
|
||||||
Next put together the qemu command line:
|
Next put together the qemu command line:
|
||||||
|
|
||||||
qemu -enable-kvm -usb $memory $disk $whatever \
|
qemu -enable-kvm -usb $memory $disk $whatever \
|
||||||
-display sdl \
|
-display [ sdl | gtk ] \
|
||||||
-vga std \
|
-vga std \
|
||||||
-device usb-tablet
|
-device usb-tablet
|
||||||
|
|
||||||
@@ -37,6 +41,20 @@ The "display=video2" sets up the input routing. Any input coming from
|
|||||||
the window which belongs to the video.2 display adapter will be routed
|
the window which belongs to the video.2 display adapter will be routed
|
||||||
to these input devices.
|
to these input devices.
|
||||||
|
|
||||||
|
The sdl2 ui will start up with two windows, one for each display
|
||||||
|
device. The gtk ui will start with a single window and each display
|
||||||
|
in a separate tab. You can either simply switch tabs to switch heads,
|
||||||
|
or use the "View / Detach tab" menu item to move one of the displays
|
||||||
|
to its own window so you can see both display devices side-by-side.
|
||||||
|
|
||||||
|
Note on spice: Spice handles multihead just fine. But it can't do
|
||||||
|
multiseat. For tablet events the event source is sent to the spice
|
||||||
|
agent. But qemu can't figure it, so it can't do input routing.
|
||||||
|
Fixing this needs a new or extended input interface between
|
||||||
|
libspice-server and qemu. For keyboard events it is even worse: The
|
||||||
|
event source isn't included in the spice protocol, so the wire
|
||||||
|
protocol must be extended to support this.
|
||||||
|
|
||||||
|
|
||||||
guest side
|
guest side
|
||||||
----------
|
----------
|
||||||
@@ -46,29 +64,37 @@ You need a pretty recent linux guest. systemd with loginctl. kernel
|
|||||||
fully updated for the new kernel though, i.e. the live iso doesn't cut
|
fully updated for the new kernel though, i.e. the live iso doesn't cut
|
||||||
it.
|
it.
|
||||||
|
|
||||||
Now we'll have to configure the guest. Boot and login. By default
|
Now we'll have to configure the guest. Boot and login. "lspci -vt"
|
||||||
all devices belong to seat0. You can use "loginctl seat-status seat0"
|
should list the pci bridge with the display adapter and usb controller:
|
||||||
to list them all (and to get the sysfs paths for cut+paste). Now
|
|
||||||
we'll go assign all pci devices connected the pci bridge in slot 12 to
|
|
||||||
a new head:
|
|
||||||
|
|
||||||
loginctl attach seat-qemu \
|
[root@fedora ~]# lspci -vt
|
||||||
/sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/drm/card1
|
-[0000:00]-+-00.0 Intel Corporation 440FX - 82441FX PMC [Natoma]
|
||||||
loginctl attach seat-qemu \
|
[ ... ]
|
||||||
/sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/graphics/fb1
|
\-12.0-[01]--+-02.0 Device 1234:1111
|
||||||
loginctl attach seat-qemu \
|
\-0f.0 NEC Corporation USB 3.0 Host Controller
|
||||||
/sys/devices/pci0000:00/0000:00:12.0/0000:01:0f.0/usb2
|
|
||||||
|
|
||||||
Use "loginctl seat-status seat-qemu" to check the result. It isn't
|
Good. Now lets tell the system that the pci bridge and all devices
|
||||||
needed to assign the usb devices to the head individually, assigning a
|
below it belong to a separate seat by dropping a file into
|
||||||
usb (root) hub will automatically assign all usb devices connected to
|
/etc/udev/rules.d:
|
||||||
it too.
|
|
||||||
|
|
||||||
BTW: loginctl writes udev rules to /etc/udev/rules.d to make these
|
[root@fedora ~]# cat /etc/udev/rules.d/70-qemu-autoseat.rules
|
||||||
device assignments permanent, so you need to do this only once.
|
SUBSYSTEMS=="pci", DEVPATH=="*/0000:00:12.0", TAG+="seat", ENV{ID_AUTOSEAT}="1"
|
||||||
|
|
||||||
Now simply restart gdm (rebooting will do too), and a login screen
|
Reboot. System should come up with two seats. With loginctl you can
|
||||||
should show up on the second head.
|
check the configuration:
|
||||||
|
|
||||||
|
[root@fedora ~]# loginctl list-seats
|
||||||
|
SEAT
|
||||||
|
seat0
|
||||||
|
seat-pci-pci-0000_00_12_0
|
||||||
|
|
||||||
|
2 seats listed.
|
||||||
|
|
||||||
|
You can use "loginctl seat-status seat-pci-pci-0000_00_12_0" to list
|
||||||
|
the devices attached to the seat.
|
||||||
|
|
||||||
|
Background info is here:
|
||||||
|
http://www.freedesktop.org/wiki/Software/systemd/multiseat/
|
||||||
|
|
||||||
Enjoy!
|
Enjoy!
|
||||||
|
|
||||||
|
428
hw/misc/vfio.c
428
hw/misc/vfio.c
@@ -133,6 +133,15 @@ enum {
|
|||||||
VFIO_INT_MSIX = 3,
|
VFIO_INT_MSIX = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct VFIOAddressSpace {
|
||||||
|
AddressSpace *as;
|
||||||
|
QLIST_HEAD(, VFIOContainer) containers;
|
||||||
|
QLIST_ENTRY(VFIOAddressSpace) list;
|
||||||
|
} VFIOAddressSpace;
|
||||||
|
|
||||||
|
static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces =
|
||||||
|
QLIST_HEAD_INITIALIZER(vfio_address_spaces);
|
||||||
|
|
||||||
struct VFIOGroup;
|
struct VFIOGroup;
|
||||||
|
|
||||||
typedef struct VFIOType1 {
|
typedef struct VFIOType1 {
|
||||||
@@ -142,6 +151,7 @@ typedef struct VFIOType1 {
|
|||||||
} VFIOType1;
|
} VFIOType1;
|
||||||
|
|
||||||
typedef struct VFIOContainer {
|
typedef struct VFIOContainer {
|
||||||
|
VFIOAddressSpace *space;
|
||||||
int fd; /* /dev/vfio/vfio, empowered by the attached groups */
|
int fd; /* /dev/vfio/vfio, empowered by the attached groups */
|
||||||
struct {
|
struct {
|
||||||
/* enable abstraction to support various iommu backends */
|
/* enable abstraction to support various iommu backends */
|
||||||
@@ -150,10 +160,18 @@ typedef struct VFIOContainer {
|
|||||||
};
|
};
|
||||||
void (*release)(struct VFIOContainer *);
|
void (*release)(struct VFIOContainer *);
|
||||||
} iommu_data;
|
} iommu_data;
|
||||||
|
QLIST_HEAD(, VFIOGuestIOMMU) giommu_list;
|
||||||
QLIST_HEAD(, VFIOGroup) group_list;
|
QLIST_HEAD(, VFIOGroup) group_list;
|
||||||
QLIST_ENTRY(VFIOContainer) next;
|
QLIST_ENTRY(VFIOContainer) next;
|
||||||
} VFIOContainer;
|
} VFIOContainer;
|
||||||
|
|
||||||
|
typedef struct VFIOGuestIOMMU {
|
||||||
|
VFIOContainer *container;
|
||||||
|
MemoryRegion *iommu;
|
||||||
|
Notifier n;
|
||||||
|
QLIST_ENTRY(VFIOGuestIOMMU) giommu_next;
|
||||||
|
} VFIOGuestIOMMU;
|
||||||
|
|
||||||
/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */
|
/* Cache of MSI-X setup plus extra mmap and memory region for split BAR map */
|
||||||
typedef struct VFIOMSIXInfo {
|
typedef struct VFIOMSIXInfo {
|
||||||
uint8_t table_bar;
|
uint8_t table_bar;
|
||||||
@@ -234,9 +252,6 @@ static const VFIORomBlacklistEntry romblacklist[] = {
|
|||||||
|
|
||||||
#define MSIX_CAP_LENGTH 12
|
#define MSIX_CAP_LENGTH 12
|
||||||
|
|
||||||
static QLIST_HEAD(, VFIOContainer)
|
|
||||||
container_list = QLIST_HEAD_INITIALIZER(container_list);
|
|
||||||
|
|
||||||
static QLIST_HEAD(, VFIOGroup)
|
static QLIST_HEAD(, VFIOGroup)
|
||||||
group_list = QLIST_HEAD_INITIALIZER(group_list);
|
group_list = QLIST_HEAD_INITIALIZER(group_list);
|
||||||
|
|
||||||
@@ -1668,6 +1683,149 @@ static void vfio_probe_ati_bar4_window_quirk(VFIODevice *vdev, int nr)
|
|||||||
vdev->host.function);
|
vdev->host.function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define PCI_VENDOR_ID_REALTEK 0x10ec
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RTL8168 devices have a backdoor that can access the MSI-X table. At BAR2
|
||||||
|
* offset 0x70 there is a dword data register, offset 0x74 is a dword address
|
||||||
|
* register. According to the Linux r8169 driver, the MSI-X table is addressed
|
||||||
|
* when the "type" portion of the address register is set to 0x1. This appears
|
||||||
|
* to be bits 16:30. Bit 31 is both a write indicator and some sort of
|
||||||
|
* "address latched" indicator. Bits 12:15 are a mask field, which we can
|
||||||
|
* ignore because the MSI-X table should always be accessed as a dword (full
|
||||||
|
* mask). Bits 0:11 is offset within the type.
|
||||||
|
*
|
||||||
|
* Example trace:
|
||||||
|
*
|
||||||
|
* Read from MSI-X table offset 0
|
||||||
|
* vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x1f000, 4) // store read addr
|
||||||
|
* vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x8001f000 // latch
|
||||||
|
* vfio: vfio_bar_read(0000:05:00.0:BAR2+0x70, 4) = 0xfee00398 // read data
|
||||||
|
*
|
||||||
|
* Write 0xfee00000 to MSI-X table offset 0
|
||||||
|
* vfio: vfio_bar_write(0000:05:00.0:BAR2+0x70, 0xfee00000, 4) // write data
|
||||||
|
* vfio: vfio_bar_write(0000:05:00.0:BAR2+0x74, 0x8001f000, 4) // do write
|
||||||
|
* vfio: vfio_bar_read(0000:05:00.0:BAR2+0x74, 4) = 0x1f000 // complete
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint64_t vfio_rtl8168_window_quirk_read(void *opaque,
|
||||||
|
hwaddr addr, unsigned size)
|
||||||
|
{
|
||||||
|
VFIOQuirk *quirk = opaque;
|
||||||
|
VFIODevice *vdev = quirk->vdev;
|
||||||
|
|
||||||
|
switch (addr) {
|
||||||
|
case 4: /* address */
|
||||||
|
if (quirk->data.flags) {
|
||||||
|
DPRINTF("%s fake read(%04x:%02x:%02x.%d)\n",
|
||||||
|
memory_region_name(&quirk->mem), vdev->host.domain,
|
||||||
|
vdev->host.bus, vdev->host.slot, vdev->host.function);
|
||||||
|
|
||||||
|
return quirk->data.address_match ^ 0x10000000U;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0: /* data */
|
||||||
|
if (quirk->data.flags) {
|
||||||
|
uint64_t val;
|
||||||
|
|
||||||
|
DPRINTF("%s MSI-X table read(%04x:%02x:%02x.%d)\n",
|
||||||
|
memory_region_name(&quirk->mem), vdev->host.domain,
|
||||||
|
vdev->host.bus, vdev->host.slot, vdev->host.function);
|
||||||
|
|
||||||
|
if (!(vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_mem_read(&vdev->pdev.msix_table_mmio,
|
||||||
|
(hwaddr)(quirk->data.address_match & 0xfff),
|
||||||
|
&val, size);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF("%s direct read(%04x:%02x:%02x.%d)\n",
|
||||||
|
memory_region_name(&quirk->mem), vdev->host.domain,
|
||||||
|
vdev->host.bus, vdev->host.slot, vdev->host.function);
|
||||||
|
|
||||||
|
return vfio_bar_read(&vdev->bars[quirk->data.bar], addr + 0x70, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vfio_rtl8168_window_quirk_write(void *opaque, hwaddr addr,
|
||||||
|
uint64_t data, unsigned size)
|
||||||
|
{
|
||||||
|
VFIOQuirk *quirk = opaque;
|
||||||
|
VFIODevice *vdev = quirk->vdev;
|
||||||
|
|
||||||
|
switch (addr) {
|
||||||
|
case 4: /* address */
|
||||||
|
if ((data & 0x7fff0000) == 0x10000) {
|
||||||
|
if (data & 0x10000000U &&
|
||||||
|
vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) {
|
||||||
|
|
||||||
|
DPRINTF("%s MSI-X table write(%04x:%02x:%02x.%d)\n",
|
||||||
|
memory_region_name(&quirk->mem), vdev->host.domain,
|
||||||
|
vdev->host.bus, vdev->host.slot, vdev->host.function);
|
||||||
|
|
||||||
|
io_mem_write(&vdev->pdev.msix_table_mmio,
|
||||||
|
(hwaddr)(quirk->data.address_match & 0xfff),
|
||||||
|
data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
quirk->data.flags = 1;
|
||||||
|
quirk->data.address_match = data;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
quirk->data.flags = 0;
|
||||||
|
break;
|
||||||
|
case 0: /* data */
|
||||||
|
quirk->data.address_mask = data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF("%s direct write(%04x:%02x:%02x.%d)\n",
|
||||||
|
memory_region_name(&quirk->mem), vdev->host.domain,
|
||||||
|
vdev->host.bus, vdev->host.slot, vdev->host.function);
|
||||||
|
|
||||||
|
vfio_bar_write(&vdev->bars[quirk->data.bar], addr + 0x70, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps vfio_rtl8168_window_quirk = {
|
||||||
|
.read = vfio_rtl8168_window_quirk_read,
|
||||||
|
.write = vfio_rtl8168_window_quirk_write,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 4,
|
||||||
|
.unaligned = false,
|
||||||
|
},
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void vfio_probe_rtl8168_bar2_window_quirk(VFIODevice *vdev, int nr)
|
||||||
|
{
|
||||||
|
PCIDevice *pdev = &vdev->pdev;
|
||||||
|
VFIOQuirk *quirk;
|
||||||
|
|
||||||
|
if (pci_get_word(pdev->config + PCI_VENDOR_ID) != PCI_VENDOR_ID_REALTEK ||
|
||||||
|
pci_get_word(pdev->config + PCI_DEVICE_ID) != 0x8168 || nr != 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
quirk = g_malloc0(sizeof(*quirk));
|
||||||
|
quirk->vdev = vdev;
|
||||||
|
quirk->data.bar = nr;
|
||||||
|
|
||||||
|
memory_region_init_io(&quirk->mem, OBJECT(vdev), &vfio_rtl8168_window_quirk,
|
||||||
|
quirk, "vfio-rtl8168-window-quirk", 8);
|
||||||
|
memory_region_add_subregion_overlap(&vdev->bars[nr].mem,
|
||||||
|
0x70, &quirk->mem, 1);
|
||||||
|
|
||||||
|
QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
|
||||||
|
|
||||||
|
DPRINTF("Enabled RTL8168 BAR2 window quirk for device %04x:%02x:%02x.%x\n",
|
||||||
|
vdev->host.domain, vdev->host.bus, vdev->host.slot,
|
||||||
|
vdev->host.function);
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Trap the BAR2 MMIO window to config space as well.
|
* Trap the BAR2 MMIO window to config space as well.
|
||||||
*/
|
*/
|
||||||
@@ -2071,6 +2229,7 @@ static void vfio_bar_quirk_setup(VFIODevice *vdev, int nr)
|
|||||||
vfio_probe_nvidia_bar5_window_quirk(vdev, nr);
|
vfio_probe_nvidia_bar5_window_quirk(vdev, nr);
|
||||||
vfio_probe_nvidia_bar0_88000_quirk(vdev, nr);
|
vfio_probe_nvidia_bar0_88000_quirk(vdev, nr);
|
||||||
vfio_probe_nvidia_bar0_1800_quirk(vdev, nr);
|
vfio_probe_nvidia_bar0_1800_quirk(vdev, nr);
|
||||||
|
vfio_probe_rtl8168_bar2_window_quirk(vdev, nr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_bar_quirk_teardown(VFIODevice *vdev, int nr)
|
static void vfio_bar_quirk_teardown(VFIODevice *vdev, int nr)
|
||||||
@@ -2232,7 +2391,8 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova,
|
|||||||
|
|
||||||
static bool vfio_listener_skipped_section(MemoryRegionSection *section)
|
static bool vfio_listener_skipped_section(MemoryRegionSection *section)
|
||||||
{
|
{
|
||||||
return !memory_region_is_ram(section->mr) ||
|
return (!memory_region_is_ram(section->mr) &&
|
||||||
|
!memory_region_is_iommu(section->mr)) ||
|
||||||
/*
|
/*
|
||||||
* Sizing an enabled 64-bit BAR can cause spurious mappings to
|
* Sizing an enabled 64-bit BAR can cause spurious mappings to
|
||||||
* addresses in the upper part of the 64-bit address space. These
|
* addresses in the upper part of the 64-bit address space. These
|
||||||
@@ -2242,17 +2402,75 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section)
|
|||||||
section->offset_within_address_space & (1ULL << 63);
|
section->offset_within_address_space & (1ULL << 63);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void vfio_iommu_map_notify(Notifier *n, void *data)
|
||||||
|
{
|
||||||
|
VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n);
|
||||||
|
VFIOContainer *container = giommu->container;
|
||||||
|
IOMMUTLBEntry *iotlb = data;
|
||||||
|
MemoryRegion *mr;
|
||||||
|
hwaddr xlat;
|
||||||
|
hwaddr len = iotlb->addr_mask + 1;
|
||||||
|
void *vaddr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
DPRINTF("iommu map @ %"HWADDR_PRIx" - %"HWADDR_PRIx"\n",
|
||||||
|
iotlb->iova, iotlb->iova + iotlb->addr_mask);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The IOMMU TLB entry we have just covers translation through
|
||||||
|
* this IOMMU to its immediate target. We need to translate
|
||||||
|
* it the rest of the way through to memory.
|
||||||
|
*/
|
||||||
|
mr = address_space_translate(&address_space_memory,
|
||||||
|
iotlb->translated_addr,
|
||||||
|
&xlat, &len, iotlb->perm & IOMMU_WO);
|
||||||
|
if (!memory_region_is_ram(mr)) {
|
||||||
|
DPRINTF("iommu map to non memory area %"HWADDR_PRIx"\n",
|
||||||
|
xlat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Translation truncates length to the IOMMU page size,
|
||||||
|
* check that it did not truncate too much.
|
||||||
|
*/
|
||||||
|
if (len & iotlb->addr_mask) {
|
||||||
|
DPRINTF("iommu has granularity incompatible with target AS\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iotlb->perm != IOMMU_NONE) {
|
||||||
|
vaddr = memory_region_get_ram_ptr(mr) + xlat;
|
||||||
|
|
||||||
|
ret = vfio_dma_map(container, iotlb->iova,
|
||||||
|
iotlb->addr_mask + 1, vaddr,
|
||||||
|
!(iotlb->perm & IOMMU_WO) || mr->readonly);
|
||||||
|
if (ret) {
|
||||||
|
error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
|
||||||
|
"0x%"HWADDR_PRIx", %p) = %d (%m)",
|
||||||
|
container, iotlb->iova,
|
||||||
|
iotlb->addr_mask + 1, vaddr, ret);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = vfio_dma_unmap(container, iotlb->iova, iotlb->addr_mask + 1);
|
||||||
|
if (ret) {
|
||||||
|
error_report("vfio_dma_unmap(%p, 0x%"HWADDR_PRIx", "
|
||||||
|
"0x%"HWADDR_PRIx") = %d (%m)",
|
||||||
|
container, iotlb->iova,
|
||||||
|
iotlb->addr_mask + 1, ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void vfio_listener_region_add(MemoryListener *listener,
|
static void vfio_listener_region_add(MemoryListener *listener,
|
||||||
MemoryRegionSection *section)
|
MemoryRegionSection *section)
|
||||||
{
|
{
|
||||||
VFIOContainer *container = container_of(listener, VFIOContainer,
|
VFIOContainer *container = container_of(listener, VFIOContainer,
|
||||||
iommu_data.type1.listener);
|
iommu_data.type1.listener);
|
||||||
hwaddr iova, end;
|
hwaddr iova, end;
|
||||||
|
Int128 llend;
|
||||||
void *vaddr;
|
void *vaddr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
assert(!memory_region_is_iommu(section->mr));
|
|
||||||
|
|
||||||
if (vfio_listener_skipped_section(section)) {
|
if (vfio_listener_skipped_section(section)) {
|
||||||
DPRINTF("SKIPPING region_add %"HWADDR_PRIx" - %"PRIx64"\n",
|
DPRINTF("SKIPPING region_add %"HWADDR_PRIx" - %"PRIx64"\n",
|
||||||
section->offset_within_address_space,
|
section->offset_within_address_space,
|
||||||
@@ -2268,21 +2486,65 @@ static void vfio_listener_region_add(MemoryListener *listener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
|
iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
|
||||||
end = (section->offset_within_address_space + int128_get64(section->size)) &
|
llend = int128_make64(section->offset_within_address_space);
|
||||||
TARGET_PAGE_MASK;
|
llend = int128_add(llend, section->size);
|
||||||
|
llend = int128_and(llend, int128_exts64(TARGET_PAGE_MASK));
|
||||||
|
|
||||||
if (iova >= end) {
|
if (int128_ge(int128_make64(iova), llend)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memory_region_ref(section->mr);
|
||||||
|
|
||||||
|
if (memory_region_is_iommu(section->mr)) {
|
||||||
|
VFIOGuestIOMMU *giommu;
|
||||||
|
|
||||||
|
DPRINTF("region_add [iommu] %"HWADDR_PRIx" - %"HWADDR_PRIx"\n",
|
||||||
|
iova, int128_get64(int128_sub(llend, int128_one())));
|
||||||
|
/*
|
||||||
|
* FIXME: We should do some checking to see if the
|
||||||
|
* capabilities of the host VFIO IOMMU are adequate to model
|
||||||
|
* the guest IOMMU
|
||||||
|
*
|
||||||
|
* FIXME: For VFIO iommu types which have KVM acceleration to
|
||||||
|
* avoid bouncing all map/unmaps through qemu this way, this
|
||||||
|
* would be the right place to wire that up (tell the KVM
|
||||||
|
* device emulation the VFIO iommu handles to use).
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* This assumes that the guest IOMMU is empty of
|
||||||
|
* mappings at this point.
|
||||||
|
*
|
||||||
|
* One way of doing this is:
|
||||||
|
* 1. Avoid sharing IOMMUs between emulated devices or different
|
||||||
|
* IOMMU groups.
|
||||||
|
* 2. Implement VFIO_IOMMU_ENABLE in the host kernel to fail if
|
||||||
|
* there are some mappings in IOMMU.
|
||||||
|
*
|
||||||
|
* VFIO on SPAPR does that. Other IOMMU models may do that different,
|
||||||
|
* they must make sure there are no existing mappings or
|
||||||
|
* loop through existing mappings to map them into VFIO.
|
||||||
|
*/
|
||||||
|
giommu = g_malloc0(sizeof(*giommu));
|
||||||
|
giommu->iommu = section->mr;
|
||||||
|
giommu->container = container;
|
||||||
|
giommu->n.notify = vfio_iommu_map_notify;
|
||||||
|
QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next);
|
||||||
|
memory_region_register_iommu_notifier(giommu->iommu, &giommu->n);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Here we assume that memory_region_is_ram(section->mr)==true */
|
||||||
|
|
||||||
|
end = int128_get64(llend);
|
||||||
vaddr = memory_region_get_ram_ptr(section->mr) +
|
vaddr = memory_region_get_ram_ptr(section->mr) +
|
||||||
section->offset_within_region +
|
section->offset_within_region +
|
||||||
(iova - section->offset_within_address_space);
|
(iova - section->offset_within_address_space);
|
||||||
|
|
||||||
DPRINTF("region_add %"HWADDR_PRIx" - %"HWADDR_PRIx" [%p]\n",
|
DPRINTF("region_add [ram] %"HWADDR_PRIx" - %"HWADDR_PRIx" [%p]\n",
|
||||||
iova, end - 1, vaddr);
|
iova, end - 1, vaddr);
|
||||||
|
|
||||||
memory_region_ref(section->mr);
|
|
||||||
ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly);
|
ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
|
error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
|
||||||
@@ -2326,6 +2588,27 @@ static void vfio_listener_region_del(MemoryListener *listener,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (memory_region_is_iommu(section->mr)) {
|
||||||
|
VFIOGuestIOMMU *giommu;
|
||||||
|
|
||||||
|
QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) {
|
||||||
|
if (giommu->iommu == section->mr) {
|
||||||
|
memory_region_unregister_iommu_notifier(&giommu->n);
|
||||||
|
QLIST_REMOVE(giommu, giommu_next);
|
||||||
|
g_free(giommu);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: We assume the one big unmap below is adequate to
|
||||||
|
* remove any individual page mappings in the IOMMU which
|
||||||
|
* might have been copied into VFIO. This works for a page table
|
||||||
|
* based IOMMU where a big unmap flattens a large range of IO-PTEs.
|
||||||
|
* That may not be true for all IOMMU types.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
|
iova = TARGET_PAGE_ALIGN(section->offset_within_address_space);
|
||||||
end = (section->offset_within_address_space + int128_get64(section->size)) &
|
end = (section->offset_within_address_space + int128_get64(section->size)) &
|
||||||
TARGET_PAGE_MASK;
|
TARGET_PAGE_MASK;
|
||||||
@@ -3274,16 +3557,43 @@ static void vfio_kvm_device_del_group(VFIOGroup *group)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_connect_container(VFIOGroup *group)
|
static VFIOAddressSpace *vfio_get_address_space(AddressSpace *as)
|
||||||
|
{
|
||||||
|
VFIOAddressSpace *space;
|
||||||
|
|
||||||
|
QLIST_FOREACH(space, &vfio_address_spaces, list) {
|
||||||
|
if (space->as == as) {
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No suitable VFIOAddressSpace, create a new one */
|
||||||
|
space = g_malloc0(sizeof(*space));
|
||||||
|
space->as = as;
|
||||||
|
QLIST_INIT(&space->containers);
|
||||||
|
|
||||||
|
QLIST_INSERT_HEAD(&vfio_address_spaces, space, list);
|
||||||
|
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vfio_put_address_space(VFIOAddressSpace *space)
|
||||||
|
{
|
||||||
|
if (QLIST_EMPTY(&space->containers)) {
|
||||||
|
QLIST_REMOVE(space, list);
|
||||||
|
g_free(space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vfio_connect_container(VFIOGroup *group, AddressSpace *as)
|
||||||
{
|
{
|
||||||
VFIOContainer *container;
|
VFIOContainer *container;
|
||||||
int ret, fd;
|
int ret, fd;
|
||||||
|
VFIOAddressSpace *space;
|
||||||
|
|
||||||
if (group->container) {
|
space = vfio_get_address_space(as);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QLIST_FOREACH(container, &container_list, next) {
|
QLIST_FOREACH(container, &space->containers, next) {
|
||||||
if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
|
if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
|
||||||
group->container = container;
|
group->container = container;
|
||||||
QLIST_INSERT_HEAD(&container->group_list, group, container_next);
|
QLIST_INSERT_HEAD(&container->group_list, group, container_next);
|
||||||
@@ -3294,35 +3604,35 @@ static int vfio_connect_container(VFIOGroup *group)
|
|||||||
fd = qemu_open("/dev/vfio/vfio", O_RDWR);
|
fd = qemu_open("/dev/vfio/vfio", O_RDWR);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
error_report("vfio: failed to open /dev/vfio/vfio: %m");
|
error_report("vfio: failed to open /dev/vfio/vfio: %m");
|
||||||
return -errno;
|
ret = -errno;
|
||||||
|
goto put_space_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ioctl(fd, VFIO_GET_API_VERSION);
|
ret = ioctl(fd, VFIO_GET_API_VERSION);
|
||||||
if (ret != VFIO_API_VERSION) {
|
if (ret != VFIO_API_VERSION) {
|
||||||
error_report("vfio: supported vfio version: %d, "
|
error_report("vfio: supported vfio version: %d, "
|
||||||
"reported version: %d", VFIO_API_VERSION, ret);
|
"reported version: %d", VFIO_API_VERSION, ret);
|
||||||
close(fd);
|
ret = -EINVAL;
|
||||||
return -EINVAL;
|
goto close_fd_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = g_malloc0(sizeof(*container));
|
container = g_malloc0(sizeof(*container));
|
||||||
|
container->space = space;
|
||||||
container->fd = fd;
|
container->fd = fd;
|
||||||
|
|
||||||
if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) {
|
if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) {
|
||||||
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
|
ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to set group container: %m");
|
error_report("vfio: failed to set group container: %m");
|
||||||
g_free(container);
|
ret = -errno;
|
||||||
close(fd);
|
goto free_container_exit;
|
||||||
return -errno;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
|
ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
error_report("vfio: failed to set iommu for container: %m");
|
error_report("vfio: failed to set iommu for container: %m");
|
||||||
g_free(container);
|
ret = -errno;
|
||||||
close(fd);
|
goto free_container_exit;
|
||||||
return -errno;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
container->iommu_data.type1.listener = vfio_memory_listener;
|
container->iommu_data.type1.listener = vfio_memory_listener;
|
||||||
@@ -3333,29 +3643,39 @@ static int vfio_connect_container(VFIOGroup *group)
|
|||||||
|
|
||||||
if (container->iommu_data.type1.error) {
|
if (container->iommu_data.type1.error) {
|
||||||
ret = container->iommu_data.type1.error;
|
ret = container->iommu_data.type1.error;
|
||||||
vfio_listener_release(container);
|
|
||||||
g_free(container);
|
|
||||||
close(fd);
|
|
||||||
error_report("vfio: memory listener initialization failed for container");
|
error_report("vfio: memory listener initialization failed for container");
|
||||||
return ret;
|
goto listener_release_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
container->iommu_data.type1.initialized = true;
|
container->iommu_data.type1.initialized = true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
error_report("vfio: No available IOMMU models");
|
error_report("vfio: No available IOMMU models");
|
||||||
g_free(container);
|
ret = -EINVAL;
|
||||||
close(fd);
|
goto free_container_exit;
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QLIST_INIT(&container->group_list);
|
QLIST_INIT(&container->group_list);
|
||||||
QLIST_INSERT_HEAD(&container_list, container, next);
|
QLIST_INSERT_HEAD(&space->containers, container, next);
|
||||||
|
|
||||||
group->container = container;
|
group->container = container;
|
||||||
QLIST_INSERT_HEAD(&container->group_list, group, container_next);
|
QLIST_INSERT_HEAD(&container->group_list, group, container_next);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
listener_release_exit:
|
||||||
|
vfio_listener_release(container);
|
||||||
|
|
||||||
|
free_container_exit:
|
||||||
|
g_free(container);
|
||||||
|
|
||||||
|
close_fd_exit:
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
put_space_exit:
|
||||||
|
vfio_put_address_space(space);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_disconnect_container(VFIOGroup *group)
|
static void vfio_disconnect_container(VFIOGroup *group)
|
||||||
@@ -3371,6 +3691,8 @@ static void vfio_disconnect_container(VFIOGroup *group)
|
|||||||
group->container = NULL;
|
group->container = NULL;
|
||||||
|
|
||||||
if (QLIST_EMPTY(&container->group_list)) {
|
if (QLIST_EMPTY(&container->group_list)) {
|
||||||
|
VFIOAddressSpace *space = container->space;
|
||||||
|
|
||||||
if (container->iommu_data.release) {
|
if (container->iommu_data.release) {
|
||||||
container->iommu_data.release(container);
|
container->iommu_data.release(container);
|
||||||
}
|
}
|
||||||
@@ -3378,10 +3700,12 @@ static void vfio_disconnect_container(VFIOGroup *group)
|
|||||||
DPRINTF("vfio_disconnect_container: close container->fd\n");
|
DPRINTF("vfio_disconnect_container: close container->fd\n");
|
||||||
close(container->fd);
|
close(container->fd);
|
||||||
g_free(container);
|
g_free(container);
|
||||||
|
|
||||||
|
vfio_put_address_space(space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static VFIOGroup *vfio_get_group(int groupid)
|
static VFIOGroup *vfio_get_group(int groupid, AddressSpace *as)
|
||||||
{
|
{
|
||||||
VFIOGroup *group;
|
VFIOGroup *group;
|
||||||
char path[32];
|
char path[32];
|
||||||
@@ -3389,7 +3713,14 @@ static VFIOGroup *vfio_get_group(int groupid)
|
|||||||
|
|
||||||
QLIST_FOREACH(group, &group_list, next) {
|
QLIST_FOREACH(group, &group_list, next) {
|
||||||
if (group->groupid == groupid) {
|
if (group->groupid == groupid) {
|
||||||
return group;
|
/* Found it. Now is it already in the right context? */
|
||||||
|
if (group->container->space->as == as) {
|
||||||
|
return group;
|
||||||
|
} else {
|
||||||
|
error_report("vfio: group %d used in multiple address spaces",
|
||||||
|
group->groupid);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3399,34 +3730,27 @@ static VFIOGroup *vfio_get_group(int groupid)
|
|||||||
group->fd = qemu_open(path, O_RDWR);
|
group->fd = qemu_open(path, O_RDWR);
|
||||||
if (group->fd < 0) {
|
if (group->fd < 0) {
|
||||||
error_report("vfio: error opening %s: %m", path);
|
error_report("vfio: error opening %s: %m", path);
|
||||||
g_free(group);
|
goto free_group_exit;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) {
|
if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) {
|
||||||
error_report("vfio: error getting group status: %m");
|
error_report("vfio: error getting group status: %m");
|
||||||
close(group->fd);
|
goto close_fd_exit;
|
||||||
g_free(group);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
|
if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
|
||||||
error_report("vfio: error, group %d is not viable, please ensure "
|
error_report("vfio: error, group %d is not viable, please ensure "
|
||||||
"all devices within the iommu_group are bound to their "
|
"all devices within the iommu_group are bound to their "
|
||||||
"vfio bus driver.", groupid);
|
"vfio bus driver.", groupid);
|
||||||
close(group->fd);
|
goto close_fd_exit;
|
||||||
g_free(group);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
group->groupid = groupid;
|
group->groupid = groupid;
|
||||||
QLIST_INIT(&group->device_list);
|
QLIST_INIT(&group->device_list);
|
||||||
|
|
||||||
if (vfio_connect_container(group)) {
|
if (vfio_connect_container(group, as)) {
|
||||||
error_report("vfio: failed to setup container for group %d", groupid);
|
error_report("vfio: failed to setup container for group %d", groupid);
|
||||||
close(group->fd);
|
goto close_fd_exit;
|
||||||
g_free(group);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (QLIST_EMPTY(&group_list)) {
|
if (QLIST_EMPTY(&group_list)) {
|
||||||
@@ -3438,6 +3762,14 @@ static VFIOGroup *vfio_get_group(int groupid)
|
|||||||
vfio_kvm_device_add_group(group);
|
vfio_kvm_device_add_group(group);
|
||||||
|
|
||||||
return group;
|
return group;
|
||||||
|
|
||||||
|
close_fd_exit:
|
||||||
|
close(group->fd);
|
||||||
|
|
||||||
|
free_group_exit:
|
||||||
|
g_free(group);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_put_group(VFIOGroup *group)
|
static void vfio_put_group(VFIOGroup *group)
|
||||||
@@ -3768,7 +4100,7 @@ static int vfio_initfn(PCIDevice *pdev)
|
|||||||
DPRINTF("%s(%04x:%02x:%02x.%x) group %d\n", __func__, vdev->host.domain,
|
DPRINTF("%s(%04x:%02x:%02x.%x) group %d\n", __func__, vdev->host.domain,
|
||||||
vdev->host.bus, vdev->host.slot, vdev->host.function, groupid);
|
vdev->host.bus, vdev->host.slot, vdev->host.function, groupid);
|
||||||
|
|
||||||
group = vfio_get_group(groupid);
|
group = vfio_get_group(groupid, pci_device_iommu_address_space(pdev));
|
||||||
if (!group) {
|
if (!group) {
|
||||||
error_report("vfio: failed to get group %d", groupid);
|
error_report("vfio: failed to get group %d", groupid);
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
@@ -38,6 +38,11 @@ static inline Int128 int128_2_64(void)
|
|||||||
return (Int128) { 0, 1 };
|
return (Int128) { 0, 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline Int128 int128_exts64(int64_t a)
|
||||||
|
{
|
||||||
|
return (Int128) { .lo = a, .hi = (a < 0) ? -1 : 0 };
|
||||||
|
}
|
||||||
|
|
||||||
static inline Int128 int128_and(Int128 a, Int128 b)
|
static inline Int128 int128_and(Int128 a, Int128 b)
|
||||||
{
|
{
|
||||||
return (Int128) { a.lo & b.lo, a.hi & b.hi };
|
return (Int128) { a.lo & b.lo, a.hi & b.hi };
|
||||||
|
@@ -82,6 +82,8 @@ void do_mouse_set(Monitor *mon, const QDict *qdict);
|
|||||||
#define QEMU_KEY_CTRL_PAGEDOWN 0xe407
|
#define QEMU_KEY_CTRL_PAGEDOWN 0xe407
|
||||||
|
|
||||||
void kbd_put_keysym_console(QemuConsole *s, int keysym);
|
void kbd_put_keysym_console(QemuConsole *s, int keysym);
|
||||||
|
bool kbd_put_qcode_console(QemuConsole *s, int qcode);
|
||||||
|
void kbd_put_string_console(QemuConsole *s, const char *str, int len);
|
||||||
void kbd_put_keysym(int keysym);
|
void kbd_put_keysym(int keysym);
|
||||||
|
|
||||||
/* consoles */
|
/* consoles */
|
||||||
|
@@ -39,6 +39,7 @@ InputEvent *qemu_input_event_new_key(KeyValue *key, bool down);
|
|||||||
void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down);
|
void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down);
|
||||||
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down);
|
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down);
|
||||||
void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down);
|
void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down);
|
||||||
|
void qemu_input_event_send_key_delay(uint32_t delay_ms);
|
||||||
int qemu_input_key_number_to_qcode(uint8_t nr);
|
int qemu_input_key_number_to_qcode(uint8_t nr);
|
||||||
int qemu_input_key_value_to_number(const KeyValue *value);
|
int qemu_input_key_value_to_number(const KeyValue *value);
|
||||||
int qemu_input_key_value_to_qcode(const KeyValue *value);
|
int qemu_input_key_value_to_qcode(const KeyValue *value);
|
||||||
|
7
memory.c
7
memory.c
@@ -1722,12 +1722,19 @@ void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name)
|
|||||||
|
|
||||||
void address_space_destroy(AddressSpace *as)
|
void address_space_destroy(AddressSpace *as)
|
||||||
{
|
{
|
||||||
|
MemoryListener *listener;
|
||||||
|
|
||||||
/* Flush out anything from MemoryListeners listening in on this */
|
/* Flush out anything from MemoryListeners listening in on this */
|
||||||
memory_region_transaction_begin();
|
memory_region_transaction_begin();
|
||||||
as->root = NULL;
|
as->root = NULL;
|
||||||
memory_region_transaction_commit();
|
memory_region_transaction_commit();
|
||||||
QTAILQ_REMOVE(&address_spaces, as, address_spaces_link);
|
QTAILQ_REMOVE(&address_spaces, as, address_spaces_link);
|
||||||
address_space_destroy_dispatch(as);
|
address_space_destroy_dispatch(as);
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(listener, &memory_listeners, link) {
|
||||||
|
assert(listener->address_space_filter != as);
|
||||||
|
}
|
||||||
|
|
||||||
flatview_unref(as->current_map);
|
flatview_unref(as->current_map);
|
||||||
g_free(as->name);
|
g_free(as->name);
|
||||||
g_free(as->ioeventfds);
|
g_free(as->ioeventfds);
|
||||||
|
Binary file not shown.
BIN
pc-bios/bios.bin
BIN
pc-bios/bios.bin
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -287,6 +287,7 @@ static int print_block_option_help(const char *filename, const char *fmt)
|
|||||||
proto_drv = bdrv_find_protocol(filename, true);
|
proto_drv = bdrv_find_protocol(filename, true);
|
||||||
if (!proto_drv) {
|
if (!proto_drv) {
|
||||||
error_report("Unknown protocol '%s'", filename);
|
error_report("Unknown protocol '%s'", filename);
|
||||||
|
free_option_parameters(create_options);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
create_options = append_option_parameters(create_options,
|
create_options = append_option_parameters(create_options,
|
||||||
@@ -662,9 +663,7 @@ static int img_check(int argc, char **argv)
|
|||||||
ret = collect_image_check(bs, check, filename, fmt, fix);
|
ret = collect_image_check(bs, check, filename, fmt, fix);
|
||||||
|
|
||||||
if (ret == -ENOTSUP) {
|
if (ret == -ENOTSUP) {
|
||||||
if (output_format == OFORMAT_HUMAN) {
|
error_report("This image format does not support checks");
|
||||||
error_report("This image format does not support checks");
|
|
||||||
}
|
|
||||||
ret = 63;
|
ret = 63;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@@ -1454,7 +1453,7 @@ static int img_convert(int argc, char **argv)
|
|||||||
ret = bdrv_parse_cache_flags(cache, &flags);
|
ret = bdrv_parse_cache_flags(cache, &flags);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("Invalid cache option: %s", cache);
|
error_report("Invalid cache option: %s", cache);
|
||||||
return -1;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
out_bs = bdrv_new_open("target", out_filename, out_fmt, flags, true, quiet);
|
out_bs = bdrv_new_open("target", out_filename, out_fmt, flags, true, quiet);
|
||||||
|
22
qemu-io.c
22
qemu-io.c
@@ -54,6 +54,7 @@ static int openfile(char *name, int flags, int growable, QDict *opts)
|
|||||||
|
|
||||||
if (qemuio_bs) {
|
if (qemuio_bs) {
|
||||||
fprintf(stderr, "file open already, try 'help close'\n");
|
fprintf(stderr, "file open already, try 'help close'\n");
|
||||||
|
QDECREF(opts);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +62,8 @@ static int openfile(char *name, int flags, int growable, QDict *opts)
|
|||||||
if (bdrv_open(&qemuio_bs, name, NULL, opts, flags | BDRV_O_PROTOCOL,
|
if (bdrv_open(&qemuio_bs, name, NULL, opts, flags | BDRV_O_PROTOCOL,
|
||||||
NULL, &local_err))
|
NULL, &local_err))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
|
fprintf(stderr, "%s: can't open%s%s: %s\n", progname,
|
||||||
|
name ? " device " : "", name ?: "",
|
||||||
error_get_pretty(local_err));
|
error_get_pretty(local_err));
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
return 1;
|
return 1;
|
||||||
@@ -72,7 +74,8 @@ static int openfile(char *name, int flags, int growable, QDict *opts)
|
|||||||
if (bdrv_open(&qemuio_bs, name, NULL, opts, flags, NULL, &local_err)
|
if (bdrv_open(&qemuio_bs, name, NULL, opts, flags, NULL, &local_err)
|
||||||
< 0)
|
< 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: can't open device %s: %s\n", progname, name,
|
fprintf(stderr, "%s: can't open%s%s: %s\n", progname,
|
||||||
|
name ? " device " : "", name ?: "",
|
||||||
error_get_pretty(local_err));
|
error_get_pretty(local_err));
|
||||||
error_free(local_err);
|
error_free(local_err);
|
||||||
bdrv_unref(qemuio_bs);
|
bdrv_unref(qemuio_bs);
|
||||||
@@ -118,6 +121,7 @@ static const cmdinfo_t open_cmd = {
|
|||||||
|
|
||||||
static QemuOptsList empty_opts = {
|
static QemuOptsList empty_opts = {
|
||||||
.name = "drive",
|
.name = "drive",
|
||||||
|
.merge_lists = true,
|
||||||
.head = QTAILQ_HEAD_INITIALIZER(empty_opts.head),
|
.head = QTAILQ_HEAD_INITIALIZER(empty_opts.head),
|
||||||
.desc = {
|
.desc = {
|
||||||
/* no elements => accept any params */
|
/* no elements => accept any params */
|
||||||
@@ -132,7 +136,7 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
|
|||||||
int growable = 0;
|
int growable = 0;
|
||||||
int c;
|
int c;
|
||||||
QemuOpts *qopts;
|
QemuOpts *qopts;
|
||||||
QDict *opts = NULL;
|
QDict *opts;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "snrgo:")) != EOF) {
|
while ((c = getopt(argc, argv, "snrgo:")) != EOF) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
@@ -149,15 +153,14 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
|
|||||||
growable = 1;
|
growable = 1;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
qopts = qemu_opts_parse(&empty_opts, optarg, 0);
|
if (!qemu_opts_parse(&empty_opts, optarg, 0)) {
|
||||||
if (qopts == NULL) {
|
|
||||||
printf("could not parse option list -- %s\n", optarg);
|
printf("could not parse option list -- %s\n", optarg);
|
||||||
|
qemu_opts_reset(&empty_opts);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
opts = qemu_opts_to_qdict(qopts, opts);
|
|
||||||
qemu_opts_del(qopts);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
qemu_opts_reset(&empty_opts);
|
||||||
return qemuio_command_usage(&open_cmd);
|
return qemuio_command_usage(&open_cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,11 +169,16 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
|
|||||||
flags |= BDRV_O_RDWR;
|
flags |= BDRV_O_RDWR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qopts = qemu_opts_find(&empty_opts, NULL);
|
||||||
|
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
|
||||||
|
qemu_opts_reset(&empty_opts);
|
||||||
|
|
||||||
if (optind == argc - 1) {
|
if (optind == argc - 1) {
|
||||||
return openfile(argv[optind], flags, growable, opts);
|
return openfile(argv[optind], flags, growable, opts);
|
||||||
} else if (optind == argc) {
|
} else if (optind == argc) {
|
||||||
return openfile(NULL, flags, growable, opts);
|
return openfile(NULL, flags, growable, opts);
|
||||||
} else {
|
} else {
|
||||||
|
QDECREF(opts);
|
||||||
return qemuio_command_usage(&open_cmd);
|
return qemuio_command_usage(&open_cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Submodule roms/seabios updated: b1d4dc9084...e51488c5f8
@@ -1047,6 +1047,14 @@ gd_update(const char *tab, int x, int y, int w, int h) "tab=%s, x=%d, y=%d, w=%d
|
|||||||
gd_key_event(const char *tab, int gdk_keycode, int qemu_keycode, const char *action) "tab=%s, translated GDK keycode %d to QEMU keycode %d (%s)"
|
gd_key_event(const char *tab, int gdk_keycode, int qemu_keycode, const char *action) "tab=%s, translated GDK keycode %d to QEMU keycode %d (%s)"
|
||||||
gd_grab(const char *tab, const char *device, bool on) "tab=%s, %s %d"
|
gd_grab(const char *tab, const char *device, bool on) "tab=%s, %s %d"
|
||||||
|
|
||||||
|
# ui/vnc.c
|
||||||
|
vnc_key_guest_leds(bool caps, bool num, bool scroll) "caps %d, num %d, scroll %d"
|
||||||
|
vnc_key_map_init(const char *layout) "%s"
|
||||||
|
vnc_key_event_ext(bool down, int sym, int keycode, const char *name) "down %d, sym 0x%x, keycode 0x%x [%s]"
|
||||||
|
vnc_key_event_map(bool down, int sym, int keycode, const char *name) "down %d, sym 0x%x -> keycode 0x%x [%s]"
|
||||||
|
vnc_key_sync_numlock(bool on) "%d"
|
||||||
|
vnc_key_sync_capslock(bool on) "%d"
|
||||||
|
|
||||||
# ui/input.c
|
# ui/input.c
|
||||||
input_event_key_number(int conidx, int number, const char *qcode, bool down) "con %d, key number 0x%x [%s], down %d"
|
input_event_key_number(int conidx, int number, const char *qcode, bool down) "con %d, key number 0x%x [%s], down %d"
|
||||||
input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d"
|
input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d"
|
||||||
|
33
ui/console.c
33
ui/console.c
@@ -1109,6 +1109,39 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const int qcode_to_keysym[Q_KEY_CODE_MAX] = {
|
||||||
|
[Q_KEY_CODE_UP] = QEMU_KEY_UP,
|
||||||
|
[Q_KEY_CODE_DOWN] = QEMU_KEY_DOWN,
|
||||||
|
[Q_KEY_CODE_RIGHT] = QEMU_KEY_RIGHT,
|
||||||
|
[Q_KEY_CODE_LEFT] = QEMU_KEY_LEFT,
|
||||||
|
[Q_KEY_CODE_HOME] = QEMU_KEY_HOME,
|
||||||
|
[Q_KEY_CODE_END] = QEMU_KEY_END,
|
||||||
|
[Q_KEY_CODE_PGUP] = QEMU_KEY_PAGEUP,
|
||||||
|
[Q_KEY_CODE_PGDN] = QEMU_KEY_PAGEDOWN,
|
||||||
|
[Q_KEY_CODE_DELETE] = QEMU_KEY_DELETE,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool kbd_put_qcode_console(QemuConsole *s, int qcode)
|
||||||
|
{
|
||||||
|
int keysym;
|
||||||
|
|
||||||
|
keysym = qcode_to_keysym[qcode];
|
||||||
|
if (keysym == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
kbd_put_keysym_console(s, keysym);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kbd_put_string_console(QemuConsole *s, const char *str, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len && str[i]; i++) {
|
||||||
|
kbd_put_keysym_console(s, str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void kbd_put_keysym(int keysym)
|
void kbd_put_keysym(int keysym)
|
||||||
{
|
{
|
||||||
kbd_put_keysym_console(active_console, keysym);
|
kbd_put_keysym_console(active_console, keysym);
|
||||||
|
10
ui/curses.c
10
ui/curses.c
@@ -277,31 +277,41 @@ static void curses_refresh(DisplayChangeListener *dcl)
|
|||||||
* events, we need to emit both for each key received */
|
* events, we need to emit both for each key received */
|
||||||
if (keycode & SHIFT) {
|
if (keycode & SHIFT) {
|
||||||
qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
|
qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
}
|
}
|
||||||
if (keycode & CNTRL) {
|
if (keycode & CNTRL) {
|
||||||
qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
|
qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
}
|
}
|
||||||
if (keycode & ALT) {
|
if (keycode & ALT) {
|
||||||
qemu_input_event_send_key_number(NULL, ALT_CODE, true);
|
qemu_input_event_send_key_number(NULL, ALT_CODE, true);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
}
|
}
|
||||||
if (keycode & ALTGR) {
|
if (keycode & ALTGR) {
|
||||||
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
|
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
|
qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
|
qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
|
|
||||||
if (keycode & ALTGR) {
|
if (keycode & ALTGR) {
|
||||||
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
|
qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
}
|
}
|
||||||
if (keycode & ALT) {
|
if (keycode & ALT) {
|
||||||
qemu_input_event_send_key_number(NULL, ALT_CODE, false);
|
qemu_input_event_send_key_number(NULL, ALT_CODE, false);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
}
|
}
|
||||||
if (keycode & CNTRL) {
|
if (keycode & CNTRL) {
|
||||||
qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
|
qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
}
|
}
|
||||||
if (keycode & SHIFT) {
|
if (keycode & SHIFT) {
|
||||||
qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
|
qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
keysym = curses2qemu[chr];
|
keysym = curses2qemu[chr];
|
||||||
|
@@ -74,27 +74,6 @@ int index_from_key(const char *key)
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static KeyValue **keyvalues;
|
|
||||||
static int keyvalues_size;
|
|
||||||
static QEMUTimer *key_timer;
|
|
||||||
|
|
||||||
static void free_keyvalues(void)
|
|
||||||
{
|
|
||||||
g_free(keyvalues);
|
|
||||||
keyvalues = NULL;
|
|
||||||
keyvalues_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void release_keys(void *opaque)
|
|
||||||
{
|
|
||||||
while (keyvalues_size > 0) {
|
|
||||||
qemu_input_event_send_key(NULL, keyvalues[--keyvalues_size],
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
|
|
||||||
free_keyvalues();
|
|
||||||
}
|
|
||||||
|
|
||||||
static KeyValue *copy_key_value(KeyValue *src)
|
static KeyValue *copy_key_value(KeyValue *src)
|
||||||
{
|
{
|
||||||
KeyValue *dst = g_new(KeyValue, 1);
|
KeyValue *dst = g_new(KeyValue, 1);
|
||||||
@@ -107,30 +86,18 @@ void qmp_send_key(KeyValueList *keys, bool has_hold_time, int64_t hold_time,
|
|||||||
{
|
{
|
||||||
KeyValueList *p;
|
KeyValueList *p;
|
||||||
|
|
||||||
if (!key_timer) {
|
|
||||||
key_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, release_keys, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyvalues != NULL) {
|
|
||||||
timer_del(key_timer);
|
|
||||||
release_keys(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_hold_time) {
|
if (!has_hold_time) {
|
||||||
hold_time = 100;
|
hold_time = 0; /* use default */
|
||||||
}
|
}
|
||||||
|
|
||||||
for (p = keys; p != NULL; p = p->next) {
|
for (p = keys; p != NULL; p = p->next) {
|
||||||
qemu_input_event_send_key(NULL, copy_key_value(p->value), true);
|
qemu_input_event_send_key(NULL, copy_key_value(p->value), true);
|
||||||
|
qemu_input_event_send_key_delay(hold_time);
|
||||||
keyvalues = g_realloc(keyvalues, sizeof(KeyValue *) *
|
}
|
||||||
(keyvalues_size + 1));
|
for (p = keys; p != NULL; p = p->next) {
|
||||||
keyvalues[keyvalues_size++] = copy_key_value(p->value);
|
qemu_input_event_send_key(NULL, copy_key_value(p->value), false);
|
||||||
|
qemu_input_event_send_key_delay(hold_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* delayed key up events */
|
|
||||||
timer_mod(key_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
|
|
||||||
muldiv64(get_ticks_per_sec(), hold_time, 1000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
|
static void legacy_kbd_event(DeviceState *dev, QemuConsole *src,
|
||||||
|
108
ui/input.c
108
ui/input.c
@@ -14,11 +14,31 @@ struct QemuInputHandlerState {
|
|||||||
QemuConsole *con;
|
QemuConsole *con;
|
||||||
QTAILQ_ENTRY(QemuInputHandlerState) node;
|
QTAILQ_ENTRY(QemuInputHandlerState) node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct QemuInputEventQueue QemuInputEventQueue;
|
||||||
|
struct QemuInputEventQueue {
|
||||||
|
enum {
|
||||||
|
QEMU_INPUT_QUEUE_DELAY = 1,
|
||||||
|
QEMU_INPUT_QUEUE_EVENT,
|
||||||
|
QEMU_INPUT_QUEUE_SYNC,
|
||||||
|
} type;
|
||||||
|
QEMUTimer *timer;
|
||||||
|
uint32_t delay_ms;
|
||||||
|
QemuConsole *src;
|
||||||
|
InputEvent *evt;
|
||||||
|
QTAILQ_ENTRY(QemuInputEventQueue) node;
|
||||||
|
};
|
||||||
|
|
||||||
static QTAILQ_HEAD(, QemuInputHandlerState) handlers =
|
static QTAILQ_HEAD(, QemuInputHandlerState) handlers =
|
||||||
QTAILQ_HEAD_INITIALIZER(handlers);
|
QTAILQ_HEAD_INITIALIZER(handlers);
|
||||||
static NotifierList mouse_mode_notifiers =
|
static NotifierList mouse_mode_notifiers =
|
||||||
NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
|
NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers);
|
||||||
|
|
||||||
|
static QTAILQ_HEAD(QemuInputEventQueueHead, QemuInputEventQueue) kbd_queue =
|
||||||
|
QTAILQ_HEAD_INITIALIZER(kbd_queue);
|
||||||
|
static QEMUTimer *kbd_timer;
|
||||||
|
static uint32_t kbd_default_delay_ms = 10;
|
||||||
|
|
||||||
QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev,
|
QemuInputHandlerState *qemu_input_handler_register(DeviceState *dev,
|
||||||
QemuInputHandler *handler)
|
QemuInputHandler *handler)
|
||||||
{
|
{
|
||||||
@@ -171,6 +191,73 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qemu_input_queue_process(void *opaque)
|
||||||
|
{
|
||||||
|
struct QemuInputEventQueueHead *queue = opaque;
|
||||||
|
QemuInputEventQueue *item;
|
||||||
|
|
||||||
|
g_assert(!QTAILQ_EMPTY(queue));
|
||||||
|
item = QTAILQ_FIRST(queue);
|
||||||
|
g_assert(item->type == QEMU_INPUT_QUEUE_DELAY);
|
||||||
|
QTAILQ_REMOVE(queue, item, node);
|
||||||
|
g_free(item);
|
||||||
|
|
||||||
|
while (!QTAILQ_EMPTY(queue)) {
|
||||||
|
item = QTAILQ_FIRST(queue);
|
||||||
|
switch (item->type) {
|
||||||
|
case QEMU_INPUT_QUEUE_DELAY:
|
||||||
|
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)
|
||||||
|
+ item->delay_ms);
|
||||||
|
return;
|
||||||
|
case QEMU_INPUT_QUEUE_EVENT:
|
||||||
|
qemu_input_event_send(item->src, item->evt);
|
||||||
|
qapi_free_InputEvent(item->evt);
|
||||||
|
break;
|
||||||
|
case QEMU_INPUT_QUEUE_SYNC:
|
||||||
|
qemu_input_event_sync();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QTAILQ_REMOVE(queue, item, node);
|
||||||
|
g_free(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemu_input_queue_delay(struct QemuInputEventQueueHead *queue,
|
||||||
|
QEMUTimer *timer, uint32_t delay_ms)
|
||||||
|
{
|
||||||
|
QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1);
|
||||||
|
bool start_timer = QTAILQ_EMPTY(queue);
|
||||||
|
|
||||||
|
item->type = QEMU_INPUT_QUEUE_DELAY;
|
||||||
|
item->delay_ms = delay_ms;
|
||||||
|
item->timer = timer;
|
||||||
|
QTAILQ_INSERT_TAIL(queue, item, node);
|
||||||
|
|
||||||
|
if (start_timer) {
|
||||||
|
timer_mod(item->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)
|
||||||
|
+ item->delay_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemu_input_queue_event(struct QemuInputEventQueueHead *queue,
|
||||||
|
QemuConsole *src, InputEvent *evt)
|
||||||
|
{
|
||||||
|
QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1);
|
||||||
|
|
||||||
|
item->type = QEMU_INPUT_QUEUE_EVENT;
|
||||||
|
item->src = src;
|
||||||
|
item->evt = evt;
|
||||||
|
QTAILQ_INSERT_TAIL(queue, item, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemu_input_queue_sync(struct QemuInputEventQueueHead *queue)
|
||||||
|
{
|
||||||
|
QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1);
|
||||||
|
|
||||||
|
item->type = QEMU_INPUT_QUEUE_SYNC;
|
||||||
|
QTAILQ_INSERT_TAIL(queue, item, node);
|
||||||
|
}
|
||||||
|
|
||||||
void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
|
void qemu_input_event_send(QemuConsole *src, InputEvent *evt)
|
||||||
{
|
{
|
||||||
QemuInputHandlerState *s;
|
QemuInputHandlerState *s;
|
||||||
@@ -230,9 +317,14 @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down)
|
|||||||
{
|
{
|
||||||
InputEvent *evt;
|
InputEvent *evt;
|
||||||
evt = qemu_input_event_new_key(key, down);
|
evt = qemu_input_event_new_key(key, down);
|
||||||
qemu_input_event_send(src, evt);
|
if (QTAILQ_EMPTY(&kbd_queue)) {
|
||||||
qemu_input_event_sync();
|
qemu_input_event_send(src, evt);
|
||||||
qapi_free_InputEvent(evt);
|
qemu_input_event_sync();
|
||||||
|
qapi_free_InputEvent(evt);
|
||||||
|
} else {
|
||||||
|
qemu_input_queue_event(&kbd_queue, src, evt);
|
||||||
|
qemu_input_queue_sync(&kbd_queue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down)
|
void qemu_input_event_send_key_number(QemuConsole *src, int num, bool down)
|
||||||
@@ -251,6 +343,16 @@ void qemu_input_event_send_key_qcode(QemuConsole *src, QKeyCode q, bool down)
|
|||||||
qemu_input_event_send_key(src, key, down);
|
qemu_input_event_send_key(src, key, down);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qemu_input_event_send_key_delay(uint32_t delay_ms)
|
||||||
|
{
|
||||||
|
if (!kbd_timer) {
|
||||||
|
kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, qemu_input_queue_process,
|
||||||
|
&kbd_queue);
|
||||||
|
}
|
||||||
|
qemu_input_queue_delay(&kbd_queue, kbd_timer,
|
||||||
|
delay_ms ? delay_ms : kbd_default_delay_ms);
|
||||||
|
}
|
||||||
|
|
||||||
InputEvent *qemu_input_event_new_btn(InputButton btn, bool down)
|
InputEvent *qemu_input_event_new_btn(InputButton btn, bool down)
|
||||||
{
|
{
|
||||||
InputEvent *evt = g_new0(InputEvent, 1);
|
InputEvent *evt = g_new0(InputEvent, 1);
|
||||||
|
68
ui/sdl2.c
68
ui/sdl2.c
@@ -49,6 +49,7 @@ static struct sdl2_state {
|
|||||||
int idx;
|
int idx;
|
||||||
int last_vm_running; /* per console for caption reasons */
|
int last_vm_running; /* per console for caption reasons */
|
||||||
int x, y;
|
int x, y;
|
||||||
|
int hidden;
|
||||||
} *sdl2_console;
|
} *sdl2_console;
|
||||||
|
|
||||||
static SDL_Surface *guest_sprite_surface;
|
static SDL_Surface *guest_sprite_surface;
|
||||||
@@ -136,6 +137,9 @@ static void do_sdl_resize(struct sdl2_state *scon, int width, int height,
|
|||||||
} else {
|
} else {
|
||||||
flags |= SDL_WINDOW_RESIZABLE;
|
flags |= SDL_WINDOW_RESIZABLE;
|
||||||
}
|
}
|
||||||
|
if (scon->hidden) {
|
||||||
|
flags |= SDL_WINDOW_HIDDEN;
|
||||||
|
}
|
||||||
|
|
||||||
scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
|
scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
|
||||||
SDL_WINDOWPOS_UNDEFINED,
|
SDL_WINDOWPOS_UNDEFINED,
|
||||||
@@ -210,6 +214,23 @@ static void sdl_process_key(struct sdl2_state *scon,
|
|||||||
int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
|
int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
|
||||||
QemuConsole *con = scon ? scon->dcl.con : NULL;
|
QemuConsole *con = scon ? scon->dcl.con : NULL;
|
||||||
|
|
||||||
|
if (!qemu_console_is_graphic(con)) {
|
||||||
|
if (ev->type == SDL_KEYDOWN) {
|
||||||
|
switch (ev->keysym.scancode) {
|
||||||
|
case SDL_SCANCODE_RETURN:
|
||||||
|
kbd_put_keysym_console(con, '\n');
|
||||||
|
break;
|
||||||
|
case SDL_SCANCODE_BACKSPACE:
|
||||||
|
kbd_put_keysym_console(con, QEMU_KEY_BACKSPACE);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
kbd_put_qcode_console(con, qcode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (ev->keysym.scancode) {
|
switch (ev->keysym.scancode) {
|
||||||
#if 0
|
#if 0
|
||||||
case SDL_SCANCODE_NUMLOCKCLEAR:
|
case SDL_SCANCODE_NUMLOCKCLEAR:
|
||||||
@@ -305,6 +326,11 @@ static void sdl_show_cursor(void)
|
|||||||
|
|
||||||
static void sdl_grab_start(struct sdl2_state *scon)
|
static void sdl_grab_start(struct sdl2_state *scon)
|
||||||
{
|
{
|
||||||
|
QemuConsole *con = scon ? scon->dcl.con : NULL;
|
||||||
|
|
||||||
|
if (!con || !qemu_console_is_graphic(con)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* If the application is not active, do not try to enter grab state. This
|
* If the application is not active, do not try to enter grab state. This
|
||||||
* prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
|
* prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
|
||||||
@@ -458,7 +484,7 @@ static void toggle_full_screen(struct sdl2_state *scon)
|
|||||||
|
|
||||||
static void handle_keydown(SDL_Event *ev)
|
static void handle_keydown(SDL_Event *ev)
|
||||||
{
|
{
|
||||||
int mod_state;
|
int mod_state, win;
|
||||||
struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
|
struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
|
||||||
|
|
||||||
if (alt_grab) {
|
if (alt_grab) {
|
||||||
@@ -473,6 +499,27 @@ static void handle_keydown(SDL_Event *ev)
|
|||||||
|
|
||||||
if (gui_key_modifier_pressed) {
|
if (gui_key_modifier_pressed) {
|
||||||
switch (ev->key.keysym.scancode) {
|
switch (ev->key.keysym.scancode) {
|
||||||
|
case SDL_SCANCODE_2:
|
||||||
|
case SDL_SCANCODE_3:
|
||||||
|
case SDL_SCANCODE_4:
|
||||||
|
case SDL_SCANCODE_5:
|
||||||
|
case SDL_SCANCODE_6:
|
||||||
|
case SDL_SCANCODE_7:
|
||||||
|
case SDL_SCANCODE_8:
|
||||||
|
case SDL_SCANCODE_9:
|
||||||
|
win = ev->key.keysym.scancode - SDL_SCANCODE_1;
|
||||||
|
if (win < sdl2_num_outputs) {
|
||||||
|
sdl2_console[win].hidden = !sdl2_console[win].hidden;
|
||||||
|
if (sdl2_console[win].real_window) {
|
||||||
|
if (sdl2_console[win].hidden) {
|
||||||
|
SDL_HideWindow(sdl2_console[win].real_window);
|
||||||
|
} else {
|
||||||
|
SDL_ShowWindow(sdl2_console[win].real_window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gui_keysym = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case SDL_SCANCODE_F:
|
case SDL_SCANCODE_F:
|
||||||
toggle_full_screen(scon);
|
toggle_full_screen(scon);
|
||||||
gui_keysym = 1;
|
gui_keysym = 1;
|
||||||
@@ -544,6 +591,17 @@ static void handle_keyup(SDL_Event *ev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_textinput(SDL_Event *ev)
|
||||||
|
{
|
||||||
|
struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
|
||||||
|
QemuConsole *con = scon ? scon->dcl.con : NULL;
|
||||||
|
|
||||||
|
if (qemu_console_is_graphic(con)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_mousemotion(SDL_Event *ev)
|
static void handle_mousemotion(SDL_Event *ev)
|
||||||
{
|
{
|
||||||
int max_x, max_y;
|
int max_x, max_y;
|
||||||
@@ -680,6 +738,9 @@ static void sdl_refresh(DisplayChangeListener *dcl)
|
|||||||
case SDL_KEYUP:
|
case SDL_KEYUP:
|
||||||
handle_keyup(ev);
|
handle_keyup(ev);
|
||||||
break;
|
break;
|
||||||
|
case SDL_TEXTINPUT:
|
||||||
|
handle_textinput(ev);
|
||||||
|
break;
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
if (!no_quit) {
|
if (!no_quit) {
|
||||||
no_shutdown = 0;
|
no_shutdown = 0;
|
||||||
@@ -808,7 +869,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
|
|||||||
|
|
||||||
for (i = 0;; i++) {
|
for (i = 0;; i++) {
|
||||||
QemuConsole *con = qemu_console_lookup_by_index(i);
|
QemuConsole *con = qemu_console_lookup_by_index(i);
|
||||||
if (!con || !qemu_console_is_graphic(con)) {
|
if (!con) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -816,6 +877,9 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
|
|||||||
sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs);
|
sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs);
|
||||||
for (i = 0; i < sdl2_num_outputs; i++) {
|
for (i = 0; i < sdl2_num_outputs; i++) {
|
||||||
QemuConsole *con = qemu_console_lookup_by_index(i);
|
QemuConsole *con = qemu_console_lookup_by_index(i);
|
||||||
|
if (!qemu_console_is_graphic(con)) {
|
||||||
|
sdl2_console[i].hidden = true;
|
||||||
|
}
|
||||||
sdl2_console[i].dcl.ops = &dcl_ops;
|
sdl2_console[i].dcl.ops = &dcl_ops;
|
||||||
sdl2_console[i].dcl.con = con;
|
sdl2_console[i].dcl.con = con;
|
||||||
register_displaychangelistener(&sdl2_console[i].dcl);
|
register_displaychangelistener(&sdl2_console[i].dcl);
|
||||||
|
@@ -181,6 +181,10 @@ tight_detect_smooth_image24(VncState *vs, int w, int h)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pixels == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 95% smooth or more ... */
|
/* 95% smooth or more ... */
|
||||||
if (stats[0] * 33 / pixels >= 95) {
|
if (stats[0] * 33 / pixels >= 95) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -267,7 +271,9 @@ tight_detect_smooth_image24(VncState *vs, int w, int h)
|
|||||||
y += w; \
|
y += w; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
\
|
if (pixels == 0) { \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \
|
if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \
|
||||||
return 0; \
|
return 0; \
|
||||||
} \
|
} \
|
||||||
|
63
ui/vnc.c
63
ui/vnc.c
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include "vnc.h"
|
#include "vnc.h"
|
||||||
#include "vnc-jobs.h"
|
#include "vnc-jobs.h"
|
||||||
|
#include "trace.h"
|
||||||
#include "sysemu/sysemu.h"
|
#include "sysemu/sysemu.h"
|
||||||
#include "qemu/sockets.h"
|
#include "qemu/sockets.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
@@ -1552,7 +1553,9 @@ static void press_key(VncState *vs, int keysym)
|
|||||||
{
|
{
|
||||||
int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK;
|
int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK;
|
||||||
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true);
|
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
|
qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false);
|
||||||
|
qemu_input_event_send_key_delay(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int current_led_state(VncState *vs)
|
static int current_led_state(VncState *vs)
|
||||||
@@ -1597,6 +1600,10 @@ static void kbd_leds(void *opaque, int ledstate)
|
|||||||
int caps, num, scr;
|
int caps, num, scr;
|
||||||
bool has_changed = (ledstate != current_led_state(vs));
|
bool has_changed = (ledstate != current_led_state(vs));
|
||||||
|
|
||||||
|
trace_vnc_key_guest_leds((ledstate & QEMU_CAPS_LOCK_LED),
|
||||||
|
(ledstate & QEMU_NUM_LOCK_LED),
|
||||||
|
(ledstate & QEMU_SCROLL_LOCK_LED));
|
||||||
|
|
||||||
caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0;
|
caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0;
|
||||||
num = ledstate & QEMU_NUM_LOCK_LED ? 1 : 0;
|
num = ledstate & QEMU_NUM_LOCK_LED ? 1 : 0;
|
||||||
scr = ledstate & QEMU_SCROLL_LOCK_LED ? 1 : 0;
|
scr = ledstate & QEMU_SCROLL_LOCK_LED ? 1 : 0;
|
||||||
@@ -1659,11 +1666,13 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
|
|||||||
*/
|
*/
|
||||||
if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
|
if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) {
|
||||||
if (!vs->modifiers_state[0x45]) {
|
if (!vs->modifiers_state[0x45]) {
|
||||||
|
trace_vnc_key_sync_numlock(true);
|
||||||
vs->modifiers_state[0x45] = 1;
|
vs->modifiers_state[0x45] = 1;
|
||||||
press_key(vs, 0xff7f);
|
press_key(vs, 0xff7f);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (vs->modifiers_state[0x45]) {
|
if (vs->modifiers_state[0x45]) {
|
||||||
|
trace_vnc_key_sync_numlock(false);
|
||||||
vs->modifiers_state[0x45] = 0;
|
vs->modifiers_state[0x45] = 0;
|
||||||
press_key(vs, 0xff7f);
|
press_key(vs, 0xff7f);
|
||||||
}
|
}
|
||||||
@@ -1682,11 +1691,13 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym)
|
|||||||
int capslock = !!(vs->modifiers_state[0x3a]);
|
int capslock = !!(vs->modifiers_state[0x3a]);
|
||||||
if (capslock) {
|
if (capslock) {
|
||||||
if (uppercase == shift) {
|
if (uppercase == shift) {
|
||||||
|
trace_vnc_key_sync_capslock(false);
|
||||||
vs->modifiers_state[0x3a] = 0;
|
vs->modifiers_state[0x3a] = 0;
|
||||||
press_key(vs, 0xffe5);
|
press_key(vs, 0xffe5);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (uppercase != shift) {
|
if (uppercase != shift) {
|
||||||
|
trace_vnc_key_sync_capslock(true);
|
||||||
vs->modifiers_state[0x3a] = 1;
|
vs->modifiers_state[0x3a] = 1;
|
||||||
press_key(vs, 0xffe5);
|
press_key(vs, 0xffe5);
|
||||||
}
|
}
|
||||||
@@ -1819,6 +1830,11 @@ static void vnc_release_modifiers(VncState *vs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *code2name(int keycode)
|
||||||
|
{
|
||||||
|
return QKeyCode_lookup[qemu_input_key_number_to_qcode(keycode)];
|
||||||
|
}
|
||||||
|
|
||||||
static void key_event(VncState *vs, int down, uint32_t sym)
|
static void key_event(VncState *vs, int down, uint32_t sym)
|
||||||
{
|
{
|
||||||
int keycode;
|
int keycode;
|
||||||
@@ -1829,6 +1845,7 @@ static void key_event(VncState *vs, int down, uint32_t sym)
|
|||||||
}
|
}
|
||||||
|
|
||||||
keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK;
|
keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK;
|
||||||
|
trace_vnc_key_event_map(down, sym, keycode, code2name(keycode));
|
||||||
do_key_event(vs, down, keycode, sym);
|
do_key_event(vs, down, keycode, sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1836,10 +1853,12 @@ static void ext_key_event(VncState *vs, int down,
|
|||||||
uint32_t sym, uint16_t keycode)
|
uint32_t sym, uint16_t keycode)
|
||||||
{
|
{
|
||||||
/* if the user specifies a keyboard layout, always use it */
|
/* if the user specifies a keyboard layout, always use it */
|
||||||
if (keyboard_layout)
|
if (keyboard_layout) {
|
||||||
key_event(vs, down, sym);
|
key_event(vs, down, sym);
|
||||||
else
|
} else {
|
||||||
|
trace_vnc_key_event_ext(down, sym, keycode, code2name(keycode));
|
||||||
do_key_event(vs, down, keycode, sym);
|
do_key_event(vs, down, keycode, sym);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void framebuffer_update_request(VncState *vs, int incremental,
|
static void framebuffer_update_request(VncState *vs, int incremental,
|
||||||
@@ -2929,10 +2948,12 @@ void vnc_display_init(DisplayState *ds)
|
|||||||
QTAILQ_INIT(&vs->clients);
|
QTAILQ_INIT(&vs->clients);
|
||||||
vs->expires = TIME_MAX;
|
vs->expires = TIME_MAX;
|
||||||
|
|
||||||
if (keyboard_layout)
|
if (keyboard_layout) {
|
||||||
|
trace_vnc_key_map_init(keyboard_layout);
|
||||||
vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
|
vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
|
||||||
else
|
} else {
|
||||||
vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us");
|
vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us");
|
||||||
|
}
|
||||||
|
|
||||||
if (!vs->kbd_layout)
|
if (!vs->kbd_layout)
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -2976,26 +2997,6 @@ static void vnc_display_close(DisplayState *ds)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vnc_display_disable_login(DisplayState *ds)
|
|
||||||
{
|
|
||||||
VncDisplay *vs = vnc_display;
|
|
||||||
|
|
||||||
if (!vs) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vs->password) {
|
|
||||||
g_free(vs->password);
|
|
||||||
}
|
|
||||||
|
|
||||||
vs->password = NULL;
|
|
||||||
if (vs->auth == VNC_AUTH_NONE) {
|
|
||||||
vs->auth = VNC_AUTH_VNC;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vnc_display_password(DisplayState *ds, const char *password)
|
int vnc_display_password(DisplayState *ds, const char *password)
|
||||||
{
|
{
|
||||||
VncDisplay *vs = vnc_display;
|
VncDisplay *vs = vnc_display;
|
||||||
@@ -3003,20 +3004,18 @@ int vnc_display_password(DisplayState *ds, const char *password)
|
|||||||
if (!vs) {
|
if (!vs) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (vs->auth == VNC_AUTH_NONE) {
|
||||||
if (!password) {
|
error_printf_unless_qmp("If you want use passwords please enable "
|
||||||
/* This is not the intention of this interface but err on the side
|
"password auth using '-vnc ${dpy},password'.");
|
||||||
of being safe */
|
return -EINVAL;
|
||||||
return vnc_display_disable_login(ds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vs->password) {
|
if (vs->password) {
|
||||||
g_free(vs->password);
|
g_free(vs->password);
|
||||||
vs->password = NULL;
|
vs->password = NULL;
|
||||||
}
|
}
|
||||||
vs->password = g_strdup(password);
|
if (password) {
|
||||||
if (vs->auth == VNC_AUTH_NONE) {
|
vs->password = g_strdup(password);
|
||||||
vs->auth = VNC_AUTH_VNC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Reference in New Issue
Block a user