mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-05 04:41:20 +00:00
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:
parent
7f44743c52
commit
c444af1ac2
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user