1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2020 Intel Corporation
4  */
5 
6 #include "gem/i915_gem_ioctls.h"
7 #include "gem/i915_gem_lmem.h"
8 #include "gem/i915_gem_region.h"
9 
10 #include "i915_drv.h"
11 #include "i915_trace.h"
12 #include "i915_user_extensions.h"
13 
14 static u32 object_max_page_size(struct drm_i915_gem_object *obj)
15 {
16 	u32 max_page_size = 0;
17 	int i;
18 
19 	for (i = 0; i < obj->mm.n_placements; i++) {
20 		struct intel_memory_region *mr = obj->mm.placements[i];
21 
22 		GEM_BUG_ON(!is_power_of_2(mr->min_page_size));
23 		max_page_size = max_t(u32, max_page_size, mr->min_page_size);
24 	}
25 
26 	GEM_BUG_ON(!max_page_size);
27 	return max_page_size;
28 }
29 
30 static void object_set_placements(struct drm_i915_gem_object *obj,
31 				  struct intel_memory_region **placements,
32 				  unsigned int n_placements)
33 {
34 	GEM_BUG_ON(!n_placements);
35 
36 	/*
37 	 * For the common case of one memory region, skip storing an
38 	 * allocated array and just point at the region directly.
39 	 */
40 	if (n_placements == 1) {
41 		struct intel_memory_region *mr = placements[0];
42 		struct drm_i915_private *i915 = mr->i915;
43 
44 		obj->mm.placements = &i915->mm.regions[mr->id];
45 		obj->mm.n_placements = 1;
46 	} else {
47 		obj->mm.placements = placements;
48 		obj->mm.n_placements = n_placements;
49 	}
50 }
51 
52 static int i915_gem_publish(struct drm_i915_gem_object *obj,
53 			    struct drm_file *file,
54 			    u64 *size_p,
55 			    u32 *handle_p)
56 {
57 	u64 size = obj->base.size;
58 	int ret;
59 
60 	ret = drm_gem_handle_create(file, &obj->base, handle_p);
61 	/* drop reference from allocate - handle holds it now */
62 	i915_gem_object_put(obj);
63 	if (ret)
64 		return ret;
65 
66 	*size_p = size;
67 	return 0;
68 }
69 
70 static int
71 i915_gem_setup(struct drm_i915_gem_object *obj, u64 size)
72 {
73 	struct intel_memory_region *mr = obj->mm.placements[0];
74 	unsigned int flags;
75 	int ret;
76 
77 	size = round_up(size, object_max_page_size(obj));
78 	if (size == 0)
79 		return -EINVAL;
80 
81 	/* For most of the ABI (e.g. mmap) we think in system pages */
82 	GEM_BUG_ON(!IS_ALIGNED(size, PAGE_SIZE));
83 
84 	if (i915_gem_object_size_2big(size))
85 		return -E2BIG;
86 
87 	/*
88 	 * I915_BO_ALLOC_USER will make sure the object is cleared before
89 	 * any user access.
90 	 */
91 	flags = I915_BO_ALLOC_USER;
92 
93 	ret = mr->ops->init_object(mr, obj, size, flags);
94 	if (ret)
95 		return ret;
96 
97 	GEM_BUG_ON(size != obj->base.size);
98 
99 	trace_i915_gem_object_create(obj);
100 	return 0;
101 }
102 
103 int
104 i915_gem_dumb_create(struct drm_file *file,
105 		     struct drm_device *dev,
106 		     struct drm_mode_create_dumb *args)
107 {
108 	struct drm_i915_gem_object *obj;
109 	struct intel_memory_region *mr;
110 	enum intel_memory_type mem_type;
111 	int cpp = DIV_ROUND_UP(args->bpp, 8);
112 	u32 format;
113 	int ret;
114 
115 	switch (cpp) {
116 	case 1:
117 		format = DRM_FORMAT_C8;
118 		break;
119 	case 2:
120 		format = DRM_FORMAT_RGB565;
121 		break;
122 	case 4:
123 		format = DRM_FORMAT_XRGB8888;
124 		break;
125 	default:
126 		return -EINVAL;
127 	}
128 
129 	/* have to work out size/pitch and return them */
130 	args->pitch = ALIGN(args->width * cpp, 64);
131 
132 	/* align stride to page size so that we can remap */
133 	if (args->pitch > intel_plane_fb_max_stride(to_i915(dev), format,
134 						    DRM_FORMAT_MOD_LINEAR))
135 		args->pitch = ALIGN(args->pitch, 4096);
136 
137 	if (args->pitch < args->width)
138 		return -EINVAL;
139 
140 	args->size = mul_u32_u32(args->pitch, args->height);
141 
142 	mem_type = INTEL_MEMORY_SYSTEM;
143 	if (HAS_LMEM(to_i915(dev)))
144 		mem_type = INTEL_MEMORY_LOCAL;
145 
146 	obj = i915_gem_object_alloc();
147 	if (!obj)
148 		return -ENOMEM;
149 
150 	mr = intel_memory_region_by_type(to_i915(dev), mem_type);
151 	object_set_placements(obj, &mr, 1);
152 
153 	ret = i915_gem_setup(obj, args->size);
154 	if (ret)
155 		goto object_free;
156 
157 	return i915_gem_publish(obj, file, &args->size, &args->handle);
158 
159 object_free:
160 	i915_gem_object_free(obj);
161 	return ret;
162 }
163 
164 /**
165  * Creates a new mm object and returns a handle to it.
166  * @dev: drm device pointer
167  * @data: ioctl data blob
168  * @file: drm file pointer
169  */
170 int
171 i915_gem_create_ioctl(struct drm_device *dev, void *data,
172 		      struct drm_file *file)
173 {
174 	struct drm_i915_private *i915 = to_i915(dev);
175 	struct drm_i915_gem_create *args = data;
176 	struct drm_i915_gem_object *obj;
177 	struct intel_memory_region *mr;
178 	int ret;
179 
180 	i915_gem_flush_free_objects(i915);
181 
182 	obj = i915_gem_object_alloc();
183 	if (!obj)
184 		return -ENOMEM;
185 
186 	mr = intel_memory_region_by_type(i915, INTEL_MEMORY_SYSTEM);
187 	object_set_placements(obj, &mr, 1);
188 
189 	ret = i915_gem_setup(obj, args->size);
190 	if (ret)
191 		goto object_free;
192 
193 	return i915_gem_publish(obj, file, &args->size, &args->handle);
194 
195 object_free:
196 	i915_gem_object_free(obj);
197 	return ret;
198 }
199 
200 struct create_ext {
201 	struct drm_i915_private *i915;
202 	struct drm_i915_gem_object *vanilla_object;
203 };
204 
205 static void repr_placements(char *buf, size_t size,
206 			    struct intel_memory_region **placements,
207 			    int n_placements)
208 {
209 	int i;
210 
211 	buf[0] = '\0';
212 
213 	for (i = 0; i < n_placements; i++) {
214 		struct intel_memory_region *mr = placements[i];
215 		int r;
216 
217 		r = snprintf(buf, size, "\n  %s -> { class: %d, inst: %d }",
218 			     mr->name, mr->type, mr->instance);
219 		if (r >= size)
220 			return;
221 
222 		buf += r;
223 		size -= r;
224 	}
225 }
226 
227 static int set_placements(struct drm_i915_gem_create_ext_memory_regions *args,
228 			  struct create_ext *ext_data)
229 {
230 	struct drm_i915_private *i915 = ext_data->i915;
231 	struct drm_i915_gem_memory_class_instance __user *uregions =
232 		u64_to_user_ptr(args->regions);
233 	struct drm_i915_gem_object *obj = ext_data->vanilla_object;
234 	struct intel_memory_region **placements;
235 	u32 mask;
236 	int i, ret = 0;
237 
238 	if (args->pad) {
239 		drm_dbg(&i915->drm, "pad should be zero\n");
240 		ret = -EINVAL;
241 	}
242 
243 	if (!args->num_regions) {
244 		drm_dbg(&i915->drm, "num_regions is zero\n");
245 		ret = -EINVAL;
246 	}
247 
248 	if (args->num_regions > ARRAY_SIZE(i915->mm.regions)) {
249 		drm_dbg(&i915->drm, "num_regions is too large\n");
250 		ret = -EINVAL;
251 	}
252 
253 	if (ret)
254 		return ret;
255 
256 	placements = kmalloc_array(args->num_regions,
257 				   sizeof(struct intel_memory_region *),
258 				   GFP_KERNEL);
259 	if (!placements)
260 		return -ENOMEM;
261 
262 	mask = 0;
263 	for (i = 0; i < args->num_regions; i++) {
264 		struct drm_i915_gem_memory_class_instance region;
265 		struct intel_memory_region *mr;
266 
267 		if (copy_from_user(&region, uregions, sizeof(region))) {
268 			ret = -EFAULT;
269 			goto out_free;
270 		}
271 
272 		mr = intel_memory_region_lookup(i915,
273 						region.memory_class,
274 						region.memory_instance);
275 		if (!mr || mr->private) {
276 			drm_dbg(&i915->drm, "Device is missing region { class: %d, inst: %d } at index = %d\n",
277 				region.memory_class, region.memory_instance, i);
278 			ret = -EINVAL;
279 			goto out_dump;
280 		}
281 
282 		if (mask & BIT(mr->id)) {
283 			drm_dbg(&i915->drm, "Found duplicate placement %s -> { class: %d, inst: %d } at index = %d\n",
284 				mr->name, region.memory_class,
285 				region.memory_instance, i);
286 			ret = -EINVAL;
287 			goto out_dump;
288 		}
289 
290 		placements[i] = mr;
291 		mask |= BIT(mr->id);
292 
293 		++uregions;
294 	}
295 
296 	if (obj->mm.placements) {
297 		ret = -EINVAL;
298 		goto out_dump;
299 	}
300 
301 	object_set_placements(obj, placements, args->num_regions);
302 	if (args->num_regions == 1)
303 		kfree(placements);
304 
305 	return 0;
306 
307 out_dump:
308 	if (1) {
309 		char buf[256];
310 
311 		if (obj->mm.placements) {
312 			repr_placements(buf,
313 					sizeof(buf),
314 					obj->mm.placements,
315 					obj->mm.n_placements);
316 			drm_dbg(&i915->drm,
317 				"Placements were already set in previous EXT. Existing placements: %s\n",
318 				buf);
319 		}
320 
321 		repr_placements(buf, sizeof(buf), placements, i);
322 		drm_dbg(&i915->drm, "New placements(so far validated): %s\n", buf);
323 	}
324 
325 out_free:
326 	kfree(placements);
327 	return ret;
328 }
329 
330 static int ext_set_placements(struct i915_user_extension __user *base,
331 			      void *data)
332 {
333 	struct drm_i915_gem_create_ext_memory_regions ext;
334 
335 	if (!IS_ENABLED(CONFIG_DRM_I915_UNSTABLE_FAKE_LMEM))
336 		return -ENODEV;
337 
338 	if (copy_from_user(&ext, base, sizeof(ext)))
339 		return -EFAULT;
340 
341 	return set_placements(&ext, data);
342 }
343 
344 static const i915_user_extension_fn create_extensions[] = {
345 	[I915_GEM_CREATE_EXT_MEMORY_REGIONS] = ext_set_placements,
346 };
347 
348 /**
349  * Creates a new mm object and returns a handle to it.
350  * @dev: drm device pointer
351  * @data: ioctl data blob
352  * @file: drm file pointer
353  */
354 int
355 i915_gem_create_ext_ioctl(struct drm_device *dev, void *data,
356 			  struct drm_file *file)
357 {
358 	struct drm_i915_private *i915 = to_i915(dev);
359 	struct drm_i915_gem_create_ext *args = data;
360 	struct create_ext ext_data = { .i915 = i915 };
361 	struct intel_memory_region **placements_ext;
362 	struct drm_i915_gem_object *obj;
363 	int ret;
364 
365 	if (args->flags)
366 		return -EINVAL;
367 
368 	i915_gem_flush_free_objects(i915);
369 
370 	obj = i915_gem_object_alloc();
371 	if (!obj)
372 		return -ENOMEM;
373 
374 	ext_data.vanilla_object = obj;
375 	ret = i915_user_extensions(u64_to_user_ptr(args->extensions),
376 				   create_extensions,
377 				   ARRAY_SIZE(create_extensions),
378 				   &ext_data);
379 	placements_ext = obj->mm.placements;
380 	if (ret)
381 		goto object_free;
382 
383 	if (!placements_ext) {
384 		struct intel_memory_region *mr =
385 			intel_memory_region_by_type(i915, INTEL_MEMORY_SYSTEM);
386 
387 		object_set_placements(obj, &mr, 1);
388 	}
389 
390 	ret = i915_gem_setup(obj, args->size);
391 	if (ret)
392 		goto object_free;
393 
394 	return i915_gem_publish(obj, file, &args->size, &args->handle);
395 
396 object_free:
397 	if (obj->mm.n_placements > 1)
398 		kfree(placements_ext);
399 	i915_gem_object_free(obj);
400 	return ret;
401 }
402