xref: /openbmc/linux/drivers/gpu/drm/i915/i915_query.c (revision 31af04cd)
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 
13 static int query_topology_info(struct drm_i915_private *dev_priv,
14 			       struct drm_i915_query_item *query_item)
15 {
16 	const struct sseu_dev_info *sseu = &INTEL_INFO(dev_priv)->sseu;
17 	struct drm_i915_query_topology_info topo;
18 	u32 slice_length, subslice_length, eu_length, total_length;
19 
20 	if (query_item->flags != 0)
21 		return -EINVAL;
22 
23 	if (sseu->max_slices == 0)
24 		return -ENODEV;
25 
26 	BUILD_BUG_ON(sizeof(u8) != sizeof(sseu->slice_mask));
27 
28 	slice_length = sizeof(sseu->slice_mask);
29 	subslice_length = sseu->max_slices *
30 		DIV_ROUND_UP(sseu->max_subslices, BITS_PER_BYTE);
31 	eu_length = sseu->max_slices * sseu->max_subslices *
32 		DIV_ROUND_UP(sseu->max_eus_per_subslice, BITS_PER_BYTE);
33 
34 	total_length = sizeof(topo) + slice_length + subslice_length + eu_length;
35 
36 	if (query_item->length == 0)
37 		return total_length;
38 
39 	if (query_item->length < total_length)
40 		return -EINVAL;
41 
42 	if (copy_from_user(&topo, u64_to_user_ptr(query_item->data_ptr),
43 			   sizeof(topo)))
44 		return -EFAULT;
45 
46 	if (topo.flags != 0)
47 		return -EINVAL;
48 
49 	if (!access_ok(u64_to_user_ptr(query_item->data_ptr),
50 		       total_length))
51 		return -EFAULT;
52 
53 	memset(&topo, 0, sizeof(topo));
54 	topo.max_slices = sseu->max_slices;
55 	topo.max_subslices = sseu->max_subslices;
56 	topo.max_eus_per_subslice = sseu->max_eus_per_subslice;
57 
58 	topo.subslice_offset = slice_length;
59 	topo.subslice_stride = DIV_ROUND_UP(sseu->max_subslices, BITS_PER_BYTE);
60 	topo.eu_offset = slice_length + subslice_length;
61 	topo.eu_stride =
62 		DIV_ROUND_UP(sseu->max_eus_per_subslice, BITS_PER_BYTE);
63 
64 	if (__copy_to_user(u64_to_user_ptr(query_item->data_ptr),
65 			   &topo, sizeof(topo)))
66 		return -EFAULT;
67 
68 	if (__copy_to_user(u64_to_user_ptr(query_item->data_ptr + sizeof(topo)),
69 			   &sseu->slice_mask, slice_length))
70 		return -EFAULT;
71 
72 	if (__copy_to_user(u64_to_user_ptr(query_item->data_ptr +
73 					   sizeof(topo) + slice_length),
74 			   sseu->subslice_mask, subslice_length))
75 		return -EFAULT;
76 
77 	if (__copy_to_user(u64_to_user_ptr(query_item->data_ptr +
78 					   sizeof(topo) +
79 					   slice_length + subslice_length),
80 			   sseu->eu_mask, eu_length))
81 		return -EFAULT;
82 
83 	return total_length;
84 }
85 
86 static int (* const i915_query_funcs[])(struct drm_i915_private *dev_priv,
87 					struct drm_i915_query_item *query_item) = {
88 	query_topology_info,
89 };
90 
91 int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
92 {
93 	struct drm_i915_private *dev_priv = to_i915(dev);
94 	struct drm_i915_query *args = data;
95 	struct drm_i915_query_item __user *user_item_ptr =
96 		u64_to_user_ptr(args->items_ptr);
97 	u32 i;
98 
99 	if (args->flags != 0)
100 		return -EINVAL;
101 
102 	for (i = 0; i < args->num_items; i++, user_item_ptr++) {
103 		struct drm_i915_query_item item;
104 		unsigned long func_idx;
105 		int ret;
106 
107 		if (copy_from_user(&item, user_item_ptr, sizeof(item)))
108 			return -EFAULT;
109 
110 		if (item.query_id == 0)
111 			return -EINVAL;
112 
113 		if (overflows_type(item.query_id - 1, unsigned long))
114 			return -EINVAL;
115 
116 		func_idx = item.query_id - 1;
117 
118 		ret = -EINVAL;
119 		if (func_idx < ARRAY_SIZE(i915_query_funcs)) {
120 			func_idx = array_index_nospec(func_idx,
121 						      ARRAY_SIZE(i915_query_funcs));
122 			ret = i915_query_funcs[func_idx](dev_priv, &item);
123 		}
124 
125 		/* Only write the length back to userspace if they differ. */
126 		if (ret != item.length && put_user(ret, &user_item_ptr->length))
127 			return -EFAULT;
128 	}
129 
130 	return 0;
131 }
132