Compare commits
	
		
			55 Commits
		
	
	
		
			pull-roms-
			...
			pull-input
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					2082bac151 | ||
| 
						 | 
					90525fe279 | ||
| 
						 | 
					3503206a90 | ||
| 
						 | 
					2deb4acc7c | ||
| 
						 | 
					5a165668e7 | ||
| 
						 | 
					2e377f1730 | ||
| 
						 | 
					be1a717624 | ||
| 
						 | 
					e00fcfeab3 | ||
| 
						 | 
					278073ba29 | ||
| 
						 | 
					82ea61c6da | ||
| 
						 | 
					1673e89e93 | ||
| 
						 | 
					36f5db59cf | ||
| 
						 | 
					7bafd8889e | ||
| 
						 | 
					b52991537c | ||
| 
						 | 
					4006617552 | ||
| 
						 | 
					cf864569cd | ||
| 
						 | 
					7bd3055ffd | ||
| 
						 | 
					b791c3b38c | ||
| 
						 | 
					322fd1f4f7 | ||
| 
						 | 
					b88a3e01f5 | ||
| 
						 | 
					d81d410635 | ||
| 
						 | 
					381626a969 | ||
| 
						 | 
					95dd1c4d7a | ||
| 
						 | 
					9a1d111e70 | ||
| 
						 | 
					ad489e9346 | ||
| 
						 | 
					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);
 | 
			
		||||
            ret = -EINVAL;
 | 
			
		||||
        }
 | 
			
		||||
        QDECREF(image_options);
 | 
			
		||||
        goto done;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -475,6 +475,7 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
 | 
			
		||||
        case QTYPE_QERROR: {
 | 
			
		||||
            QString *value = qerror_human((QError *)obj);
 | 
			
		||||
            func_fprintf(f, "%s", qstring_get_str(value));
 | 
			
		||||
            QDECREF(value);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case QTYPE_NONE:
 | 
			
		||||
 
 | 
			
		||||
@@ -1308,6 +1308,7 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
 | 
			
		||||
    options = qdict_clone_shallow(bs->options);
 | 
			
		||||
 | 
			
		||||
    ret = qcow2_open(bs, options, flags, &local_err);
 | 
			
		||||
    QDECREF(options);
 | 
			
		||||
    if (local_err) {
 | 
			
		||||
        error_setg(errp, "Could not reopen qcow2 layer: %s",
 | 
			
		||||
                   error_get_pretty(local_err));
 | 
			
		||||
@@ -1318,8 +1319,6 @@ static void qcow2_invalidate_cache(BlockDriverState *bs, Error **errp)
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QDECREF(options);
 | 
			
		||||
 | 
			
		||||
    if (crypt_method) {
 | 
			
		||||
        s->crypt_method = crypt_method;
 | 
			
		||||
        memcpy(&s->aes_encrypt_key, &aes_encrypt_key, sizeof(aes_encrypt_key));
 | 
			
		||||
 
 | 
			
		||||
@@ -1192,7 +1192,7 @@ again:
 | 
			
		||||
        if (size == 0)
 | 
			
		||||
#endif
 | 
			
		||||
#if defined(__APPLE__) && defined(__MACH__)
 | 
			
		||||
        size = LONG_LONG_MAX;
 | 
			
		||||
        size = LLONG_MAX;
 | 
			
		||||
#else
 | 
			
		||||
        size = lseek(fd, 0LL, SEEK_END);
 | 
			
		||||
#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));
 | 
			
		||||
    /* we don't need to update entire object */
 | 
			
		||||
    datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
 | 
			
		||||
    inode = g_malloc(datalen);
 | 
			
		||||
 | 
			
		||||
    /* refresh inode. */
 | 
			
		||||
    fd = connect_to_sdog(s, &local_err);
 | 
			
		||||
@@ -2202,8 +2203,6 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
 | 
			
		||||
        goto cleanup;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inode = (SheepdogInode *)g_malloc(datalen);
 | 
			
		||||
 | 
			
		||||
    ret = read_object(fd, (char *)inode, vid_to_vdi_oid(new_vid),
 | 
			
		||||
                      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);
 | 
			
		||||
 | 
			
		||||
cleanup:
 | 
			
		||||
    g_free(inode);
 | 
			
		||||
    closesocket(fd);
 | 
			
		||||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1534,7 +1534,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
 | 
			
		||||
    int ret, i;
 | 
			
		||||
    BlockDriverState *bs = NULL;
 | 
			
		||||
    VMDK4Header header;
 | 
			
		||||
    Error *local_err;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count;
 | 
			
		||||
    uint32_t *gd_buf = NULL;
 | 
			
		||||
    int gd_buf_size;
 | 
			
		||||
@@ -1700,7 +1700,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
 | 
			
		||||
{
 | 
			
		||||
    int idx = 0;
 | 
			
		||||
    BlockDriverState *new_bs = NULL;
 | 
			
		||||
    Error *local_err;
 | 
			
		||||
    Error *local_err = NULL;
 | 
			
		||||
    char *desc = NULL;
 | 
			
		||||
    int64_t total_size = 0, filesize;
 | 
			
		||||
    const char *adapter_type = NULL;
 | 
			
		||||
@@ -1881,7 +1881,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
 | 
			
		||||
    } else {
 | 
			
		||||
        ret = bdrv_create_file(filename, options, &local_err);
 | 
			
		||||
        if (ret < 0) {
 | 
			
		||||
            error_setg_errno(errp, -ret, "Could not create image file");
 | 
			
		||||
            error_propagate(errp, local_err);
 | 
			
		||||
            goto exit;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -1889,7 +1889,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options,
 | 
			
		||||
    ret = bdrv_open(&new_bs, filename, NULL, NULL,
 | 
			
		||||
                    BDRV_O_RDWR | BDRV_O_PROTOCOL, NULL, &local_err);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_setg_errno(errp, -ret, "Could not write description");
 | 
			
		||||
        error_propagate(errp, local_err);
 | 
			
		||||
        goto exit;
 | 
			
		||||
    }
 | 
			
		||||
    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->read_only =
 | 
			
		||||
		(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
 | 
			
		||||
	}
 | 
			
		||||
        } else {
 | 
			
		||||
            g_free(buffer);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    closedir(dir);
 | 
			
		||||
 | 
			
		||||
@@ -1866,7 +1868,7 @@ static int check_directory_consistency(BDRVVVFATState *s,
 | 
			
		||||
 | 
			
		||||
	if (s->used_clusters[cluster_num] & USED_ANY) {
 | 
			
		||||
	    fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
 | 
			
		||||
	    return 0;
 | 
			
		||||
            goto fail;
 | 
			
		||||
	}
 | 
			
		||||
	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:");
 | 
			
		||||
 | 
			
		||||
    ret = bdrv_create(bdrv_qcow, s->qcow_filename, options, errp);
 | 
			
		||||
    free_option_parameters(options);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        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);
 | 
			
		||||
    if (error) {
 | 
			
		||||
        error_propagate(errp, error);
 | 
			
		||||
        return NULL;
 | 
			
		||||
        goto err_no_opts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qemu_opts_absorb_qdict(opts, bs_opts, &error);
 | 
			
		||||
@@ -564,8 +564,9 @@ bdrv_new_err:
 | 
			
		||||
    g_free(dinfo->id);
 | 
			
		||||
    g_free(dinfo);
 | 
			
		||||
early_err:
 | 
			
		||||
    QDECREF(bs_opts);
 | 
			
		||||
    qemu_opts_del(opts);
 | 
			
		||||
err_no_opts:
 | 
			
		||||
    QDECREF(bs_opts);
 | 
			
		||||
    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 */
 | 
			
		||||
    dinfo = blockdev_init(filename, bs_opts, &local_err);
 | 
			
		||||
    bs_opts = NULL;
 | 
			
		||||
    if (dinfo == NULL) {
 | 
			
		||||
        if (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:
 | 
			
		||||
    qemu_opts_del(legacy_opts);
 | 
			
		||||
    QDECREF(bs_opts);
 | 
			
		||||
    return dinfo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,16 +6,20 @@ host side
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
First you must compile qemu with a user interface supporting
 | 
			
		||||
multihead/multiseat and input event routing.  Right now this list is
 | 
			
		||||
pretty short: sdl2.
 | 
			
		||||
multihead/multiseat and input event routing.  Right now this
 | 
			
		||||
list includes sdl2 and gtk (both 2+3):
 | 
			
		||||
 | 
			
		||||
  ./configure --enable-sdl --with-sdlabi=2.0
 | 
			
		||||
 | 
			
		||||
or
 | 
			
		||||
 | 
			
		||||
  ./configure --enable-gtk
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Next put together the qemu command line:
 | 
			
		||||
 | 
			
		||||
qemu	-enable-kvm -usb $memory $disk $whatever \
 | 
			
		||||
	-display sdl \
 | 
			
		||||
	-display [ sdl | gtk ] \
 | 
			
		||||
	-vga std \
 | 
			
		||||
	-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
 | 
			
		||||
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
 | 
			
		||||
----------
 | 
			
		||||
@@ -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
 | 
			
		||||
it.
 | 
			
		||||
 | 
			
		||||
Now we'll have to configure the guest.  Boot and login.  By default
 | 
			
		||||
all devices belong to seat0.  You can use "loginctl seat-status seat0"
 | 
			
		||||
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:
 | 
			
		||||
Now we'll have to configure the guest.  Boot and login.  "lspci -vt"
 | 
			
		||||
should list the pci bridge with the display adapter and usb controller:
 | 
			
		||||
 | 
			
		||||
loginctl attach seat-qemu \
 | 
			
		||||
	 /sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/drm/card1
 | 
			
		||||
loginctl attach seat-qemu \
 | 
			
		||||
	 /sys/devices/pci0000:00/0000:00:12.0/0000:01:02.0/graphics/fb1
 | 
			
		||||
loginctl attach seat-qemu \
 | 
			
		||||
	 /sys/devices/pci0000:00/0000:00:12.0/0000:01:0f.0/usb2
 | 
			
		||||
    [root@fedora ~]# lspci -vt
 | 
			
		||||
    -[0000:00]-+-00.0  Intel Corporation 440FX - 82441FX PMC [Natoma]
 | 
			
		||||
               [ ... ]
 | 
			
		||||
               \-12.0-[01]--+-02.0  Device 1234:1111
 | 
			
		||||
                            \-0f.0  NEC Corporation USB 3.0 Host Controller
 | 
			
		||||
 | 
			
		||||
Use "loginctl seat-status seat-qemu" to check the result.  It isn't
 | 
			
		||||
needed to assign the usb devices to the head individually, assigning a
 | 
			
		||||
usb (root) hub will automatically assign all usb devices connected to
 | 
			
		||||
it too.
 | 
			
		||||
Good.  Now lets tell the system that the pci bridge and all devices
 | 
			
		||||
below it belong to a separate seat by dropping a file into
 | 
			
		||||
/etc/udev/rules.d:
 | 
			
		||||
 | 
			
		||||
BTW: loginctl writes udev rules to /etc/udev/rules.d to make these
 | 
			
		||||
device assignments permanent, so you need to do this only once.
 | 
			
		||||
    [root@fedora ~]# cat /etc/udev/rules.d/70-qemu-autoseat.rules
 | 
			
		||||
    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
 | 
			
		||||
should show up on the second head.
 | 
			
		||||
Reboot.  System should come up with two seats.  With loginctl you can
 | 
			
		||||
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!
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										428
									
								
								hw/misc/vfio.c
									
									
									
									
									
								
							
							
						
						
									
										428
									
								
								hw/misc/vfio.c
									
									
									
									
									
								
							@@ -133,6 +133,15 @@ enum {
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
typedef struct VFIOType1 {
 | 
			
		||||
@@ -142,6 +151,7 @@ typedef struct VFIOType1 {
 | 
			
		||||
} VFIOType1;
 | 
			
		||||
 | 
			
		||||
typedef struct VFIOContainer {
 | 
			
		||||
    VFIOAddressSpace *space;
 | 
			
		||||
    int fd; /* /dev/vfio/vfio, empowered by the attached groups */
 | 
			
		||||
    struct {
 | 
			
		||||
        /* enable abstraction to support various iommu backends */
 | 
			
		||||
@@ -150,10 +160,18 @@ typedef struct VFIOContainer {
 | 
			
		||||
        };
 | 
			
		||||
        void (*release)(struct VFIOContainer *);
 | 
			
		||||
    } iommu_data;
 | 
			
		||||
    QLIST_HEAD(, VFIOGuestIOMMU) giommu_list;
 | 
			
		||||
    QLIST_HEAD(, VFIOGroup) group_list;
 | 
			
		||||
    QLIST_ENTRY(VFIOContainer) next;
 | 
			
		||||
} 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 */
 | 
			
		||||
typedef struct VFIOMSIXInfo {
 | 
			
		||||
    uint8_t table_bar;
 | 
			
		||||
@@ -234,9 +252,6 @@ static const VFIORomBlacklistEntry romblacklist[] = {
 | 
			
		||||
 | 
			
		||||
#define MSIX_CAP_LENGTH 12
 | 
			
		||||
 | 
			
		||||
static QLIST_HEAD(, VFIOContainer)
 | 
			
		||||
    container_list = QLIST_HEAD_INITIALIZER(container_list);
 | 
			
		||||
 | 
			
		||||
static QLIST_HEAD(, VFIOGroup)
 | 
			
		||||
    group_list = QLIST_HEAD_INITIALIZER(group_list);
 | 
			
		||||
 | 
			
		||||
@@ -1668,6 +1683,149 @@ static void vfio_probe_ati_bar4_window_quirk(VFIODevice *vdev, int nr)
 | 
			
		||||
            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.
 | 
			
		||||
 */
 | 
			
		||||
@@ -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_bar0_88000_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)
 | 
			
		||||
@@ -2232,7 +2391,8 @@ static int vfio_dma_map(VFIOContainer *container, hwaddr iova,
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
            * 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
                                     MemoryRegionSection *section)
 | 
			
		||||
{
 | 
			
		||||
    VFIOContainer *container = container_of(listener, VFIOContainer,
 | 
			
		||||
                                            iommu_data.type1.listener);
 | 
			
		||||
    hwaddr iova, end;
 | 
			
		||||
    Int128 llend;
 | 
			
		||||
    void *vaddr;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    assert(!memory_region_is_iommu(section->mr));
 | 
			
		||||
 | 
			
		||||
    if (vfio_listener_skipped_section(section)) {
 | 
			
		||||
        DPRINTF("SKIPPING region_add %"HWADDR_PRIx" - %"PRIx64"\n",
 | 
			
		||||
                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);
 | 
			
		||||
    end = (section->offset_within_address_space + int128_get64(section->size)) &
 | 
			
		||||
          TARGET_PAGE_MASK;
 | 
			
		||||
    llend = int128_make64(section->offset_within_address_space);
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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) +
 | 
			
		||||
            section->offset_within_region +
 | 
			
		||||
            (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);
 | 
			
		||||
 | 
			
		||||
    memory_region_ref(section->mr);
 | 
			
		||||
    ret = vfio_dma_map(container, iova, end - iova, vaddr, section->readonly);
 | 
			
		||||
    if (ret) {
 | 
			
		||||
        error_report("vfio_dma_map(%p, 0x%"HWADDR_PRIx", "
 | 
			
		||||
@@ -2326,6 +2588,27 @@ static void vfio_listener_region_del(MemoryListener *listener,
 | 
			
		||||
        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);
 | 
			
		||||
    end = (section->offset_within_address_space + int128_get64(section->size)) &
 | 
			
		||||
          TARGET_PAGE_MASK;
 | 
			
		||||
@@ -3274,16 +3557,43 @@ static void vfio_kvm_device_del_group(VFIOGroup *group)
 | 
			
		||||
#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;
 | 
			
		||||
    int ret, fd;
 | 
			
		||||
    VFIOAddressSpace *space;
 | 
			
		||||
 | 
			
		||||
    if (group->container) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    space = vfio_get_address_space(as);
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(container, &container_list, next) {
 | 
			
		||||
    QLIST_FOREACH(container, &space->containers, next) {
 | 
			
		||||
        if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) {
 | 
			
		||||
            group->container = container;
 | 
			
		||||
            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);
 | 
			
		||||
    if (fd < 0) {
 | 
			
		||||
        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);
 | 
			
		||||
    if (ret != VFIO_API_VERSION) {
 | 
			
		||||
        error_report("vfio: supported vfio version: %d, "
 | 
			
		||||
                     "reported version: %d", VFIO_API_VERSION, ret);
 | 
			
		||||
        close(fd);
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto close_fd_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    container = g_malloc0(sizeof(*container));
 | 
			
		||||
    container->space = space;
 | 
			
		||||
    container->fd = fd;
 | 
			
		||||
 | 
			
		||||
    if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) {
 | 
			
		||||
        ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd);
 | 
			
		||||
        if (ret) {
 | 
			
		||||
            error_report("vfio: failed to set group container: %m");
 | 
			
		||||
            g_free(container);
 | 
			
		||||
            close(fd);
 | 
			
		||||
            return -errno;
 | 
			
		||||
            ret = -errno;
 | 
			
		||||
            goto free_container_exit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ret = ioctl(fd, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU);
 | 
			
		||||
        if (ret) {
 | 
			
		||||
            error_report("vfio: failed to set iommu for container: %m");
 | 
			
		||||
            g_free(container);
 | 
			
		||||
            close(fd);
 | 
			
		||||
            return -errno;
 | 
			
		||||
            ret = -errno;
 | 
			
		||||
            goto free_container_exit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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) {
 | 
			
		||||
            ret = container->iommu_data.type1.error;
 | 
			
		||||
            vfio_listener_release(container);
 | 
			
		||||
            g_free(container);
 | 
			
		||||
            close(fd);
 | 
			
		||||
            error_report("vfio: memory listener initialization failed for container");
 | 
			
		||||
            return ret;
 | 
			
		||||
            goto listener_release_exit;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        container->iommu_data.type1.initialized = true;
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
        error_report("vfio: No available IOMMU models");
 | 
			
		||||
        g_free(container);
 | 
			
		||||
        close(fd);
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
        ret = -EINVAL;
 | 
			
		||||
        goto free_container_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QLIST_INIT(&container->group_list);
 | 
			
		||||
    QLIST_INSERT_HEAD(&container_list, container, next);
 | 
			
		||||
    QLIST_INSERT_HEAD(&space->containers, container, next);
 | 
			
		||||
 | 
			
		||||
    group->container = container;
 | 
			
		||||
    QLIST_INSERT_HEAD(&container->group_list, group, container_next);
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
@@ -3371,6 +3691,8 @@ static void vfio_disconnect_container(VFIOGroup *group)
 | 
			
		||||
    group->container = NULL;
 | 
			
		||||
 | 
			
		||||
    if (QLIST_EMPTY(&container->group_list)) {
 | 
			
		||||
        VFIOAddressSpace *space = container->space;
 | 
			
		||||
 | 
			
		||||
        if (container->iommu_data.release) {
 | 
			
		||||
            container->iommu_data.release(container);
 | 
			
		||||
        }
 | 
			
		||||
@@ -3378,10 +3700,12 @@ static void vfio_disconnect_container(VFIOGroup *group)
 | 
			
		||||
        DPRINTF("vfio_disconnect_container: close container->fd\n");
 | 
			
		||||
        close(container->fd);
 | 
			
		||||
        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;
 | 
			
		||||
    char path[32];
 | 
			
		||||
@@ -3389,7 +3713,14 @@ static VFIOGroup *vfio_get_group(int groupid)
 | 
			
		||||
 | 
			
		||||
    QLIST_FOREACH(group, &group_list, next) {
 | 
			
		||||
        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);
 | 
			
		||||
    if (group->fd < 0) {
 | 
			
		||||
        error_report("vfio: error opening %s: %m", path);
 | 
			
		||||
        g_free(group);
 | 
			
		||||
        return NULL;
 | 
			
		||||
        goto free_group_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ioctl(group->fd, VFIO_GROUP_GET_STATUS, &status)) {
 | 
			
		||||
        error_report("vfio: error getting group status: %m");
 | 
			
		||||
        close(group->fd);
 | 
			
		||||
        g_free(group);
 | 
			
		||||
        return NULL;
 | 
			
		||||
        goto close_fd_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!(status.flags & VFIO_GROUP_FLAGS_VIABLE)) {
 | 
			
		||||
        error_report("vfio: error, group %d is not viable, please ensure "
 | 
			
		||||
                     "all devices within the iommu_group are bound to their "
 | 
			
		||||
                     "vfio bus driver.", groupid);
 | 
			
		||||
        close(group->fd);
 | 
			
		||||
        g_free(group);
 | 
			
		||||
        return NULL;
 | 
			
		||||
        goto close_fd_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    group->groupid = groupid;
 | 
			
		||||
    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);
 | 
			
		||||
        close(group->fd);
 | 
			
		||||
        g_free(group);
 | 
			
		||||
        return NULL;
 | 
			
		||||
        goto close_fd_exit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (QLIST_EMPTY(&group_list)) {
 | 
			
		||||
@@ -3438,6 +3762,14 @@ static VFIOGroup *vfio_get_group(int groupid)
 | 
			
		||||
    vfio_kvm_device_add_group(group);
 | 
			
		||||
 | 
			
		||||
    return group;
 | 
			
		||||
 | 
			
		||||
close_fd_exit:
 | 
			
		||||
    close(group->fd);
 | 
			
		||||
 | 
			
		||||
free_group_exit:
 | 
			
		||||
    g_free(group);
 | 
			
		||||
 | 
			
		||||
    return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
            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) {
 | 
			
		||||
        error_report("vfio: failed to get group %d", groupid);
 | 
			
		||||
        return -ENOENT;
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,26 @@
 | 
			
		||||
#include "qemu/iov.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
void usb_pick_speed(USBPort *port)
 | 
			
		||||
{
 | 
			
		||||
    static const int speeds[] = {
 | 
			
		||||
        USB_SPEED_SUPER,
 | 
			
		||||
        USB_SPEED_HIGH,
 | 
			
		||||
        USB_SPEED_FULL,
 | 
			
		||||
        USB_SPEED_LOW,
 | 
			
		||||
    };
 | 
			
		||||
    USBDevice *udev = port->dev;
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < ARRAY_SIZE(speeds); i++) {
 | 
			
		||||
        if ((udev->speedmask & (1 << speeds[i])) &&
 | 
			
		||||
            (port->speedmask & (1 << speeds[i]))) {
 | 
			
		||||
            udev->speed = speeds[i];
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void usb_attach(USBPort *port)
 | 
			
		||||
{
 | 
			
		||||
    USBDevice *dev = port->dev;
 | 
			
		||||
@@ -35,6 +55,7 @@ void usb_attach(USBPort *port)
 | 
			
		||||
    assert(dev != NULL);
 | 
			
		||||
    assert(dev->attached);
 | 
			
		||||
    assert(dev->state == USB_STATE_NOTATTACHED);
 | 
			
		||||
    usb_pick_speed(port);
 | 
			
		||||
    port->ops->attach(port);
 | 
			
		||||
    dev->state = USB_STATE_ATTACHED;
 | 
			
		||||
    usb_device_handle_attach(dev);
 | 
			
		||||
 
 | 
			
		||||
@@ -518,18 +518,6 @@ void usb_desc_init(USBDevice *dev)
 | 
			
		||||
 | 
			
		||||
void usb_desc_attach(USBDevice *dev)
 | 
			
		||||
{
 | 
			
		||||
    const USBDesc *desc = usb_device_get_usb_desc(dev);
 | 
			
		||||
 | 
			
		||||
    assert(desc != NULL);
 | 
			
		||||
    if (desc->super && (dev->port->speedmask & USB_SPEED_MASK_SUPER)) {
 | 
			
		||||
        dev->speed = USB_SPEED_SUPER;
 | 
			
		||||
    } else if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
 | 
			
		||||
        dev->speed = USB_SPEED_HIGH;
 | 
			
		||||
    } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) {
 | 
			
		||||
        dev->speed = USB_SPEED_FULL;
 | 
			
		||||
    } else {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    usb_desc_setdefaults(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -27,87 +27,10 @@
 | 
			
		||||
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "hw/usb/ehci-regs.h"
 | 
			
		||||
#include "hw/usb/hcd-ehci.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
 | 
			
		||||
/* Capability Registers Base Address - section 2.2 */
 | 
			
		||||
#define CAPLENGTH        0x0000  /* 1-byte, 0x0001 reserved */
 | 
			
		||||
#define HCIVERSION       0x0002  /* 2-bytes, i/f version # */
 | 
			
		||||
#define HCSPARAMS        0x0004  /* 4-bytes, structural params */
 | 
			
		||||
#define HCCPARAMS        0x0008  /* 4-bytes, capability params */
 | 
			
		||||
#define EECP             HCCPARAMS + 1
 | 
			
		||||
#define HCSPPORTROUTE1   0x000c
 | 
			
		||||
#define HCSPPORTROUTE2   0x0010
 | 
			
		||||
 | 
			
		||||
#define USBCMD           0x0000
 | 
			
		||||
#define USBCMD_RUNSTOP   (1 << 0)      // run / Stop
 | 
			
		||||
#define USBCMD_HCRESET   (1 << 1)      // HC Reset
 | 
			
		||||
#define USBCMD_FLS       (3 << 2)      // Frame List Size
 | 
			
		||||
#define USBCMD_FLS_SH    2             // Frame List Size Shift
 | 
			
		||||
#define USBCMD_PSE       (1 << 4)      // Periodic Schedule Enable
 | 
			
		||||
#define USBCMD_ASE       (1 << 5)      // Asynch Schedule Enable
 | 
			
		||||
#define USBCMD_IAAD      (1 << 6)      // Int Asynch Advance Doorbell
 | 
			
		||||
#define USBCMD_LHCR      (1 << 7)      // Light Host Controller Reset
 | 
			
		||||
#define USBCMD_ASPMC     (3 << 8)      // Async Sched Park Mode Count
 | 
			
		||||
#define USBCMD_ASPME     (1 << 11)     // Async Sched Park Mode Enable
 | 
			
		||||
#define USBCMD_ITC       (0x7f << 16)  // Int Threshold Control
 | 
			
		||||
#define USBCMD_ITC_SH    16            // Int Threshold Control Shift
 | 
			
		||||
 | 
			
		||||
#define USBSTS           0x0004
 | 
			
		||||
#define USBSTS_RO_MASK   0x0000003f
 | 
			
		||||
#define USBSTS_INT       (1 << 0)      // USB Interrupt
 | 
			
		||||
#define USBSTS_ERRINT    (1 << 1)      // Error Interrupt
 | 
			
		||||
#define USBSTS_PCD       (1 << 2)      // Port Change Detect
 | 
			
		||||
#define USBSTS_FLR       (1 << 3)      // Frame List Rollover
 | 
			
		||||
#define USBSTS_HSE       (1 << 4)      // Host System Error
 | 
			
		||||
#define USBSTS_IAA       (1 << 5)      // Interrupt on Async Advance
 | 
			
		||||
#define USBSTS_HALT      (1 << 12)     // HC Halted
 | 
			
		||||
#define USBSTS_REC       (1 << 13)     // Reclamation
 | 
			
		||||
#define USBSTS_PSS       (1 << 14)     // Periodic Schedule Status
 | 
			
		||||
#define USBSTS_ASS       (1 << 15)     // Asynchronous Schedule Status
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Interrupt enable bits correspond to the interrupt active bits in USBSTS
 | 
			
		||||
 *  so no need to redefine here.
 | 
			
		||||
 */
 | 
			
		||||
#define USBINTR              0x0008
 | 
			
		||||
#define USBINTR_MASK         0x0000003f
 | 
			
		||||
 | 
			
		||||
#define FRINDEX              0x000c
 | 
			
		||||
#define CTRLDSSEGMENT        0x0010
 | 
			
		||||
#define PERIODICLISTBASE     0x0014
 | 
			
		||||
#define ASYNCLISTADDR        0x0018
 | 
			
		||||
#define ASYNCLISTADDR_MASK   0xffffffe0
 | 
			
		||||
 | 
			
		||||
#define CONFIGFLAG           0x0040
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Bits that are reserved or are read-only are masked out of values
 | 
			
		||||
 * written to us by software
 | 
			
		||||
 */
 | 
			
		||||
#define PORTSC_RO_MASK       0x007001c0
 | 
			
		||||
#define PORTSC_RWC_MASK      0x0000002a
 | 
			
		||||
#define PORTSC_WKOC_E        (1 << 22)    // Wake on Over Current Enable
 | 
			
		||||
#define PORTSC_WKDS_E        (1 << 21)    // Wake on Disconnect Enable
 | 
			
		||||
#define PORTSC_WKCN_E        (1 << 20)    // Wake on Connect Enable
 | 
			
		||||
#define PORTSC_PTC           (15 << 16)   // Port Test Control
 | 
			
		||||
#define PORTSC_PTC_SH        16           // Port Test Control shift
 | 
			
		||||
#define PORTSC_PIC           (3 << 14)    // Port Indicator Control
 | 
			
		||||
#define PORTSC_PIC_SH        14           // Port Indicator Control Shift
 | 
			
		||||
#define PORTSC_POWNER        (1 << 13)    // Port Owner
 | 
			
		||||
#define PORTSC_PPOWER        (1 << 12)    // Port Power
 | 
			
		||||
#define PORTSC_LINESTAT      (3 << 10)    // Port Line Status
 | 
			
		||||
#define PORTSC_LINESTAT_SH   10           // Port Line Status Shift
 | 
			
		||||
#define PORTSC_PRESET        (1 << 8)     // Port Reset
 | 
			
		||||
#define PORTSC_SUSPEND       (1 << 7)     // Port Suspend
 | 
			
		||||
#define PORTSC_FPRES         (1 << 6)     // Force Port Resume
 | 
			
		||||
#define PORTSC_OCC           (1 << 5)     // Over Current Change
 | 
			
		||||
#define PORTSC_OCA           (1 << 4)     // Over Current Active
 | 
			
		||||
#define PORTSC_PEDC          (1 << 3)     // Port Enable/Disable Change
 | 
			
		||||
#define PORTSC_PED           (1 << 2)     // Port Enable/Disable
 | 
			
		||||
#define PORTSC_CSC           (1 << 1)     // Connect Status Change
 | 
			
		||||
#define PORTSC_CONNECT       (1 << 0)     // Current Connect Status
 | 
			
		||||
 | 
			
		||||
#define FRAME_TIMER_FREQ 1000
 | 
			
		||||
#define FRAME_TIMER_NS   (1000000000 / FRAME_TIMER_FREQ)
 | 
			
		||||
#define UFRAME_TIMER_NS  (FRAME_TIMER_NS / 8)
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@
 | 
			
		||||
 */
 | 
			
		||||
#include "hw/hw.h"
 | 
			
		||||
#include "hw/usb.h"
 | 
			
		||||
#include "hw/usb/uhci-regs.h"
 | 
			
		||||
#include "hw/pci/pci.h"
 | 
			
		||||
#include "qemu/timer.h"
 | 
			
		||||
#include "qemu/iov.h"
 | 
			
		||||
@@ -37,41 +38,6 @@
 | 
			
		||||
//#define DEBUG
 | 
			
		||||
//#define DEBUG_DUMP_DATA
 | 
			
		||||
 | 
			
		||||
#define UHCI_CMD_FGR      (1 << 4)
 | 
			
		||||
#define UHCI_CMD_EGSM     (1 << 3)
 | 
			
		||||
#define UHCI_CMD_GRESET   (1 << 2)
 | 
			
		||||
#define UHCI_CMD_HCRESET  (1 << 1)
 | 
			
		||||
#define UHCI_CMD_RS       (1 << 0)
 | 
			
		||||
 | 
			
		||||
#define UHCI_STS_HCHALTED (1 << 5)
 | 
			
		||||
#define UHCI_STS_HCPERR   (1 << 4)
 | 
			
		||||
#define UHCI_STS_HSERR    (1 << 3)
 | 
			
		||||
#define UHCI_STS_RD       (1 << 2)
 | 
			
		||||
#define UHCI_STS_USBERR   (1 << 1)
 | 
			
		||||
#define UHCI_STS_USBINT   (1 << 0)
 | 
			
		||||
 | 
			
		||||
#define TD_CTRL_SPD     (1 << 29)
 | 
			
		||||
#define TD_CTRL_ERROR_SHIFT  27
 | 
			
		||||
#define TD_CTRL_IOS     (1 << 25)
 | 
			
		||||
#define TD_CTRL_IOC     (1 << 24)
 | 
			
		||||
#define TD_CTRL_ACTIVE  (1 << 23)
 | 
			
		||||
#define TD_CTRL_STALL   (1 << 22)
 | 
			
		||||
#define TD_CTRL_BABBLE  (1 << 20)
 | 
			
		||||
#define TD_CTRL_NAK     (1 << 19)
 | 
			
		||||
#define TD_CTRL_TIMEOUT (1 << 18)
 | 
			
		||||
 | 
			
		||||
#define UHCI_PORT_SUSPEND (1 << 12)
 | 
			
		||||
#define UHCI_PORT_RESET (1 << 9)
 | 
			
		||||
#define UHCI_PORT_LSDA  (1 << 8)
 | 
			
		||||
#define UHCI_PORT_RD    (1 << 6)
 | 
			
		||||
#define UHCI_PORT_ENC   (1 << 3)
 | 
			
		||||
#define UHCI_PORT_EN    (1 << 2)
 | 
			
		||||
#define UHCI_PORT_CSC   (1 << 1)
 | 
			
		||||
#define UHCI_PORT_CCS   (1 << 0)
 | 
			
		||||
 | 
			
		||||
#define UHCI_PORT_READ_ONLY    (0x1bb)
 | 
			
		||||
#define UHCI_PORT_WRITE_CLEAR  (UHCI_PORT_CSC | UHCI_PORT_ENC)
 | 
			
		||||
 | 
			
		||||
#define FRAME_TIMER_FREQ 1000
 | 
			
		||||
 | 
			
		||||
#define FRAME_MAX_LOOPS  256
 | 
			
		||||
 
 | 
			
		||||
@@ -498,6 +498,7 @@ typedef struct XHCIEvRingSeg {
 | 
			
		||||
enum xhci_flags {
 | 
			
		||||
    XHCI_FLAG_USE_MSI = 1,
 | 
			
		||||
    XHCI_FLAG_USE_MSI_X,
 | 
			
		||||
    XHCI_FLAG_SS_FIRST,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
 | 
			
		||||
@@ -714,10 +715,18 @@ static XHCIPort *xhci_lookup_port(XHCIState *xhci, struct USBPort *uport)
 | 
			
		||||
    case USB_SPEED_LOW:
 | 
			
		||||
    case USB_SPEED_FULL:
 | 
			
		||||
    case USB_SPEED_HIGH:
 | 
			
		||||
        index = uport->index;
 | 
			
		||||
        if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) {
 | 
			
		||||
            index = uport->index + xhci->numports_3;
 | 
			
		||||
        } else {
 | 
			
		||||
            index = uport->index;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case USB_SPEED_SUPER:
 | 
			
		||||
        index = uport->index + xhci->numports_2;
 | 
			
		||||
        if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) {
 | 
			
		||||
            index = uport->index;
 | 
			
		||||
        } else {
 | 
			
		||||
            index = uport->index + xhci->numports_2;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        return NULL;
 | 
			
		||||
@@ -2856,7 +2865,7 @@ static void xhci_port_update(XHCIPort *port, int is_detach)
 | 
			
		||||
 | 
			
		||||
static void xhci_port_reset(XHCIPort *port, bool warm_reset)
 | 
			
		||||
{
 | 
			
		||||
    trace_usb_xhci_port_reset(port->portnr);
 | 
			
		||||
    trace_usb_xhci_port_reset(port->portnr, warm_reset);
 | 
			
		||||
 | 
			
		||||
    if (!xhci_port_have_device(port)) {
 | 
			
		||||
        return;
 | 
			
		||||
@@ -2972,7 +2981,11 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
 | 
			
		||||
        ret = 0x20425355; /* "USB " */
 | 
			
		||||
        break;
 | 
			
		||||
    case 0x28: /* Supported Protocol:08 */
 | 
			
		||||
        ret = 0x00000001 | (xhci->numports_2<<8);
 | 
			
		||||
        if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) {
 | 
			
		||||
            ret = (xhci->numports_2<<8) | (xhci->numports_3+1);
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = (xhci->numports_2<<8) | 1;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 0x2c: /* Supported Protocol:0c */
 | 
			
		||||
        ret = 0x00000000; /* reserved */
 | 
			
		||||
@@ -2984,7 +2997,11 @@ static uint64_t xhci_cap_read(void *ptr, hwaddr reg, unsigned size)
 | 
			
		||||
        ret = 0x20425355; /* "USB " */
 | 
			
		||||
        break;
 | 
			
		||||
    case 0x38: /* Supported Protocol:08 */
 | 
			
		||||
        ret = 0x00000000 | (xhci->numports_2+1) | (xhci->numports_3<<8);
 | 
			
		||||
        if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) {
 | 
			
		||||
            ret = (xhci->numports_3<<8) | 1;
 | 
			
		||||
        } else {
 | 
			
		||||
            ret = (xhci->numports_3<<8) | (xhci->numports_2+1);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case 0x3c: /* Supported Protocol:0c */
 | 
			
		||||
        ret = 0x00000000; /* reserved */
 | 
			
		||||
@@ -3517,8 +3534,13 @@ static void usb_xhci_init(XHCIState *xhci)
 | 
			
		||||
    for (i = 0; i < usbports; i++) {
 | 
			
		||||
        speedmask = 0;
 | 
			
		||||
        if (i < xhci->numports_2) {
 | 
			
		||||
            port = &xhci->ports[i];
 | 
			
		||||
            port->portnr = i + 1;
 | 
			
		||||
            if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) {
 | 
			
		||||
                port = &xhci->ports[i + xhci->numports_3];
 | 
			
		||||
                port->portnr = i + 1 + xhci->numports_3;
 | 
			
		||||
            } else {
 | 
			
		||||
                port = &xhci->ports[i];
 | 
			
		||||
                port->portnr = i + 1;
 | 
			
		||||
            }
 | 
			
		||||
            port->uport = &xhci->uports[i];
 | 
			
		||||
            port->speedmask =
 | 
			
		||||
                USB_SPEED_MASK_LOW  |
 | 
			
		||||
@@ -3528,8 +3550,13 @@ static void usb_xhci_init(XHCIState *xhci)
 | 
			
		||||
            speedmask |= port->speedmask;
 | 
			
		||||
        }
 | 
			
		||||
        if (i < xhci->numports_3) {
 | 
			
		||||
            port = &xhci->ports[i + xhci->numports_2];
 | 
			
		||||
            port->portnr = i + 1 + xhci->numports_2;
 | 
			
		||||
            if (xhci_get_flag(xhci, XHCI_FLAG_SS_FIRST)) {
 | 
			
		||||
                port = &xhci->ports[i];
 | 
			
		||||
                port->portnr = i + 1;
 | 
			
		||||
            } else {
 | 
			
		||||
                port = &xhci->ports[i + xhci->numports_2];
 | 
			
		||||
                port->portnr = i + 1 + xhci->numports_2;
 | 
			
		||||
            }
 | 
			
		||||
            port->uport = &xhci->uports[i];
 | 
			
		||||
            port->speedmask = USB_SPEED_MASK_SUPER;
 | 
			
		||||
            snprintf(port->name, sizeof(port->name), "usb3 port #%d", i+1);
 | 
			
		||||
@@ -3788,6 +3815,8 @@ static const VMStateDescription vmstate_xhci = {
 | 
			
		||||
static Property xhci_properties[] = {
 | 
			
		||||
    DEFINE_PROP_BIT("msi",      XHCIState, flags, XHCI_FLAG_USE_MSI, true),
 | 
			
		||||
    DEFINE_PROP_BIT("msix",     XHCIState, flags, XHCI_FLAG_USE_MSI_X, true),
 | 
			
		||||
    DEFINE_PROP_BIT("superspeed-ports-first",
 | 
			
		||||
                    XHCIState, flags, XHCI_FLAG_SS_FIRST, true),
 | 
			
		||||
    DEFINE_PROP_UINT32("intrs", XHCIState, numintrs, MAXINTRS),
 | 
			
		||||
    DEFINE_PROP_UINT32("slots", XHCIState, numslots, MAXSLOTS),
 | 
			
		||||
    DEFINE_PROP_UINT32("p2",    XHCIState, numports_2, 4),
 | 
			
		||||
 
 | 
			
		||||
@@ -111,6 +111,7 @@ struct USBHostRequest {
 | 
			
		||||
    unsigned char                    *buffer;
 | 
			
		||||
    unsigned char                    *cbuf;
 | 
			
		||||
    unsigned int                     clen;
 | 
			
		||||
    bool                             usb3ep0quirk;
 | 
			
		||||
    QTAILQ_ENTRY(USBHostRequest)     next;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -146,6 +147,10 @@ static void usb_host_attach_kernel(USBHostDevice *s);
 | 
			
		||||
#define BULK_TIMEOUT         0        /* unlimited */
 | 
			
		||||
#define INTR_TIMEOUT         0        /* unlimited */
 | 
			
		||||
 | 
			
		||||
#if LIBUSBX_API_VERSION >= 0x01000103
 | 
			
		||||
# define HAVE_STREAMS 1
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static const char *speed_name[] = {
 | 
			
		||||
    [LIBUSB_SPEED_UNKNOWN] = "?",
 | 
			
		||||
    [LIBUSB_SPEED_LOW]     = "1.5",
 | 
			
		||||
@@ -346,6 +351,13 @@ static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer)
 | 
			
		||||
    r->p->actual_length = xfer->actual_length;
 | 
			
		||||
    if (r->in && xfer->actual_length) {
 | 
			
		||||
        memcpy(r->cbuf, r->buffer + 8, xfer->actual_length);
 | 
			
		||||
 | 
			
		||||
        /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
 | 
			
		||||
         * to work redirected to a not superspeed capable hcd */
 | 
			
		||||
        if (r->usb3ep0quirk && xfer->actual_length >= 18 &&
 | 
			
		||||
            r->cbuf[7] == 9) {
 | 
			
		||||
            r->cbuf[7] = 64;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
 | 
			
		||||
                                r->p->status, r->p->actual_length);
 | 
			
		||||
@@ -672,11 +684,17 @@ static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p)
 | 
			
		||||
 | 
			
		||||
/* ------------------------------------------------------------------------ */
 | 
			
		||||
 | 
			
		||||
static bool usb_host_full_speed_compat(USBHostDevice *s)
 | 
			
		||||
static void usb_host_speed_compat(USBHostDevice *s)
 | 
			
		||||
{
 | 
			
		||||
    USBDevice *udev = USB_DEVICE(s);
 | 
			
		||||
    struct libusb_config_descriptor *conf;
 | 
			
		||||
    const struct libusb_interface_descriptor *intf;
 | 
			
		||||
    const struct libusb_endpoint_descriptor *endp;
 | 
			
		||||
#ifdef HAVE_STREAMS
 | 
			
		||||
    struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
 | 
			
		||||
#endif
 | 
			
		||||
    bool compat_high = true;
 | 
			
		||||
    bool compat_full = true;
 | 
			
		||||
    uint8_t type;
 | 
			
		||||
    int rc, c, i, a, e;
 | 
			
		||||
 | 
			
		||||
@@ -693,10 +711,27 @@ static bool usb_host_full_speed_compat(USBHostDevice *s)
 | 
			
		||||
                    type = endp->bmAttributes & 0x3;
 | 
			
		||||
                    switch (type) {
 | 
			
		||||
                    case 0x01: /* ISO */
 | 
			
		||||
                        return false;
 | 
			
		||||
                        compat_full = false;
 | 
			
		||||
                        compat_high = false;
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 0x02: /* BULK */
 | 
			
		||||
#ifdef HAVE_STREAMS
 | 
			
		||||
                        rc = libusb_get_ss_endpoint_companion_descriptor
 | 
			
		||||
                            (ctx, endp, &endp_ss_comp);
 | 
			
		||||
                        if (rc == LIBUSB_SUCCESS) {
 | 
			
		||||
                            libusb_free_ss_endpoint_companion_descriptor
 | 
			
		||||
                                (endp_ss_comp);
 | 
			
		||||
                            compat_full = false;
 | 
			
		||||
                            compat_high = false;
 | 
			
		||||
                        }
 | 
			
		||||
#endif
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 0x03: /* INTERRUPT */
 | 
			
		||||
                        if (endp->wMaxPacketSize > 64) {
 | 
			
		||||
                            return false;
 | 
			
		||||
                            compat_full = false;
 | 
			
		||||
                        }
 | 
			
		||||
                        if (endp->wMaxPacketSize > 1024) {
 | 
			
		||||
                            compat_high = false;
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -705,7 +740,17 @@ static bool usb_host_full_speed_compat(USBHostDevice *s)
 | 
			
		||||
        }
 | 
			
		||||
        libusb_free_config_descriptor(conf);
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
 | 
			
		||||
    udev->speedmask = (1 << udev->speed);
 | 
			
		||||
    if (udev->speed == USB_SPEED_SUPER && compat_high) {
 | 
			
		||||
        udev->speedmask |= USB_SPEED_HIGH;
 | 
			
		||||
    }
 | 
			
		||||
    if (udev->speed == USB_SPEED_SUPER && compat_full) {
 | 
			
		||||
        udev->speedmask |= USB_SPEED_FULL;
 | 
			
		||||
    }
 | 
			
		||||
    if (udev->speed == USB_SPEED_HIGH && compat_full) {
 | 
			
		||||
        udev->speedmask |= USB_SPEED_FULL;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void usb_host_ep_update(USBHostDevice *s)
 | 
			
		||||
@@ -720,7 +765,7 @@ static void usb_host_ep_update(USBHostDevice *s)
 | 
			
		||||
    struct libusb_config_descriptor *conf;
 | 
			
		||||
    const struct libusb_interface_descriptor *intf;
 | 
			
		||||
    const struct libusb_endpoint_descriptor *endp;
 | 
			
		||||
#if LIBUSBX_API_VERSION >= 0x01000103
 | 
			
		||||
#ifdef HAVE_STREAMS
 | 
			
		||||
    struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
 | 
			
		||||
#endif
 | 
			
		||||
    uint8_t devep, type;
 | 
			
		||||
@@ -768,7 +813,7 @@ static void usb_host_ep_update(USBHostDevice *s)
 | 
			
		||||
            usb_ep_set_type(udev, pid, ep, type);
 | 
			
		||||
            usb_ep_set_ifnum(udev, pid, ep, i);
 | 
			
		||||
            usb_ep_set_halted(udev, pid, ep, 0);
 | 
			
		||||
#if LIBUSBX_API_VERSION >= 0x01000103
 | 
			
		||||
#ifdef HAVE_STREAMS
 | 
			
		||||
            if (type == LIBUSB_TRANSFER_TYPE_BULK &&
 | 
			
		||||
                    libusb_get_ss_endpoint_companion_descriptor(ctx, endp,
 | 
			
		||||
                        &endp_ss_comp) == LIBUSB_SUCCESS) {
 | 
			
		||||
@@ -813,10 +858,7 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev)
 | 
			
		||||
    usb_host_ep_update(s);
 | 
			
		||||
 | 
			
		||||
    udev->speed     = speed_map[libusb_get_device_speed(dev)];
 | 
			
		||||
    udev->speedmask = (1 << udev->speed);
 | 
			
		||||
    if (udev->speed == USB_SPEED_HIGH && usb_host_full_speed_compat(s)) {
 | 
			
		||||
        udev->speedmask |= USB_SPEED_MASK_FULL;
 | 
			
		||||
    }
 | 
			
		||||
    usb_host_speed_compat(s);
 | 
			
		||||
 | 
			
		||||
    if (s->ddesc.iProduct) {
 | 
			
		||||
        libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct,
 | 
			
		||||
@@ -1162,6 +1204,14 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
 | 
			
		||||
        memcpy(r->buffer + 8, r->cbuf, r->clen);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
 | 
			
		||||
     * to work redirected to a not superspeed capable hcd */
 | 
			
		||||
    if (udev->speed == USB_SPEED_SUPER &&
 | 
			
		||||
        !((udev->port->speedmask & USB_SPEED_MASK_SUPER)) &&
 | 
			
		||||
        request == 0x8006 && value == 0x100 && index == 0) {
 | 
			
		||||
        r->usb3ep0quirk = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    libusb_fill_control_transfer(r->xfer, s->dh, r->buffer,
 | 
			
		||||
                                 usb_host_req_complete_ctrl, r,
 | 
			
		||||
                                 CONTROL_TIMEOUT);
 | 
			
		||||
@@ -1215,7 +1265,7 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
 | 
			
		||||
        }
 | 
			
		||||
        ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
 | 
			
		||||
        if (p->stream) {
 | 
			
		||||
#if LIBUSBX_API_VERSION >= 0x01000103
 | 
			
		||||
#ifdef HAVE_STREAMS
 | 
			
		||||
            libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream,
 | 
			
		||||
                                             r->buffer, size,
 | 
			
		||||
                                             usb_host_req_complete_data, r,
 | 
			
		||||
@@ -1296,7 +1346,7 @@ static void usb_host_handle_reset(USBDevice *udev)
 | 
			
		||||
static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps,
 | 
			
		||||
                                  int nr_eps, int streams)
 | 
			
		||||
{
 | 
			
		||||
#if LIBUSBX_API_VERSION >= 0x01000103
 | 
			
		||||
#ifdef HAVE_STREAMS
 | 
			
		||||
    USBHostDevice *s = USB_HOST_DEVICE(udev);
 | 
			
		||||
    unsigned char endpoints[30];
 | 
			
		||||
    int i, rc;
 | 
			
		||||
@@ -1326,7 +1376,7 @@ static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps,
 | 
			
		||||
static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps,
 | 
			
		||||
                                  int nr_eps)
 | 
			
		||||
{
 | 
			
		||||
#if LIBUSBX_API_VERSION >= 0x01000103
 | 
			
		||||
#ifdef HAVE_STREAMS
 | 
			
		||||
    USBHostDevice *s = USB_HOST_DEVICE(udev);
 | 
			
		||||
    unsigned char endpoints[30];
 | 
			
		||||
    int i;
 | 
			
		||||
 
 | 
			
		||||
@@ -271,6 +271,10 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
 | 
			
		||||
            .driver   = "apic",\
 | 
			
		||||
            .property = "version",\
 | 
			
		||||
            .value    = stringify(0x11),\
 | 
			
		||||
        },{\
 | 
			
		||||
            .driver   = "nec-usb-xhci",\
 | 
			
		||||
            .property = "superspeed-ports-first",\
 | 
			
		||||
            .value    = "off",\
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#define PC_COMPAT_1_7 \
 | 
			
		||||
 
 | 
			
		||||
@@ -458,6 +458,7 @@ void usb_ep_combine_input_packets(USBEndpoint *ep);
 | 
			
		||||
void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p);
 | 
			
		||||
void usb_combined_packet_cancel(USBDevice *dev, USBPacket *p);
 | 
			
		||||
 | 
			
		||||
void usb_pick_speed(USBPort *port);
 | 
			
		||||
void usb_attach(USBPort *port);
 | 
			
		||||
void usb_detach(USBPort *port);
 | 
			
		||||
void usb_port_reset(USBPort *port);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										82
									
								
								include/hw/usb/ehci-regs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								include/hw/usb/ehci-regs.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
#ifndef HW_USB_EHCI_REGS_H
 | 
			
		||||
#define HW_USB_EHCI_REGS_H 1
 | 
			
		||||
 | 
			
		||||
/* Capability Registers Base Address - section 2.2 */
 | 
			
		||||
#define CAPLENGTH        0x0000  /* 1-byte, 0x0001 reserved */
 | 
			
		||||
#define HCIVERSION       0x0002  /* 2-bytes, i/f version # */
 | 
			
		||||
#define HCSPARAMS        0x0004  /* 4-bytes, structural params */
 | 
			
		||||
#define HCCPARAMS        0x0008  /* 4-bytes, capability params */
 | 
			
		||||
#define EECP             HCCPARAMS + 1
 | 
			
		||||
#define HCSPPORTROUTE1   0x000c
 | 
			
		||||
#define HCSPPORTROUTE2   0x0010
 | 
			
		||||
 | 
			
		||||
#define USBCMD           0x0000
 | 
			
		||||
#define USBCMD_RUNSTOP   (1 << 0)      // run / Stop
 | 
			
		||||
#define USBCMD_HCRESET   (1 << 1)      // HC Reset
 | 
			
		||||
#define USBCMD_FLS       (3 << 2)      // Frame List Size
 | 
			
		||||
#define USBCMD_FLS_SH    2             // Frame List Size Shift
 | 
			
		||||
#define USBCMD_PSE       (1 << 4)      // Periodic Schedule Enable
 | 
			
		||||
#define USBCMD_ASE       (1 << 5)      // Asynch Schedule Enable
 | 
			
		||||
#define USBCMD_IAAD      (1 << 6)      // Int Asynch Advance Doorbell
 | 
			
		||||
#define USBCMD_LHCR      (1 << 7)      // Light Host Controller Reset
 | 
			
		||||
#define USBCMD_ASPMC     (3 << 8)      // Async Sched Park Mode Count
 | 
			
		||||
#define USBCMD_ASPME     (1 << 11)     // Async Sched Park Mode Enable
 | 
			
		||||
#define USBCMD_ITC       (0x7f << 16)  // Int Threshold Control
 | 
			
		||||
#define USBCMD_ITC_SH    16            // Int Threshold Control Shift
 | 
			
		||||
 | 
			
		||||
#define USBSTS           0x0004
 | 
			
		||||
#define USBSTS_RO_MASK   0x0000003f
 | 
			
		||||
#define USBSTS_INT       (1 << 0)      // USB Interrupt
 | 
			
		||||
#define USBSTS_ERRINT    (1 << 1)      // Error Interrupt
 | 
			
		||||
#define USBSTS_PCD       (1 << 2)      // Port Change Detect
 | 
			
		||||
#define USBSTS_FLR       (1 << 3)      // Frame List Rollover
 | 
			
		||||
#define USBSTS_HSE       (1 << 4)      // Host System Error
 | 
			
		||||
#define USBSTS_IAA       (1 << 5)      // Interrupt on Async Advance
 | 
			
		||||
#define USBSTS_HALT      (1 << 12)     // HC Halted
 | 
			
		||||
#define USBSTS_REC       (1 << 13)     // Reclamation
 | 
			
		||||
#define USBSTS_PSS       (1 << 14)     // Periodic Schedule Status
 | 
			
		||||
#define USBSTS_ASS       (1 << 15)     // Asynchronous Schedule Status
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *  Interrupt enable bits correspond to the interrupt active bits in USBSTS
 | 
			
		||||
 *  so no need to redefine here.
 | 
			
		||||
 */
 | 
			
		||||
#define USBINTR              0x0008
 | 
			
		||||
#define USBINTR_MASK         0x0000003f
 | 
			
		||||
 | 
			
		||||
#define FRINDEX              0x000c
 | 
			
		||||
#define CTRLDSSEGMENT        0x0010
 | 
			
		||||
#define PERIODICLISTBASE     0x0014
 | 
			
		||||
#define ASYNCLISTADDR        0x0018
 | 
			
		||||
#define ASYNCLISTADDR_MASK   0xffffffe0
 | 
			
		||||
 | 
			
		||||
#define CONFIGFLAG           0x0040
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Bits that are reserved or are read-only are masked out of values
 | 
			
		||||
 * written to us by software
 | 
			
		||||
 */
 | 
			
		||||
#define PORTSC_RO_MASK       0x007001c0
 | 
			
		||||
#define PORTSC_RWC_MASK      0x0000002a
 | 
			
		||||
#define PORTSC_WKOC_E        (1 << 22)    // Wake on Over Current Enable
 | 
			
		||||
#define PORTSC_WKDS_E        (1 << 21)    // Wake on Disconnect Enable
 | 
			
		||||
#define PORTSC_WKCN_E        (1 << 20)    // Wake on Connect Enable
 | 
			
		||||
#define PORTSC_PTC           (15 << 16)   // Port Test Control
 | 
			
		||||
#define PORTSC_PTC_SH        16           // Port Test Control shift
 | 
			
		||||
#define PORTSC_PIC           (3 << 14)    // Port Indicator Control
 | 
			
		||||
#define PORTSC_PIC_SH        14           // Port Indicator Control Shift
 | 
			
		||||
#define PORTSC_POWNER        (1 << 13)    // Port Owner
 | 
			
		||||
#define PORTSC_PPOWER        (1 << 12)    // Port Power
 | 
			
		||||
#define PORTSC_LINESTAT      (3 << 10)    // Port Line Status
 | 
			
		||||
#define PORTSC_LINESTAT_SH   10           // Port Line Status Shift
 | 
			
		||||
#define PORTSC_PRESET        (1 << 8)     // Port Reset
 | 
			
		||||
#define PORTSC_SUSPEND       (1 << 7)     // Port Suspend
 | 
			
		||||
#define PORTSC_FPRES         (1 << 6)     // Force Port Resume
 | 
			
		||||
#define PORTSC_OCC           (1 << 5)     // Over Current Change
 | 
			
		||||
#define PORTSC_OCA           (1 << 4)     // Over Current Active
 | 
			
		||||
#define PORTSC_PEDC          (1 << 3)     // Port Enable/Disable Change
 | 
			
		||||
#define PORTSC_PED           (1 << 2)     // Port Enable/Disable
 | 
			
		||||
#define PORTSC_CSC           (1 << 1)     // Connect Status Change
 | 
			
		||||
#define PORTSC_CONNECT       (1 << 0)     // Current Connect Status
 | 
			
		||||
 | 
			
		||||
#endif /* HW_USB_EHCI_REGS_H */
 | 
			
		||||
							
								
								
									
										40
									
								
								include/hw/usb/uhci-regs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								include/hw/usb/uhci-regs.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
#ifndef HW_USB_UHCI_REGS_H
 | 
			
		||||
#define HW_USB_UHCI_REGS_H 1
 | 
			
		||||
 | 
			
		||||
#define UHCI_CMD_FGR      (1 << 4)
 | 
			
		||||
#define UHCI_CMD_EGSM     (1 << 3)
 | 
			
		||||
#define UHCI_CMD_GRESET   (1 << 2)
 | 
			
		||||
#define UHCI_CMD_HCRESET  (1 << 1)
 | 
			
		||||
#define UHCI_CMD_RS       (1 << 0)
 | 
			
		||||
 | 
			
		||||
#define UHCI_STS_HCHALTED (1 << 5)
 | 
			
		||||
#define UHCI_STS_HCPERR   (1 << 4)
 | 
			
		||||
#define UHCI_STS_HSERR    (1 << 3)
 | 
			
		||||
#define UHCI_STS_RD       (1 << 2)
 | 
			
		||||
#define UHCI_STS_USBERR   (1 << 1)
 | 
			
		||||
#define UHCI_STS_USBINT   (1 << 0)
 | 
			
		||||
 | 
			
		||||
#define TD_CTRL_SPD     (1 << 29)
 | 
			
		||||
#define TD_CTRL_ERROR_SHIFT  27
 | 
			
		||||
#define TD_CTRL_IOS     (1 << 25)
 | 
			
		||||
#define TD_CTRL_IOC     (1 << 24)
 | 
			
		||||
#define TD_CTRL_ACTIVE  (1 << 23)
 | 
			
		||||
#define TD_CTRL_STALL   (1 << 22)
 | 
			
		||||
#define TD_CTRL_BABBLE  (1 << 20)
 | 
			
		||||
#define TD_CTRL_NAK     (1 << 19)
 | 
			
		||||
#define TD_CTRL_TIMEOUT (1 << 18)
 | 
			
		||||
 | 
			
		||||
#define UHCI_PORT_SUSPEND (1 << 12)
 | 
			
		||||
#define UHCI_PORT_RESET (1 << 9)
 | 
			
		||||
#define UHCI_PORT_LSDA  (1 << 8)
 | 
			
		||||
#define UHCI_PORT_RSVD1 (1 << 7)
 | 
			
		||||
#define UHCI_PORT_RD    (1 << 6)
 | 
			
		||||
#define UHCI_PORT_ENC   (1 << 3)
 | 
			
		||||
#define UHCI_PORT_EN    (1 << 2)
 | 
			
		||||
#define UHCI_PORT_CSC   (1 << 1)
 | 
			
		||||
#define UHCI_PORT_CCS   (1 << 0)
 | 
			
		||||
 | 
			
		||||
#define UHCI_PORT_READ_ONLY    (0x1bb)
 | 
			
		||||
#define UHCI_PORT_WRITE_CLEAR  (UHCI_PORT_CSC | UHCI_PORT_ENC)
 | 
			
		||||
 | 
			
		||||
#endif /* HW_USB_UHCI_REGS_H */
 | 
			
		||||
@@ -38,6 +38,11 @@ static inline Int128 int128_2_64(void)
 | 
			
		||||
    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)
 | 
			
		||||
{
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
/* 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_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_delay(uint32_t delay_ms);
 | 
			
		||||
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_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)
 | 
			
		||||
{
 | 
			
		||||
    MemoryListener *listener;
 | 
			
		||||
 | 
			
		||||
    /* Flush out anything from MemoryListeners listening in on this */
 | 
			
		||||
    memory_region_transaction_begin();
 | 
			
		||||
    as->root = NULL;
 | 
			
		||||
    memory_region_transaction_commit();
 | 
			
		||||
    QTAILQ_REMOVE(&address_spaces, as, address_spaces_link);
 | 
			
		||||
    address_space_destroy_dispatch(as);
 | 
			
		||||
 | 
			
		||||
    QTAILQ_FOREACH(listener, &memory_listeners, link) {
 | 
			
		||||
        assert(listener->address_space_filter != as);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    flatview_unref(as->current_map);
 | 
			
		||||
    g_free(as->name);
 | 
			
		||||
    g_free(as->ioeventfds);
 | 
			
		||||
 
 | 
			
		||||
@@ -287,6 +287,7 @@ static int print_block_option_help(const char *filename, const char *fmt)
 | 
			
		||||
        proto_drv = bdrv_find_protocol(filename, true);
 | 
			
		||||
        if (!proto_drv) {
 | 
			
		||||
            error_report("Unknown protocol '%s'", filename);
 | 
			
		||||
            free_option_parameters(create_options);
 | 
			
		||||
            return 1;
 | 
			
		||||
        }
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        goto fail;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1454,7 +1453,7 @@ static int img_convert(int argc, char **argv)
 | 
			
		||||
    ret = bdrv_parse_cache_flags(cache, &flags);
 | 
			
		||||
    if (ret < 0) {
 | 
			
		||||
        error_report("Invalid cache option: %s", cache);
 | 
			
		||||
        return -1;
 | 
			
		||||
        goto out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
        fprintf(stderr, "file open already, try 'help close'\n");
 | 
			
		||||
        QDECREF(opts);
 | 
			
		||||
        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,
 | 
			
		||||
                      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_free(local_err);
 | 
			
		||||
            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)
 | 
			
		||||
            < 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_free(local_err);
 | 
			
		||||
            bdrv_unref(qemuio_bs);
 | 
			
		||||
@@ -118,6 +121,7 @@ static const cmdinfo_t open_cmd = {
 | 
			
		||||
 | 
			
		||||
static QemuOptsList empty_opts = {
 | 
			
		||||
    .name = "drive",
 | 
			
		||||
    .merge_lists = true,
 | 
			
		||||
    .head = QTAILQ_HEAD_INITIALIZER(empty_opts.head),
 | 
			
		||||
    .desc = {
 | 
			
		||||
        /* no elements => accept any params */
 | 
			
		||||
@@ -132,7 +136,7 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
 | 
			
		||||
    int growable = 0;
 | 
			
		||||
    int c;
 | 
			
		||||
    QemuOpts *qopts;
 | 
			
		||||
    QDict *opts = NULL;
 | 
			
		||||
    QDict *opts;
 | 
			
		||||
 | 
			
		||||
    while ((c = getopt(argc, argv, "snrgo:")) != EOF) {
 | 
			
		||||
        switch (c) {
 | 
			
		||||
@@ -149,15 +153,14 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
 | 
			
		||||
            growable = 1;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'o':
 | 
			
		||||
            qopts = qemu_opts_parse(&empty_opts, optarg, 0);
 | 
			
		||||
            if (qopts == NULL) {
 | 
			
		||||
            if (!qemu_opts_parse(&empty_opts, optarg, 0)) {
 | 
			
		||||
                printf("could not parse option list -- %s\n", optarg);
 | 
			
		||||
                qemu_opts_reset(&empty_opts);
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
            opts = qemu_opts_to_qdict(qopts, opts);
 | 
			
		||||
            qemu_opts_del(qopts);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            qemu_opts_reset(&empty_opts);
 | 
			
		||||
            return qemuio_command_usage(&open_cmd);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -166,11 +169,16 @@ static int open_f(BlockDriverState *bs, int argc, char **argv)
 | 
			
		||||
        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) {
 | 
			
		||||
        return openfile(argv[optind], flags, growable, opts);
 | 
			
		||||
    } else if (optind == argc) {
 | 
			
		||||
        return openfile(NULL, flags, growable, opts);
 | 
			
		||||
    } else {
 | 
			
		||||
        QDECREF(opts);
 | 
			
		||||
        return qemuio_command_usage(&open_cmd);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -154,6 +154,8 @@ gcov-files-i386-y += hw/pci-bridge/ioh3420.c
 | 
			
		||||
check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF)
 | 
			
		||||
gcov-files-i386-y += hw/usb/hcd-ehci.c
 | 
			
		||||
gcov-files-i386-y += hw/usb/hcd-uhci.c
 | 
			
		||||
gcov-files-i386-y += hw/usb/dev-hid.c
 | 
			
		||||
gcov-files-i386-y += hw/usb/dev-storage.c
 | 
			
		||||
check-qtest-x86_64-y = $(check-qtest-i386-y)
 | 
			
		||||
gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c
 | 
			
		||||
gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y))
 | 
			
		||||
@@ -319,7 +321,7 @@ tests/ac97-test$(EXESUF): tests/ac97-test.o
 | 
			
		||||
tests/es1370-test$(EXESUF): tests/es1370-test.o
 | 
			
		||||
tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o
 | 
			
		||||
tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o
 | 
			
		||||
tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o
 | 
			
		||||
tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-pc-obj-y)
 | 
			
		||||
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
 | 
			
		||||
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a libqemustub.a
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -103,7 +103,7 @@ void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value)
 | 
			
		||||
 | 
			
		||||
void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value)
 | 
			
		||||
{
 | 
			
		||||
    dev->bus->config_writew(dev->bus, dev->devfn, offset, value);
 | 
			
		||||
    dev->bus->config_writel(dev->bus, dev->devfn, offset, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,12 +9,149 @@
 | 
			
		||||
 | 
			
		||||
#include <glib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include "libqtest.h"
 | 
			
		||||
#include "libqos/pci-pc.h"
 | 
			
		||||
#include "qemu/osdep.h"
 | 
			
		||||
#include "hw/usb/uhci-regs.h"
 | 
			
		||||
#include "hw/usb/ehci-regs.h"
 | 
			
		||||
 | 
			
		||||
/* Tests only initialization so far. TODO: Replace with functional tests */
 | 
			
		||||
static void pci_nop(void)
 | 
			
		||||
struct qhc {
 | 
			
		||||
    QPCIDevice *dev;
 | 
			
		||||
    void *base;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static QPCIBus *pcibus;
 | 
			
		||||
static struct qhc uhci1;
 | 
			
		||||
static struct qhc uhci2;
 | 
			
		||||
static struct qhc uhci3;
 | 
			
		||||
static struct qhc ehci1;
 | 
			
		||||
 | 
			
		||||
/* helpers */
 | 
			
		||||
 | 
			
		||||
static void pci_init_one(struct qhc *hc, uint32_t devfn, int bar)
 | 
			
		||||
{
 | 
			
		||||
    hc->dev = qpci_device_find(pcibus, devfn);
 | 
			
		||||
    g_assert(hc->dev != NULL);
 | 
			
		||||
    qpci_device_enable(hc->dev);
 | 
			
		||||
    hc->base = qpci_iomap(hc->dev, bar);
 | 
			
		||||
    g_assert(hc->base != NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
static void uhci_port_update(struct qhc *hc, int port,
 | 
			
		||||
                             uint16_t set, uint16_t clear)
 | 
			
		||||
{
 | 
			
		||||
    void *addr = hc->base + 0x10 + 2 * port;
 | 
			
		||||
    uint16_t value;
 | 
			
		||||
 | 
			
		||||
    value = qpci_io_readw(hc->dev, addr);
 | 
			
		||||
    value |= set;
 | 
			
		||||
    value &= ~clear;
 | 
			
		||||
    qpci_io_writew(hc->dev, addr, value);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static void uhci_port_test(struct qhc *hc, int port, uint16_t expect)
 | 
			
		||||
{
 | 
			
		||||
    void *addr = hc->base + 0x10 + 2 * port;
 | 
			
		||||
    uint16_t value = qpci_io_readw(hc->dev, addr);
 | 
			
		||||
    uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1);
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    fprintf(stderr, "%s: %d, have 0x%04x, want 0x%04x\n",
 | 
			
		||||
            __func__, port, value & mask, expect & mask);
 | 
			
		||||
#endif
 | 
			
		||||
    g_assert((value & mask) == (expect & mask));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ehci_port_test(struct qhc *hc, int port, uint32_t expect)
 | 
			
		||||
{
 | 
			
		||||
    void *addr = hc->base + 0x64 + 4 * port;
 | 
			
		||||
    uint32_t value = qpci_io_readl(hc->dev, addr);
 | 
			
		||||
    uint16_t mask = ~(PORTSC_CSC | PORTSC_PEDC | PORTSC_OCC);
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    fprintf(stderr, "%s: %d, have 0x%08x, want 0x%08x\n",
 | 
			
		||||
            __func__, port, value & mask, expect & mask);
 | 
			
		||||
#endif
 | 
			
		||||
    g_assert((value & mask) == (expect & mask));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* tests */
 | 
			
		||||
 | 
			
		||||
static void pci_init(void)
 | 
			
		||||
{
 | 
			
		||||
    if (pcibus) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    pcibus = qpci_init_pc();
 | 
			
		||||
    g_assert(pcibus != NULL);
 | 
			
		||||
 | 
			
		||||
    pci_init_one(&uhci1, QPCI_DEVFN(0x1d, 0), 4);
 | 
			
		||||
    pci_init_one(&uhci2, QPCI_DEVFN(0x1d, 1), 4);
 | 
			
		||||
    pci_init_one(&uhci3, QPCI_DEVFN(0x1d, 2), 4);
 | 
			
		||||
    pci_init_one(&ehci1, QPCI_DEVFN(0x1d, 7), 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pci_uhci_port_1(void)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(pcibus != NULL);
 | 
			
		||||
 | 
			
		||||
    uhci_port_test(&uhci1, 0, UHCI_PORT_CCS); /* usb-tablet  */
 | 
			
		||||
    uhci_port_test(&uhci1, 1, UHCI_PORT_CCS); /* usb-storage */
 | 
			
		||||
    uhci_port_test(&uhci2, 0, 0);
 | 
			
		||||
    uhci_port_test(&uhci2, 1, 0);
 | 
			
		||||
    uhci_port_test(&uhci3, 0, 0);
 | 
			
		||||
    uhci_port_test(&uhci3, 1, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pci_ehci_port_1(void)
 | 
			
		||||
{
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    g_assert(pcibus != NULL);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < 6; i++) {
 | 
			
		||||
        ehci_port_test(&ehci1, i, PORTSC_POWNER | PORTSC_PPOWER);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pci_ehci_config(void)
 | 
			
		||||
{
 | 
			
		||||
    /* hands over all ports from companion uhci to ehci */
 | 
			
		||||
    qpci_io_writew(ehci1.dev, ehci1.base + 0x60, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pci_uhci_port_2(void)
 | 
			
		||||
{
 | 
			
		||||
    g_assert(pcibus != NULL);
 | 
			
		||||
 | 
			
		||||
    uhci_port_test(&uhci1, 0, 0); /* usb-tablet,  @ehci */
 | 
			
		||||
    uhci_port_test(&uhci1, 1, 0); /* usb-storage, @ehci */
 | 
			
		||||
    uhci_port_test(&uhci2, 0, 0);
 | 
			
		||||
    uhci_port_test(&uhci2, 1, 0);
 | 
			
		||||
    uhci_port_test(&uhci3, 0, 0);
 | 
			
		||||
    uhci_port_test(&uhci3, 1, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void pci_ehci_port_2(void)
 | 
			
		||||
{
 | 
			
		||||
    static uint32_t expect[] = {
 | 
			
		||||
        PORTSC_PPOWER | PORTSC_CONNECT, /* usb-tablet  */
 | 
			
		||||
        PORTSC_PPOWER | PORTSC_CONNECT, /* usb-storage */
 | 
			
		||||
        PORTSC_PPOWER,
 | 
			
		||||
        PORTSC_PPOWER,
 | 
			
		||||
        PORTSC_PPOWER,
 | 
			
		||||
        PORTSC_PPOWER,
 | 
			
		||||
    };
 | 
			
		||||
    int i;
 | 
			
		||||
 | 
			
		||||
    g_assert(pcibus != NULL);
 | 
			
		||||
 | 
			
		||||
    for (i = 0; i < 6; i++) {
 | 
			
		||||
        ehci_port_test(&ehci1, i, expect[i]);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
@@ -22,7 +159,12 @@ int main(int argc, char **argv)
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    g_test_init(&argc, &argv, NULL);
 | 
			
		||||
    qtest_add_func("/ehci/pci/nop", pci_nop);
 | 
			
		||||
    qtest_add_func("/ehci/pci/init", pci_init);
 | 
			
		||||
    qtest_add_func("/ehci/pci/uhci-port-1", pci_uhci_port_1);
 | 
			
		||||
    qtest_add_func("/ehci/pci/ehci-port-1", pci_ehci_port_1);
 | 
			
		||||
    qtest_add_func("/ehci/pci/ehci-config", pci_ehci_config);
 | 
			
		||||
    qtest_add_func("/ehci/pci/uhci-port-2", pci_uhci_port_2);
 | 
			
		||||
    qtest_add_func("/ehci/pci/ehci-port-2", pci_ehci_port_2);
 | 
			
		||||
 | 
			
		||||
    qtest_start("-machine q35 -device ich9-usb-ehci1,bus=pcie.0,addr=1d.7,"
 | 
			
		||||
                "multifunction=on,id=ich9-ehci-1 "
 | 
			
		||||
@@ -31,7 +173,10 @@ int main(int argc, char **argv)
 | 
			
		||||
                "-device ich9-usb-uhci2,bus=pcie.0,addr=1d.1,"
 | 
			
		||||
                "multifunction=on,masterbus=ich9-ehci-1.0,firstport=2 "
 | 
			
		||||
                "-device ich9-usb-uhci3,bus=pcie.0,addr=1d.2,"
 | 
			
		||||
                "multifunction=on,masterbus=ich9-ehci-1.0,firstport=4");
 | 
			
		||||
                "multifunction=on,masterbus=ich9-ehci-1.0,firstport=4 "
 | 
			
		||||
                "-drive if=none,id=usbcdrom,media=cdrom "
 | 
			
		||||
                "-device usb-tablet,bus=ich9-ehci-1.0,port=1,usb_version=1 "
 | 
			
		||||
                "-device usb-storage,bus=ich9-ehci-1.0,port=2,drive=usbcdrom ");
 | 
			
		||||
    ret = g_test_run();
 | 
			
		||||
 | 
			
		||||
    qtest_end();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								trace-events
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								trace-events
									
									
									
									
									
								
							@@ -371,7 +371,7 @@ usb_xhci_irq_msix_use(uint32_t nr) "nr %d"
 | 
			
		||||
usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d"
 | 
			
		||||
usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
 | 
			
		||||
usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
 | 
			
		||||
usb_xhci_port_reset(uint32_t port) "port %d"
 | 
			
		||||
usb_xhci_port_reset(uint32_t port, bool warm) "port %d, warm %d"
 | 
			
		||||
usb_xhci_port_link(uint32_t port, uint32_t pls) "port %d, pls %d"
 | 
			
		||||
usb_xhci_port_notify(uint32_t port, uint32_t pls) "port %d, bits %x"
 | 
			
		||||
usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
 | 
			
		||||
@@ -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_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
 | 
			
		||||
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"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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)
 | 
			
		||||
{
 | 
			
		||||
    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 */
 | 
			
		||||
            if (keycode & SHIFT) {
 | 
			
		||||
                qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
 | 
			
		||||
                qemu_input_event_send_key_delay(0);
 | 
			
		||||
            }
 | 
			
		||||
            if (keycode & CNTRL) {
 | 
			
		||||
                qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
 | 
			
		||||
                qemu_input_event_send_key_delay(0);
 | 
			
		||||
            }
 | 
			
		||||
            if (keycode & ALT) {
 | 
			
		||||
                qemu_input_event_send_key_number(NULL, ALT_CODE, true);
 | 
			
		||||
                qemu_input_event_send_key_delay(0);
 | 
			
		||||
            }
 | 
			
		||||
            if (keycode & ALTGR) {
 | 
			
		||||
                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_delay(0);
 | 
			
		||||
            qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
 | 
			
		||||
            qemu_input_event_send_key_delay(0);
 | 
			
		||||
 | 
			
		||||
            if (keycode & ALTGR) {
 | 
			
		||||
                qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
 | 
			
		||||
                qemu_input_event_send_key_delay(0);
 | 
			
		||||
            }
 | 
			
		||||
            if (keycode & ALT) {
 | 
			
		||||
                qemu_input_event_send_key_number(NULL, ALT_CODE, false);
 | 
			
		||||
                qemu_input_event_send_key_delay(0);
 | 
			
		||||
            }
 | 
			
		||||
            if (keycode & CNTRL) {
 | 
			
		||||
                qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
 | 
			
		||||
                qemu_input_event_send_key_delay(0);
 | 
			
		||||
            }
 | 
			
		||||
            if (keycode & SHIFT) {
 | 
			
		||||
                qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
 | 
			
		||||
                qemu_input_event_send_key_delay(0);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            keysym = curses2qemu[chr];
 | 
			
		||||
 
 | 
			
		||||
@@ -74,27 +74,6 @@ int index_from_key(const char *key)
 | 
			
		||||
    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)
 | 
			
		||||
{
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
        hold_time = 100;
 | 
			
		||||
        hold_time = 0; /* use default */
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (p = keys; p != NULL; p = p->next) {
 | 
			
		||||
        qemu_input_event_send_key(NULL, copy_key_value(p->value), true);
 | 
			
		||||
 | 
			
		||||
        keyvalues = g_realloc(keyvalues, sizeof(KeyValue *) *
 | 
			
		||||
                              (keyvalues_size + 1));
 | 
			
		||||
        keyvalues[keyvalues_size++] = copy_key_value(p->value);
 | 
			
		||||
        qemu_input_event_send_key_delay(hold_time);
 | 
			
		||||
    }
 | 
			
		||||
    for (p = keys; p != NULL; p = p->next) {
 | 
			
		||||
        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,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										108
									
								
								ui/input.c
									
									
									
									
									
								
							
							
						
						
									
										108
									
								
								ui/input.c
									
									
									
									
									
								
							@@ -14,11 +14,31 @@ struct QemuInputHandlerState {
 | 
			
		||||
    QemuConsole       *con;
 | 
			
		||||
    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 =
 | 
			
		||||
    QTAILQ_HEAD_INITIALIZER(handlers);
 | 
			
		||||
static NotifierList 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,
 | 
			
		||||
                                                   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)
 | 
			
		||||
{
 | 
			
		||||
    QemuInputHandlerState *s;
 | 
			
		||||
@@ -230,9 +317,14 @@ void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down)
 | 
			
		||||
{
 | 
			
		||||
    InputEvent *evt;
 | 
			
		||||
    evt = qemu_input_event_new_key(key, down);
 | 
			
		||||
    qemu_input_event_send(src, evt);
 | 
			
		||||
    qemu_input_event_sync();
 | 
			
		||||
    qapi_free_InputEvent(evt);
 | 
			
		||||
    if (QTAILQ_EMPTY(&kbd_queue)) {
 | 
			
		||||
        qemu_input_event_send(src, 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)
 | 
			
		||||
@@ -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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 *evt = g_new0(InputEvent, 1);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								ui/sdl2.c
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								ui/sdl2.c
									
									
									
									
									
								
							@@ -49,6 +49,7 @@ static struct sdl2_state {
 | 
			
		||||
    int idx;
 | 
			
		||||
    int last_vm_running; /* per console for caption reasons */
 | 
			
		||||
    int x, y;
 | 
			
		||||
    int hidden;
 | 
			
		||||
} *sdl2_console;
 | 
			
		||||
 | 
			
		||||
static SDL_Surface *guest_sprite_surface;
 | 
			
		||||
@@ -136,6 +137,9 @@ static void do_sdl_resize(struct sdl2_state *scon, int width, int height,
 | 
			
		||||
        } else {
 | 
			
		||||
            flags |= SDL_WINDOW_RESIZABLE;
 | 
			
		||||
        }
 | 
			
		||||
        if (scon->hidden) {
 | 
			
		||||
            flags |= SDL_WINDOW_HIDDEN;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        scon->real_window = SDL_CreateWindow("", 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];
 | 
			
		||||
    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) {
 | 
			
		||||
#if 0
 | 
			
		||||
    case SDL_SCANCODE_NUMLOCKCLEAR:
 | 
			
		||||
@@ -305,6 +326,11 @@ static void sdl_show_cursor(void)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
     * 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)
 | 
			
		||||
{
 | 
			
		||||
    int mod_state;
 | 
			
		||||
    int mod_state, win;
 | 
			
		||||
    struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
 | 
			
		||||
 | 
			
		||||
    if (alt_grab) {
 | 
			
		||||
@@ -473,6 +499,27 @@ static void handle_keydown(SDL_Event *ev)
 | 
			
		||||
 | 
			
		||||
    if (gui_key_modifier_pressed) {
 | 
			
		||||
        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:
 | 
			
		||||
            toggle_full_screen(scon);
 | 
			
		||||
            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)
 | 
			
		||||
{
 | 
			
		||||
    int max_x, max_y;
 | 
			
		||||
@@ -680,6 +738,9 @@ static void sdl_refresh(DisplayChangeListener *dcl)
 | 
			
		||||
        case SDL_KEYUP:
 | 
			
		||||
            handle_keyup(ev);
 | 
			
		||||
            break;
 | 
			
		||||
        case SDL_TEXTINPUT:
 | 
			
		||||
            handle_textinput(ev);
 | 
			
		||||
            break;
 | 
			
		||||
        case SDL_QUIT:
 | 
			
		||||
            if (!no_quit) {
 | 
			
		||||
                no_shutdown = 0;
 | 
			
		||||
@@ -808,7 +869,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
 | 
			
		||||
 | 
			
		||||
    for (i = 0;; i++) {
 | 
			
		||||
        QemuConsole *con = qemu_console_lookup_by_index(i);
 | 
			
		||||
        if (!con || !qemu_console_is_graphic(con)) {
 | 
			
		||||
        if (!con) {
 | 
			
		||||
            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);
 | 
			
		||||
    for (i = 0; i < sdl2_num_outputs; 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.con = con;
 | 
			
		||||
        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 ... */
 | 
			
		||||
    if (stats[0] * 33 / pixels >= 95) {
 | 
			
		||||
        return 0;
 | 
			
		||||
@@ -267,7 +271,9 @@ tight_detect_smooth_image24(VncState *vs, int w, int h)
 | 
			
		||||
                y += w;                                                 \
 | 
			
		||||
            }                                                           \
 | 
			
		||||
        }                                                               \
 | 
			
		||||
                                                                        \
 | 
			
		||||
        if (pixels == 0) {                                              \
 | 
			
		||||
            return 0;                                                   \
 | 
			
		||||
        }                                                               \
 | 
			
		||||
        if ((stats[0] + stats[1]) * 100 / pixels >= 90) {               \
 | 
			
		||||
            return 0;                                                   \
 | 
			
		||||
        }                                                               \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										63
									
								
								ui/vnc.c
									
									
									
									
									
								
							
							
						
						
									
										63
									
								
								ui/vnc.c
									
									
									
									
									
								
							@@ -26,6 +26,7 @@
 | 
			
		||||
 | 
			
		||||
#include "vnc.h"
 | 
			
		||||
#include "vnc-jobs.h"
 | 
			
		||||
#include "trace.h"
 | 
			
		||||
#include "sysemu/sysemu.h"
 | 
			
		||||
#include "qemu/sockets.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;
 | 
			
		||||
    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_delay(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int current_led_state(VncState *vs)
 | 
			
		||||
@@ -1597,6 +1600,10 @@ static void kbd_leds(void *opaque, int ledstate)
 | 
			
		||||
    int caps, num, scr;
 | 
			
		||||
    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;
 | 
			
		||||
    num  = ledstate & QEMU_NUM_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 (!vs->modifiers_state[0x45]) {
 | 
			
		||||
                trace_vnc_key_sync_numlock(true);
 | 
			
		||||
                vs->modifiers_state[0x45] = 1;
 | 
			
		||||
                press_key(vs, 0xff7f);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (vs->modifiers_state[0x45]) {
 | 
			
		||||
                trace_vnc_key_sync_numlock(false);
 | 
			
		||||
                vs->modifiers_state[0x45] = 0;
 | 
			
		||||
                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]);
 | 
			
		||||
        if (capslock) {
 | 
			
		||||
            if (uppercase == shift) {
 | 
			
		||||
                trace_vnc_key_sync_capslock(false);
 | 
			
		||||
                vs->modifiers_state[0x3a] = 0;
 | 
			
		||||
                press_key(vs, 0xffe5);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if (uppercase != shift) {
 | 
			
		||||
                trace_vnc_key_sync_capslock(true);
 | 
			
		||||
                vs->modifiers_state[0x3a] = 1;
 | 
			
		||||
                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)
 | 
			
		||||
{
 | 
			
		||||
    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;
 | 
			
		||||
    trace_vnc_key_event_map(down, sym, keycode, code2name(keycode));
 | 
			
		||||
    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)
 | 
			
		||||
{
 | 
			
		||||
    /* if the user specifies a keyboard layout, always use it */
 | 
			
		||||
    if (keyboard_layout)
 | 
			
		||||
    if (keyboard_layout) {
 | 
			
		||||
        key_event(vs, down, sym);
 | 
			
		||||
    else
 | 
			
		||||
    } else {
 | 
			
		||||
        trace_vnc_key_event_ext(down, sym, keycode, code2name(keycode));
 | 
			
		||||
        do_key_event(vs, down, keycode, sym);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void framebuffer_update_request(VncState *vs, int incremental,
 | 
			
		||||
@@ -2929,10 +2948,12 @@ void vnc_display_init(DisplayState *ds)
 | 
			
		||||
    QTAILQ_INIT(&vs->clients);
 | 
			
		||||
    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);
 | 
			
		||||
    else
 | 
			
		||||
    } else {
 | 
			
		||||
        vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!vs->kbd_layout)
 | 
			
		||||
        exit(1);
 | 
			
		||||
@@ -2976,26 +2997,6 @@ static void vnc_display_close(DisplayState *ds)
 | 
			
		||||
#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)
 | 
			
		||||
{
 | 
			
		||||
    VncDisplay *vs = vnc_display;
 | 
			
		||||
@@ -3003,20 +3004,18 @@ int vnc_display_password(DisplayState *ds, const char *password)
 | 
			
		||||
    if (!vs) {
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!password) {
 | 
			
		||||
        /* This is not the intention of this interface but err on the side
 | 
			
		||||
           of being safe */
 | 
			
		||||
        return vnc_display_disable_login(ds);
 | 
			
		||||
    if (vs->auth == VNC_AUTH_NONE) {
 | 
			
		||||
        error_printf_unless_qmp("If you want use passwords please enable "
 | 
			
		||||
                                "password auth using '-vnc ${dpy},password'.");
 | 
			
		||||
        return -EINVAL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (vs->password) {
 | 
			
		||||
        g_free(vs->password);
 | 
			
		||||
        vs->password = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    vs->password = g_strdup(password);
 | 
			
		||||
    if (vs->auth == VNC_AUTH_NONE) {
 | 
			
		||||
        vs->auth = VNC_AUTH_VNC;
 | 
			
		||||
    if (password) {
 | 
			
		||||
        vs->password = g_strdup(password);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user