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 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 34 static 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 98 static int 99 query_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 enum intel_engine_id id; 109 int len, ret; 110 111 if (query_item->flags) 112 return -EINVAL; 113 114 len = sizeof(struct drm_i915_query_engine_info) + 115 RUNTIME_INFO(i915)->num_engines * 116 sizeof(struct drm_i915_engine_info); 117 118 ret = copy_query_item(&query, sizeof(query), len, query_item); 119 if (ret != 0) 120 return ret; 121 122 if (query.num_engines || query.rsvd[0] || query.rsvd[1] || 123 query.rsvd[2]) 124 return -EINVAL; 125 126 info_ptr = &query_ptr->engines[0]; 127 128 for_each_engine(engine, i915, id) { 129 info.engine.engine_class = engine->uabi_class; 130 info.engine.engine_instance = engine->instance; 131 info.capabilities = engine->uabi_capabilities; 132 133 if (__copy_to_user(info_ptr, &info, sizeof(info))) 134 return -EFAULT; 135 136 query.num_engines++; 137 info_ptr++; 138 } 139 140 if (__copy_to_user(query_ptr, &query, sizeof(query))) 141 return -EFAULT; 142 143 return len; 144 } 145 146 static int (* const i915_query_funcs[])(struct drm_i915_private *dev_priv, 147 struct drm_i915_query_item *query_item) = { 148 query_topology_info, 149 query_engine_info, 150 }; 151 152 int i915_query_ioctl(struct drm_device *dev, void *data, struct drm_file *file) 153 { 154 struct drm_i915_private *dev_priv = to_i915(dev); 155 struct drm_i915_query *args = data; 156 struct drm_i915_query_item __user *user_item_ptr = 157 u64_to_user_ptr(args->items_ptr); 158 u32 i; 159 160 if (args->flags != 0) 161 return -EINVAL; 162 163 for (i = 0; i < args->num_items; i++, user_item_ptr++) { 164 struct drm_i915_query_item item; 165 unsigned long func_idx; 166 int ret; 167 168 if (copy_from_user(&item, user_item_ptr, sizeof(item))) 169 return -EFAULT; 170 171 if (item.query_id == 0) 172 return -EINVAL; 173 174 if (overflows_type(item.query_id - 1, unsigned long)) 175 return -EINVAL; 176 177 func_idx = item.query_id - 1; 178 179 ret = -EINVAL; 180 if (func_idx < ARRAY_SIZE(i915_query_funcs)) { 181 func_idx = array_index_nospec(func_idx, 182 ARRAY_SIZE(i915_query_funcs)); 183 ret = i915_query_funcs[func_idx](dev_priv, &item); 184 } 185 186 /* Only write the length back to userspace if they differ. */ 187 if (ret != item.length && put_user(ret, &user_item_ptr->length)) 188 return -EFAULT; 189 } 190 191 return 0; 192 } 193