Pass pre-opened PCI device sysfs config file to QEMU

This allows libvirt to open the PCI device sysfs config file prior
to dropping privileges so qemu can access the full config space.
Without this, a de-privileged qemu can only access the first 64
bytes of config space.

* src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Detect support
  for pci-assign.configfd option. Use this option when formatting
  PCI device string if possible
* src/qemu/qemu_driver.c: Pre-open PCI sysfs config file and pass
  to QEMU
This commit is contained in:
Alex Williamson 2010-05-26 13:32:38 +01:00 committed by Daniel P. Berrange
parent 7f44743c52
commit c444af1ac2
3 changed files with 134 additions and 4 deletions

View File

@ -1354,6 +1354,48 @@ fail:
return -1;
}
static void qemudParsePCIDeviceStrs(const char *qemu, unsigned long long *flags)
{
const char *const qemuarg[] = { qemu, "-device", "pci-assign,?", NULL };
const char *const qemuenv[] = { "LC_ALL=C", NULL };
pid_t child;
int status;
int newstderr = -1;
if (virExec(qemuarg, qemuenv, NULL,
&child, -1, NULL, &newstderr, VIR_EXEC_CLEAR_CAPS) < 0)
return;
char *pciassign = NULL;
enum { MAX_PCI_OUTPUT_SIZE = 1024*4 };
int len = virFileReadLimFD(newstderr, MAX_PCI_OUTPUT_SIZE, &pciassign);
if (len < 0) {
virReportSystemError(errno,
_("Unable to read %s pci-assign device output"),
qemu);
goto cleanup;
}
if (strstr(pciassign, "pci-assign.configfd"))
*flags |= QEMUD_CMD_FLAG_PCI_CONFIGFD;
cleanup:
VIR_FREE(pciassign);
close(newstderr);
rewait:
if (waitpid(child, &status, 0) != child) {
if (errno == EINTR)
goto rewait;
VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"),
WEXITSTATUS(status), (unsigned long)child);
}
if (WEXITSTATUS(status) != 0) {
VIR_WARN("Unexpected exit status '%d', qemu probably failed",
WEXITSTATUS(status));
}
}
int qemudExtractVersionInfo(const char *qemu,
unsigned int *retversion,
unsigned long long *retflags) {
@ -1387,6 +1429,9 @@ int qemudExtractVersionInfo(const char *qemu,
&version, &is_kvm, &kvm_version) == -1)
goto cleanup2;
if (flags & QEMUD_CMD_FLAG_DEVICE)
qemudParsePCIDeviceStrs(qemu, &flags);
if (retversion)
*retversion = version;
if (retflags)
@ -2896,8 +2941,33 @@ error:
}
int
qemudOpenPCIConfig(virDomainHostdevDefPtr dev)
{
char *path = NULL;
int configfd = -1;
if (virAsprintf(&path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/config",
dev->source.subsys.u.pci.domain,
dev->source.subsys.u.pci.bus,
dev->source.subsys.u.pci.slot,
dev->source.subsys.u.pci.function) < 0) {
virReportOOMError();
return -1;
}
configfd = open(path, O_RDWR, 0);
if (configfd < 0)
virReportSystemError(errno, _("Failed opening %s"), path);
VIR_FREE(path);
return configfd;
}
char *
qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, const char *configfd)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
@ -2907,6 +2977,8 @@ qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
dev->source.subsys.u.pci.slot,
dev->source.subsys.u.pci.function);
virBufferVSprintf(&buf, ",id=%s", dev->info.alias);
if (configfd && *configfd)
virBufferVSprintf(&buf, ",configfd=%s", configfd);
if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0)
goto error;
@ -4611,8 +4683,30 @@ int qemudBuildCommandLine(virConnectPtr conn,
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
char *configfd_name = NULL;
if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
int configfd = qemudOpenPCIConfig(hostdev);
if (configfd >= 0) {
if (virAsprintf(&configfd_name, "%d", configfd) < 0) {
close(configfd);
virReportOOMError();
goto no_memory;
}
if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) {
VIR_FREE(configfd_name);
close(configfd);
goto no_memory;
}
(*vmfds)[(*nvmfds)++] = configfd;
}
}
ADD_ARG_LIT("-device");
if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name);
VIR_FREE(configfd_name);
if (!devstr)
goto error;
ADD_ARG(devstr);
} else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) {

View File

@ -89,6 +89,7 @@ enum qemud_cmd_flags {
QEMUD_CMD_FLAG_NO_HPET = (1LL << 33), /* -no-hpet flag is supported */
QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection supported */
QEMUD_CMD_FLAG_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */
QEMUD_CMD_FLAG_PCI_CONFIGFD = (1LL << 36), /* pci-assign.configfd */
};
/* Main driver state */
@ -247,7 +248,10 @@ char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound);
/* Legacy, pre device support */
char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev);
/* Current, best practice */
char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev);
char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev,
const char *configfd);
int qemudOpenPCIConfig(virDomainHostdevDefPtr dev);
/* Current, best practice */
char * qemuBuildChrChardevStr(virDomainChrDefPtr dev);

View File

@ -7658,6 +7658,8 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
pciDevice *pci;
int ret;
char *devstr = NULL;
int configfd = -1;
char *configfd_name = NULL;
if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
virReportOOMError();
@ -7688,8 +7690,32 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
goto error;
if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0)
goto error;
if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
configfd = qemudOpenPCIConfig(hostdev);
if (configfd >= 0) {
if (virAsprintf(&configfd_name, "fd-%s",
hostdev->info.alias) < 0) {
virReportOOMError();
goto error;
}
if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev)))
qemuDomainObjEnterMonitorWithDriver(driver, vm);
if (qemuMonitorSendFileHandle(priv->mon, configfd_name,
configfd) < 0) {
qemuDomainObjExitMonitorWithDriver(driver, vm);
goto error;
}
qemuDomainObjExitMonitorWithDriver(driver, vm);
}
}
if (!virDomainObjIsActive(vm)) {
qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("guest unexpectedly quit during hotplug"));
goto error;
}
if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name)))
goto error;
qemuDomainObjEnterMonitorWithDriver(driver, vm);
@ -7713,6 +7739,9 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
VIR_FREE(devstr);
VIR_FREE(configfd_name);
if (configfd >= 0)
close(configfd);
return 0;
@ -7724,6 +7753,9 @@ error:
VIR_FREE(devstr);
pciDeviceListDel(driver->activePciHostdevs, pci);
VIR_FREE(configfd_name);
if (configfd >= 0)
close(configfd);
return -1;
}