162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Mediated virtual PCI display host device driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Emulate enough of qemu stdvga to make bochs-drm.ko happy.  That is
662306a36Sopenharmony_ci * basically the vram memory bar and the bochs dispi interface vbe
762306a36Sopenharmony_ci * registers in the mmio register bar.	Specifically it does *not*
862306a36Sopenharmony_ci * include any legacy vga stuff.  Device looks a lot like "qemu -device
962306a36Sopenharmony_ci * secondary-vga".
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *   (c) Gerd Hoffmann <kraxel@redhat.com>
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * based on mtty driver which is:
1462306a36Sopenharmony_ci *   Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
1562306a36Sopenharmony_ci *	 Author: Neo Jia <cjia@nvidia.com>
1662306a36Sopenharmony_ci *		 Kirti Wankhede <kwankhede@nvidia.com>
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
1962306a36Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as
2062306a36Sopenharmony_ci * published by the Free Software Foundation.
2162306a36Sopenharmony_ci */
2262306a36Sopenharmony_ci#include <linux/init.h>
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci#include <linux/kernel.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <linux/vmalloc.h>
2762306a36Sopenharmony_ci#include <linux/cdev.h>
2862306a36Sopenharmony_ci#include <linux/vfio.h>
2962306a36Sopenharmony_ci#include <linux/iommu.h>
3062306a36Sopenharmony_ci#include <linux/sysfs.h>
3162306a36Sopenharmony_ci#include <linux/mdev.h>
3262306a36Sopenharmony_ci#include <linux/pci.h>
3362306a36Sopenharmony_ci#include <linux/dma-buf.h>
3462306a36Sopenharmony_ci#include <linux/highmem.h>
3562306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
3662306a36Sopenharmony_ci#include <drm/drm_rect.h>
3762306a36Sopenharmony_ci#include <drm/drm_modeset_lock.h>
3862306a36Sopenharmony_ci#include <drm/drm_property.h>
3962306a36Sopenharmony_ci#include <drm/drm_plane.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define VBE_DISPI_INDEX_ID		0x0
4362306a36Sopenharmony_ci#define VBE_DISPI_INDEX_XRES		0x1
4462306a36Sopenharmony_ci#define VBE_DISPI_INDEX_YRES		0x2
4562306a36Sopenharmony_ci#define VBE_DISPI_INDEX_BPP		0x3
4662306a36Sopenharmony_ci#define VBE_DISPI_INDEX_ENABLE		0x4
4762306a36Sopenharmony_ci#define VBE_DISPI_INDEX_BANK		0x5
4862306a36Sopenharmony_ci#define VBE_DISPI_INDEX_VIRT_WIDTH	0x6
4962306a36Sopenharmony_ci#define VBE_DISPI_INDEX_VIRT_HEIGHT	0x7
5062306a36Sopenharmony_ci#define VBE_DISPI_INDEX_X_OFFSET	0x8
5162306a36Sopenharmony_ci#define VBE_DISPI_INDEX_Y_OFFSET	0x9
5262306a36Sopenharmony_ci#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa
5362306a36Sopenharmony_ci#define VBE_DISPI_INDEX_COUNT		0xb
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define VBE_DISPI_ID0			0xB0C0
5662306a36Sopenharmony_ci#define VBE_DISPI_ID1			0xB0C1
5762306a36Sopenharmony_ci#define VBE_DISPI_ID2			0xB0C2
5862306a36Sopenharmony_ci#define VBE_DISPI_ID3			0xB0C3
5962306a36Sopenharmony_ci#define VBE_DISPI_ID4			0xB0C4
6062306a36Sopenharmony_ci#define VBE_DISPI_ID5			0xB0C5
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define VBE_DISPI_DISABLED		0x00
6362306a36Sopenharmony_ci#define VBE_DISPI_ENABLED		0x01
6462306a36Sopenharmony_ci#define VBE_DISPI_GETCAPS		0x02
6562306a36Sopenharmony_ci#define VBE_DISPI_8BIT_DAC		0x20
6662306a36Sopenharmony_ci#define VBE_DISPI_LFB_ENABLED		0x40
6762306a36Sopenharmony_ci#define VBE_DISPI_NOCLEARMEM		0x80
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define MBOCHS_NAME		  "mbochs"
7162306a36Sopenharmony_ci#define MBOCHS_CLASS_NAME	  "mbochs"
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define MBOCHS_EDID_REGION_INDEX  VFIO_PCI_NUM_REGIONS
7462306a36Sopenharmony_ci#define MBOCHS_NUM_REGIONS        (MBOCHS_EDID_REGION_INDEX+1)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define MBOCHS_CONFIG_SPACE_SIZE  0xff
7762306a36Sopenharmony_ci#define MBOCHS_MMIO_BAR_OFFSET	  PAGE_SIZE
7862306a36Sopenharmony_ci#define MBOCHS_MMIO_BAR_SIZE	  PAGE_SIZE
7962306a36Sopenharmony_ci#define MBOCHS_EDID_OFFSET	  (MBOCHS_MMIO_BAR_OFFSET +	\
8062306a36Sopenharmony_ci				   MBOCHS_MMIO_BAR_SIZE)
8162306a36Sopenharmony_ci#define MBOCHS_EDID_SIZE	  PAGE_SIZE
8262306a36Sopenharmony_ci#define MBOCHS_MEMORY_BAR_OFFSET  (MBOCHS_EDID_OFFSET + \
8362306a36Sopenharmony_ci				   MBOCHS_EDID_SIZE)
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define MBOCHS_EDID_BLOB_OFFSET   (MBOCHS_EDID_SIZE/2)
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define STORE_LE16(addr, val)	(*(u16 *)addr = val)
8862306a36Sopenharmony_ci#define STORE_LE32(addr, val)	(*(u32 *)addr = val)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int max_mbytes = 256;
9462306a36Sopenharmony_cimodule_param_named(count, max_mbytes, int, 0444);
9562306a36Sopenharmony_ciMODULE_PARM_DESC(mem, "megabytes available to " MBOCHS_NAME " devices");
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define MBOCHS_TYPE_1 "small"
9962306a36Sopenharmony_ci#define MBOCHS_TYPE_2 "medium"
10062306a36Sopenharmony_ci#define MBOCHS_TYPE_3 "large"
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct mbochs_type {
10362306a36Sopenharmony_ci	struct mdev_type type;
10462306a36Sopenharmony_ci	u32 mbytes;
10562306a36Sopenharmony_ci	u32 max_x;
10662306a36Sopenharmony_ci	u32 max_y;
10762306a36Sopenharmony_ci} mbochs_types[] = {
10862306a36Sopenharmony_ci	{
10962306a36Sopenharmony_ci		.type.sysfs_name	= MBOCHS_TYPE_1,
11062306a36Sopenharmony_ci		.type.pretty_name	= MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_1,
11162306a36Sopenharmony_ci		.mbytes = 4,
11262306a36Sopenharmony_ci		.max_x  = 800,
11362306a36Sopenharmony_ci		.max_y  = 600,
11462306a36Sopenharmony_ci	}, {
11562306a36Sopenharmony_ci		.type.sysfs_name	= MBOCHS_TYPE_2,
11662306a36Sopenharmony_ci		.type.pretty_name	= MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_2,
11762306a36Sopenharmony_ci		.mbytes = 16,
11862306a36Sopenharmony_ci		.max_x  = 1920,
11962306a36Sopenharmony_ci		.max_y  = 1440,
12062306a36Sopenharmony_ci	}, {
12162306a36Sopenharmony_ci		.type.sysfs_name	= MBOCHS_TYPE_3,
12262306a36Sopenharmony_ci		.type.pretty_name	= MBOCHS_CLASS_NAME "-" MBOCHS_TYPE_3,
12362306a36Sopenharmony_ci		.mbytes = 64,
12462306a36Sopenharmony_ci		.max_x  = 0,
12562306a36Sopenharmony_ci		.max_y  = 0,
12662306a36Sopenharmony_ci	},
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic struct mdev_type *mbochs_mdev_types[] = {
13062306a36Sopenharmony_ci	&mbochs_types[0].type,
13162306a36Sopenharmony_ci	&mbochs_types[1].type,
13262306a36Sopenharmony_ci	&mbochs_types[2].type,
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic dev_t		mbochs_devt;
13662306a36Sopenharmony_cistatic struct class	*mbochs_class;
13762306a36Sopenharmony_cistatic struct cdev	mbochs_cdev;
13862306a36Sopenharmony_cistatic struct device	mbochs_dev;
13962306a36Sopenharmony_cistatic struct mdev_parent mbochs_parent;
14062306a36Sopenharmony_cistatic atomic_t mbochs_avail_mbytes;
14162306a36Sopenharmony_cistatic const struct vfio_device_ops mbochs_dev_ops;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistruct vfio_region_info_ext {
14462306a36Sopenharmony_ci	struct vfio_region_info          base;
14562306a36Sopenharmony_ci	struct vfio_region_info_cap_type type;
14662306a36Sopenharmony_ci};
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistruct mbochs_mode {
14962306a36Sopenharmony_ci	u32 drm_format;
15062306a36Sopenharmony_ci	u32 bytepp;
15162306a36Sopenharmony_ci	u32 width;
15262306a36Sopenharmony_ci	u32 height;
15362306a36Sopenharmony_ci	u32 stride;
15462306a36Sopenharmony_ci	u32 __pad;
15562306a36Sopenharmony_ci	u64 offset;
15662306a36Sopenharmony_ci	u64 size;
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistruct mbochs_dmabuf {
16062306a36Sopenharmony_ci	struct mbochs_mode mode;
16162306a36Sopenharmony_ci	u32 id;
16262306a36Sopenharmony_ci	struct page **pages;
16362306a36Sopenharmony_ci	pgoff_t pagecount;
16462306a36Sopenharmony_ci	struct dma_buf *buf;
16562306a36Sopenharmony_ci	struct mdev_state *mdev_state;
16662306a36Sopenharmony_ci	struct list_head next;
16762306a36Sopenharmony_ci	bool unlinked;
16862306a36Sopenharmony_ci};
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci/* State of each mdev device */
17162306a36Sopenharmony_cistruct mdev_state {
17262306a36Sopenharmony_ci	struct vfio_device vdev;
17362306a36Sopenharmony_ci	u8 *vconfig;
17462306a36Sopenharmony_ci	u64 bar_mask[3];
17562306a36Sopenharmony_ci	u32 memory_bar_mask;
17662306a36Sopenharmony_ci	struct mutex ops_lock;
17762306a36Sopenharmony_ci	struct mdev_device *mdev;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	const struct mbochs_type *type;
18062306a36Sopenharmony_ci	u16 vbe[VBE_DISPI_INDEX_COUNT];
18162306a36Sopenharmony_ci	u64 memsize;
18262306a36Sopenharmony_ci	struct page **pages;
18362306a36Sopenharmony_ci	pgoff_t pagecount;
18462306a36Sopenharmony_ci	struct vfio_region_gfx_edid edid_regs;
18562306a36Sopenharmony_ci	u8 edid_blob[0x400];
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	struct list_head dmabufs;
18862306a36Sopenharmony_ci	u32 active_id;
18962306a36Sopenharmony_ci	u32 next_id;
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic const char *vbe_name_list[VBE_DISPI_INDEX_COUNT] = {
19362306a36Sopenharmony_ci	[VBE_DISPI_INDEX_ID]               = "id",
19462306a36Sopenharmony_ci	[VBE_DISPI_INDEX_XRES]             = "xres",
19562306a36Sopenharmony_ci	[VBE_DISPI_INDEX_YRES]             = "yres",
19662306a36Sopenharmony_ci	[VBE_DISPI_INDEX_BPP]              = "bpp",
19762306a36Sopenharmony_ci	[VBE_DISPI_INDEX_ENABLE]           = "enable",
19862306a36Sopenharmony_ci	[VBE_DISPI_INDEX_BANK]             = "bank",
19962306a36Sopenharmony_ci	[VBE_DISPI_INDEX_VIRT_WIDTH]       = "virt-width",
20062306a36Sopenharmony_ci	[VBE_DISPI_INDEX_VIRT_HEIGHT]      = "virt-height",
20162306a36Sopenharmony_ci	[VBE_DISPI_INDEX_X_OFFSET]         = "x-offset",
20262306a36Sopenharmony_ci	[VBE_DISPI_INDEX_Y_OFFSET]         = "y-offset",
20362306a36Sopenharmony_ci	[VBE_DISPI_INDEX_VIDEO_MEMORY_64K] = "video-mem",
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic const char *vbe_name(u32 index)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	if (index < ARRAY_SIZE(vbe_name_list))
20962306a36Sopenharmony_ci		return vbe_name_list[index];
21062306a36Sopenharmony_ci	return "(invalid)";
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic struct page *__mbochs_get_page(struct mdev_state *mdev_state,
21462306a36Sopenharmony_ci				      pgoff_t pgoff);
21562306a36Sopenharmony_cistatic struct page *mbochs_get_page(struct mdev_state *mdev_state,
21662306a36Sopenharmony_ci				    pgoff_t pgoff);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void mbochs_create_config_space(struct mdev_state *mdev_state)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_VENDOR_ID],
22162306a36Sopenharmony_ci		   0x1234);
22262306a36Sopenharmony_ci	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_DEVICE_ID],
22362306a36Sopenharmony_ci		   0x1111);
22462306a36Sopenharmony_ci	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_VENDOR_ID],
22562306a36Sopenharmony_ci		   PCI_SUBVENDOR_ID_REDHAT_QUMRANET);
22662306a36Sopenharmony_ci	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_SUBSYSTEM_ID],
22762306a36Sopenharmony_ci		   PCI_SUBDEVICE_ID_QEMU);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_COMMAND],
23062306a36Sopenharmony_ci		   PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
23162306a36Sopenharmony_ci	STORE_LE16((u16 *) &mdev_state->vconfig[PCI_CLASS_DEVICE],
23262306a36Sopenharmony_ci		   PCI_CLASS_DISPLAY_OTHER);
23362306a36Sopenharmony_ci	mdev_state->vconfig[PCI_CLASS_REVISION] =  0x01;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	STORE_LE32((u32 *) &mdev_state->vconfig[PCI_BASE_ADDRESS_0],
23662306a36Sopenharmony_ci		   PCI_BASE_ADDRESS_SPACE_MEMORY |
23762306a36Sopenharmony_ci		   PCI_BASE_ADDRESS_MEM_TYPE_32	 |
23862306a36Sopenharmony_ci		   PCI_BASE_ADDRESS_MEM_PREFETCH);
23962306a36Sopenharmony_ci	mdev_state->bar_mask[0] = ~(mdev_state->memsize) + 1;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	STORE_LE32((u32 *) &mdev_state->vconfig[PCI_BASE_ADDRESS_2],
24262306a36Sopenharmony_ci		   PCI_BASE_ADDRESS_SPACE_MEMORY |
24362306a36Sopenharmony_ci		   PCI_BASE_ADDRESS_MEM_TYPE_32);
24462306a36Sopenharmony_ci	mdev_state->bar_mask[2] = ~(MBOCHS_MMIO_BAR_SIZE) + 1;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic int mbochs_check_framebuffer(struct mdev_state *mdev_state,
24862306a36Sopenharmony_ci				    struct mbochs_mode *mode)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct device *dev = mdev_dev(mdev_state->mdev);
25162306a36Sopenharmony_ci	u16 *vbe = mdev_state->vbe;
25262306a36Sopenharmony_ci	u32 virt_width;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mdev_state->ops_lock));
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (!(vbe[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED))
25762306a36Sopenharmony_ci		goto nofb;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	memset(mode, 0, sizeof(*mode));
26062306a36Sopenharmony_ci	switch (vbe[VBE_DISPI_INDEX_BPP]) {
26162306a36Sopenharmony_ci	case 32:
26262306a36Sopenharmony_ci		mode->drm_format = DRM_FORMAT_XRGB8888;
26362306a36Sopenharmony_ci		mode->bytepp = 4;
26462306a36Sopenharmony_ci		break;
26562306a36Sopenharmony_ci	default:
26662306a36Sopenharmony_ci		dev_info_ratelimited(dev, "%s: bpp %d not supported\n",
26762306a36Sopenharmony_ci				     __func__, vbe[VBE_DISPI_INDEX_BPP]);
26862306a36Sopenharmony_ci		goto nofb;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	mode->width  = vbe[VBE_DISPI_INDEX_XRES];
27262306a36Sopenharmony_ci	mode->height = vbe[VBE_DISPI_INDEX_YRES];
27362306a36Sopenharmony_ci	virt_width  = vbe[VBE_DISPI_INDEX_VIRT_WIDTH];
27462306a36Sopenharmony_ci	if (virt_width < mode->width)
27562306a36Sopenharmony_ci		virt_width = mode->width;
27662306a36Sopenharmony_ci	mode->stride = virt_width * mode->bytepp;
27762306a36Sopenharmony_ci	mode->size   = (u64)mode->stride * mode->height;
27862306a36Sopenharmony_ci	mode->offset = ((u64)vbe[VBE_DISPI_INDEX_X_OFFSET] * mode->bytepp +
27962306a36Sopenharmony_ci		       (u64)vbe[VBE_DISPI_INDEX_Y_OFFSET] * mode->stride);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (mode->width < 64 || mode->height < 64) {
28262306a36Sopenharmony_ci		dev_info_ratelimited(dev, "%s: invalid resolution %dx%d\n",
28362306a36Sopenharmony_ci				     __func__, mode->width, mode->height);
28462306a36Sopenharmony_ci		goto nofb;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci	if (mode->offset + mode->size > mdev_state->memsize) {
28762306a36Sopenharmony_ci		dev_info_ratelimited(dev, "%s: framebuffer memory overflow\n",
28862306a36Sopenharmony_ci				     __func__);
28962306a36Sopenharmony_ci		goto nofb;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return 0;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cinofb:
29562306a36Sopenharmony_ci	memset(mode, 0, sizeof(*mode));
29662306a36Sopenharmony_ci	return -EINVAL;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic bool mbochs_modes_equal(struct mbochs_mode *mode1,
30062306a36Sopenharmony_ci			       struct mbochs_mode *mode2)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	return memcmp(mode1, mode2, sizeof(struct mbochs_mode)) == 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset,
30662306a36Sopenharmony_ci				 char *buf, u32 count)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct device *dev = mdev_dev(mdev_state->mdev);
30962306a36Sopenharmony_ci	int index = (offset - PCI_BASE_ADDRESS_0) / 0x04;
31062306a36Sopenharmony_ci	u32 cfg_addr;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	switch (offset) {
31362306a36Sopenharmony_ci	case PCI_BASE_ADDRESS_0:
31462306a36Sopenharmony_ci	case PCI_BASE_ADDRESS_2:
31562306a36Sopenharmony_ci		cfg_addr = *(u32 *)buf;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		if (cfg_addr == 0xffffffff) {
31862306a36Sopenharmony_ci			cfg_addr = (cfg_addr & mdev_state->bar_mask[index]);
31962306a36Sopenharmony_ci		} else {
32062306a36Sopenharmony_ci			cfg_addr &= PCI_BASE_ADDRESS_MEM_MASK;
32162306a36Sopenharmony_ci			if (cfg_addr)
32262306a36Sopenharmony_ci				dev_info(dev, "BAR #%d @ 0x%x\n",
32362306a36Sopenharmony_ci					 index, cfg_addr);
32462306a36Sopenharmony_ci		}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		cfg_addr |= (mdev_state->vconfig[offset] &
32762306a36Sopenharmony_ci			     ~PCI_BASE_ADDRESS_MEM_MASK);
32862306a36Sopenharmony_ci		STORE_LE32(&mdev_state->vconfig[offset], cfg_addr);
32962306a36Sopenharmony_ci		break;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic void handle_mmio_write(struct mdev_state *mdev_state, u16 offset,
33462306a36Sopenharmony_ci			      char *buf, u32 count)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct device *dev = mdev_dev(mdev_state->mdev);
33762306a36Sopenharmony_ci	int index;
33862306a36Sopenharmony_ci	u16 reg16;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	switch (offset) {
34162306a36Sopenharmony_ci	case 0x400 ... 0x41f: /* vga ioports remapped */
34262306a36Sopenharmony_ci		goto unhandled;
34362306a36Sopenharmony_ci	case 0x500 ... 0x515: /* bochs dispi interface */
34462306a36Sopenharmony_ci		if (count != 2)
34562306a36Sopenharmony_ci			goto unhandled;
34662306a36Sopenharmony_ci		index = (offset - 0x500) / 2;
34762306a36Sopenharmony_ci		reg16 = *(u16 *)buf;
34862306a36Sopenharmony_ci		if (index < ARRAY_SIZE(mdev_state->vbe))
34962306a36Sopenharmony_ci			mdev_state->vbe[index] = reg16;
35062306a36Sopenharmony_ci		dev_dbg(dev, "%s: vbe write %d = %d (%s)\n",
35162306a36Sopenharmony_ci			__func__, index, reg16, vbe_name(index));
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci	case 0x600 ... 0x607: /* qemu extended regs */
35462306a36Sopenharmony_ci		goto unhandled;
35562306a36Sopenharmony_ci	default:
35662306a36Sopenharmony_ciunhandled:
35762306a36Sopenharmony_ci		dev_dbg(dev, "%s: @0x%03x, count %d (unhandled)\n",
35862306a36Sopenharmony_ci			__func__, offset, count);
35962306a36Sopenharmony_ci		break;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic void handle_mmio_read(struct mdev_state *mdev_state, u16 offset,
36462306a36Sopenharmony_ci			     char *buf, u32 count)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct device *dev = mdev_dev(mdev_state->mdev);
36762306a36Sopenharmony_ci	struct vfio_region_gfx_edid *edid;
36862306a36Sopenharmony_ci	u16 reg16 = 0;
36962306a36Sopenharmony_ci	int index;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	switch (offset) {
37262306a36Sopenharmony_ci	case 0x000 ... 0x3ff: /* edid block */
37362306a36Sopenharmony_ci		edid = &mdev_state->edid_regs;
37462306a36Sopenharmony_ci		if (edid->link_state != VFIO_DEVICE_GFX_LINK_STATE_UP ||
37562306a36Sopenharmony_ci		    offset >= edid->edid_size) {
37662306a36Sopenharmony_ci			memset(buf, 0, count);
37762306a36Sopenharmony_ci			break;
37862306a36Sopenharmony_ci		}
37962306a36Sopenharmony_ci		memcpy(buf, mdev_state->edid_blob + offset, count);
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	case 0x500 ... 0x515: /* bochs dispi interface */
38262306a36Sopenharmony_ci		if (count != 2)
38362306a36Sopenharmony_ci			goto unhandled;
38462306a36Sopenharmony_ci		index = (offset - 0x500) / 2;
38562306a36Sopenharmony_ci		if (index < ARRAY_SIZE(mdev_state->vbe))
38662306a36Sopenharmony_ci			reg16 = mdev_state->vbe[index];
38762306a36Sopenharmony_ci		dev_dbg(dev, "%s: vbe read %d = %d (%s)\n",
38862306a36Sopenharmony_ci			__func__, index, reg16, vbe_name(index));
38962306a36Sopenharmony_ci		*(u16 *)buf = reg16;
39062306a36Sopenharmony_ci		break;
39162306a36Sopenharmony_ci	default:
39262306a36Sopenharmony_ciunhandled:
39362306a36Sopenharmony_ci		dev_dbg(dev, "%s: @0x%03x, count %d (unhandled)\n",
39462306a36Sopenharmony_ci			__func__, offset, count);
39562306a36Sopenharmony_ci		memset(buf, 0, count);
39662306a36Sopenharmony_ci		break;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic void handle_edid_regs(struct mdev_state *mdev_state, u16 offset,
40162306a36Sopenharmony_ci			     char *buf, u32 count, bool is_write)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	char *regs = (void *)&mdev_state->edid_regs;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (offset + count > sizeof(mdev_state->edid_regs))
40662306a36Sopenharmony_ci		return;
40762306a36Sopenharmony_ci	if (count != 4)
40862306a36Sopenharmony_ci		return;
40962306a36Sopenharmony_ci	if (offset % 4)
41062306a36Sopenharmony_ci		return;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (is_write) {
41362306a36Sopenharmony_ci		switch (offset) {
41462306a36Sopenharmony_ci		case offsetof(struct vfio_region_gfx_edid, link_state):
41562306a36Sopenharmony_ci		case offsetof(struct vfio_region_gfx_edid, edid_size):
41662306a36Sopenharmony_ci			memcpy(regs + offset, buf, count);
41762306a36Sopenharmony_ci			break;
41862306a36Sopenharmony_ci		default:
41962306a36Sopenharmony_ci			/* read-only regs */
42062306a36Sopenharmony_ci			break;
42162306a36Sopenharmony_ci		}
42262306a36Sopenharmony_ci	} else {
42362306a36Sopenharmony_ci		memcpy(buf, regs + offset, count);
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_cistatic void handle_edid_blob(struct mdev_state *mdev_state, u16 offset,
42862306a36Sopenharmony_ci			     char *buf, u32 count, bool is_write)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	if (offset + count > mdev_state->edid_regs.edid_max_size)
43162306a36Sopenharmony_ci		return;
43262306a36Sopenharmony_ci	if (is_write)
43362306a36Sopenharmony_ci		memcpy(mdev_state->edid_blob + offset, buf, count);
43462306a36Sopenharmony_ci	else
43562306a36Sopenharmony_ci		memcpy(buf, mdev_state->edid_blob + offset, count);
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistatic ssize_t mdev_access(struct mdev_state *mdev_state, char *buf,
43962306a36Sopenharmony_ci			   size_t count, loff_t pos, bool is_write)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct page *pg;
44262306a36Sopenharmony_ci	loff_t poff;
44362306a36Sopenharmony_ci	char *map;
44462306a36Sopenharmony_ci	int ret = 0;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	mutex_lock(&mdev_state->ops_lock);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (pos < MBOCHS_CONFIG_SPACE_SIZE) {
44962306a36Sopenharmony_ci		if (is_write)
45062306a36Sopenharmony_ci			handle_pci_cfg_write(mdev_state, pos, buf, count);
45162306a36Sopenharmony_ci		else
45262306a36Sopenharmony_ci			memcpy(buf, (mdev_state->vconfig + pos), count);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	} else if (pos >= MBOCHS_MMIO_BAR_OFFSET &&
45562306a36Sopenharmony_ci		   pos + count <= (MBOCHS_MMIO_BAR_OFFSET +
45662306a36Sopenharmony_ci				   MBOCHS_MMIO_BAR_SIZE)) {
45762306a36Sopenharmony_ci		pos -= MBOCHS_MMIO_BAR_OFFSET;
45862306a36Sopenharmony_ci		if (is_write)
45962306a36Sopenharmony_ci			handle_mmio_write(mdev_state, pos, buf, count);
46062306a36Sopenharmony_ci		else
46162306a36Sopenharmony_ci			handle_mmio_read(mdev_state, pos, buf, count);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	} else if (pos >= MBOCHS_EDID_OFFSET &&
46462306a36Sopenharmony_ci		   pos + count <= (MBOCHS_EDID_OFFSET +
46562306a36Sopenharmony_ci				   MBOCHS_EDID_SIZE)) {
46662306a36Sopenharmony_ci		pos -= MBOCHS_EDID_OFFSET;
46762306a36Sopenharmony_ci		if (pos < MBOCHS_EDID_BLOB_OFFSET) {
46862306a36Sopenharmony_ci			handle_edid_regs(mdev_state, pos, buf, count, is_write);
46962306a36Sopenharmony_ci		} else {
47062306a36Sopenharmony_ci			pos -= MBOCHS_EDID_BLOB_OFFSET;
47162306a36Sopenharmony_ci			handle_edid_blob(mdev_state, pos, buf, count, is_write);
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	} else if (pos >= MBOCHS_MEMORY_BAR_OFFSET &&
47562306a36Sopenharmony_ci		   pos + count <=
47662306a36Sopenharmony_ci		   MBOCHS_MEMORY_BAR_OFFSET + mdev_state->memsize) {
47762306a36Sopenharmony_ci		pos -= MBOCHS_MMIO_BAR_OFFSET;
47862306a36Sopenharmony_ci		poff = pos & ~PAGE_MASK;
47962306a36Sopenharmony_ci		pg = __mbochs_get_page(mdev_state, pos >> PAGE_SHIFT);
48062306a36Sopenharmony_ci		map = kmap(pg);
48162306a36Sopenharmony_ci		if (is_write)
48262306a36Sopenharmony_ci			memcpy(map + poff, buf, count);
48362306a36Sopenharmony_ci		else
48462306a36Sopenharmony_ci			memcpy(buf, map + poff, count);
48562306a36Sopenharmony_ci		kunmap(pg);
48662306a36Sopenharmony_ci		put_page(pg);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	} else {
48962306a36Sopenharmony_ci		dev_dbg(mdev_state->vdev.dev, "%s: %s @0x%llx (unhandled)\n",
49062306a36Sopenharmony_ci			__func__, is_write ? "WR" : "RD", pos);
49162306a36Sopenharmony_ci		ret = -1;
49262306a36Sopenharmony_ci		goto accessfailed;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	ret = count;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ciaccessfailed:
49962306a36Sopenharmony_ci	mutex_unlock(&mdev_state->ops_lock);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return ret;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int mbochs_reset(struct mdev_state *mdev_state)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	u32 size64k = mdev_state->memsize / (64 * 1024);
50762306a36Sopenharmony_ci	int i;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mdev_state->vbe); i++)
51062306a36Sopenharmony_ci		mdev_state->vbe[i] = 0;
51162306a36Sopenharmony_ci	mdev_state->vbe[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID5;
51262306a36Sopenharmony_ci	mdev_state->vbe[VBE_DISPI_INDEX_VIDEO_MEMORY_64K] = size64k;
51362306a36Sopenharmony_ci	return 0;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic int mbochs_init_dev(struct vfio_device *vdev)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct mdev_state *mdev_state =
51962306a36Sopenharmony_ci		container_of(vdev, struct mdev_state, vdev);
52062306a36Sopenharmony_ci	struct mdev_device *mdev = to_mdev_device(vdev->dev);
52162306a36Sopenharmony_ci	struct mbochs_type *type =
52262306a36Sopenharmony_ci		container_of(mdev->type, struct mbochs_type, type);
52362306a36Sopenharmony_ci	int avail_mbytes = atomic_read(&mbochs_avail_mbytes);
52462306a36Sopenharmony_ci	int ret = -ENOMEM;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	do {
52762306a36Sopenharmony_ci		if (avail_mbytes < type->mbytes)
52862306a36Sopenharmony_ci			return -ENOSPC;
52962306a36Sopenharmony_ci	} while (!atomic_try_cmpxchg(&mbochs_avail_mbytes, &avail_mbytes,
53062306a36Sopenharmony_ci				     avail_mbytes - type->mbytes));
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	mdev_state->vconfig = kzalloc(MBOCHS_CONFIG_SPACE_SIZE, GFP_KERNEL);
53362306a36Sopenharmony_ci	if (!mdev_state->vconfig)
53462306a36Sopenharmony_ci		goto err_avail;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	mdev_state->memsize = type->mbytes * 1024 * 1024;
53762306a36Sopenharmony_ci	mdev_state->pagecount = mdev_state->memsize >> PAGE_SHIFT;
53862306a36Sopenharmony_ci	mdev_state->pages = kcalloc(mdev_state->pagecount,
53962306a36Sopenharmony_ci				    sizeof(struct page *),
54062306a36Sopenharmony_ci				    GFP_KERNEL);
54162306a36Sopenharmony_ci	if (!mdev_state->pages)
54262306a36Sopenharmony_ci		goto err_vconfig;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	mutex_init(&mdev_state->ops_lock);
54562306a36Sopenharmony_ci	mdev_state->mdev = mdev;
54662306a36Sopenharmony_ci	INIT_LIST_HEAD(&mdev_state->dmabufs);
54762306a36Sopenharmony_ci	mdev_state->next_id = 1;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	mdev_state->type = type;
55062306a36Sopenharmony_ci	mdev_state->edid_regs.max_xres = type->max_x;
55162306a36Sopenharmony_ci	mdev_state->edid_regs.max_yres = type->max_y;
55262306a36Sopenharmony_ci	mdev_state->edid_regs.edid_offset = MBOCHS_EDID_BLOB_OFFSET;
55362306a36Sopenharmony_ci	mdev_state->edid_regs.edid_max_size = sizeof(mdev_state->edid_blob);
55462306a36Sopenharmony_ci	mbochs_create_config_space(mdev_state);
55562306a36Sopenharmony_ci	mbochs_reset(mdev_state);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	dev_info(vdev->dev, "%s: %s, %d MB, %ld pages\n", __func__,
55862306a36Sopenharmony_ci		 type->type.pretty_name, type->mbytes, mdev_state->pagecount);
55962306a36Sopenharmony_ci	return 0;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cierr_vconfig:
56262306a36Sopenharmony_ci	kfree(mdev_state->vconfig);
56362306a36Sopenharmony_cierr_avail:
56462306a36Sopenharmony_ci	atomic_add(type->mbytes, &mbochs_avail_mbytes);
56562306a36Sopenharmony_ci	return ret;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int mbochs_probe(struct mdev_device *mdev)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct mdev_state *mdev_state;
57162306a36Sopenharmony_ci	int ret = -ENOMEM;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	mdev_state = vfio_alloc_device(mdev_state, vdev, &mdev->dev,
57462306a36Sopenharmony_ci				       &mbochs_dev_ops);
57562306a36Sopenharmony_ci	if (IS_ERR(mdev_state))
57662306a36Sopenharmony_ci		return PTR_ERR(mdev_state);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	ret = vfio_register_emulated_iommu_dev(&mdev_state->vdev);
57962306a36Sopenharmony_ci	if (ret)
58062306a36Sopenharmony_ci		goto err_put_vdev;
58162306a36Sopenharmony_ci	dev_set_drvdata(&mdev->dev, mdev_state);
58262306a36Sopenharmony_ci	return 0;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cierr_put_vdev:
58562306a36Sopenharmony_ci	vfio_put_device(&mdev_state->vdev);
58662306a36Sopenharmony_ci	return ret;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic void mbochs_release_dev(struct vfio_device *vdev)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct mdev_state *mdev_state =
59262306a36Sopenharmony_ci		container_of(vdev, struct mdev_state, vdev);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	atomic_add(mdev_state->type->mbytes, &mbochs_avail_mbytes);
59562306a36Sopenharmony_ci	kfree(mdev_state->pages);
59662306a36Sopenharmony_ci	kfree(mdev_state->vconfig);
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic void mbochs_remove(struct mdev_device *mdev)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct mdev_state *mdev_state = dev_get_drvdata(&mdev->dev);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	vfio_unregister_group_dev(&mdev_state->vdev);
60462306a36Sopenharmony_ci	vfio_put_device(&mdev_state->vdev);
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic ssize_t mbochs_read(struct vfio_device *vdev, char __user *buf,
60862306a36Sopenharmony_ci			   size_t count, loff_t *ppos)
60962306a36Sopenharmony_ci{
61062306a36Sopenharmony_ci	struct mdev_state *mdev_state =
61162306a36Sopenharmony_ci		container_of(vdev, struct mdev_state, vdev);
61262306a36Sopenharmony_ci	unsigned int done = 0;
61362306a36Sopenharmony_ci	int ret;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	while (count) {
61662306a36Sopenharmony_ci		size_t filled;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		if (count >= 4 && !(*ppos % 4)) {
61962306a36Sopenharmony_ci			u32 val;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci			ret =  mdev_access(mdev_state, (char *)&val, sizeof(val),
62262306a36Sopenharmony_ci					   *ppos, false);
62362306a36Sopenharmony_ci			if (ret <= 0)
62462306a36Sopenharmony_ci				goto read_err;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci			if (copy_to_user(buf, &val, sizeof(val)))
62762306a36Sopenharmony_ci				goto read_err;
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci			filled = 4;
63062306a36Sopenharmony_ci		} else if (count >= 2 && !(*ppos % 2)) {
63162306a36Sopenharmony_ci			u16 val;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci			ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
63462306a36Sopenharmony_ci					  *ppos, false);
63562306a36Sopenharmony_ci			if (ret <= 0)
63662306a36Sopenharmony_ci				goto read_err;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci			if (copy_to_user(buf, &val, sizeof(val)))
63962306a36Sopenharmony_ci				goto read_err;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci			filled = 2;
64262306a36Sopenharmony_ci		} else {
64362306a36Sopenharmony_ci			u8 val;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci			ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
64662306a36Sopenharmony_ci					  *ppos, false);
64762306a36Sopenharmony_ci			if (ret <= 0)
64862306a36Sopenharmony_ci				goto read_err;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci			if (copy_to_user(buf, &val, sizeof(val)))
65162306a36Sopenharmony_ci				goto read_err;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci			filled = 1;
65462306a36Sopenharmony_ci		}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci		count -= filled;
65762306a36Sopenharmony_ci		done += filled;
65862306a36Sopenharmony_ci		*ppos += filled;
65962306a36Sopenharmony_ci		buf += filled;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	return done;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ciread_err:
66562306a36Sopenharmony_ci	return -EFAULT;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic ssize_t mbochs_write(struct vfio_device *vdev, const char __user *buf,
66962306a36Sopenharmony_ci			    size_t count, loff_t *ppos)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct mdev_state *mdev_state =
67262306a36Sopenharmony_ci		container_of(vdev, struct mdev_state, vdev);
67362306a36Sopenharmony_ci	unsigned int done = 0;
67462306a36Sopenharmony_ci	int ret;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	while (count) {
67762306a36Sopenharmony_ci		size_t filled;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci		if (count >= 4 && !(*ppos % 4)) {
68062306a36Sopenharmony_ci			u32 val;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci			if (copy_from_user(&val, buf, sizeof(val)))
68362306a36Sopenharmony_ci				goto write_err;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci			ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
68662306a36Sopenharmony_ci					  *ppos, true);
68762306a36Sopenharmony_ci			if (ret <= 0)
68862306a36Sopenharmony_ci				goto write_err;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci			filled = 4;
69162306a36Sopenharmony_ci		} else if (count >= 2 && !(*ppos % 2)) {
69262306a36Sopenharmony_ci			u16 val;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci			if (copy_from_user(&val, buf, sizeof(val)))
69562306a36Sopenharmony_ci				goto write_err;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci			ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
69862306a36Sopenharmony_ci					  *ppos, true);
69962306a36Sopenharmony_ci			if (ret <= 0)
70062306a36Sopenharmony_ci				goto write_err;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci			filled = 2;
70362306a36Sopenharmony_ci		} else {
70462306a36Sopenharmony_ci			u8 val;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci			if (copy_from_user(&val, buf, sizeof(val)))
70762306a36Sopenharmony_ci				goto write_err;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci			ret = mdev_access(mdev_state, (char *)&val, sizeof(val),
71062306a36Sopenharmony_ci					  *ppos, true);
71162306a36Sopenharmony_ci			if (ret <= 0)
71262306a36Sopenharmony_ci				goto write_err;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci			filled = 1;
71562306a36Sopenharmony_ci		}
71662306a36Sopenharmony_ci		count -= filled;
71762306a36Sopenharmony_ci		done += filled;
71862306a36Sopenharmony_ci		*ppos += filled;
71962306a36Sopenharmony_ci		buf += filled;
72062306a36Sopenharmony_ci	}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	return done;
72362306a36Sopenharmony_ciwrite_err:
72462306a36Sopenharmony_ci	return -EFAULT;
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic struct page *__mbochs_get_page(struct mdev_state *mdev_state,
72862306a36Sopenharmony_ci				      pgoff_t pgoff)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mdev_state->ops_lock));
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (!mdev_state->pages[pgoff]) {
73362306a36Sopenharmony_ci		mdev_state->pages[pgoff] =
73462306a36Sopenharmony_ci			alloc_pages(GFP_HIGHUSER | __GFP_ZERO, 0);
73562306a36Sopenharmony_ci		if (!mdev_state->pages[pgoff])
73662306a36Sopenharmony_ci			return NULL;
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	get_page(mdev_state->pages[pgoff]);
74062306a36Sopenharmony_ci	return mdev_state->pages[pgoff];
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic struct page *mbochs_get_page(struct mdev_state *mdev_state,
74462306a36Sopenharmony_ci				    pgoff_t pgoff)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	struct page *page;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (WARN_ON(pgoff >= mdev_state->pagecount))
74962306a36Sopenharmony_ci		return NULL;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	mutex_lock(&mdev_state->ops_lock);
75262306a36Sopenharmony_ci	page = __mbochs_get_page(mdev_state, pgoff);
75362306a36Sopenharmony_ci	mutex_unlock(&mdev_state->ops_lock);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	return page;
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic void mbochs_put_pages(struct mdev_state *mdev_state)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct device *dev = mdev_dev(mdev_state->mdev);
76162306a36Sopenharmony_ci	int i, count = 0;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mdev_state->ops_lock));
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	for (i = 0; i < mdev_state->pagecount; i++) {
76662306a36Sopenharmony_ci		if (!mdev_state->pages[i])
76762306a36Sopenharmony_ci			continue;
76862306a36Sopenharmony_ci		put_page(mdev_state->pages[i]);
76962306a36Sopenharmony_ci		mdev_state->pages[i] = NULL;
77062306a36Sopenharmony_ci		count++;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci	dev_dbg(dev, "%s: %d pages released\n", __func__, count);
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic vm_fault_t mbochs_region_vm_fault(struct vm_fault *vmf)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	struct vm_area_struct *vma = vmf->vma;
77862306a36Sopenharmony_ci	struct mdev_state *mdev_state = vma->vm_private_data;
77962306a36Sopenharmony_ci	pgoff_t page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	if (page_offset >= mdev_state->pagecount)
78262306a36Sopenharmony_ci		return VM_FAULT_SIGBUS;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	vmf->page = mbochs_get_page(mdev_state, page_offset);
78562306a36Sopenharmony_ci	if (!vmf->page)
78662306a36Sopenharmony_ci		return VM_FAULT_SIGBUS;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	return 0;
78962306a36Sopenharmony_ci}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cistatic const struct vm_operations_struct mbochs_region_vm_ops = {
79262306a36Sopenharmony_ci	.fault = mbochs_region_vm_fault,
79362306a36Sopenharmony_ci};
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic int mbochs_mmap(struct vfio_device *vdev, struct vm_area_struct *vma)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	struct mdev_state *mdev_state =
79862306a36Sopenharmony_ci		container_of(vdev, struct mdev_state, vdev);
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	if (vma->vm_pgoff != MBOCHS_MEMORY_BAR_OFFSET >> PAGE_SHIFT)
80162306a36Sopenharmony_ci		return -EINVAL;
80262306a36Sopenharmony_ci	if (vma->vm_end < vma->vm_start)
80362306a36Sopenharmony_ci		return -EINVAL;
80462306a36Sopenharmony_ci	if (vma->vm_end - vma->vm_start > mdev_state->memsize)
80562306a36Sopenharmony_ci		return -EINVAL;
80662306a36Sopenharmony_ci	if ((vma->vm_flags & VM_SHARED) == 0)
80762306a36Sopenharmony_ci		return -EINVAL;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	vma->vm_ops = &mbochs_region_vm_ops;
81062306a36Sopenharmony_ci	vma->vm_private_data = mdev_state;
81162306a36Sopenharmony_ci	return 0;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_cistatic vm_fault_t mbochs_dmabuf_vm_fault(struct vm_fault *vmf)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	struct vm_area_struct *vma = vmf->vma;
81762306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf = vma->vm_private_data;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (WARN_ON(vmf->pgoff >= dmabuf->pagecount))
82062306a36Sopenharmony_ci		return VM_FAULT_SIGBUS;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	vmf->page = dmabuf->pages[vmf->pgoff];
82362306a36Sopenharmony_ci	get_page(vmf->page);
82462306a36Sopenharmony_ci	return 0;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic const struct vm_operations_struct mbochs_dmabuf_vm_ops = {
82862306a36Sopenharmony_ci	.fault = mbochs_dmabuf_vm_fault,
82962306a36Sopenharmony_ci};
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic int mbochs_mmap_dmabuf(struct dma_buf *buf, struct vm_area_struct *vma)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf = buf->priv;
83462306a36Sopenharmony_ci	struct device *dev = mdev_dev(dmabuf->mdev_state->mdev);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	dev_dbg(dev, "%s: %d\n", __func__, dmabuf->id);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if ((vma->vm_flags & VM_SHARED) == 0)
83962306a36Sopenharmony_ci		return -EINVAL;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	vma->vm_ops = &mbochs_dmabuf_vm_ops;
84262306a36Sopenharmony_ci	vma->vm_private_data = dmabuf;
84362306a36Sopenharmony_ci	return 0;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void mbochs_print_dmabuf(struct mbochs_dmabuf *dmabuf,
84762306a36Sopenharmony_ci				const char *prefix)
84862306a36Sopenharmony_ci{
84962306a36Sopenharmony_ci	struct device *dev = mdev_dev(dmabuf->mdev_state->mdev);
85062306a36Sopenharmony_ci	u32 fourcc = dmabuf->mode.drm_format;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	dev_dbg(dev, "%s/%d: %c%c%c%c, %dx%d, stride %d, off 0x%llx, size 0x%llx, pages %ld\n",
85362306a36Sopenharmony_ci		prefix, dmabuf->id,
85462306a36Sopenharmony_ci		fourcc ? ((fourcc >>  0) & 0xff) : '-',
85562306a36Sopenharmony_ci		fourcc ? ((fourcc >>  8) & 0xff) : '-',
85662306a36Sopenharmony_ci		fourcc ? ((fourcc >> 16) & 0xff) : '-',
85762306a36Sopenharmony_ci		fourcc ? ((fourcc >> 24) & 0xff) : '-',
85862306a36Sopenharmony_ci		dmabuf->mode.width, dmabuf->mode.height, dmabuf->mode.stride,
85962306a36Sopenharmony_ci		dmabuf->mode.offset, dmabuf->mode.size, dmabuf->pagecount);
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic struct sg_table *mbochs_map_dmabuf(struct dma_buf_attachment *at,
86362306a36Sopenharmony_ci					  enum dma_data_direction direction)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf = at->dmabuf->priv;
86662306a36Sopenharmony_ci	struct device *dev = mdev_dev(dmabuf->mdev_state->mdev);
86762306a36Sopenharmony_ci	struct sg_table *sg;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	dev_dbg(dev, "%s: %d\n", __func__, dmabuf->id);
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	sg = kzalloc(sizeof(*sg), GFP_KERNEL);
87262306a36Sopenharmony_ci	if (!sg)
87362306a36Sopenharmony_ci		goto err1;
87462306a36Sopenharmony_ci	if (sg_alloc_table_from_pages(sg, dmabuf->pages, dmabuf->pagecount,
87562306a36Sopenharmony_ci				      0, dmabuf->mode.size, GFP_KERNEL) < 0)
87662306a36Sopenharmony_ci		goto err2;
87762306a36Sopenharmony_ci	if (dma_map_sgtable(at->dev, sg, direction, 0))
87862306a36Sopenharmony_ci		goto err3;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	return sg;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cierr3:
88362306a36Sopenharmony_ci	sg_free_table(sg);
88462306a36Sopenharmony_cierr2:
88562306a36Sopenharmony_ci	kfree(sg);
88662306a36Sopenharmony_cierr1:
88762306a36Sopenharmony_ci	return ERR_PTR(-ENOMEM);
88862306a36Sopenharmony_ci}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_cistatic void mbochs_unmap_dmabuf(struct dma_buf_attachment *at,
89162306a36Sopenharmony_ci				struct sg_table *sg,
89262306a36Sopenharmony_ci				enum dma_data_direction direction)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf = at->dmabuf->priv;
89562306a36Sopenharmony_ci	struct device *dev = mdev_dev(dmabuf->mdev_state->mdev);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	dev_dbg(dev, "%s: %d\n", __func__, dmabuf->id);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	dma_unmap_sgtable(at->dev, sg, direction, 0);
90062306a36Sopenharmony_ci	sg_free_table(sg);
90162306a36Sopenharmony_ci	kfree(sg);
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_cistatic void mbochs_release_dmabuf(struct dma_buf *buf)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf = buf->priv;
90762306a36Sopenharmony_ci	struct mdev_state *mdev_state = dmabuf->mdev_state;
90862306a36Sopenharmony_ci	struct device *dev = mdev_dev(mdev_state->mdev);
90962306a36Sopenharmony_ci	pgoff_t pg;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	dev_dbg(dev, "%s: %d\n", __func__, dmabuf->id);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	for (pg = 0; pg < dmabuf->pagecount; pg++)
91462306a36Sopenharmony_ci		put_page(dmabuf->pages[pg]);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	mutex_lock(&mdev_state->ops_lock);
91762306a36Sopenharmony_ci	dmabuf->buf = NULL;
91862306a36Sopenharmony_ci	if (dmabuf->unlinked)
91962306a36Sopenharmony_ci		kfree(dmabuf);
92062306a36Sopenharmony_ci	mutex_unlock(&mdev_state->ops_lock);
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic struct dma_buf_ops mbochs_dmabuf_ops = {
92462306a36Sopenharmony_ci	.map_dma_buf	  = mbochs_map_dmabuf,
92562306a36Sopenharmony_ci	.unmap_dma_buf	  = mbochs_unmap_dmabuf,
92662306a36Sopenharmony_ci	.release	  = mbochs_release_dmabuf,
92762306a36Sopenharmony_ci	.mmap		  = mbochs_mmap_dmabuf,
92862306a36Sopenharmony_ci};
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_cistatic struct mbochs_dmabuf *mbochs_dmabuf_alloc(struct mdev_state *mdev_state,
93162306a36Sopenharmony_ci						 struct mbochs_mode *mode)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf;
93462306a36Sopenharmony_ci	pgoff_t page_offset, pg;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mdev_state->ops_lock));
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	dmabuf = kzalloc(sizeof(struct mbochs_dmabuf), GFP_KERNEL);
93962306a36Sopenharmony_ci	if (!dmabuf)
94062306a36Sopenharmony_ci		return NULL;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	dmabuf->mode = *mode;
94362306a36Sopenharmony_ci	dmabuf->id = mdev_state->next_id++;
94462306a36Sopenharmony_ci	dmabuf->pagecount = DIV_ROUND_UP(mode->size, PAGE_SIZE);
94562306a36Sopenharmony_ci	dmabuf->pages = kcalloc(dmabuf->pagecount, sizeof(struct page *),
94662306a36Sopenharmony_ci				GFP_KERNEL);
94762306a36Sopenharmony_ci	if (!dmabuf->pages)
94862306a36Sopenharmony_ci		goto err_free_dmabuf;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	page_offset = dmabuf->mode.offset >> PAGE_SHIFT;
95162306a36Sopenharmony_ci	for (pg = 0; pg < dmabuf->pagecount; pg++) {
95262306a36Sopenharmony_ci		dmabuf->pages[pg] = __mbochs_get_page(mdev_state,
95362306a36Sopenharmony_ci						      page_offset + pg);
95462306a36Sopenharmony_ci		if (!dmabuf->pages[pg])
95562306a36Sopenharmony_ci			goto err_free_pages;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	dmabuf->mdev_state = mdev_state;
95962306a36Sopenharmony_ci	list_add(&dmabuf->next, &mdev_state->dmabufs);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	mbochs_print_dmabuf(dmabuf, __func__);
96262306a36Sopenharmony_ci	return dmabuf;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_cierr_free_pages:
96562306a36Sopenharmony_ci	while (pg > 0)
96662306a36Sopenharmony_ci		put_page(dmabuf->pages[--pg]);
96762306a36Sopenharmony_ci	kfree(dmabuf->pages);
96862306a36Sopenharmony_cierr_free_dmabuf:
96962306a36Sopenharmony_ci	kfree(dmabuf);
97062306a36Sopenharmony_ci	return NULL;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic struct mbochs_dmabuf *
97462306a36Sopenharmony_cimbochs_dmabuf_find_by_mode(struct mdev_state *mdev_state,
97562306a36Sopenharmony_ci			   struct mbochs_mode *mode)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mdev_state->ops_lock));
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	list_for_each_entry(dmabuf, &mdev_state->dmabufs, next)
98262306a36Sopenharmony_ci		if (mbochs_modes_equal(&dmabuf->mode, mode))
98362306a36Sopenharmony_ci			return dmabuf;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	return NULL;
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic struct mbochs_dmabuf *
98962306a36Sopenharmony_cimbochs_dmabuf_find_by_id(struct mdev_state *mdev_state, u32 id)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mdev_state->ops_lock));
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	list_for_each_entry(dmabuf, &mdev_state->dmabufs, next)
99662306a36Sopenharmony_ci		if (dmabuf->id == id)
99762306a36Sopenharmony_ci			return dmabuf;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	return NULL;
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cistatic int mbochs_dmabuf_export(struct mbochs_dmabuf *dmabuf)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	struct mdev_state *mdev_state = dmabuf->mdev_state;
100562306a36Sopenharmony_ci	struct device *dev = mdev_state->vdev.dev;
100662306a36Sopenharmony_ci	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
100762306a36Sopenharmony_ci	struct dma_buf *buf;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	WARN_ON(!mutex_is_locked(&mdev_state->ops_lock));
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	if (!IS_ALIGNED(dmabuf->mode.offset, PAGE_SIZE)) {
101262306a36Sopenharmony_ci		dev_info_ratelimited(dev, "%s: framebuffer not page-aligned\n",
101362306a36Sopenharmony_ci				     __func__);
101462306a36Sopenharmony_ci		return -EINVAL;
101562306a36Sopenharmony_ci	}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	exp_info.ops = &mbochs_dmabuf_ops;
101862306a36Sopenharmony_ci	exp_info.size = dmabuf->mode.size;
101962306a36Sopenharmony_ci	exp_info.priv = dmabuf;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	buf = dma_buf_export(&exp_info);
102262306a36Sopenharmony_ci	if (IS_ERR(buf)) {
102362306a36Sopenharmony_ci		dev_info_ratelimited(dev, "%s: dma_buf_export failed: %ld\n",
102462306a36Sopenharmony_ci				     __func__, PTR_ERR(buf));
102562306a36Sopenharmony_ci		return PTR_ERR(buf);
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	dmabuf->buf = buf;
102962306a36Sopenharmony_ci	dev_dbg(dev, "%s: %d\n", __func__, dmabuf->id);
103062306a36Sopenharmony_ci	return 0;
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic int mbochs_get_region_info(struct mdev_state *mdev_state,
103462306a36Sopenharmony_ci				  struct vfio_region_info_ext *ext)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci	struct vfio_region_info *region_info = &ext->base;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (region_info->index >= MBOCHS_NUM_REGIONS)
103962306a36Sopenharmony_ci		return -EINVAL;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	switch (region_info->index) {
104262306a36Sopenharmony_ci	case VFIO_PCI_CONFIG_REGION_INDEX:
104362306a36Sopenharmony_ci		region_info->offset = 0;
104462306a36Sopenharmony_ci		region_info->size   = MBOCHS_CONFIG_SPACE_SIZE;
104562306a36Sopenharmony_ci		region_info->flags  = (VFIO_REGION_INFO_FLAG_READ |
104662306a36Sopenharmony_ci				       VFIO_REGION_INFO_FLAG_WRITE);
104762306a36Sopenharmony_ci		break;
104862306a36Sopenharmony_ci	case VFIO_PCI_BAR0_REGION_INDEX:
104962306a36Sopenharmony_ci		region_info->offset = MBOCHS_MEMORY_BAR_OFFSET;
105062306a36Sopenharmony_ci		region_info->size   = mdev_state->memsize;
105162306a36Sopenharmony_ci		region_info->flags  = (VFIO_REGION_INFO_FLAG_READ  |
105262306a36Sopenharmony_ci				       VFIO_REGION_INFO_FLAG_WRITE |
105362306a36Sopenharmony_ci				       VFIO_REGION_INFO_FLAG_MMAP);
105462306a36Sopenharmony_ci		break;
105562306a36Sopenharmony_ci	case VFIO_PCI_BAR2_REGION_INDEX:
105662306a36Sopenharmony_ci		region_info->offset = MBOCHS_MMIO_BAR_OFFSET;
105762306a36Sopenharmony_ci		region_info->size   = MBOCHS_MMIO_BAR_SIZE;
105862306a36Sopenharmony_ci		region_info->flags  = (VFIO_REGION_INFO_FLAG_READ  |
105962306a36Sopenharmony_ci				       VFIO_REGION_INFO_FLAG_WRITE);
106062306a36Sopenharmony_ci		break;
106162306a36Sopenharmony_ci	case MBOCHS_EDID_REGION_INDEX:
106262306a36Sopenharmony_ci		ext->base.argsz = sizeof(*ext);
106362306a36Sopenharmony_ci		ext->base.offset = MBOCHS_EDID_OFFSET;
106462306a36Sopenharmony_ci		ext->base.size = MBOCHS_EDID_SIZE;
106562306a36Sopenharmony_ci		ext->base.flags = (VFIO_REGION_INFO_FLAG_READ  |
106662306a36Sopenharmony_ci				   VFIO_REGION_INFO_FLAG_WRITE |
106762306a36Sopenharmony_ci				   VFIO_REGION_INFO_FLAG_CAPS);
106862306a36Sopenharmony_ci		ext->base.cap_offset = offsetof(typeof(*ext), type);
106962306a36Sopenharmony_ci		ext->type.header.id = VFIO_REGION_INFO_CAP_TYPE;
107062306a36Sopenharmony_ci		ext->type.header.version = 1;
107162306a36Sopenharmony_ci		ext->type.header.next = 0;
107262306a36Sopenharmony_ci		ext->type.type = VFIO_REGION_TYPE_GFX;
107362306a36Sopenharmony_ci		ext->type.subtype = VFIO_REGION_SUBTYPE_GFX_EDID;
107462306a36Sopenharmony_ci		break;
107562306a36Sopenharmony_ci	default:
107662306a36Sopenharmony_ci		region_info->size   = 0;
107762306a36Sopenharmony_ci		region_info->offset = 0;
107862306a36Sopenharmony_ci		region_info->flags  = 0;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	return 0;
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_cistatic int mbochs_get_irq_info(struct vfio_irq_info *irq_info)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	irq_info->count = 0;
108762306a36Sopenharmony_ci	return 0;
108862306a36Sopenharmony_ci}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_cistatic int mbochs_get_device_info(struct vfio_device_info *dev_info)
109162306a36Sopenharmony_ci{
109262306a36Sopenharmony_ci	dev_info->flags = VFIO_DEVICE_FLAGS_PCI;
109362306a36Sopenharmony_ci	dev_info->num_regions = MBOCHS_NUM_REGIONS;
109462306a36Sopenharmony_ci	dev_info->num_irqs = VFIO_PCI_NUM_IRQS;
109562306a36Sopenharmony_ci	return 0;
109662306a36Sopenharmony_ci}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic int mbochs_query_gfx_plane(struct mdev_state *mdev_state,
109962306a36Sopenharmony_ci				  struct vfio_device_gfx_plane_info *plane)
110062306a36Sopenharmony_ci{
110162306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf;
110262306a36Sopenharmony_ci	struct mbochs_mode mode;
110362306a36Sopenharmony_ci	int ret;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (plane->flags & VFIO_GFX_PLANE_TYPE_PROBE) {
110662306a36Sopenharmony_ci		if (plane->flags == (VFIO_GFX_PLANE_TYPE_PROBE |
110762306a36Sopenharmony_ci				     VFIO_GFX_PLANE_TYPE_DMABUF))
110862306a36Sopenharmony_ci			return 0;
110962306a36Sopenharmony_ci		return -EINVAL;
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (plane->flags != VFIO_GFX_PLANE_TYPE_DMABUF)
111362306a36Sopenharmony_ci		return -EINVAL;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	plane->drm_format_mod = 0;
111662306a36Sopenharmony_ci	plane->x_pos	      = 0;
111762306a36Sopenharmony_ci	plane->y_pos	      = 0;
111862306a36Sopenharmony_ci	plane->x_hot	      = 0;
111962306a36Sopenharmony_ci	plane->y_hot	      = 0;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	mutex_lock(&mdev_state->ops_lock);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	ret = -EINVAL;
112462306a36Sopenharmony_ci	if (plane->drm_plane_type == DRM_PLANE_TYPE_PRIMARY)
112562306a36Sopenharmony_ci		ret = mbochs_check_framebuffer(mdev_state, &mode);
112662306a36Sopenharmony_ci	if (ret < 0) {
112762306a36Sopenharmony_ci		plane->drm_format     = 0;
112862306a36Sopenharmony_ci		plane->width	      = 0;
112962306a36Sopenharmony_ci		plane->height	      = 0;
113062306a36Sopenharmony_ci		plane->stride	      = 0;
113162306a36Sopenharmony_ci		plane->size	      = 0;
113262306a36Sopenharmony_ci		plane->dmabuf_id      = 0;
113362306a36Sopenharmony_ci		goto done;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	dmabuf = mbochs_dmabuf_find_by_mode(mdev_state, &mode);
113762306a36Sopenharmony_ci	if (!dmabuf)
113862306a36Sopenharmony_ci		mbochs_dmabuf_alloc(mdev_state, &mode);
113962306a36Sopenharmony_ci	if (!dmabuf) {
114062306a36Sopenharmony_ci		mutex_unlock(&mdev_state->ops_lock);
114162306a36Sopenharmony_ci		return -ENOMEM;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	plane->drm_format     = dmabuf->mode.drm_format;
114562306a36Sopenharmony_ci	plane->width	      = dmabuf->mode.width;
114662306a36Sopenharmony_ci	plane->height	      = dmabuf->mode.height;
114762306a36Sopenharmony_ci	plane->stride	      = dmabuf->mode.stride;
114862306a36Sopenharmony_ci	plane->size	      = dmabuf->mode.size;
114962306a36Sopenharmony_ci	plane->dmabuf_id      = dmabuf->id;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_cidone:
115262306a36Sopenharmony_ci	if (plane->drm_plane_type == DRM_PLANE_TYPE_PRIMARY &&
115362306a36Sopenharmony_ci	    mdev_state->active_id != plane->dmabuf_id) {
115462306a36Sopenharmony_ci		dev_dbg(mdev_state->vdev.dev, "%s: primary: %d => %d\n",
115562306a36Sopenharmony_ci			__func__, mdev_state->active_id, plane->dmabuf_id);
115662306a36Sopenharmony_ci		mdev_state->active_id = plane->dmabuf_id;
115762306a36Sopenharmony_ci	}
115862306a36Sopenharmony_ci	mutex_unlock(&mdev_state->ops_lock);
115962306a36Sopenharmony_ci	return 0;
116062306a36Sopenharmony_ci}
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_cistatic int mbochs_get_gfx_dmabuf(struct mdev_state *mdev_state, u32 id)
116362306a36Sopenharmony_ci{
116462306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	mutex_lock(&mdev_state->ops_lock);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	dmabuf = mbochs_dmabuf_find_by_id(mdev_state, id);
116962306a36Sopenharmony_ci	if (!dmabuf) {
117062306a36Sopenharmony_ci		mutex_unlock(&mdev_state->ops_lock);
117162306a36Sopenharmony_ci		return -ENOENT;
117262306a36Sopenharmony_ci	}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	if (!dmabuf->buf)
117562306a36Sopenharmony_ci		mbochs_dmabuf_export(dmabuf);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	mutex_unlock(&mdev_state->ops_lock);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	if (!dmabuf->buf)
118062306a36Sopenharmony_ci		return -EINVAL;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	return dma_buf_fd(dmabuf->buf, 0);
118362306a36Sopenharmony_ci}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_cistatic long mbochs_ioctl(struct vfio_device *vdev, unsigned int cmd,
118662306a36Sopenharmony_ci			 unsigned long arg)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	struct mdev_state *mdev_state =
118962306a36Sopenharmony_ci		container_of(vdev, struct mdev_state, vdev);
119062306a36Sopenharmony_ci	int ret = 0;
119162306a36Sopenharmony_ci	unsigned long minsz, outsz;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	switch (cmd) {
119462306a36Sopenharmony_ci	case VFIO_DEVICE_GET_INFO:
119562306a36Sopenharmony_ci	{
119662306a36Sopenharmony_ci		struct vfio_device_info info;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci		minsz = offsetofend(struct vfio_device_info, num_irqs);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci		if (copy_from_user(&info, (void __user *)arg, minsz))
120162306a36Sopenharmony_ci			return -EFAULT;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci		if (info.argsz < minsz)
120462306a36Sopenharmony_ci			return -EINVAL;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci		ret = mbochs_get_device_info(&info);
120762306a36Sopenharmony_ci		if (ret)
120862306a36Sopenharmony_ci			return ret;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci		if (copy_to_user((void __user *)arg, &info, minsz))
121162306a36Sopenharmony_ci			return -EFAULT;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci		return 0;
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci	case VFIO_DEVICE_GET_REGION_INFO:
121662306a36Sopenharmony_ci	{
121762306a36Sopenharmony_ci		struct vfio_region_info_ext info;
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci		minsz = offsetofend(typeof(info), base.offset);
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci		if (copy_from_user(&info, (void __user *)arg, minsz))
122262306a36Sopenharmony_ci			return -EFAULT;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci		outsz = info.base.argsz;
122562306a36Sopenharmony_ci		if (outsz < minsz)
122662306a36Sopenharmony_ci			return -EINVAL;
122762306a36Sopenharmony_ci		if (outsz > sizeof(info))
122862306a36Sopenharmony_ci			return -EINVAL;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci		ret = mbochs_get_region_info(mdev_state, &info);
123162306a36Sopenharmony_ci		if (ret)
123262306a36Sopenharmony_ci			return ret;
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci		if (copy_to_user((void __user *)arg, &info, outsz))
123562306a36Sopenharmony_ci			return -EFAULT;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci		return 0;
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	case VFIO_DEVICE_GET_IRQ_INFO:
124162306a36Sopenharmony_ci	{
124262306a36Sopenharmony_ci		struct vfio_irq_info info;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci		minsz = offsetofend(struct vfio_irq_info, count);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci		if (copy_from_user(&info, (void __user *)arg, minsz))
124762306a36Sopenharmony_ci			return -EFAULT;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		if ((info.argsz < minsz) ||
125062306a36Sopenharmony_ci		    (info.index >= VFIO_PCI_NUM_IRQS))
125162306a36Sopenharmony_ci			return -EINVAL;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		ret = mbochs_get_irq_info(&info);
125462306a36Sopenharmony_ci		if (ret)
125562306a36Sopenharmony_ci			return ret;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci		if (copy_to_user((void __user *)arg, &info, minsz))
125862306a36Sopenharmony_ci			return -EFAULT;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci		return 0;
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	case VFIO_DEVICE_QUERY_GFX_PLANE:
126462306a36Sopenharmony_ci	{
126562306a36Sopenharmony_ci		struct vfio_device_gfx_plane_info plane;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci		minsz = offsetofend(struct vfio_device_gfx_plane_info,
126862306a36Sopenharmony_ci				    region_index);
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci		if (copy_from_user(&plane, (void __user *)arg, minsz))
127162306a36Sopenharmony_ci			return -EFAULT;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci		if (plane.argsz < minsz)
127462306a36Sopenharmony_ci			return -EINVAL;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci		ret = mbochs_query_gfx_plane(mdev_state, &plane);
127762306a36Sopenharmony_ci		if (ret)
127862306a36Sopenharmony_ci			return ret;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci		if (copy_to_user((void __user *)arg, &plane, minsz))
128162306a36Sopenharmony_ci			return -EFAULT;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci		return 0;
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	case VFIO_DEVICE_GET_GFX_DMABUF:
128762306a36Sopenharmony_ci	{
128862306a36Sopenharmony_ci		u32 dmabuf_id;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci		if (get_user(dmabuf_id, (__u32 __user *)arg))
129162306a36Sopenharmony_ci			return -EFAULT;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		return mbochs_get_gfx_dmabuf(mdev_state, dmabuf_id);
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	case VFIO_DEVICE_SET_IRQS:
129762306a36Sopenharmony_ci		return -EINVAL;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	case VFIO_DEVICE_RESET:
130062306a36Sopenharmony_ci		return mbochs_reset(mdev_state);
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci	return -ENOTTY;
130362306a36Sopenharmony_ci}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cistatic void mbochs_close_device(struct vfio_device *vdev)
130662306a36Sopenharmony_ci{
130762306a36Sopenharmony_ci	struct mdev_state *mdev_state =
130862306a36Sopenharmony_ci		container_of(vdev, struct mdev_state, vdev);
130962306a36Sopenharmony_ci	struct mbochs_dmabuf *dmabuf, *tmp;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	mutex_lock(&mdev_state->ops_lock);
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci	list_for_each_entry_safe(dmabuf, tmp, &mdev_state->dmabufs, next) {
131462306a36Sopenharmony_ci		list_del(&dmabuf->next);
131562306a36Sopenharmony_ci		if (dmabuf->buf) {
131662306a36Sopenharmony_ci			/* free in mbochs_release_dmabuf() */
131762306a36Sopenharmony_ci			dmabuf->unlinked = true;
131862306a36Sopenharmony_ci		} else {
131962306a36Sopenharmony_ci			kfree(dmabuf);
132062306a36Sopenharmony_ci		}
132162306a36Sopenharmony_ci	}
132262306a36Sopenharmony_ci	mbochs_put_pages(mdev_state);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	mutex_unlock(&mdev_state->ops_lock);
132562306a36Sopenharmony_ci}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_cistatic ssize_t
132862306a36Sopenharmony_cimemory_show(struct device *dev, struct device_attribute *attr,
132962306a36Sopenharmony_ci	    char *buf)
133062306a36Sopenharmony_ci{
133162306a36Sopenharmony_ci	struct mdev_state *mdev_state = dev_get_drvdata(dev);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	return sprintf(buf, "%d MB\n", mdev_state->type->mbytes);
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(memory);
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_cistatic struct attribute *mdev_dev_attrs[] = {
133862306a36Sopenharmony_ci	&dev_attr_memory.attr,
133962306a36Sopenharmony_ci	NULL,
134062306a36Sopenharmony_ci};
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_cistatic const struct attribute_group mdev_dev_group = {
134362306a36Sopenharmony_ci	.name  = "vendor",
134462306a36Sopenharmony_ci	.attrs = mdev_dev_attrs,
134562306a36Sopenharmony_ci};
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cistatic const struct attribute_group *mdev_dev_groups[] = {
134862306a36Sopenharmony_ci	&mdev_dev_group,
134962306a36Sopenharmony_ci	NULL,
135062306a36Sopenharmony_ci};
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_cistatic ssize_t mbochs_show_description(struct mdev_type *mtype, char *buf)
135362306a36Sopenharmony_ci{
135462306a36Sopenharmony_ci	struct mbochs_type *type =
135562306a36Sopenharmony_ci		container_of(mtype, struct mbochs_type, type);
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	return sprintf(buf, "virtual display, %d MB video memory\n",
135862306a36Sopenharmony_ci		       type ? type->mbytes  : 0);
135962306a36Sopenharmony_ci}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_cistatic unsigned int mbochs_get_available(struct mdev_type *mtype)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	struct mbochs_type *type =
136462306a36Sopenharmony_ci		container_of(mtype, struct mbochs_type, type);
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	return atomic_read(&mbochs_avail_mbytes) / type->mbytes;
136762306a36Sopenharmony_ci}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_cistatic const struct vfio_device_ops mbochs_dev_ops = {
137062306a36Sopenharmony_ci	.close_device = mbochs_close_device,
137162306a36Sopenharmony_ci	.init = mbochs_init_dev,
137262306a36Sopenharmony_ci	.release = mbochs_release_dev,
137362306a36Sopenharmony_ci	.read = mbochs_read,
137462306a36Sopenharmony_ci	.write = mbochs_write,
137562306a36Sopenharmony_ci	.ioctl = mbochs_ioctl,
137662306a36Sopenharmony_ci	.mmap = mbochs_mmap,
137762306a36Sopenharmony_ci	.bind_iommufd	= vfio_iommufd_emulated_bind,
137862306a36Sopenharmony_ci	.unbind_iommufd	= vfio_iommufd_emulated_unbind,
137962306a36Sopenharmony_ci	.attach_ioas	= vfio_iommufd_emulated_attach_ioas,
138062306a36Sopenharmony_ci	.detach_ioas	= vfio_iommufd_emulated_detach_ioas,
138162306a36Sopenharmony_ci};
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_cistatic struct mdev_driver mbochs_driver = {
138462306a36Sopenharmony_ci	.device_api = VFIO_DEVICE_API_PCI_STRING,
138562306a36Sopenharmony_ci	.driver = {
138662306a36Sopenharmony_ci		.name = "mbochs",
138762306a36Sopenharmony_ci		.owner = THIS_MODULE,
138862306a36Sopenharmony_ci		.mod_name = KBUILD_MODNAME,
138962306a36Sopenharmony_ci		.dev_groups = mdev_dev_groups,
139062306a36Sopenharmony_ci	},
139162306a36Sopenharmony_ci	.probe = mbochs_probe,
139262306a36Sopenharmony_ci	.remove	= mbochs_remove,
139362306a36Sopenharmony_ci	.get_available = mbochs_get_available,
139462306a36Sopenharmony_ci	.show_description = mbochs_show_description,
139562306a36Sopenharmony_ci};
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_cistatic const struct file_operations vd_fops = {
139862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
139962306a36Sopenharmony_ci};
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistatic void mbochs_device_release(struct device *dev)
140262306a36Sopenharmony_ci{
140362306a36Sopenharmony_ci	/* nothing */
140462306a36Sopenharmony_ci}
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_cistatic int __init mbochs_dev_init(void)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	int ret = 0;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	atomic_set(&mbochs_avail_mbytes, max_mbytes);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	ret = alloc_chrdev_region(&mbochs_devt, 0, MINORMASK + 1, MBOCHS_NAME);
141362306a36Sopenharmony_ci	if (ret < 0) {
141462306a36Sopenharmony_ci		pr_err("Error: failed to register mbochs_dev, err: %d\n", ret);
141562306a36Sopenharmony_ci		return ret;
141662306a36Sopenharmony_ci	}
141762306a36Sopenharmony_ci	cdev_init(&mbochs_cdev, &vd_fops);
141862306a36Sopenharmony_ci	cdev_add(&mbochs_cdev, mbochs_devt, MINORMASK + 1);
141962306a36Sopenharmony_ci	pr_info("%s: major %d\n", __func__, MAJOR(mbochs_devt));
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	ret = mdev_register_driver(&mbochs_driver);
142262306a36Sopenharmony_ci	if (ret)
142362306a36Sopenharmony_ci		goto err_cdev;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	mbochs_class = class_create(MBOCHS_CLASS_NAME);
142662306a36Sopenharmony_ci	if (IS_ERR(mbochs_class)) {
142762306a36Sopenharmony_ci		pr_err("Error: failed to register mbochs_dev class\n");
142862306a36Sopenharmony_ci		ret = PTR_ERR(mbochs_class);
142962306a36Sopenharmony_ci		goto err_driver;
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci	mbochs_dev.class = mbochs_class;
143262306a36Sopenharmony_ci	mbochs_dev.release = mbochs_device_release;
143362306a36Sopenharmony_ci	dev_set_name(&mbochs_dev, "%s", MBOCHS_NAME);
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	ret = device_register(&mbochs_dev);
143662306a36Sopenharmony_ci	if (ret)
143762306a36Sopenharmony_ci		goto err_put;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	ret = mdev_register_parent(&mbochs_parent, &mbochs_dev, &mbochs_driver,
144062306a36Sopenharmony_ci				   mbochs_mdev_types,
144162306a36Sopenharmony_ci				   ARRAY_SIZE(mbochs_mdev_types));
144262306a36Sopenharmony_ci	if (ret)
144362306a36Sopenharmony_ci		goto err_device;
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	return 0;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_cierr_device:
144862306a36Sopenharmony_ci	device_del(&mbochs_dev);
144962306a36Sopenharmony_cierr_put:
145062306a36Sopenharmony_ci	put_device(&mbochs_dev);
145162306a36Sopenharmony_ci	class_destroy(mbochs_class);
145262306a36Sopenharmony_cierr_driver:
145362306a36Sopenharmony_ci	mdev_unregister_driver(&mbochs_driver);
145462306a36Sopenharmony_cierr_cdev:
145562306a36Sopenharmony_ci	cdev_del(&mbochs_cdev);
145662306a36Sopenharmony_ci	unregister_chrdev_region(mbochs_devt, MINORMASK + 1);
145762306a36Sopenharmony_ci	return ret;
145862306a36Sopenharmony_ci}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_cistatic void __exit mbochs_dev_exit(void)
146162306a36Sopenharmony_ci{
146262306a36Sopenharmony_ci	mbochs_dev.bus = NULL;
146362306a36Sopenharmony_ci	mdev_unregister_parent(&mbochs_parent);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	device_unregister(&mbochs_dev);
146662306a36Sopenharmony_ci	mdev_unregister_driver(&mbochs_driver);
146762306a36Sopenharmony_ci	cdev_del(&mbochs_cdev);
146862306a36Sopenharmony_ci	unregister_chrdev_region(mbochs_devt, MINORMASK + 1);
146962306a36Sopenharmony_ci	class_destroy(mbochs_class);
147062306a36Sopenharmony_ci	mbochs_class = NULL;
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ciMODULE_IMPORT_NS(DMA_BUF);
147462306a36Sopenharmony_cimodule_init(mbochs_dev_init)
147562306a36Sopenharmony_cimodule_exit(mbochs_dev_exit)
1476