summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_property.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_property.c')
-rw-r--r--drivers/gpu/drm/drm_property.c253
1 files changed, 177 insertions, 76 deletions
diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c
index 3e88fa24eab3..596272149a35 100644
--- a/drivers/gpu/drm/drm_property.c
+++ b/drivers/gpu/drm/drm_property.c
@@ -21,7 +21,13 @@
*/
#include <linux/export.h>
-#include <drm/drmP.h>
+#include <linux/uaccess.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/drm_property.h>
#include "drm_crtc_internal.h"
@@ -38,7 +44,7 @@
* property types and ranges.
*
* Properties don't store the current value directly, but need to be
- * instatiated by attaching them to a &drm_mode_object with
+ * instantiated by attaching them to a &drm_mode_object with
* drm_object_attach_property().
*
* Property values are only 64bit. To support bigger piles of data (like gamma
@@ -50,11 +56,27 @@
* IOCTL and in the get/set property IOCTL.
*/
-static bool drm_property_type_valid(struct drm_property *property)
+static bool drm_property_flags_valid(u32 flags)
{
- if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
- return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
- return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+ u32 legacy_type = flags & DRM_MODE_PROP_LEGACY_TYPE;
+ u32 ext_type = flags & DRM_MODE_PROP_EXTENDED_TYPE;
+
+ /* Reject undefined/deprecated flags */
+ if (flags & ~(DRM_MODE_PROP_LEGACY_TYPE |
+ DRM_MODE_PROP_EXTENDED_TYPE |
+ DRM_MODE_PROP_IMMUTABLE |
+ DRM_MODE_PROP_ATOMIC))
+ return false;
+
+ /* We want either a legacy type or an extended type, but not both */
+ if (!legacy_type == !ext_type)
+ return false;
+
+ /* Only one legacy type at a time please */
+ if (legacy_type && !is_power_of_2(legacy_type))
+ return false;
+
+ return true;
}
/**
@@ -72,12 +94,19 @@ static bool drm_property_type_valid(struct drm_property *property)
* Returns:
* A pointer to the newly created property on success, NULL on failure.
*/
-struct drm_property *drm_property_create(struct drm_device *dev, int flags,
- const char *name, int num_values)
+struct drm_property *drm_property_create(struct drm_device *dev,
+ u32 flags, const char *name,
+ int num_values)
{
struct drm_property *property = NULL;
int ret;
+ if (WARN_ON(!drm_property_flags_valid(flags)))
+ return NULL;
+
+ if (WARN_ON(strlen(name) >= DRM_PROP_NAME_LEN))
+ return NULL;
+
property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
if (!property)
return NULL;
@@ -99,15 +128,10 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
property->num_values = num_values;
INIT_LIST_HEAD(&property->enum_list);
- if (name) {
- strncpy(property->name, name, DRM_PROP_NAME_LEN);
- property->name[DRM_PROP_NAME_LEN-1] = '\0';
- }
+ strscpy_pad(property->name, name, DRM_PROP_NAME_LEN);
list_add_tail(&property->head, &dev->mode_config.property_list);
- WARN_ON(!drm_property_type_valid(property));
-
return property;
fail:
kfree(property->values);
@@ -135,10 +159,10 @@ EXPORT_SYMBOL(drm_property_create);
* Returns:
* A pointer to the newly created property on success, NULL on failure.
*/
-struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
- const char *name,
- const struct drm_prop_enum_list *props,
- int num_values)
+struct drm_property *drm_property_create_enum(struct drm_device *dev,
+ u32 flags, const char *name,
+ const struct drm_prop_enum_list *props,
+ int num_values)
{
struct drm_property *property;
int i, ret;
@@ -150,9 +174,9 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
return NULL;
for (i = 0; i < num_values; i++) {
- ret = drm_property_add_enum(property, i,
- props[i].type,
- props[i].name);
+ ret = drm_property_add_enum(property,
+ props[i].type,
+ props[i].name);
if (ret) {
drm_property_destroy(dev, property);
return NULL;
@@ -184,13 +208,13 @@ EXPORT_SYMBOL(drm_property_create_enum);
* A pointer to the newly created property on success, NULL on failure.
*/
struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
- int flags, const char *name,
- const struct drm_prop_enum_list *props,
- int num_props,
- uint64_t supported_bits)
+ u32 flags, const char *name,
+ const struct drm_prop_enum_list *props,
+ int num_props,
+ uint64_t supported_bits)
{
struct drm_property *property;
- int i, ret, index = 0;
+ int i, ret;
int num_values = hweight64(supported_bits);
flags |= DRM_MODE_PROP_BITMASK;
@@ -202,14 +226,9 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
if (!(supported_bits & (1ULL << props[i].type)))
continue;
- if (WARN_ON(index >= num_values)) {
- drm_property_destroy(dev, property);
- return NULL;
- }
-
- ret = drm_property_add_enum(property, index++,
- props[i].type,
- props[i].name);
+ ret = drm_property_add_enum(property,
+ props[i].type,
+ props[i].name);
if (ret) {
drm_property_destroy(dev, property);
return NULL;
@@ -221,8 +240,8 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
EXPORT_SYMBOL(drm_property_create_bitmask);
static struct drm_property *property_create_range(struct drm_device *dev,
- int flags, const char *name,
- uint64_t min, uint64_t max)
+ u32 flags, const char *name,
+ uint64_t min, uint64_t max)
{
struct drm_property *property;
@@ -255,9 +274,9 @@ static struct drm_property *property_create_range(struct drm_device *dev,
* Returns:
* A pointer to the newly created property on success, NULL on failure.
*/
-struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
- const char *name,
- uint64_t min, uint64_t max)
+struct drm_property *drm_property_create_range(struct drm_device *dev,
+ u32 flags, const char *name,
+ uint64_t min, uint64_t max)
{
return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
name, min, max);
@@ -284,8 +303,8 @@ EXPORT_SYMBOL(drm_property_create_range);
* A pointer to the newly created property on success, NULL on failure.
*/
struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
- int flags, const char *name,
- int64_t min, int64_t max)
+ u32 flags, const char *name,
+ int64_t min, int64_t max)
{
return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
name, I642U64(min), I642U64(max));
@@ -311,7 +330,7 @@ EXPORT_SYMBOL(drm_property_create_signed_range);
* A pointer to the newly created property on success, NULL on failure.
*/
struct drm_property *drm_property_create_object(struct drm_device *dev,
- int flags, const char *name,
+ u32 flags, const char *name,
uint32_t type)
{
struct drm_property *property;
@@ -347,8 +366,8 @@ EXPORT_SYMBOL(drm_property_create_object);
* Returns:
* A pointer to the newly created property on success, NULL on failure.
*/
-struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags,
- const char *name)
+struct drm_property *drm_property_create_bool(struct drm_device *dev,
+ u32 flags, const char *name)
{
return drm_property_create_range(dev, flags, name, 0, 1);
}
@@ -357,7 +376,6 @@ EXPORT_SYMBOL(drm_property_create_bool);
/**
* drm_property_add_enum - add a possible value to an enumeration property
* @property: enumeration property to change
- * @index: index of the new enumeration
* @value: value of the new enumeration
* @name: symbolic name of the new enumeration
*
@@ -369,39 +387,41 @@ EXPORT_SYMBOL(drm_property_create_bool);
* Returns:
* Zero on success, error code on failure.
*/
-int drm_property_add_enum(struct drm_property *property, int index,
+int drm_property_add_enum(struct drm_property *property,
uint64_t value, const char *name)
{
struct drm_property_enum *prop_enum;
+ int index = 0;
+
+ if (WARN_ON(strlen(name) >= DRM_PROP_NAME_LEN))
+ return -EINVAL;
- if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
- drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
+ if (WARN_ON(!drm_property_type_is(property, DRM_MODE_PROP_ENUM) &&
+ !drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
return -EINVAL;
/*
* Bitmask enum properties have the additional constraint of values
* from 0 to 63
*/
- if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
- (value > 63))
+ if (WARN_ON(drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
+ value > 63))
return -EINVAL;
- if (!list_empty(&property->enum_list)) {
- list_for_each_entry(prop_enum, &property->enum_list, head) {
- if (prop_enum->value == value) {
- strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
- prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
- return 0;
- }
- }
+ list_for_each_entry(prop_enum, &property->enum_list, head) {
+ if (WARN_ON(prop_enum->value == value))
+ return -EINVAL;
+ index++;
}
+ if (WARN_ON(index >= property->num_values))
+ return -EINVAL;
+
prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
if (!prop_enum)
return -ENOMEM;
- strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
- prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+ strscpy_pad(prop_enum->name, name, DRM_PROP_NAME_LEN);
prop_enum->value = value;
property->values[index] = value;
@@ -413,7 +433,7 @@ EXPORT_SYMBOL(drm_property_add_enum);
/**
* drm_property_destroy - destroy a drm property
* @dev: drm device
- * @property: property to destry
+ * @property: property to destroy
*
* This function frees a property including any attached resources like
* enumeration values.
@@ -448,14 +468,13 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
uint64_t __user *values_ptr;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return -EINVAL;
+ return -EOPNOTSUPP;
- property = drm_property_find(dev, out_resp->prop_id);
+ property = drm_property_find(dev, file_priv, out_resp->prop_id);
if (!property)
return -ENOENT;
- strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
- out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
+ strscpy_pad(out_resp->name, property->name, DRM_PROP_NAME_LEN);
out_resp->flags = property->flags;
value_count = property->num_values;
@@ -516,7 +535,7 @@ static void drm_property_free_blob(struct kref *kref)
drm_mode_object_unregister(blob->dev, &blob->base);
- kfree(blob);
+ kvfree(blob);
}
/**
@@ -540,16 +559,17 @@ drm_property_create_blob(struct drm_device *dev, size_t length,
struct drm_property_blob *blob;
int ret;
- if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob))
+ if (!length || length > INT_MAX - sizeof(struct drm_property_blob))
return ERR_PTR(-EINVAL);
- blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
+ blob = kvzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
if (!blob)
return ERR_PTR(-ENOMEM);
/* This must be explicitly initialised, so we can safely call list_del
* on it in the removal handler, even if it isn't in a file list. */
INIT_LIST_HEAD(&blob->head_file);
+ blob->data = (void *)blob + sizeof(*blob);
blob->length = length;
blob->dev = dev;
@@ -559,7 +579,7 @@ drm_property_create_blob(struct drm_device *dev, size_t length,
ret = __drm_mode_object_add(dev, &blob->base, DRM_MODE_OBJECT_BLOB,
true, drm_property_free_blob);
if (ret) {
- kfree(blob);
+ kvfree(blob);
return ERR_PTR(-EINVAL);
}
@@ -622,7 +642,7 @@ EXPORT_SYMBOL(drm_property_blob_get);
* @id: id of the blob property
*
* If successful, this takes an additional reference to the blob property.
- * callers need to make sure to eventually unreference the returned property
+ * callers need to make sure to eventually unreferenced the returned property
* again, using drm_property_blob_put().
*
* Return:
@@ -634,7 +654,7 @@ struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
struct drm_mode_object *obj;
struct drm_property_blob *blob = NULL;
- obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB);
+ obj = __drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_BLOB);
if (obj)
blob = obj_to_blob(obj);
return blob;
@@ -709,6 +729,87 @@ err_created:
}
EXPORT_SYMBOL(drm_property_replace_global_blob);
+/**
+ * drm_property_replace_blob - replace a blob property
+ * @blob: a pointer to the member blob to be replaced
+ * @new_blob: the new blob to replace with
+ *
+ * Return: true if the blob was in fact replaced.
+ */
+bool drm_property_replace_blob(struct drm_property_blob **blob,
+ struct drm_property_blob *new_blob)
+{
+ struct drm_property_blob *old_blob = *blob;
+
+ if (old_blob == new_blob)
+ return false;
+
+ drm_property_blob_put(old_blob);
+ if (new_blob)
+ drm_property_blob_get(new_blob);
+ *blob = new_blob;
+ return true;
+}
+EXPORT_SYMBOL(drm_property_replace_blob);
+
+/**
+ * drm_property_replace_blob_from_id - replace a blob property taking a reference
+ * @dev: DRM device
+ * @blob: a pointer to the member blob to be replaced
+ * @blob_id: the id of the new blob to replace with
+ * @expected_size: expected size of the blob property
+ * @expected_elem_size: expected size of an element in the blob property
+ * @replaced: if the blob was in fact replaced
+ *
+ * Look up the new blob from id, take its reference, check expected sizes of
+ * the blob and its element and replace the old blob by the new one. Advertise
+ * if the replacement operation was successful.
+ *
+ * Return: true if the blob was in fact replaced. -EINVAL if the new blob was
+ * not found or sizes don't match.
+ */
+int drm_property_replace_blob_from_id(struct drm_device *dev,
+ struct drm_property_blob **blob,
+ uint64_t blob_id,
+ ssize_t expected_size,
+ ssize_t expected_elem_size,
+ bool *replaced)
+{
+ struct drm_property_blob *new_blob = NULL;
+
+ if (blob_id != 0) {
+ new_blob = drm_property_lookup_blob(dev, blob_id);
+ if (new_blob == NULL) {
+ drm_dbg_atomic(dev,
+ "cannot find blob ID %llu\n", blob_id);
+ return -EINVAL;
+ }
+
+ if (expected_size > 0 &&
+ new_blob->length != expected_size) {
+ drm_dbg_atomic(dev,
+ "[BLOB:%d] length %zu different from expected %zu\n",
+ new_blob->base.id, new_blob->length, expected_size);
+ drm_property_blob_put(new_blob);
+ return -EINVAL;
+ }
+ if (expected_elem_size > 0 &&
+ new_blob->length % expected_elem_size != 0) {
+ drm_dbg_atomic(dev,
+ "[BLOB:%d] length %zu not divisible by element size %zu\n",
+ new_blob->base.id, new_blob->length, expected_elem_size);
+ drm_property_blob_put(new_blob);
+ return -EINVAL;
+ }
+ }
+
+ *replaced |= drm_property_replace_blob(blob, new_blob);
+ drm_property_blob_put(new_blob);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_property_replace_blob_from_id);
+
int drm_mode_getblob_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -717,7 +818,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
int ret = 0;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return -EINVAL;
+ return -EOPNOTSUPP;
blob = drm_property_lookup_blob(dev, out_resp->blob_id);
if (!blob)
@@ -746,7 +847,7 @@ int drm_mode_createblob_ioctl(struct drm_device *dev,
int ret = 0;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return -EINVAL;
+ return -EOPNOTSUPP;
blob = drm_property_create_blob(dev, out_resp->length, NULL);
if (IS_ERR(blob))
@@ -783,7 +884,7 @@ int drm_mode_destroyblob_ioctl(struct drm_device *dev,
int ret = 0;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
- return -EINVAL;
+ return -EOPNOTSUPP;
blob = drm_property_lookup_blob(dev, out_resp->blob_id);
if (!blob)
@@ -826,7 +927,7 @@ err:
* value doesn't become invalid part way through the property update due to
* race). The value returned by reference via 'obj' should be passed back
* to drm_property_change_valid_put() after the property is set (and the
- * object to which the property is attached has a chance to take it's own
+ * object to which the property is attached has a chance to take its own
* reference).
*/
bool drm_property_change_valid_get(struct drm_property *property,
@@ -874,7 +975,7 @@ bool drm_property_change_valid_get(struct drm_property *property,
if (value == 0)
return true;
- *ref = __drm_mode_object_find(property->dev, value,
+ *ref = __drm_mode_object_find(property->dev, NULL, value,
property->values[0]);
return *ref != NULL;
}