root/drivers/vfio/pci/vfio_pci_igd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vfio_pci_igd_rw
  2. vfio_pci_igd_release
  3. vfio_pci_igd_opregion_init
  4. vfio_pci_igd_cfg_rw
  5. vfio_pci_igd_cfg_release
  6. vfio_pci_igd_cfg_init
  7. vfio_pci_igd_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * VFIO PCI Intel Graphics support
   4  *
   5  * Copyright (C) 2016 Red Hat, Inc.  All rights reserved.
   6  *      Author: Alex Williamson <alex.williamson@redhat.com>
   7  *
   8  * Register a device specific region through which to provide read-only
   9  * access to the Intel IGD opregion.  The register defining the opregion
  10  * address is also virtualized to prevent user modification.
  11  */
  12 
  13 #include <linux/io.h>
  14 #include <linux/pci.h>
  15 #include <linux/uaccess.h>
  16 #include <linux/vfio.h>
  17 
  18 #include "vfio_pci_private.h"
  19 
  20 #define OPREGION_SIGNATURE      "IntelGraphicsMem"
  21 #define OPREGION_SIZE           (8 * 1024)
  22 #define OPREGION_PCI_ADDR       0xfc
  23 
  24 static size_t vfio_pci_igd_rw(struct vfio_pci_device *vdev, char __user *buf,
  25                               size_t count, loff_t *ppos, bool iswrite)
  26 {
  27         unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) - VFIO_PCI_NUM_REGIONS;
  28         void *base = vdev->region[i].data;
  29         loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
  30 
  31         if (pos >= vdev->region[i].size || iswrite)
  32                 return -EINVAL;
  33 
  34         count = min(count, (size_t)(vdev->region[i].size - pos));
  35 
  36         if (copy_to_user(buf, base + pos, count))
  37                 return -EFAULT;
  38 
  39         *ppos += count;
  40 
  41         return count;
  42 }
  43 
  44 static void vfio_pci_igd_release(struct vfio_pci_device *vdev,
  45                                  struct vfio_pci_region *region)
  46 {
  47         memunmap(region->data);
  48 }
  49 
  50 static const struct vfio_pci_regops vfio_pci_igd_regops = {
  51         .rw             = vfio_pci_igd_rw,
  52         .release        = vfio_pci_igd_release,
  53 };
  54 
  55 static int vfio_pci_igd_opregion_init(struct vfio_pci_device *vdev)
  56 {
  57         __le32 *dwordp = (__le32 *)(vdev->vconfig + OPREGION_PCI_ADDR);
  58         u32 addr, size;
  59         void *base;
  60         int ret;
  61 
  62         ret = pci_read_config_dword(vdev->pdev, OPREGION_PCI_ADDR, &addr);
  63         if (ret)
  64                 return ret;
  65 
  66         if (!addr || !(~addr))
  67                 return -ENODEV;
  68 
  69         base = memremap(addr, OPREGION_SIZE, MEMREMAP_WB);
  70         if (!base)
  71                 return -ENOMEM;
  72 
  73         if (memcmp(base, OPREGION_SIGNATURE, 16)) {
  74                 memunmap(base);
  75                 return -EINVAL;
  76         }
  77 
  78         size = le32_to_cpu(*(__le32 *)(base + 16));
  79         if (!size) {
  80                 memunmap(base);
  81                 return -EINVAL;
  82         }
  83 
  84         size *= 1024; /* In KB */
  85 
  86         if (size != OPREGION_SIZE) {
  87                 memunmap(base);
  88                 base = memremap(addr, size, MEMREMAP_WB);
  89                 if (!base)
  90                         return -ENOMEM;
  91         }
  92 
  93         ret = vfio_pci_register_dev_region(vdev,
  94                 PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
  95                 VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION,
  96                 &vfio_pci_igd_regops, size, VFIO_REGION_INFO_FLAG_READ, base);
  97         if (ret) {
  98                 memunmap(base);
  99                 return ret;
 100         }
 101 
 102         /* Fill vconfig with the hw value and virtualize register */
 103         *dwordp = cpu_to_le32(addr);
 104         memset(vdev->pci_config_map + OPREGION_PCI_ADDR,
 105                PCI_CAP_ID_INVALID_VIRT, 4);
 106 
 107         return ret;
 108 }
 109 
 110 static size_t vfio_pci_igd_cfg_rw(struct vfio_pci_device *vdev,
 111                                   char __user *buf, size_t count, loff_t *ppos,
 112                                   bool iswrite)
 113 {
 114         unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) - VFIO_PCI_NUM_REGIONS;
 115         struct pci_dev *pdev = vdev->region[i].data;
 116         loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
 117         size_t size;
 118         int ret;
 119 
 120         if (pos >= vdev->region[i].size || iswrite)
 121                 return -EINVAL;
 122 
 123         size = count = min(count, (size_t)(vdev->region[i].size - pos));
 124 
 125         if ((pos & 1) && size) {
 126                 u8 val;
 127 
 128                 ret = pci_user_read_config_byte(pdev, pos, &val);
 129                 if (ret)
 130                         return pcibios_err_to_errno(ret);
 131 
 132                 if (copy_to_user(buf + count - size, &val, 1))
 133                         return -EFAULT;
 134 
 135                 pos++;
 136                 size--;
 137         }
 138 
 139         if ((pos & 3) && size > 2) {
 140                 u16 val;
 141 
 142                 ret = pci_user_read_config_word(pdev, pos, &val);
 143                 if (ret)
 144                         return pcibios_err_to_errno(ret);
 145 
 146                 val = cpu_to_le16(val);
 147                 if (copy_to_user(buf + count - size, &val, 2))
 148                         return -EFAULT;
 149 
 150                 pos += 2;
 151                 size -= 2;
 152         }
 153 
 154         while (size > 3) {
 155                 u32 val;
 156 
 157                 ret = pci_user_read_config_dword(pdev, pos, &val);
 158                 if (ret)
 159                         return pcibios_err_to_errno(ret);
 160 
 161                 val = cpu_to_le32(val);
 162                 if (copy_to_user(buf + count - size, &val, 4))
 163                         return -EFAULT;
 164 
 165                 pos += 4;
 166                 size -= 4;
 167         }
 168 
 169         while (size >= 2) {
 170                 u16 val;
 171 
 172                 ret = pci_user_read_config_word(pdev, pos, &val);
 173                 if (ret)
 174                         return pcibios_err_to_errno(ret);
 175 
 176                 val = cpu_to_le16(val);
 177                 if (copy_to_user(buf + count - size, &val, 2))
 178                         return -EFAULT;
 179 
 180                 pos += 2;
 181                 size -= 2;
 182         }
 183 
 184         while (size) {
 185                 u8 val;
 186 
 187                 ret = pci_user_read_config_byte(pdev, pos, &val);
 188                 if (ret)
 189                         return pcibios_err_to_errno(ret);
 190 
 191                 if (copy_to_user(buf + count - size, &val, 1))
 192                         return -EFAULT;
 193 
 194                 pos++;
 195                 size--;
 196         }
 197 
 198         *ppos += count;
 199 
 200         return count;
 201 }
 202 
 203 static void vfio_pci_igd_cfg_release(struct vfio_pci_device *vdev,
 204                                      struct vfio_pci_region *region)
 205 {
 206         struct pci_dev *pdev = region->data;
 207 
 208         pci_dev_put(pdev);
 209 }
 210 
 211 static const struct vfio_pci_regops vfio_pci_igd_cfg_regops = {
 212         .rw             = vfio_pci_igd_cfg_rw,
 213         .release        = vfio_pci_igd_cfg_release,
 214 };
 215 
 216 static int vfio_pci_igd_cfg_init(struct vfio_pci_device *vdev)
 217 {
 218         struct pci_dev *host_bridge, *lpc_bridge;
 219         int ret;
 220 
 221         host_bridge = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0));
 222         if (!host_bridge)
 223                 return -ENODEV;
 224 
 225         if (host_bridge->vendor != PCI_VENDOR_ID_INTEL ||
 226             host_bridge->class != (PCI_CLASS_BRIDGE_HOST << 8)) {
 227                 pci_dev_put(host_bridge);
 228                 return -EINVAL;
 229         }
 230 
 231         ret = vfio_pci_register_dev_region(vdev,
 232                 PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
 233                 VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG,
 234                 &vfio_pci_igd_cfg_regops, host_bridge->cfg_size,
 235                 VFIO_REGION_INFO_FLAG_READ, host_bridge);
 236         if (ret) {
 237                 pci_dev_put(host_bridge);
 238                 return ret;
 239         }
 240 
 241         lpc_bridge = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0x1f, 0));
 242         if (!lpc_bridge)
 243                 return -ENODEV;
 244 
 245         if (lpc_bridge->vendor != PCI_VENDOR_ID_INTEL ||
 246             lpc_bridge->class != (PCI_CLASS_BRIDGE_ISA << 8)) {
 247                 pci_dev_put(lpc_bridge);
 248                 return -EINVAL;
 249         }
 250 
 251         ret = vfio_pci_register_dev_region(vdev,
 252                 PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
 253                 VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG,
 254                 &vfio_pci_igd_cfg_regops, lpc_bridge->cfg_size,
 255                 VFIO_REGION_INFO_FLAG_READ, lpc_bridge);
 256         if (ret) {
 257                 pci_dev_put(lpc_bridge);
 258                 return ret;
 259         }
 260 
 261         return 0;
 262 }
 263 
 264 int vfio_pci_igd_init(struct vfio_pci_device *vdev)
 265 {
 266         int ret;
 267 
 268         ret = vfio_pci_igd_opregion_init(vdev);
 269         if (ret)
 270                 return ret;
 271 
 272         ret = vfio_pci_igd_cfg_init(vdev);
 273         if (ret)
 274                 return ret;
 275 
 276         return 0;
 277 }

/* [<][>][^][v][top][bottom][index][help] */