18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * SPDX-License-Identifier: MIT 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright © 2018 Intel Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/nospec.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include "i915_drv.h" 108c2ecf20Sopenharmony_ci#include "i915_perf.h" 118c2ecf20Sopenharmony_ci#include "i915_query.h" 128c2ecf20Sopenharmony_ci#include <uapi/drm/i915_drm.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic int copy_query_item(void *query_hdr, size_t query_sz, 158c2ecf20Sopenharmony_ci u32 total_length, 168c2ecf20Sopenharmony_ci struct drm_i915_query_item *query_item) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci if (query_item->length == 0) 198c2ecf20Sopenharmony_ci return total_length; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci if (query_item->length < total_length) 228c2ecf20Sopenharmony_ci return -EINVAL; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci if (copy_from_user(query_hdr, u64_to_user_ptr(query_item->data_ptr), 258c2ecf20Sopenharmony_ci query_sz)) 268c2ecf20Sopenharmony_ci return -EFAULT; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci return 0; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int query_topology_info(struct drm_i915_private *dev_priv, 328c2ecf20Sopenharmony_ci struct drm_i915_query_item *query_item) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci const struct sseu_dev_info *sseu = &dev_priv->gt.info.sseu; 358c2ecf20Sopenharmony_ci struct drm_i915_query_topology_info topo; 368c2ecf20Sopenharmony_ci u32 slice_length, subslice_length, eu_length, total_length; 378c2ecf20Sopenharmony_ci int ret; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (query_item->flags != 0) 408c2ecf20Sopenharmony_ci return -EINVAL; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (sseu->max_slices == 0) 438c2ecf20Sopenharmony_ci return -ENODEV; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(u8) != sizeof(sseu->slice_mask)); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci slice_length = sizeof(sseu->slice_mask); 488c2ecf20Sopenharmony_ci subslice_length = sseu->max_slices * sseu->ss_stride; 498c2ecf20Sopenharmony_ci eu_length = sseu->max_slices * sseu->max_subslices * sseu->eu_stride; 508c2ecf20Sopenharmony_ci total_length = sizeof(topo) + slice_length + subslice_length + 518c2ecf20Sopenharmony_ci eu_length; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci ret = copy_query_item(&topo, sizeof(topo), total_length, 548c2ecf20Sopenharmony_ci query_item); 558c2ecf20Sopenharmony_ci if (ret != 0) 568c2ecf20Sopenharmony_ci return ret; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (topo.flags != 0) 598c2ecf20Sopenharmony_ci return -EINVAL; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci memset(&topo, 0, sizeof(topo)); 628c2ecf20Sopenharmony_ci topo.max_slices = sseu->max_slices; 638c2ecf20Sopenharmony_ci topo.max_subslices = sseu->max_subslices; 648c2ecf20Sopenharmony_ci topo.max_eus_per_subslice = sseu->max_eus_per_subslice; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci topo.subslice_offset = slice_length; 678c2ecf20Sopenharmony_ci topo.subslice_stride = sseu->ss_stride; 688c2ecf20Sopenharmony_ci topo.eu_offset = slice_length + subslice_length; 698c2ecf20Sopenharmony_ci topo.eu_stride = sseu->eu_stride; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(query_item->data_ptr), 728c2ecf20Sopenharmony_ci &topo, sizeof(topo))) 738c2ecf20Sopenharmony_ci return -EFAULT; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(query_item->data_ptr + sizeof(topo)), 768c2ecf20Sopenharmony_ci &sseu->slice_mask, slice_length)) 778c2ecf20Sopenharmony_ci return -EFAULT; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(query_item->data_ptr + 808c2ecf20Sopenharmony_ci sizeof(topo) + slice_length), 818c2ecf20Sopenharmony_ci sseu->subslice_mask, subslice_length)) 828c2ecf20Sopenharmony_ci return -EFAULT; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (copy_to_user(u64_to_user_ptr(query_item->data_ptr + 858c2ecf20Sopenharmony_ci sizeof(topo) + 868c2ecf20Sopenharmony_ci slice_length + subslice_length), 878c2ecf20Sopenharmony_ci sseu->eu_mask, eu_length)) 888c2ecf20Sopenharmony_ci return -EFAULT; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return total_length; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int 948c2ecf20Sopenharmony_ciquery_engine_info(struct drm_i915_private *i915, 958c2ecf20Sopenharmony_ci struct drm_i915_query_item *query_item) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct drm_i915_query_engine_info __user *query_ptr = 988c2ecf20Sopenharmony_ci u64_to_user_ptr(query_item->data_ptr); 998c2ecf20Sopenharmony_ci struct drm_i915_engine_info __user *info_ptr; 1008c2ecf20Sopenharmony_ci struct drm_i915_query_engine_info query; 1018c2ecf20Sopenharmony_ci struct drm_i915_engine_info info = { }; 1028c2ecf20Sopenharmony_ci unsigned int num_uabi_engines = 0; 1038c2ecf20Sopenharmony_ci struct intel_engine_cs *engine; 1048c2ecf20Sopenharmony_ci int len, ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (query_item->flags) 1078c2ecf20Sopenharmony_ci return -EINVAL; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for_each_uabi_engine(engine, i915) 1108c2ecf20Sopenharmony_ci num_uabi_engines++; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci len = struct_size(query_ptr, engines, num_uabi_engines); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ret = copy_query_item(&query, sizeof(query), len, query_item); 1158c2ecf20Sopenharmony_ci if (ret != 0) 1168c2ecf20Sopenharmony_ci return ret; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (query.num_engines || query.rsvd[0] || query.rsvd[1] || 1198c2ecf20Sopenharmony_ci query.rsvd[2]) 1208c2ecf20Sopenharmony_ci return -EINVAL; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci info_ptr = &query_ptr->engines[0]; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci for_each_uabi_engine(engine, i915) { 1258c2ecf20Sopenharmony_ci info.engine.engine_class = engine->uabi_class; 1268c2ecf20Sopenharmony_ci info.engine.engine_instance = engine->uabi_instance; 1278c2ecf20Sopenharmony_ci info.capabilities = engine->uabi_capabilities; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (copy_to_user(info_ptr, &info, sizeof(info))) 1308c2ecf20Sopenharmony_ci return -EFAULT; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci query.num_engines++; 1338c2ecf20Sopenharmony_ci info_ptr++; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci if (copy_to_user(query_ptr, &query, sizeof(query))) 1378c2ecf20Sopenharmony_ci return -EFAULT; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return len; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int can_copy_perf_config_registers_or_number(u32 user_n_regs, 1438c2ecf20Sopenharmony_ci u64 user_regs_ptr, 1448c2ecf20Sopenharmony_ci u32 kernel_n_regs) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci /* 1478c2ecf20Sopenharmony_ci * We'll just put the number of registers, and won't copy the 1488c2ecf20Sopenharmony_ci * register. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci if (user_n_regs == 0) 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (user_n_regs < kernel_n_regs) 1548c2ecf20Sopenharmony_ci return -EINVAL; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int copy_perf_config_registers_or_number(const struct i915_oa_reg *kernel_regs, 1608c2ecf20Sopenharmony_ci u32 kernel_n_regs, 1618c2ecf20Sopenharmony_ci u64 user_regs_ptr, 1628c2ecf20Sopenharmony_ci u32 *user_n_regs) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci u32 __user *p = u64_to_user_ptr(user_regs_ptr); 1658c2ecf20Sopenharmony_ci u32 r; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (*user_n_regs == 0) { 1688c2ecf20Sopenharmony_ci *user_n_regs = kernel_n_regs; 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci *user_n_regs = kernel_n_regs; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!user_write_access_begin(p, 2 * sizeof(u32) * kernel_n_regs)) 1758c2ecf20Sopenharmony_ci return -EFAULT; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci for (r = 0; r < kernel_n_regs; r++, p += 2) { 1788c2ecf20Sopenharmony_ci unsafe_put_user(i915_mmio_reg_offset(kernel_regs[r].addr), 1798c2ecf20Sopenharmony_ci p, Efault); 1808c2ecf20Sopenharmony_ci unsafe_put_user(kernel_regs[r].value, p + 1, Efault); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci user_write_access_end(); 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ciEfault: 1858c2ecf20Sopenharmony_ci user_write_access_end(); 1868c2ecf20Sopenharmony_ci return -EFAULT; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int query_perf_config_data(struct drm_i915_private *i915, 1908c2ecf20Sopenharmony_ci struct drm_i915_query_item *query_item, 1918c2ecf20Sopenharmony_ci bool use_uuid) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct drm_i915_query_perf_config __user *user_query_config_ptr = 1948c2ecf20Sopenharmony_ci u64_to_user_ptr(query_item->data_ptr); 1958c2ecf20Sopenharmony_ci struct drm_i915_perf_oa_config __user *user_config_ptr = 1968c2ecf20Sopenharmony_ci u64_to_user_ptr(query_item->data_ptr + 1978c2ecf20Sopenharmony_ci sizeof(struct drm_i915_query_perf_config)); 1988c2ecf20Sopenharmony_ci struct drm_i915_perf_oa_config user_config; 1998c2ecf20Sopenharmony_ci struct i915_perf *perf = &i915->perf; 2008c2ecf20Sopenharmony_ci struct i915_oa_config *oa_config; 2018c2ecf20Sopenharmony_ci char uuid[UUID_STRING_LEN + 1]; 2028c2ecf20Sopenharmony_ci u64 config_id; 2038c2ecf20Sopenharmony_ci u32 flags, total_size; 2048c2ecf20Sopenharmony_ci int ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (!perf->i915) 2078c2ecf20Sopenharmony_ci return -ENODEV; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci total_size = 2108c2ecf20Sopenharmony_ci sizeof(struct drm_i915_query_perf_config) + 2118c2ecf20Sopenharmony_ci sizeof(struct drm_i915_perf_oa_config); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (query_item->length == 0) 2148c2ecf20Sopenharmony_ci return total_size; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (query_item->length < total_size) { 2178c2ecf20Sopenharmony_ci DRM_DEBUG("Invalid query config data item size=%u expected=%u\n", 2188c2ecf20Sopenharmony_ci query_item->length, total_size); 2198c2ecf20Sopenharmony_ci return -EINVAL; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (get_user(flags, &user_query_config_ptr->flags)) 2238c2ecf20Sopenharmony_ci return -EFAULT; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (flags != 0) 2268c2ecf20Sopenharmony_ci return -EINVAL; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (use_uuid) { 2298c2ecf20Sopenharmony_ci struct i915_oa_config *tmp; 2308c2ecf20Sopenharmony_ci int id; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(user_query_config_ptr->uuid) >= sizeof(uuid)); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci memset(&uuid, 0, sizeof(uuid)); 2358c2ecf20Sopenharmony_ci if (copy_from_user(uuid, user_query_config_ptr->uuid, 2368c2ecf20Sopenharmony_ci sizeof(user_query_config_ptr->uuid))) 2378c2ecf20Sopenharmony_ci return -EFAULT; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci oa_config = NULL; 2408c2ecf20Sopenharmony_ci rcu_read_lock(); 2418c2ecf20Sopenharmony_ci idr_for_each_entry(&perf->metrics_idr, tmp, id) { 2428c2ecf20Sopenharmony_ci if (!strcmp(tmp->uuid, uuid)) { 2438c2ecf20Sopenharmony_ci oa_config = i915_oa_config_get(tmp); 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci rcu_read_unlock(); 2488c2ecf20Sopenharmony_ci } else { 2498c2ecf20Sopenharmony_ci if (get_user(config_id, &user_query_config_ptr->config)) 2508c2ecf20Sopenharmony_ci return -EFAULT; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci oa_config = i915_perf_get_oa_config(perf, config_id); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci if (!oa_config) 2558c2ecf20Sopenharmony_ci return -ENOENT; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (copy_from_user(&user_config, user_config_ptr, sizeof(user_config))) { 2588c2ecf20Sopenharmony_ci ret = -EFAULT; 2598c2ecf20Sopenharmony_ci goto out; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ret = can_copy_perf_config_registers_or_number(user_config.n_boolean_regs, 2638c2ecf20Sopenharmony_ci user_config.boolean_regs_ptr, 2648c2ecf20Sopenharmony_ci oa_config->b_counter_regs_len); 2658c2ecf20Sopenharmony_ci if (ret) 2668c2ecf20Sopenharmony_ci goto out; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci ret = can_copy_perf_config_registers_or_number(user_config.n_flex_regs, 2698c2ecf20Sopenharmony_ci user_config.flex_regs_ptr, 2708c2ecf20Sopenharmony_ci oa_config->flex_regs_len); 2718c2ecf20Sopenharmony_ci if (ret) 2728c2ecf20Sopenharmony_ci goto out; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci ret = can_copy_perf_config_registers_or_number(user_config.n_mux_regs, 2758c2ecf20Sopenharmony_ci user_config.mux_regs_ptr, 2768c2ecf20Sopenharmony_ci oa_config->mux_regs_len); 2778c2ecf20Sopenharmony_ci if (ret) 2788c2ecf20Sopenharmony_ci goto out; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci ret = copy_perf_config_registers_or_number(oa_config->b_counter_regs, 2818c2ecf20Sopenharmony_ci oa_config->b_counter_regs_len, 2828c2ecf20Sopenharmony_ci user_config.boolean_regs_ptr, 2838c2ecf20Sopenharmony_ci &user_config.n_boolean_regs); 2848c2ecf20Sopenharmony_ci if (ret) 2858c2ecf20Sopenharmony_ci goto out; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci ret = copy_perf_config_registers_or_number(oa_config->flex_regs, 2888c2ecf20Sopenharmony_ci oa_config->flex_regs_len, 2898c2ecf20Sopenharmony_ci user_config.flex_regs_ptr, 2908c2ecf20Sopenharmony_ci &user_config.n_flex_regs); 2918c2ecf20Sopenharmony_ci if (ret) 2928c2ecf20Sopenharmony_ci goto out; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci ret = copy_perf_config_registers_or_number(oa_config->mux_regs, 2958c2ecf20Sopenharmony_ci oa_config->mux_regs_len, 2968c2ecf20Sopenharmony_ci user_config.mux_regs_ptr, 2978c2ecf20Sopenharmony_ci &user_config.n_mux_regs); 2988c2ecf20Sopenharmony_ci if (ret) 2998c2ecf20Sopenharmony_ci goto out; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci memcpy(user_config.uuid, oa_config->uuid, sizeof(user_config.uuid)); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (copy_to_user(user_config_ptr, &user_config, sizeof(user_config))) { 3048c2ecf20Sopenharmony_ci ret = -EFAULT; 3058c2ecf20Sopenharmony_ci goto out; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci ret = total_size; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciout: 3118c2ecf20Sopenharmony_ci i915_oa_config_put(oa_config); 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic size_t sizeof_perf_config_list(size_t count) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci return sizeof(struct drm_i915_query_perf_config) + sizeof(u64) * count; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic size_t sizeof_perf_metrics(struct i915_perf *perf) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct i915_oa_config *tmp; 3238c2ecf20Sopenharmony_ci size_t i; 3248c2ecf20Sopenharmony_ci int id; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci i = 1; 3278c2ecf20Sopenharmony_ci rcu_read_lock(); 3288c2ecf20Sopenharmony_ci idr_for_each_entry(&perf->metrics_idr, tmp, id) 3298c2ecf20Sopenharmony_ci i++; 3308c2ecf20Sopenharmony_ci rcu_read_unlock(); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return sizeof_perf_config_list(i); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int query_perf_config_list(struct drm_i915_private *i915, 3368c2ecf20Sopenharmony_ci struct drm_i915_query_item *query_item) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct drm_i915_query_perf_config __user *user_query_config_ptr = 3398c2ecf20Sopenharmony_ci u64_to_user_ptr(query_item->data_ptr); 3408c2ecf20Sopenharmony_ci struct i915_perf *perf = &i915->perf; 3418c2ecf20Sopenharmony_ci u64 *oa_config_ids = NULL; 3428c2ecf20Sopenharmony_ci int alloc, n_configs; 3438c2ecf20Sopenharmony_ci u32 flags; 3448c2ecf20Sopenharmony_ci int ret; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (!perf->i915) 3478c2ecf20Sopenharmony_ci return -ENODEV; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (query_item->length == 0) 3508c2ecf20Sopenharmony_ci return sizeof_perf_metrics(perf); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (get_user(flags, &user_query_config_ptr->flags)) 3538c2ecf20Sopenharmony_ci return -EFAULT; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (flags != 0) 3568c2ecf20Sopenharmony_ci return -EINVAL; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci n_configs = 1; 3598c2ecf20Sopenharmony_ci do { 3608c2ecf20Sopenharmony_ci struct i915_oa_config *tmp; 3618c2ecf20Sopenharmony_ci u64 *ids; 3628c2ecf20Sopenharmony_ci int id; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ids = krealloc(oa_config_ids, 3658c2ecf20Sopenharmony_ci n_configs * sizeof(*oa_config_ids), 3668c2ecf20Sopenharmony_ci GFP_KERNEL); 3678c2ecf20Sopenharmony_ci if (!ids) 3688c2ecf20Sopenharmony_ci return -ENOMEM; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci alloc = fetch_and_zero(&n_configs); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ids[n_configs++] = 1ull; /* reserved for test_config */ 3738c2ecf20Sopenharmony_ci rcu_read_lock(); 3748c2ecf20Sopenharmony_ci idr_for_each_entry(&perf->metrics_idr, tmp, id) { 3758c2ecf20Sopenharmony_ci if (n_configs < alloc) 3768c2ecf20Sopenharmony_ci ids[n_configs] = id; 3778c2ecf20Sopenharmony_ci n_configs++; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci rcu_read_unlock(); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci oa_config_ids = ids; 3828c2ecf20Sopenharmony_ci } while (n_configs > alloc); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (query_item->length < sizeof_perf_config_list(n_configs)) { 3858c2ecf20Sopenharmony_ci DRM_DEBUG("Invalid query config list item size=%u expected=%zu\n", 3868c2ecf20Sopenharmony_ci query_item->length, 3878c2ecf20Sopenharmony_ci sizeof_perf_config_list(n_configs)); 3888c2ecf20Sopenharmony_ci kfree(oa_config_ids); 3898c2ecf20Sopenharmony_ci return -EINVAL; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (put_user(n_configs, &user_query_config_ptr->config)) { 3938c2ecf20Sopenharmony_ci kfree(oa_config_ids); 3948c2ecf20Sopenharmony_ci return -EFAULT; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci ret = copy_to_user(user_query_config_ptr + 1, 3988c2ecf20Sopenharmony_ci oa_config_ids, 3998c2ecf20Sopenharmony_ci n_configs * sizeof(*oa_config_ids)); 4008c2ecf20Sopenharmony_ci kfree(oa_config_ids); 4018c2ecf20Sopenharmony_ci if (ret) 4028c2ecf20Sopenharmony_ci return -EFAULT; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return sizeof_perf_config_list(n_configs); 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int query_perf_config(struct drm_i915_private *i915, 4088c2ecf20Sopenharmony_ci struct drm_i915_query_item *query_item) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci switch (query_item->flags) { 4118c2ecf20Sopenharmony_ci case DRM_I915_QUERY_PERF_CONFIG_LIST: 4128c2ecf20Sopenharmony_ci return query_perf_config_list(i915, query_item); 4138c2ecf20Sopenharmony_ci case DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_UUID: 4148c2ecf20Sopenharmony_ci return query_perf_config_data(i915, query_item, true); 4158c2ecf20Sopenharmony_ci case DRM_I915_QUERY_PERF_CONFIG_DATA_FOR_ID: 4168c2ecf20Sopenharmony_ci return query_perf_config_data(i915, query_item, false); 4178c2ecf20Sopenharmony_ci default: 4188c2ecf20Sopenharmony_ci return -EINVAL; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int (* const i915_query_funcs[])(struct drm_i915_private *dev_priv, 4238c2ecf20Sopenharmony_ci struct drm_i915_query_item *query_item) = { 4248c2ecf20Sopenharmony_ci query_topology_info, 4258c2ecf20Sopenharmony_ci query_engine_info, 4268c2ecf20Sopenharmony_ci query_perf_config, 4278c2ecf20Sopenharmony_ci}; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ciint i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci struct drm_i915_private *dev_priv = to_i915(dev); 4328c2ecf20Sopenharmony_ci struct drm_i915_query *args = data; 4338c2ecf20Sopenharmony_ci struct drm_i915_query_item __user *user_item_ptr = 4348c2ecf20Sopenharmony_ci u64_to_user_ptr(args->items_ptr); 4358c2ecf20Sopenharmony_ci u32 i; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (args->flags != 0) 4388c2ecf20Sopenharmony_ci return -EINVAL; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci for (i = 0; i < args->num_items; i++, user_item_ptr++) { 4418c2ecf20Sopenharmony_ci struct drm_i915_query_item item; 4428c2ecf20Sopenharmony_ci unsigned long func_idx; 4438c2ecf20Sopenharmony_ci int ret; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (copy_from_user(&item, user_item_ptr, sizeof(item))) 4468c2ecf20Sopenharmony_ci return -EFAULT; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (item.query_id == 0) 4498c2ecf20Sopenharmony_ci return -EINVAL; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (overflows_type(item.query_id - 1, unsigned long)) 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci func_idx = item.query_id - 1; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci ret = -EINVAL; 4578c2ecf20Sopenharmony_ci if (func_idx < ARRAY_SIZE(i915_query_funcs)) { 4588c2ecf20Sopenharmony_ci func_idx = array_index_nospec(func_idx, 4598c2ecf20Sopenharmony_ci ARRAY_SIZE(i915_query_funcs)); 4608c2ecf20Sopenharmony_ci ret = i915_query_funcs[func_idx](dev_priv, &item); 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Only write the length back to userspace if they differ. */ 4648c2ecf20Sopenharmony_ci if (ret != item.length && put_user(ret, &user_item_ptr->length)) 4658c2ecf20Sopenharmony_ci return -EFAULT; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 470