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; 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, int qemudExtractVersionInfo(const char *qemu,
unsigned int *retversion, unsigned int *retversion,
unsigned long long *retflags) { unsigned long long *retflags) {
@ -1387,6 +1429,9 @@ int qemudExtractVersionInfo(const char *qemu,
&version, &is_kvm, &kvm_version) == -1) &version, &is_kvm, &kvm_version) == -1)
goto cleanup2; goto cleanup2;
if (flags & QEMUD_CMD_FLAG_DEVICE)
qemudParsePCIDeviceStrs(qemu, &flags);
if (retversion) if (retversion)
*retversion = version; *retversion = version;
if (retflags) 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 * char *
qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev) qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, const char *configfd)
{ {
virBuffer buf = VIR_BUFFER_INITIALIZER; virBuffer buf = VIR_BUFFER_INITIALIZER;
@ -2907,6 +2977,8 @@ qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev)
dev->source.subsys.u.pci.slot, dev->source.subsys.u.pci.slot,
dev->source.subsys.u.pci.function); dev->source.subsys.u.pci.function);
virBufferVSprintf(&buf, ",id=%s", dev->info.alias); virBufferVSprintf(&buf, ",id=%s", dev->info.alias);
if (configfd && *configfd)
virBufferVSprintf(&buf, ",configfd=%s", configfd);
if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0) if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0)
goto error; goto error;
@ -4611,8 +4683,30 @@ int qemudBuildCommandLine(virConnectPtr conn,
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { 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"); ADD_ARG_LIT("-device");
if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev))) devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name);
VIR_FREE(configfd_name);
if (!devstr)
goto error; goto error;
ADD_ARG(devstr); ADD_ARG(devstr);
} else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) { } 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_HPET = (1LL << 33), /* -no-hpet flag is supported */
QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection 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_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */
QEMUD_CMD_FLAG_PCI_CONFIGFD = (1LL << 36), /* pci-assign.configfd */
}; };
/* Main driver state */ /* Main driver state */
@ -247,7 +248,10 @@ char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound);
/* Legacy, pre device support */ /* Legacy, pre device support */
char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev); char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev);
/* Current, best practice */ /* Current, best practice */
char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev); char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev,
const char *configfd);
int qemudOpenPCIConfig(virDomainHostdevDefPtr dev);
/* Current, best practice */ /* Current, best practice */
char * qemuBuildChrChardevStr(virDomainChrDefPtr dev); char * qemuBuildChrChardevStr(virDomainChrDefPtr dev);

View File

@ -7658,6 +7658,8 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
pciDevice *pci; pciDevice *pci;
int ret; int ret;
char *devstr = NULL; char *devstr = NULL;
int configfd = -1;
char *configfd_name = NULL;
if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
virReportOOMError(); virReportOOMError();
@ -7688,8 +7690,32 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
goto error; goto error;
if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0) if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0)
goto error; 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; goto error;
qemuDomainObjEnterMonitorWithDriver(driver, vm); qemuDomainObjEnterMonitorWithDriver(driver, vm);
@ -7713,6 +7739,9 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
VIR_FREE(devstr); VIR_FREE(devstr);
VIR_FREE(configfd_name);
if (configfd >= 0)
close(configfd);
return 0; return 0;
@ -7724,6 +7753,9 @@ error:
VIR_FREE(devstr); VIR_FREE(devstr);
pciDeviceListDel(driver->activePciHostdevs, pci); pciDeviceListDel(driver->activePciHostdevs, pci);
VIR_FREE(configfd_name);
if (configfd >= 0)
close(configfd);
return -1; return -1;
} }