Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1/*
  2 * SPDX-License-Identifier: MIT
  3 *
  4 * Copyright © 2018 Intel Corporation
  5 */
  6
  7#include <linux/nospec.h>
  8
  9#include "i915_drv.h"
 10#include "i915_query.h"
 11#include <uapi/drm/i915_drm.h>
 12
 13static int copy_query_item(void *query_hdr, size_t query_sz,
 14			   u32 total_length,
 15			   struct drm_i915_query_item *query_item)
 16{
 17	if (query_item->length == 0)
 18		return total_length;
 19
 20	if (query_item->length < total_length)
 21		return -EINVAL;
 22
 23	if (copy_from_user(query_hdr, u64_to_user_ptr(query_item->data_ptr),
 24			   query_sz))
 25		return -EFAULT;
 26
 27	if (!access_ok(u64_to_user_ptr(query_item->data_ptr),
 28		       total_length))
 29		return -EFAULT;
 30
 31	return 0;
 32}
 33
 34static int query_topology_info(struct drm_i915_private *dev_priv,
 35			       struct drm_i915_query_item *query_item)
 36{
 37	const struct sseu_dev_info *sseu = &RUNTIME_INFO(dev_priv)->sseu;
 38	struct drm_i915_query_topology_info topo;
 39	u32 slice_length, subslice_length, eu_length, total_length;
 40	u8 subslice_stride = GEN_SSEU_STRIDE(sseu->max_subslices);
 41	u8 eu_stride = GEN_SSEU_STRIDE(sseu->max_eus_per_subslice);
 42	int ret;
 43
 44	if (query_item->flags != 0)
 45		return -EINVAL;
 46
 47	if (sseu->max_slices == 0)
 48		return -ENODEV;
 49
 50	BUILD_BUG_ON(sizeof(u8) != sizeof(sseu->slice_mask));
 51
 52	slice_length = sizeof(sseu->slice_mask);
 53	subslice_length = sseu->max_slices * subslice_stride;
 54	eu_length = sseu->max_slices * sseu->max_subslices * eu_stride;
 55	total_length = sizeof(topo) + slice_length + subslice_length +
 56		       eu_length;
 57
 58	ret = copy_query_item(&topo, sizeof(topo), total_length,
 59			      query_item);
 60	if (ret != 0)
 61		return ret;
 62
 63	if (topo.flags != 0)
 64		return -EINVAL;
 65
 66	memset(&topo, 0, sizeof(topo));
 67	topo.max_slices = sseu->max_slices;
 68	topo.max_subslices = sseu->max_subslices;
 69	topo.max_eus_per_subslice = sseu->max_eus_per_subslice;
 70
 71	topo.subslice_offset = slice_length;
 72	topo.subslice_stride = subslice_stride;
 73	topo.eu_offset = slice_length + subslice_length;
 74	topo.eu_stride = eu_stride;
 75
 76	if (__copy_to_user(u64_to_user_ptr(query_item->data_ptr),
 77			   &topo, sizeof(topo)))
 78		return -EFAULT;
 79
 80	if (__copy_to_user(u64_to_user_ptr(query_item->data_ptr + sizeof(topo)),
 81			   &sseu->slice_mask, slice_length))
 82		return -EFAULT;
 83
 84	if (__copy_to_user(u64_to_user_ptr(query_item->data_ptr +
 85					   sizeof(topo) + slice_length),
 86			   sseu->subslice_mask, subslice_length))
 87		return -EFAULT;
 88
 89	if (__copy_to_user(u64_to_user_ptr(query_item->data_ptr +
 90					   sizeof(topo) +
 91					   slice_length + subslice_length),
 92			   sseu->eu_mask, eu_length))
 93		return -EFAULT;
 94
 95	return total_length;
 96}
 97
 98static int
 99query_engine_info(struct drm_i915_private *i915,
100		  struct drm_i915_query_item *query_item)
101{
102	struct drm_i915_query_engine_info __user *query_ptr =
103				u64_to_user_ptr(query_item->data_ptr);
104	struct drm_i915_engine_info __user *info_ptr;
105	struct drm_i915_query_engine_info query;
106	struct drm_i915_engine_info info = { };
107	struct intel_engine_cs *engine;
108	int len, ret;
109
110	if (query_item->flags)
111		return -EINVAL;
112
113	len = sizeof(struct drm_i915_query_engine_info) +
114	      RUNTIME_INFO(i915)->num_engines *
115	      sizeof(struct drm_i915_engine_info);
116
117	ret = copy_query_item(&query, sizeof(query), len, query_item);
118	if (ret != 0)
119		return ret;
120
121	if (query.num_engines || query.rsvd[0] || query.rsvd[1] ||
122	    query.rsvd[2])
123		return -EINVAL;
124
125	info_ptr = &query_ptr->engines[0];
126
127	for_each_uabi_engine(engine, i915) {
128		info.engine.engine_class = engine->uabi_class;
129		info.engine.engine_instance = engine->uabi_instance;
130		info.capabilities = engine->uabi_capabilities;
131
132		if (__copy_to_user(info_ptr, &info, sizeof(info)))
133			return -EFAULT;
134
135		query.num_engines++;
136		info_ptr++;
137	}
138
139	if (__copy_to_user(query_ptr, &query, sizeof(query)))
140		return -EFAULT;
141
142	return len;
143}
144
145static int (* const i915_query_funcs[])(struct drm_i915_private *dev_priv,
146					struct drm_i915_query_item *query_item) = {
147	query_topology_info,
148	query_engine_info,
149};
150
151int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
152{
153	struct drm_i915_private *dev_priv = to_i915(dev);
154	struct drm_i915_query *args = data;
155	struct drm_i915_query_item __user *user_item_ptr =
156		u64_to_user_ptr(args->items_ptr);
157	u32 i;
158
159	if (args->flags != 0)
160		return -EINVAL;
161
162	for (i = 0; i < args->num_items; i++, user_item_ptr++) {
163		struct drm_i915_query_item item;
164		unsigned long func_idx;
165		int ret;
166
167		if (copy_from_user(&item, user_item_ptr, sizeof(item)))
168			return -EFAULT;
169
170		if (item.query_id == 0)
171			return -EINVAL;
172
173		if (overflows_type(item.query_id - 1, unsigned long))
174			return -EINVAL;
175
176		func_idx = item.query_id - 1;
177
178		ret = -EINVAL;
179		if (func_idx < ARRAY_SIZE(i915_query_funcs)) {
180			func_idx = array_index_nospec(func_idx,
181						      ARRAY_SIZE(i915_query_funcs));
182			ret = i915_query_funcs[func_idx](dev_priv, &item);
183		}
184
185		/* Only write the length back to userspace if they differ. */
186		if (ret != item.length && put_user(ret, &user_item_ptr->length))
187			return -EFAULT;
188	}
189
190	return 0;
191}