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