lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20241231100256.194753-1-sander@svanheule.net>
Date: Tue, 31 Dec 2024 11:02:56 +0100
From: Sander Vanheule <sander@...nheule.net>
To: Mark Brown <broonie@...nel.org>,
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	"Rafael J. Wysocki" <rafael@...nel.org>,
	linux-kernel@...r.kernel.org
Cc: Sander Vanheule <sander@...nheule.net>
Subject: [PATCH] regmap: add cache validity to REGCACHE_FLAT

The flat regcache will always assume the data in the cache is valid.
Since the cache takes priority over hardware values, this may shadow the
actual state of the device. This is not the case with REGCACHE_RBTREE
for example, which makes these implementation behave differently.

Add a new containing cache structure with the flat data table and a
bitmap indicating cache validity. Use this to provide an implementation
for regcache_ops.drop.

Since this makes the flat cache behave more like other caches, a special
case in the read_writeonly unit test can also be dropped. This reverts
commit d0c99ffe2126 ("regmap: Allow reads from write only registers with
the flat cache").

Signed-off-by: Sander Vanheule <sander@...nheule.net>
---
 drivers/base/regmap/regcache-flat.c | 70 ++++++++++++++++++++++++-----
 drivers/base/regmap/regmap-kunit.c  | 15 ++-----
 2 files changed, 63 insertions(+), 22 deletions(-)

diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index f36d3618b67c..4bdf2e58ade1 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -6,6 +6,8 @@
 //
 // Author: Mark Brown <broonie@...nsource.wolfsonmicro.com>
 
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
@@ -18,34 +20,65 @@ static inline unsigned int regcache_flat_get_index(const struct regmap *map,
 	return regcache_get_index_by_order(map, reg);
 }
 
+struct regcache_flat_data {
+	unsigned int *data;
+	unsigned long *valid;
+};
+
 static int regcache_flat_init(struct regmap *map)
 {
 	int i;
-	unsigned int *cache;
+	unsigned int cache_size;
+	struct regcache_flat_data *cache = NULL;
+	unsigned long *cache_valid = NULL;
+	unsigned int *cache_data = NULL;
 
 	if (!map || map->reg_stride_order < 0 || !map->max_register_is_set)
 		return -EINVAL;
 
-	map->cache = kcalloc(regcache_flat_get_index(map, map->max_register)
-			     + 1, sizeof(unsigned int), map->alloc_flags);
-	if (!map->cache)
+	cache_size = regcache_flat_get_index(map, map->max_register) + 1;
+	cache_data = kcalloc(cache_size, sizeof(unsigned int), map->alloc_flags);
+	if (!cache_data)
 		return -ENOMEM;
 
+	cache_valid = bitmap_zalloc(cache_size, map->alloc_flags);
+	if (!cache_valid)
+		goto err_free_valid;
+
+	map->cache = kmalloc(sizeof(*cache), map->alloc_flags);
+	if (!map->cache)
+		goto err_free;
+
 	cache = map->cache;
+	cache->valid = cache_valid;
+	cache->data = cache_data;
 
 	for (i = 0; i < map->num_reg_defaults; i++) {
 		unsigned int reg = map->reg_defaults[i].reg;
 		unsigned int index = regcache_flat_get_index(map, reg);
 
-		cache[index] = map->reg_defaults[i].def;
+		cache->data[index] = map->reg_defaults[i].def;
+		__set_bit(index, cache->valid);
 	}
 
 	return 0;
+
+err_free:
+	kfree(cache_data);
+err_free_valid:
+	bitmap_free(cache_valid);
+	return -ENOMEM;
 }
 
 static int regcache_flat_exit(struct regmap *map)
 {
-	kfree(map->cache);
+	struct regcache_flat_data *cache = map->cache;
+
+	if (cache) {
+		bitmap_free(cache->valid);
+		kfree(cache->data);
+	}
+	kfree(cache);
 	map->cache = NULL;
 
 	return 0;
@@ -54,10 +87,13 @@ static int regcache_flat_exit(struct regmap *map)
 static int regcache_flat_read(struct regmap *map,
 			      unsigned int reg, unsigned int *value)
 {
-	unsigned int *cache = map->cache;
+	struct regcache_flat_data *cache = map->cache;
 	unsigned int index = regcache_flat_get_index(map, reg);
 
-	*value = cache[index];
+	if (!test_bit(index, cache->valid))
+		return -ENOENT;
+
+	*value = cache->data[index];
 
 	return 0;
 }
@@ -65,10 +101,23 @@ static int regcache_flat_read(struct regmap *map,
 static int regcache_flat_write(struct regmap *map, unsigned int reg,
 			       unsigned int value)
 {
-	unsigned int *cache = map->cache;
+	struct regcache_flat_data *cache = map->cache;
 	unsigned int index = regcache_flat_get_index(map, reg);
 
-	cache[index] = value;
+	cache->data[index] = value;
+	__set_bit(index, cache->valid);
+
+	return 0;
+}
+
+static int regcache_flat_drop(struct regmap *map, unsigned int min,
+			      unsigned int max)
+{
+	struct regcache_flat_data *cache = map->cache;
+	unsigned int index_min = regcache_flat_get_index(map, min);
+	unsigned int index_max = regcache_flat_get_index(map, max);
+
+	bitmap_clear(cache->valid, index_min, index_max - index_min);
 
 	return 0;
 }
@@ -80,4 +129,5 @@ struct regcache_ops regcache_flat_ops = {
 	.exit = regcache_flat_exit,
 	.read = regcache_flat_read,
 	.write = regcache_flat_write,
+	.drop = regcache_flat_drop,
 };
diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c
index 64ea340950b6..676f1a91c352 100644
--- a/drivers/base/regmap/regmap-kunit.c
+++ b/drivers/base/regmap/regmap-kunit.c
@@ -567,18 +567,9 @@ static void read_writeonly(struct kunit *test)
 	for (i = 0; i < BLOCK_TEST_SIZE; i++)
 		data->read[i] = false;
 
-	/*
-	 * Try to read all the registers, the writeonly one should
-	 * fail if we aren't using the flat cache.
-	 */
-	for (i = 0; i < BLOCK_TEST_SIZE; i++) {
-		if (config.cache_type != REGCACHE_FLAT) {
-			KUNIT_EXPECT_EQ(test, i != 5,
-					regmap_read(map, i, &val) == 0);
-		} else {
-			KUNIT_EXPECT_EQ(test, 0, regmap_read(map, i, &val));
-		}
-	}
+	/* Try to read all the registers, the writeonly one should fail */
+	for (i = 0; i < BLOCK_TEST_SIZE; i++)
+		KUNIT_EXPECT_EQ(test, i != 5, regmap_read(map, i, &val) == 0);
 
 	/* Did we trigger a hardware access? */
 	KUNIT_EXPECT_FALSE(test, data->read[5]);
-- 
2.47.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ