]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/base/regmap/regmap-debugfs.c
regmap: debugfs: Ensure we don't underflow when printing access masks
[mirror_ubuntu-zesty-kernel.git] / drivers / base / regmap / regmap-debugfs.c
index 0c94b661c16f87da22f1ed90a0c1d3512bcaf69e..1f327898e53d12ffaf8c09a0bc3b9f573f973b1e 100644 (file)
@@ -432,7 +432,7 @@ static ssize_t regmap_access_read_file(struct file *file,
                /* If we're in the region the user is trying to read */
                if (p >= *ppos) {
                        /* ...but not beyond it */
-                       if (buf_pos >= count - 1 - tot_len)
+                       if (buf_pos + tot_len + 1 >= count)
                                break;
 
                        /* Format the register */
@@ -469,10 +469,92 @@ static const struct file_operations regmap_access_fops = {
        .llseek = default_llseek,
 };
 
+static ssize_t regmap_cache_only_write_file(struct file *file,
+                                           const char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+       struct regmap *map = container_of(file->private_data,
+                                         struct regmap, cache_only);
+       ssize_t result;
+       bool was_enabled, require_sync = false;
+       int err;
+
+       map->lock(map->lock_arg);
+
+       was_enabled = map->cache_only;
+
+       result = debugfs_write_file_bool(file, user_buf, count, ppos);
+       if (result < 0) {
+               map->unlock(map->lock_arg);
+               return result;
+       }
+
+       if (map->cache_only && !was_enabled) {
+               dev_warn(map->dev, "debugfs cache_only=Y forced\n");
+               add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+       } else if (!map->cache_only && was_enabled) {
+               dev_warn(map->dev, "debugfs cache_only=N forced: syncing cache\n");
+               require_sync = true;
+       }
+
+       map->unlock(map->lock_arg);
+
+       if (require_sync) {
+               err = regcache_sync(map);
+               if (err)
+                       dev_err(map->dev, "Failed to sync cache %d\n", err);
+       }
+
+       return result;
+}
+
+static const struct file_operations regmap_cache_only_fops = {
+       .open = simple_open,
+       .read = debugfs_read_file_bool,
+       .write = regmap_cache_only_write_file,
+};
+
+static ssize_t regmap_cache_bypass_write_file(struct file *file,
+                                             const char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct regmap *map = container_of(file->private_data,
+                                         struct regmap, cache_bypass);
+       ssize_t result;
+       bool was_enabled;
+
+       map->lock(map->lock_arg);
+
+       was_enabled = map->cache_bypass;
+
+       result = debugfs_write_file_bool(file, user_buf, count, ppos);
+       if (result < 0)
+               goto out;
+
+       if (map->cache_bypass && !was_enabled) {
+               dev_warn(map->dev, "debugfs cache_bypass=Y forced\n");
+               add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+       } else if (!map->cache_bypass && was_enabled) {
+               dev_warn(map->dev, "debugfs cache_bypass=N forced\n");
+       }
+
+out:
+       map->unlock(map->lock_arg);
+
+       return result;
+}
+
+static const struct file_operations regmap_cache_bypass_fops = {
+       .open = simple_open,
+       .read = debugfs_read_file_bool,
+       .write = regmap_cache_bypass_write_file,
+};
+
 void regmap_debugfs_init(struct regmap *map, const char *name)
 {
        struct rb_node *next;
        struct regmap_range_node *range_node;
+       const char *devname = "dummy";
 
        /* If we don't have the debugfs root yet, postpone init */
        if (!regmap_debugfs_root) {
@@ -491,12 +573,15 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
        INIT_LIST_HEAD(&map->debugfs_off_cache);
        mutex_init(&map->cache_lock);
 
+       if (map->dev)
+               devname = dev_name(map->dev);
+
        if (name) {
                map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
-                                             dev_name(map->dev), name);
+                                             devname, name);
                name = map->debugfs_name;
        } else {
-               name = dev_name(map->dev);
+               name = devname;
        }
 
        map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
@@ -514,10 +599,11 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
        if (map->max_register || regmap_readable(map, 0)) {
                umode_t registers_mode;
 
-               if (IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS))
-                       registers_mode = 0600;
-               else
-                       registers_mode = 0400;
+#if defined(REGMAP_ALLOW_WRITE_DEBUGFS)
+               registers_mode = 0600;
+#else
+               registers_mode = 0400;
+#endif
 
                debugfs_create_file("registers", registers_mode, map->debugfs,
                                    map, &regmap_map_fops);
@@ -526,12 +612,13 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
        }
 
        if (map->cache_type) {
-               debugfs_create_bool("cache_only", 0400, map->debugfs,
-                                   &map->cache_only);
+               debugfs_create_file("cache_only", 0600, map->debugfs,
+                                   &map->cache_only, &regmap_cache_only_fops);
                debugfs_create_bool("cache_dirty", 0400, map->debugfs,
                                    &map->cache_dirty);
-               debugfs_create_bool("cache_bypass", 0400, map->debugfs,
-                                   &map->cache_bypass);
+               debugfs_create_file("cache_bypass", 0600, map->debugfs,
+                                   &map->cache_bypass,
+                                   &regmap_cache_bypass_fops);
        }
 
        next = rb_first(&map->range_tree);