linux/drivers/vfio/pci/vfio_pci_igd.c
<<
>>
Prefs
   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
  24static 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
  44static void vfio_pci_igd_release(struct vfio_pci_device *vdev,
  45                                 struct vfio_pci_region *region)
  46{
  47        memunmap(region->data);
  48}
  49
  50static const struct vfio_pci_regops vfio_pci_igd_regops = {
  51        .rw             = vfio_pci_igd_rw,
  52        .release        = vfio_pci_igd_release,
  53};
  54
  55static 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
 110static 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
 203static 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
 211static 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
 216static 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
 264int 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}
 278