Commit 9a3a0876 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'regmap-fix-v5.9-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap fixes from Mark Brown:
 "Two issues here - one is a fix for use after free issues in the case
  where a regmap overrides its name using something dynamically
  generated, the other is that we weren't handling access checks
  non-incrementing I/O on registers within paged register regions
  correctly resulting in spurious errors.

  Both of these are quite rare but serious if they occur"

* tag 'regmap-fix-v5.9-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: fix page selection for noinc writes
  regmap: fix page selection for noinc reads
  regmap: debugfs: Add back in erroneously removed initialisation of ret
  regmap: debugfs: Fix handling of name string for debugfs init delays
parents 6d28cf7d 05669b63
...@@ -217,7 +217,7 @@ struct regmap_field { ...@@ -217,7 +217,7 @@ struct regmap_field {
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
extern void regmap_debugfs_initcall(void); extern void regmap_debugfs_initcall(void);
extern void regmap_debugfs_init(struct regmap *map, const char *name); extern void regmap_debugfs_init(struct regmap *map);
extern void regmap_debugfs_exit(struct regmap *map); extern void regmap_debugfs_exit(struct regmap *map);
static inline void regmap_debugfs_disable(struct regmap *map) static inline void regmap_debugfs_disable(struct regmap *map)
...@@ -227,7 +227,7 @@ static inline void regmap_debugfs_disable(struct regmap *map) ...@@ -227,7 +227,7 @@ static inline void regmap_debugfs_disable(struct regmap *map)
#else #else
static inline void regmap_debugfs_initcall(void) { } static inline void regmap_debugfs_initcall(void) { }
static inline void regmap_debugfs_init(struct regmap *map, const char *name) { } static inline void regmap_debugfs_init(struct regmap *map) { }
static inline void regmap_debugfs_exit(struct regmap *map) { } static inline void regmap_debugfs_exit(struct regmap *map) { }
static inline void regmap_debugfs_disable(struct regmap *map) { } static inline void regmap_debugfs_disable(struct regmap *map) { }
#endif #endif
...@@ -259,7 +259,7 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, ...@@ -259,7 +259,7 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx,
int regcache_lookup_reg(struct regmap *map, unsigned int reg); int regcache_lookup_reg(struct regmap *map, unsigned int reg);
int _regmap_raw_write(struct regmap *map, unsigned int reg, int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len); const void *val, size_t val_len, bool noinc);
void regmap_async_complete_cb(struct regmap_async *async, int ret); void regmap_async_complete_cb(struct regmap_async *async, int ret);
......
...@@ -717,7 +717,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data, ...@@ -717,7 +717,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
map->cache_bypass = true; map->cache_bypass = true;
ret = _regmap_raw_write(map, base, *data, count * val_bytes); ret = _regmap_raw_write(map, base, *data, count * val_bytes, false);
if (ret) if (ret)
dev_err(map->dev, "Unable to sync registers %#x-%#x. %d\n", dev_err(map->dev, "Unable to sync registers %#x-%#x. %d\n",
base, cur - map->reg_stride, ret); base, cur - map->reg_stride, ret);
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
struct regmap_debugfs_node { struct regmap_debugfs_node {
struct regmap *map; struct regmap *map;
const char *name;
struct list_head link; struct list_head link;
}; };
...@@ -544,11 +543,12 @@ static const struct file_operations regmap_cache_bypass_fops = { ...@@ -544,11 +543,12 @@ static const struct file_operations regmap_cache_bypass_fops = {
.write = regmap_cache_bypass_write_file, .write = regmap_cache_bypass_write_file,
}; };
void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_init(struct regmap *map)
{ {
struct rb_node *next; struct rb_node *next;
struct regmap_range_node *range_node; struct regmap_range_node *range_node;
const char *devname = "dummy"; const char *devname = "dummy";
const char *name = map->name;
/* /*
* Userspace can initiate reads from the hardware over debugfs. * Userspace can initiate reads from the hardware over debugfs.
...@@ -569,7 +569,6 @@ void regmap_debugfs_init(struct regmap *map, const char *name) ...@@ -569,7 +569,6 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
if (!node) if (!node)
return; return;
node->map = map; node->map = map;
node->name = name;
mutex_lock(&regmap_debugfs_early_lock); mutex_lock(&regmap_debugfs_early_lock);
list_add(&node->link, &regmap_debugfs_early_list); list_add(&node->link, &regmap_debugfs_early_list);
mutex_unlock(&regmap_debugfs_early_lock); mutex_unlock(&regmap_debugfs_early_lock);
...@@ -679,7 +678,7 @@ void regmap_debugfs_initcall(void) ...@@ -679,7 +678,7 @@ void regmap_debugfs_initcall(void)
mutex_lock(&regmap_debugfs_early_lock); mutex_lock(&regmap_debugfs_early_lock);
list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) { list_for_each_entry_safe(node, tmp, &regmap_debugfs_early_list, link) {
regmap_debugfs_init(node->map, node->name); regmap_debugfs_init(node->map);
list_del(&node->link); list_del(&node->link);
kfree(node); kfree(node);
} }
......
...@@ -581,14 +581,34 @@ static void regmap_range_exit(struct regmap *map) ...@@ -581,14 +581,34 @@ static void regmap_range_exit(struct regmap *map)
kfree(map->selector_work_buf); kfree(map->selector_work_buf);
} }
static int regmap_set_name(struct regmap *map, const struct regmap_config *config)
{
if (config->name) {
const char *name = kstrdup_const(config->name, GFP_KERNEL);
if (!name)
return -ENOMEM;
kfree_const(map->name);
map->name = name;
}
return 0;
}
int regmap_attach_dev(struct device *dev, struct regmap *map, int regmap_attach_dev(struct device *dev, struct regmap *map,
const struct regmap_config *config) const struct regmap_config *config)
{ {
struct regmap **m; struct regmap **m;
int ret;
map->dev = dev; map->dev = dev;
regmap_debugfs_init(map, config->name); ret = regmap_set_name(map, config);
if (ret)
return ret;
regmap_debugfs_init(map);
/* Add a devres resource for dev_get_regmap() */ /* Add a devres resource for dev_get_regmap() */
m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL);
...@@ -687,13 +707,9 @@ struct regmap *__regmap_init(struct device *dev, ...@@ -687,13 +707,9 @@ struct regmap *__regmap_init(struct device *dev,
goto err; goto err;
} }
if (config->name) { ret = regmap_set_name(map, config);
map->name = kstrdup_const(config->name, GFP_KERNEL); if (ret)
if (!map->name) { goto err_map;
ret = -ENOMEM;
goto err_map;
}
}
if (config->disable_locking) { if (config->disable_locking) {
map->lock = map->unlock = regmap_lock_unlock_none; map->lock = map->unlock = regmap_lock_unlock_none;
...@@ -1137,7 +1153,7 @@ struct regmap *__regmap_init(struct device *dev, ...@@ -1137,7 +1153,7 @@ struct regmap *__regmap_init(struct device *dev,
if (ret != 0) if (ret != 0)
goto err_regcache; goto err_regcache;
} else { } else {
regmap_debugfs_init(map, config->name); regmap_debugfs_init(map);
} }
return map; return map;
...@@ -1297,6 +1313,8 @@ EXPORT_SYMBOL_GPL(regmap_field_free); ...@@ -1297,6 +1313,8 @@ EXPORT_SYMBOL_GPL(regmap_field_free);
*/ */
int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
{ {
int ret;
regcache_exit(map); regcache_exit(map);
regmap_debugfs_exit(map); regmap_debugfs_exit(map);
...@@ -1309,7 +1327,11 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config) ...@@ -1309,7 +1327,11 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
map->readable_noinc_reg = config->readable_noinc_reg; map->readable_noinc_reg = config->readable_noinc_reg;
map->cache_type = config->cache_type; map->cache_type = config->cache_type;
regmap_debugfs_init(map, config->name); ret = regmap_set_name(map, config);
if (ret)
return ret;
regmap_debugfs_init(map);
map->cache_bypass = false; map->cache_bypass = false;
map->cache_only = false; map->cache_only = false;
...@@ -1464,7 +1486,7 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes, ...@@ -1464,7 +1486,7 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
} }
static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
const void *val, size_t val_len) const void *val, size_t val_len, bool noinc)
{ {
struct regmap_range_node *range; struct regmap_range_node *range;
unsigned long flags; unsigned long flags;
...@@ -1523,7 +1545,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, ...@@ -1523,7 +1545,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
win_residue, val_len / map->format.val_bytes); win_residue, val_len / map->format.val_bytes);
ret = _regmap_raw_write_impl(map, reg, val, ret = _regmap_raw_write_impl(map, reg, val,
win_residue * win_residue *
map->format.val_bytes); map->format.val_bytes, noinc);
if (ret != 0) if (ret != 0)
return ret; return ret;
...@@ -1537,7 +1559,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, ...@@ -1537,7 +1559,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
win_residue = range->window_len - win_offset; win_residue = range->window_len - win_offset;
} }
ret = _regmap_select_page(map, &reg, range, val_num); ret = _regmap_select_page(map, &reg, range, noinc ? 1 : val_num);
if (ret != 0) if (ret != 0)
return ret; return ret;
} }
...@@ -1745,7 +1767,8 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, ...@@ -1745,7 +1767,8 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg,
map->work_buf + map->work_buf +
map->format.reg_bytes + map->format.reg_bytes +
map->format.pad_bytes, map->format.pad_bytes,
map->format.val_bytes); map->format.val_bytes,
false);
} }
static inline void *_regmap_map_get_context(struct regmap *map) static inline void *_regmap_map_get_context(struct regmap *map)
...@@ -1839,7 +1862,7 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val) ...@@ -1839,7 +1862,7 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val)
EXPORT_SYMBOL_GPL(regmap_write_async); EXPORT_SYMBOL_GPL(regmap_write_async);
int _regmap_raw_write(struct regmap *map, unsigned int reg, int _regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len) const void *val, size_t val_len, bool noinc)
{ {
size_t val_bytes = map->format.val_bytes; size_t val_bytes = map->format.val_bytes;
size_t val_count = val_len / val_bytes; size_t val_count = val_len / val_bytes;
...@@ -1860,7 +1883,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, ...@@ -1860,7 +1883,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
/* Write as many bytes as possible with chunk_size */ /* Write as many bytes as possible with chunk_size */
for (i = 0; i < chunk_count; i++) { for (i = 0; i < chunk_count; i++) {
ret = _regmap_raw_write_impl(map, reg, val, chunk_bytes); ret = _regmap_raw_write_impl(map, reg, val, chunk_bytes, noinc);
if (ret) if (ret)
return ret; return ret;
...@@ -1871,7 +1894,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, ...@@ -1871,7 +1894,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
/* Write remaining bytes */ /* Write remaining bytes */
if (val_len) if (val_len)
ret = _regmap_raw_write_impl(map, reg, val, val_len); ret = _regmap_raw_write_impl(map, reg, val, val_len, noinc);
return ret; return ret;
} }
...@@ -1904,7 +1927,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, ...@@ -1904,7 +1927,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
map->lock(map->lock_arg); map->lock(map->lock_arg);
ret = _regmap_raw_write(map, reg, val, val_len); ret = _regmap_raw_write(map, reg, val, val_len, false);
map->unlock(map->lock_arg); map->unlock(map->lock_arg);
...@@ -1962,7 +1985,7 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg, ...@@ -1962,7 +1985,7 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg,
write_len = map->max_raw_write; write_len = map->max_raw_write;
else else
write_len = val_len; write_len = val_len;
ret = _regmap_raw_write(map, reg, val, write_len); ret = _regmap_raw_write(map, reg, val, write_len, true);
if (ret) if (ret)
goto out_unlock; goto out_unlock;
val = ((u8 *)val) + write_len; val = ((u8 *)val) + write_len;
...@@ -2439,7 +2462,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg, ...@@ -2439,7 +2462,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
map->async = true; map->async = true;
ret = _regmap_raw_write(map, reg, val, val_len); ret = _regmap_raw_write(map, reg, val, val_len, false);
map->async = false; map->async = false;
...@@ -2450,7 +2473,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg, ...@@ -2450,7 +2473,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
EXPORT_SYMBOL_GPL(regmap_raw_write_async); EXPORT_SYMBOL_GPL(regmap_raw_write_async);
static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
unsigned int val_len) unsigned int val_len, bool noinc)
{ {
struct regmap_range_node *range; struct regmap_range_node *range;
int ret; int ret;
...@@ -2463,7 +2486,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, ...@@ -2463,7 +2486,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
range = _regmap_range_lookup(map, reg); range = _regmap_range_lookup(map, reg);
if (range) { if (range) {
ret = _regmap_select_page(map, &reg, range, ret = _regmap_select_page(map, &reg, range,
val_len / map->format.val_bytes); noinc ? 1 : val_len / map->format.val_bytes);
if (ret != 0) if (ret != 0)
return ret; return ret;
} }
...@@ -2501,7 +2524,7 @@ static int _regmap_bus_read(void *context, unsigned int reg, ...@@ -2501,7 +2524,7 @@ static int _regmap_bus_read(void *context, unsigned int reg,
if (!map->format.parse_val) if (!map->format.parse_val)
return -EINVAL; return -EINVAL;
ret = _regmap_raw_read(map, reg, work_val, map->format.val_bytes); ret = _regmap_raw_read(map, reg, work_val, map->format.val_bytes, false);
if (ret == 0) if (ret == 0)
*val = map->format.parse_val(work_val); *val = map->format.parse_val(work_val);
...@@ -2617,7 +2640,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, ...@@ -2617,7 +2640,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
/* Read bytes that fit into whole chunks */ /* Read bytes that fit into whole chunks */
for (i = 0; i < chunk_count; i++) { for (i = 0; i < chunk_count; i++) {
ret = _regmap_raw_read(map, reg, val, chunk_bytes); ret = _regmap_raw_read(map, reg, val, chunk_bytes, false);
if (ret != 0) if (ret != 0)
goto out; goto out;
...@@ -2628,7 +2651,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, ...@@ -2628,7 +2651,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
/* Read remaining bytes */ /* Read remaining bytes */
if (val_len) { if (val_len) {
ret = _regmap_raw_read(map, reg, val, val_len); ret = _regmap_raw_read(map, reg, val, val_len, false);
if (ret != 0) if (ret != 0)
goto out; goto out;
} }
...@@ -2703,7 +2726,7 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg, ...@@ -2703,7 +2726,7 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
read_len = map->max_raw_read; read_len = map->max_raw_read;
else else
read_len = val_len; read_len = val_len;
ret = _regmap_raw_read(map, reg, val, read_len); ret = _regmap_raw_read(map, reg, val, read_len, true);
if (ret) if (ret)
goto out_unlock; goto out_unlock;
val = ((u8 *)val) + read_len; val = ((u8 *)val) + read_len;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment