in pci/vfio_pci_igd.c [168:279]
static int vfio_pci_igd_opregion_init(struct vfio_pci_core_device *vdev)
{
__le32 *dwordp = (__le32 *)(vdev->vconfig + OPREGION_PCI_ADDR);
u32 addr, size;
struct igd_opregion_vbt *opregionvbt;
int ret;
u16 version;
ret = pci_read_config_dword(vdev->pdev, OPREGION_PCI_ADDR, &addr);
if (ret)
return ret;
if (!addr || !(~addr))
return -ENODEV;
opregionvbt = kzalloc(sizeof(*opregionvbt), GFP_KERNEL);
if (!opregionvbt)
return -ENOMEM;
opregionvbt->opregion = memremap(addr, OPREGION_SIZE, MEMREMAP_WB);
if (!opregionvbt->opregion) {
kfree(opregionvbt);
return -ENOMEM;
}
if (memcmp(opregionvbt->opregion, OPREGION_SIGNATURE, 16)) {
memunmap(opregionvbt->opregion);
kfree(opregionvbt);
return -EINVAL;
}
size = le32_to_cpu(*(__le32 *)(opregionvbt->opregion + 16));
if (!size) {
memunmap(opregionvbt->opregion);
kfree(opregionvbt);
return -EINVAL;
}
size *= 1024; /* In KB */
/*
* OpRegion and VBT:
* When VBT data doesn't exceed 6KB, it's stored in Mailbox #4.
* When VBT data exceeds 6KB size, Mailbox #4 is no longer large enough
* to hold the VBT data, the Extended VBT region is introduced since
* OpRegion 2.0 to hold the VBT data. Since OpRegion 2.0, RVDA/RVDS are
* introduced to define the extended VBT data location and size.
* OpRegion 2.0: RVDA defines the absolute physical address of the
* extended VBT data, RVDS defines the VBT data size.
* OpRegion 2.1 and above: RVDA defines the relative address of the
* extended VBT data to OpRegion base, RVDS defines the VBT data size.
*
* Due to the RVDA definition diff in OpRegion VBT (also the only diff
* between 2.0 and 2.1), exposing OpRegion and VBT as a contiguous range
* for OpRegion 2.0 and above makes it possible to support the
* non-contiguous VBT through a single vfio region. From r/w ops view,
* only contiguous VBT after OpRegion with version 2.1+ is exposed,
* regardless the host OpRegion is 2.0 or non-contiguous 2.1+. The r/w
* ops will on-the-fly shift the actural offset into VBT so that data at
* correct position can be returned to the requester.
*/
version = le16_to_cpu(*(__le16 *)(opregionvbt->opregion +
OPREGION_VERSION));
if (version >= 0x0200) {
u64 rvda = le64_to_cpu(*(__le64 *)(opregionvbt->opregion +
OPREGION_RVDA));
u32 rvds = le32_to_cpu(*(__le32 *)(opregionvbt->opregion +
OPREGION_RVDS));
/* The extended VBT is valid only when RVDA/RVDS are non-zero */
if (rvda && rvds) {
size += rvds;
/*
* Extended VBT location by RVDA:
* Absolute physical addr for 2.0.
* Relative addr to OpRegion header for 2.1+.
*/
if (version == 0x0200)
addr = rvda;
else
addr += rvda;
opregionvbt->vbt_ex = memremap(addr, rvds, MEMREMAP_WB);
if (!opregionvbt->vbt_ex) {
memunmap(opregionvbt->opregion);
kfree(opregionvbt);
return -ENOMEM;
}
}
}
ret = vfio_pci_register_dev_region(vdev,
PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &vfio_pci_igd_regops,
size, VFIO_REGION_INFO_FLAG_READ, opregionvbt);
if (ret) {
if (opregionvbt->vbt_ex)
memunmap(opregionvbt->vbt_ex);
memunmap(opregionvbt->opregion);
kfree(opregionvbt);
return ret;
}
/* Fill vconfig with the hw value and virtualize register */
*dwordp = cpu_to_le32(addr);
memset(vdev->pci_config_map + OPREGION_PCI_ADDR,
PCI_CAP_ID_INVALID_VIRT, 4);
return ret;
}