diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index d4df410..c4a6615 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -33,6 +33,7 @@ i915-y += i915_cmd_parser.o \
 	  i915_gem_stolen.o \
 	  i915_gem_tiling.o \
 	  i915_gem_userptr.o \
+	  i915_gem_gvtbuffer.o \
 	  i915_gpu_error.o \
 	  i915_trace_points.o \
 	  intel_lrc.o \
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index eca8e50..f150602 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1351,6 +1351,7 @@ const struct drm_ioctl_desc i915_ioctls[] = {
 	DRM_IOCTL_DEF_DRV(I915_GEM_USERPTR, i915_gem_userptr_ioctl, DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_GETPARAM, i915_gem_context_getparam_ioctl, DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_SETPARAM, i915_gem_context_setparam_ioctl, DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(I915_GEM_GVTBUFFER, i915_gem_gvtbuffer_ioctl, DRM_RENDER_ALLOW),
 };
 
 int i915_max_ioctl = ARRAY_SIZE(i915_ioctls);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index fc5ddee..d387754 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3238,6 +3238,9 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data,
 int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data,
 				    struct drm_file *file_priv);
 
+int i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data,
+			     struct drm_file *file);
+
 /* i915_gem_evict.c */
 int __must_check i915_gem_evict_something(struct drm_device *dev,
 					  struct i915_address_space *vm,
diff --git a/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c
new file mode 100644
index 0000000..3aa2ee8
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_gem_gvtbuffer.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright © 2012 - 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include "i915_drv.h"
+#include "i915_trace.h"
+#include "intel_drv.h"
+#include <linux/swap.h>
+
+#include "gvt/fb_decoder.h"
+
+static int i915_gem_gvtbuffer_get_pages(struct drm_i915_gem_object *obj)
+{
+	BUG();
+	return -EINVAL;
+}
+
+static void i915_gem_gvtbuffer_put_pages(struct drm_i915_gem_object *obj)
+{
+	/* like stolen memory, this should only be called during free
+	 * after clearing pin count.
+	 */
+	sg_free_table(obj->pages);
+	kfree(obj->pages);
+}
+
+static const struct drm_i915_gem_object_ops i915_gem_gvtbuffer_ops = {
+	.get_pages = i915_gem_gvtbuffer_get_pages,
+	.put_pages = i915_gem_gvtbuffer_put_pages,
+};
+
+#define GEN8_DECODE_PTE(pte) \
+	((dma_addr_t)(((((u64)pte) >> 12) & 0x7ffffffULL) << 12))
+
+#define GEN7_DECODE_PTE(pte) \
+	((dma_addr_t)(((((u64)pte) & 0x7f0) << 28) | (u64)(pte & 0xfffff000)))
+
+static struct sg_table *
+i915_create_sg_pages_for_gvtbuffer(struct drm_device *dev,
+			     u32 start, u32 num_pages)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct sg_table *st;
+	struct scatterlist *sg;
+	int i;
+
+	st = kmalloc(sizeof(*st), GFP_KERNEL);
+	if (st == NULL)
+		return NULL;
+
+	if (sg_alloc_table(st, num_pages, GFP_KERNEL)) {
+		kfree(st);
+		return NULL;
+	}
+
+	if (INTEL_INFO(dev)->gen >= 8) {
+		gen8_pte_t __iomem *gtt_entries =
+			(gen8_pte_t __iomem *)dev_priv->gtt.gsm +
+			(start >> PAGE_SHIFT);
+		for_each_sg(st->sgl, sg, num_pages, i) {
+			sg->offset = 0;
+			sg->length = PAGE_SIZE;
+			sg_dma_address(sg) =
+				GEN8_DECODE_PTE(readq(&gtt_entries[i]));
+			sg_dma_len(sg) = PAGE_SIZE;
+		}
+	} else {
+		gen6_pte_t __iomem *gtt_entries =
+			(gen6_pte_t __iomem *)dev_priv->gtt.gsm +
+			(start >> PAGE_SHIFT);
+		for_each_sg(st->sgl, sg, num_pages, i) {
+			sg->offset = 0;
+			sg->length = PAGE_SIZE;
+			sg_dma_address(sg) =
+				GEN7_DECODE_PTE(readq(&gtt_entries[i]));
+			sg_dma_len(sg) = PAGE_SIZE;
+		}
+	}
+
+	return st;
+}
+
+struct drm_i915_gem_object *
+i915_gem_object_create_gvtbuffer(struct drm_device *dev,
+				 u32 start, u32 num_pages)
+{
+	struct drm_i915_gem_object *obj;
+	obj = i915_gem_object_alloc(dev);
+	if (obj == NULL)
+		return NULL;
+
+	drm_gem_private_object_init(dev, &obj->base, num_pages << PAGE_SHIFT);
+	i915_gem_object_init(obj, &i915_gem_gvtbuffer_ops);
+
+	obj->pages = i915_create_sg_pages_for_gvtbuffer(dev, start, num_pages);
+	if (obj->pages == NULL) {
+		i915_gem_object_free(obj);
+		return NULL;
+	}
+
+	i915_gem_object_pin_pages(obj);
+	obj->cache_level = I915_CACHE_L3_LLC;
+
+	DRM_DEBUG_DRIVER("GVT_GEM: backing store base = 0x%x pages = 0x%x\n",
+			 start, num_pages);
+	return obj;
+}
+
+static int gvt_decode_information(struct drm_device *dev,
+				  struct drm_i915_gem_gvtbuffer *args)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	u32 vmid = args->vmid;
+	struct gvt_fb_format fb;
+	struct gvt_primary_plane_format *p;
+	struct gvt_cursor_plane_format *c;
+	struct gvt_pipe_format *pipe;
+
+	if (gvt_decode_fb_format(dev_priv->vgpu.host_private_data, vmid, &fb))
+		return -EINVAL;
+
+	pipe = ((args->pipe_id >= I915_MAX_PIPES) ?
+		NULL : &fb.pipes[args->pipe_id]);
+
+	if (!pipe || !pipe->primary.enabled) {
+		DRM_DEBUG_DRIVER("GVT_GEM: Invalid pipe_id: %d\n",
+				 args->pipe_id);
+		return -EINVAL;
+	}
+
+	if ((args->plane_id) == I915_GVT_PLANE_PRIMARY) {
+		p = &pipe->primary;
+		args->enabled = p->enabled;
+		args->x_offset = p->x_offset;
+		args->y_offset = p->y_offset;
+		args->start = p->base;
+		args->width = p->width;
+		args->height = p->height;
+		args->stride = p->stride;
+		args->bpp = p->bpp;
+		args->hw_format = p->hw_format;
+		args->drm_format = p->drm_format;
+		args->tiled = p->tiled;
+	} else if ((args->plane_id) == I915_GVT_PLANE_CURSOR) {
+		c = &pipe->cursor;
+		args->enabled = c->enabled;
+		args->x_offset = c->x_hot;
+		args->y_offset = c->y_hot;
+		args->x_pos = c->x_pos;
+		args->y_pos = c->y_pos;
+		args->start = c->base;
+		args->width = c->width;
+		args->height = c->height;
+		args->stride = c->width * (c->bpp / 8);
+		args->bpp = c->bpp;
+		args->tiled = 0;
+	} else {
+		DRM_DEBUG_DRIVER("GVT_GEM: Invalid plaine_id: %d\n",
+				 args->plane_id);
+		return -EINVAL;
+	}
+
+	args->size = (((args->width * args->height * args->bpp) / 8) +
+		      (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+
+	if (args->start & (PAGE_SIZE - 1)) {
+		DRM_DEBUG_DRIVER("GVT_GEM: Not aligned fb start address: "
+				 "0x%x\n", args->start);
+		return -EINVAL;
+	}
+
+	if (((args->start >> PAGE_SHIFT) + args->size) >
+	    gtt_total_entries(dev_priv->gtt)) {
+		DRM_DEBUG_DRIVER("GVT: Invalid GTT offset or size\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/**
+ * Creates a new mm object that wraps some user memory.
+ */
+int
+i915_gem_gvtbuffer_ioctl(struct drm_device *dev, void *data,
+			 struct drm_file *file)
+{
+	struct drm_i915_gem_gvtbuffer *args = data;
+	struct drm_i915_gem_object *obj;
+	u32 handle;
+	int ret;
+
+	if (INTEL_INFO(dev)->gen < 7)
+		return -EPERM;
+#if 0
+	if (!gvt_check_host())
+		return -EPERM;
+#endif
+	ret = gvt_decode_information(dev, args);
+	if (ret)
+		return ret;
+
+	if (args->flags & I915_GVTBUFFER_QUERY_ONLY)
+		return 0;
+
+	obj = i915_gem_object_create_gvtbuffer(dev, args->start, args->size);
+	if (!obj) {
+		DRM_DEBUG_DRIVER("GVT_GEM: Failed to create gem object"
+					" for VM FB!\n");
+		return -EINVAL;
+	}
+
+	obj->tiling_mode = args->tiled ? I915_TILING_X : I915_TILING_NONE;
+	obj->stride = args->tiled ? args->stride : 0;
+
+	ret = drm_gem_handle_create(file, &obj->base, &handle);
+	if (ret) {
+		/* TODO: Double confirm the error handling path */
+		i915_gem_object_free(obj);
+		return ret;
+	}
+
+	/* drop reference from allocate - handle holds it now */
+	drm_gem_object_unreference(&obj->base);
+
+	args->handle = handle;
+	return 0;
+}
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index 6a19371..5ba115a 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -230,6 +230,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_I915_GEM_USERPTR		0x33
 #define DRM_I915_GEM_CONTEXT_GETPARAM	0x34
 #define DRM_I915_GEM_CONTEXT_SETPARAM	0x35
+#define DRM_I915_GEM_GVTBUFFER          0x36
 
 #define DRM_IOCTL_I915_INIT		DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
 #define DRM_IOCTL_I915_FLUSH		DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@@ -283,6 +284,7 @@ typedef struct _drm_i915_sarea {
 #define DRM_IOCTL_I915_GEM_USERPTR			DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_USERPTR, struct drm_i915_gem_userptr)
 #define DRM_IOCTL_I915_GEM_CONTEXT_GETPARAM	DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_GETPARAM, struct drm_i915_gem_context_param)
 #define DRM_IOCTL_I915_GEM_CONTEXT_SETPARAM	DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_SETPARAM, struct drm_i915_gem_context_param)
+#define DRM_IOCTL_I915_GEM_GVTBUFFER		DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GVTBUFFER, struct drm_i915_gem_gvtbuffer)
 
 /* Allow drivers to submit batchbuffers directly to hardware, relying
  * on the security mechanisms provided by hardware.
@@ -1168,4 +1170,40 @@ struct drm_i915_gem_context_param {
 	__u64 value;
 };
 
+struct drm_i915_gem_gvtbuffer {
+        __u32 vmid;
+	__u32 plane_id;
+#define I915_GVT_PLANE_PRIMARY 1
+#define I915_GVT_PLANE_SPRITE 2
+#define I915_GVT_PLANE_CURSOR 3
+	__u32 pipe_id;
+	__u32 phys_pipe_id;
+	__u8  enabled;
+	__u8  tiled;
+	__u32 bpp;
+	__u32 hw_format;
+	__u32 drm_format;
+	__u32 start;
+	__u32 x_pos;
+	__u32 y_pos;
+	__u32 x_offset;
+	__u32 y_offset;
+	__u32 size;
+	__u32 width;
+	__u32 height;
+	__u32 stride;
+	__u64 user_ptr;
+	__u32 user_size;
+	__u32 flags;
+#define I915_GVTBUFFER_READ_ONLY (1<<0)
+#define I915_GVTBUFFER_QUERY_ONLY (1<<1)
+#define I915_GVTBUFFER_UNSYNCHRONIZED 0x80000000
+	/**
+	 * Returned handle for the object.
+	 *
+	 * Object handles are nonzero.
+	 */
+	__u32 handle;
+};
+
 #endif /* _UAPI_I915_DRM_H_ */
