diff --git a/tools/testing/cxl/uunit/Makefile b/tools/testing/cxl/uunit/Makefile
new file mode 100644
index 000000000000..290a4c332b8b
--- /dev/null
+++ b/tools/testing/cxl/uunit/Makefile
@@ -0,0 +1,141 @@
+# SPDX-License-Identifier: GPL-2.0
+ROOT_DIR := $(CURDIR)
+KERNEL_DIR := ../../../..
+UNIT_TEST_DIR := $(ROOT_DIR)/app
+
+V ?= @
+.SECONDARY:
+
+# Pull environment (include paths, cpp/c flags) from top-level Makefile
+LINUXINCLUDE_STRING    = $(shell make -C $(KERNEL_DIR) -qp | grep "^LINUXINCLUDE :=")
+KBUILD_CPPFLAGS_STRING = $(shell make -C $(KERNEL_DIR) -qp | grep "^KBUILD_CPPFLAGS :=")
+KBUILD_CFLAGS_STRING   = $(shell make -C $(KERNEL_DIR) -qp | grep "^KBUILD_CFLAGS :=")
+
+$(eval $(subst ./,$(KERNEL_DIR)/,$(LINUXINCLUDE_STRING)))
+$(eval $(KBUILD_CPPFLAGS_STRING))
+$(eval $(KBUILD_CFLAGS_STRING))
+
+KBUILD_NAME_STRING = -DKBUILD_BASENAME='"uunit"' -DKBUILD_MODNAME='"uunit"' -DKBUILD_MODFILE='"uunit"'
+
+# Add uunit include directories
+INCLUDE_FLAGS := -I$(ROOT_DIR)/include -I$(ROOT_DIR)/build/include
+# Add kernel root to include directories, so unit tests can include kernel source directly
+INCLUDE_FLAGS := $(INCLUDE_FLAGS) -I$(KERNEL_DIR)
+# Add stubs for KBUILD defines
+INCLUDE_FLAGS := $(INCLUDE_FLAGS) $(KBUILD_NAME_STRING)
+# Include kernel build parameters
+INCLUDE_FLAGS := $(INCLUDE_FLAGS) $(LINUXINCLUDE) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)
+# Adjust kernel parameters
+#  shadow-call-stack not available in userspace
+#  no-frame-larger-than restricts ability to put objects on stack in unit tests
+#  -O0 to enable better debuggability
+#  -D__NO_FORTIFY since fortify does not work with some compilers with -O0
+#  Don't use thunk-extern, fentry, or stack-protector-strong with unit tests
+INCLUDE_FLAGS := $(INCLUDE_FLAGS) -fno-sanitize=shadow-call-stack -Wno-frame-larger-than -O0 -D__NO_FORTIFY
+INCLUDE_FLAGS := $(subst -mindirect-branch=thunk-extern,,$(INCLUDE_FLAGS))
+INCLUDE_FLAGS := $(subst -mfunction-return=thunk-extern,,$(INCLUDE_FLAGS))
+INCLUDE_FLAGS := $(subst -fstack-protector-strong,,$(INCLUDE_FLAGS))
+INCLUDE_FLAGS := $(subst -mfentry,,$(INCLUDE_FLAGS))
+INCLUDE_FLAGS := $(subst -DCC_USING_FENTRY,,$(INCLUDE_FLAGS))
+
+LINK_FLAGS = $(INCLUDE_FLAGS) -no-pie -lcunit
+
+# The following source files are built and linked into each unit test application
+# instead of creating stubs for them.
+KERNEL_SRC = \
+	drivers/base/bus.c \
+	drivers/base/driver.c \
+	lib/kobject.c \
+	lib/klist.c \
+	lib/refcount.c \
+	lib/xarray.c \
+	lib/find_bit.c \
+	lib/hweight.c \
+	lib/radix-tree.c \
+	lib/uuid.c \
+	lib/hexdump.c \
+	lib/kstrtox.c \
+	lib/sort.c
+
+UNIT_TEST_SRC = \
+	$(UNIT_TEST_DIR)/core_hdm_ut.c \
+	$(UNIT_TEST_DIR)/core_region_ut.c
+UNIT_TEST_BIN = $(patsubst $(UNIT_TEST_DIR)/%.c,build/bin/%,$(UNIT_TEST_SRC))
+UNIT_TEST_EXEC = $(patsubst build/bin/%,exec_%,$(UNIT_TEST_BIN))
+
+all: $(UNIT_TEST_BIN)
+
+clean:
+	$(V)rm -rf build
+
+# compiler_attributes.h uses __attribute__(__error__) for static asserts - this depends
+# on the compiler optimizing away the function, so for unit test builds with -O0 we
+# create a new version of that header to ensure the attribute doesn't get used.
+MODIFIED_HEADERS = build/include/linux/compiler_attributes.h
+
+# We will disable some configuration settings for unit tests, to simplify the unit test
+# environment.
+MODIFIED_HEADERS += build/include/generated/autoconf.h
+
+# WARN_FLAGS generates constraint errors on x86. So we will modify bug.h slightly to
+# workaround this problem.
+MODIFIED_HEADERS += build/include/asm-generic/bug.h
+
+build/include/linux/compiler_attributes.h: $(KERNEL_DIR)/include/linux/compiler_attributes.h
+	$(V)echo [HEADER] $(subst build/,,$@)
+	$(V)mkdir -p $(dir $@)
+	$(V)sed 's/__has_attribute(__error__)/& \&\& defined(__OPTIMIZE__)/' $< > $@
+
+build/include/generated/autoconf.h: $(KERNEL_DIR)/include/generated/autoconf.h
+	$(V)echo [HEADER] $(subst build/,,$@)
+	$(V)mkdir -p $(dir $@)
+	$(V)cp $< $@
+	$(V)sed -i 's/#define CONFIG_DYNAMIC_DEBUG 1//' $@
+	$(V)sed -i 's/#define CONFIG_DYNAMIC_DEBUG_CORE 1//' $@
+	$(V)sed -i 's/#define CONFIG_HAVE_FENTRY 1//' $@
+	$(V)sed -i 's/#define CONFIG_LIST_HARDENED 1//' $@
+	$(V)sed -i 's/#define CONFIG_PREEMPTION 1//' $@
+	$(V)sed -i 's/#define CONFIG_PREEMPT_DYNAMIC 1//' $@
+	$(V)sed -i 's/#define CONFIG_DEBUG_PREEMPT 1//' $@
+
+build/include/asm-generic/bug.h: $(KERNEL_DIR)/include/asm-generic/bug.h
+	$(V)echo [HEADER] $(subst build/,,$@)
+	$(V)mkdir -p $(dir $@)
+	$(V)cp $< $@
+	$(V)sed -i 's/#ifndef __WARN_FLAGS/#undef __WARN_FLAGS\n&/' $@
+
+# Kernel source cannot be compiled directly, it defines functions with slightly different
+# signatures than same stdlib functions. So we build them with preprocessor wrappers that
+# re-define offending functions to something non-stdlib.
+WRAPPED_KERNEL_SRC = $(patsubst %.c,build/wrap/%.c,$(KERNEL_SRC))
+WRAPPED_KERNEL_OBJ = $(subst .c,.o,$(WRAPPED_KERNEL_SRC))
+
+build/wrap/%.c: $(MODIFIED_HEADERS) $(KERNEL_DIR)/%.c
+	$(V)echo [SRC] $(subst build/,,$@)
+	$(V)mkdir -p $(dir $@)
+	$(V)echo \#include \"pre.h\" > $@
+	$(V)echo \#include \"$(KERNEL_DIR)/$*.c\" >> $@
+	$(V)echo \#include \"post.h\" >> $@
+
+build/wrap/%.o: build/wrap/%.c Makefile
+	$(V)echo [OBJ] $(subst build/,,$@)
+	$(V)$(CC) -o $@ -c $< $(INCLUDE_FLAGS)
+
+# Unit test applications include cxl source directly, which requires internal cxl headers.
+build/%_ut.o: INCLUDE_FLAGS += -I$(KERNEL_DIR)/drivers/cxl
+
+build/obj/%_ut.o: $(UNIT_TEST_DIR)/%_ut.c $(ROOT_DIR)/stub.c $(MODIFIED_HEADERS)  Makefile
+	$(V)echo [OBJ] $(subst build/,,$@)
+	$(V)mkdir -p $(dir $@)
+	$(V)$(CC) -o $@ -c $< $(INCLUDE_FLAGS) -DMODULE
+
+build/bin/%_ut: $(WRAPPED_KERNEL_OBJ) build/obj/%_ut.o
+	$(V)echo [LINK] $(subst build/,,$@)
+	$(V)mkdir -p $(dir $@)
+	$(V)$(CC) -o $@ $^ $(LINK_FLAGS)
+
+exec_%: build/bin/%
+	$(V)echo [RUN] $(subst build/,,$^)
+	$(V)$^
+
+runtests: $(UNIT_TEST_EXEC)
diff --git a/tools/testing/cxl/uunit/app/core_hdm_ut.c b/tools/testing/cxl/uunit/app/core_hdm_ut.c
new file mode 100644
index 000000000000..cc2ff52d916c
--- /dev/null
+++ b/tools/testing/cxl/uunit/app/core_hdm_ut.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "pre.h"
+#include "drivers/cxl/core/hdm.c"
+#include "post.h"
+#include "../stub.c"
+
+#include <CUnit/Basic.h>
+#include <stdlib.h>
+
+static void
+add_hdm_decoder_test(void)
+{
+	struct cxl_port port;
+	struct cxl_decoder decoder;
+	int targets[8];
+
+	CU_ASSERT(add_hdm_decoder(&port, &decoder, targets) == 0);
+}
+
+static void
+cxl_settle_decoders_test(void)
+{
+	struct cxl_hdm hdm;
+	char _regs[0x200];
+	void *regs = _regs;
+	unsigned int msecs;
+
+	*(long *)&_regs[CXL_HDM_DECODER0_CTRL_OFFSET(0)] = CXL_HDM_DECODER0_CTRL_COMMITTED;
+	*(long *)&_regs[CXL_HDM_DECODER0_CTRL_OFFSET(1)] = CXL_HDM_DECODER0_CTRL_COMMITTED;
+	*(long *)&_regs[CXL_HDM_DECODER0_CTRL_OFFSET(2)] = 0;
+
+	hdm.regs.hdm_decoder = regs;
+
+	/*
+	 * With just one decoder, we should not see a delay, since all decoders report they
+	 * are committed.
+	 */
+	hdm.decoder_count = 1;
+	msecs = g_msecs;
+	cxl_settle_decoders(&hdm);
+	CU_ASSERT(g_msecs == msecs);
+
+	/*
+	 * With two decoders, we should not see a delay, since all decoders report they
+	 * are committed.
+	 */
+	hdm.decoder_count = 2;
+	msecs = g_msecs;
+	cxl_settle_decoders(&hdm);
+	CU_ASSERT(g_msecs == msecs);
+
+	/*
+	 * With three decoders, we should see a delay, since not all decoders report they
+	 * are committed. Check that the delay is at least as big as the spec defined
+	 * 10ms commit timeout (CXL 2.0 8.2.5.12.20)..
+	 */
+	hdm.decoder_count = 3;
+	msecs = g_msecs;
+	cxl_settle_decoders(&hdm);
+	CU_ASSERT(g_msecs >= msecs + 10);
+}
+
+int
+main(int argc, char **argv)
+{
+	CU_pSuite suite = NULL;
+	unsigned int num_failures;
+
+	CU_set_error_action(CUEA_ABORT);
+	CU_initialize_registry();
+
+	suite = CU_add_suite("app_suite", NULL, NULL);
+	CU_ADD_TEST(suite, add_hdm_decoder_test);
+	CU_ADD_TEST(suite, cxl_settle_decoders_test);
+
+	CU_basic_set_mode(CU_BRM_VERBOSE);
+	CU_basic_run_tests();
+	num_failures = CU_get_number_of_failures();
+	CU_cleanup_registry();
+
+	return num_failures;
+}
diff --git a/tools/testing/cxl/uunit/app/core_region_ut.c b/tools/testing/cxl/uunit/app/core_region_ut.c
new file mode 100644
index 000000000000..770ec200c87f
--- /dev/null
+++ b/tools/testing/cxl/uunit/app/core_region_ut.c
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "pre.h"
+#include "drivers/cxl/core/region.c"
+#include "post.h"
+#include "../stub.c"
+
+#include <CUnit/Basic.h>
+#include <stdlib.h>
+
+DECLARE_RWSEM(cxl_dpa_rwsem);
+
+static void
+uuid_show_test(void)
+{
+	struct cxl_region region = { .dev.type = &cxl_region_type };
+	char buf[128];
+
+	region.mode = CXL_DECODER_RAM;
+	CU_ASSERT(uuid_show(&region.dev, NULL, buf) == 1);
+}
+
+static void
+is_cxl_pmem_region_test(void)
+{
+	struct device dev;
+
+	dev.type = &cxl_pmem_region_type;
+	CU_ASSERT(is_cxl_pmem_region(&dev) == true);
+
+	dev.type = &cxl_dax_region_type;
+	CU_ASSERT(is_cxl_pmem_region(&dev) == false);
+}
+
+const struct device_type dummy_region_type = {
+	.name = "dummy"
+};
+
+static void
+is_dup_test(void)
+{
+	struct device dummy = { .type = &dummy_region_type };
+	struct cxl_region region = { .dev.type = &cxl_region_type };
+	uuid_t uuid = { 0 };
+
+	/* non-region devices should always return 0 */
+	CU_ASSERT(is_dup(&dummy, NULL) == 0);
+
+	/*
+	 * uuid matches, indicates the specified uuid duplicates
+	 * the uuid for an existing region
+	 * return -EBUSY
+	 */
+	CU_ASSERT(is_dup(&region.dev, &uuid) == -EBUSY);
+
+	/*
+	 * uuid does not match
+	 */
+	uuid.b[0] = 1;
+	CU_ASSERT(is_dup(&region.dev, &uuid) == 0);
+}
+
+static void
+interleave_ways_store_test(void)
+{
+	struct cxl_region *region = calloc(1, sizeof(*region));
+	const char *str0 = "0";
+	const char *str1 = "1";
+	const char *str16 = "16";
+	char buf[32];
+	struct cxl_root_decoder *root_decoder = calloc(1, sizeof(*root_decoder));
+
+	region->dev.type = &cxl_region_type;
+	region->dev.parent = &root_decoder->cxlsd.cxld.dev;
+	root_decoder->cxlsd.cxld.interleave_ways = 1;
+	region->params.interleave_ways = 0xFF;
+
+	CU_ASSERT(interleave_ways_store(&region->dev, NULL, str0, strlen(str0)) < 0);
+	CU_ASSERT(region->params.interleave_ways == 0xFF);
+
+	CU_ASSERT(interleave_ways_store(&region->dev, NULL, str1, strlen(str1)) == strlen(str1));
+	CU_ASSERT(region->params.interleave_ways == 1);
+	/* interleave_ways_show appends a newline to the value string */
+	CU_ASSERT(interleave_ways_show(&region->dev, NULL, buf) == strlen(str1) + 1);
+	CU_ASSERT(strncmp(buf, str1, strlen(str1)) == 0);
+
+	region->params.interleave_ways = 0xFF;
+	CU_ASSERT(interleave_ways_store(&region->dev, NULL, str16, strlen(str16)) == strlen(str16));
+	CU_ASSERT(region->params.interleave_ways == 16);
+	/* interleave_ways_show appends a newline to the value string */
+	CU_ASSERT(interleave_ways_show(&region->dev, NULL, buf) == strlen(str16) + 1);
+	CU_ASSERT(strncmp(buf, str16, strlen(str16)) == 0);
+
+	free(root_decoder);
+	free(region);
+}
+
+static void
+cxl_port_setup_targets_test(void)
+{
+	struct cxl_port			port;
+	struct cxl_region		region;
+	struct cxl_endpoint_decoder	ep_decoder, ep_decoder2;
+	struct cxl_port			ep_decoder_port, ep_decoder_port2;
+	struct cxl_region_ref		region_ref;
+	struct cxl_port			parent_port;
+	struct cxl_switch_decoder	switch_decoder;
+	struct cxl_root_decoder		root_decoder;
+	struct resource			resource;
+	struct cxl_memdev		memdev, memdev2;
+	struct cxl_ep			ep, ep2;
+	struct cxl_dport		dport;
+
+	ep_decoder.cxld.dev.parent = &ep_decoder_port.dev;
+	ep_decoder.pos = 0;
+	xa_init(&port.regions);
+	radix_tree_init();
+	CU_ASSERT(xa_insert(&port.regions, (unsigned long)&region, &region_ref, GFP_KERNEL) == 0);
+	region_ref.nr_targets = 2;
+	port.dev.parent = &parent_port.dev;
+	region_ref.region = &region;
+	region_ref.port = &port;
+	region_ref.decoder = &switch_decoder.cxld;
+	region.dev.parent = &root_decoder.cxlsd.cxld.dev;
+	region.params.interleave_granularity = 256;
+	root_decoder.cxlsd.cxld.interleave_ways = 2;
+	switch_decoder.nr_targets = 2;
+	resource.start = 0x1000000000;
+	resource.end =   0x2000000000;
+	region.params.res = &resource;
+	region.params.nr_targets = 2;
+	region.params.targets[0] = &ep_decoder;
+	region.params.targets[1] = &ep_decoder2;
+	ep_decoder_port.uport_dev = &memdev.dev;
+	xa_init(&port.endpoints);
+	ep.dport = &dport;
+	CU_ASSERT(xa_insert(&port.endpoints, (unsigned long)&memdev, &ep, GFP_KERNEL) == 0);
+	CU_ASSERT(cxl_port_setup_targets(&port, &region, &ep_decoder) == 0);
+	CU_ASSERT(region_ref.nr_targets_set == 1);
+	CU_ASSERT(switch_decoder.target[0] == &dport);
+
+	ep_decoder2.pos = 1;
+	ep_decoder2.cxld.dev.parent = &ep_decoder_port2.dev;
+	ep_decoder_port2.uport_dev = &memdev2.dev;
+	ep2.dport = &dport;
+	CU_ASSERT(xa_insert(&port.endpoints, (unsigned long)&memdev2, &ep2, GFP_KERNEL) == 0);
+	CU_ASSERT(cxl_port_setup_targets(&port, &region, &ep_decoder2) == 0);
+	CU_ASSERT(region_ref.nr_targets_set == 1);
+	CU_ASSERT(switch_decoder.target[0] == &dport);
+	CU_ASSERT(switch_decoder.target[1] == NULL);
+}
+
+static void
+cxl_region_setup_targets_test(void)
+{
+	struct cxl_port			port;
+	struct cxl_region		region;
+	struct cxl_endpoint_decoder	ep_decoder, ep_decoder2;
+	struct cxl_port			ep_decoder_port, ep_decoder_port2;
+	struct cxl_region_ref		region_ref;
+	struct cxl_port			parent_port;
+	struct cxl_switch_decoder	switch_decoder;
+	struct cxl_root_decoder		root_decoder;
+	struct resource			resource;
+	struct cxl_memdev		memdev, memdev2;
+	struct cxl_ep			ep, ep2;
+	struct cxl_dport		dport;
+	struct cxl_dev_state		dev_state, dev_state2;
+
+	ep_decoder.cxld.dev.parent = &ep_decoder_port.dev;
+	ep_decoder.pos = 0;
+	xa_init(&port.regions);
+	radix_tree_init();
+	CU_ASSERT(xa_insert(&port.regions, (unsigned long)&region, &region_ref, GFP_KERNEL) == 0);
+	region_ref.nr_targets = 2;
+	port.dev.parent = &parent_port.dev;
+	region_ref.region = &region;
+	region_ref.port = &port;
+	region_ref.decoder = &switch_decoder.cxld;
+	region.dev.parent = &root_decoder.cxlsd.cxld.dev;
+	region.params.interleave_granularity = 256;
+	root_decoder.cxlsd.cxld.interleave_ways = 2;
+	switch_decoder.nr_targets = 2;
+	resource.start = 0x1000000000;
+	resource.end =   0x2000000000;
+	region.params.res = &resource;
+	region.params.nr_targets = 2;
+	region.params.targets[0] = &ep_decoder;
+	region.params.targets[1] = &ep_decoder2;
+	ep_decoder_port.uport_dev = &memdev.dev;
+	ep_decoder_port.dev.parent = &port.dev;
+	ep_decoder_port2.dev.parent = &port.dev;
+	memdev.cxlds = &dev_state;
+	memdev2.cxlds = &dev_state2;
+	dev_state.rcd = 0;
+	dev_state2.rcd = 0;
+	xa_init(&port.endpoints);
+	ep.dport = &dport;
+
+	ep_decoder2.pos = 1;
+	ep_decoder2.cxld.dev.parent = &ep_decoder_port2.dev;
+	ep_decoder_port2.uport_dev = &memdev2.dev;
+	ep2.dport = &dport;
+
+	CU_ASSERT(xa_insert(&port.endpoints, (unsigned long)&memdev, &ep, GFP_KERNEL) == 0);
+	CU_ASSERT(xa_insert(&port.endpoints, (unsigned long)&memdev2, &ep2, GFP_KERNEL) == 0);
+
+	CU_ASSERT(cxl_region_setup_targets(&region) == 0);
+
+	CU_ASSERT(region_ref.nr_targets_set == 1);
+	CU_ASSERT(switch_decoder.target[0] == &dport);
+	CU_ASSERT(switch_decoder.target[1] == NULL);
+}
+
+int
+main(int argc, char **argv)
+{
+	CU_pSuite suite = NULL;
+	unsigned int num_failures;
+
+	CU_set_error_action(CUEA_ABORT);
+	CU_initialize_registry();
+
+	suite = CU_add_suite("app_suite", NULL, NULL);
+	CU_ADD_TEST(suite, uuid_show_test);
+	CU_ADD_TEST(suite, is_dup_test);
+	CU_ADD_TEST(suite, is_cxl_pmem_region_test);
+	CU_ADD_TEST(suite, interleave_ways_store_test);
+	CU_ADD_TEST(suite, cxl_port_setup_targets_test);
+	CU_ADD_TEST(suite, cxl_region_setup_targets_test);
+
+	CU_basic_set_mode(CU_BRM_VERBOSE);
+	CU_basic_run_tests();
+	num_failures = CU_get_number_of_failures();
+	CU_cleanup_registry();
+
+	return num_failures;
+}
diff --git a/tools/testing/cxl/uunit/include/post.h b/tools/testing/cxl/uunit/include/post.h
new file mode 100644
index 000000000000..231f5bc15c3a
--- /dev/null
+++ b/tools/testing/cxl/uunit/include/post.h
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+#undef strncpy
+#undef strcpy
+#undef strcat
+#undef strncat
+#undef strlcat
+#undef strlcpy
+#undef errno
+#undef loff_t
+#undef dev_t
+#undef timer_t
+#undef int64_t
+#undef u_int64_t
+#undef sigset_t
+#undef fd_set
+#undef blkcnt_t
+#undef nlink_t
+#undef ffs
+
+#undef abs
+#undef __alloc_size__
diff --git a/tools/testing/cxl/uunit/include/pre.h b/tools/testing/cxl/uunit/include/pre.h
new file mode 100644
index 000000000000..1eb88e51740f
--- /dev/null
+++ b/tools/testing/cxl/uunit/include/pre.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+#define strncpy		__kstrncpy
+#define strcpy		__kstrcpy
+#define strcat		__kstrcat
+#define strncat		__kstrncat
+#define strlcat		__kstrlcat
+#define strlcpy		__kstrlcpy
+#define errno		__kerrno
+#define loff_t		__kloff_t
+#define dev_t		__kdev_t
+#define timer_t		__ktimer_t
+#define int64_t		__kint64_t
+#define u_int64_t	__ku_int64_t
+#define sigset_t	__ksigset_t
+#define fd_set		__kfd_set
+#define blkcnt_t	__kblkcnt_t
+#define nlink_t		__knlink_t
+
+#define TRACE_EVENT(name, ...) static inline void trace_##name(...) {}
+#define WARN(lvl, ...) printk(__VA_ARGS__)
+#define WARN_ON_ONCE(cond) ({if(cond)printk(#cond);cond;})
+#define __DISABLE_TRACE_MMIO__
diff --git a/tools/testing/cxl/uunit/stub.c b/tools/testing/cxl/uunit/stub.c
new file mode 100644
index 000000000000..208dab1e7d6d
--- /dev/null
+++ b/tools/testing/cxl/uunit/stub.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "pre.h"
+#include "linux/cpuhotplug.h"
+#include "linux/rwsem.h"
+#include "linux/kobject.h"
+#include "linux/device/bus.h"
+#include "drivers/cxl/cxl.h"
+#include "post.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/* All modules require one of these. */
+struct module __this_module;
+
+/*
+ * All stubs must be place in a section for the associated kernel source file that defines it.
+ * All sections must be sorted by pathname.
+ */
+
+/*
+ * arch
+ * These cannot be mapped to a specific file, since archs are free to define them wherever they
+ * want in their arch directory.
+ */
+void __udelay(unsigned long usecs) { }
+unsigned long __stack_chk_guard;
+#ifdef CONFIG_ARCH_HAS_CPU_CACHE_INVALIDATE_MEMREGION
+int cpu_cache_invalidate_memregion(int res_desc) { return 0; }
+bool cpu_cache_has_invalidate_memregion(void) { return true; }
+#endif
+
+#ifdef CONFIG_ARM64
+/* arch/arm64/kernel/alternative.c */
+void alt_cb_patch_nops(struct alt_instr *alt, __le32 *origptr, __le32 *updptr, int nr_inst) {}
+
+/* arch/arm64/lib/copy_from_user.S */
+unsigned long __arch_copy_from_user(void *to, const void *from, unsigned long n) { return n; }
+DECLARE_BITMAP(system_cpucaps, ARM64_NCAPS);
+#endif
+
+#ifdef CONFIG_X86_64
+/* arch/x86/kernel/cpu/common.c */
+struct pcpu_hot pcpu_hot;
+
+/* arch/x86/kernel/setup_percpu.c */
+unsigned long this_cpu_off;
+#endif
+
+/* drivers/base/core.c */
+int device_add(struct device *dev) { return 0; }
+struct device *get_device(struct device *dev) { return NULL; }
+void put_device(struct device *dev) { }
+void device_unregister(struct device *dev) { }
+int device_for_each_child(struct device *dev, void *data,
+		int (*fn)(struct device *dev, void *data)) { return 0; }
+struct device *device_find_child(struct device *dev, void *data,
+		int (*match)(struct device *dev, void *data)) { return NULL; }
+struct device *device_find_child_by_name(struct device *parent, const char *name)
+{ return NULL; }
+const char *dev_driver_string(const struct device *dev) { return NULL; }
+void device_del(struct device *dev) { }
+int device_match_name(struct device *dev, const void *name) { return 0; }
+void device_initialize(struct device *dev) { }
+int dev_set_name(struct device *dev, const char* name, ...) { return 0; }
+void device_remove_groups(struct device *dev, const struct attribute_group **groups) { }
+int device_register(struct device *dev) { return 0; }
+int device_add_groups(struct device *dev, const struct attribute_group **groups) { return 0; }
+struct kobject *virtual_device_parent(struct device *dev) { return NULL; }
+struct kset *devices_kset;
+
+/* drivers/base/dd.c */
+int device_attach(struct device *dev) { return 0; }
+void device_release_driver(struct device *dev) { }
+int device_driver_attach(struct device_driver *drv, struct device *dev) { return 0; }
+void device_driver_detach(struct device *dev) { }
+int driver_attach(struct device_driver *drv) { return 0; }
+void driver_detach(struct device_driver *drv) { }
+void device_initial_probe(struct device *dev) { }
+void deferred_probe_extend_timeout(void) { }
+
+/* drivers/base/devres.c */
+int __devm_add_action(struct device *dev, void (*action)(void *), void *data, const char *name) { return 0; }
+void devm_release_action(struct device *dev, void (*action)(void *), void *data) { }
+void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp) { return NULL; }
+void devm_remove_action(struct device *dev, void (*action)(void *), void *data) { }
+
+/* drivers/base/module.c */
+void module_add_driver(struct module *mod, struct device_driver *drv) { }
+void module_remove_driver(struct device_driver *drv) { }
+
+/* drivers/base/power/runtime.c */
+int __pm_runtime_idle(struct device *dev, int rpmflags) { return 0; }
+
+/* drivers/char/random.c */
+void get_random_bytes(void *buf, size_t len) { }
+
+/* drivers/cxl/core/mbox.c */
+int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
+		struct cxl_region *cxlr) { return 0; }
+
+/* drivers/cxl/core/memdev.c */
+bool is_cxl_memdev(const struct device *dev) { return false; }
+
+/* drivers/cxl/core/pmem.c */
+struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd) { return NULL; }
+
+/* drivers/cxl/core/port.c */
+struct attribute_group cxl_base_attribute_group;
+struct bus_type cxl_bus_type;
+DECLARE_RWSEM(cxl_region_rwsem);
+struct cxl_port *to_cxl_port(const struct device *dev)
+{
+	return container_of(dev, struct cxl_port, dev);
+}
+struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev)
+{
+	return container_of(dev, struct cxl_root_decoder, cxlsd.cxld.dev);
+}
+struct cxl_switch_decoder *to_cxl_switch_decoder(struct device *dev)
+{
+	return container_of(dev, struct cxl_switch_decoder, cxld.dev);
+}
+struct cxl_endpoint_decoder *to_cxl_endpoint_decoder(struct device *dev) { return NULL; }
+struct cxl_decoder *to_cxl_decoder(struct device *dev) { return NULL; }
+bool is_endpoint_decoder(struct device *dev) { return true; }
+bool is_root_decoder(struct device *dev) { return true; }
+bool is_switch_decoder(struct device *dev) { return true; }
+int __cxl_driver_register(struct cxl_driver *cxl_drv, struct module *owner,
+		const char *modname) { return 0; }
+void cxl_driver_unregister(struct cxl_driver *cxl_drv) { }
+int cxl_decoder_add_locked(struct cxl_decoder *cxld, int *target_map) { return 0; }
+int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld) { return 0; }
+struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port,
+						    unsigned int nr_targets) { return NULL; }
+struct cxl_endpoint_decoder *cxl_endpoint_decoder_alloc(struct cxl_port *port) { return NULL; }
+int cxl_num_decoders_committed(struct cxl_port *port) { return 0; }
+
+/* drivers/cxl/core/regs.c */
+int cxl_map_component_regs(const struct cxl_register_map *map, struct cxl_component_regs *regs,
+			   unsigned long map_mask) { return 0; }
+void cxl_probe_component_regs(struct device *dev, void *base, struct cxl_component_reg_map *map) { }
+
+/* drivers/irqchip/irq-gic-v3.c */
+struct static_key_false gic_nonsecure_priorities;
+
+/* fs/kernfs/dir.c */
+void kernfs_get(struct kernfs_node *kn) { }
+void kernfs_put(struct kernfs_node *kn) { }
+
+/* fs/seq_file.c */
+void seq_printf(struct seq_file *m, const char *f, ...) { }
+
+/* fs/sysfs/file.c */
+int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, const void *ns) { return 0; }
+void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, const void *ns) { }
+int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { return 0; }
+int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, const void *new_ns) { return 0; }
+int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, const void *new_ns) { return 0; }
+int sysfs_create_groups(struct kobject *kobj, const struct attribute_group **groups) { return 0; }
+void sysfs_remove_groups(struct kobject *kobj, const struct attribute_group **groups) { }
+int sysfs_update_group(struct kobject *kobj, const struct attribute_group *grp) { return 0; }
+int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name) { return 0; }
+void sysfs_remove_link(struct kobject *kobj, const char *name) { }
+void sysfs_remove_dir(struct kobject *kobj) { }
+int sysfs_emit(char *buf, const char *fmt, ...)
+{
+	va_list argptr;
+	int rc;
+
+	va_start(argptr, fmt);
+	rc = vsprintf(buf, fmt, argptr);
+	va_end(argptr);
+
+	return rc;
+}
+bool sysfs_streq(const char *s1, const char *s2) { return true; }
+
+/* include linux/bitfield.h */
+/* Resolves undefined reference error when compiling at -O0 */
+void __bad_mask(void) { }
+
+/* include/linux/dev_printk.h, lib/dynamic_debug.c */
+void _dev_err(const struct device *dev, const char *fmt, ...) { }
+void _dev_warn(const struct device *dev, const char *fmt, ...) { }
+void _dev_info(const struct device *dev, const char *fmt, ...) { }
+void __dynamic_dev_dbg(struct _ddebug *descriptor, const struct device *dev,
+		const char *fmt, ...) { }
+void __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...) { }
+void __warn_printk(const char *fmt, ...) { }
+int _printk(const char *fmt, ...) { return 0; }
+const char *kvasprintf_const(gfp_t gfp, const char *fmt, va_list ap) { return NULL; }
+
+/* kernel/cpu.c */
+int __cpuhp_setup_state(enum cpuhp_state state, const char *name, bool invoke,
+		int (*startup)(unsigned int cpu),
+		int (*teardown)(unsigned int cpu), bool multi_instance) { return 0; }
+
+/* kernel/locking/mutex.c */
+void mutex_lock(struct mutex *lock) { }
+void mutex_unlock(struct mutex *lock) { }
+void __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key) { }
+
+/* kernel/locking/rwsem.c */
+void __init_rwsem(struct rw_semaphore *sem, const char *name, struct lock_class_key *key) { }
+void up_read(struct rw_semaphore *sem) { }
+void down_read(struct rw_semaphore *sem) { }
+int down_read_interruptible(struct rw_semaphore *sem) { return 0; }
+void up_write(struct rw_semaphore *sem) { }
+void down_write(struct rw_semaphore *sem) { }
+int down_write_killable(struct rw_semaphore *sem) { return 0; }
+
+/* kernel/locking/spinlock.c */
+void _raw_spin_lock(raw_spinlock_t *lock) { }
+void _raw_spin_lock_irq(raw_spinlock_t *lock) { }
+void _raw_spin_lock_bh(raw_spinlock_t *lock) { }
+void _raw_spin_unlock(raw_spinlock_t *lock) { }
+void _raw_spin_unlock_irq(raw_spinlock_t *lock) { }
+void _raw_spin_unlock_bh(raw_spinlock_t *lock) { }
+unsigned long _raw_spin_lock_irqsave(raw_spinlock_t *lock) { return 0; }
+void _raw_spin_unlock_irqrestore(raw_spinlock_t *lock, unsigned long flags) { }
+
+/* kernel/notifier.c */
+int blocking_notifier_chain_register(struct blocking_notifier_head *nh, struct notifier_block *n) { return 0; }
+int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, struct notifier_block *n) { return 0; }
+int blocking_notifier_call_chain(struct blocking_notifier_head *nh, unsigned long val, void *v) { return 0; }
+
+/* kernel/panic.c */
+void __stack_chk_fail(void) { }
+void warn_slowpath_fmt(const char *file, const int line, unsigned taint, const char *fmt, ...) { }
+
+/* kernel/rcu/tree.c */
+void call_rcu(struct rcu_head *head, rcu_callback_t func) { }
+
+/* kernel/rcu/tree_plugin.h */
+void __rcu_read_lock(void) { }
+void __rcu_read_unlock(void) { }
+
+/* kernel/resource.c */
+int insert_resource(struct resource *parent, struct resource *new) { return 0; }
+int remove_resource(struct resource *old) { return 0; }
+struct resource *alloc_free_mem_region(struct resource *base, unsigned long size,
+		unsigned long align, const char *name) { return NULL; }
+struct resource *__request_region(struct resource *parent,
+				  resource_size_t start, resource_size_t n,
+				  const char *name, int flags) { return NULL; }
+void __release_region(struct resource *parent, resource_size_t start, resource_size_t n) { }
+int walk_iomem_res_desc(unsigned long desc, unsigned long flags, u64 start, u64 end,
+		void *arg, int (*func)(struct resource *, void *)) { return 0; }
+
+/* kernel/sched/core.c */
+void dynamic_preempt_schedule(void) { }
+int dynamic_might_resched(void) { return 0; }
+int wake_up_process(struct task_struct *p) { return 0; }
+void schedule(void) { }
+//void preempt_schedule(void) { }
+//DEFINE_STATIC_CALL(preempt_schedule, preempt_schedule);
+
+/* kernel/time/timer.c */
+unsigned int g_msecs;
+void msleep(unsigned int msecs) { g_msecs += msecs; }
+void usleep_range_state(unsigned long min, unsigned long max, unsigned int state) { }
+
+/* lib/bitmap.c */
+void __bitmap_clear(unsigned long *map, unsigned int start, int len) { }
+
+/* lib/ctype.c */
+const unsigned char _ctype[1];
+
+/* lib/dump_stack.c */
+asmlinkage void dump_stack_lvl(const char *log_lvl) { }
+
+/* lib/kobject_uevent.c */
+int kobject_uevent(struct kobject *kobj, enum kobject_action) { return 0; }
+int kobject_uevent_env(struct kobject *kobj, enum kobject_action, char *envp[]) { return 0; }
+int kobject_synth_uevent(struct kobject *kobj, const char *buf, size_t count) { return 0; }
+
+/* lib/logic_iomem.c */
+#ifndef CONFIG_GENERIC_IOREMAP
+void *ioremap(phys_addr_t offset, size_t size) { return NULL; }
+#endif
+void iounmap(volatile void *addr) { }
+
+/* lib/memregion.c */
+int memregion_alloc(gfp_t gfp) { return 0; }
+void memregion_free(int id) { }
+
+/* lib/smp_processor_id.c */
+unsigned int debug_smp_processor_id(void) { return 0; }
+
+/* lib/string.c */
+char *strnchr(const char *s, size_t count, int c) { return NULL; }
+
+/* lib/string_helpers.c */
+char *strreplace(char *str, char old, char new) { return str; }
+
+/* lib/usercopy.c */
+#ifndef INLINE_COPY_FROM_USER
+unsigned long _copy_from_user(void *to, const void *from, unsigned long n) { return n; }
+#endif
+
+/* mm/maccess.c */
+void __copy_overflow(int size, unsigned long count) { }
+
+/* mm/percpu.c */
+unsigned long __per_cpu_offset[NR_CPUS];
+
+/* mm/slab_common.c */
+void *__kmalloc(size_t size, gfp_t flags) { return malloc(size); }
+void kfree(const void *objp) { free((void *)objp); }
+void kfree_const(const void *x) { free((void *)x); }
+struct kmem_cache {
+	unsigned int obj_size;
+};
+struct kmem_cache *kmem_cache_create(const char *name, unsigned int size, unsigned int align,
+		slab_flags_t flags, void (*ctor)(void *))
+{
+	struct kmem_cache *s = calloc(1, sizeof(*s));
+	s->obj_size = size;
+	return s;
+}
+void *kmem_cache_alloc(struct kmem_cache *s, gfp_t flags) { return malloc(s->obj_size); }
+void *kmem_cache_alloc_lru(struct kmem_cache *s, struct list_lru *lru, gfp_t gfpflags) { return malloc(s->obj_size); }
+void kmem_cache_free(struct kmem_cache *s, void *objp) { free(objp); }
+struct kmem_cache *kmalloc_caches[NR_KMALLOC_TYPES][KMALLOC_SHIFT_HIGH + 1];
+void *kmalloc_trace(struct kmem_cache *s, gfp_t flags, size_t size) { return NULL; }
+
+/* mm/usercopy.c */
+void __check_object_size(const void *ptr, unsigned long n, bool to_user) { }
+
+/* mm/util.c */
+char *kstrdup(const char *s, gfp_t gfp) { return strdup(s); }
+const char *kstrdup_const(const char *s, gfp_t gfp) { return strdup(s); }
+char *kstrndup(const char *s, size_t max, gfp_t gfp) { return strndup(s, max); }
+
