mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-24 14:45:24 +00:00
Add "redirdev" redirection device
- create a new "redirdev" element for this purpose
This commit is contained in:
parent
fdd14a9d05
commit
162efa1a7c
@ -1387,6 +1387,45 @@
|
||||
not used by qemu.</dd>
|
||||
</dl>
|
||||
|
||||
<h4><a name="elementsRedir">Redirected devices</a></h4>
|
||||
|
||||
<p>
|
||||
USB device redirection through a character device is
|
||||
supported <span class="since">since after 0.9.5 (KVM
|
||||
only)</span>:
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
...
|
||||
<devices>
|
||||
<redirdev bus='usb' type='tcp'>
|
||||
<source mode='connect' host='localhost' service='4000'/>
|
||||
</redirdev>
|
||||
</devices>
|
||||
...</pre>
|
||||
|
||||
<dl>
|
||||
<dt><code>redirdev</code></dt>
|
||||
<dd>The <code>redirdev</code> element is the main container for
|
||||
describing redirected devices. <code>bus</code> must be "usb"
|
||||
for a USB device.
|
||||
|
||||
An additional attribute <code>type</code> is required,
|
||||
matching one of the
|
||||
supported <a href="#elementsConsole">serial device</a> types,
|
||||
to describe the host side of the
|
||||
tunnel; <code>type='tcp'</code>
|
||||
or <code>type='spicevmc'</code> (which uses the usbredir
|
||||
channel of a <a href="#elementsGraphics">SPICE graphics
|
||||
device</a>) are typical. Further sub-elements, such
|
||||
as <code><source></code>, may be required according to
|
||||
the given type, although a <code><target></code>
|
||||
sub-element is not required (since the consumer of the
|
||||
character device is the hypervisor itself, rather than a
|
||||
device visible in the guest).</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
<h4><a name="elementsSmartcard">Smartcard devices</a></h4>
|
||||
|
||||
<p>
|
||||
|
@ -1724,21 +1724,25 @@
|
||||
</element>
|
||||
</define>
|
||||
|
||||
<define name="qemucdevSrcTypeChoice">
|
||||
<choice>
|
||||
<value>dev</value>
|
||||
<value>file</value>
|
||||
<value>pipe</value>
|
||||
<value>unix</value>
|
||||
<value>tcp</value>
|
||||
<value>udp</value>
|
||||
<value>null</value>
|
||||
<value>stdio</value>
|
||||
<value>vc</value>
|
||||
<value>pty</value>
|
||||
<value>spicevmc</value>
|
||||
</choice>
|
||||
</define>
|
||||
|
||||
<define name="qemucdevSrcType">
|
||||
<attribute name="type">
|
||||
<choice>
|
||||
<value>dev</value>
|
||||
<value>file</value>
|
||||
<value>pipe</value>
|
||||
<value>unix</value>
|
||||
<value>tcp</value>
|
||||
<value>udp</value>
|
||||
<value>null</value>
|
||||
<value>stdio</value>
|
||||
<value>vc</value>
|
||||
<value>pty</value>
|
||||
<value>spicevmc</value>
|
||||
</choice>
|
||||
<ref name="qemucdevSrcTypeChoice"/>
|
||||
</attribute>
|
||||
</define>
|
||||
<define name="qemucdevSrcDef">
|
||||
@ -1975,6 +1979,19 @@
|
||||
</optional>
|
||||
</element>
|
||||
</define>
|
||||
<define name="redirdev">
|
||||
<element name="redirdev">
|
||||
<attribute name="bus">
|
||||
<choice>
|
||||
<value>usb</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
<attribute name="type">
|
||||
<ref name="qemucdevSrcTypeChoice"/>
|
||||
</attribute>
|
||||
<ref name="qemucdevSrcDef"/>
|
||||
</element>
|
||||
</define>
|
||||
<define name="hostdev">
|
||||
<element name="hostdev">
|
||||
<optional>
|
||||
@ -2139,6 +2156,7 @@
|
||||
<ref name="channel"/>
|
||||
<ref name="smartcard"/>
|
||||
<ref name="hub"/>
|
||||
<ref name="redirdev"/>
|
||||
</choice>
|
||||
</zeroOrMore>
|
||||
<optional>
|
||||
|
@ -308,6 +308,66 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virDomainAuditRedirdev:
|
||||
* @vm: domain making a change in pass-through host device
|
||||
* @redirdev: device being attached or removed
|
||||
* @reason: one of "start", "attach", or "detach"
|
||||
* @success: true if the device passthrough operation succeeded
|
||||
*
|
||||
* Log an audit message about an attempted device passthrough change.
|
||||
*/
|
||||
void
|
||||
virDomainAuditRedirdev(virDomainObjPtr vm, virDomainRedirdevDefPtr redirdev,
|
||||
const char *reason, bool success)
|
||||
{
|
||||
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
||||
char *vmname;
|
||||
char *address;
|
||||
char *device;
|
||||
const char *virt;
|
||||
|
||||
virUUIDFormat(vm->def->uuid, uuidstr);
|
||||
if (!(vmname = virAuditEncode("vm", vm->def->name))) {
|
||||
VIR_WARN("OOM while encoding audit message");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) {
|
||||
VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType);
|
||||
virt = "?";
|
||||
}
|
||||
|
||||
switch (redirdev->bus) {
|
||||
case VIR_DOMAIN_REDIRDEV_BUS_USB:
|
||||
if (virAsprintf(&address, "USB redirdev") < 0) {
|
||||
VIR_WARN("OOM while encoding audit message");
|
||||
goto cleanup;
|
||||
}
|
||||
default:
|
||||
VIR_WARN("Unexpected redirdev bus while encoding audit message: %d",
|
||||
redirdev->bus);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!(device = virAuditEncode("device", VIR_AUDIT_STR(address)))) {
|
||||
VIR_WARN("OOM while encoding audit message");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success,
|
||||
"virt=%s resrc=dev reason=%s %s uuid=%s bus=%s %s",
|
||||
virt, reason, vmname, uuidstr,
|
||||
virDomainRedirdevBusTypeToString(redirdev->bus),
|
||||
device);
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(vmname);
|
||||
VIR_FREE(device);
|
||||
VIR_FREE(address);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virDomainAuditCgroup:
|
||||
* @vm: domain making the cgroups ACL change
|
||||
@ -538,6 +598,11 @@ virDomainAuditStart(virDomainObjPtr vm, const char *reason, bool success)
|
||||
virDomainAuditHostdev(vm, hostdev, "start", true);
|
||||
}
|
||||
|
||||
for (i = 0 ; i < vm->def->nredirdevs ; i++) {
|
||||
virDomainRedirdevDefPtr redirdev = vm->def->redirdevs[i];
|
||||
virDomainAuditRedirdev(vm, redirdev, "start", true);
|
||||
}
|
||||
|
||||
virDomainAuditMemory(vm, 0, vm->def->mem.cur_balloon, "start", true);
|
||||
virDomainAuditVcpu(vm, 0, vm->def->vcpus, "start", true);
|
||||
|
||||
|
@ -101,5 +101,10 @@ void virDomainAuditVcpu(virDomainObjPtr vm,
|
||||
void virDomainAuditSecurityLabel(virDomainObjPtr vm,
|
||||
bool success)
|
||||
ATTRIBUTE_NONNULL(1);
|
||||
void virDomainAuditRedirdev(virDomainObjPtr vm,
|
||||
virDomainRedirdevDefPtr def,
|
||||
const char *reason,
|
||||
bool success)
|
||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
|
||||
|
||||
#endif /* __VIR_DOMAIN_AUDIT_H__ */
|
||||
|
@ -127,7 +127,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST,
|
||||
"watchdog",
|
||||
"controller",
|
||||
"graphics",
|
||||
"hub")
|
||||
"hub",
|
||||
"redirdev")
|
||||
|
||||
VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
|
||||
"none",
|
||||
@ -441,6 +442,9 @@ VIR_ENUM_IMPL(virDomainState, VIR_DOMAIN_CRASHED+1,
|
||||
VIR_ENUM_IMPL(virDomainHub, VIR_DOMAIN_HUB_TYPE_LAST,
|
||||
"usb")
|
||||
|
||||
VIR_ENUM_IMPL(virDomainRedirdevBus, VIR_DOMAIN_REDIRDEV_BUS_LAST,
|
||||
"usb")
|
||||
|
||||
#define VIR_DOMAIN_NOSTATE_LAST (VIR_DOMAIN_NOSTATE_UNKNOWN + 1)
|
||||
VIR_ENUM_IMPL(virDomainNostateReason, VIR_DOMAIN_NOSTATE_LAST,
|
||||
"unknown")
|
||||
@ -1013,6 +1017,17 @@ void virDomainHubDefFree(virDomainHubDefPtr def)
|
||||
VIR_FREE(def);
|
||||
}
|
||||
|
||||
void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def)
|
||||
{
|
||||
if (!def)
|
||||
return;
|
||||
|
||||
virDomainChrSourceDefClear(&def->source.chr);
|
||||
virDomainDeviceInfoClear(&def->info);
|
||||
|
||||
VIR_FREE(def);
|
||||
}
|
||||
|
||||
void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
|
||||
{
|
||||
if (!def)
|
||||
@ -1052,6 +1067,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
|
||||
case VIR_DOMAIN_DEVICE_HUB:
|
||||
virDomainHubDefFree(def->data.hub);
|
||||
break;
|
||||
case VIR_DOMAIN_DEVICE_REDIRDEV:
|
||||
virDomainRedirdevDefFree(def->data.redirdev);
|
||||
break;
|
||||
}
|
||||
|
||||
VIR_FREE(def);
|
||||
@ -5344,7 +5362,6 @@ virDomainHostdevDefParseXML(const xmlNodePtr node,
|
||||
virBitmapPtr bootMap,
|
||||
unsigned int flags)
|
||||
{
|
||||
|
||||
xmlNodePtr cur;
|
||||
virDomainHostdevDefPtr def;
|
||||
char *mode, *type = NULL, *managed = NULL;
|
||||
@ -5391,8 +5408,8 @@ virDomainHostdevDefParseXML(const xmlNodePtr node,
|
||||
if (xmlStrEqual(cur->name, BAD_CAST "source")) {
|
||||
if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
|
||||
def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
|
||||
if (virDomainHostdevSubsysUsbDefParseXML(cur, def) < 0)
|
||||
goto error;
|
||||
if (virDomainHostdevSubsysUsbDefParseXML(cur, def) < 0)
|
||||
goto error;
|
||||
}
|
||||
if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
|
||||
def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
|
||||
@ -5445,6 +5462,68 @@ error:
|
||||
}
|
||||
|
||||
|
||||
static virDomainRedirdevDefPtr
|
||||
virDomainRedirdevDefParseXML(const xmlNodePtr node,
|
||||
unsigned int flags)
|
||||
{
|
||||
xmlNodePtr cur;
|
||||
virDomainRedirdevDefPtr def;
|
||||
char *bus, *type = NULL;
|
||||
|
||||
if (VIR_ALLOC(def) < 0) {
|
||||
virReportOOMError();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bus = virXMLPropString(node, "bus");
|
||||
if (bus) {
|
||||
if ((def->bus = virDomainRedirdevBusTypeFromString(bus)) < 0) {
|
||||
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("unknown redirdev bus '%s'"), bus);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
def->bus = VIR_DOMAIN_REDIRDEV_BUS_USB;
|
||||
}
|
||||
|
||||
type = virXMLPropString(node, "type");
|
||||
if (type) {
|
||||
if ((def->source.chr.type = virDomainChrTypeFromString(type)) < 0) {
|
||||
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
_("unknown redirdev character device type '%s'"), type);
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
|
||||
"%s", _("missing type in redirdev"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
cur = node->children;
|
||||
while (cur != NULL) {
|
||||
if (cur->type == XML_ELEMENT_NODE) {
|
||||
if (xmlStrEqual(cur->name, BAD_CAST "source")) {
|
||||
int remaining;
|
||||
|
||||
remaining = virDomainChrSourceDefParseXML(&def->source.chr, cur, flags);
|
||||
if (remaining != 0)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
VIR_FREE(bus);
|
||||
VIR_FREE(type);
|
||||
return def;
|
||||
|
||||
error:
|
||||
virDomainRedirdevDefFree(def);
|
||||
def = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
static int virDomainLifecycleParseXML(xmlXPathContextPtr ctxt,
|
||||
const char *xpath,
|
||||
int *val,
|
||||
@ -5650,6 +5729,10 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps,
|
||||
dev->type = VIR_DOMAIN_DEVICE_HUB;
|
||||
if (!(dev->data.hub = virDomainHubDefParseXML(node, flags)))
|
||||
goto error;
|
||||
} else if (xmlStrEqual(node->name, BAD_CAST "redirdev")) {
|
||||
dev->type = VIR_DOMAIN_DEVICE_REDIRDEV;
|
||||
if (!(dev->data.redirdev = virDomainRedirdevDefParseXML(node, flags)))
|
||||
goto error;
|
||||
} else {
|
||||
virDomainReportError(VIR_ERR_XML_ERROR,
|
||||
"%s", _("unknown device type"));
|
||||
@ -7069,6 +7152,22 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
|
||||
}
|
||||
VIR_FREE(nodes);
|
||||
|
||||
/* analysis of the redirected devices */
|
||||
if ((n = virXPathNodeSet("./devices/redirdev", ctxt, &nodes)) < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (n && VIR_ALLOC_N(def->redirdevs, n) < 0)
|
||||
goto no_memory;
|
||||
for (i = 0 ; i < n ; i++) {
|
||||
virDomainRedirdevDefPtr redirdev = virDomainRedirdevDefParseXML(nodes[i],
|
||||
flags);
|
||||
if (!redirdev)
|
||||
goto error;
|
||||
|
||||
def->redirdevs[def->nredirdevs++] = redirdev;
|
||||
}
|
||||
VIR_FREE(nodes);
|
||||
|
||||
/* analysis of security label */
|
||||
if (virSecurityLabelDefParseXML(def, ctxt, flags) == -1)
|
||||
goto error;
|
||||
@ -10158,6 +10257,22 @@ virDomainHostdevDefFormat(virBufferPtr buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
virDomainRedirdevDefFormat(virBufferPtr buf,
|
||||
virDomainRedirdevDefPtr def,
|
||||
unsigned int flags)
|
||||
{
|
||||
const char *bus;
|
||||
|
||||
bus = virDomainRedirdevBusTypeToString(def->bus);
|
||||
|
||||
virBufferAsprintf(buf, " <redirdev bus='%s'", bus);
|
||||
if (virDomainChrSourceDefFormat(buf, &def->source.chr, false, flags) < 0)
|
||||
return -1;
|
||||
virBufferAddLit(buf, " </redirdev>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
virDomainHubDefFormat(virBufferPtr buf,
|
||||
@ -10592,6 +10707,10 @@ virDomainDefFormatInternal(virDomainDefPtr def,
|
||||
if (virDomainHostdevDefFormat(&buf, def->hostdevs[n], flags) < 0)
|
||||
goto cleanup;
|
||||
|
||||
for (n = 0 ; n < def->nredirdevs ; n++)
|
||||
if (virDomainRedirdevDefFormat(&buf, def->redirdevs[n], flags) < 0)
|
||||
goto cleanup;
|
||||
|
||||
for (n = 0 ; n < def->nhubs ; n++)
|
||||
if (virDomainHubDefFormat(&buf, def->hubs[n], flags) < 0)
|
||||
goto cleanup;
|
||||
|
@ -939,6 +939,23 @@ struct _virDomainHostdevDef {
|
||||
virDomainDeviceInfo info; /* Guest address */
|
||||
};
|
||||
|
||||
enum virDomainRedirdevBus {
|
||||
VIR_DOMAIN_REDIRDEV_BUS_USB,
|
||||
|
||||
VIR_DOMAIN_REDIRDEV_BUS_LAST
|
||||
};
|
||||
|
||||
typedef struct _virDomainRedirdevDef virDomainRedirdevDef;
|
||||
typedef virDomainRedirdevDef *virDomainRedirdevDefPtr;
|
||||
struct _virDomainRedirdevDef {
|
||||
int bus; /* enum virDomainRedirdevBus */
|
||||
|
||||
union {
|
||||
virDomainChrSourceDef chr;
|
||||
} source;
|
||||
|
||||
virDomainDeviceInfo info; /* Guest address */
|
||||
};
|
||||
|
||||
enum {
|
||||
VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO,
|
||||
@ -979,6 +996,7 @@ enum virDomainDeviceType {
|
||||
VIR_DOMAIN_DEVICE_CONTROLLER,
|
||||
VIR_DOMAIN_DEVICE_GRAPHICS,
|
||||
VIR_DOMAIN_DEVICE_HUB,
|
||||
VIR_DOMAIN_DEVICE_REDIRDEV,
|
||||
|
||||
VIR_DOMAIN_DEVICE_LAST,
|
||||
};
|
||||
@ -1000,6 +1018,7 @@ struct _virDomainDeviceDef {
|
||||
virDomainWatchdogDefPtr watchdog;
|
||||
virDomainGraphicsDefPtr graphics;
|
||||
virDomainHubDefPtr hub;
|
||||
virDomainRedirdevDefPtr redirdev;
|
||||
} data;
|
||||
};
|
||||
|
||||
@ -1312,6 +1331,9 @@ struct _virDomainDef {
|
||||
int nhostdevs;
|
||||
virDomainHostdevDefPtr *hostdevs;
|
||||
|
||||
int nredirdevs;
|
||||
virDomainRedirdevDefPtr *redirdevs;
|
||||
|
||||
int nsmartcards;
|
||||
virDomainSmartcardDefPtr *smartcards;
|
||||
|
||||
@ -1479,6 +1501,7 @@ void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def);
|
||||
void virDomainVideoDefFree(virDomainVideoDefPtr def);
|
||||
void virDomainHostdevDefFree(virDomainHostdevDefPtr def);
|
||||
void virDomainHubDefFree(virDomainHubDefPtr def);
|
||||
void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def);
|
||||
void virDomainDeviceDefFree(virDomainDeviceDefPtr def);
|
||||
int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
|
||||
int type);
|
||||
@ -1760,6 +1783,7 @@ VIR_ENUM_DECL(virDomainVideo)
|
||||
VIR_ENUM_DECL(virDomainHostdevMode)
|
||||
VIR_ENUM_DECL(virDomainHostdevSubsys)
|
||||
VIR_ENUM_DECL(virDomainHub)
|
||||
VIR_ENUM_DECL(virDomainRedirdevBus)
|
||||
VIR_ENUM_DECL(virDomainInput)
|
||||
VIR_ENUM_DECL(virDomainInputBus)
|
||||
VIR_ENUM_DECL(virDomainGraphics)
|
||||
|
@ -219,6 +219,7 @@ virDomainAuditHostdev;
|
||||
virDomainAuditMemory;
|
||||
virDomainAuditNet;
|
||||
virDomainAuditNetDevice;
|
||||
virDomainAuditRedirdev;
|
||||
virDomainAuditSecurityLabel;
|
||||
virDomainAuditStart;
|
||||
virDomainAuditStop;
|
||||
@ -375,6 +376,8 @@ virDomainObjSetState;
|
||||
virDomainObjTaint;
|
||||
virDomainObjUnlock;
|
||||
virDomainObjUnref;
|
||||
virDomainRedirdevBusTypeFromString;
|
||||
virDomainRedirdevBusTypeToString;
|
||||
virDomainRemoveInactive;
|
||||
virDomainSaveConfig;
|
||||
virDomainSaveStatus;
|
||||
|
@ -606,6 +606,33 @@ qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx)
|
||||
{
|
||||
if (idx == -1) {
|
||||
int i;
|
||||
idx = 0;
|
||||
for (i = 0 ; i < def->nredirdevs ; i++) {
|
||||
int thisidx;
|
||||
if ((thisidx = qemuDomainDeviceAliasIndex(&def->redirdevs[i]->info, "redir")) < 0) {
|
||||
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
||||
_("Unable to determine device index for redirected device"));
|
||||
return -1;
|
||||
}
|
||||
if (thisidx >= idx)
|
||||
idx = thisidx + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (virAsprintf(&redirdev->info.alias, "redir%d", idx) < 0) {
|
||||
virReportOOMError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller)
|
||||
{
|
||||
@ -653,6 +680,10 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virBitmapPtr qemuCaps)
|
||||
if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0)
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < def->nredirdevs ; i++) {
|
||||
if (qemuAssignDeviceRedirdevAlias(def, def->redirdevs[i], i) < 0)
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < def->nvideos ; i++) {
|
||||
if (virAsprintf(&def->videos[i]->info.alias, "video%d", i) < 0)
|
||||
goto no_memory;
|
||||
@ -2348,6 +2379,45 @@ qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev)
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
qemuBuildRedirdevDevStr(virDomainRedirdevDefPtr dev,
|
||||
virBitmapPtr qemuCaps)
|
||||
{
|
||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||
|
||||
if (dev->bus != VIR_DOMAIN_REDIRDEV_BUS_USB) {
|
||||
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("Redirection bus %s is not supported by QEMU"),
|
||||
virDomainRedirdevBusTypeToString(dev->bus));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!qemuCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR)) {
|
||||
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||
_("USB redirection is not supported "
|
||||
"by this version of QEMU"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
virBufferAsprintf(&buf, "usb-redir,chardev=char%s,id=%s",
|
||||
dev->info.alias,
|
||||
dev->info.alias);
|
||||
|
||||
if (qemuBuildDeviceAddressStr(&buf, &dev->info, qemuCaps) < 0)
|
||||
goto error;
|
||||
|
||||
if (virBufferError(&buf)) {
|
||||
virReportOOMError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
return virBufferContentAndReset(&buf);
|
||||
|
||||
error:
|
||||
virBufferFreeAndReset(&buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
|
||||
virBitmapPtr qemuCaps)
|
||||
@ -4839,6 +4909,32 @@ qemuBuildCommandLine(virConnectPtr conn,
|
||||
virCommandAddArgList(cmd, "-watchdog-action", action, NULL);
|
||||
}
|
||||
|
||||
/* Add redirected devices */
|
||||
for (i = 0 ; i < def->nredirdevs ; i++) {
|
||||
virDomainRedirdevDefPtr redirdev = def->redirdevs[i];
|
||||
char *devstr;
|
||||
|
||||
virCommandAddArg(cmd, "-chardev");
|
||||
if (!(devstr = qemuBuildChrChardevStr(&redirdev->source.chr,
|
||||
redirdev->info.alias,
|
||||
qemuCaps))) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
virCommandAddArg(cmd, devstr);
|
||||
VIR_FREE(devstr);
|
||||
|
||||
if (!qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
|
||||
goto error;
|
||||
|
||||
virCommandAddArg(cmd, "-device");
|
||||
if (!(devstr = qemuBuildRedirdevDevStr(redirdev, qemuCaps)))
|
||||
goto error;
|
||||
virCommandAddArg(cmd, devstr);
|
||||
VIR_FREE(devstr);
|
||||
}
|
||||
|
||||
|
||||
/* Add host passthrough hardware */
|
||||
for (i = 0 ; i < def->nhostdevs ; i++) {
|
||||
virDomainHostdevDefPtr hostdev = def->hostdevs[i];
|
||||
|
@ -120,6 +120,7 @@ char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev,
|
||||
virBitmapPtr qemuCaps);
|
||||
|
||||
char * qemuBuildHubDevStr(virDomainHubDefPtr dev, virBitmapPtr qemuCaps);
|
||||
char * qemuBuildRedirdevDevStr(virDomainRedirdevDefPtr dev, virBitmapPtr qemuCaps);
|
||||
|
||||
|
||||
int qemuNetworkIfaceConnect(virDomainDefPtr def,
|
||||
@ -189,8 +190,9 @@ int qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr ad
|
||||
int qemuDomainNetVLAN(virDomainNetDefPtr def);
|
||||
int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx);
|
||||
int qemuAssignDeviceDiskAlias(virDomainDiskDefPtr def, virBitmapPtr qemuCaps);
|
||||
int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr net, int idx);
|
||||
int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int idx);
|
||||
int qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller);
|
||||
int qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx);
|
||||
|
||||
int
|
||||
qemuParseKeywords(const char *str,
|
||||
|
@ -4978,6 +4978,13 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm,
|
||||
dev->data.hostdev = NULL;
|
||||
break;
|
||||
|
||||
case VIR_DOMAIN_DEVICE_REDIRDEV:
|
||||
ret = qemuDomainAttachRedirdevDevice(driver, vm,
|
||||
dev->data.redirdev);
|
||||
if (!ret)
|
||||
dev->data.redirdev = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
||||
_("device type '%s' cannot be attached"),
|
||||
|
@ -911,6 +911,49 @@ error:
|
||||
}
|
||||
|
||||
|
||||
int qemuDomainAttachRedirdevDevice(struct qemud_driver *driver,
|
||||
virDomainObjPtr vm,
|
||||
virDomainRedirdevDefPtr redirdev)
|
||||
{
|
||||
int ret;
|
||||
qemuDomainObjPrivatePtr priv = vm->privateData;
|
||||
char *devstr = NULL;
|
||||
|
||||
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
|
||||
if (qemuAssignDeviceRedirdevAlias(vm->def, redirdev, -1) < 0)
|
||||
goto error;
|
||||
if (!(devstr = qemuBuildRedirdevDevStr(redirdev, priv->qemuCaps)))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (VIR_REALLOC_N(vm->def->redirdevs, vm->def->nredirdevs+1) < 0) {
|
||||
virReportOOMError();
|
||||
goto error;
|
||||
}
|
||||
|
||||
qemuDomainObjEnterMonitorWithDriver(driver, vm);
|
||||
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE))
|
||||
ret = qemuMonitorAddDevice(priv->mon, devstr);
|
||||
else
|
||||
goto error;
|
||||
|
||||
qemuDomainObjExitMonitorWithDriver(driver, vm);
|
||||
virDomainAuditRedirdev(vm, redirdev, "attach", ret == 0);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
vm->def->redirdevs[vm->def->nredirdevs++] = redirdev;
|
||||
|
||||
VIR_FREE(devstr);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
VIR_FREE(devstr);
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
|
||||
virDomainObjPtr vm,
|
||||
virDomainHostdevDefPtr hostdev)
|
||||
|
@ -53,6 +53,9 @@ int qemuDomainAttachHostPciDevice(struct qemud_driver *driver,
|
||||
int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver,
|
||||
virDomainObjPtr vm,
|
||||
virDomainHostdevDefPtr hostdev);
|
||||
int qemuDomainAttachRedirdevDevice(struct qemud_driver *driver,
|
||||
virDomainObjPtr vm,
|
||||
virDomainRedirdevDefPtr hostdev);
|
||||
int qemuDomainAttachHostDevice(struct qemud_driver *driver,
|
||||
virDomainObjPtr vm,
|
||||
virDomainHostdevDefPtr hostdev);
|
||||
|
8
tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args
Normal file
8
tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args
Normal file
@ -0,0 +1,8 @@
|
||||
LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -chardev socket,id=charmonitor,path=/tmp/test-monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c \
|
||||
-device ich9-usb-ehci1,id=usb,bus=pci.0,multifunction=on,addr=0x4.0x7 \
|
||||
-device ich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4.0x0 \
|
||||
-device ich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pci.0,multifunction=on,addr=0x4.0x1 \
|
||||
-device ich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pci.0,multifunction=on,addr=0x4.0x2 \
|
||||
-chardev socket,id=charredir0,host=localhost,port=4000 \
|
||||
-device usb-redir,chardev=charredir0,id=redir0 \
|
||||
-device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0
|
38
tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml
Normal file
38
tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<domain type='qemu'>
|
||||
<name>QEMUGuest1</name>
|
||||
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
|
||||
<memory>219136</memory>
|
||||
<currentMemory>219200</currentMemory>
|
||||
<vcpu>1</vcpu>
|
||||
<os>
|
||||
<type arch='i686' machine='pc'>hvm</type>
|
||||
<boot dev='hd'/>
|
||||
</os>
|
||||
<clock offset='utc'/>
|
||||
<on_poweroff>destroy</on_poweroff>
|
||||
<on_reboot>restart</on_reboot>
|
||||
<on_crash>destroy</on_crash>
|
||||
<devices>
|
||||
<emulator>/usr/bin/qemu</emulator>
|
||||
<controller type='usb' index='0' model='ich9-ehci1'>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/>
|
||||
</controller>
|
||||
<controller type='usb' index='0' model='ich9-uhci1'>
|
||||
<master startport='0'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
|
||||
</controller>
|
||||
<controller type='usb' index='0' model='ich9-uhci2'>
|
||||
<master startport='2'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/>
|
||||
</controller>
|
||||
<controller type='usb' index='0' model='ich9-uhci3'>
|
||||
<master startport='4'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/>
|
||||
</controller>
|
||||
<redirdev bus='usb' type='tcp'>
|
||||
<source mode='connect' host='localhost' service='4000'/>
|
||||
<protocol type='raw'/>
|
||||
</redirdev>
|
||||
<memballoon model='virtio'/>
|
||||
</devices>
|
||||
</domain>
|
@ -507,6 +507,10 @@ mymain(void)
|
||||
DO_TEST("usb-ports", false,
|
||||
QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_USB_HUB,
|
||||
QEMU_CAPS_NODEFCONFIG);
|
||||
DO_TEST("usb-redir", false,
|
||||
QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG,
|
||||
QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_USB_HUB,
|
||||
QEMU_CAPS_ICH9_USB_EHCI1, QEMU_CAPS_USB_REDIR);
|
||||
|
||||
DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE);
|
||||
|
||||
|
@ -189,6 +189,8 @@ mymain(void)
|
||||
DO_TEST("lease");
|
||||
DO_TEST("event_idx");
|
||||
|
||||
DO_TEST("usb-redir");
|
||||
|
||||
/* These tests generate different XML */
|
||||
DO_TEST_DIFFERENT("balloon-device-auto");
|
||||
DO_TEST_DIFFERENT("channel-virtio-auto");
|
||||
|
Loading…
Reference in New Issue
Block a user