diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 00d9ab3..cfad8c1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2298,8 +2298,14 @@ void *i915_gem_object_alloc(struct drm_device *dev);
 void i915_gem_object_free(struct drm_i915_gem_object *obj);
 void i915_gem_object_init(struct drm_i915_gem_object *obj,
 			 const struct drm_i915_gem_object_ops *ops);
-struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
-						  size_t size);
+struct drm_i915_gem_object *_i915_gem_alloc_object(struct drm_device *dev,
+						  size_t size,
+						  bool user_obj);
+#define i915_gem_alloc_object(dev, size) _i915_gem_alloc_object(dev, size, false)
+int i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj, unsigned flags);
+int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj);
+void i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj);
+#define i915_is_soft_pinned(vma) ((vma)->obj->ops->get_pages == i915_gem_userptr_get_pages)
 void i915_init_vm(struct drm_i915_private *dev_priv,
 		  struct i915_address_space *vm);
 void i915_gem_free_object(struct drm_gem_object *obj);
@@ -2311,11 +2317,13 @@ void i915_gem_vma_destroy(struct i915_vma *vma);
 #define PIN_ALIASING	(1<<3)
 #define PIN_GLOBAL_ALIASED (PIN_ALIASING | PIN_GLOBAL)
 #define PIN_OFFSET_BIAS (1<<4)
+#define PIN_SOFT	(1<<5)
 #define PIN_OFFSET_MASK (PAGE_MASK)
 int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
 				     struct i915_address_space *vm,
 				     uint32_t alignment,
 				     uint64_t flags);
+int i915_vma_softpin(struct i915_vma *vma, uint64_t start, uint64_t size);
 int __must_check i915_vma_unbind(struct i915_vma *vma);
 int i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
 void i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 6413f3a..75d2454 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -347,21 +347,33 @@ static int
 i915_gem_create(struct drm_file *file,
 		struct drm_device *dev,
 		uint64_t size,
+		uint64_t loc,
 		uint32_t *handle_p)
 {
 	struct drm_i915_gem_object *obj;
 	int ret;
 	u32 handle;
+	bool user_obj = false;
+
+	if (size & BIT_ULL(63)) {
+		if (!HAS_48B_PPGTT(dev) || !USES_FULL_PPGTT(dev))
+			return -EINVAL;
+		size &= ~BIT_ULL(63);
+		user_obj = true;
+	}
 
 	size = roundup(size, PAGE_SIZE);
 	if (size == 0)
 		return -EINVAL;
 
 	/* Allocate the new object */
-	obj = i915_gem_alloc_object(dev, size);
+	obj = _i915_gem_alloc_object(dev, size, user_obj);
 	if (obj == NULL)
 		return -ENOMEM;
 
+	if (user_obj)
+		 obj->userptr.ptr = loc;
+
 	ret = drm_gem_handle_create(file, &obj->base, &handle);
 	/* drop reference from allocate - handle holds it now */
 	drm_gem_object_unreference_unlocked(&obj->base);
@@ -380,7 +392,7 @@ i915_gem_dumb_create(struct drm_file *file,
 	/* have to work out size/pitch and return them */
 	args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 64);
 	args->size = args->pitch * args->height;
-	return i915_gem_create(file, dev,
+	return i915_gem_create(file, dev, 0,
 			       args->size, &args->handle);
 }
 
@@ -392,9 +404,10 @@ i915_gem_create_ioctl(struct drm_device *dev, void *data,
 		      struct drm_file *file)
 {
 	struct drm_i915_gem_create *args = data;
+	uint64_t location = ((uint64_t)args->handle) << 32 | args->pad;
 
 	return i915_gem_create(file, dev,
-			       args->size, &args->handle);
+			       args->size, location, &args->handle);
 }
 
 static inline int
@@ -3038,7 +3051,10 @@ int i915_vma_unbind(struct i915_vma *vma)
 	if (i915_is_ggtt(vma->vm))
 		obj->map_and_fenceable = true;
 
-	drm_mm_remove_node(&vma->node);
+	if (i915_is_soft_pinned(vma))
+		vma->node.allocated = 0;
+	else
+		drm_mm_remove_node(&vma->node);
 	i915_gem_vma_destroy(vma);
 
 	/* Since the unbound list is global, only move to that list if
@@ -3544,6 +3560,8 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
 	if (IS_ERR(vma))
 		goto err_unpin;
 
+	BUG_ON(i915_is_soft_pinned(vma));
+
 search_free:
 	ret = drm_mm_insert_node_in_range_generic(&vm->mm, &vma->node,
 						  size, alignment,
@@ -4227,6 +4245,43 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj,
 	return 0;
 }
 
+int i915_vma_softpin(struct i915_vma *vma, uint64_t start, uint64_t size)
+{
+	struct drm_i915_private *dev_priv = to_i915(vma->vm->dev);
+	struct drm_i915_gem_object *obj = vma->obj;
+	int ret;
+
+	BUG_ON(!i915_is_soft_pinned(vma));
+	ret = i915_gem_object_get_pages(obj);
+	if (ret)
+		return ret;
+
+	i915_gem_object_pin_pages(obj);
+
+	ret = i915_gem_gtt_prepare_object(obj);
+	if (ret)
+		goto err_release_pages;
+
+	vma->node.allocated = 1;
+
+	list_move_tail(&obj->global_list, &dev_priv->mm.bound_list);
+	list_add_tail(&vma->mm_list, &vma->vm->inactive_list);
+
+	vma->node.start = start;
+	vma->node.size = size;
+	trace_i915_vma_bind(vma, PIN_SOFT);
+	i915_gem_vma_bind(vma, obj->cache_level, SOFT_PINNED);
+	vma->pin_count++;
+
+	return 0;
+
+err_release_pages:
+	i915_gem_vma_destroy(vma);
+	i915_gem_object_unpin_pages(obj);
+
+	return ret;
+}
+
 void
 i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj)
 {
@@ -4473,8 +4528,16 @@ static const struct drm_i915_gem_object_ops i915_gem_object_ops = {
 	.put_pages = i915_gem_object_put_pages_gtt,
 };
 
-struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
-						  size_t size)
+/* Soft pinned objects are those which have user pages, and an offset defined by
+ * userspace
+ */
+static const struct drm_i915_gem_object_ops i915_gem_soft_pin_object_ops = {
+	.get_pages = i915_gem_userptr_get_pages,
+	.put_pages = i915_gem_userptr_put_pages,
+};
+
+struct drm_i915_gem_object *_i915_gem_alloc_object(struct drm_device *dev,
+						   size_t size, bool user_obj)
 {
 	struct drm_i915_gem_object *obj;
 	struct address_space *mapping;
@@ -4499,7 +4562,16 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
 	mapping = file_inode(obj->base.filp)->i_mapping;
 	mapping_set_gfp_mask(mapping, mask);
 
-	i915_gem_object_init(obj, &i915_gem_object_ops);
+	if (user_obj) {
+		i915_gem_object_init(obj, &i915_gem_soft_pin_object_ops);
+		obj->userptr.mm = get_task_mm(current);
+		if (i915_gem_userptr_init__mmu_notifier(obj, 0)) {
+			i915_gem_object_free(obj);
+			drm_gem_object_unreference_unlocked(&obj->base);
+			return NULL;
+		}
+	} else
+		i915_gem_object_init(obj, &i915_gem_object_ops);
 
 	obj->base.write_domain = I915_GEM_DOMAIN_CPU;
 	obj->base.read_domains = I915_GEM_DOMAIN_CPU;
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index fdd68d6..c1761f0 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -571,7 +571,10 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
 	if (entry->flags & __EXEC_OBJECT_NEEDS_BIAS)
 		flags |= BATCH_OFFSET_BIAS | PIN_OFFSET_BIAS;
 
-	ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, flags);
+	if (i915_is_soft_pinned(vma))
+		ret = i915_vma_softpin(vma, entry->offset, vma->obj->base.size);
+	else
+		ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, flags);
 	if (ret)
 		return ret;
 
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
index 92acd95..e4ab4ba 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.h
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
@@ -185,6 +185,7 @@ struct i915_vma {
 /* Only use this if you know you want a strictly aliased binding */
 #define ALIASING_BIND (1<<1)
 #define PTE_READ_ONLY (1<<2)
+#define SOFT_PINNED   (1<<3) /* Just debug for now */
 	int (*bind_vma)(struct i915_vma *vma,
 			enum i915_cache_level cache_level,
 			u32 flags);
diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c
index fe69fc8..ff7aea6 100644
--- a/drivers/gpu/drm/i915/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/i915_gem_userptr.c
@@ -341,7 +341,7 @@ i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj)
 	obj->userptr.mn = NULL;
 }
 
-static int
+int
 i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj,
 				    unsigned flags)
 {
@@ -519,7 +519,7 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
 	kfree(work);
 }
 
-static int
+int
 i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
 {
 	const int num_pages = obj->base.size >> PAGE_SHIFT;
@@ -598,6 +598,7 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
 					get_task_struct(work->task);
 
 					INIT_WORK(&work->work, __i915_gem_userptr_get_pages_worker);
+					WARN_ON(!obj->userptr.ptr);
 					schedule_work(&work->work);
 				} else
 					ret = -ENOMEM;
@@ -621,7 +622,7 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
 	return ret;
 }
 
-static void
+void
 i915_gem_userptr_put_pages(struct drm_i915_gem_object *obj)
 {
 	struct scatterlist *sg;
diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h
index ff57f07..413a4fb 100644
--- a/include/uapi/drm/i915_drm.h
+++ b/include/uapi/drm/i915_drm.h
@@ -662,7 +662,8 @@ struct drm_i915_gem_exec_object2 {
 #define EXEC_OBJECT_NEEDS_FENCE (1<<0)
 #define EXEC_OBJECT_NEEDS_GTT	(1<<1)
 #define EXEC_OBJECT_WRITE	(1<<2)
-#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_WRITE<<1)
+#define EXEC_OBJECT_SOFT_PINNED (1<<3)
+#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_SOFT_PINNED<<1)
 	__u64 flags;
 
 	__u64 rsvd1;
