Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SAMPLE_VFIO_MDEV_MTTY) += mtty.o
obj-$(CONFIG_SAMPLE_VFIO_MDEV_MDPY) += mdpy.o
obj-$(CONFIG_SAMPLE_VFIO_MDEV_MDPY_FB) += mdpy-fb.o
obj-$(CONFIG_SAMPLE_VFIO_MDEV_MBOCHS) += mbochs.o
+100
View File
@@ -0,0 +1,100 @@
Using the mtty vfio-mdev sample code
====================================
mtty is a sample vfio-mdev driver that demonstrates how to use the mediated
device framework.
The sample driver creates an mdev device that simulates a serial port over a PCI
card.
1. Build and load the mtty.ko module.
This step creates a dummy device, /sys/devices/virtual/mtty/mtty/
Files in this device directory in sysfs are similar to the following::
# tree /sys/devices/virtual/mtty/mtty/
/sys/devices/virtual/mtty/mtty/
|-- mdev_supported_types
| |-- mtty-1
| | |-- available_instances
| | |-- create
| | |-- device_api
| | |-- devices
| | `-- name
| `-- mtty-2
| |-- available_instances
| |-- create
| |-- device_api
| |-- devices
| `-- name
|-- mtty_dev
| `-- sample_mtty_dev
|-- power
| |-- autosuspend_delay_ms
| |-- control
| |-- runtime_active_time
| |-- runtime_status
| `-- runtime_suspended_time
|-- subsystem -> ../../../../class/mtty
`-- uevent
2. Create a mediated device by using the dummy device that you created in the
previous step::
# echo "83b8f4f2-509f-382f-3c1e-e6bfe0fa1001" > \
/sys/devices/virtual/mtty/mtty/mdev_supported_types/mtty-2/create
3. Add parameters to qemu-kvm::
-device vfio-pci,\
sysfsdev=/sys/bus/mdev/devices/83b8f4f2-509f-382f-3c1e-e6bfe0fa1001
4. Boot the VM.
In the Linux guest VM, with no hardware on the host, the device appears
as follows::
# lspci -s 00:05.0 -xxvv
00:05.0 Serial controller: Device 4348:3253 (rev 10) (prog-if 02 [16550])
Subsystem: Device 4348:3253
Physical Slot: 5
Control: I/O+ Mem- BusMaster- SpecCycle- MemWINV- VGASnoop- ParErr-
Stepping- SERR- FastB2B- DisINTx-
Status: Cap- 66MHz- UDF- FastB2B- ParErr- DEVSEL=medium >TAbort-
<TAbort- <MAbort- >SERR- <PERR- INTx-
Interrupt: pin A routed to IRQ 10
Region 0: I/O ports at c150 [size=8]
Region 1: I/O ports at c158 [size=8]
Kernel driver in use: serial
00: 48 43 53 32 01 00 00 02 10 02 00 07 00 00 00 00
10: 51 c1 00 00 59 c1 00 00 00 00 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 48 43 53 32
30: 00 00 00 00 00 00 00 00 00 00 00 00 0a 01 00 00
In the Linux guest VM, dmesg output for the device is as follows:
serial 0000:00:05.0: PCI INT A -> Link[LNKA] -> GSI 10 (level, high) -> IRQ 10
0000:00:05.0: ttyS1 at I/O 0xc150 (irq = 10) is a 16550A
0000:00:05.0: ttyS2 at I/O 0xc158 (irq = 10) is a 16550A
5. In the Linux guest VM, check the serial ports::
# setserial -g /dev/ttyS*
/dev/ttyS0, UART: 16550A, Port: 0x03f8, IRQ: 4
/dev/ttyS1, UART: 16550A, Port: 0xc150, IRQ: 10
/dev/ttyS2, UART: 16550A, Port: 0xc158, IRQ: 10
6. Using minicom or any terminal emulation program, open port /dev/ttyS1 or
/dev/ttyS2 with hardware flow control disabled.
7. Type data on the minicom terminal or send data to the terminal emulation
program and read the data.
Data is loop backed from hosts mtty driver.
8. Destroy the mediated device that you created::
# echo 1 > /sys/bus/mdev/devices/83b8f4f2-509f-382f-3c1e-e6bfe0fa1001/remove
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Simple pci display device.
*
* Framebuffer memory is pci bar 0.
* Configuration (read-only) is in pci config space.
* Format field uses drm fourcc codes.
* ATM only DRM_FORMAT_XRGB8888 is supported.
*/
/* pci ids */
#define MDPY_PCI_VENDOR_ID PCI_VENDOR_ID_REDHAT
#define MDPY_PCI_DEVICE_ID 0x000f
#define MDPY_PCI_SUBVENDOR_ID PCI_SUBVENDOR_ID_REDHAT_QUMRANET
#define MDPY_PCI_SUBDEVICE_ID PCI_SUBDEVICE_ID_QEMU
/* pci cfg space offsets for fb config (dword) */
#define MDPY_VENDORCAP_OFFSET 0x40
#define MDPY_VENDORCAP_SIZE 0x10
#define MDPY_FORMAT_OFFSET (MDPY_VENDORCAP_OFFSET + 0x04)
#define MDPY_WIDTH_OFFSET (MDPY_VENDORCAP_OFFSET + 0x08)
#define MDPY_HEIGHT_OFFSET (MDPY_VENDORCAP_OFFSET + 0x0c)
+233
View File
@@ -0,0 +1,233 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Framebuffer driver for mdpy (mediated virtual pci display device).
*
* See mdpy-defs.h for device specs
*
* (c) Gerd Hoffmann <kraxel@redhat.com>
*
* Using some code snippets from simplefb and cirrusfb.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/errno.h>
#include <linux/fb.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <drm/drm_fourcc.h>
#include "mdpy-defs.h"
static const struct fb_fix_screeninfo mdpy_fb_fix = {
.id = "mdpy-fb",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.accel = FB_ACCEL_NONE,
};
static const struct fb_var_screeninfo mdpy_fb_var = {
.height = -1,
.width = -1,
.activate = FB_ACTIVATE_NOW,
.vmode = FB_VMODE_NONINTERLACED,
.bits_per_pixel = 32,
.transp.offset = 24,
.red.offset = 16,
.green.offset = 8,
.blue.offset = 0,
.transp.length = 8,
.red.length = 8,
.green.length = 8,
.blue.length = 8,
};
#define PSEUDO_PALETTE_SIZE 16
struct mdpy_fb_par {
u32 palette[PSEUDO_PALETTE_SIZE];
};
static int mdpy_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info)
{
u32 *pal = info->pseudo_palette;
u32 cr = red >> (16 - info->var.red.length);
u32 cg = green >> (16 - info->var.green.length);
u32 cb = blue >> (16 - info->var.blue.length);
u32 value, mask;
if (regno >= PSEUDO_PALETTE_SIZE)
return -EINVAL;
value = (cr << info->var.red.offset) |
(cg << info->var.green.offset) |
(cb << info->var.blue.offset);
if (info->var.transp.length > 0) {
mask = (1 << info->var.transp.length) - 1;
mask <<= info->var.transp.offset;
value |= mask;
}
pal[regno] = value;
return 0;
}
static void mdpy_fb_destroy(struct fb_info *info)
{
if (info->screen_base)
iounmap(info->screen_base);
}
static const struct fb_ops mdpy_fb_ops = {
.owner = THIS_MODULE,
FB_DEFAULT_IOMEM_OPS,
.fb_destroy = mdpy_fb_destroy,
.fb_setcolreg = mdpy_fb_setcolreg,
};
static int mdpy_fb_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct fb_info *info;
struct mdpy_fb_par *par;
u32 format, width, height;
int ret;
ret = pci_enable_device(pdev);
if (ret < 0)
return ret;
ret = pci_request_regions(pdev, "mdpy-fb");
if (ret < 0)
goto err_disable_dev;
pci_read_config_dword(pdev, MDPY_FORMAT_OFFSET, &format);
pci_read_config_dword(pdev, MDPY_WIDTH_OFFSET, &width);
pci_read_config_dword(pdev, MDPY_HEIGHT_OFFSET, &height);
if (format != DRM_FORMAT_XRGB8888) {
pci_err(pdev, "format mismatch (0x%x != 0x%x)\n",
format, DRM_FORMAT_XRGB8888);
ret = -EINVAL;
goto err_release_regions;
}
if (width < 100 || width > 10000) {
pci_err(pdev, "width (%d) out of range\n", width);
ret = -EINVAL;
goto err_release_regions;
}
if (height < 100 || height > 10000) {
pci_err(pdev, "height (%d) out of range\n", height);
ret = -EINVAL;
goto err_release_regions;
}
pci_info(pdev, "mdpy found: %dx%d framebuffer\n",
width, height);
info = framebuffer_alloc(sizeof(struct mdpy_fb_par), &pdev->dev);
if (!info) {
ret = -ENOMEM;
goto err_release_regions;
}
pci_set_drvdata(pdev, info);
par = info->par;
info->fix = mdpy_fb_fix;
info->fix.smem_start = pci_resource_start(pdev, 0);
info->fix.smem_len = pci_resource_len(pdev, 0);
info->fix.line_length = width * 4;
info->var = mdpy_fb_var;
info->var.xres = width;
info->var.yres = height;
info->var.xres_virtual = width;
info->var.yres_virtual = height;
info->screen_size = info->fix.smem_len;
info->screen_base = ioremap(info->fix.smem_start,
info->screen_size);
if (!info->screen_base) {
pci_err(pdev, "ioremap(pcibar) failed\n");
ret = -EIO;
goto err_release_fb;
}
info->fbops = &mdpy_fb_ops;
info->pseudo_palette = par->palette;
ret = register_framebuffer(info);
if (ret < 0) {
pci_err(pdev, "mdpy-fb device register failed: %d\n", ret);
goto err_unmap;
}
pci_info(pdev, "fb%d registered\n", info->node);
return 0;
err_unmap:
iounmap(info->screen_base);
err_release_fb:
framebuffer_release(info);
err_release_regions:
pci_release_regions(pdev);
err_disable_dev:
pci_disable_device(pdev);
return ret;
}
static void mdpy_fb_remove(struct pci_dev *pdev)
{
struct fb_info *info = pci_get_drvdata(pdev);
unregister_framebuffer(info);
iounmap(info->screen_base);
framebuffer_release(info);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
static struct pci_device_id mdpy_fb_pci_table[] = {
{
.vendor = MDPY_PCI_VENDOR_ID,
.device = MDPY_PCI_DEVICE_ID,
.subvendor = MDPY_PCI_SUBVENDOR_ID,
.subdevice = MDPY_PCI_SUBDEVICE_ID,
}, {
/* end of list */
}
};
static struct pci_driver mdpy_fb_pci_driver = {
.name = "mdpy-fb",
.id_table = mdpy_fb_pci_table,
.probe = mdpy_fb_probe,
.remove = mdpy_fb_remove,
};
static int __init mdpy_fb_init(void)
{
int ret;
ret = pci_register_driver(&mdpy_fb_pci_driver);
if (ret)
return ret;
return 0;
}
module_init(mdpy_fb_init);
MODULE_DEVICE_TABLE(pci, mdpy_fb_pci_table);
MODULE_DESCRIPTION("Framebuffer driver for mdpy (mediated virtual pci display device)");
MODULE_LICENSE("GPL v2");
+743
View File
@@ -0,0 +1,743 @@
// 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/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_DESCRIPTION("Mediated virtual PCI display host device driver");
MODULE_LICENSE("GPL v2");
#define MDPY_TYPE_1 "vga"
#define MDPY_TYPE_2 "xga"
#define MDPY_TYPE_3 "hd"
static struct mdpy_type {
struct mdev_type type;
u32 format;
u32 bytepp;
u32 width;
u32 height;
} mdpy_types[] = {
{
.type.sysfs_name = MDPY_TYPE_1,
.type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_1,
.format = DRM_FORMAT_XRGB8888,
.bytepp = 4,
.width = 640,
.height = 480,
}, {
.type.sysfs_name = MDPY_TYPE_2,
.type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_2,
.format = DRM_FORMAT_XRGB8888,
.bytepp = 4,
.width = 1024,
.height = 768,
}, {
.type.sysfs_name = MDPY_TYPE_3,
.type.pretty_name = MDPY_CLASS_NAME "-" MDPY_TYPE_3,
.format = DRM_FORMAT_XRGB8888,
.bytepp = 4,
.width = 1920,
.height = 1080,
},
};
static struct mdev_type *mdpy_mdev_types[] = {
&mdpy_types[0].type,
&mdpy_types[1].type,
&mdpy_types[2].type,
};
static dev_t mdpy_devt;
static const struct class mdpy_class = {
.name = MDPY_CLASS_NAME,
};
static struct cdev mdpy_cdev;
static struct device mdpy_dev;
static struct mdev_parent mdpy_parent;
static const struct vfio_device_ops mdpy_dev_ops;
/* State of each mdev device */
struct mdev_state {
struct vfio_device vdev;
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 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_state *mdev_state, char *buf,
size_t count, loff_t pos, bool is_write)
{
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(mdev_state->vdev.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_state *mdev_state)
{
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_init_dev(struct vfio_device *vdev)
{
struct mdev_state *mdev_state =
container_of(vdev, struct mdev_state, vdev);
struct mdev_device *mdev = to_mdev_device(vdev->dev);
const struct mdpy_type *type =
container_of(mdev->type, struct mdpy_type, type);
u32 fbsize;
int ret = -ENOMEM;
mdev_state->vconfig = kzalloc(MDPY_CONFIG_SPACE_SIZE, GFP_KERNEL);
if (!mdev_state->vconfig)
return ret;
fbsize = roundup_pow_of_two(type->width * type->height * type->bytepp);
mdev_state->memblk = vmalloc_user(fbsize);
if (!mdev_state->memblk)
goto out_vconfig;
mutex_init(&mdev_state->ops_lock);
mdev_state->mdev = mdev;
mdev_state->type = type;
mdev_state->memsize = fbsize;
mdpy_create_config_space(mdev_state);
mdpy_reset(mdev_state);
dev_info(vdev->dev, "%s: %s (%dx%d)\n", __func__, type->type.pretty_name,
type->width, type->height);
return 0;
out_vconfig:
kfree(mdev_state->vconfig);
return ret;
}
static int mdpy_probe(struct mdev_device *mdev)
{
struct mdev_state *mdev_state;
int ret;
mdev_state = vfio_alloc_device(mdev_state, vdev, &mdev->dev,
&mdpy_dev_ops);
if (IS_ERR(mdev_state))
return PTR_ERR(mdev_state);
ret = vfio_register_emulated_iommu_dev(&mdev_state->vdev);
if (ret)
goto err_put_vdev;
dev_set_drvdata(&mdev->dev, mdev_state);
return 0;
err_put_vdev:
vfio_put_device(&mdev_state->vdev);
return ret;
}
static void mdpy_release_dev(struct vfio_device *vdev)
{
struct mdev_state *mdev_state =
container_of(vdev, struct mdev_state, vdev);
vfree(mdev_state->memblk);
kfree(mdev_state->vconfig);
}
static void mdpy_remove(struct mdev_device *mdev)
{
struct mdev_state *mdev_state = dev_get_drvdata(&mdev->dev);
dev_info(&mdev->dev, "%s\n", __func__);
vfio_unregister_group_dev(&mdev_state->vdev);
vfio_put_device(&mdev_state->vdev);
}
static ssize_t mdpy_read(struct vfio_device *vdev, char __user *buf,
size_t count, loff_t *ppos)
{
struct mdev_state *mdev_state =
container_of(vdev, struct mdev_state, vdev);
unsigned int done = 0;
int ret;
while (count) {
size_t filled;
if (count >= 4 && !(*ppos % 4)) {
u32 val;
ret = mdev_access(mdev_state, (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_state, (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_state, (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 vfio_device *vdev, const char __user *buf,
size_t count, loff_t *ppos)
{
struct mdev_state *mdev_state =
container_of(vdev, struct mdev_state, vdev);
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_state, (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_state, (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_state, (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 vfio_device *vdev, struct vm_area_struct *vma)
{
struct mdev_state *mdev_state =
container_of(vdev, struct mdev_state, vdev);
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(vma, mdev_state->memblk, 0);
}
static int mdpy_ioctl_get_region_info(struct vfio_device *vdev,
struct vfio_region_info *region_info,
struct vfio_info_cap *caps)
{
struct mdev_state *mdev_state =
container_of(vdev, struct mdev_state, vdev);
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 vfio_irq_info *irq_info)
{
irq_info->count = 0;
return 0;
}
static int mdpy_get_device_info(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_state *mdev_state,
struct vfio_device_gfx_plane_info *plane)
{
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;
/* unused */
plane->drm_format_mod = 0;
plane->x_pos = 0;
plane->y_pos = 0;
plane->x_hot = 0;
plane->y_hot = 0;
return 0;
}
static long mdpy_ioctl(struct vfio_device *vdev, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
unsigned long minsz;
struct mdev_state *mdev_state =
container_of(vdev, struct mdev_state, vdev);
switch (cmd) {
case VFIO_DEVICE_GET_INFO:
{
struct vfio_device_info info;
minsz = offsetofend(struct vfio_device_info, num_irqs);
if (copy_from_user(&info, (void __user *)arg, minsz))
return -EFAULT;
if (info.argsz < minsz)
return -EINVAL;
ret = mdpy_get_device_info(&info);
if (ret)
return ret;
memcpy(&mdev_state->dev_info, &info, sizeof(info));
if (copy_to_user((void __user *)arg, &info, minsz))
return -EFAULT;
return 0;
}
case VFIO_DEVICE_GET_IRQ_INFO:
{
struct vfio_irq_info info;
minsz = offsetofend(struct vfio_irq_info, count);
if (copy_from_user(&info, (void __user *)arg, minsz))
return -EFAULT;
if ((info.argsz < minsz) ||
(info.index >= mdev_state->dev_info.num_irqs))
return -EINVAL;
ret = mdpy_get_irq_info(&info);
if (ret)
return ret;
if (copy_to_user((void __user *)arg, &info, minsz))
return -EFAULT;
return 0;
}
case VFIO_DEVICE_QUERY_GFX_PLANE:
{
struct vfio_device_gfx_plane_info plane = {};
minsz = offsetofend(struct vfio_device_gfx_plane_info,
region_index);
if (copy_from_user(&plane, (void __user *)arg, minsz))
return -EFAULT;
if (plane.argsz < minsz)
return -EINVAL;
ret = mdpy_query_gfx_plane(mdev_state, &plane);
if (ret)
return ret;
if (copy_to_user((void __user *)arg, &plane, minsz))
return -EFAULT;
return 0;
}
case VFIO_DEVICE_SET_IRQS:
return -EINVAL;
case VFIO_DEVICE_RESET:
return mdpy_reset(mdev_state);
}
return -ENOTTY;
}
static ssize_t
resolution_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct mdev_state *mdev_state = dev_get_drvdata(dev);
return sprintf(buf, "%dx%d\n",
mdev_state->type->width,
mdev_state->type->height);
}
static DEVICE_ATTR_RO(resolution);
static struct attribute *mdev_dev_attrs[] = {
&dev_attr_resolution.attr,
NULL,
};
static const struct attribute_group mdev_dev_group = {
.name = "vendor",
.attrs = mdev_dev_attrs,
};
static const struct attribute_group *mdev_dev_groups[] = {
&mdev_dev_group,
NULL,
};
static ssize_t mdpy_show_description(struct mdev_type *mtype, char *buf)
{
struct mdpy_type *type = container_of(mtype, struct mdpy_type, type);
return sprintf(buf, "virtual display, %dx%d framebuffer\n",
type->width, type->height);
}
static const struct vfio_device_ops mdpy_dev_ops = {
.init = mdpy_init_dev,
.release = mdpy_release_dev,
.read = mdpy_read,
.write = mdpy_write,
.ioctl = mdpy_ioctl,
.get_region_info_caps = mdpy_ioctl_get_region_info,
.mmap = mdpy_mmap,
.bind_iommufd = vfio_iommufd_emulated_bind,
.unbind_iommufd = vfio_iommufd_emulated_unbind,
.attach_ioas = vfio_iommufd_emulated_attach_ioas,
.detach_ioas = vfio_iommufd_emulated_detach_ioas,
};
static struct mdev_driver mdpy_driver = {
.device_api = VFIO_DEVICE_API_PCI_STRING,
.max_instances = 4,
.driver = {
.name = "mdpy",
.owner = THIS_MODULE,
.mod_name = KBUILD_MODNAME,
.dev_groups = mdev_dev_groups,
},
.probe = mdpy_probe,
.remove = mdpy_remove,
.show_description = mdpy_show_description,
};
static const struct file_operations vd_fops = {
.owner = THIS_MODULE,
};
static void mdpy_device_release(struct device *dev)
{
/* nothing */
}
static int __init mdpy_dev_init(void)
{
int ret = 0;
ret = alloc_chrdev_region(&mdpy_devt, 0, MINORMASK + 1, MDPY_NAME);
if (ret < 0) {
pr_err("Error: failed to register mdpy_dev, err: %d\n", ret);
return ret;
}
cdev_init(&mdpy_cdev, &vd_fops);
cdev_add(&mdpy_cdev, mdpy_devt, MINORMASK + 1);
pr_info("%s: major %d\n", __func__, MAJOR(mdpy_devt));
ret = mdev_register_driver(&mdpy_driver);
if (ret)
goto err_cdev;
ret = class_register(&mdpy_class);
if (ret)
goto err_driver;
mdpy_dev.class = &mdpy_class;
mdpy_dev.release = mdpy_device_release;
dev_set_name(&mdpy_dev, "%s", MDPY_NAME);
ret = device_register(&mdpy_dev);
if (ret)
goto err_put;
ret = mdev_register_parent(&mdpy_parent, &mdpy_dev, &mdpy_driver,
mdpy_mdev_types,
ARRAY_SIZE(mdpy_mdev_types));
if (ret)
goto err_device;
return 0;
err_device:
device_del(&mdpy_dev);
err_put:
put_device(&mdpy_dev);
class_unregister(&mdpy_class);
err_driver:
mdev_unregister_driver(&mdpy_driver);
err_cdev:
cdev_del(&mdpy_cdev);
unregister_chrdev_region(mdpy_devt, MINORMASK + 1);
return ret;
}
static void __exit mdpy_dev_exit(void)
{
mdpy_dev.bus = NULL;
mdev_unregister_parent(&mdpy_parent);
device_unregister(&mdpy_dev);
mdev_unregister_driver(&mdpy_driver);
cdev_del(&mdpy_cdev);
unregister_chrdev_region(mdpy_devt, MINORMASK + 1);
class_unregister(&mdpy_class);
}
module_param_named(count, mdpy_driver.max_instances, int, 0444);
MODULE_PARM_DESC(count, "number of " MDPY_NAME " devices");
module_init(mdpy_dev_init)
module_exit(mdpy_dev_exit)
File diff suppressed because it is too large Load Diff