diff --git a/lib/i915/intel_memory_region.c b/lib/i915/intel_memory_region.c
new file mode 100644
index 00000000..983ac4df
--- /dev/null
+++ b/lib/i915/intel_memory_region.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright © 2020 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 <linux/limits.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "i915/gem_create.h"
+#include "intel_reg.h"
+#include "drmtest.h"
+#include "ioctl_wrappers.h"
+#include "igt_dummyload.h"
+#include "igt_gt.h"
+#include "igt_params.h"
+#include "igt_sysfs.h"
+#include "intel_chipset.h"
+#include "igt_collection.h"
+#include "igt_device.h"
+
+#include "i915/intel_memory_region.h"
+
+#define i915_query_items(fd, items, n_items) do { \
+		igt_assert_eq(__i915_query_items(fd, items, n_items), 0); \
+		errno = 0; \
+	} while (0)
+#define i915_query_items_err(fd, items, n_items, err) do { \
+		igt_assert_eq(__i915_query_items(fd, items, n_items), -err); \
+	} while (0)
+
+static int
+__i915_query(int fd, struct drm_i915_query *q)
+{
+	if (igt_ioctl(fd, DRM_IOCTL_I915_QUERY, q))
+		return -errno;
+	return 0;
+}
+
+static int
+__i915_query_items(int fd, struct drm_i915_query_item *items, uint32_t n_items)
+{
+	struct drm_i915_query q = {
+		.num_items = n_items,
+		.items_ptr = to_user_pointer(items),
+	};
+	return __i915_query(fd, &q);
+}
+
+bool gem_has_query_support(int fd)
+{
+	struct drm_i915_query query = {};
+
+	return __i915_query(fd, &query) == 0;
+}
+
+const char *get_memory_region_name(uint32_t region)
+{
+	uint16_t class = MEMORY_TYPE_FROM_REGION(region);
+
+	switch (class) {
+	case I915_MEMORY_CLASS_SYSTEM:
+		return "smem";
+	case I915_MEMORY_CLASS_DEVICE:
+		return "lmem";
+	}
+	igt_assert_f(false, "Unknown memory region");
+}
+
+/**
+ *  gem_get_batch_size:
+ *  @fd: open i915 drm file descriptor
+ *  @mem_region_type: used memory_region type
+ *
+ *  With introduction of LMEM we observe different page sizes for those two
+ *  memory regions. Without this helper funtion we may be prone to forget
+ *  about setting proper page size.
+ */
+uint32_t gem_get_batch_size(int fd, uint8_t mem_region_type)
+{
+	return (mem_region_type == I915_MEMORY_CLASS_DEVICE) ? 65536 : 4096;
+}
+
+/**
+ * gem_get_query_memory_regions:
+ * @fd: open i915 drm file descriptor
+ *
+ * This function wraps query mechanism for memory regions.
+ *
+ * Returns: Filled struct with available memory regions.
+ */
+struct drm_i915_query_memory_regions *gem_get_query_memory_regions(int fd)
+{
+	struct drm_i915_query_item item;
+	struct drm_i915_query_memory_regions *query_info;
+
+	memset(&item, 0, sizeof(item));
+	item.query_id = DRM_I915_QUERY_MEMORY_REGIONS;
+	i915_query_items(fd, &item, 1);
+
+	query_info = calloc(1, item.length);
+
+	item.data_ptr = to_user_pointer(query_info);
+	i915_query_items(fd, &item, 1);
+
+	return query_info;
+}
+
+/**
+ * gem_get_lmem_region_count:
+ * @fd: open i915 drm file descriptor
+ *
+ * Helper function to check how many lmem regions are available on device.
+ *
+ * Returns: Number of found lmem regions.
+ */
+uint8_t gem_get_lmem_region_count(int fd)
+{
+	struct drm_i915_query_memory_regions *query_info;
+	uint8_t num_regions;
+	uint8_t lmem_regions = 0;
+
+	query_info = gem_get_query_memory_regions(fd);
+	num_regions = query_info->num_regions;
+
+	for (int i = 0; i < num_regions; i++) {
+		if (query_info->regions[i].region.memory_class == I915_MEMORY_CLASS_DEVICE)
+			lmem_regions += 1;
+	}
+
+	return lmem_regions;
+}
+
+/**
+ * gem_has_lmem:
+ * @fd: open i915 drm file descriptor
+ *
+ * Helper function to check if lmem is available on device.
+ *
+ * Returns: True if at least one lmem region was found.
+ */
+bool gem_has_lmem(int fd)
+{
+	return gem_get_lmem_region_count(fd) > 0;
+}
+
+/* A version of gem_create_in_memory_region_list which can be allowed to
+   fail so that the object creation can be retried */
+int __gem_create_in_memory_region_list(int fd, uint32_t *handle, uint64_t size,
+				       struct drm_i915_gem_memory_class_instance *mem_regions,
+				       int num_regions)
+{
+	struct drm_i915_gem_create_ext_memory_regions ext_regions = {
+		.base = { .name = I915_GEM_CREATE_EXT_MEMORY_REGIONS },
+		.num_regions = num_regions,
+		.regions = to_user_pointer(mem_regions),
+	};
+
+	return __gem_create_ext(fd, &size, handle, &ext_regions.base);
+}
+
+/* gem_create_in_memory_region_list:
+ * @fd: opened i915 drm file descriptor
+ * @size: requested size of the buffer
+ * @mem_regions: memory regions array (priority list)
+ * @num_regions: @mem_regions length
+ */
+uint32_t gem_create_in_memory_region_list(int fd, uint64_t size,
+					  struct drm_i915_gem_memory_class_instance *mem_regions,
+					  int num_regions)
+{
+	uint32_t handle;
+	int ret = __gem_create_in_memory_region_list(fd, &handle, size,
+						     mem_regions, num_regions);
+	igt_assert_eq(ret, 0);
+	return handle;
+}
+
+static bool __region_belongs_to_regions_type(struct drm_i915_gem_memory_class_instance region,
+					     uint32_t *mem_regions_type,
+					     int num_regions)
+{
+	for (int i = 0; i < num_regions; i++)
+		if (mem_regions_type[i] == region.memory_class)
+			return true;
+	return false;
+}
+
+struct igt_collection *
+__get_memory_region_set(struct drm_i915_query_memory_regions *regions,
+			uint32_t *mem_regions_type,
+			int num_regions)
+{
+	struct drm_i915_gem_memory_class_instance region;
+	struct igt_collection *set;
+	int count = 0, pos = 0;
+
+	for (int i = 0; i < regions->num_regions; i++) {
+		region = regions->regions[i].region;
+		if (__region_belongs_to_regions_type(region,
+						     mem_regions_type,
+						     num_regions))
+			count++;
+	}
+
+	set = igt_collection_create(count);
+
+	for (int i = 0; i < regions->num_regions; i++) {
+		region = regions->regions[i].region;
+		if (__region_belongs_to_regions_type(region,
+						     mem_regions_type,
+						     num_regions))
+			igt_collection_set_value(set, pos++,
+						 INTEL_MEMORY_REGION_ID(region.memory_class,
+									region.memory_instance));
+	}
+
+	igt_assert(count == pos);
+
+	return set;
+}
+
+/**
+  * memregion_dynamic_subtest_name:
+  * @igt_collection: memory region collection
+  *
+  * Function iterates over all memory regions inside the collection (keeped
+  * in the value field) and generates the name which can be used during dynamic
+  * subtest creation.
+  *
+  * Returns: newly allocated string, has to be freed by caller. Asserts if
+  * caller tries to create a name using empty collection.
+  */
+char *memregion_dynamic_subtest_name(struct igt_collection *set)
+{
+	struct igt_collection_data *data;
+	char *name, *p;
+	uint32_t region, len;
+
+	igt_assert(set && set->size);
+	/* enough for "name%d-" * n */
+	len = set->size * 8;
+	p = name = malloc(len);
+	igt_assert(name);
+
+	for_each_collection_data(data, set) {
+		int r;
+
+		region = data->value;
+		if (IS_DEVICE_MEMORY_REGION(region))
+			r = snprintf(p, len, "%s%d-",
+				     get_memory_region_name(region),
+				     MEMORY_INSTANCE_FROM_REGION(region));
+		else
+			r = snprintf(p, len, "%s-",
+				     get_memory_region_name(region));
+
+		igt_assert(r > 0);
+		p += r;
+		len -= r;
+	}
+
+	/* remove last '-' */
+	*(p - 1) = 0;
+
+	return name;
+}
+
+/**
+ * intel_dump_gpu_meminfo:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ *
+ * Outputs memory regions and their sizes.
+ */
+void intel_dump_gpu_meminfo(struct drm_i915_query_memory_regions *info)
+{
+	int i;
+
+	igt_assert(info);
+
+	igt_info("GPU meminfo:\n");
+
+	for (i = 0; i < info->num_regions; i++) {
+		uint32_t region = INTEL_MEMORY_REGION_ID(info->regions[i].region.memory_class,
+							 info->regions[i].region.memory_instance);
+		const char *name = get_memory_region_name(region);
+
+		igt_info("- %s [%d] memory [size: 0x%llx, available: 0x%llx]\n",
+			 name, info->regions[i].region.memory_instance,
+			 info->regions[i].probed_size,
+			 info->regions[i].unallocated_size);
+	}
+}
+
+/**
+ * gpu_meminfo_region_count:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ *
+ * Returns: number of regions for type @memory_class
+ */
+uint32_t gpu_meminfo_region_count(struct drm_i915_query_memory_regions *info,
+				  uint16_t memory_class)
+{
+	uint32_t num = 0;
+	int i;
+
+	igt_assert(info);
+
+	for (i = 0; i < info->num_regions; i++)
+		if (info->regions[i].region.memory_class == memory_class)
+			num++;
+
+	return num;
+}
+
+/**
+ * gpu_meminfo_region_total_size:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ *
+ * Returns: total size of all regions which are type @memory_class, -1 when the
+ * size of at least one region is unknown
+ */
+uint64_t gpu_meminfo_region_total_size(struct drm_i915_query_memory_regions *info,
+				       uint16_t memory_class)
+{
+	uint64_t total = 0;
+	int i;
+
+	igt_assert(info);
+
+	for (i = 0; i < info->num_regions; i++)
+		if (info->regions[i].region.memory_class == memory_class) {
+			if (info->regions[i].probed_size == -1)
+				return -1;
+
+			total += info->regions[i].probed_size;
+		}
+
+	return total;
+}
+
+/**
+ * gpu_meminfo_region_total_available:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ *
+ * Returns: available size of all regions which are type @memory_class, -1 when
+ * the size of at least one region cannot be estimated
+ */
+uint64_t gpu_meminfo_region_total_available(struct drm_i915_query_memory_regions *info,
+					    uint16_t memory_class)
+{
+	uint64_t avail = 0;
+	int i;
+
+	igt_assert(info);
+
+	for (i = 0; i < info->num_regions; i++)
+		if (info->regions[i].region.memory_class == memory_class) {
+			if (info->regions[i].unallocated_size == -1)
+				return -1;
+
+			avail += info->regions[i].unallocated_size;
+		}
+
+	return avail;
+}
+
+/**
+ * gpu_meminfo_region_size:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ * @memory_instance: memory region instance
+ *
+ * Returns: available size of @memory_instance which type is @memory_class, -1
+ * when the size is unknown
+ */
+uint64_t gpu_meminfo_region_size(struct drm_i915_query_memory_regions *info,
+				 uint16_t memory_class,
+				 uint16_t memory_instance)
+{
+	int i;
+
+	igt_assert(info);
+
+	for (i = 0; i < info->num_regions; i++)
+		if (info->regions[i].region.memory_class == memory_class &&
+		     info->regions[i].region.memory_instance == memory_instance)
+			return info->regions[i].probed_size;
+
+	return 0;
+}
+
+/**
+ * gpu_meminfo_region_available:
+ * @info: pointer to drm_i915_query_memory_regions structure
+ * @memory_class: memory region class
+ * @memory_instance: memory region instance
+ *
+ * Returns: available size of @memory_instance region which type is
+ * @memory_class, -1 when the size cannot be estimated
+ */
+uint64_t gpu_meminfo_region_available(struct drm_i915_query_memory_regions *info,
+				      uint16_t memory_class,
+				      uint16_t memory_instance)
+{
+	int i;
+
+	igt_assert(info);
+
+	for (i = 0; i < info->num_regions; i++)
+		if (info->regions[i].region.memory_class == memory_class &&
+		     info->regions[i].region.memory_instance == memory_instance)
+			return info->regions[i].unallocated_size;
+
+	return 0;
+}
diff --git a/lib/i915/intel_memory_region.h b/lib/i915/intel_memory_region.h
new file mode 100644
index 00000000..e9d1d66d
--- /dev/null
+++ b/lib/i915/intel_memory_region.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2020 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 "igt_collection.h"
+
+#ifndef INTEL_MEMORY_REGION_H
+#define INTEL_MEMORY_REGION_H
+
+#define I915_SYSTEM_MEMORY I915_MEMORY_CLASS_SYSTEM
+#define I915_DEVICE_MEMORY I915_MEMORY_CLASS_DEVICE
+#define I915_STOLEN_SYSTEM_MEMORY I915_MEMORY_CLASS_STOLEN_SYSTEM
+#define I915_STOLEN_DEVICE_MEMORY I915_MEMORY_CLASS_STOLEN_DEVICE
+
+#define INTEL_MEMORY_REGION_ID(type, instance) ((type) << 16u | (instance))
+#define MEMORY_TYPE_FROM_REGION(r) ((r) >> 16u)
+#define MEMORY_INSTANCE_FROM_REGION(r) ((r) & 0xffff)
+
+#define IS_MEMORY_REGION_TYPE(region, type) \
+	(MEMORY_TYPE_FROM_REGION(region) == type)
+
+#define IS_DEVICE_MEMORY_REGION(region) \
+	IS_MEMORY_REGION_TYPE(region, I915_MEMORY_CLASS_DEVICE)
+#define IS_SYSTEM_MEMORY_REGION(region) \
+	IS_MEMORY_REGION_TYPE(region, I915_MEMORY_CLASS_SYSTEM)
+
+#define IS_STOLEN_MEMORY_REGION(region) \
+	(IS_MEMORY_REGION_TYPE(region, I915_MEMORY_CLASS_STOLEN_SYSTEM) || \
+	 IS_MEMORY_REGION_TYPE(region, I915_MEMORY_CLASS_STOLEN_DEVICE))
+
+#define REGION_SMEM    INTEL_MEMORY_REGION_ID(I915_MEMORY_CLASS_SYSTEM, 0)
+#define REGION_LMEM(n) INTEL_MEMORY_REGION_ID(I915_MEMORY_CLASS_DEVICE, (n))
+#define REGION_STLN_SMEM(n) INTEL_MEMORY_REGION_ID(I915_MEMORY_CLASS_STOLEN_SYSTEM, (n))
+#define REGION_STLN_LMEM(n) INTEL_MEMORY_REGION_ID(I915_MEMORY_CLASS_STOLEN_DEVICE, (n))
+
+bool gem_has_query_support(int fd);
+
+const char *get_memory_region_name(uint32_t region);
+uint32_t gem_get_batch_size(int fd, uint8_t mem_region_type);
+
+struct drm_i915_query_memory_regions *gem_get_query_memory_regions(int fd);
+
+uint8_t gem_get_lmem_region_count(int fd);
+
+bool gem_has_lmem(int fd);
+
+int __gem_create_in_memory_region_list(int fd, uint32_t *handle, uint64_t size,
+				       struct drm_i915_gem_memory_class_instance *mem_regions,
+				       int num_regions);
+
+uint32_t gem_create_in_memory_region_list(int fd, uint64_t size,
+					  struct drm_i915_gem_memory_class_instance *mem_regions,
+					  int num_regions);
+
+/*
+ * XXX: the whole converting to class_instance thing is meant as a temporary
+ * stop gap which should keep everything working, such that we don't have to
+ * rewrite the world in one go to fit the new uAPI.
+ */
+#define __gem_create_in_memory_regions(fd, handle, size, regions...) ({ \
+	unsigned int arr__[] = { regions }; \
+	struct drm_i915_gem_memory_class_instance arr_query__[ARRAY_SIZE(arr__)]; \
+	for (int i__  = 0; i__ < ARRAY_SIZE(arr_query__); ++i__) { \
+		arr_query__[i__].memory_class = MEMORY_TYPE_FROM_REGION(arr__[i__]);  \
+		arr_query__[i__].memory_instance = MEMORY_INSTANCE_FROM_REGION(arr__[i__]);  \
+	} \
+	__gem_create_in_memory_region_list(fd, handle, size, arr_query__, ARRAY_SIZE(arr_query__)); \
+})
+#define gem_create_in_memory_regions(fd, size, regions...) ({ \
+	unsigned int arr__[] = { regions }; \
+	struct drm_i915_gem_memory_class_instance arr_query__[ARRAY_SIZE(arr__)]; \
+	for (int i__  = 0; i__ < ARRAY_SIZE(arr_query__); ++i__) { \
+		arr_query__[i__].memory_class = MEMORY_TYPE_FROM_REGION(arr__[i__]);  \
+		arr_query__[i__].memory_instance = MEMORY_INSTANCE_FROM_REGION(arr__[i__]);  \
+	} \
+	gem_create_in_memory_region_list(fd, size, arr_query__, ARRAY_SIZE(arr_query__)); \
+})
+
+struct igt_collection *
+__get_memory_region_set(struct drm_i915_query_memory_regions *regions,
+			uint32_t *mem_regions_type,
+			int num_regions);
+
+/*
+ * Helper macro to create igt_collection which contains all memory regions
+ * which matches mem_region_types array. Useful to filter out stolen memory
+ * from accessible memory regions.
+ */
+#define get_memory_region_set(regions, mem_region_types...) ({ \
+	unsigned int arr__[] = { mem_region_types }; \
+	__get_memory_region_set(regions, arr__, ARRAY_SIZE(arr__)); \
+})
+
+char *memregion_dynamic_subtest_name(struct igt_collection *set);
+
+void intel_dump_gpu_meminfo(struct drm_i915_query_memory_regions *info);
+
+uint32_t gpu_meminfo_region_count(struct drm_i915_query_memory_regions *info,
+				  uint16_t region_class);
+uint64_t gpu_meminfo_region_total_size(struct drm_i915_query_memory_regions *info,
+				       uint16_t region_class);
+uint64_t gpu_meminfo_region_total_available(struct drm_i915_query_memory_regions *info,
+					    uint16_t region_type);
+
+uint64_t gpu_meminfo_region_size(struct drm_i915_query_memory_regions *info,
+				 uint16_t memory_class,
+				 uint16_t memory_instance);
+uint64_t gpu_meminfo_region_available(struct drm_i915_query_memory_regions *info,
+				      uint16_t memory_class,
+				      uint16_t memory_instance);
+
+#endif /* INTEL_MEMORY_REGION_H */
diff --git a/lib/ioctl_wrappers.h b/lib/ioctl_wrappers.h
index 9ea67365..36640e30 100644
--- a/lib/ioctl_wrappers.h
+++ b/lib/ioctl_wrappers.h
@@ -38,6 +38,7 @@
 
 #include "i915/gem_context.h"
 #include "i915/gem_scheduler.h"
+#include "i915/intel_memory_region.h"
 
 /**
  * igt_ioctl:
diff --git a/lib/meson.build b/lib/meson.build
index d5a1c970..078a357b 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -10,6 +10,7 @@ lib_sources = [
 	'i915/gem_ring.c',
 	'i915/gem_mman.c',
 	'i915/gem_vm.c',
+	'i915/intel_memory_region.c',
 	'igt_collection.c',
 	'igt_color_encoding.c',
 	'igt_debugfs.c',
