Commit 9a3a0876 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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);

+1 −1
Original line number Diff line number Diff line
@@ -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);
+3 −4
Original line number Diff line number Diff line
@@ -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(&regmap_debugfs_early_lock);
		list_add(&node->link, &regmap_debugfs_early_list);
		mutex_unlock(&regmap_debugfs_early_lock);
@@ -679,7 +678,7 @@ void regmap_debugfs_initcall(void)

	mutex_lock(&regmap_debugfs_early_lock);
	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);
		kfree(node);
	}
+49 −26
Original line number Diff line number Diff line
@@ -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;
	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 @@ struct regmap *__regmap_init(struct device *dev,
		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, &reg, range, val_num);
		ret = _regmap_select_page(map, &reg, 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, &reg, 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;