From: Denis Mukhin <dmukhin@ford.com>
Introduce some basic infrastructure for doing domain ID allocation unit tests,
and add a few tests that ensure correctness of the domain ID allocator in
dom0less and non-dom0less scenarios.
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes since v10:
- new patch
---
tools/tests/Makefile | 2 +-
tools/tests/domid/.gitignore | 2 +
tools/tests/domid/Makefile | 69 ++++++++++++++
tools/tests/domid/include/xen/domain.h | 124 +++++++++++++++++++++++++
tools/tests/domid/test-domid.c | 88 ++++++++++++++++++
5 files changed, 284 insertions(+), 1 deletion(-)
create mode 100644 tools/tests/domid/.gitignore
create mode 100644 tools/tests/domid/Makefile
create mode 100644 tools/tests/domid/include/xen/domain.h
create mode 100644 tools/tests/domid/test-domid.c
diff --git a/tools/tests/Makefile b/tools/tests/Makefile
index 36928676a666..ff1666425436 100644
--- a/tools/tests/Makefile
+++ b/tools/tests/Makefile
@@ -1,7 +1,7 @@
XEN_ROOT = $(CURDIR)/../..
include $(XEN_ROOT)/tools/Rules.mk
-SUBDIRS-y :=
+SUBDIRS-y := domid
SUBDIRS-y += resource
SUBDIRS-$(CONFIG_X86) += cpu-policy
SUBDIRS-$(CONFIG_X86) += tsx
diff --git a/tools/tests/domid/.gitignore b/tools/tests/domid/.gitignore
new file mode 100644
index 000000000000..91ac43232518
--- /dev/null
+++ b/tools/tests/domid/.gitignore
@@ -0,0 +1,2 @@
+*.o
+test-domid-*
diff --git a/tools/tests/domid/Makefile b/tools/tests/domid/Makefile
new file mode 100644
index 000000000000..e0b6a0e0a49c
--- /dev/null
+++ b/tools/tests/domid/Makefile
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Unit tests for domain ID allocator.
+#
+# There are two flavors of the test:
+# - default; domain ID can be reused.
+# - dom0less: each domain ID can be allocated only once.
+#
+# Copyright 2025 Ford Motor Company
+
+XEN_ROOT=$(CURDIR)/../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+TESTS := test-domid-default test-domid-CONFIG_DOM0LESS_BOOT
+
+vpath domid.c $(XEN_ROOT)/xen/common/
+
+.PHONY: all
+all: $(TESTS)
+
+.PHONY: run
+run: $(TESTS)
+ $(foreach t,$(TESTS),./$(t);)
+
+.PHONY: clean
+clean:
+ $(RM) -- *.o $(TESTS) $(DEPS_RM)
+
+.PHONY: distclean
+distclean: clean
+ $(RM) -- *~
+
+.PHONY: install
+install: all
+ $(INSTALL_DIR) $(DESTDIR)$(LIBEXEC)/tests
+ $(INSTALL_PROG) test-domid-* $(DESTDIR)$(LIBEXEC)/tests
+
+.PHONY: uninstall
+uninstall:
+ $(RM) -- $(DESTDIR)$(LIBEXEC)/tests/test-domid-*
+
+CFLAGS += -D__XEN_TOOLS__
+CFLAGS += $(APPEND_CFLAGS)
+CFLAGS += $(CFLAGS_xeninclude)
+CFLAGS += -I./include/
+
+LDFLAGS += $(APPEND_LDFLAGS)
+
+# default (no dom0less) scenario
+domid-default.o: domid.c
+ $(CC) $(CFLAGS) -c $^ -o $@ $(LDFLAGS)
+
+test-domid-default.o: test-domid.c
+ $(CC) $(CFLAGS) -c $^ -o $@ $(LDFLAGS)
+
+test-domid-default: domid-default.o test-domid-default.o
+ $(CC) $^ -o $@ $(LDFLAGS)
+
+# dom0less scenario
+domid-CONFIG_DOM0LESS_BOOT.o: domid.c
+ $(CC) $(CFLAGS) -DCONFIG_DOM0LESS_BOOT -c $^ -o $@ $(LDFLAGS)
+
+test-domid-CONFIG_DOM0LESS_BOOT.o: test-domid.c
+ $(CC) $(CFLAGS) -DCONFIG_DOM0LESS_BOOT -c $^ -o $@ $(LDFLAGS)
+
+test-domid-CONFIG_DOM0LESS_BOOT: domid-CONFIG_DOM0LESS_BOOT.o test-domid-CONFIG_DOM0LESS_BOOT.o
+ $(CC) -DCONFIG_DOM0LESS_BOOT $^ -o $@ $(LDFLAGS)
+
+-include $(DEPS_INCLUDE)
diff --git a/tools/tests/domid/include/xen/domain.h b/tools/tests/domid/include/xen/domain.h
new file mode 100644
index 000000000000..465568c0dd02
--- /dev/null
+++ b/tools/tests/domid/include/xen/domain.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Unit test harness for domain ID allocator.
+ *
+ * Copyright 2025 Ford Motor Company
+ */
+
+#ifndef _TEST_HARNESS_
+#define _TEST_HARNESS_
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xen-tools/common-macros.h>
+
+#define BUG_ON(x) assert(!(x))
+#define ASSERT(x) assert(x)
+
+#define __xen_mk_uint(x) x ## U
+#define xen_mk_uint(x) __xen_mk_uint(x)
+
+#define DOMID_FIRST_RESERVED xen_mk_uint(10)
+#define DOMID_INVALID xen_mk_uint(11)
+
+#define DEFINE_SPINLOCK(x) unsigned long *(x)
+#define spin_lock(x) ((*(x))++)
+#define spin_unlock(x) ((*(x))--)
+
+#define BITS_PER_LONG sizeof(unsigned long)
+#define BITS_PER_WORD (8U * BITS_PER_LONG)
+#define BITS_TO_LONGS(bits) \
+ (((bits) + BITS_PER_LONG - 1) / BITS_PER_LONG)
+#define DECLARE_BITMAP(name, bits) \
+ unsigned long name[BITS_TO_LONGS(bits)]
+
+static inline int __test_and_set_bit(unsigned int nr, unsigned long *addr)
+{
+ unsigned long mask = 1UL << (nr % BITS_PER_WORD);
+ unsigned long *p = addr + (nr / BITS_PER_WORD);
+ int old = (*p & mask) != 0;
+
+ *p |= mask;
+
+ return old;
+}
+
+static inline void __set_bit(unsigned int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = 1UL << (nr % BITS_PER_WORD);
+ unsigned long *p = (unsigned long *)addr + (nr / BITS_PER_WORD);
+
+ *p |= mask;
+}
+
+static inline void __clear_bit(unsigned int nr, volatile unsigned long *addr)
+{
+ unsigned long mask = 1UL << (nr % BITS_PER_WORD);
+ unsigned long *p = (unsigned long *)addr + (nr / BITS_PER_WORD);
+
+ *p &= ~mask;
+}
+
+static inline unsigned long find_next_zero_bit(const unsigned long *addr,
+ unsigned long size,
+ unsigned long offset)
+{
+ unsigned long idx = offset / BITS_PER_WORD;
+ unsigned long bit = offset % BITS_PER_WORD;
+
+ if (offset >= size)
+ return size;
+
+ while (offset < size)
+ {
+ unsigned long val = addr[idx] | (~0UL >> (BITS_PER_WORD - bit));
+
+ if (~val)
+ {
+ unsigned long pos = __builtin_ffsl(~val);
+
+ if (pos > 0)
+ {
+ unsigned long rc = idx * BITS_PER_WORD + (pos - 1);
+
+ if (rc < size)
+ return rc;
+ }
+ }
+
+ offset = (idx + 1) * BITS_PER_WORD;
+ idx++;
+ bit = 0;
+ }
+
+ return size;
+}
+
+#define printk printf
+
+#define cf_check
+
+typedef bool spinlock_t;
+typedef uint16_t domid_t;
+
+/* See include/xen/domain.h */
+extern domid_t domid_alloc(domid_t domid);
+extern void domid_free(domid_t domid);
+
+#endif /* _TEST_HARNESS_ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/tools/tests/domid/test-domid.c b/tools/tests/domid/test-domid.c
new file mode 100644
index 000000000000..f077d77be0df
--- /dev/null
+++ b/tools/tests/domid/test-domid.c
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Unit tests for domain ID allocator.
+ *
+ * Copyright 2025 Ford Motor Company
+ */
+
+/* Local test include replicating hypervisor includes. */
+#include <xen/domain.h>
+
+int main(int argc, char **argv)
+{
+ domid_t expected, allocated;
+
+ printk("%s DOMID_FIRST_RESERVED=%u DOMID_INVALID=%u\n",
+ argv[0], DOMID_FIRST_RESERVED, DOMID_INVALID);
+
+ /* Test ID#0 cannot be allocated twice. */
+ allocated = domid_alloc(0);
+ printk("expected %u allocated %u\n", 0, allocated);
+ ASSERT(allocated == 0);
+ allocated = domid_alloc(0);
+ printk("expected %u allocated %u\n", DOMID_INVALID, allocated);
+ ASSERT(allocated == DOMID_INVALID);
+
+ /* Ensure ID is not allocated. */
+ domid_free(0);
+
+ /*
+ * Test that that two consecutive calls of domid_alloc(DOMID_INVALID)
+ * will never return the same ID.
+ * NB: ID#0 is reserved and shall not be allocated by
+ * domid_alloc(DOMID_INVALID).
+ */
+ for ( expected = 0; expected < DOMID_FIRST_RESERVED - 1; expected++ )
+ {
+ allocated = domid_alloc(DOMID_INVALID);
+ printk("expected %u allocated %u\n", expected + 1, allocated);
+ ASSERT(allocated == expected + 1);
+ }
+ for ( expected = 0; expected < DOMID_FIRST_RESERVED; expected++ )
+ {
+ allocated = domid_alloc(DOMID_INVALID);
+ printk("expected %u allocated %u\n", DOMID_INVALID, allocated);
+ ASSERT(allocated == DOMID_INVALID);
+ }
+
+ /* Re-allocate first ID from [1..DOMID_FIRST_RESERVED - 1]. */
+#ifdef CONFIG_DOM0LESS_BOOT
+ /* Each ID is allowed to be allocated only once. */
+ expected = DOMID_INVALID;
+#else
+ expected = 1;
+#endif
+ domid_free(1);
+ allocated = domid_alloc(DOMID_INVALID);
+ printk("expected %u allocated %u\n", expected, allocated);
+ ASSERT(allocated == expected);
+
+ /* Re-allocate last ID from [1..DOMID_FIRST_RESERVED - 1]. */
+#ifdef CONFIG_DOM0LESS_BOOT
+ /* Each ID is allowed to be allocated only once. */
+ expected = DOMID_INVALID;
+#else
+ expected = DOMID_FIRST_RESERVED - 1;
+#endif
+ domid_free(DOMID_FIRST_RESERVED - 1);
+ allocated = domid_alloc(DOMID_INVALID);
+ printk("expected %u allocated %u\n", expected, allocated);
+ ASSERT(allocated == expected);
+
+ /* Allocate an invalid ID. */
+ expected = DOMID_INVALID;
+ allocated = domid_alloc(DOMID_FIRST_RESERVED);
+ printk("expected %u allocated %u\n", expected, allocated);
+ ASSERT(allocated == expected);
+
+ return 0;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
2.34.1
© 2016 - 2025 Red Hat, Inc.