1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * SPDX-License-Identifier: MIT
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright © 2018 Intel Corporation
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <linux/nospec.h>
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include "i915_drv.h"
10*4882a593Smuzhiyun #include "i915_perf.h"
11*4882a593Smuzhiyun #include "i915_query.h"
12*4882a593Smuzhiyun #include <uapi/drm/i915_drm.h>
13*4882a593Smuzhiyun
copy_query_item(void * query_hdr,size_t query_sz,u32 total_length,struct drm_i915_query_item * query_item)14*4882a593Smuzhiyun static int copy_query_item(void *query_hdr, size_t query_sz,
15*4882a593Smuzhiyun u32 total_length,
16*4882a593Smuzhiyun struct drm_i915_query_item *query_item)
17*4882a593Smuzhiyun {
18*4882a593Smuzhiyun if (query_item->length == 0)
19*4882a593Smuzhiyun return total_length;
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun if (query_item->length < total_length)
22*4882a593Smuzhiyun return -EINVAL;
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun if (copy_from_user(query_hdr, u64_to_user_ptr(query_item->data_ptr),
25*4882a593Smuzhiyun query_sz))
26*4882a593Smuzhiyun return -EFAULT;
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun return 0;
29*4882a593Smuzhiyun }
30*4882a593Smuzhiyun
query_topology_info(struct drm_i915_private * dev_priv,struct drm_i915_query_item * query_item)31*4882a593Smuzhiyun static int query_topology_info(struct drm_i915_private *dev_priv,
32*4882a593Smuzhiyun struct drm_i915_query_item *query_item)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun const struct sseu_dev_info *sseu = &dev_priv->gt.info.sseu;
35*4882a593Smuzhiyun struct drm_i915_query_topology_info topo;
36*4882a593Smuzhiyun u32 slice_length, subslice_length, eu_length, total_length;
37*4882a593Smuzhiyun int ret;
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun if (query_item->flags != 0)
40*4882a593Smuzhiyun return -EINVAL;
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun if (sseu->max_slices == 0)
43*4882a593Smuzhiyun return -ENODEV;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun BUILD_BUG_ON(sizeof(u8) != sizeof(sseu->slice_mask));
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun slice_length = sizeof(sseu->slice_mask);
48*4882a593Smuzhiyun subslice_length = sseu->max_slices * sseu->ss_stride;
49*4882a593Smuzhiyun eu_length = sseu->max_slices * sseu->max_subslices * sseu->eu_stride;
50*4882a593Smuzhiyun total_length = sizeof(topo) + slice_length + subslice_length +
51*4882a593Smuzhiyun eu_length;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun ret = copy_query_item(&topo, sizeof(topo), total_length,
54*4882a593Smuzhiyun query_item);
55*4882a593Smuzhiyun if (ret != 0)
56*4882a593Smuzhiyun return ret;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun if (topo.flags != 0)
59*4882a593Smuzhiyun return -EINVAL;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun memset(&topo, 0, sizeof(topo));
62*4882a593Smuzhiyun topo.max_slices = sseu->max_slices;
63*4882a593Smuzhiyun topo.max_subslices = sseu->max_subslices;
64*4882a593Smuzhiyun topo.max_eus_per_subslice = sseu->max_eus_per_subslice;
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun topo.subslice_offset = slice_length;
67*4882a593Smuzhiyun topo.subslice_stride = sseu->ss_stride;
68*4882a593Smuzhiyun topo.eu_offset = slice_length + subslice_length;
69*4882a593Smuzhiyun topo.eu_stride = sseu->eu_stride;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun if (copy_to_user(u64_to_user_ptr(query_item->data_ptr),
72*4882a593Smuzhiyun &topo, sizeof(topo)))
73*4882a593Smuzhiyun return -EFAULT;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun if (copy_to_user(u64_to_user_ptr(query_item->data_ptr + sizeof(topo)),
76*4882a593Smuzhiyun &sseu->slice_mask, slice_length))
77*4882a593Smuzhiyun return -EFAULT;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun if (copy_to_user(u64_to_user_ptr(query_item->data_ptr +
80*4882a593Smuzhiyun sizeof(topo) + slice_length),
81*4882a593Smuzhiyun sseu->subslice_mask, subslice_length))
82*4882a593Smuzhiyun return -EFAULT;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun if (copy_to_user(u64_to_user_ptr(query_item->data_ptr +
85*4882a593Smuzhiyun sizeof(topo) +
86*4882a593Smuzhiyun slice_length + subslice_length),
87*4882a593Smuzhiyun sseu->eu_mask, eu_length))
88*4882a593Smuzhiyun return -EFAULT;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun return total_length;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun static int
query_engine_info(struct drm_i915_private * i915,struct drm_i915_query_item * query_item)94*4882a593Smuzhiyun query_engine_info(struct drm_i915_private *i915,
95*4882a593Smuzhiyun struct drm_i915_query_item *query_item)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun struct drm_i915_query_engine_info __user *query_ptr =
98*4882a593Smuzhiyun u64_to_user_ptr(query_item->data_ptr);
99*4882a593Smuzhiyun struct drm_i915_engine_info __user *info_ptr;
100*4882a593Smuzhiyun struct drm_i915_query_engine_info query;
101*4882a593Smuzhiyun struct drm_i915_engine_info info = { };
102*4882a593Smuzhiyun unsigned int num_uabi_engines = 0;
103*4882a593Smuzhiyun struct intel_engine_cs *engine;
104*4882a593Smuzhiyun int len, ret;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun if (query_item->flags)
107*4882a593Smuzhiyun return -EINVAL;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun for_each_uabi_engine(engine, i915)
110*4882a593Smuzhiyun num_uabi_engines++;
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun len = struct_size(query_ptr, engines, num_uabi_engines);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun ret = copy_query_item(&query, sizeof(query), len, query_item);
115*4882a593Smuzhiyun if (ret != 0)
116*4882a593Smuzhiyun return ret;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun if (query.num_engines || query.rsvd[0] || query.rsvd[1] ||
119*4882a593Smuzhiyun query.rsvd[2])
120*4882a593Smuzhiyun return -EINVAL;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun info_ptr = &query_ptr->engines[0];
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun for_each_uabi_engine(engine, i915) {
125*4882a593Smuzhiyun info.engine.engine_class = engine->uabi_class;
126*4882a593Smuzhiyun info.engine.engine_instance = engine->uabi_instance;
127*4882a593Smuzhiyun info.capabilities = engine->uabi_capabilities;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun if (copy_to_user(info_ptr, &info, sizeof(info)))
130*4882a593Smuzhiyun return -EFAULT;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun query.num_engines++;
133*4882a593Smuzhiyun info_ptr++;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun if (copy_to_user(query_ptr, &query, sizeof(query)))
137*4882a593Smuzhiyun return -EFAULT;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun return len;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
can_copy_perf_config_registers_or_number(u32 user_n_regs,u64 user_regs_ptr,u32 kernel_n_regs)142*4882a593Smuzhiyun static int can_copy_perf_config_registers_or_number(u32 user_n_regs,
143*4882a593Smuzhiyun u64 user_regs_ptr,
144*4882a593Smuzhiyun u32 kernel_n_regs)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun /*
147*4882a593Smuzhiyun * We'll just put the number of registers, and won't copy the
148*4882a593Smuzhiyun * register.
149*4882a593Smuzhiyun */
150*4882a593Smuzhiyun if (user_n_regs == 0)
151*4882a593Smuzhiyun return 0;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun if (user_n_regs < kernel_n_regs)
154*4882a593Smuzhiyun return -EINVAL;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun return 0;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
copy_perf_config_registers_or_number(const struct i915_oa_reg * kernel_regs,u32 kernel_n_regs,u64 user_regs_ptr,u32 * user_n_regs)159*4882a593Smuzhiyun static int copy_perf_config_registers_or_number(const struct i915_oa_reg *kernel_regs,
160*4882a593Smuzhiyun u32 kernel_n_regs,
161*4882a593Smuzhiyun u64 user_regs_ptr,
162*4882a593Smuzhiyun u32 *user_n_regs)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun u32 __user *p = u64_to_user_ptr(user_regs_ptr);
165*4882a593Smuzhiyun u32 r;
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun if (*user_n_regs == 0) {
168*4882a593Smuzhiyun *user_n_regs = kernel_n_regs;
169*4882a593Smuzhiyun return 0;
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun *user_n_regs = kernel_n_regs;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun if (!user_write_access_begin(p, 2 * sizeof(u32) * kernel_n_regs))
175*4882a593Smuzhiyun return -EFAULT;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun for (r = 0; r < kernel_n_regs; r++, p += 2) {
178*4882a593Smuzhiyun unsafe_put_user(i915_mmio_reg_offset(kernel_regs[r].addr),
179*4882a593Smuzhiyun p, Efault);
180*4882a593Smuzhiyun unsafe_put_user(kernel_regs[r].value, p + 1, Efault);
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun user_write_access_end();
183*4882a593Smuzhiyun return 0;
184*4882a593Smuzhiyun Efault:
185*4882a593Smuzhiyun user_write_access_end();
186*4882a593Smuzhiyun return -EFAULT;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
query_perf_config_data(struct drm_i915_private * i915,struct drm_i915_query_item * query_item,bool use_uuid)189*4882a593Smuzhiyun static int query_perf_config_data(struct drm_i915_private *i915,
190*4882a593Smuzhiyun struct drm_i915_query_item *query_item,
191*4882a593Smuzhiyun bool use_uuid)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun struct drm_i915_query_perf_config __user *user_query_config_ptr =
194*4882a593Smuzhiyun u64_to_user_ptr(query_item->data_ptr);
195*4882a593Smuzhiyun struct drm_i915_perf_oa_config __user *user_config_ptr =
196*4882a593Smuzhiyun u64_to_user_ptr(query_item->data_ptr +
197*4882a593Smuzhiyun sizeof(struct drm_i915_query_perf_config));
198*4882a593Smuzhiyun struct drm_i915_perf_oa_config user_config;
199*4882a593Smuzhiyun struct i915_perf *perf = &i915->perf;
200*4882a593Smuzhiyun struct i915_oa_config *oa_config;
201*4882a593Smuzhiyun char uuid[UUID_STRING_LEN + 1];
202*4882a593Smuzhiyun u64 config_id;
203*4882a593Smuzhiyun u32 flags, total_size;
204*4882a593Smuzhiyun int ret;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if (!perf->i915)
207*4882a593Smuzhiyun return -ENODEV;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun total_size =
210*4882a593Smuzhiyun sizeof(struct drm_i915_query_perf_config) +
211*4882a593Smuzhiyun sizeof(struct drm_i915_perf_oa_config);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun if (query_item->length == 0)
214*4882a593Smuzhiyun return total_size;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun if (query_item->length < total_size) {
217*4882a593Smuzhiyun DRM_DEBUG("Invalid query config data item size=%u expected=%u\n",
218*4882a593Smuzhiyun query_item->length, total_size);
219*4882a593Smuzhiyun return -EINVAL;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun if (get_user(flags, &user_query_config_ptr->flags))
223*4882a593Smuzhiyun return -EFAULT;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun if (flags != 0)
226*4882a593Smuzhiyun return -EINVAL;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun if (use_uuid) {
229*4882a593Smuzhiyun struct i915_oa_config *tmp;
230*4882a593Smuzhiyun int id;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun BUILD_BUG_ON(sizeof(user_query_config_ptr->uuid) >= sizeof(uuid));
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun memset(&uuid, 0, sizeof(uuid));
235*4882a593Smuzhiyun if (copy_from_user(uuid, user_query_config_ptr->uuid,
236*4882a593Smuzhiyun sizeof(user_query_config_ptr->uuid)))
237*4882a593Smuzhiyun return -EFAULT;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun oa_config = NULL;
240*4882a593Smuzhiyun rcu_read_lock();
241*4882a593Smuzhiyun idr_for_each_entry(&perf->metrics_idr, tmp, id) {
242*4882a593Smuzhiyun if (!strcmp(tmp->uuid, uuid)) {
243*4882a593Smuzhiyun oa_config = i915_oa_config_get(tmp);
244*4882a593Smuzhiyun break;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun rcu_read_unlock();
248*4882a593Smuzhiyun } else {
249*4882a593Smuzhiyun if (get_user(config_id, &user_query_config_ptr->config))
250*4882a593Smuzhiyun return -EFAULT;
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun oa_config = i915_perf_get_oa_config(perf, config_id);
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun if (!oa_config)
255*4882a593Smuzhiyun return -ENOENT;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun if (copy_from_user(&user_config, user_config_ptr, sizeof(user_config))) {
258*4882a593Smuzhiyun ret = -EFAULT;
259*4882a593Smuzhiyun goto out;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun ret = can_copy_perf_config_registers_or_number(user_config.n_boolean_regs,
263*4882a593Smuzhiyun user_config.boolean_regs_ptr,
264*4882a593Smuzhiyun oa_config->b_counter_regs_len);
265*4882a593Smuzhiyun if (ret)
266*4882a593Smuzhiyun goto out;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun ret = can_copy_perf_config_registers_or_number(user_config.n_flex_regs,
269*4882a593Smuzhiyun user_config.flex_regs_ptr,
270*4882a593Smuzhiyun oa_config->flex_regs_len);
271*4882a593Smuzhiyun if (ret)
272*4882a593Smuzhiyun goto out;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun ret = can_copy_perf_config_registers_or_number(user_config.n_mux_regs,
275*4882a593Smuzhiyun user_config.mux_regs_ptr,
276*4882a593Smuzhiyun oa_config->mux_regs_len);
277*4882a593Smuzhiyun if (ret)
278*4882a593Smuzhiyun goto out;
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun ret = copy_perf_config_registers_or_number(oa_config->b_counter_regs,
281*4882a593Smuzhiyun oa_config->b_counter_regs_len,
282*4882a593Smuzhiyun user_config.boolean_regs_ptr,
283*4882a593Smuzhiyun &user_config.n_boolean_regs);
284*4882a593Smuzhiyun if (ret)
285*4882a593Smuzhiyun goto out;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun ret = copy_perf_config_registers_or_number(oa_config->flex_regs,
288*4882a593Smuzhiyun oa_config->flex_regs_len,
289*4882a593Smuzhiyun user_config.flex_regs_ptr,
290*4882a593Smuzhiyun &user_config.n_flex_regs);
291*4882a593Smuzhiyun if (ret)
292*4882a593Smuzhiyun goto out;
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun ret = copy_perf_config_registers_or_number(oa_config->mux_regs,
295*4882a593Smuzhiyun oa_config->mux_regs_len,
296*4882a593Smuzhiyun user_config.mux_regs_ptr,
297*4882a593Smuzhiyun &user_config.n_mux_regs);
298*4882a593Smuzhiyun if (ret)
299*4882a593Smuzhiyun goto out;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun memcpy(user_config.uuid, oa_config->uuid, sizeof(user_config.uuid));
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun if (copy_to_user(user_config_ptr, &user_config, sizeof(user_config))) {
304*4882a593Smuzhiyun ret = -EFAULT;
305*4882a593Smuzhiyun goto out;
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun ret = total_size;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun out:
311*4882a593Smuzhiyun i915_oa_config_put(oa_config);
312*4882a593Smuzhiyun return ret;
313*4882a593Smuzhiyun }
314*4882a593Smuzhiyun
sizeof_perf_config_list(size_t count)315*4882a593Smuzhiyun static size_t sizeof_perf_config_list(size_t count)
316*4882a593Smuzhiyun {
317*4882a593Smuzhiyun return sizeof(struct drm_i915_query_perf_config) + sizeof(u64) * count;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun
sizeof_perf_metrics(struct i915_perf * perf)320*4882a593Smuzhiyun static size_t sizeof_perf_metrics(struct i915_perf *perf)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun struct i915_oa_config *tmp;
323*4882a593Smuzhiyun size_t i;
324*4882a593Smuzhiyun int id;
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun i = 1;
327*4882a593Smuzhiyun rcu_read_lock();
328*4882a593Smuzhiyun idr_for_each_entry(&perf->metrics_idr, tmp, id)
329*4882a593Smuzhiyun i++;
330*4882a593Smuzhiyun rcu_read_unlock();
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun return sizeof_perf_config_list(i);
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
query_perf_config_list(struct drm_i915_private * i915,struct drm_i915_query_item * query_item)335*4882a593Smuzhiyun static int query_perf_config_list(struct drm_i915_private *i915,
336*4882a593Smuzhiyun struct drm_i915_query_item *query_item)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun struct drm_i915_query_perf_config __user *user_query_config_ptr =
339*4882a593Smuzhiyun u64_to_user_ptr(query_item->data_ptr);
340*4882a593Smuzhiyun struct i915_perf *perf = &i915->perf;
341*4882a593Smuzhiyun u64 *oa_config_ids = NULL;
342*4882a593Smuzhiyun int alloc, n_configs;
343*4882a593Smuzhiyun u32 flags;
344*4882a593Smuzhiyun int ret;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun if (!perf->i915)
347*4882a593Smuzhiyun return -ENODEV;
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun if (query_item->length == 0)
350*4882a593Smuzhiyun return sizeof_perf_metrics(perf);
351*4882a593Smuzhiyun
352*4882a593Smuzhiyun if (get_user(flags, &user_query_config_ptr->flags))
353*4882a593Smuzhiyun return -EFAULT;
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun if (flags != 0)
356*4882a593Smuzhiyun return -EINVAL;
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun n_configs = 1;
359*4882a593Smuzhiyun do {
360*4882a593Smuzhiyun struct i915_oa_config *tmp;
361*4882a593Smuzhiyun u64 *ids;
362*4882a593Smuzhiyun int id;
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun ids = krealloc(oa_config_ids,
365*4882a593Smuzhiyun n_configs * sizeof(*oa_config_ids),
366*4882a593Smuzhiyun GFP_KERNEL);
367*4882a593Smuzhiyun if (!ids)
368*4882a593Smuzhiyun return -ENOMEM;
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun alloc = fetch_and_zero(&n_configs);
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun ids[n_configs++] = 1ull; /* reserved for test_config */
373*4882a593Smuzhiyun rcu_read_lock();
374*4882a593Smuzhiyun idr_for_each_entry(&perf->metrics_idr, tmp, id) {
375*4882a593Smuzhiyun if (n_configs < alloc)
376*4882a593Smuzhiyun ids[n_configs] = id;
377*4882a593Smuzhiyun n_configs++;
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun rcu_read_unlock();
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun oa_config_ids = ids;
382*4882a593Smuzhiyun } while (n_configs > alloc);
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun if (query_item->length < sizeof_perf_config_list(n_configs)) {
385*4882a593Smuzhiyun DRM_DEBUG("Invalid query config list item size=%u expected=%zu\n",
386*4882a593Smuzhiyun query_item->length,
387*4882a593Smuzhiyun sizeof_perf_config_list(n_configs));
388*4882a593Smuzhiyun kfree(oa_config_ids);
389*4882a593Smuzhiyun return -EINVAL;
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun if (put_user(n_configs, &user_query_config_ptr->config)) {
393*4882a593Smuzhiyun kfree(oa_config_ids);
394*4882a593Smuzhiyun return -EFAULT;
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun ret = copy_to_user(user_query_config_ptr + 1,
398*4882a593Smuzhiyun oa_config_ids,
399*4882a593Smuzhiyun n_configs * sizeof(*oa_config_ids));
400*4882a593Smuzhiyun kfree(oa_config_ids);
401*4882a593Smuzhiyun if (ret)
402*4882a593Smuzhiyun return -EFAULT;
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun return sizeof_perf_config_list(n_configs);
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun
query_perf_config(struct drm_i915_private * i915,struct drm_i915_query_item * query_item)407*4882a593Smuzhiyun static int query_perf_config(struct drm_i915_private *i915,
408*4882a593Smuzhiyun struct drm_i915_query_item *query_item)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun switch (query_item->flags) {
411*4882a593Smuzhiyun case DRM_I915_QUERY_PERF_CONFIG_LIST:
412*4882a593Smuzhiyun return query_perf_config_list(i915, query_item);
413*4882a593Smuzhiyun case DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_UUID:
414*4882a593Smuzhiyun return query_perf_config_data(i915, query_item, true);
415*4882a593Smuzhiyun case DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_ID:
416*4882a593Smuzhiyun return query_perf_config_data(i915, query_item, false);
417*4882a593Smuzhiyun default:
418*4882a593Smuzhiyun return -EINVAL;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun static int (* const i915_query_funcs[])(struct drm_i915_private *dev_priv,
423*4882a593Smuzhiyun struct drm_i915_query_item *query_item) = {
424*4882a593Smuzhiyun query_topology_info,
425*4882a593Smuzhiyun query_engine_info,
426*4882a593Smuzhiyun query_perf_config,
427*4882a593Smuzhiyun };
428*4882a593Smuzhiyun
i915_query_ioctl(struct drm_device * dev,void * data,struct drm_file * file)429*4882a593Smuzhiyun int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun struct drm_i915_private *dev_priv = to_i915(dev);
432*4882a593Smuzhiyun struct drm_i915_query *args = data;
433*4882a593Smuzhiyun struct drm_i915_query_item __user *user_item_ptr =
434*4882a593Smuzhiyun u64_to_user_ptr(args->items_ptr);
435*4882a593Smuzhiyun u32 i;
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun if (args->flags != 0)
438*4882a593Smuzhiyun return -EINVAL;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun for (i = 0; i < args->num_items; i++, user_item_ptr++) {
441*4882a593Smuzhiyun struct drm_i915_query_item item;
442*4882a593Smuzhiyun unsigned long func_idx;
443*4882a593Smuzhiyun int ret;
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun if (copy_from_user(&item, user_item_ptr, sizeof(item)))
446*4882a593Smuzhiyun return -EFAULT;
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun if (item.query_id == 0)
449*4882a593Smuzhiyun return -EINVAL;
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun if (overflows_type(item.query_id - 1, unsigned long))
452*4882a593Smuzhiyun return -EINVAL;
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun func_idx = item.query_id - 1;
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun ret = -EINVAL;
457*4882a593Smuzhiyun if (func_idx < ARRAY_SIZE(i915_query_funcs)) {
458*4882a593Smuzhiyun func_idx = array_index_nospec(func_idx,
459*4882a593Smuzhiyun ARRAY_SIZE(i915_query_funcs));
460*4882a593Smuzhiyun ret = i915_query_funcs[func_idx](dev_priv, &item);
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun /* Only write the length back to userspace if they differ. */
464*4882a593Smuzhiyun if (ret != item.length && put_user(ret, &user_item_ptr->length))
465*4882a593Smuzhiyun return -EFAULT;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun return 0;
469*4882a593Smuzhiyun }
470