[PATCH 5/7] util: change call sequence for virPCIDeviceFindCapabilityOffset()
Daniel P. Berrangé
berrange at redhat.com
Wed Dec 9 09:18:41 UTC 2020
On Tue, Dec 08, 2020 at 08:37:43PM -0500, Laine Stump wrote:
> Previously there was no way to differentiate between this function 1)
> encountering an error while reading the pci config, and 2) determining
> that the device in question is a conventional PCI device, and so has
> no Express Capabilities.
>
> The difference between these two conditions is important, because an
> unprivileged libvirtd will be unable to read all of the pci config (it
> can only read the first 64 bytes, and will get ENOENT when it tries to
> seek past that limit) even though the device is in fact a PCIe device.
>
> This patch just changes virPCIDeviceFindCapabilityOffset() to put the
> determined offset into an argument of the function (rather than
> sending it back as the return value), and to return the standard "0 on
> success, -1 on failure". Failure is determined by checking the value
> of errno after each attemptd read of the config file (which can only
> work reliably if errno is reset to 0 before each read, and after
> virPCIDeviceFindCapabilityOffset() has finished examining it).
>
> (NB: if the config file is read successfully, but no Express
> Capabilities are found, then the function returns success, but the
> returned offset will be 0 (which is an impossible offset for Express
> Capabilities, and so easily recognizeable).
>
> An upcoming patch will take advantage of the change made here.
>
> Signed-off-by: Laine Stump <laine at redhat.com>
> ---
> src/util/virpci.c | 42 +++++++++++++++++++++++++++++++-----------
> 1 file changed, 31 insertions(+), 11 deletions(-)
>
> diff --git a/src/util/virpci.c b/src/util/virpci.c
> index 9109fb4f3a..2f99bb93bd 100644
> --- a/src/util/virpci.c
> +++ b/src/util/virpci.c
> @@ -329,6 +329,7 @@ virPCIDeviceRead(virPCIDevicePtr dev,
> unsigned int buflen)
> {
> memset(buf, 0, buflen);
> + errno = 0;
>
> if (lseek(cfgfd, pos, SEEK_SET) != pos ||
> saferead(cfgfd, buf, buflen) != buflen) {
> @@ -483,19 +484,24 @@ virPCIDeviceIterDevices(virPCIDeviceIterPredicate predicate,
> return ret;
> }
>
I think there should be some API docs added here to describe the
semantics this method is trying to achieve wrt unprivileged
usage and pci vs pcie.
> -static uint8_t
> +static int
> virPCIDeviceFindCapabilityOffset(virPCIDevicePtr dev,
> int cfgfd,
> - unsigned int capability)
> + unsigned int capability,
> + unsigned int *offset)
> {
> uint16_t status;
> uint8_t pos;
>
> + *offset = 0; /* assume failure (*nothing* can be at offset 0) */
> +
> status = virPCIDeviceRead16(dev, cfgfd, PCI_STATUS);
> - if (!(status & PCI_STATUS_CAP_LIST))
> - return 0;
> + if (errno != 0 || !(status & PCI_STATUS_CAP_LIST))
> + goto error;
>
> pos = virPCIDeviceRead8(dev, cfgfd, PCI_CAPABILITY_LIST);
> + if (errno != 0)
> + goto error;
>
> /* Zero indicates last capability, capabilities can't
> * be in the config space header and 0xff is returned
> @@ -506,18 +512,31 @@ virPCIDeviceFindCapabilityOffset(virPCIDevicePtr dev,
> */
> while (pos >= PCI_CONF_HEADER_LEN && pos != 0xff) {
> uint8_t capid = virPCIDeviceRead8(dev, cfgfd, pos);
> + if (errno != 0)
> + goto error;
> +
> if (capid == capability) {
> VIR_DEBUG("%s %s: found cap 0x%.2x at 0x%.2x",
> dev->id, dev->name, capability, pos);
> - return pos;
> + *offset = pos;
> + return 0;
> }
>
> pos = virPCIDeviceRead8(dev, cfgfd, pos + 1);
> + if (errno != 0)
> + goto error;
> }
>
> - VIR_DEBUG("%s %s: failed to find cap 0x%.2x", dev->id, dev->name, capability);
> + error:
> + VIR_DEBUG("%s %s: failed to find cap 0x%.2x (%s)",
> + dev->id, dev->name, capability, g_strerror(errno));
>
> - return 0;
> + /* reset errno in case the failure was due to insufficient
> + * privileges to read the entire PCI config file
> + */
> + errno = 0;
> +
> + return -1;
> }
>
> static unsigned int
> @@ -553,7 +572,7 @@ static bool
> virPCIDeviceDetectFunctionLevelReset(virPCIDevicePtr dev, int cfgfd)
> {
> uint32_t caps;
> - uint8_t pos;
> + unsigned int pos;
> g_autofree char *path = NULL;
> int found;
>
> @@ -575,7 +594,8 @@ virPCIDeviceDetectFunctionLevelReset(virPCIDevicePtr dev, int cfgfd)
> * the same thing, except for conventional PCI
> * devices. This is not common yet.
> */
> - pos = virPCIDeviceFindCapabilityOffset(dev, cfgfd, PCI_CAP_ID_AF);
> + virPCIDeviceFindCapabilityOffset(dev, cfgfd, PCI_CAP_ID_AF, &pos);
> +
> if (pos) {
> caps = virPCIDeviceRead16(dev, cfgfd, pos + PCI_AF_CAP);
> if (caps & PCI_AF_CAP_FLR) {
> @@ -885,8 +905,8 @@ virPCIDeviceTryPowerManagementReset(virPCIDevicePtr dev, int cfgfd)
> static int
> virPCIDeviceInit(virPCIDevicePtr dev, int cfgfd)
> {
> - dev->pcie_cap_pos = virPCIDeviceFindCapabilityOffset(dev, cfgfd, PCI_CAP_ID_EXP);
> - dev->pci_pm_cap_pos = virPCIDeviceFindCapabilityOffset(dev, cfgfd, PCI_CAP_ID_PM);
> + virPCIDeviceFindCapabilityOffset(dev, cfgfd, PCI_CAP_ID_EXP, &dev->pcie_cap_pos);
> + virPCIDeviceFindCapabilityOffset(dev, cfgfd, PCI_CAP_ID_PM, &dev->pci_pm_cap_pos);
> dev->has_flr = virPCIDeviceDetectFunctionLevelReset(dev, cfgfd);
> dev->has_pm_reset = virPCIDeviceDetectPowerManagementReset(dev, cfgfd);
>
> --
> 2.28.0
>
Regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
More information about the libvir-list
mailing list