// SPDX-License-Identifier: GPL-2.0/* * Mediated virtual PCI display host device driver * * See mdpy-defs.h for device specs * * (c) Gerd Hoffmann <kraxel@redhat.com> * * based on mtty driver which is: * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * Author: Neo Jia <cjia@nvidia.com> * Kirti Wankhede <kwankhede@nvidia.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/cdev.h>#include <linux/vfio.h>#include <linux/iommu.h>#include <linux/sysfs.h>#include <linux/mdev.h>#include <linux/pci.h>#include <drm/drm_fourcc.h>#include "mdpy-defs.h" #define MDPY_NAME "mdpy"#define MDPY_CLASS_NAME "mdpy" #define MDPY_CONFIG_SPACE_SIZE 0xff#define MDPY_MEMORY_BAR_OFFSET PAGE_SIZE#define MDPY_DISPLAY_REGION 16 #define STORE_LE16(addr, val) (*(u16 *)addr = val)#define STORE_LE32(addr, val) (*(u32 *)addr = val) MODULE_LICENSE("GPL v2"); static int max_devices = 4;module_param_named(count, max_devices, int, 0444);MODULE_PARM_DESC(count, "number of " MDPY_NAME " devices"); #define MDPY_TYPE_1 "vga"#define MDPY_TYPE_2 "xga"#define MDPY_TYPE_3 "hd" static const struct mdpy_type { const char *name; u32 format; u32 bytepp; u32 width; u32 height;} mdpy_types[] = { { .name = MDPY_CLASS_NAME "-" MDPY_TYPE_1, .format = DRM_FORMAT_XRGB8888, .bytepp = 4, .width = 640, .height = 480, }, { .name = MDPY_CLASS_NAME "-" MDPY_TYPE_2, .format = DRM_FORMAT_XRGB8888, .bytepp = 4, .width = 1024, .height = 768, }, { .name = MDPY_CLASS_NAME "-" MDPY_TYPE_3, .format = DRM_FORMAT_XRGB8888, .bytepp = 4, .width = 1920, .height = 1080, },}; static dev_t mdpy_devt;static struct class *mdpy_class;static struct cdev mdpy_cdev;static struct device mdpy_dev;static u32 mdpy_count; /* State of each mdev device */struct mdev_state { u8 *vconfig; u32 bar_mask; struct mutex ops_lock; struct mdev_device *mdev; struct vfio_device_info dev_info; const struct mdpy_type *type; u32 memsize; void *memblk;}; static const struct mdpy_type *mdpy_find_type(struct kobject *kobj){ int i; for (i = 0; i < ARRAY_SIZE(mdpy_types); i++) if (strcmp(mdpy_types[i].name, kobj->name) == 0) return mdpy_types + i; return NULL;} static void mdpy_create_config_space(struct mdev_state *mdev_state){ STORE_LE16((u16 *) &mdev_state->vconfig[PCI_VENDOR_ID], MDPY_PCI_VENDOR_ID); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_DEVICE_ID], MDPY_PCI_DEVICE_ID); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_VENDOR_ID], MDPY_PCI_SUBVENDOR_ID); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_ID], MDPY_PCI_SUBDEVICE_ID); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_COMMAND], PCI_COMMAND_IO | PCI_COMMAND_MEMORY); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_STATUS], PCI_STATUS_CAP_LIST); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_CLASS_DEVICE], PCI_CLASS_DISPLAY_OTHER); mdev_state->vconfig[PCI_CLASS_REVISION] = 0x01; STORE_LE32((u32 *) &mdev_state->vconfig[PCI_BASE_ADDRESS_0], PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32 | PCI_BASE_ADDRESS_MEM_PREFETCH); mdev_state->bar_mask = ~(mdev_state->memsize) + 1; /* vendor specific capability for the config registers */ mdev_state->vconfig[PCI_CAPABILITY_LIST] = MDPY_VENDORCAP_OFFSET; mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 0] = 0x09; /* vendor cap */ mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 1] = 0x00; /* next ptr */ mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 2] = MDPY_VENDORCAP_SIZE; STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_FORMAT_OFFSET], mdev_state->type->format); STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_WIDTH_OFFSET], mdev_state->type->width); STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_HEIGHT_OFFSET], mdev_state->type->height);} static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, char *buf, u32 count){ struct device *dev = mdev_dev(mdev_state->mdev); u32 cfg_addr; switch (offset) { case PCI_BASE_ADDRESS_0: cfg_addr = *(u32 *)buf; if (cfg_addr == 0xffffffff) { cfg_addr = (cfg_addr & mdev_state->bar_mask); } else { cfg_addr &= PCI_BASE_ADDRESS_MEM_MASK; if (cfg_addr) dev_info(dev, "BAR0 @ 0x%x\n", cfg_addr); } cfg_addr |= (mdev_state->vconfig[offset] & ~PCI_BASE_ADDRESS_MEM_MASK); STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); break; }} static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count, loff_t pos, bool is_write){ struct mdev_state *mdev_state = mdev_get_drvdata(mdev); struct device *dev = mdev_dev(mdev); int ret = 0; mutex_lock(&mdev_state->ops_lock); if (pos < MDPY_CONFIG_SPACE_SIZE) { if (is_write) handle_pci_cfg_write(mdev_state, pos, buf, count); else memcpy(buf, (mdev_state->vconfig + pos), count); } else if ((pos >= MDPY_MEMORY_BAR_OFFSET) && (pos + count <= MDPY_MEMORY_BAR_OFFSET + mdev_state->memsize)) { pos -= MDPY_MEMORY_BAR_OFFSET; if (is_write) memcpy(mdev_state->memblk, buf, count); else memcpy(buf, mdev_state->memblk, count); } else { dev_info(dev, "%s: %s @0x%llx (unhandled)\n", __func__, is_write ? "WR" : "RD", pos); ret = -1; goto accessfailed; } ret = count; accessfailed: mutex_unlock(&mdev_state->ops_lock); return ret;} static int mdpy_reset(struct mdev_device *mdev){ struct mdev_state *mdev_state = mdev_get_drvdata(mdev); u32 stride, i; /* initialize with gray gradient */ stride = mdev_state->type->width * mdev_state->type->bytepp; for (i = 0; i < mdev_state->type->height; i++) memset(mdev_state->memblk + i * stride, i * 255 / mdev_state->type->height, stride); return 0;} static int mdpy_create(struct kobject *kobj, struct mdev_device *mdev){ const struct mdpy_type *type = mdpy_find_type(kobj); struct device *dev = mdev_dev(mdev); struct mdev_state *mdev_state; u32 fbsize; if (mdpy_count >= max_devices) return -ENOMEM; mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); if (mdev_state == NULL) return -ENOMEM; mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL); if (mdev_state->vconfig == NULL) { kfree(mdev_state); return -ENOMEM; } if (!type) type = &mdpy_types[0]; fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp); mdev_state->memblk = vmalloc_user(fbsize); if (!mdev_state->memblk) { kfree(mdev_state->vconfig); kfree(mdev_state); return -ENOMEM; } dev_info(dev, "%s: %s (%dx%d)\n", __func__, kobj->name, type->width, type->height); mutex_init(&mdev_state->ops_lock); mdev_state->mdev = mdev; mdev_set_drvdata(mdev, mdev_state); mdev_state->type = type; mdev_state->memsize = fbsize; mdpy_create_config_space(mdev_state); mdpy_reset(mdev); mdpy_count++; return 0;} static int mdpy_remove(struct mdev_device *mdev){ struct mdev_state *mdev_state = mdev_get_drvdata(mdev); struct device *dev = mdev_dev(mdev); dev_info(dev, "%s\n", __func__); mdev_set_drvdata(mdev, NULL); vfree(mdev_state->memblk); kfree(mdev_state->vconfig); kfree(mdev_state); mdpy_count--; return 0;} static ssize_t mdpy_read(struct mdev_device *mdev, char __user *buf, size_t count, loff_t *ppos){ unsigned int done = 0; int ret; while (count) { size_t filled; if (count >= 4 && !(*ppos % 4)) { u32 val; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, false); if (ret <= 0) goto read_err; if (copy_to_user(buf, &val, sizeof(val))) goto read_err; filled = 4; } else if (count >= 2 && !(*ppos % 2)) { u16 val; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, false); if (ret <= 0) goto read_err; if (copy_to_user(buf, &val, sizeof(val))) goto read_err; filled = 2; } else { u8 val; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, false); if (ret <= 0) goto read_err; if (copy_to_user(buf, &val, sizeof(val))) goto read_err; filled = 1; } count -= filled; done += filled; *ppos += filled; buf += filled; } return done; read_err: return -EFAULT;} static ssize_t mdpy_write(struct mdev_device *mdev, const char __user *buf, size_t count, loff_t *ppos){ unsigned int done = 0; int ret; while (count) { size_t filled; if (count >= 4 && !(*ppos % 4)) { u32 val; if (copy_from_user(&val, buf, sizeof(val))) goto write_err; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, true); if (ret <= 0) goto write_err; filled = 4; } else if (count >= 2 && !(*ppos % 2)) { u16 val; if (copy_from_user(&val, buf, sizeof(val))) goto write_err; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, true); if (ret <= 0) goto write_err; filled = 2; } else { u8 val; if (copy_from_user(&val, buf, sizeof(val))) goto write_err; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, true); if (ret <= 0) goto write_err; filled = 1; } count -= filled; done += filled; *ppos += filled; buf += filled; } return done;write_err: return -EFAULT;} static int mdpy_mmap(struct mdev_device *mdev, struct vm_area_struct *vma){ struct mdev_state *mdev_state = mdev_get_drvdata(mdev); if (vma->vm_pgoff != MDPY_MEMORY_BAR_OFFSET >> PAGE_SHIFT) return -EINVAL; if (vma->vm_end < vma->vm_start) return -EINVAL; if (vma->vm_end - vma->vm_start > mdev_state->memsize) return -EINVAL; if ((vma->vm_flags & VM_SHARED) == 0) return -EINVAL; return remap_vmalloc_range_partial(vma, vma->vm_start, mdev_state->memblk, 0, vma->vm_end - vma->vm_start);} static int mdpy_get_region_info(struct mdev_device *mdev, struct vfio_region_info *region_info, u16 *cap_type_id, void **cap_type){ struct mdev_state *mdev_state; mdev_state = mdev_get_drvdata(mdev); if (!mdev_state) return -EINVAL; if (region_info->index >= VFIO_PCI_NUM_REGIONS && region_info->index != MDPY_DISPLAY_REGION) return -EINVAL; switch (region_info->index) { case VFIO_PCI_CONFIG_REGION_INDEX: region_info->offset = 0; region_info->size = MDPY_CONFIG_SPACE_SIZE; region_info->flags = (VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE); break; case VFIO_PCI_BAR0_REGION_INDEX: case MDPY_DISPLAY_REGION: region_info->offset = MDPY_MEMORY_BAR_OFFSET; region_info->size = mdev_state->memsize; region_info->flags = (VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE | VFIO_REGION_INFO_FLAG_MMAP); break; default: region_info->size = 0; region_info->offset = 0; region_info->flags = 0; } return 0;} static int mdpy_get_irq_info(struct mdev_device *mdev, struct vfio_irq_info *irq_info){ irq_info->count = 0; return 0;} static int mdpy_get_device_info(struct mdev_device *mdev, struct vfio_device_info *dev_info){ dev_info->flags = VFIO_DEVICE_FLAGS_PCI; dev_info->num_regions = VFIO_PCI_NUM_REGIONS; dev_info->num_irqs = VFIO_PCI_NUM_IRQS; return 0;} static int mdpy_query_gfx_plane(struct mdev_device *mdev, struct vfio_device_gfx_plane_info *plane){ struct mdev_state *mdev_state = mdev_get_drvdata(mdev); if (plane->flags & VFIO_GFX_PLANE_TYPE_PROBE) { if (plane->flags == (VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION)) return 0; return -EINVAL; } if (plane->flags != VFIO_GFX_PLANE_TYPE_REGION) return -EINVAL; plane->drm_format = mdev_state->type->format; plane->width = mdev_state->type->width; plane->height = mdev_state->type->height; plane->stride = (mdev_state->type->width * mdev_state->type->bytepp); plane->size = mdev_state->memsize; plane->region_index = MDPY_DISPLAY_REGION;Lines 1–500 of 807. Load more
// SPDX-License-Identifier: GPL-2.0 /* * Mediated virtual PCI display host device driver * * See mdpy-defs.h for device specs * * (c) Gerd Hoffmann <kraxel@redhat.com> * * based on mtty driver which is: * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. * Author: Neo Jia <cjia@nvidia.com> * Kirti Wankhede <kwankhede@nvidia.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/init.h> #include <linux/module.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/cdev.h> #include <linux/vfio.h> #include <linux/iommu.h> #include <linux/sysfs.h> #include <linux/mdev.h> #include <linux/pci.h> #include <drm/drm_fourcc.h> #include "mdpy-defs.h" #define MDPY_NAME "mdpy" #define MDPY_CLASS_NAME "mdpy" #define MDPY_CONFIG_SPACE_SIZE 0xff #define MDPY_MEMORY_BAR_OFFSET PAGE_SIZE #define MDPY_DISPLAY_REGION 16 #define STORE_LE16(addr, val) (*(u16 *)addr = val) #define STORE_LE32(addr, val) (*(u32 *)addr = val) MODULE_LICENSE("GPL v2"); static int max_devices = 4; module_param_named(count, max_devices, int, 0444); MODULE_PARM_DESC(count, "number of " MDPY_NAME " devices"); #define MDPY_TYPE_1 "vga" #define MDPY_TYPE_2 "xga" #define MDPY_TYPE_3 "hd" static const struct mdpy_type { const char *name; u32 format; u32 bytepp; u32 width; u32 height; } mdpy_types[] = { { .name = MDPY_CLASS_NAME "-" MDPY_TYPE_1, .format = DRM_FORMAT_XRGB8888, .bytepp = 4, .width = 640, .height = 480, }, { .name = MDPY_CLASS_NAME "-" MDPY_TYPE_2, .format = DRM_FORMAT_XRGB8888, .bytepp = 4, .width = 1024, .height = 768, }, { .name = MDPY_CLASS_NAME "-" MDPY_TYPE_3, .format = DRM_FORMAT_XRGB8888, .bytepp = 4, .width = 1920, .height = 1080, }, }; static dev_t mdpy_devt; static struct class *mdpy_class; static struct cdev mdpy_cdev; static struct device mdpy_dev; static u32 mdpy_count; /* State of each mdev device */ struct mdev_state { u8 *vconfig; u32 bar_mask; struct mutex ops_lock; struct mdev_device *mdev; struct vfio_device_info dev_info; const struct mdpy_type *type; u32 memsize; void *memblk; }; static const struct mdpy_type *mdpy_find_type(struct kobject *kobj) { int i; for (i = 0; i < ARRAY_SIZE(mdpy_types); i++) if (strcmp(mdpy_types[i].name, kobj->name) == 0) return mdpy_types + i; return NULL; } static void mdpy_create_config_space(struct mdev_state *mdev_state) { STORE_LE16((u16 *) &mdev_state->vconfig[PCI_VENDOR_ID], MDPY_PCI_VENDOR_ID); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_DEVICE_ID], MDPY_PCI_DEVICE_ID); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_VENDOR_ID], MDPY_PCI_SUBVENDOR_ID); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_ID], MDPY_PCI_SUBDEVICE_ID); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_COMMAND], PCI_COMMAND_IO | PCI_COMMAND_MEMORY); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_STATUS], PCI_STATUS_CAP_LIST); STORE_LE16((u16 *) &mdev_state->vconfig[PCI_CLASS_DEVICE], PCI_CLASS_DISPLAY_OTHER); mdev_state->vconfig[PCI_CLASS_REVISION] = 0x01; STORE_LE32((u32 *) &mdev_state->vconfig[PCI_BASE_ADDRESS_0], PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_32 | PCI_BASE_ADDRESS_MEM_PREFETCH); mdev_state->bar_mask = ~(mdev_state->memsize) + 1; /* vendor specific capability for the config registers */ mdev_state->vconfig[PCI_CAPABILITY_LIST] = MDPY_VENDORCAP_OFFSET; mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 0] = 0x09; /* vendor cap */ mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 1] = 0x00; /* next ptr */ mdev_state->vconfig[MDPY_VENDORCAP_OFFSET + 2] = MDPY_VENDORCAP_SIZE; STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_FORMAT_OFFSET], mdev_state->type->format); STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_WIDTH_OFFSET], mdev_state->type->width); STORE_LE32((u32 *) &mdev_state->vconfig[MDPY_HEIGHT_OFFSET], mdev_state->type->height); } static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset, char *buf, u32 count) { struct device *dev = mdev_dev(mdev_state->mdev); u32 cfg_addr; switch (offset) { case PCI_BASE_ADDRESS_0: cfg_addr = *(u32 *)buf; if (cfg_addr == 0xffffffff) { cfg_addr = (cfg_addr & mdev_state->bar_mask); } else { cfg_addr &= PCI_BASE_ADDRESS_MEM_MASK; if (cfg_addr) dev_info(dev, "BAR0 @ 0x%x\n", cfg_addr); } cfg_addr |= (mdev_state->vconfig[offset] & ~PCI_BASE_ADDRESS_MEM_MASK); STORE_LE32(&mdev_state->vconfig[offset], cfg_addr); break; } } static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count, loff_t pos, bool is_write) { struct mdev_state *mdev_state = mdev_get_drvdata(mdev); struct device *dev = mdev_dev(mdev); int ret = 0; mutex_lock(&mdev_state->ops_lock); if (pos < MDPY_CONFIG_SPACE_SIZE) { if (is_write) handle_pci_cfg_write(mdev_state, pos, buf, count); else memcpy(buf, (mdev_state->vconfig + pos), count); } else if ((pos >= MDPY_MEMORY_BAR_OFFSET) && (pos + count <= MDPY_MEMORY_BAR_OFFSET + mdev_state->memsize)) { pos -= MDPY_MEMORY_BAR_OFFSET; if (is_write) memcpy(mdev_state->memblk, buf, count); else memcpy(buf, mdev_state->memblk, count); } else { dev_info(dev, "%s: %s @0x%llx (unhandled)\n", __func__, is_write ? "WR" : "RD", pos); ret = -1; goto accessfailed; } ret = count; accessfailed: mutex_unlock(&mdev_state->ops_lock); return ret; } static int mdpy_reset(struct mdev_device *mdev) { struct mdev_state *mdev_state = mdev_get_drvdata(mdev); u32 stride, i; /* initialize with gray gradient */ stride = mdev_state->type->width * mdev_state->type->bytepp; for (i = 0; i < mdev_state->type->height; i++) memset(mdev_state->memblk + i * stride, i * 255 / mdev_state->type->height, stride); return 0; } static int mdpy_create(struct kobject *kobj, struct mdev_device *mdev) { const struct mdpy_type *type = mdpy_find_type(kobj); struct device *dev = mdev_dev(mdev); struct mdev_state *mdev_state; u32 fbsize; if (mdpy_count >= max_devices) return -ENOMEM; mdev_state = kzalloc(sizeof(struct mdev_state), GFP_KERNEL); if (mdev_state == NULL) return -ENOMEM; mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL); if (mdev_state->vconfig == NULL) { kfree(mdev_state); return -ENOMEM; } if (!type) type = &mdpy_types[0]; fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp); mdev_state->memblk = vmalloc_user(fbsize); if (!mdev_state->memblk) { kfree(mdev_state->vconfig); kfree(mdev_state); return -ENOMEM; } dev_info(dev, "%s: %s (%dx%d)\n", __func__, kobj->name, type->width, type->height); mutex_init(&mdev_state->ops_lock); mdev_state->mdev = mdev; mdev_set_drvdata(mdev, mdev_state); mdev_state->type = type; mdev_state->memsize = fbsize; mdpy_create_config_space(mdev_state); mdpy_reset(mdev); mdpy_count++; return 0; } static int mdpy_remove(struct mdev_device *mdev) { struct mdev_state *mdev_state = mdev_get_drvdata(mdev); struct device *dev = mdev_dev(mdev); dev_info(dev, "%s\n", __func__); mdev_set_drvdata(mdev, NULL); vfree(mdev_state->memblk); kfree(mdev_state->vconfig); kfree(mdev_state); mdpy_count--; return 0; } static ssize_t mdpy_read(struct mdev_device *mdev, char __user *buf, size_t count, loff_t *ppos) { unsigned int done = 0; int ret; while (count) { size_t filled; if (count >= 4 && !(*ppos % 4)) { u32 val; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, false); if (ret <= 0) goto read_err; if (copy_to_user(buf, &val, sizeof(val))) goto read_err; filled = 4; } else if (count >= 2 && !(*ppos % 2)) { u16 val; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, false); if (ret <= 0) goto read_err; if (copy_to_user(buf, &val, sizeof(val))) goto read_err; filled = 2; } else { u8 val; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, false); if (ret <= 0) goto read_err; if (copy_to_user(buf, &val, sizeof(val))) goto read_err; filled = 1; } count -= filled; done += filled; *ppos += filled; buf += filled; } return done; read_err: return -EFAULT; } static ssize_t mdpy_write(struct mdev_device *mdev, const char __user *buf, size_t count, loff_t *ppos) { unsigned int done = 0; int ret; while (count) { size_t filled; if (count >= 4 && !(*ppos % 4)) { u32 val; if (copy_from_user(&val, buf, sizeof(val))) goto write_err; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, true); if (ret <= 0) goto write_err; filled = 4; } else if (count >= 2 && !(*ppos % 2)) { u16 val; if (copy_from_user(&val, buf, sizeof(val))) goto write_err; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, true); if (ret <= 0) goto write_err; filled = 2; } else { u8 val; if (copy_from_user(&val, buf, sizeof(val))) goto write_err; ret = mdev_access(mdev, (char *)&val, sizeof(val), *ppos, true); if (ret <= 0) goto write_err; filled = 1; } count -= filled; done += filled; *ppos += filled; buf += filled; } return done; write_err: return -EFAULT; } static int mdpy_mmap(struct mdev_device *mdev, struct vm_area_struct *vma) { struct mdev_state *mdev_state = mdev_get_drvdata(mdev); if (vma->vm_pgoff != MDPY_MEMORY_BAR_OFFSET >> PAGE_SHIFT) return -EINVAL; if (vma->vm_end < vma->vm_start) return -EINVAL; if (vma->vm_end - vma->vm_start > mdev_state->memsize) return -EINVAL; if ((vma->vm_flags & VM_SHARED) == 0) return -EINVAL; return remap_vmalloc_range_partial(vma, vma->vm_start, mdev_state->memblk, 0, vma->vm_end - vma->vm_start); } static int mdpy_get_region_info(struct mdev_device *mdev, struct vfio_region_info *region_info, u16 *cap_type_id, void **cap_type) { struct mdev_state *mdev_state; mdev_state = mdev_get_drvdata(mdev); if (!mdev_state) return -EINVAL; if (region_info->index >= VFIO_PCI_NUM_REGIONS && region_info->index != MDPY_DISPLAY_REGION) return -EINVAL; switch (region_info->index) { case VFIO_PCI_CONFIG_REGION_INDEX: region_info->offset = 0; region_info->size = MDPY_CONFIG_SPACE_SIZE; region_info->flags = (VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE); break; case VFIO_PCI_BAR0_REGION_INDEX: case MDPY_DISPLAY_REGION: region_info->offset = MDPY_MEMORY_BAR_OFFSET; region_info->size = mdev_state->memsize; region_info->flags = (VFIO_REGION_INFO_FLAG_READ | VFIO_REGION_INFO_FLAG_WRITE | VFIO_REGION_INFO_FLAG_MMAP); break; default: region_info->size = 0; region_info->offset = 0; region_info->flags = 0; } return 0; } static int mdpy_get_irq_info(struct mdev_device *mdev, struct vfio_irq_info *irq_info) { irq_info->count = 0; return 0; } static int mdpy_get_device_info(struct mdev_device *mdev, struct vfio_device_info *dev_info) { dev_info->flags = VFIO_DEVICE_FLAGS_PCI; dev_info->num_regions = VFIO_PCI_NUM_REGIONS; dev_info->num_irqs = VFIO_PCI_NUM_IRQS; return 0; } static int mdpy_query_gfx_plane(struct mdev_device *mdev, struct vfio_device_gfx_plane_info *plane) { struct mdev_state *mdev_state = mdev_get_drvdata(mdev); if (plane->flags & VFIO_GFX_PLANE_TYPE_PROBE) { if (plane->flags == (VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION)) return 0; return -EINVAL; } if (plane->flags != VFIO_GFX_PLANE_TYPE_REGION) return -EINVAL; plane->drm_format = mdev_state->type->format; plane->width = mdev_state->type->width; plane->height = mdev_state->type->height; plane->stride = (mdev_state->type->width * mdev_state->type->bytepp); plane->size = mdev_state->memsize; plane->region_index = MDPY_DISPLAY_REGION;