1 | /* |
2 | * Copyright (c) 2016 Intel Corporation |
3 | * |
4 | * Permission to use, copy, modify, distribute, and sell this software and its |
5 | * documentation for any purpose is hereby granted without fee, provided that |
6 | * the above copyright notice appear in all copies and that both that copyright |
7 | * notice and this permission notice appear in supporting documentation, and |
8 | * that the name of the copyright holders not be used in advertising or |
9 | * publicity pertaining to distribution of the software without specific, |
10 | * written prior permission. The copyright holders make no representations |
11 | * about the suitability of this software for any purpose. It is provided "as |
12 | * is" without express or implied warranty. |
13 | * |
14 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
16 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
20 | * OF THIS SOFTWARE. |
21 | */ |
22 | |
23 | #include <linux/export.h> |
24 | #include <linux/uaccess.h> |
25 | |
26 | #include <drm/drm_crtc.h> |
27 | #include <drm/drm_drv.h> |
28 | #include <drm/drm_file.h> |
29 | #include <drm/drm_framebuffer.h> |
30 | #include <drm/drm_property.h> |
31 | |
32 | #include "drm_crtc_internal.h" |
33 | |
34 | /** |
35 | * DOC: overview |
36 | * |
37 | * Properties as represented by &drm_property are used to extend the modeset |
38 | * interface exposed to userspace. For the atomic modeset IOCTL properties are |
39 | * even the only way to transport metadata about the desired new modeset |
40 | * configuration from userspace to the kernel. Properties have a well-defined |
41 | * value range, which is enforced by the drm core. See the documentation of the |
42 | * flags member of &struct drm_property for an overview of the different |
43 | * property types and ranges. |
44 | * |
45 | * Properties don't store the current value directly, but need to be |
46 | * instantiated by attaching them to a &drm_mode_object with |
47 | * drm_object_attach_property(). |
48 | * |
49 | * Property values are only 64bit. To support bigger piles of data (like gamma |
50 | * tables, color correction matrices or large structures) a property can instead |
51 | * point at a &drm_property_blob with that additional data. |
52 | * |
53 | * Properties are defined by their symbolic name, userspace must keep a |
54 | * per-object mapping from those names to the property ID used in the atomic |
55 | * IOCTL and in the get/set property IOCTL. |
56 | */ |
57 | |
58 | static bool drm_property_flags_valid(u32 flags) |
59 | { |
60 | u32 legacy_type = flags & DRM_MODE_PROP_LEGACY_TYPE; |
61 | u32 ext_type = flags & DRM_MODE_PROP_EXTENDED_TYPE; |
62 | |
63 | /* Reject undefined/deprecated flags */ |
64 | if (flags & ~(DRM_MODE_PROP_LEGACY_TYPE | |
65 | DRM_MODE_PROP_EXTENDED_TYPE | |
66 | DRM_MODE_PROP_IMMUTABLE | |
67 | DRM_MODE_PROP_ATOMIC)) |
68 | return false; |
69 | |
70 | /* We want either a legacy type or an extended type, but not both */ |
71 | if (!legacy_type == !ext_type) |
72 | return false; |
73 | |
74 | /* Only one legacy type at a time please */ |
75 | if (legacy_type && !is_power_of_2(n: legacy_type)) |
76 | return false; |
77 | |
78 | return true; |
79 | } |
80 | |
81 | /** |
82 | * drm_property_create - create a new property type |
83 | * @dev: drm device |
84 | * @flags: flags specifying the property type |
85 | * @name: name of the property |
86 | * @num_values: number of pre-defined values |
87 | * |
88 | * This creates a new generic drm property which can then be attached to a drm |
89 | * object with drm_object_attach_property(). The returned property object must |
90 | * be freed with drm_property_destroy(), which is done automatically when |
91 | * calling drm_mode_config_cleanup(). |
92 | * |
93 | * Returns: |
94 | * A pointer to the newly created property on success, NULL on failure. |
95 | */ |
96 | struct drm_property *drm_property_create(struct drm_device *dev, |
97 | u32 flags, const char *name, |
98 | int num_values) |
99 | { |
100 | struct drm_property *property = NULL; |
101 | int ret; |
102 | |
103 | if (WARN_ON(!drm_property_flags_valid(flags))) |
104 | return NULL; |
105 | |
106 | if (WARN_ON(strlen(name) >= DRM_PROP_NAME_LEN)) |
107 | return NULL; |
108 | |
109 | property = kzalloc(size: sizeof(struct drm_property), GFP_KERNEL); |
110 | if (!property) |
111 | return NULL; |
112 | |
113 | property->dev = dev; |
114 | |
115 | if (num_values) { |
116 | property->values = kcalloc(n: num_values, size: sizeof(uint64_t), |
117 | GFP_KERNEL); |
118 | if (!property->values) |
119 | goto fail; |
120 | } |
121 | |
122 | ret = drm_mode_object_add(dev, obj: &property->base, DRM_MODE_OBJECT_PROPERTY); |
123 | if (ret) |
124 | goto fail; |
125 | |
126 | property->flags = flags; |
127 | property->num_values = num_values; |
128 | INIT_LIST_HEAD(list: &property->enum_list); |
129 | |
130 | strscpy_pad(dest: property->name, src: name, DRM_PROP_NAME_LEN); |
131 | |
132 | list_add_tail(new: &property->head, head: &dev->mode_config.property_list); |
133 | |
134 | return property; |
135 | fail: |
136 | kfree(objp: property->values); |
137 | kfree(objp: property); |
138 | return NULL; |
139 | } |
140 | EXPORT_SYMBOL(drm_property_create); |
141 | |
142 | /** |
143 | * drm_property_create_enum - create a new enumeration property type |
144 | * @dev: drm device |
145 | * @flags: flags specifying the property type |
146 | * @name: name of the property |
147 | * @props: enumeration lists with property values |
148 | * @num_values: number of pre-defined values |
149 | * |
150 | * This creates a new generic drm property which can then be attached to a drm |
151 | * object with drm_object_attach_property(). The returned property object must |
152 | * be freed with drm_property_destroy(), which is done automatically when |
153 | * calling drm_mode_config_cleanup(). |
154 | * |
155 | * Userspace is only allowed to set one of the predefined values for enumeration |
156 | * properties. |
157 | * |
158 | * Returns: |
159 | * A pointer to the newly created property on success, NULL on failure. |
160 | */ |
161 | struct drm_property *drm_property_create_enum(struct drm_device *dev, |
162 | u32 flags, const char *name, |
163 | const struct drm_prop_enum_list *props, |
164 | int num_values) |
165 | { |
166 | struct drm_property *property; |
167 | int i, ret; |
168 | |
169 | flags |= DRM_MODE_PROP_ENUM; |
170 | |
171 | property = drm_property_create(dev, flags, name, num_values); |
172 | if (!property) |
173 | return NULL; |
174 | |
175 | for (i = 0; i < num_values; i++) { |
176 | ret = drm_property_add_enum(property, |
177 | value: props[i].type, |
178 | name: props[i].name); |
179 | if (ret) { |
180 | drm_property_destroy(dev, property); |
181 | return NULL; |
182 | } |
183 | } |
184 | |
185 | return property; |
186 | } |
187 | EXPORT_SYMBOL(drm_property_create_enum); |
188 | |
189 | /** |
190 | * drm_property_create_bitmask - create a new bitmask property type |
191 | * @dev: drm device |
192 | * @flags: flags specifying the property type |
193 | * @name: name of the property |
194 | * @props: enumeration lists with property bitflags |
195 | * @num_props: size of the @props array |
196 | * @supported_bits: bitmask of all supported enumeration values |
197 | * |
198 | * This creates a new bitmask drm property which can then be attached to a drm |
199 | * object with drm_object_attach_property(). The returned property object must |
200 | * be freed with drm_property_destroy(), which is done automatically when |
201 | * calling drm_mode_config_cleanup(). |
202 | * |
203 | * Compared to plain enumeration properties userspace is allowed to set any |
204 | * or'ed together combination of the predefined property bitflag values |
205 | * |
206 | * Returns: |
207 | * A pointer to the newly created property on success, NULL on failure. |
208 | */ |
209 | struct drm_property *drm_property_create_bitmask(struct drm_device *dev, |
210 | u32 flags, const char *name, |
211 | const struct drm_prop_enum_list *props, |
212 | int num_props, |
213 | uint64_t supported_bits) |
214 | { |
215 | struct drm_property *property; |
216 | int i, ret; |
217 | int num_values = hweight64(supported_bits); |
218 | |
219 | flags |= DRM_MODE_PROP_BITMASK; |
220 | |
221 | property = drm_property_create(dev, flags, name, num_values); |
222 | if (!property) |
223 | return NULL; |
224 | for (i = 0; i < num_props; i++) { |
225 | if (!(supported_bits & (1ULL << props[i].type))) |
226 | continue; |
227 | |
228 | ret = drm_property_add_enum(property, |
229 | value: props[i].type, |
230 | name: props[i].name); |
231 | if (ret) { |
232 | drm_property_destroy(dev, property); |
233 | return NULL; |
234 | } |
235 | } |
236 | |
237 | return property; |
238 | } |
239 | EXPORT_SYMBOL(drm_property_create_bitmask); |
240 | |
241 | static struct drm_property *property_create_range(struct drm_device *dev, |
242 | u32 flags, const char *name, |
243 | uint64_t min, uint64_t max) |
244 | { |
245 | struct drm_property *property; |
246 | |
247 | property = drm_property_create(dev, flags, name, 2); |
248 | if (!property) |
249 | return NULL; |
250 | |
251 | property->values[0] = min; |
252 | property->values[1] = max; |
253 | |
254 | return property; |
255 | } |
256 | |
257 | /** |
258 | * drm_property_create_range - create a new unsigned ranged property type |
259 | * @dev: drm device |
260 | * @flags: flags specifying the property type |
261 | * @name: name of the property |
262 | * @min: minimum value of the property |
263 | * @max: maximum value of the property |
264 | * |
265 | * This creates a new generic drm property which can then be attached to a drm |
266 | * object with drm_object_attach_property(). The returned property object must |
267 | * be freed with drm_property_destroy(), which is done automatically when |
268 | * calling drm_mode_config_cleanup(). |
269 | * |
270 | * Userspace is allowed to set any unsigned integer value in the (min, max) |
271 | * range inclusive. |
272 | * |
273 | * Returns: |
274 | * A pointer to the newly created property on success, NULL on failure. |
275 | */ |
276 | struct drm_property *drm_property_create_range(struct drm_device *dev, |
277 | u32 flags, const char *name, |
278 | uint64_t min, uint64_t max) |
279 | { |
280 | return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, |
281 | name, min, max); |
282 | } |
283 | EXPORT_SYMBOL(drm_property_create_range); |
284 | |
285 | /** |
286 | * drm_property_create_signed_range - create a new signed ranged property type |
287 | * @dev: drm device |
288 | * @flags: flags specifying the property type |
289 | * @name: name of the property |
290 | * @min: minimum value of the property |
291 | * @max: maximum value of the property |
292 | * |
293 | * This creates a new generic drm property which can then be attached to a drm |
294 | * object with drm_object_attach_property(). The returned property object must |
295 | * be freed with drm_property_destroy(), which is done automatically when |
296 | * calling drm_mode_config_cleanup(). |
297 | * |
298 | * Userspace is allowed to set any signed integer value in the (min, max) |
299 | * range inclusive. |
300 | * |
301 | * Returns: |
302 | * A pointer to the newly created property on success, NULL on failure. |
303 | */ |
304 | struct drm_property *drm_property_create_signed_range(struct drm_device *dev, |
305 | u32 flags, const char *name, |
306 | int64_t min, int64_t max) |
307 | { |
308 | return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, |
309 | name, min: I642U64(val: min), max: I642U64(val: max)); |
310 | } |
311 | EXPORT_SYMBOL(drm_property_create_signed_range); |
312 | |
313 | /** |
314 | * drm_property_create_object - create a new object property type |
315 | * @dev: drm device |
316 | * @flags: flags specifying the property type |
317 | * @name: name of the property |
318 | * @type: object type from DRM_MODE_OBJECT_* defines |
319 | * |
320 | * This creates a new generic drm property which can then be attached to a drm |
321 | * object with drm_object_attach_property(). The returned property object must |
322 | * be freed with drm_property_destroy(), which is done automatically when |
323 | * calling drm_mode_config_cleanup(). |
324 | * |
325 | * Userspace is only allowed to set this to any property value of the given |
326 | * @type. Only useful for atomic properties, which is enforced. |
327 | * |
328 | * Returns: |
329 | * A pointer to the newly created property on success, NULL on failure. |
330 | */ |
331 | struct drm_property *drm_property_create_object(struct drm_device *dev, |
332 | u32 flags, const char *name, |
333 | uint32_t type) |
334 | { |
335 | struct drm_property *property; |
336 | |
337 | flags |= DRM_MODE_PROP_OBJECT; |
338 | |
339 | if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC))) |
340 | return NULL; |
341 | |
342 | property = drm_property_create(dev, flags, name, 1); |
343 | if (!property) |
344 | return NULL; |
345 | |
346 | property->values[0] = type; |
347 | |
348 | return property; |
349 | } |
350 | EXPORT_SYMBOL(drm_property_create_object); |
351 | |
352 | /** |
353 | * drm_property_create_bool - create a new boolean property type |
354 | * @dev: drm device |
355 | * @flags: flags specifying the property type |
356 | * @name: name of the property |
357 | * |
358 | * This creates a new generic drm property which can then be attached to a drm |
359 | * object with drm_object_attach_property(). The returned property object must |
360 | * be freed with drm_property_destroy(), which is done automatically when |
361 | * calling drm_mode_config_cleanup(). |
362 | * |
363 | * This is implemented as a ranged property with only {0, 1} as valid values. |
364 | * |
365 | * Returns: |
366 | * A pointer to the newly created property on success, NULL on failure. |
367 | */ |
368 | struct drm_property *drm_property_create_bool(struct drm_device *dev, |
369 | u32 flags, const char *name) |
370 | { |
371 | return drm_property_create_range(dev, flags, name, 0, 1); |
372 | } |
373 | EXPORT_SYMBOL(drm_property_create_bool); |
374 | |
375 | /** |
376 | * drm_property_add_enum - add a possible value to an enumeration property |
377 | * @property: enumeration property to change |
378 | * @value: value of the new enumeration |
379 | * @name: symbolic name of the new enumeration |
380 | * |
381 | * This functions adds enumerations to a property. |
382 | * |
383 | * It's use is deprecated, drivers should use one of the more specific helpers |
384 | * to directly create the property with all enumerations already attached. |
385 | * |
386 | * Returns: |
387 | * Zero on success, error code on failure. |
388 | */ |
389 | int drm_property_add_enum(struct drm_property *property, |
390 | uint64_t value, const char *name) |
391 | { |
392 | struct drm_property_enum *prop_enum; |
393 | int index = 0; |
394 | |
395 | if (WARN_ON(strlen(name) >= DRM_PROP_NAME_LEN)) |
396 | return -EINVAL; |
397 | |
398 | if (WARN_ON(!drm_property_type_is(property, DRM_MODE_PROP_ENUM) && |
399 | !drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) |
400 | return -EINVAL; |
401 | |
402 | /* |
403 | * Bitmask enum properties have the additional constraint of values |
404 | * from 0 to 63 |
405 | */ |
406 | if (WARN_ON(drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && |
407 | value > 63)) |
408 | return -EINVAL; |
409 | |
410 | list_for_each_entry(prop_enum, &property->enum_list, head) { |
411 | if (WARN_ON(prop_enum->value == value)) |
412 | return -EINVAL; |
413 | index++; |
414 | } |
415 | |
416 | if (WARN_ON(index >= property->num_values)) |
417 | return -EINVAL; |
418 | |
419 | prop_enum = kzalloc(size: sizeof(struct drm_property_enum), GFP_KERNEL); |
420 | if (!prop_enum) |
421 | return -ENOMEM; |
422 | |
423 | strscpy_pad(dest: prop_enum->name, src: name, DRM_PROP_NAME_LEN); |
424 | prop_enum->value = value; |
425 | |
426 | property->values[index] = value; |
427 | list_add_tail(new: &prop_enum->head, head: &property->enum_list); |
428 | return 0; |
429 | } |
430 | EXPORT_SYMBOL(drm_property_add_enum); |
431 | |
432 | /** |
433 | * drm_property_destroy - destroy a drm property |
434 | * @dev: drm device |
435 | * @property: property to destroy |
436 | * |
437 | * This function frees a property including any attached resources like |
438 | * enumeration values. |
439 | */ |
440 | void drm_property_destroy(struct drm_device *dev, struct drm_property *property) |
441 | { |
442 | struct drm_property_enum *prop_enum, *pt; |
443 | |
444 | list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { |
445 | list_del(entry: &prop_enum->head); |
446 | kfree(objp: prop_enum); |
447 | } |
448 | |
449 | if (property->num_values) |
450 | kfree(objp: property->values); |
451 | drm_mode_object_unregister(dev, object: &property->base); |
452 | list_del(entry: &property->head); |
453 | kfree(objp: property); |
454 | } |
455 | EXPORT_SYMBOL(drm_property_destroy); |
456 | |
457 | int drm_mode_getproperty_ioctl(struct drm_device *dev, |
458 | void *data, struct drm_file *file_priv) |
459 | { |
460 | struct drm_mode_get_property *out_resp = data; |
461 | struct drm_property *property; |
462 | int enum_count = 0; |
463 | int value_count = 0; |
464 | int i, copied; |
465 | struct drm_property_enum *prop_enum; |
466 | struct drm_mode_property_enum __user *enum_ptr; |
467 | uint64_t __user *values_ptr; |
468 | |
469 | if (!drm_core_check_feature(dev, feature: DRIVER_MODESET)) |
470 | return -EOPNOTSUPP; |
471 | |
472 | property = drm_property_find(dev, file_priv, id: out_resp->prop_id); |
473 | if (!property) |
474 | return -ENOENT; |
475 | |
476 | strscpy_pad(dest: out_resp->name, src: property->name, DRM_PROP_NAME_LEN); |
477 | out_resp->flags = property->flags; |
478 | |
479 | value_count = property->num_values; |
480 | values_ptr = u64_to_user_ptr(out_resp->values_ptr); |
481 | |
482 | for (i = 0; i < value_count; i++) { |
483 | if (i < out_resp->count_values && |
484 | put_user(property->values[i], values_ptr + i)) { |
485 | return -EFAULT; |
486 | } |
487 | } |
488 | out_resp->count_values = value_count; |
489 | |
490 | copied = 0; |
491 | enum_ptr = u64_to_user_ptr(out_resp->enum_blob_ptr); |
492 | |
493 | if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || |
494 | drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { |
495 | list_for_each_entry(prop_enum, &property->enum_list, head) { |
496 | enum_count++; |
497 | if (out_resp->count_enum_blobs < enum_count) |
498 | continue; |
499 | |
500 | if (copy_to_user(to: &enum_ptr[copied].value, |
501 | from: &prop_enum->value, n: sizeof(uint64_t))) |
502 | return -EFAULT; |
503 | |
504 | if (copy_to_user(to: &enum_ptr[copied].name, |
505 | from: &prop_enum->name, DRM_PROP_NAME_LEN)) |
506 | return -EFAULT; |
507 | copied++; |
508 | } |
509 | out_resp->count_enum_blobs = enum_count; |
510 | } |
511 | |
512 | /* |
513 | * NOTE: The idea seems to have been to use this to read all the blob |
514 | * property values. But nothing ever added them to the corresponding |
515 | * list, userspace always used the special-purpose get_blob ioctl to |
516 | * read the value for a blob property. It also doesn't make a lot of |
517 | * sense to return values here when everything else is just metadata for |
518 | * the property itself. |
519 | */ |
520 | if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) |
521 | out_resp->count_enum_blobs = 0; |
522 | |
523 | return 0; |
524 | } |
525 | |
526 | static void drm_property_free_blob(struct kref *kref) |
527 | { |
528 | struct drm_property_blob *blob = |
529 | container_of(kref, struct drm_property_blob, base.refcount); |
530 | |
531 | mutex_lock(&blob->dev->mode_config.blob_lock); |
532 | list_del(entry: &blob->head_global); |
533 | mutex_unlock(lock: &blob->dev->mode_config.blob_lock); |
534 | |
535 | drm_mode_object_unregister(dev: blob->dev, object: &blob->base); |
536 | |
537 | kvfree(addr: blob); |
538 | } |
539 | |
540 | /** |
541 | * drm_property_create_blob - Create new blob property |
542 | * @dev: DRM device to create property for |
543 | * @length: Length to allocate for blob data |
544 | * @data: If specified, copies data into blob |
545 | * |
546 | * Creates a new blob property for a specified DRM device, optionally |
547 | * copying data. Note that blob properties are meant to be invariant, hence the |
548 | * data must be filled out before the blob is used as the value of any property. |
549 | * |
550 | * Returns: |
551 | * New blob property with a single reference on success, or an ERR_PTR |
552 | * value on failure. |
553 | */ |
554 | struct drm_property_blob * |
555 | drm_property_create_blob(struct drm_device *dev, size_t length, |
556 | const void *data) |
557 | { |
558 | struct drm_property_blob *blob; |
559 | int ret; |
560 | |
561 | if (!length || length > INT_MAX - sizeof(struct drm_property_blob)) |
562 | return ERR_PTR(error: -EINVAL); |
563 | |
564 | blob = kvzalloc(size: sizeof(struct drm_property_blob)+length, GFP_KERNEL); |
565 | if (!blob) |
566 | return ERR_PTR(error: -ENOMEM); |
567 | |
568 | /* This must be explicitly initialised, so we can safely call list_del |
569 | * on it in the removal handler, even if it isn't in a file list. */ |
570 | INIT_LIST_HEAD(list: &blob->head_file); |
571 | blob->data = (void *)blob + sizeof(*blob); |
572 | blob->length = length; |
573 | blob->dev = dev; |
574 | |
575 | if (data) |
576 | memcpy(blob->data, data, length); |
577 | |
578 | ret = __drm_mode_object_add(dev, obj: &blob->base, DRM_MODE_OBJECT_BLOB, |
579 | register_obj: true, obj_free_cb: drm_property_free_blob); |
580 | if (ret) { |
581 | kvfree(addr: blob); |
582 | return ERR_PTR(error: -EINVAL); |
583 | } |
584 | |
585 | mutex_lock(&dev->mode_config.blob_lock); |
586 | list_add_tail(new: &blob->head_global, |
587 | head: &dev->mode_config.property_blob_list); |
588 | mutex_unlock(lock: &dev->mode_config.blob_lock); |
589 | |
590 | return blob; |
591 | } |
592 | EXPORT_SYMBOL(drm_property_create_blob); |
593 | |
594 | /** |
595 | * drm_property_blob_put - release a blob property reference |
596 | * @blob: DRM blob property |
597 | * |
598 | * Releases a reference to a blob property. May free the object. |
599 | */ |
600 | void drm_property_blob_put(struct drm_property_blob *blob) |
601 | { |
602 | if (!blob) |
603 | return; |
604 | |
605 | drm_mode_object_put(obj: &blob->base); |
606 | } |
607 | EXPORT_SYMBOL(drm_property_blob_put); |
608 | |
609 | void drm_property_destroy_user_blobs(struct drm_device *dev, |
610 | struct drm_file *file_priv) |
611 | { |
612 | struct drm_property_blob *blob, *bt; |
613 | |
614 | /* |
615 | * When the file gets released that means no one else can access the |
616 | * blob list any more, so no need to grab dev->blob_lock. |
617 | */ |
618 | list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) { |
619 | list_del_init(entry: &blob->head_file); |
620 | drm_property_blob_put(blob); |
621 | } |
622 | } |
623 | |
624 | /** |
625 | * drm_property_blob_get - acquire blob property reference |
626 | * @blob: DRM blob property |
627 | * |
628 | * Acquires a reference to an existing blob property. Returns @blob, which |
629 | * allows this to be used as a shorthand in assignments. |
630 | */ |
631 | struct drm_property_blob *drm_property_blob_get(struct drm_property_blob *blob) |
632 | { |
633 | drm_mode_object_get(obj: &blob->base); |
634 | return blob; |
635 | } |
636 | EXPORT_SYMBOL(drm_property_blob_get); |
637 | |
638 | /** |
639 | * drm_property_lookup_blob - look up a blob property and take a reference |
640 | * @dev: drm device |
641 | * @id: id of the blob property |
642 | * |
643 | * If successful, this takes an additional reference to the blob property. |
644 | * callers need to make sure to eventually unreferenced the returned property |
645 | * again, using drm_property_blob_put(). |
646 | * |
647 | * Return: |
648 | * NULL on failure, pointer to the blob on success. |
649 | */ |
650 | struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, |
651 | uint32_t id) |
652 | { |
653 | struct drm_mode_object *obj; |
654 | struct drm_property_blob *blob = NULL; |
655 | |
656 | obj = __drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_BLOB); |
657 | if (obj) |
658 | blob = obj_to_blob(obj); |
659 | return blob; |
660 | } |
661 | EXPORT_SYMBOL(drm_property_lookup_blob); |
662 | |
663 | /** |
664 | * drm_property_replace_global_blob - replace existing blob property |
665 | * @dev: drm device |
666 | * @replace: location of blob property pointer to be replaced |
667 | * @length: length of data for new blob, or 0 for no data |
668 | * @data: content for new blob, or NULL for no data |
669 | * @obj_holds_id: optional object for property holding blob ID |
670 | * @prop_holds_id: optional property holding blob ID |
671 | * @return 0 on success or error on failure |
672 | * |
673 | * This function will replace a global property in the blob list, optionally |
674 | * updating a property which holds the ID of that property. |
675 | * |
676 | * If length is 0 or data is NULL, no new blob will be created, and the holding |
677 | * property, if specified, will be set to 0. |
678 | * |
679 | * Access to the replace pointer is assumed to be protected by the caller, e.g. |
680 | * by holding the relevant modesetting object lock for its parent. |
681 | * |
682 | * For example, a drm_connector has a 'PATH' property, which contains the ID |
683 | * of a blob property with the value of the MST path information. Calling this |
684 | * function with replace pointing to the connector's path_blob_ptr, length and |
685 | * data set for the new path information, obj_holds_id set to the connector's |
686 | * base object, and prop_holds_id set to the path property name, will perform |
687 | * a completely atomic update. The access to path_blob_ptr is protected by the |
688 | * caller holding a lock on the connector. |
689 | */ |
690 | int drm_property_replace_global_blob(struct drm_device *dev, |
691 | struct drm_property_blob **replace, |
692 | size_t length, |
693 | const void *data, |
694 | struct drm_mode_object *obj_holds_id, |
695 | struct drm_property *prop_holds_id) |
696 | { |
697 | struct drm_property_blob *new_blob = NULL; |
698 | struct drm_property_blob *old_blob = NULL; |
699 | int ret; |
700 | |
701 | WARN_ON(replace == NULL); |
702 | |
703 | old_blob = *replace; |
704 | |
705 | if (length && data) { |
706 | new_blob = drm_property_create_blob(dev, length, data); |
707 | if (IS_ERR(ptr: new_blob)) |
708 | return PTR_ERR(ptr: new_blob); |
709 | } |
710 | |
711 | if (obj_holds_id) { |
712 | ret = drm_object_property_set_value(obj: obj_holds_id, |
713 | property: prop_holds_id, |
714 | val: new_blob ? |
715 | new_blob->base.id : 0); |
716 | if (ret != 0) |
717 | goto err_created; |
718 | } |
719 | |
720 | drm_property_blob_put(old_blob); |
721 | *replace = new_blob; |
722 | |
723 | return 0; |
724 | |
725 | err_created: |
726 | drm_property_blob_put(new_blob); |
727 | return ret; |
728 | } |
729 | EXPORT_SYMBOL(drm_property_replace_global_blob); |
730 | |
731 | /** |
732 | * drm_property_replace_blob - replace a blob property |
733 | * @blob: a pointer to the member blob to be replaced |
734 | * @new_blob: the new blob to replace with |
735 | * |
736 | * Return: true if the blob was in fact replaced. |
737 | */ |
738 | bool drm_property_replace_blob(struct drm_property_blob **blob, |
739 | struct drm_property_blob *new_blob) |
740 | { |
741 | struct drm_property_blob *old_blob = *blob; |
742 | |
743 | if (old_blob == new_blob) |
744 | return false; |
745 | |
746 | drm_property_blob_put(old_blob); |
747 | if (new_blob) |
748 | drm_property_blob_get(new_blob); |
749 | *blob = new_blob; |
750 | return true; |
751 | } |
752 | EXPORT_SYMBOL(drm_property_replace_blob); |
753 | |
754 | int drm_mode_getblob_ioctl(struct drm_device *dev, |
755 | void *data, struct drm_file *file_priv) |
756 | { |
757 | struct drm_mode_get_blob *out_resp = data; |
758 | struct drm_property_blob *blob; |
759 | int ret = 0; |
760 | |
761 | if (!drm_core_check_feature(dev, feature: DRIVER_MODESET)) |
762 | return -EOPNOTSUPP; |
763 | |
764 | blob = drm_property_lookup_blob(dev, out_resp->blob_id); |
765 | if (!blob) |
766 | return -ENOENT; |
767 | |
768 | if (out_resp->length == blob->length) { |
769 | if (copy_to_user(u64_to_user_ptr(out_resp->data), |
770 | from: blob->data, |
771 | n: blob->length)) { |
772 | ret = -EFAULT; |
773 | goto unref; |
774 | } |
775 | } |
776 | out_resp->length = blob->length; |
777 | unref: |
778 | drm_property_blob_put(blob); |
779 | |
780 | return ret; |
781 | } |
782 | |
783 | int drm_mode_createblob_ioctl(struct drm_device *dev, |
784 | void *data, struct drm_file *file_priv) |
785 | { |
786 | struct drm_mode_create_blob *out_resp = data; |
787 | struct drm_property_blob *blob; |
788 | int ret = 0; |
789 | |
790 | if (!drm_core_check_feature(dev, feature: DRIVER_MODESET)) |
791 | return -EOPNOTSUPP; |
792 | |
793 | blob = drm_property_create_blob(dev, out_resp->length, NULL); |
794 | if (IS_ERR(ptr: blob)) |
795 | return PTR_ERR(ptr: blob); |
796 | |
797 | if (copy_from_user(to: blob->data, |
798 | u64_to_user_ptr(out_resp->data), |
799 | n: out_resp->length)) { |
800 | ret = -EFAULT; |
801 | goto out_blob; |
802 | } |
803 | |
804 | /* Dropping the lock between create_blob and our access here is safe |
805 | * as only the same file_priv can remove the blob; at this point, it is |
806 | * not associated with any file_priv. */ |
807 | mutex_lock(&dev->mode_config.blob_lock); |
808 | out_resp->blob_id = blob->base.id; |
809 | list_add_tail(new: &blob->head_file, head: &file_priv->blobs); |
810 | mutex_unlock(lock: &dev->mode_config.blob_lock); |
811 | |
812 | return 0; |
813 | |
814 | out_blob: |
815 | drm_property_blob_put(blob); |
816 | return ret; |
817 | } |
818 | |
819 | int drm_mode_destroyblob_ioctl(struct drm_device *dev, |
820 | void *data, struct drm_file *file_priv) |
821 | { |
822 | struct drm_mode_destroy_blob *out_resp = data; |
823 | struct drm_property_blob *blob = NULL, *bt; |
824 | bool found = false; |
825 | int ret = 0; |
826 | |
827 | if (!drm_core_check_feature(dev, feature: DRIVER_MODESET)) |
828 | return -EOPNOTSUPP; |
829 | |
830 | blob = drm_property_lookup_blob(dev, out_resp->blob_id); |
831 | if (!blob) |
832 | return -ENOENT; |
833 | |
834 | mutex_lock(&dev->mode_config.blob_lock); |
835 | /* Ensure the property was actually created by this user. */ |
836 | list_for_each_entry(bt, &file_priv->blobs, head_file) { |
837 | if (bt == blob) { |
838 | found = true; |
839 | break; |
840 | } |
841 | } |
842 | |
843 | if (!found) { |
844 | ret = -EPERM; |
845 | goto err; |
846 | } |
847 | |
848 | /* We must drop head_file here, because we may not be the last |
849 | * reference on the blob. */ |
850 | list_del_init(entry: &blob->head_file); |
851 | mutex_unlock(lock: &dev->mode_config.blob_lock); |
852 | |
853 | /* One reference from lookup, and one from the filp. */ |
854 | drm_property_blob_put(blob); |
855 | drm_property_blob_put(blob); |
856 | |
857 | return 0; |
858 | |
859 | err: |
860 | mutex_unlock(lock: &dev->mode_config.blob_lock); |
861 | drm_property_blob_put(blob); |
862 | |
863 | return ret; |
864 | } |
865 | |
866 | /* Some properties could refer to dynamic refcnt'd objects, or things that |
867 | * need special locking to handle lifetime issues (ie. to ensure the prop |
868 | * value doesn't become invalid part way through the property update due to |
869 | * race). The value returned by reference via 'obj' should be passed back |
870 | * to drm_property_change_valid_put() after the property is set (and the |
871 | * object to which the property is attached has a chance to take its own |
872 | * reference). |
873 | */ |
874 | bool drm_property_change_valid_get(struct drm_property *property, |
875 | uint64_t value, struct drm_mode_object **ref) |
876 | { |
877 | int i; |
878 | |
879 | if (property->flags & DRM_MODE_PROP_IMMUTABLE) |
880 | return false; |
881 | |
882 | *ref = NULL; |
883 | |
884 | if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { |
885 | if (value < property->values[0] || value > property->values[1]) |
886 | return false; |
887 | return true; |
888 | } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { |
889 | int64_t svalue = U642I64(val: value); |
890 | |
891 | if (svalue < U642I64(val: property->values[0]) || |
892 | svalue > U642I64(val: property->values[1])) |
893 | return false; |
894 | return true; |
895 | } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { |
896 | uint64_t valid_mask = 0; |
897 | |
898 | for (i = 0; i < property->num_values; i++) |
899 | valid_mask |= (1ULL << property->values[i]); |
900 | return !(value & ~valid_mask); |
901 | } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { |
902 | struct drm_property_blob *blob; |
903 | |
904 | if (value == 0) |
905 | return true; |
906 | |
907 | blob = drm_property_lookup_blob(property->dev, value); |
908 | if (blob) { |
909 | *ref = &blob->base; |
910 | return true; |
911 | } else { |
912 | return false; |
913 | } |
914 | } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { |
915 | /* a zero value for an object property translates to null: */ |
916 | if (value == 0) |
917 | return true; |
918 | |
919 | *ref = __drm_mode_object_find(dev: property->dev, NULL, id: value, |
920 | type: property->values[0]); |
921 | return *ref != NULL; |
922 | } |
923 | |
924 | for (i = 0; i < property->num_values; i++) |
925 | if (property->values[i] == value) |
926 | return true; |
927 | return false; |
928 | } |
929 | |
930 | void drm_property_change_valid_put(struct drm_property *property, |
931 | struct drm_mode_object *ref) |
932 | { |
933 | if (!ref) |
934 | return; |
935 | |
936 | if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { |
937 | drm_mode_object_put(obj: ref); |
938 | } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) |
939 | drm_property_blob_put(obj_to_blob(ref)); |
940 | } |
941 | |