kunit: fake: added library to make faking devices easier

Adds infrastructure to allow hardware fakes to be easily inserted behind
iomemory access routines.

Change-Id: I2491a1eccb83a3e93317a67731f61c6b45c73ded
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
diff --git a/include/test/fake.h b/include/test/fake.h
new file mode 100644
index 0000000..d92e47c
--- /dev/null
+++ b/include/test/fake.h
@@ -0,0 +1,51 @@
+#ifndef _TEST_FAKE_H
+#define _TEST_FAKE_H
+
+#include <test/mock.h>
+
+struct fake_device;
+
+struct fake_register_map_entry {
+	phys_addr_t offset;
+	bool valid_entry;
+	u8 (*readb)(struct fake_device *fd);
+	u16 (*readw)(struct fake_device *fd);
+	u32 (*readl)(struct fake_device *fd);
+	u64 (*readq)(struct fake_device *fd);
+	void (*writeb)(struct fake_device *fd, u8 value);
+	void (*writew)(struct fake_device *fd, u16 value);
+	void (*writel)(struct fake_device *fd, u32 value);
+	void (*writeq)(struct fake_device *fd, u64 value);
+};
+
+#define FAKE_32_RW(offset_, read, write) { .offset = offset_, .valid_entry = true, .readl = read, .writel = write, }
+#define FAKE_32_RO(offset, read) FAKE_32_RW(offset, read, NULL)
+#define FAKE_32_WO(offset, write) FAKE_32_RW(offset, NULL, write)
+#define FAKE_32_NOP(offset) FAKE_32_RW(offset, NULL, NULL)
+
+struct fake_device_description {
+	struct fake_register_map_entry *register_map;
+};
+
+struct fake_device {
+	/* private */
+	struct mock_action readl_action;
+	struct mock_action writel_action;
+	const struct fake_device_description *description;
+	struct test *test;
+	void *priv;
+};
+
+static inline struct test *fake_get_test(struct fake_device *fd)
+{
+	return fd->test;
+}
+
+static inline void *fake_get_data(struct fake_device *fd)
+{
+	return fd->priv;
+}
+
+void fake_device_init(struct test *test, const struct fake_device_description *descr, void *priv);
+
+#endif /* _TEST_FAKE_H */
diff --git a/test/Makefile b/test/Makefile
index 4d69804..606c081 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -2,6 +2,6 @@
   test-stream.o
 obj-$(CONFIG_TEST_TEST)		+= \
   test-mock.o mock-macro-test.o mock-test.o string-stream-test.o \
-  test-stream-test.o
+  test-stream-test.o fake.o
 obj-$(CONFIG_TEST_DEATH_TEST)	+= test-death-test.o
 obj-$(CONFIG_EXAMPLE_TEST)	+= example-test.o
diff --git a/test/fake.c b/test/fake.c
new file mode 100644
index 0000000..4d2ce57
--- /dev/null
+++ b/test/fake.c
@@ -0,0 +1,94 @@
+#include <test/fake.h>
+#include <linux/io.h>
+
+static struct fake_register_map_entry *fake_find_reg_action(struct fake_device *fd, phys_addr_t offset)
+{
+	struct fake_register_map_entry *reg_entry;
+	struct test *test = fake_get_test(fd);
+
+	for (reg_entry = fd->description->register_map; reg_entry->valid_entry; reg_entry++) {
+		if (reg_entry->offset == offset)
+			break;
+	}
+
+	ASSERT_NOT_ERR_OR_NULL(test, reg_entry);
+
+	return reg_entry;
+}
+
+static u32 fake_readl(struct fake_device *fd, phys_addr_t offset)
+{
+	struct fake_register_map_entry *reg_entry;
+
+	reg_entry = fake_find_reg_action(fd, offset);
+
+	if (reg_entry->readl)
+		return reg_entry->readl(fd);
+	else
+		return 0;
+}
+
+static void *fake_readl_action(struct mock_action *this, const void **params, int len)
+{
+	struct fake_device *fd = container_of(this, struct fake_device, readl_action);
+	struct test *test = fake_get_test(fd);
+	phys_addr_t offset;
+	u32 *ret;
+
+	ASSERT_EQ(test, len, 1);
+	offset = CONVERT_TO_ACTUAL_TYPE(phys_addr_t, params[0]);
+	ret = test_kzalloc(test, sizeof(*ret), GFP_KERNEL);
+	*ret = fake_readl(fd, offset);
+
+	return ret;
+}
+
+static void fake_writel(struct fake_device *fd, phys_addr_t offset, u32 value)
+{
+	struct fake_register_map_entry *reg_entry;
+
+	reg_entry = fake_find_reg_action(fd, offset);
+
+	if (reg_entry->writel)
+		reg_entry->writel(fd, value);
+}
+
+static void *fake_writel_action(struct mock_action *this, const void **params, int len)
+{
+	struct fake_device *fd = container_of(this, struct fake_device, writel_action);
+	struct test *test = fake_get_test(fd);
+	phys_addr_t offset;
+	u32 *ret, value;
+
+	ASSERT_EQ(test, len, 2);
+	value = CONVERT_TO_ACTUAL_TYPE(u32, params[0]);
+	offset = CONVERT_TO_ACTUAL_TYPE(phys_addr_t, params[1]);
+	ret = test_kzalloc(test, sizeof(*ret), GFP_KERNEL);
+	*ret = 0;
+	fake_writel(fd, offset, value);
+
+	return ret;
+}
+
+void fake_device_init(struct test *test, const struct fake_device_description *descr, void *priv)
+{
+	struct fake_device *fd;
+
+	fd = test_kzalloc(test, sizeof(*fd), GFP_KERNEL);
+
+	fd->description = descr;
+	fd->test = test;
+	fd->priv = priv;
+	fd->readl_action.do_action = fake_readl_action;
+	fd->writel_action.do_action = fake_writel_action;
+
+	mock_set_default_action(mock_get_global_mock(),
+				"readl",
+				readl,
+				&fd->readl_action);
+	mock_set_default_action(mock_get_global_mock(),
+				"writel",
+				writel,
+				&fd->writel_action);
+}
+