Skip to content

Commit b851ade

Browse files
TinaZhangZWzhenyw
authored andcommitted
drm/i915/gvt: Add opregion support
Windows guest driver needs vbt in opregion, to configure the setting for display. Without opregion support, the display registers won't be set and this blocks display model to get the correct information of the guest display plane. This patch is to provide a virtual opregion for guest. The original author of this patch is Xiaoguang Chen. This patch is split from the "Dma-buf support for GVT-g" patch set, with being rebased to the latest gvt-staging branch. v3: - add checking region index during intel_vgpu_rw. (Xiong) v2: - refine intel_vgpu_reg_release_opregion. (Xiong) Here are the previous version comments: v18: - unmap vgpu's opregion when destroying vgpu. v16: - rebase to 4.14.0-rc6. Signed-off-by: Bing Niu <bing.niu@intel.com> Signed-off-by: Tina Zhang <tina.zhang@intel.com> Tested-by: Xiong Zhang <xiong.y.zhang@intel.com> Cc: Zhenyu Wang <zhenyuw@linux.intel.com> Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
1 parent 4dff110 commit b851ade

File tree

6 files changed

+184
-19
lines changed

6 files changed

+184
-19
lines changed

drivers/gpu/drm/i915/gvt/gvt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ struct intel_vgpu_irq {
125125
struct intel_vgpu_opregion {
126126
bool mapped;
127127
void *va;
128+
void *va_gopregion;
128129
u32 gfn[INTEL_GVT_OPREGION_PAGES];
129130
};
130131

drivers/gpu/drm/i915/gvt/hypercall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct intel_gvt_mpt {
5555
unsigned long mfn, unsigned int nr, bool map);
5656
int (*set_trap_area)(unsigned long handle, u64 start, u64 end,
5757
bool map);
58+
int (*set_opregion)(void *vgpu);
5859
};
5960

6061
extern struct intel_gvt_mpt xengt_mpt;

drivers/gpu/drm/i915/gvt/kvmgt.c

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,23 @@ static const struct intel_gvt_ops *intel_gvt_ops;
5353
#define VFIO_PCI_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_PCI_OFFSET_SHIFT)
5454
#define VFIO_PCI_OFFSET_MASK (((u64)(1) << VFIO_PCI_OFFSET_SHIFT) - 1)
5555

56+
#define OPREGION_SIGNATURE "IntelGraphicsMem"
57+
58+
struct vfio_region;
59+
struct intel_vgpu_regops {
60+
size_t (*rw)(struct intel_vgpu *vgpu, char *buf,
61+
size_t count, loff_t *ppos, bool iswrite);
62+
void (*release)(struct intel_vgpu *vgpu,
63+
struct vfio_region *region);
64+
};
65+
5666
struct vfio_region {
5767
u32 type;
5868
u32 subtype;
5969
size_t size;
6070
u32 flags;
71+
const struct intel_vgpu_regops *ops;
72+
void *data;
6173
};
6274

6375
struct kvmgt_pgfn {
@@ -316,6 +328,87 @@ static void kvmgt_protect_table_del(struct kvmgt_guest_info *info,
316328
}
317329
}
318330

331+
static size_t intel_vgpu_reg_rw_opregion(struct intel_vgpu *vgpu, char *buf,
332+
size_t count, loff_t *ppos, bool iswrite)
333+
{
334+
unsigned int i = VFIO_PCI_OFFSET_TO_INDEX(*ppos) -
335+
VFIO_PCI_NUM_REGIONS;
336+
void *base = vgpu->vdev.region[i].data;
337+
loff_t pos = *ppos & VFIO_PCI_OFFSET_MASK;
338+
339+
if (pos >= vgpu->vdev.region[i].size || iswrite) {
340+
gvt_vgpu_err("invalid op or offset for Intel vgpu OpRegion\n");
341+
return -EINVAL;
342+
}
343+
count = min(count, (size_t)(vgpu->vdev.region[i].size - pos));
344+
memcpy(buf, base + pos, count);
345+
346+
return count;
347+
}
348+
349+
static void intel_vgpu_reg_release_opregion(struct intel_vgpu *vgpu,
350+
struct vfio_region *region)
351+
{
352+
}
353+
354+
static const struct intel_vgpu_regops intel_vgpu_regops_opregion = {
355+
.rw = intel_vgpu_reg_rw_opregion,
356+
.release = intel_vgpu_reg_release_opregion,
357+
};
358+
359+
static int intel_vgpu_register_reg(struct intel_vgpu *vgpu,
360+
unsigned int type, unsigned int subtype,
361+
const struct intel_vgpu_regops *ops,
362+
size_t size, u32 flags, void *data)
363+
{
364+
struct vfio_region *region;
365+
366+
region = krealloc(vgpu->vdev.region,
367+
(vgpu->vdev.num_regions + 1) * sizeof(*region),
368+
GFP_KERNEL);
369+
if (!region)
370+
return -ENOMEM;
371+
372+
vgpu->vdev.region = region;
373+
vgpu->vdev.region[vgpu->vdev.num_regions].type = type;
374+
vgpu->vdev.region[vgpu->vdev.num_regions].subtype = subtype;
375+
vgpu->vdev.region[vgpu->vdev.num_regions].ops = ops;
376+
vgpu->vdev.region[vgpu->vdev.num_regions].size = size;
377+
vgpu->vdev.region[vgpu->vdev.num_regions].flags = flags;
378+
vgpu->vdev.region[vgpu->vdev.num_regions].data = data;
379+
vgpu->vdev.num_regions++;
380+
381+
return 0;
382+
}
383+
384+
static int kvmgt_set_opregion(void *p_vgpu)
385+
{
386+
struct intel_vgpu *vgpu = (struct intel_vgpu *)p_vgpu;
387+
void *base;
388+
int ret;
389+
390+
/* Each vgpu has its own opregion, although VFIO would create another
391+
* one later. This one is used to expose opregion to VFIO. And the
392+
* other one created by VFIO later, is used by guest actually.
393+
*/
394+
base = vgpu_opregion(vgpu)->va;
395+
if (!base)
396+
return -ENOMEM;
397+
398+
if (memcmp(base, OPREGION_SIGNATURE, 16)) {
399+
memunmap(base);
400+
return -EINVAL;
401+
}
402+
403+
ret = intel_vgpu_register_reg(vgpu,
404+
PCI_VENDOR_ID_INTEL | VFIO_REGION_TYPE_PCI_VENDOR_TYPE,
405+
VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION,
406+
&intel_vgpu_regops_opregion, OPREGION_SIZE,
407+
VFIO_REGION_INFO_FLAG_READ, base);
408+
409+
return ret;
410+
}
411+
319412
static int intel_vgpu_create(struct kobject *kobj, struct mdev_device *mdev)
320413
{
321414
struct intel_vgpu *vgpu = NULL;
@@ -546,7 +639,7 @@ static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf,
546639
int ret = -EINVAL;
547640

548641

549-
if (index >= VFIO_PCI_NUM_REGIONS) {
642+
if (index >= VFIO_PCI_NUM_REGIONS + vgpu->vdev.num_regions) {
550643
gvt_vgpu_err("invalid index: %u\n", index);
551644
return -EINVAL;
552645
}
@@ -574,8 +667,14 @@ static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf,
574667
case VFIO_PCI_BAR5_REGION_INDEX:
575668
case VFIO_PCI_VGA_REGION_INDEX:
576669
case VFIO_PCI_ROM_REGION_INDEX:
670+
break;
577671
default:
578-
gvt_vgpu_err("unsupported region: %u\n", index);
672+
if (index >= VFIO_PCI_NUM_REGIONS + vgpu->vdev.num_regions)
673+
return -EINVAL;
674+
675+
index -= VFIO_PCI_NUM_REGIONS;
676+
return vgpu->vdev.region[index].ops->rw(vgpu, buf, count,
677+
ppos, is_write);
579678
}
580679

581680
return ret == 0 ? count : ret;
@@ -838,7 +937,8 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd,
838937

839938
info.flags = VFIO_DEVICE_FLAGS_PCI;
840939
info.flags |= VFIO_DEVICE_FLAGS_RESET;
841-
info.num_regions = VFIO_PCI_NUM_REGIONS;
940+
info.num_regions = VFIO_PCI_NUM_REGIONS +
941+
vgpu->vdev.num_regions;
842942
info.num_irqs = VFIO_PCI_NUM_IRQS;
843943

844944
return copy_to_user((void __user *)arg, &info, minsz) ?
@@ -959,6 +1059,7 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd,
9591059
}
9601060

9611061
if (caps.size) {
1062+
info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
9621063
if (info.argsz < sizeof(info) + caps.size) {
9631064
info.argsz = sizeof(info) + caps.size;
9641065
info.cap_offset = 0;
@@ -1426,6 +1527,7 @@ struct intel_gvt_mpt kvmgt_mpt = {
14261527
.read_gpa = kvmgt_read_gpa,
14271528
.write_gpa = kvmgt_write_gpa,
14281529
.gfn_to_mfn = kvmgt_gfn_to_pfn,
1530+
.set_opregion = kvmgt_set_opregion,
14291531
};
14301532
EXPORT_SYMBOL_GPL(kvmgt_mpt);
14311533

drivers/gpu/drm/i915/gvt/mpt.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,4 +294,19 @@ static inline int intel_gvt_hypervisor_set_trap_area(
294294
return intel_gvt_host.mpt->set_trap_area(vgpu->handle, start, end, map);
295295
}
296296

297+
/**
298+
* intel_gvt_hypervisor_set_opregion - Set opregion for guest
299+
* @vgpu: a vGPU
300+
*
301+
* Returns:
302+
* Zero on success, negative error code if failed.
303+
*/
304+
static inline int intel_gvt_hypervisor_set_opregion(struct intel_vgpu *vgpu)
305+
{
306+
if (!intel_gvt_host.mpt->set_opregion)
307+
return 0;
308+
309+
return intel_gvt_host.mpt->set_opregion(vgpu);
310+
}
311+
297312
#endif /* _GVT_MPT_H_ */

drivers/gpu/drm/i915/gvt/opregion.c

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -297,19 +297,41 @@ static int map_vgpu_opregion(struct intel_vgpu *vgpu, bool map)
297297
*/
298298
int intel_vgpu_opregion_base_write_handler(struct intel_vgpu *vgpu, u32 gpa)
299299
{
300-
int i, ret;
301300

302-
/**
303-
* Wins guest on Xengt will write this register twice: xen hvmloader and
304-
* windows graphic driver.
305-
*/
306-
if (vgpu_opregion(vgpu)->mapped)
307-
map_vgpu_opregion(vgpu, false);
301+
int i, ret = 0;
302+
unsigned long pfn;
303+
304+
gvt_dbg_core("emulate opregion from kernel\n");
305+
306+
switch (intel_gvt_host.hypervisor_type) {
307+
case INTEL_GVT_HYPERVISOR_KVM:
308+
pfn = intel_gvt_hypervisor_gfn_to_mfn(vgpu, gpa >> PAGE_SHIFT);
309+
vgpu_opregion(vgpu)->va_gopregion = memremap(pfn << PAGE_SHIFT,
310+
INTEL_GVT_OPREGION_SIZE,
311+
MEMREMAP_WB);
312+
if (!vgpu_opregion(vgpu)->va_gopregion) {
313+
gvt_vgpu_err("failed to map guest opregion\n");
314+
ret = -EFAULT;
315+
}
316+
vgpu_opregion(vgpu)->mapped = true;
317+
break;
318+
case INTEL_GVT_HYPERVISOR_XEN:
319+
/**
320+
* Wins guest on Xengt will write this register twice: xen
321+
* hvmloader and windows graphic driver.
322+
*/
323+
if (vgpu_opregion(vgpu)->mapped)
324+
map_vgpu_opregion(vgpu, false);
308325

309-
for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++)
310-
vgpu_opregion(vgpu)->gfn[i] = (gpa >> PAGE_SHIFT) + i;
326+
for (i = 0; i < INTEL_GVT_OPREGION_PAGES; i++)
327+
vgpu_opregion(vgpu)->gfn[i] = (gpa >> PAGE_SHIFT) + i;
311328

312-
ret = map_vgpu_opregion(vgpu, true);
329+
ret = map_vgpu_opregion(vgpu, true);
330+
break;
331+
default:
332+
ret = -EINVAL;
333+
gvt_vgpu_err("not supported hypervisor\n");
334+
}
313335

314336
return ret;
315337
}
@@ -326,13 +348,20 @@ void intel_vgpu_clean_opregion(struct intel_vgpu *vgpu)
326348
if (!vgpu_opregion(vgpu)->va)
327349
return;
328350

329-
if (vgpu_opregion(vgpu)->mapped)
330-
map_vgpu_opregion(vgpu, false);
331-
351+
if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_XEN) {
352+
if (vgpu_opregion(vgpu)->mapped)
353+
map_vgpu_opregion(vgpu, false);
354+
} else if (intel_gvt_host.hypervisor_type == INTEL_GVT_HYPERVISOR_KVM) {
355+
if (vgpu_opregion(vgpu)->mapped) {
356+
memunmap(vgpu_opregion(vgpu)->va_gopregion);
357+
vgpu_opregion(vgpu)->va_gopregion = NULL;
358+
}
359+
}
332360
free_pages((unsigned long)vgpu_opregion(vgpu)->va,
333-
get_order(INTEL_GVT_OPREGION_SIZE));
361+
get_order(INTEL_GVT_OPREGION_SIZE));
334362

335363
vgpu_opregion(vgpu)->va = NULL;
364+
336365
}
337366

338367

@@ -454,8 +483,21 @@ int intel_vgpu_emulate_opregion_request(struct intel_vgpu *vgpu, u32 swsci)
454483
u32 *scic, *parm;
455484
u32 func, subfunc;
456485

457-
scic = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_SCIC;
458-
parm = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_PARM;
486+
switch (intel_gvt_host.hypervisor_type) {
487+
case INTEL_GVT_HYPERVISOR_XEN:
488+
scic = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_SCIC;
489+
parm = vgpu_opregion(vgpu)->va + INTEL_GVT_OPREGION_PARM;
490+
break;
491+
case INTEL_GVT_HYPERVISOR_KVM:
492+
scic = vgpu_opregion(vgpu)->va_gopregion +
493+
INTEL_GVT_OPREGION_SCIC;
494+
parm = vgpu_opregion(vgpu)->va_gopregion +
495+
INTEL_GVT_OPREGION_PARM;
496+
break;
497+
default:
498+
gvt_vgpu_err("not supported hypervisor\n");
499+
return -EINVAL;
500+
}
459501

460502
if (!(swsci & SWSCI_SCI_SELECT)) {
461503
gvt_vgpu_err("requesting SMI service\n");

drivers/gpu/drm/i915/gvt/vgpu.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,10 @@ static struct intel_vgpu *__intel_gvt_create_vgpu(struct intel_gvt *gvt,
390390
if (ret)
391391
goto out_clean_sched_policy;
392392

393+
ret = intel_gvt_hypervisor_set_opregion(vgpu);
394+
if (ret)
395+
goto out_clean_sched_policy;
396+
393397
mutex_unlock(&gvt->lock);
394398

395399
return vgpu;

0 commit comments

Comments
 (0)