static int vfio_basic_config_write()

in pci/vfio_pci_config.c [555:645]


static int vfio_basic_config_write(struct vfio_pci_core_device *vdev, int pos,
				   int count, struct perm_bits *perm,
				   int offset, __le32 val)
{
	struct pci_dev *pdev = vdev->pdev;
	__le16 *virt_cmd;
	u16 new_cmd = 0;
	int ret;

	virt_cmd = (__le16 *)&vdev->vconfig[PCI_COMMAND];

	if (offset == PCI_COMMAND) {
		bool phys_mem, virt_mem, new_mem, phys_io, virt_io, new_io;
		u16 phys_cmd;

		ret = pci_user_read_config_word(pdev, PCI_COMMAND, &phys_cmd);
		if (ret)
			return ret;

		new_cmd = le32_to_cpu(val);

		phys_io = !!(phys_cmd & PCI_COMMAND_IO);
		virt_io = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_IO);
		new_io = !!(new_cmd & PCI_COMMAND_IO);

		phys_mem = !!(phys_cmd & PCI_COMMAND_MEMORY);
		virt_mem = !!(le16_to_cpu(*virt_cmd) & PCI_COMMAND_MEMORY);
		new_mem = !!(new_cmd & PCI_COMMAND_MEMORY);

		if (!new_mem)
			vfio_pci_zap_and_down_write_memory_lock(vdev);
		else
			down_write(&vdev->memory_lock);

		/*
		 * If the user is writing mem/io enable (new_mem/io) and we
		 * think it's already enabled (virt_mem/io), but the hardware
		 * shows it disabled (phys_mem/io, then the device has
		 * undergone some kind of backdoor reset and needs to be
		 * restored before we allow it to enable the bars.
		 * SR-IOV devices will trigger this - for mem enable let's
		 * catch this now and for io enable it will be caught later
		 */
		if ((new_mem && virt_mem && !phys_mem &&
		     !pdev->no_command_memory) ||
		    (new_io && virt_io && !phys_io) ||
		    vfio_need_bar_restore(vdev))
			vfio_bar_restore(vdev);
	}

	count = vfio_default_config_write(vdev, pos, count, perm, offset, val);
	if (count < 0) {
		if (offset == PCI_COMMAND)
			up_write(&vdev->memory_lock);
		return count;
	}

	/*
	 * Save current memory/io enable bits in vconfig to allow for
	 * the test above next time.
	 */
	if (offset == PCI_COMMAND) {
		u16 mask = PCI_COMMAND_MEMORY | PCI_COMMAND_IO;

		*virt_cmd &= cpu_to_le16(~mask);
		*virt_cmd |= cpu_to_le16(new_cmd & mask);

		up_write(&vdev->memory_lock);
	}

	/* Emulate INTx disable */
	if (offset >= PCI_COMMAND && offset <= PCI_COMMAND + 1) {
		bool virt_intx_disable;

		virt_intx_disable = !!(le16_to_cpu(*virt_cmd) &
				       PCI_COMMAND_INTX_DISABLE);

		if (virt_intx_disable && !vdev->virq_disabled) {
			vdev->virq_disabled = true;
			vfio_pci_intx_mask(vdev);
		} else if (!virt_intx_disable && vdev->virq_disabled) {
			vdev->virq_disabled = false;
			vfio_pci_intx_unmask(vdev);
		}
	}

	if (is_bar(offset))
		vdev->bardirty = true;

	return count;
}