regmap: Fixes for v5.9
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. -----BEGIN PGP SIGNATURE----- iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAl9uTagTHGJyb29uaWVA a2VybmVsLm9yZwAKCRAk1otyXVSH0HQ0B/9rfdPyuAxvv21vDoQbVYI8vg98WOGg gB+JJ1WKt9o6H3R64k8drgGB1DL/vWTub2+bkIoey0XMmIOsADBq/qrWeX4ziwpl LhkDxBbTc1U0Wqkq2IONjQbp9pr4kJyoTxGc6BRyysO0TAcfrGD5JvSRru9SLiSc cWpLq6YgL1KoLSEcvy5B7n9TydCEtNw8CpnalLvxFtmlthelFFHkCZmt2+R2fYVa QsNGEPlmsiKEL7KXlnGrDcNsnJ59YBAicsaPMBUk0tGkbNt5ifdsSxUJbCmAV+41 yCt5QIlf0Ctjhb7m/Xm4efIOKbmyhGPmG7ZdrZMRXmmcLshFfxJkd+k+ =0+l/ -----END PGP SIGNATURE----- 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
This commit is contained in:
Коммит
9a3a0876b9
|
@ -217,7 +217,7 @@ struct regmap_field {
|
|||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
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);
|
||||
|
||||
static inline void regmap_debugfs_disable(struct regmap *map)
|
||||
|
@ -227,7 +227,7 @@ static inline void regmap_debugfs_disable(struct regmap *map)
|
|||
|
||||
#else
|
||||
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_disable(struct regmap *map) { }
|
||||
#endif
|
||||
|
@ -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 _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);
|
||||
|
||||
|
|
|
@ -717,7 +717,7 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
|
|||
|
||||
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)
|
||||
dev_err(map->dev, "Unable to sync registers %#x-%#x. %d\n",
|
||||
base, cur - map->reg_stride, ret);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
struct regmap_debugfs_node {
|
||||
struct regmap *map;
|
||||
const char *name;
|
||||
struct list_head link;
|
||||
};
|
||||
|
||||
|
@ -544,11 +543,12 @@ static const struct file_operations regmap_cache_bypass_fops = {
|
|||
.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 regmap_range_node *range_node;
|
||||
const char *devname = "dummy";
|
||||
const char *name = map->name;
|
||||
|
||||
/*
|
||||
* Userspace can initiate reads from the hardware over debugfs.
|
||||
|
@ -569,7 +569,6 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
|
|||
if (!node)
|
||||
return;
|
||||
node->map = map;
|
||||
node->name = name;
|
||||
mutex_lock(®map_debugfs_early_lock);
|
||||
list_add(&node->link, ®map_debugfs_early_list);
|
||||
mutex_unlock(®map_debugfs_early_lock);
|
||||
|
@ -679,7 +678,7 @@ void regmap_debugfs_initcall(void)
|
|||
|
||||
mutex_lock(®map_debugfs_early_lock);
|
||||
list_for_each_entry_safe(node, tmp, ®map_debugfs_early_list, link) {
|
||||
regmap_debugfs_init(node->map, node->name);
|
||||
regmap_debugfs_init(node->map);
|
||||
list_del(&node->link);
|
||||
kfree(node);
|
||||
}
|
||||
|
|
|
@ -581,14 +581,34 @@ static void regmap_range_exit(struct regmap *map)
|
|||
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,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap **m;
|
||||
int ret;
|
||||
|
||||
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() */
|
||||
m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL);
|
||||
|
@ -687,13 +707,9 @@ struct regmap *__regmap_init(struct device *dev,
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (config->name) {
|
||||
map->name = kstrdup_const(config->name, GFP_KERNEL);
|
||||
if (!map->name) {
|
||||
ret = -ENOMEM;
|
||||
goto err_map;
|
||||
}
|
||||
}
|
||||
ret = regmap_set_name(map, config);
|
||||
if (ret)
|
||||
goto err_map;
|
||||
|
||||
if (config->disable_locking) {
|
||||
map->lock = map->unlock = regmap_lock_unlock_none;
|
||||
|
@ -1137,7 +1153,7 @@ skip_format_initialization:
|
|||
if (ret != 0)
|
||||
goto err_regcache;
|
||||
} else {
|
||||
regmap_debugfs_init(map, config->name);
|
||||
regmap_debugfs_init(map);
|
||||
}
|
||||
|
||||
return map;
|
||||
|
@ -1297,6 +1313,8 @@ EXPORT_SYMBOL_GPL(regmap_field_free);
|
|||
*/
|
||||
int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
|
||||
{
|
||||
int ret;
|
||||
|
||||
regcache_exit(map);
|
||||
regmap_debugfs_exit(map);
|
||||
|
||||
|
@ -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->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_only = false;
|
||||
|
@ -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,
|
||||
const void *val, size_t val_len)
|
||||
const void *val, size_t val_len, bool noinc)
|
||||
{
|
||||
struct regmap_range_node *range;
|
||||
unsigned long flags;
|
||||
|
@ -1523,7 +1545,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
|
|||
win_residue, val_len / map->format.val_bytes);
|
||||
ret = _regmap_raw_write_impl(map, reg, val,
|
||||
win_residue *
|
||||
map->format.val_bytes);
|
||||
map->format.val_bytes, noinc);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
|
@ -1537,7 +1559,7 @@ static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
|
|||
win_residue = range->window_len - win_offset;
|
||||
}
|
||||
|
||||
ret = _regmap_select_page(map, ®, range, val_num);
|
||||
ret = _regmap_select_page(map, ®, range, noinc ? 1 : val_num);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
@ -1745,7 +1767,8 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg,
|
|||
map->work_buf +
|
||||
map->format.reg_bytes +
|
||||
map->format.pad_bytes,
|
||||
map->format.val_bytes);
|
||||
map->format.val_bytes,
|
||||
false);
|
||||
}
|
||||
|
||||
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)
|
|||
EXPORT_SYMBOL_GPL(regmap_write_async);
|
||||
|
||||
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_count = val_len / val_bytes;
|
||||
|
@ -1860,7 +1883,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
|
||||
/* Write as many bytes as possible with chunk_size */
|
||||
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)
|
||||
return ret;
|
||||
|
||||
|
@ -1871,7 +1894,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
|
||||
/* Write remaining bytes */
|
||||
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;
|
||||
}
|
||||
|
@ -1904,7 +1927,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
|
|||
|
||||
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);
|
||||
|
||||
|
@ -1962,7 +1985,7 @@ int regmap_noinc_write(struct regmap *map, unsigned int reg,
|
|||
write_len = map->max_raw_write;
|
||||
else
|
||||
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)
|
||||
goto out_unlock;
|
||||
val = ((u8 *)val) + write_len;
|
||||
|
@ -2439,7 +2462,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
|
|||
|
||||
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;
|
||||
|
||||
|
@ -2450,7 +2473,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
|
|||
EXPORT_SYMBOL_GPL(regmap_raw_write_async);
|
||||
|
||||
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;
|
||||
int ret;
|
||||
|
@ -2463,7 +2486,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
range = _regmap_range_lookup(map, reg);
|
||||
if (range) {
|
||||
ret = _regmap_select_page(map, ®, range,
|
||||
val_len / map->format.val_bytes);
|
||||
noinc ? 1 : val_len / map->format.val_bytes);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
@ -2501,7 +2524,7 @@ static int _regmap_bus_read(void *context, unsigned int reg,
|
|||
if (!map->format.parse_val)
|
||||
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)
|
||||
*val = map->format.parse_val(work_val);
|
||||
|
||||
|
@ -2617,7 +2640,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
|
||||
/* Read bytes that fit into whole chunks */
|
||||
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)
|
||||
goto out;
|
||||
|
||||
|
@ -2628,7 +2651,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
|||
|
||||
/* Read remaining bytes */
|
||||
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)
|
||||
goto out;
|
||||
}
|
||||
|
@ -2703,7 +2726,7 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
|
|||
read_len = map->max_raw_read;
|
||||
else
|
||||
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)
|
||||
goto out_unlock;
|
||||
val = ((u8 *)val) + read_len;
|
||||
|
|
Загрузка…
Ссылка в новой задаче